Skip to content

Commit 0b663cf

Browse files
committed
Embed PDF scripts and browse examples
Replace the example dropdown with a collapsible list on the right. Load bundled scripts on first use and take names from Info comments. prompt: Add right-side examples; embed docs/scripts in scripts.js
1 parent 09ee5b3 commit 0b663cf

62 files changed

Lines changed: 3220 additions & 686 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

‎cmd/gen-docs.ts‎

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ import { join, resolve } from "node:path";
77
import { commands as commandsDef } from "./gen-commands";
88
import { checkCdnImages } from "./r2";
99
import { copyFileNormalized, websiteDocsDir } from "./util.js";
10+
import { genPDFScripts } from "./gen-pdf-scripts";
1011

1112
const docsDir = "docs";
1213
const manualOutDir = join(".work", "docs");
@@ -157,7 +158,7 @@ function copyScriptExamples(outDir: string, pages: string[]): void {
157158
throw new Error(`${kScriptExamplesPage}: duplicate script ${name}`);
158159
}
159160
names.add(name);
160-
copyFileNormalized(join(outDir, "scripts", name), join(scriptsDir, name));
161+
copyFileNormalized(join(outDir, "scripts", name), join(docsDir, "scripts", name));
161162
}
162163
for (const file of readdirSync(scriptsDir, { withFileTypes: true })) {
163164
if (file.isFile() && file.name.endsWith(".js") && !names.has(file.name)) {
@@ -292,6 +293,7 @@ export type GenDocsOptions = {
292293
export async function main(opts: GenDocsOptions = {}) {
293294
const timeStart = performance.now();
294295
console.log("gen-docs starting");
296+
genPDFScripts();
295297

296298
// CI has no (private) sumatra-website checkout; the exe ships without the manual
297299
if (!existsSync(mdDir)) {

‎cmd/gen-pdf-scripts.ts‎

Lines changed: 29 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -1,29 +1,32 @@
1-
import { readFileSync, writeFileSync } from "node:fs";
2-
import { join } from "node:path";
1+
import { existsSync, mkdirSync, readFileSync, readdirSync, writeFileSync } from "node:fs";
2+
import { join, resolve } from "node:path";
33

4-
const scripts = {
5-
"load-a-pdf.js": "Load a PDF",
6-
"create-a-pdf.js": "Create a PDF",
7-
"add-an-annotation.js": "Add an annotation",
8-
"convert-pdf-to-plain-text.js": "Convert PDF to plain text",
9-
"convert-pdf-to-images.js": "Convert PDF to images",
10-
"create-pdf.js": "Create PDF",
11-
"create-pdf-low-level.js": "Create PDF (low level)",
12-
"merge-pdfs.js": "Merge PDFs",
13-
"create-pdf-with-thumbnails.js": "Create PDF with thumbnails",
14-
"draw-graphics-with-device.js": "Draw graphics with Device",
15-
"trace-device.js": "Trace device",
16-
"pdf-dejpx.js": "Replace JPEG 2000 images",
17-
"copy-outline.js": "Copy outline",
18-
"pdf-bake.js": "Bake annotations",
19-
};
4+
const root = resolve(import.meta.dir, "..");
5+
const kDefaultScript = "load-a-pdf.js";
6+
const kScriptSeparator = "// ---";
207

21-
const root = join(import.meta.dir, "..");
22-
let out = "// Generated by cmd/gen-pdf-scripts.ts.\nstatic const PDFScriptExample kPDFScripts[] = {\n";
23-
for (const [file, title] of Object.entries(scripts)) {
24-
const source = readFileSync(join(root, "src/pdf-scripts", file), "utf8").replaceAll("\r\n", "\n");
25-
if (source.includes(')JS"')) throw new Error(`Raw-string delimiter in ${file}`);
26-
out += ` {StrL(${JSON.stringify(title)}), StrL(R"JS(${source})JS")},\n`;
8+
export function genPDFScripts(inputRoot = root): string {
9+
const scriptsDir = join(inputRoot, "docs/scripts");
10+
const names = readdirSync(scriptsDir, { withFileTypes: true })
11+
.filter((file) => file.isFile() && file.name.endsWith(".js"))
12+
.map((file) => file.name)
13+
.sort();
14+
const first = names.indexOf(kDefaultScript);
15+
if (first >= 0) names.unshift(...names.splice(first, 1));
16+
const scripts = names.map((name) => {
17+
const source = readFileSync(join(scriptsDir, name), "utf8").replaceAll("\r\n", "\n").trimEnd();
18+
if (source.includes(kScriptSeparator)) throw new Error(`${name}: contains the script separator`);
19+
if (!source.trim()) throw new Error(`${name}: empty script`);
20+
return source;
21+
});
22+
if (!scripts.length) throw new Error("No PDF scripts found");
23+
const bundle = scripts.join(`\n${kScriptSeparator}\n`) + "\n";
24+
const output = join(inputRoot, ".work/scripts.js");
25+
mkdirSync(join(inputRoot, ".work"), { recursive: true });
26+
if (!existsSync(output) || readFileSync(output, "utf8") !== bundle) writeFileSync(output, bundle);
27+
return output;
28+
}
29+
30+
if (import.meta.main) {
31+
console.log(genPDFScripts());
2732
}
28-
out += "};\n";
29-
writeFileSync(join(root, "src/shared/PDFScriptExamples.inc"), out);

‎cmd/helper/embedded.ts‎

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ import {
1212
} from "node:fs";
1313
import { dirname, join, relative, resolve, sep } from "node:path";
1414
import type { Toolchain } from "./ng-toolchain";
15+
import { genPDFScripts } from "../gen-pdf-scripts";
1516

1617
const root = resolve(import.meta.dir, "../..");
1718
export const fontPatterns: [string, string[]][] = [
@@ -75,6 +76,7 @@ export async function packEmbedded(
7576
const staging = join(dir, "embedded");
7677
const archive = join(dir, "embedded.lzsa");
7778
const inputs = new Map<string, string>();
79+
inputs.set("scripts.js", genPDFScripts(inputRoot));
7880
const docs = join(inputRoot, ".work/docs");
7981
if (opts.manual) {
8082
for (const src of filesUnder(docs)) inputs.set(relative(docs, src), src);

‎cmd/helper/ng-shared.ts‎

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -115,7 +115,6 @@ export const sharedFiles = [
115115
"src/shared/AdvancedSettingsDialogCommon.h",
116116
"src/shared/PDFScript.h",
117117
"src/shared/PDFScriptCommon.cpp",
118-
"src/shared/PDFScriptExamples.inc",
119118
"src/shared/RestrictedUseConfig.cpp",
120119
"src/shared/RestrictedUseConfig.h",
121120
"src/shared/AnnotEditToolbarCommon.cpp",

‎cmd/pack-embedded-prebuild.cmd‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -73,6 +73,7 @@ if exist "%WORK%\docs\" (
7373
)
7474
if not exist "%STAGING%\" mkdir "%STAGING%"
7575
copy /y "%WORK%\translations.txt" "%STAGING%\translations.txt" >nul || exit /b 1
76+
copy /y "%WORK%\scripts.js" "%STAGING%\scripts.js" >nul || exit /b 1
7677
copy /y "%ROOT%\ext\marked.min.js" "%STAGING%\marked.min.js" >nul || exit /b 1
7778
copy /y "%ROOT%\ext\mermaid.min.js" "%STAGING%\mermaid.min.js" >nul || exit /b 1
7879

‎docs/scripts/add-an-annotation.js‎

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
// Info: Add a free-text annotation to the first page.
2+
// Arg: Input PDF
3+
// Arg: Output PDF
4+
5+
var doc = Document.openDocument(scriptArgs[0]);
6+
7+
var page = doc.loadPage(0);
8+
9+
var annot = page.createAnnotation("FreeText");
10+
annot.setRect([10, 10, 200, 100]);
11+
annot.setContents("Hello, world!");
12+
13+
page.update();
14+
15+
doc.save(scriptArgs[1]);
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
// Info: Render every page as a PNG at 96 DPI.
2+
// Arg: Input PDF
3+
// Arg: Output filename prefix (folder must exist)
4+
5+
var doc = Document.openDocument(scriptArgs[0]);
6+
for (var i = 0; i < doc.countPages(); ++i) {
7+
var page = doc.loadPage(i);
8+
var pixmap = page.toPixmap(Matrix.scale(96 / 72, 96 / 72), ColorSpace.DeviceRGB);
9+
pixmap.saveAsPNG(scriptArgs[1] + "-" + (i + 1) + ".png");
10+
}
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
// Info: Print the text of every page.
2+
// Arg: Input PDF
3+
4+
var doc = Document.openDocument(scriptArgs[0]);
5+
for (var i = 0; i < doc.countPages(); ++i) {
6+
var page = doc.loadPage(i);
7+
var text = page.toStructuredText().asText();
8+
print(text);
9+
}

‎docs/scripts/copy-outline.js‎

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
// Info: Copy bookmarks from another document.
2+
// Arg: Input PDF
3+
// Arg: Output PDF
4+
// Arg: Outline source PDF
5+
6+
// Copy an outline from one document to another PDF file.
7+
// Can be used to transfer outlines when converting from another format into PDF.
8+
// Can also be used to change outlines to use page numbers instead of named destinations.
9+
10+
if (scriptArgs.length != 3) {
11+
print("arguments: input.pdf output.pdf outline-source.pdf");
12+
quit();
13+
}
14+
15+
function copy_outline_rec(cursor, input, list) {
16+
list.forEach(function (node) {
17+
var page = input.resolveLink(node.uri);
18+
cursor.insert({ title: node.title, uri: "#page=" + (page + 1) });
19+
if (node.down) {
20+
cursor.prev();
21+
cursor.down();
22+
copy_outline_rec(cursor, input, node.down);
23+
cursor.up();
24+
cursor.next();
25+
}
26+
});
27+
}
28+
29+
function copy_outline(output, input, list) {
30+
var cursor = output.outlineIterator();
31+
while (cursor.item()) cursor.delete();
32+
copy_outline_rec(cursor, input, list);
33+
}
34+
35+
var input = Document.openDocument(scriptArgs[2]);
36+
var output = Document.openDocument(scriptArgs[0]);
37+
38+
copy_outline(output, input, input.loadOutline());
39+
40+
output.save(scriptArgs[1], "compress");

‎docs/scripts/create-a-pdf.js‎

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
// Info: Create a PDF with one blank page.
2+
// Arg: Unused (leave empty)
3+
// Arg: Output PDF
4+
5+
var doc = new PDFDocument();
6+
doc.insertPage(-1, doc.addPage([0, 0, 595, 842], 0, null, ""));
7+
doc.save(scriptArgs[1]);

0 commit comments

Comments
 (0)