Skip to content

Commit 7b82f61

Browse files
author
Kevin Milner
committed
new tools for organizing tex and figures for SSA submission requirements
1 parent fccb59a commit 7b82f61

2 files changed

Lines changed: 211 additions & 2 deletions

File tree

src/main/java/scratch/kevin/latex/LaTeXUtils.java

Lines changed: 143 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
import java.io.IOException;
66
import java.nio.charset.Charset;
77
import java.text.DecimalFormat;
8+
import java.util.ArrayList;
89
import java.util.HashMap;
910
import java.util.List;
1011
import java.util.Map;
@@ -287,6 +288,138 @@ public static void embedIncludes(File inputTexFile, File refDir, File outputTexF
287288
fw.close();
288289
Files.move(tmpOutputFile, outputTexFile);
289290
}
291+
292+
private static List<String> stripCommentBlocks(List<String> lines) {
293+
List<String> ret = new ArrayList<>(lines.size());
294+
295+
boolean insideComment = false;
296+
297+
for (String line : lines) {
298+
String trimmed = line.trim();
299+
if (insideComment) {
300+
if (trimmed.startsWith("\\end{comment}"))
301+
insideComment = false;
302+
} else if (trimmed.startsWith("\\begin{comment}")) {
303+
insideComment = true;
304+
} else {
305+
ret.add(line);
306+
}
307+
}
308+
309+
return ret;
310+
}
311+
312+
public static void reorganizeFiguresForSSA(File inputTexFile, File outputDir) throws IOException {
313+
Preconditions.checkState(outputDir.exists() || outputDir.mkdir(),
314+
"Output dir doesn't exist and could not be created: %s", outputDir.getAbsolutePath());
315+
316+
File inputDir = inputTexFile.getParentFile();
317+
File outputTexFile = new File(outputDir, inputTexFile.getName());
318+
319+
List<String> lines = Files.readLines(inputTexFile, Charset.defaultCharset());
320+
lines = stripCommentBlocks(lines);
321+
322+
File tmpOutputFile = new File(outputTexFile.getParentFile(), outputTexFile.getName()+".bak");
323+
FileWriter fw = new FileWriter(tmpOutputFile, Charset.defaultCharset());
324+
325+
String[] extensions = { "pdf", "eps", "ps", "png", "jpg", "jpeg", "gif" };
326+
327+
// figure out total figure count
328+
int totalNumFigures = 0;
329+
for (String line : lines)
330+
if (line.trim().startsWith("\\begin{figure}"))
331+
totalNumFigures++;
332+
System.out.println("Identified "+totalNumFigures+" figures");
333+
334+
DecimalFormat numDF = new DecimalFormat((totalNumFigures+"").replaceAll(".", "0"));
335+
336+
boolean readingFigure = false;
337+
boolean readingSubfigure = false;
338+
boolean subfigHasCaption = false;
339+
int curFigNum = 1;
340+
char curSubfigChar = 'a';
341+
int curSubfigNumExtra = 0;
342+
for (String line : lines) {
343+
String trimmed = line.trim();
344+
if (readingFigure) {
345+
if (trimmed.startsWith("\\end{figure}")) {
346+
System.out.println("DONE with figure "+curFigNum);
347+
Preconditions.checkState(!readingSubfigure, "done reading figure but still reading subfigure?");
348+
readingFigure = false;
349+
curFigNum++;
350+
curSubfigChar = 'a';
351+
} else if (trimmed.startsWith("\\begin{subfigure}")) {
352+
Preconditions.checkState(!readingSubfigure, "already reading a subfigure?");
353+
readingSubfigure = true;
354+
subfigHasCaption = false;
355+
} else if (readingSubfigure && trimmed.startsWith("\\caption")) {
356+
subfigHasCaption = true;
357+
} else if (trimmed.startsWith("\\end{subfigure}")) {
358+
Preconditions.checkState(readingSubfigure, "ending subfigure, but not reading a subfigure?");
359+
readingSubfigure = false;
360+
} else if (trimmed.startsWith("\\includegraphics")) {
361+
int pathStartIndex = line.indexOf('{')+1;
362+
Preconditions.checkState(pathStartIndex > 0,
363+
"on an includegraphics line but path {} not found; must be on a single line: %s", trimmed);
364+
String beforePath = line.substring(0, pathStartIndex);
365+
String path = line.substring(pathStartIndex);
366+
int pathEndIndex = path.indexOf('}');
367+
Preconditions.checkState(pathEndIndex >= 0, "Line doesn't close '}': %s", trimmed);
368+
String afterPath = path.substring(pathEndIndex);
369+
path = path.substring(0, pathEndIndex);
370+
371+
File figInFile = new File(inputDir, path);
372+
for (int i=0; !figInFile.exists() && i<extensions.length; i++) {
373+
figInFile = new File(inputDir, path+"."+extensions[i]);
374+
}
375+
Preconditions.checkState(figInFile.exists(),
376+
"Figure input file not found: %s. Also tried these extensions: %s", path, extensions);
377+
String outName = "figure_"+numDF.format(curFigNum);
378+
if (readingSubfigure) {
379+
if (subfigHasCaption) {
380+
outName += "_"+curSubfigChar;
381+
Preconditions.checkState(curSubfigChar != 'z',
382+
"Ran out of subfigure letters for figure %s", curFigNum);
383+
curSubfigChar++;
384+
} else if (path.contains("cpt")) {
385+
outName += "_colorscale";
386+
} else {
387+
outName += "_extra";
388+
curSubfigNumExtra++;
389+
if (curSubfigNumExtra > 1)
390+
outName += "_"+curSubfigNumExtra;
391+
}
392+
}
393+
String inName = figInFile.getName();
394+
if (inName.contains(".")) {
395+
String ext = inName.substring(inName.lastIndexOf(".")+1);
396+
outName += "."+ext;
397+
}
398+
399+
File figOutFile = new File(outputDir, outName);
400+
System.out.println("\t"+path+"\t->\t"+outName);
401+
402+
if (outName.endsWith(".pdf"))
403+
PdfFirstPageCopier.copyFirstPage(figInFile, figOutFile);
404+
else
405+
Files.copy(figInFile, figOutFile);
406+
407+
String newLine = beforePath+outName+afterPath;
408+
fw.write(newLine);
409+
fw.write("\n");
410+
continue;
411+
}
412+
} else if (trimmed.startsWith("\\begin{figure}")) {
413+
readingFigure = true;
414+
}
415+
fw.write(line);
416+
fw.write("\n");
417+
}
418+
419+
fw.close();
420+
Files.move(tmpOutputFile, outputTexFile);
421+
422+
}
290423

