@unisim/doc 0.6.0 → 0.7.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/convert.d.ts +2 -1
- package/dist/convert.d.ts.map +1 -1
- package/dist/convert.js +4 -3
- package/dist/convert.js.map +1 -1
- package/dist/index.d.ts +4 -2
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +3 -1
- package/dist/index.js.map +1 -1
- package/dist/pdfcore.d.ts +45 -3
- package/dist/pdfcore.d.ts.map +1 -1
- package/dist/pdfcore.js +300 -32
- package/dist/pdfcore.js.map +1 -1
- package/dist/truetype.d.ts +33 -0
- package/dist/truetype.d.ts.map +1 -0
- package/dist/truetype.js +289 -0
- package/dist/truetype.js.map +1 -0
- package/dist/write/pdf.d.ts +17 -1
- package/dist/write/pdf.d.ts.map +1 -1
- package/dist/write/pdf.js +72 -11
- package/dist/write/pdf.js.map +1 -1
- package/package.json +1 -1
package/dist/convert.d.ts
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import type { DocNotice, RichDoc } from './model.js';
|
|
2
2
|
import { type PdfSettings } from './write/pdfSettings.js';
|
|
3
|
+
import type { FallbackFontLoader } from './write/pdf.js';
|
|
3
4
|
export type { PdfSettings, PaperSize, Orientation, PageMargin, FontChoice } from './write/pdfSettings.js';
|
|
4
5
|
export { DEFAULT_PDF_SETTINGS } from './write/pdfSettings.js';
|
|
5
6
|
export type { DocNotice, RichDoc } from './model.js';
|
|
@@ -50,5 +51,5 @@ export declare function commonTargets(exts: readonly string[]): DocFormat[];
|
|
|
50
51
|
* and not a converted file, so that it can concatenate before writing once.
|
|
51
52
|
*/
|
|
52
53
|
export declare function readToDoc(file: File): Promise<RichDoc>;
|
|
53
|
-
export declare function convertDocument(file: File, settings: DocSettings, onProgress?: (fraction: number) => void): Promise<DocResult>;
|
|
54
|
+
export declare function convertDocument(file: File, settings: DocSettings, onProgress?: (fraction: number) => void, loadFallbackFont?: FallbackFontLoader): Promise<DocResult>;
|
|
54
55
|
//# sourceMappingURL=convert.d.ts.map
|
package/dist/convert.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"convert.d.ts","sourceRoot":"","sources":["../src/convert.ts"],"names":[],"mappings":"AA4BA,OAAO,KAAK,EAAE,SAAS,EAAQ,OAAO,EAAE,MAAM,YAAY,CAAA;AAI1D,OAAO,EAAwB,KAAK,WAAW,EAAE,MAAM,wBAAwB,CAAA;
|
|
1
|
+
{"version":3,"file":"convert.d.ts","sourceRoot":"","sources":["../src/convert.ts"],"names":[],"mappings":"AA4BA,OAAO,KAAK,EAAE,SAAS,EAAQ,OAAO,EAAE,MAAM,YAAY,CAAA;AAI1D,OAAO,EAAwB,KAAK,WAAW,EAAE,MAAM,wBAAwB,CAAA;AAI/E,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,gBAAgB,CAAA;AAExD,YAAY,EAAE,WAAW,EAAE,SAAS,EAAE,WAAW,EAAE,UAAU,EAAE,UAAU,EAAE,MAAM,wBAAwB,CAAA;AACzG,OAAO,EAAE,oBAAoB,EAAE,MAAM,wBAAwB,CAAA;AAC7D,YAAY,EAAE,SAAS,EAAE,OAAO,EAAE,MAAM,YAAY,CAAA;AAEpD,MAAM,MAAM,SAAS,GAAG,KAAK,GAAG,KAAK,GAAG,MAAM,GAAG,IAAI,GAAG,KAAK,GAAG,MAAM,CAAA;AAEtE,MAAM,WAAW,WAAW;IAC1B,MAAM,EAAE,SAAS,CAAA;IACjB,GAAG,EAAE,WAAW,CAAA;IAChB,8DAA8D;IAC9D,UAAU,EAAE,OAAO,CAAA;CACpB;AAED,eAAO,MAAM,oBAAoB,EAAE,WAIlC,CAAA;AAED,uEAAuE;AACvE;;;;;;;;GAQG;AACH,MAAM,WAAW,SAAS;IACxB,IAAI,EAAE,IAAI,CAAA;IACV,oFAAoF;IACpF,GAAG,EAAE,SAAS,CAAA;IACd,OAAO,EAAE,SAAS,EAAE,CAAA;IACpB,mDAAmD;IACnD,KAAK,CAAC,EAAE,MAAM,CAAA;CACf;AAID;;;;;;;GAOG;AACH,eAAO,MAAM,cAAc,qHAGjB,CAAA;AAIV,wBAAgB,SAAS,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAE9C;AAED,oDAAoD;AACpD,wBAAgB,UAAU,CAAC,GAAG,EAAE,MAAM,GAAG,SAAS,EAAE,CAKnD;AAED,wDAAwD;AACxD,wBAAgB,aAAa,CAAC,IAAI,EAAE,SAAS,MAAM,EAAE,GAAG,SAAS,EAAE,CAKlE;AAOD;;;;;GAKG;AACH,wBAAsB,SAAS,CAAC,IAAI,EAAE,IAAI,GAAG,OAAO,CAAC,OAAO,CAAC,CAE5D;AAsDD,wBAAsB,eAAe,CACnC,IAAI,EAAE,IAAI,EACV,QAAQ,EAAE,WAAW,EACrB,UAAU,CAAC,EAAE,CAAC,QAAQ,EAAE,MAAM,KAAK,IAAI,EACvC,gBAAgB,CAAC,EAAE,kBAAkB,GACpC,OAAO,CAAC,SAAS,CAAC,CAsEpB"}
|
package/dist/convert.js
CHANGED
|
@@ -128,7 +128,7 @@ async function read(file, ext) {
|
|
|
128
128
|
}
|
|
129
129
|
}
|
|
130
130
|
// ── The conversion ───────────────────────────────────────────────────────────
|
|
131
|
-
export async function convertDocument(file, settings, onProgress) {
|
|
131
|
+
export async function convertDocument(file, settings, onProgress, loadFallbackFont) {
|
|
132
132
|
const ext = extensionOf(file.name);
|
|
133
133
|
onProgress?.(0.05);
|
|
134
134
|
const { doc, grid } = await read(file, ext);
|
|
@@ -140,7 +140,7 @@ export async function convertDocument(file, settings, onProgress) {
|
|
|
140
140
|
switch (settings.format) {
|
|
141
141
|
case 'pdf': {
|
|
142
142
|
const { docToPdf } = await import('./write/pdf.js');
|
|
143
|
-
const result = await docToPdf(doc, settings.pdf);
|
|
143
|
+
const result = await docToPdf(doc, settings.pdf, loadFallbackFont);
|
|
144
144
|
if (result.dropped.length) {
|
|
145
145
|
// Named, not counted. "12 characters could not be written" sends
|
|
146
146
|
// somebody hunting; showing them the actual glyphs makes it obvious
|
|
@@ -150,7 +150,8 @@ export async function convertDocument(file, settings, onProgress) {
|
|
|
150
150
|
severity: 'loss',
|
|
151
151
|
message: `Some characters aren’t in the PDF’s built-in fonts and came out as “?”: ${sample}` +
|
|
152
152
|
(result.dropped.length > 12 ? ` and ${result.dropped.length - 12} more.` : '.') +
|
|
153
|
-
'
|
|
153
|
+
' That script isn’t in the fallback font this app embeds, which covers' +
|
|
154
|
+
' Latin, Cyrillic, Greek and Hebrew but not Chinese, Japanese, Korean or Arabic.',
|
|
154
155
|
});
|
|
155
156
|
}
|
|
156
157
|
onProgress?.(1);
|
package/dist/convert.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"convert.js","sourceRoot":"","sources":["../src/convert.ts"],"names":[],"mappings":"AAAA,8EAA8E;AAC9E,4EAA4E;AAC5E,EAAE;AACF,+EAA+E;AAC/E,+EAA+E;AAC/E,sCAAsC;AACtC,EAAE;AACF,6EAA6E;AAC7E,6EAA6E;AAC7E,8EAA8E;AAC9E,4EAA4E;AAC5E,4EAA4E;AAC5E,2EAA2E;AAC3E,2EAA2E;AAC3E,EAAE;AACF,gFAAgF;AAChF,4EAA4E;AAC5E,8EAA8E;AAC9E,8EAA8E;AAC9E,wEAAwE;AACxE,EAAE;AACF,iFAAiF;AACjF,kFAAkF;AAClF,8EAA8E;AAC9E,8EAA8E;AAC9E,yDAAyD;AACzD,8EAA8E;AAG9E,+EAA+E;AAC/E,+EAA+E;AAC/E,iEAAiE;AACjE,OAAO,EAAE,oBAAoB,EAAoB,MAAM,wBAAwB,CAAA;
|
|
1
|
+
{"version":3,"file":"convert.js","sourceRoot":"","sources":["../src/convert.ts"],"names":[],"mappings":"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"}
|
package/dist/index.d.ts
CHANGED
|
@@ -6,12 +6,14 @@ export { readDocx } from './read/docx.js';
|
|
|
6
6
|
export { readOdt } from './read/odt.js';
|
|
7
7
|
export { readMarkdown } from './read/markdown.js';
|
|
8
8
|
export { docToPdf } from './write/pdf.js';
|
|
9
|
-
export type { PdfResult } from './write/pdf.js';
|
|
9
|
+
export type { FallbackFontLoader, PdfResult } from './write/pdf.js';
|
|
10
10
|
export { docToHtml, docToMarkdown, docToText } from './write/text.js';
|
|
11
11
|
export { ZipArchive, unzipSupported } from './unzip.js';
|
|
12
12
|
export { mergeToPdf, readForMerge } from './merge.js';
|
|
13
13
|
export type { MergePart } from './merge.js';
|
|
14
|
-
export { PdfDocument, PdfPage, rgb, toWinAnsi, widthOfText } from './pdfcore.js';
|
|
14
|
+
export { PdfDocument, PdfPage, rgb, toWinAnsi, unspellable, widthOfText } from './pdfcore.js';
|
|
15
|
+
export { readTrueType, FontFormatError } from './truetype.js';
|
|
16
|
+
export type { EmbeddableFont } from './truetype.js';
|
|
15
17
|
export type { FontId, PdfImage, Rgb } from './pdfcore.js';
|
|
16
18
|
export { imageToJpeg, imagesToPdf } from './image.js';
|
|
17
19
|
export type { PdfPageSource } from './image.js';
|
package/dist/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AA2CA,YAAY,EACV,KAAK,EACL,SAAS,EACT,IAAI,EACJ,QAAQ,EACR,OAAO,EACP,GAAG,GACJ,MAAM,YAAY,CAAA;AAGnB,OAAO,EAAE,iBAAiB,EAAE,MAAM,YAAY,CAAA;AAG9C,OAAO,EACL,oBAAoB,EACpB,oBAAoB,EACpB,cAAc,EACd,aAAa,EACb,eAAe,EACf,SAAS,EACT,SAAS,EACT,UAAU,GACX,MAAM,cAAc,CAAA;AACrB,YAAY,EACV,SAAS,EACT,SAAS,EACT,WAAW,EACX,UAAU,EACV,WAAW,EACX,UAAU,EACV,SAAS,EACT,WAAW,GACZ,MAAM,cAAc,CAAA;AAOrB,OAAO,EAAE,QAAQ,EAAE,MAAM,gBAAgB,CAAA;AACzC,OAAO,EAAE,OAAO,EAAE,MAAM,eAAe,CAAA;AACvC,OAAO,EAAE,YAAY,EAAE,MAAM,oBAAoB,CAAA;AACjD,OAAO,EAAE,QAAQ,EAAE,MAAM,gBAAgB,CAAA;AACzC,YAAY,EAAE,SAAS,EAAE,MAAM,gBAAgB,CAAA;
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AA2CA,YAAY,EACV,KAAK,EACL,SAAS,EACT,IAAI,EACJ,QAAQ,EACR,OAAO,EACP,GAAG,GACJ,MAAM,YAAY,CAAA;AAGnB,OAAO,EAAE,iBAAiB,EAAE,MAAM,YAAY,CAAA;AAG9C,OAAO,EACL,oBAAoB,EACpB,oBAAoB,EACpB,cAAc,EACd,aAAa,EACb,eAAe,EACf,SAAS,EACT,SAAS,EACT,UAAU,GACX,MAAM,cAAc,CAAA;AACrB,YAAY,EACV,SAAS,EACT,SAAS,EACT,WAAW,EACX,UAAU,EACV,WAAW,EACX,UAAU,EACV,SAAS,EACT,WAAW,GACZ,MAAM,cAAc,CAAA;AAOrB,OAAO,EAAE,QAAQ,EAAE,MAAM,gBAAgB,CAAA;AACzC,OAAO,EAAE,OAAO,EAAE,MAAM,eAAe,CAAA;AACvC,OAAO,EAAE,YAAY,EAAE,MAAM,oBAAoB,CAAA;AACjD,OAAO,EAAE,QAAQ,EAAE,MAAM,gBAAgB,CAAA;AACzC,YAAY,EAAE,kBAAkB,EAAE,SAAS,EAAE,MAAM,gBAAgB,CAAA;AACnE,OAAO,EAAE,SAAS,EAAE,aAAa,EAAE,SAAS,EAAE,MAAM,iBAAiB,CAAA;AACrE,OAAO,EAAE,UAAU,EAAE,cAAc,EAAE,MAAM,YAAY,CAAA;AAGvD,OAAO,EAAE,UAAU,EAAE,YAAY,EAAE,MAAM,YAAY,CAAA;AACrD,YAAY,EAAE,SAAS,EAAE,MAAM,YAAY,CAAA;AAK3C,OAAO,EAAE,WAAW,EAAE,OAAO,EAAE,GAAG,EAAE,SAAS,EAAE,WAAW,EAAE,WAAW,EAAE,MAAM,cAAc,CAAA;AAG7F,OAAO,EAAE,YAAY,EAAE,eAAe,EAAE,MAAM,eAAe,CAAA;AAC7D,YAAY,EAAE,cAAc,EAAE,MAAM,eAAe,CAAA;AACnD,YAAY,EAAE,MAAM,EAAE,QAAQ,EAAE,GAAG,EAAE,MAAM,cAAc,CAAA;AACzD,OAAO,EAAE,WAAW,EAAE,WAAW,EAAE,MAAM,YAAY,CAAA;AACrD,YAAY,EAAE,aAAa,EAAE,MAAM,YAAY,CAAA;AAM/C,OAAO,EAAE,WAAW,EAAE,iBAAiB,EAAE,YAAY,EAAE,MAAM,aAAa,CAAA"}
|
package/dist/index.js
CHANGED
|
@@ -60,7 +60,9 @@ export { mergeToPdf, readForMerge } from './merge.js';
|
|
|
60
60
|
// ---- The PDF primitives, for a consumer that wants them directly ----------
|
|
61
61
|
// Universal Converter's "images → one PDF" feature builds pages itself rather
|
|
62
62
|
// than going through the document model, so the writer surface is public.
|
|
63
|
-
export { PdfDocument, PdfPage, rgb, toWinAnsi, widthOfText } from './pdfcore.js';
|
|
63
|
+
export { PdfDocument, PdfPage, rgb, toWinAnsi, unspellable, widthOfText } from './pdfcore.js';
|
|
64
|
+
// ---- Embedding a font, for the alphabets base-14 cannot spell --------------
|
|
65
|
+
export { readTrueType, FontFormatError } from './truetype.js';
|
|
64
66
|
export { imageToJpeg, imagesToPdf } from './image.js';
|
|
65
67
|
// ---- Source lengths → points ----------------------------------------------
|
|
66
68
|
// Exported so the self-tests can reach them: they are the one part of the
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,8EAA8E;AAC9E,4CAA4C;AAC5C,EAAE;AACF,0BAA0B;AAC1B,0BAA0B;AAC1B,8EAA8E;AAC9E,4EAA4E;AAC5E,uEAAuE;AACvE,wEAAwE;AACxE,sEAAsE;AACtE,+EAA+E;AAC/E,4EAA4E;AAC5E,YAAY;AACZ,EAAE;AACF,6EAA6E;AAC7E,gFAAgF;AAChF,gFAAgF;AAChF,sEAAsE;AACtE,+EAA+E;AAC/E,2EAA2E;AAC3E,gFAAgF;AAChF,sEAAsE;AACtE,EAAE;AACF,+EAA+E;AAC/E,8EAA8E;AAC9E,EAAE;AACF,uDAAuD;AACvD,uDAAuD;AACvD,6EAA6E;AAC7E,gFAAgF;AAChF,+DAA+D;AAC/D,+EAA+E;AAC/E,+EAA+E;AAC/E,+EAA+E;AAC/E,+EAA+E;AAC/E,6EAA6E;AAC7E,4DAA4D;AAC5D,EAAE;AACF,gFAAgF;AAChF,wCAAwC;AACxC,8EAA8E;AAW9E,8EAA8E;AAC9E,kEAAkE;AAClE,OAAO,EAAE,iBAAiB,EAAE,MAAM,YAAY,CAAA;AAE9C,+EAA+E;AAC/E,OAAO,EACL,oBAAoB,EACpB,oBAAoB,EACpB,cAAc,EACd,aAAa,EACb,eAAe,EACf,SAAS,EACT,SAAS,EACT,UAAU,GACX,MAAM,cAAc,CAAA;AAYrB,8EAA8E;AAC9E,gFAAgF;AAChF,8EAA8E;AAC9E,gFAAgF;AAChF,2DAA2D;AAC3D,OAAO,EAAE,QAAQ,EAAE,MAAM,gBAAgB,CAAA;AACzC,OAAO,EAAE,OAAO,EAAE,MAAM,eAAe,CAAA;AACvC,OAAO,EAAE,YAAY,EAAE,MAAM,oBAAoB,CAAA;AACjD,OAAO,EAAE,QAAQ,EAAE,MAAM,gBAAgB,CAAA;AAEzC,OAAO,EAAE,SAAS,EAAE,aAAa,EAAE,SAAS,EAAE,MAAM,iBAAiB,CAAA;AACrE,OAAO,EAAE,UAAU,EAAE,cAAc,EAAE,MAAM,YAAY,CAAA;AAEvD,8EAA8E;AAC9E,OAAO,EAAE,UAAU,EAAE,YAAY,EAAE,MAAM,YAAY,CAAA;AAGrD,8EAA8E;AAC9E,8EAA8E;AAC9E,0EAA0E;AAC1E,OAAO,EAAE,WAAW,EAAE,OAAO,EAAE,GAAG,EAAE,SAAS,EAAE,WAAW,EAAE,MAAM,cAAc,CAAA;
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,8EAA8E;AAC9E,4CAA4C;AAC5C,EAAE;AACF,0BAA0B;AAC1B,0BAA0B;AAC1B,8EAA8E;AAC9E,4EAA4E;AAC5E,uEAAuE;AACvE,wEAAwE;AACxE,sEAAsE;AACtE,+EAA+E;AAC/E,4EAA4E;AAC5E,YAAY;AACZ,EAAE;AACF,6EAA6E;AAC7E,gFAAgF;AAChF,gFAAgF;AAChF,sEAAsE;AACtE,+EAA+E;AAC/E,2EAA2E;AAC3E,gFAAgF;AAChF,sEAAsE;AACtE,EAAE;AACF,+EAA+E;AAC/E,8EAA8E;AAC9E,EAAE;AACF,uDAAuD;AACvD,uDAAuD;AACvD,6EAA6E;AAC7E,gFAAgF;AAChF,+DAA+D;AAC/D,+EAA+E;AAC/E,+EAA+E;AAC/E,+EAA+E;AAC/E,+EAA+E;AAC/E,6EAA6E;AAC7E,4DAA4D;AAC5D,EAAE;AACF,gFAAgF;AAChF,wCAAwC;AACxC,8EAA8E;AAW9E,8EAA8E;AAC9E,kEAAkE;AAClE,OAAO,EAAE,iBAAiB,EAAE,MAAM,YAAY,CAAA;AAE9C,+EAA+E;AAC/E,OAAO,EACL,oBAAoB,EACpB,oBAAoB,EACpB,cAAc,EACd,aAAa,EACb,eAAe,EACf,SAAS,EACT,SAAS,EACT,UAAU,GACX,MAAM,cAAc,CAAA;AAYrB,8EAA8E;AAC9E,gFAAgF;AAChF,8EAA8E;AAC9E,gFAAgF;AAChF,2DAA2D;AAC3D,OAAO,EAAE,QAAQ,EAAE,MAAM,gBAAgB,CAAA;AACzC,OAAO,EAAE,OAAO,EAAE,MAAM,eAAe,CAAA;AACvC,OAAO,EAAE,YAAY,EAAE,MAAM,oBAAoB,CAAA;AACjD,OAAO,EAAE,QAAQ,EAAE,MAAM,gBAAgB,CAAA;AAEzC,OAAO,EAAE,SAAS,EAAE,aAAa,EAAE,SAAS,EAAE,MAAM,iBAAiB,CAAA;AACrE,OAAO,EAAE,UAAU,EAAE,cAAc,EAAE,MAAM,YAAY,CAAA;AAEvD,8EAA8E;AAC9E,OAAO,EAAE,UAAU,EAAE,YAAY,EAAE,MAAM,YAAY,CAAA;AAGrD,8EAA8E;AAC9E,8EAA8E;AAC9E,0EAA0E;AAC1E,OAAO,EAAE,WAAW,EAAE,OAAO,EAAE,GAAG,EAAE,SAAS,EAAE,WAAW,EAAE,WAAW,EAAE,MAAM,cAAc,CAAA;AAE7F,+EAA+E;AAC/E,OAAO,EAAE,YAAY,EAAE,eAAe,EAAE,MAAM,eAAe,CAAA;AAG7D,OAAO,EAAE,WAAW,EAAE,WAAW,EAAE,MAAM,YAAY,CAAA;AAGrD,8EAA8E;AAC9E,0EAA0E;AAC1E,4EAA4E;AAC5E,sDAAsD;AACtD,OAAO,EAAE,WAAW,EAAE,iBAAiB,EAAE,YAAY,EAAE,MAAM,aAAa,CAAA"}
|
package/dist/pdfcore.d.ts
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { type FontId } from './metrics.js';
|
|
2
|
+
import type { EmbeddableFont } from './truetype.js';
|
|
2
3
|
export type { FontId };
|
|
3
4
|
export interface Rgb {
|
|
4
5
|
r: number;
|
|
@@ -16,6 +17,20 @@ export interface EncodeReport {
|
|
|
16
17
|
/** Distinct characters that could not be written at all. */
|
|
17
18
|
dropped: Set<string>;
|
|
18
19
|
}
|
|
20
|
+
/**
|
|
21
|
+
* The one embedded face, and the glyphs the document has actually asked it for.
|
|
22
|
+
*
|
|
23
|
+
* Shared by reference between the document and every page it opens, because the
|
|
24
|
+
* font objects are written ONCE at save time and have to know the full set —
|
|
25
|
+
* `/W` and `/ToUnicode` are per-document, and a page cannot know what page four
|
|
26
|
+
* used. The alternative, walking every page's ops back at save time, means
|
|
27
|
+
* parsing the content streams we just wrote.
|
|
28
|
+
*/
|
|
29
|
+
export interface EmbedState {
|
|
30
|
+
font?: EmbeddableFont;
|
|
31
|
+
/** Glyph id → the text it spells. Feeds `/W` and `/ToUnicode`. */
|
|
32
|
+
used: Map<number, string>;
|
|
33
|
+
}
|
|
19
34
|
/**
|
|
20
35
|
* A JS string as WinAnsi bytes.
|
|
21
36
|
*
|
|
@@ -24,8 +39,22 @@ export interface EncodeReport {
|
|
|
24
39
|
* it themselves halfway down page four.
|
|
25
40
|
*/
|
|
26
41
|
export declare function toWinAnsi(text: string, report?: EncodeReport): number[];
|
|
27
|
-
/**
|
|
28
|
-
|
|
42
|
+
/**
|
|
43
|
+
* The characters in `text` that WinAnsi cannot write.
|
|
44
|
+
*
|
|
45
|
+
* Empty means the base-14 fonts can spell the whole string, and no font needs
|
|
46
|
+
* to be fetched or embedded. This is what lets an English document pay nothing
|
|
47
|
+
* for a feature it does not use — the loader is never even called.
|
|
48
|
+
*/
|
|
49
|
+
export declare function unspellable(text: string): Set<string>;
|
|
50
|
+
/**
|
|
51
|
+
* The width of a string at a given size, in points.
|
|
52
|
+
*
|
|
53
|
+
* `embedded` must be the SAME face the page will draw with, or the line-breaker
|
|
54
|
+
* and the content stream disagree. Callers that never embed one pass nothing
|
|
55
|
+
* and get exactly the behaviour they had.
|
|
56
|
+
*/
|
|
57
|
+
export declare function widthOfText(font: FontId, text: string, size: number, embedded?: EmbeddableFont): number;
|
|
29
58
|
interface LinkAnnot {
|
|
30
59
|
rect: [number, number, number, number];
|
|
31
60
|
url: string;
|
|
@@ -46,8 +75,10 @@ export declare class PdfPage {
|
|
|
46
75
|
private links;
|
|
47
76
|
/** Fonts actually used, so a page's /Resources only names what it draws. */
|
|
48
77
|
private usedFonts;
|
|
78
|
+
private usesEmbedded;
|
|
49
79
|
private report?;
|
|
50
|
-
|
|
80
|
+
private embed?;
|
|
81
|
+
constructor(width: number, height: number, report?: EncodeReport, embed?: EmbedState);
|
|
51
82
|
/**
|
|
52
83
|
* Change this page's paper size. See the note on `width`/`height` above:
|
|
53
84
|
* valid only before anything is drawn.
|
|
@@ -92,6 +123,7 @@ export declare class PdfPage {
|
|
|
92
123
|
images: PdfImage[];
|
|
93
124
|
links: LinkAnnot[];
|
|
94
125
|
fonts: FontId[];
|
|
126
|
+
embedded: boolean;
|
|
95
127
|
};
|
|
96
128
|
}
|
|
97
129
|
export interface PdfMeta {
|
|
@@ -104,6 +136,16 @@ export declare class PdfDocument {
|
|
|
104
136
|
private pages;
|
|
105
137
|
/** Collected across every page, so the UI can report it once at the end. */
|
|
106
138
|
readonly report: EncodeReport;
|
|
139
|
+
private readonly embed;
|
|
140
|
+
/**
|
|
141
|
+
* Offer a face for everything WinAnsi cannot spell.
|
|
142
|
+
*
|
|
143
|
+
* Optional on purpose, and the whole document works without one — this is the
|
|
144
|
+
* difference between a Cyrillic paragraph and a row of '?', not between a PDF
|
|
145
|
+
* and no PDF. Must be set BEFORE anything is drawn, because a page splits its
|
|
146
|
+
* text between the two fonts as it draws.
|
|
147
|
+
*/
|
|
148
|
+
embedFallbackFont(font: EmbeddableFont): void;
|
|
107
149
|
addPage(width: number, height: number): PdfPage;
|
|
108
150
|
get pageCount(): number;
|
|
109
151
|
/**
|
package/dist/pdfcore.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"pdfcore.d.ts","sourceRoot":"","sources":["../src/pdfcore.ts"],"names":[],"mappings":"AAqCA,OAAO,EAAgB,KAAK,MAAM,EAAE,MAAM,cAAc,CAAA;
|
|
1
|
+
{"version":3,"file":"pdfcore.d.ts","sourceRoot":"","sources":["../src/pdfcore.ts"],"names":[],"mappings":"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"}
|
package/dist/pdfcore.js
CHANGED
|
@@ -152,45 +152,135 @@ function boxDrawing(code) {
|
|
|
152
152
|
*/
|
|
153
153
|
export function toWinAnsi(text, report) {
|
|
154
154
|
const out = [];
|
|
155
|
-
|
|
156
|
-
// combining acute, and decomposed, NEITHER half is WinAnsi: the accent has no
|
|
157
|
-
// code and the pair came out as 'e?'. Composed, it is one byte this encoder
|
|
158
|
-
// has had all along. One call, and every accented Latin document it was
|
|
159
|
-
// silently mangling comes out right.
|
|
160
|
-
for (const ch of text.normalize('NFC')) {
|
|
155
|
+
for (const ch of normalise(text)) {
|
|
161
156
|
if (IGNORABLE.has(ch))
|
|
162
157
|
continue;
|
|
163
|
-
const
|
|
164
|
-
if (
|
|
165
|
-
|
|
166
|
-
out.push(c.charCodeAt(0));
|
|
158
|
+
const bytes = winAnsiBytes(ch);
|
|
159
|
+
if (bytes) {
|
|
160
|
+
out.push(...bytes);
|
|
167
161
|
continue;
|
|
168
162
|
}
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
163
|
+
report?.dropped.add(ch);
|
|
164
|
+
out.push(0x3f); // '?'
|
|
165
|
+
}
|
|
166
|
+
return out;
|
|
167
|
+
}
|
|
168
|
+
/**
|
|
169
|
+
* NFC first, everywhere.
|
|
170
|
+
*
|
|
171
|
+
* A macOS-authored file routinely spells 'é' as a bare 'e' plus a combining
|
|
172
|
+
* acute, and decomposed, NEITHER half is WinAnsi: the accent has no code and
|
|
173
|
+
* the pair came out as 'e?'. Composed, it is one byte this encoder has had all
|
|
174
|
+
* along. ⚠️ Every path that measures or draws text has to call this, or the
|
|
175
|
+
* layout measures a string one way and writes it another.
|
|
176
|
+
*/
|
|
177
|
+
function normalise(text) {
|
|
178
|
+
return text.normalize('NFC');
|
|
179
|
+
}
|
|
180
|
+
/**
|
|
181
|
+
* One character's WinAnsi bytes, or null when the encoding has no answer for
|
|
182
|
+
* it. Null is the signal that an embedded font is the only way to write it.
|
|
183
|
+
*/
|
|
184
|
+
function winAnsiBytes(ch) {
|
|
185
|
+
const sub = SUBSTITUTES[ch] ?? boxDrawing(ch.codePointAt(0));
|
|
186
|
+
if (sub !== undefined) {
|
|
187
|
+
const out = [];
|
|
188
|
+
for (const c of sub)
|
|
189
|
+
out.push(c.charCodeAt(0));
|
|
190
|
+
return out;
|
|
191
|
+
}
|
|
192
|
+
const code = ch.codePointAt(0);
|
|
193
|
+
if (code === 9)
|
|
194
|
+
return [32]; // tab — the layout owns indentation
|
|
195
|
+
if ((code >= 32 && code <= 126) || (code >= 160 && code <= 255))
|
|
196
|
+
return [code];
|
|
197
|
+
const high = WIN_ANSI_HIGH[ch];
|
|
198
|
+
if (high !== undefined)
|
|
199
|
+
return [high];
|
|
200
|
+
return null;
|
|
201
|
+
}
|
|
202
|
+
function segment(text, embedded) {
|
|
203
|
+
const out = [];
|
|
204
|
+
const push = (glyphs) => {
|
|
205
|
+
const last = out[out.length - 1];
|
|
206
|
+
if (last && !!last.glyphs === glyphs)
|
|
207
|
+
return last;
|
|
208
|
+
const fresh = glyphs ? { bytes: [], glyphs: [], source: [] } : { bytes: [] };
|
|
209
|
+
out.push(fresh);
|
|
210
|
+
return fresh;
|
|
211
|
+
};
|
|
212
|
+
for (const ch of normalise(text)) {
|
|
213
|
+
if (IGNORABLE.has(ch))
|
|
172
214
|
continue;
|
|
173
|
-
|
|
174
|
-
if (
|
|
175
|
-
|
|
215
|
+
const bytes = winAnsiBytes(ch);
|
|
216
|
+
if (bytes) {
|
|
217
|
+
push(false).bytes.push(...bytes);
|
|
176
218
|
continue;
|
|
177
219
|
}
|
|
178
|
-
const
|
|
179
|
-
if (
|
|
180
|
-
|
|
220
|
+
const glyph = embedded?.glyphFor(ch.codePointAt(0));
|
|
221
|
+
if (glyph !== undefined && glyph > 0) {
|
|
222
|
+
const seg = push(true);
|
|
223
|
+
seg.glyphs.push(glyph);
|
|
224
|
+
seg.source.push(ch);
|
|
181
225
|
continue;
|
|
182
226
|
}
|
|
183
|
-
|
|
184
|
-
|
|
227
|
+
// No WinAnsi code and no glyph in the embedded face either — a '?' it is,
|
|
228
|
+
// and the report is what turns that into a sentence in the UI.
|
|
229
|
+
push(false).bytes.push(0x3f);
|
|
185
230
|
}
|
|
186
231
|
return out;
|
|
187
232
|
}
|
|
188
|
-
/**
|
|
189
|
-
|
|
233
|
+
/**
|
|
234
|
+
* The characters in `text` that WinAnsi cannot write.
|
|
235
|
+
*
|
|
236
|
+
* Empty means the base-14 fonts can spell the whole string, and no font needs
|
|
237
|
+
* to be fetched or embedded. This is what lets an English document pay nothing
|
|
238
|
+
* for a feature it does not use — the loader is never even called.
|
|
239
|
+
*/
|
|
240
|
+
export function unspellable(text) {
|
|
241
|
+
const out = new Set();
|
|
242
|
+
for (const ch of normalise(text)) {
|
|
243
|
+
if (IGNORABLE.has(ch))
|
|
244
|
+
continue;
|
|
245
|
+
if (!winAnsiBytes(ch))
|
|
246
|
+
out.add(ch);
|
|
247
|
+
}
|
|
248
|
+
return out;
|
|
249
|
+
}
|
|
250
|
+
/** What `segment` could not write at all, for the honest warning. */
|
|
251
|
+
function reportDropped(text, embedded, report) {
|
|
252
|
+
if (!report)
|
|
253
|
+
return;
|
|
254
|
+
for (const ch of normalise(text)) {
|
|
255
|
+
if (IGNORABLE.has(ch))
|
|
256
|
+
continue;
|
|
257
|
+
if (winAnsiBytes(ch))
|
|
258
|
+
continue;
|
|
259
|
+
const glyph = embedded?.glyphFor(ch.codePointAt(0));
|
|
260
|
+
if (glyph === undefined || glyph === 0)
|
|
261
|
+
report.dropped.add(ch);
|
|
262
|
+
}
|
|
263
|
+
}
|
|
264
|
+
/**
|
|
265
|
+
* The width of a string at a given size, in points.
|
|
266
|
+
*
|
|
267
|
+
* `embedded` must be the SAME face the page will draw with, or the line-breaker
|
|
268
|
+
* and the content stream disagree. Callers that never embed one pass nothing
|
|
269
|
+
* and get exactly the behaviour they had.
|
|
270
|
+
*/
|
|
271
|
+
export function widthOfText(font, text, size, embedded) {
|
|
190
272
|
const metrics = FONT_METRICS[font];
|
|
191
273
|
let units = 0;
|
|
192
|
-
for (const
|
|
193
|
-
|
|
274
|
+
for (const seg of segment(text, embedded)) {
|
|
275
|
+
if (seg.glyphs) {
|
|
276
|
+
for (const glyph of seg.glyphs)
|
|
277
|
+
units += embedded.advanceOf(glyph);
|
|
278
|
+
}
|
|
279
|
+
else {
|
|
280
|
+
for (const code of seg.bytes)
|
|
281
|
+
units += metrics.widths[code] ?? metrics.fallback;
|
|
282
|
+
}
|
|
283
|
+
}
|
|
194
284
|
return (units * size) / 1000;
|
|
195
285
|
}
|
|
196
286
|
// ── The document ─────────────────────────────────────────────────────────────
|
|
@@ -231,11 +321,14 @@ export class PdfPage {
|
|
|
231
321
|
links = [];
|
|
232
322
|
/** Fonts actually used, so a page's /Resources only names what it draws. */
|
|
233
323
|
usedFonts = new Set();
|
|
324
|
+
usesEmbedded = false;
|
|
234
325
|
report;
|
|
235
|
-
|
|
326
|
+
embed;
|
|
327
|
+
constructor(width, height, report, embed) {
|
|
236
328
|
this.width = width;
|
|
237
329
|
this.height = height;
|
|
238
330
|
this.report = report;
|
|
331
|
+
this.embed = embed;
|
|
239
332
|
}
|
|
240
333
|
/**
|
|
241
334
|
* Change this page's paper size. See the note on `width`/`height` above:
|
|
@@ -256,9 +349,57 @@ export class PdfPage {
|
|
|
256
349
|
drawText(text, opts) {
|
|
257
350
|
if (!text)
|
|
258
351
|
return;
|
|
259
|
-
this.
|
|
260
|
-
|
|
261
|
-
|
|
352
|
+
const embedded = this.embed?.font;
|
|
353
|
+
reportDropped(text, embedded, this.report);
|
|
354
|
+
const segments = segment(text, embedded);
|
|
355
|
+
// The overwhelmingly common case: one segment, one font, one `Tj`. Kept on
|
|
356
|
+
// its own path so an all-Latin document's content stream is byte-for-byte
|
|
357
|
+
// what it was before any of this existed.
|
|
358
|
+
if (segments.length === 1 && !segments[0].glyphs) {
|
|
359
|
+
this.usedFonts.add(opts.font);
|
|
360
|
+
this.ops.push('BT', `${num(opts.color.r)} ${num(opts.color.g)} ${num(opts.color.b)} rg`, `/${opts.font} ${num(opts.size)} Tf`, `${num(opts.x)} ${num(opts.y)} Td`, `${pdfStringFromBytes(segments[0].bytes)} Tj`, 'ET');
|
|
361
|
+
return;
|
|
362
|
+
}
|
|
363
|
+
// A mixed line — 'Reference: Приложение А'. Each stretch is drawn with its
|
|
364
|
+
// own font at its own x, advanced by the width of the one before it. This
|
|
365
|
+
// is why measuring and drawing must segment identically: the x values here
|
|
366
|
+
// are what the line-breaker was promised.
|
|
367
|
+
//
|
|
368
|
+
// ⚠️ `Tm`, NOT `Td`. `Td` is relative to the START OF THE CURRENT LINE, so
|
|
369
|
+
// a second one inside the same BT/ET lands at the SUM of the two offsets —
|
|
370
|
+
// the third segment of a line drew at 3y, off the top of the page. The
|
|
371
|
+
// single-segment path above keeps `Td` because there is exactly one of
|
|
372
|
+
// them and BT has just reset the matrix, which makes it absolute and keeps
|
|
373
|
+
// an all-Latin content stream byte-for-byte what it always was.
|
|
374
|
+
const metrics = FONT_METRICS[opts.font];
|
|
375
|
+
let x = opts.x;
|
|
376
|
+
this.ops.push('BT', `${num(opts.color.r)} ${num(opts.color.g)} ${num(opts.color.b)} rg`);
|
|
377
|
+
for (const seg of segments) {
|
|
378
|
+
let units = 0;
|
|
379
|
+
if (seg.glyphs) {
|
|
380
|
+
if (!seg.glyphs.length)
|
|
381
|
+
continue;
|
|
382
|
+
this.usesEmbedded = true;
|
|
383
|
+
for (let i = 0; i < seg.glyphs.length; i += 1) {
|
|
384
|
+
const glyph = seg.glyphs[i];
|
|
385
|
+
units += embedded.advanceOf(glyph);
|
|
386
|
+
// Last writer wins, and they agree: the same glyph reached by two
|
|
387
|
+
// code points is one entry either way.
|
|
388
|
+
this.embed.used.set(glyph, seg.source[i]);
|
|
389
|
+
}
|
|
390
|
+
this.ops.push(`/${EMBEDDED_FONT_KEY} ${num(opts.size)} Tf`, `1 0 0 1 ${num(x)} ${num(opts.y)} Tm`, `${hexStringFromGlyphs(seg.glyphs)} Tj`);
|
|
391
|
+
}
|
|
392
|
+
else {
|
|
393
|
+
if (!seg.bytes.length)
|
|
394
|
+
continue;
|
|
395
|
+
this.usedFonts.add(opts.font);
|
|
396
|
+
for (const code of seg.bytes)
|
|
397
|
+
units += metrics.widths[code] ?? metrics.fallback;
|
|
398
|
+
this.ops.push(`/${opts.font} ${num(opts.size)} Tf`, `1 0 0 1 ${num(x)} ${num(opts.y)} Tm`, `${pdfStringFromBytes(seg.bytes)} Tj`);
|
|
399
|
+
}
|
|
400
|
+
x += (units * opts.size) / 1000;
|
|
401
|
+
}
|
|
402
|
+
this.ops.push('ET');
|
|
262
403
|
}
|
|
263
404
|
drawRect(opts) {
|
|
264
405
|
if (opts.width <= 0 || opts.height <= 0)
|
|
@@ -291,15 +432,30 @@ export class PdfPage {
|
|
|
291
432
|
images: this.images,
|
|
292
433
|
links: this.links,
|
|
293
434
|
fonts: [...this.usedFonts],
|
|
435
|
+
embedded: this.usesEmbedded,
|
|
294
436
|
};
|
|
295
437
|
}
|
|
296
438
|
}
|
|
439
|
+
/** The resource name the embedded face is drawn under. */
|
|
440
|
+
const EMBEDDED_FONT_KEY = 'fb';
|
|
297
441
|
export class PdfDocument {
|
|
298
442
|
pages = [];
|
|
299
443
|
/** Collected across every page, so the UI can report it once at the end. */
|
|
300
444
|
report = { dropped: new Set() };
|
|
445
|
+
embed = { used: new Map() };
|
|
446
|
+
/**
|
|
447
|
+
* Offer a face for everything WinAnsi cannot spell.
|
|
448
|
+
*
|
|
449
|
+
* Optional on purpose, and the whole document works without one — this is the
|
|
450
|
+
* difference between a Cyrillic paragraph and a row of '?', not between a PDF
|
|
451
|
+
* and no PDF. Must be set BEFORE anything is drawn, because a page splits its
|
|
452
|
+
* text between the two fonts as it draws.
|
|
453
|
+
*/
|
|
454
|
+
embedFallbackFont(font) {
|
|
455
|
+
this.embed.font = font;
|
|
456
|
+
}
|
|
301
457
|
addPage(width, height) {
|
|
302
|
-
const page = new PdfPage(width, height, this.report);
|
|
458
|
+
const page = new PdfPage(width, height, this.report, this.embed);
|
|
303
459
|
this.pages.push(page);
|
|
304
460
|
return page;
|
|
305
461
|
}
|
|
@@ -349,9 +505,68 @@ export class PdfDocument {
|
|
|
349
505
|
`/Encoding /WinAnsiEncoding >>\n`);
|
|
350
506
|
end();
|
|
351
507
|
}
|
|
508
|
+
// The embedded face, written only when a page actually drew with it. An
|
|
509
|
+
// English document that happened to be converted by an app that offers a
|
|
510
|
+
// font still carries no font: five objects and 350 KB for nothing is not a
|
|
511
|
+
// cost to pay on the ordinary case.
|
|
512
|
+
const builtPages = this.pages.map((page) => page.build());
|
|
513
|
+
const embeddedFont = this.embed.font;
|
|
514
|
+
let embeddedId = null;
|
|
515
|
+
if (embeddedFont && builtPages.some((b) => b.embedded)) {
|
|
516
|
+
const fileId = alloc();
|
|
517
|
+
const descriptorId = alloc();
|
|
518
|
+
const descendantId = alloc();
|
|
519
|
+
const toUnicodeId = alloc();
|
|
520
|
+
embeddedId = alloc();
|
|
521
|
+
// ⚠️ `/Length1` is not decoration. For an uncompressed `/FontFile2` it is
|
|
522
|
+
// the same number as `/Length`, but it is REQUIRED, and a reader that
|
|
523
|
+
// finds it missing is entitled to reject the font — which shows up as a
|
|
524
|
+
// document whose Cyrillic is invisible rather than as an error.
|
|
525
|
+
begin(fileId);
|
|
526
|
+
push(`<< /Length ${embeddedFont.bytes.length} /Length1 ${embeddedFont.bytes.length} >>\nstream\n`);
|
|
527
|
+
push(embeddedFont.bytes);
|
|
528
|
+
push('\nendstream\n');
|
|
529
|
+
end();
|
|
530
|
+
begin(descriptorId);
|
|
531
|
+
push(`<< /Type /FontDescriptor /FontName /${embeddedFont.postScriptName} ` +
|
|
532
|
+
`/Flags ${embeddedFont.flags} ` +
|
|
533
|
+
`/FontBBox [${embeddedFont.bbox.map(num).join(' ')}] ` +
|
|
534
|
+
`/ItalicAngle ${num(embeddedFont.italicAngle)} ` +
|
|
535
|
+
`/Ascent ${num(embeddedFont.ascent)} /Descent ${num(embeddedFont.descent)} ` +
|
|
536
|
+
`/CapHeight ${num(embeddedFont.capHeight)} /StemV 80 ` +
|
|
537
|
+
`/FontFile2 ${fileId} 0 R >>\n`);
|
|
538
|
+
end();
|
|
539
|
+
// Only the glyphs this document used get a width. `/DW 1000` covers the
|
|
540
|
+
// rest, and the rest are never drawn.
|
|
541
|
+
const used = [...this.embed.used.keys()].sort((a, b) => a - b);
|
|
542
|
+
const widths = used
|
|
543
|
+
.map((glyph) => `${glyph} [${num(embeddedFont.advanceOf(glyph))}]`)
|
|
544
|
+
.join(' ');
|
|
545
|
+
begin(descendantId);
|
|
546
|
+
push(`<< /Type /Font /Subtype /CIDFontType2 /BaseFont /${embeddedFont.postScriptName} ` +
|
|
547
|
+
`/CIDSystemInfo << /Registry (Adobe) /Ordering (Identity) /Supplement 0 >> ` +
|
|
548
|
+
`/FontDescriptor ${descriptorId} 0 R /DW 1000` +
|
|
549
|
+
(widths ? ` /W [${widths}]` : '') +
|
|
550
|
+
// CIDs ARE glyph indices under Identity-H, which is the whole reason
|
|
551
|
+
// this needs no subsetter and no glyph-name table.
|
|
552
|
+
` /CIDToGIDMap /Identity >>\n`);
|
|
553
|
+
end();
|
|
554
|
+
const cmap = latin1(toUnicodeCMap(this.embed.used));
|
|
555
|
+
begin(toUnicodeId);
|
|
556
|
+
push(`<< /Length ${cmap.length} >>\nstream\n`);
|
|
557
|
+
push(cmap);
|
|
558
|
+
push('\nendstream\n');
|
|
559
|
+
end();
|
|
560
|
+
begin(embeddedId);
|
|
561
|
+
push(`<< /Type /Font /Subtype /Type0 /BaseFont /${embeddedFont.postScriptName} ` +
|
|
562
|
+
`/Encoding /Identity-H /DescendantFonts [${descendantId} 0 R] ` +
|
|
563
|
+
`/ToUnicode ${toUnicodeId} 0 R >>\n`);
|
|
564
|
+
end();
|
|
565
|
+
}
|
|
352
566
|
const pageIds = [];
|
|
353
|
-
for (
|
|
354
|
-
const built =
|
|
567
|
+
for (let index = 0; index < builtPages.length; index += 1) {
|
|
568
|
+
const built = builtPages[index];
|
|
569
|
+
const page = this.pages[index];
|
|
355
570
|
const pageId = alloc();
|
|
356
571
|
pageIds.push(pageId);
|
|
357
572
|
const contentId = alloc();
|
|
@@ -359,6 +574,7 @@ export class PdfDocument {
|
|
|
359
574
|
const linkIds = built.links.map(() => alloc());
|
|
360
575
|
const fontResources = built.fonts
|
|
361
576
|
.map((f) => `/${f} ${fontIds.get(f)} 0 R`)
|
|
577
|
+
.concat(built.embedded && embeddedId !== null ? [`/${EMBEDDED_FONT_KEY} ${embeddedId} 0 R`] : [])
|
|
362
578
|
.join(' ');
|
|
363
579
|
const xobjectResources = imageIds
|
|
364
580
|
.map((id, i) => `/Im${i} ${id} 0 R`)
|
|
@@ -432,6 +648,58 @@ function num(value) {
|
|
|
432
648
|
const fixed = value.toFixed(2);
|
|
433
649
|
return fixed.replace(/\.?0+$/, '') || '0';
|
|
434
650
|
}
|
|
651
|
+
/**
|
|
652
|
+
* Glyph indices as a hex string — how Identity-H text is written.
|
|
653
|
+
*
|
|
654
|
+
* Two bytes per glyph, big-endian. A literal `(...)` string cannot be used
|
|
655
|
+
* here: half the glyph ids in a real font contain a byte that would have to be
|
|
656
|
+
* escaped, and one missed escape shifts every glyph after it by a byte and
|
|
657
|
+
* turns the rest of the line into someone else's alphabet.
|
|
658
|
+
*/
|
|
659
|
+
function hexStringFromGlyphs(glyphs) {
|
|
660
|
+
let out = '<';
|
|
661
|
+
for (const glyph of glyphs)
|
|
662
|
+
out += (glyph & 0xffff).toString(16).padStart(4, '0');
|
|
663
|
+
return out + '>';
|
|
664
|
+
}
|
|
665
|
+
/**
|
|
666
|
+
* The `/ToUnicode` CMap — what makes embedded text selectable and searchable.
|
|
667
|
+
*
|
|
668
|
+
* Without it a reader knows which glyph to DRAW and nothing about what it says,
|
|
669
|
+
* so copying a paragraph out of the PDF yields mojibake and Cmd-F finds
|
|
670
|
+
* nothing. The base-14 path gets this free from `/WinAnsiEncoding`; Identity-H
|
|
671
|
+
* gets it only because this is here.
|
|
672
|
+
*
|
|
673
|
+
* ⚠️ `beginbfchar` takes at most 100 entries per block — a hard limit in the
|
|
674
|
+
* spec, not a convention — so the list is chunked. A single 300-entry block is
|
|
675
|
+
* the classic way this comes out looking right in one reader and empty in
|
|
676
|
+
* another.
|
|
677
|
+
*/
|
|
678
|
+
function toUnicodeCMap(used) {
|
|
679
|
+
const entries = [...used.entries()].sort((a, b) => a[0] - b[0]);
|
|
680
|
+
const head = '/CIDInit /ProcSet findresource begin\n12 dict begin\nbegincmap\n' +
|
|
681
|
+
'/CIDSystemInfo << /Registry (Adobe) /Ordering (UCS) /Supplement 0 >> def\n' +
|
|
682
|
+
'/CMapName /Adobe-Identity-UCS def\n/CMapType 2 def\n' +
|
|
683
|
+
'1 begincodespacerange\n<0000> <FFFF>\nendcodespacerange\n';
|
|
684
|
+
let body = '';
|
|
685
|
+
for (let i = 0; i < entries.length; i += 100) {
|
|
686
|
+
const chunk = entries.slice(i, i + 100);
|
|
687
|
+
body += `${chunk.length} beginbfchar\n`;
|
|
688
|
+
for (const [glyph, text] of chunk) {
|
|
689
|
+
body += `<${(glyph & 0xffff).toString(16).padStart(4, '0')}> <${utf16BeHex(text)}>\n`;
|
|
690
|
+
}
|
|
691
|
+
body += 'endbfchar\n';
|
|
692
|
+
}
|
|
693
|
+
return head + body + 'endcmap\nCMapName currentdict /CMap defineresource pop\nend\nend\n';
|
|
694
|
+
}
|
|
695
|
+
/** UTF-16BE hex, which is the only encoding a bfchar destination may use. */
|
|
696
|
+
function utf16BeHex(text) {
|
|
697
|
+
let out = '';
|
|
698
|
+
for (let i = 0; i < text.length; i += 1) {
|
|
699
|
+
out += text.charCodeAt(i).toString(16).padStart(4, '0');
|
|
700
|
+
}
|
|
701
|
+
return out;
|
|
702
|
+
}
|
|
435
703
|
/** A PDF literal string from already-encoded WinAnsi bytes. */
|
|
436
704
|
function pdfStringFromBytes(bytes) {
|
|
437
705
|
let out = '(';
|