Skip to content

Commit 192d333

Browse files
committed
Properly deal with sidenotes in long tables
1 parent 86b22ca commit 192d333

File tree

2 files changed

+105
-0
lines changed

2 files changed

+105
-0
lines changed

src/format/pdf/format-pdf.ts

Lines changed: 80 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -368,6 +368,7 @@ function pdfLatexPostProcessor(
368368
lineProcessors.push(captionFootnoteLineProcessor());
369369
lineProcessors.push(codeAnnotationPostProcessor());
370370
lineProcessors.push(codeListAnnotationPostProcessor());
371+
lineProcessors.push(longTableSidenoteProcessor());
371372

372373
await processLines(output, lineProcessors, temp);
373374
if (Object.keys(renderedCites).length > 0) {
@@ -619,6 +620,85 @@ const captionFootnoteLineProcessor = () => {
619620
};
620621
};
621622

623+
const processLongTableSidenotes = (latexLongTable: string) => {
624+
const sideNoteMarker = "\\sidenote{\\footnotesize ";
625+
let strProcessing = latexLongTable;
626+
const strOutput: string[] = [];
627+
const sidenotes: string[] = [];
628+
629+
let sidenotePos = strProcessing.indexOf(sideNoteMarker);
630+
while (sidenotePos > -1) {
631+
strOutput.push(strProcessing.substring(0, sidenotePos));
632+
633+
const remainingStr = strProcessing.substring(
634+
sidenotePos + sideNoteMarker.length,
635+
);
636+
let escaped = false;
637+
let sideNoteEnd = -1;
638+
for (let i = 0; i < remainingStr.length; i++) {
639+
const ch = remainingStr[i];
640+
if (ch === "\\") {
641+
escaped = true;
642+
} else {
643+
if (!escaped && ch === "}") {
644+
sideNoteEnd = i;
645+
break;
646+
} else {
647+
escaped = false;
648+
}
649+
}
650+
}
651+
652+
if (sideNoteEnd > -1) {
653+
strOutput.push("\\sidenotemark{}");
654+
const contents = remainingStr.substring(0, sideNoteEnd);
655+
sidenotes.push(contents);
656+
strProcessing = remainingStr.substring(sideNoteEnd + 1);
657+
sidenotePos = strProcessing.indexOf(sideNoteMarker);
658+
} else {
659+
strOutput.push(remainingStr);
660+
}
661+
}
662+
strOutput.push(strProcessing);
663+
664+
for (const note of sidenotes) {
665+
strOutput.push(`\\sidenotetext{${note}}\n`);
666+
}
667+
668+
return strOutput.join("");
669+
};
670+
671+
const longTableSidenoteProcessor = () => {
672+
let state: "scanning" | "capturing" = "scanning";
673+
let capturedLines: string[] = [];
674+
return (line: string): string | undefined => {
675+
switch (state) {
676+
case "scanning":
677+
if (line.match(/^\\begin{longtable}.*$/)) {
678+
state = "capturing";
679+
capturedLines = [line];
680+
return undefined;
681+
} else {
682+
return line;
683+
}
684+
case "capturing":
685+
capturedLines.push(line);
686+
if (line.match(/^\\end{longtable}.*$/)) {
687+
state = "scanning";
688+
689+
// read the whole figure and clear any capture state
690+
const lines = capturedLines.join("\n");
691+
capturedLines = [];
692+
693+
// Process the captions and relocate footnotes
694+
return processLongTableSidenotes(lines);
695+
} else {
696+
return undefined;
697+
}
698+
}
699+
};
700+
};
701+
622702
const calloutFigureHoldLineProcessor = () => {
623703
let state: "scanning" | "replacing" = "scanning";
624704
return (line: string): string | undefined => {
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
---
2+
format:
3+
pdf: default
4+
reference-location: margin
5+
---
6+
7+
fruit | count
8+
---|---
9+
apple | 1^[This is the {cool} best fruit]
10+
banana | 2
11+
12+
: Fruits and the animals that eat them {#tbl-fruit}
13+
14+
15+
16+
17+
fruit | count
18+
---|---
19+
apple | 1[^1]
20+
banana | 2[^2]
21+
22+
: Fruits and the animals that eat them {#tbl-fruit}
23+
24+
[^1]: This is the best fruit
25+
[^2]: This is a pretty good fruit, but not the best.

0 commit comments

Comments
 (0)