291424
public static void main(String[] args) throws IOException {
292425
// String raw = "Special characters: & % $ # _ { } ~ ^ \\ and \\& already escaped.";
@@ -300,11 +433,11 @@ public static void main(String[] args) throws IOException {
300433
// System.out.println(defineValueCommand("SlipRateExample", "\\expnum("+numberExpFormat(3.14)+")", false));
301434
// System.out.println(defineValueCommand("SlipRateExample", "\\expnum("+numberExpFormat(3e-10)+")", false));
302435

303-
File prviDir = new File("/home/kevin/Documents/papers/2024_PRVI_ERF");
436+
File prviDir = new File("/home/kevin/Documents/papers/2025_PRVI_ERF");
304437

305438
File mainBranch = new File(prviDir, "prvi25-erf-paper");
306-
File initialBranch = new File(prviDir, "initial-bssa-submission");
307439

440+
// File initialBranch = new File(prviDir, "initial-bssa-submission");
308441
// File refDir = initialBranch;
309442
// File inputFile = new File(mainBranch, "submission/original/main_for_diff.tex");
310443
// File outputFile = new File(mainBranch, "submission/original/embedded_for_diff.tex");
@@ -315,6 +448,14 @@ public static void main(String[] args) throws IOException {
315448

316449
embedDynvalIncludes(inputFile, refDir, outputFile);
317450
embedIncludes(outputFile, refDir, outputFile);
451+
452+
// File ssaOutputDir = new File(new File(mainBranch, "submission"), "final-bssa-submission");
453+
File ssaOutputDir = new File(prviDir, "final-bssa-submission-tests");
454+
Preconditions.checkState(ssaOutputDir.exists() || ssaOutputDir.mkdir(),
455+
"SSA output dir doesn't exist and could not be created: %s", ssaOutputDir.getAbsolutePath());
456+
reorganizeFiguresForSSA(outputFile, ssaOutputDir);
457+
// rename from embedded to just main
458+
Files.move(new File(ssaOutputDir, outputFile.getName()), new File(ssaOutputDir, inputFile.getName()));
318459
}
319460

320461
}
Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,68 @@
1+
package scratch.kevin.latex;
2+
3+
import com.google.common.io.Files;
4+
import com.itextpdf.text.Document;
5+
import com.itextpdf.text.DocumentException;
6+
import com.itextpdf.text.pdf.PdfCopy;
7+
import com.itextpdf.text.pdf.PdfReader;
8+
import com.itextpdf.text.pdf.PdfStamper;
9+
10+
import java.io.File;
11+
import java.io.FileOutputStream;
12+
import java.io.IOException;
13+
import java.util.HashMap;
14+
15+
import org.apache.commons.lang3.exception.ExceptionUtils;
16+
17+
public class PdfFirstPageCopier {
18+
19+
/**
20+
* Copies only the first page of the source PDF to the destination.
21+
* If the source has exactly 1 page, the full file is copied directly.
22+
*
23+
* Document metadata (info dictionary) is preserved.
24+
*/
25+
public static void copyFirstPage(File src, File dest) throws IOException {
26+
PdfReader reader = new PdfReader(src.getAbsolutePath());
27+
try {
28+
int numPages = reader.getNumberOfPages();
29+
30+
if (numPages <= 1) {
31+
// Only one page → copy whole file
32+
Files.copy(src, dest);
33+
return;
34+
}
35+
36+
// Extract metadata
37+
HashMap<String, String> info = reader.getInfo();
38+
39+
// --- Step 1: Create temp file for first-page-only PDF ---
40+
File tmp = File.createTempFile("first_page_", ".pdf");
41+
tmp.deleteOnExit();
42+
43+
Document document = new Document(reader.getPageSizeWithRotation(1));
44+
PdfCopy copy = new PdfCopy(document, new FileOutputStream(tmp));
45+
46+
document.open();
47+
copy.addPage(copy.getImportedPage(reader, 1));
48+
document.close();
49+
copy.close();
50+
51+
reader.close(); // close original reader early
52+
53+
// --- Step 2: Apply metadata via PdfStamper ---
54+
PdfReader tmpReader = new PdfReader(tmp.getAbsolutePath());
55+
PdfStamper stamper = new PdfStamper(tmpReader, new FileOutputStream(dest));
56+
stamper.setMoreInfo(info);
57+
stamper.close();
58+
tmpReader.close();
59+
60+
tmp.delete();
61+
62+
} catch (DocumentException e) {
63+
throw ExceptionUtils.asRuntimeException(e);
64+
} finally {
65+
reader.close();
66+
}
67+
}
68+
}

0 commit comments

Comments
 (0)