pdf-codec 1.4.2 → 1.6.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/README.md +56 -15
- package/dist/assets/caladea-bold.cjs +5 -0
- package/dist/assets/caladea-bold.d.cts +4 -0
- package/dist/assets/caladea-bold.d.ts +4 -0
- package/dist/assets/caladea-bold.js +4 -0
- package/dist/assets/caladea-bolditalic.cjs +5 -0
- package/dist/assets/caladea-bolditalic.d.cts +4 -0
- package/dist/assets/caladea-bolditalic.d.ts +4 -0
- package/dist/assets/caladea-bolditalic.js +4 -0
- package/dist/assets/caladea-italic.cjs +5 -0
- package/dist/assets/caladea-italic.d.cts +4 -0
- package/dist/assets/caladea-italic.d.ts +4 -0
- package/dist/assets/caladea-italic.js +4 -0
- package/dist/assets/caladea-regular.cjs +5 -0
- package/dist/assets/caladea-regular.d.cts +4 -0
- package/dist/assets/caladea-regular.d.ts +4 -0
- package/dist/assets/caladea-regular.js +4 -0
- package/dist/assets/carlito-bold.cjs +5 -0
- package/dist/assets/carlito-bold.d.cts +4 -0
- package/dist/assets/carlito-bold.d.ts +4 -0
- package/dist/assets/carlito-bold.js +4 -0
- package/dist/assets/carlito-bolditalic.cjs +5 -0
- package/dist/assets/carlito-bolditalic.d.cts +4 -0
- package/dist/assets/carlito-bolditalic.d.ts +4 -0
- package/dist/assets/carlito-bolditalic.js +4 -0
- package/dist/assets/carlito-italic.cjs +5 -0
- package/dist/assets/carlito-italic.d.cts +4 -0
- package/dist/assets/carlito-italic.d.ts +4 -0
- package/dist/assets/carlito-italic.js +4 -0
- package/dist/assets/carlito-regular.cjs +5 -0
- package/dist/assets/carlito-regular.d.cts +4 -0
- package/dist/assets/carlito-regular.d.ts +4 -0
- package/dist/assets/carlito-regular.js +4 -0
- package/dist/cff-probe.cjs +133 -0
- package/dist/cff-probe.d.cts +10 -0
- package/dist/cff-probe.d.ts +10 -0
- package/dist/cff-probe.js +132 -0
- package/dist/cmap-table.cjs +108 -0
- package/dist/cmap-table.d.cts +6 -0
- package/dist/cmap-table.d.ts +6 -0
- package/dist/cmap-table.js +107 -0
- package/dist/content-write.cjs +36 -17
- package/dist/content-write.d.cts +9 -2
- package/dist/content-write.d.ts +9 -2
- package/dist/content-write.js +35 -16
- package/dist/crypto/aes.cjs +166 -0
- package/dist/crypto/aes.d.cts +6 -0
- package/dist/crypto/aes.d.ts +6 -0
- package/dist/crypto/aes.js +163 -0
- package/dist/crypto/md5.cjs +215 -0
- package/dist/crypto/md5.d.cts +4 -0
- package/dist/crypto/md5.d.ts +4 -0
- package/dist/crypto/md5.js +214 -0
- package/dist/crypto/rc4.cjs +29 -0
- package/dist/crypto/rc4.d.cts +4 -0
- package/dist/crypto/rc4.d.ts +4 -0
- package/dist/crypto/rc4.js +28 -0
- package/dist/crypto/sha2.cjs +261 -0
- package/dist/crypto/sha2.d.cts +6 -0
- package/dist/crypto/sha2.d.ts +6 -0
- package/dist/crypto/sha2.js +258 -0
- package/dist/diagnostics.cjs +7 -0
- package/dist/diagnostics.d.cts +4 -1
- package/dist/diagnostics.d.ts +4 -1
- package/dist/diagnostics.js +7 -1
- package/dist/document.cjs +38 -2
- package/dist/document.js +38 -2
- package/dist/embedded-font-CLedn-mT.d.cts +42 -0
- package/dist/embedded-font-CbqkKgBl.d.ts +42 -0
- package/dist/embedded-font-write.cjs +92 -0
- package/dist/embedded-font-write.d.cts +22 -0
- package/dist/embedded-font-write.d.ts +22 -0
- package/dist/embedded-font-write.js +90 -0
- package/dist/embedded-font.cjs +142 -0
- package/dist/embedded-font.d.cts +2 -0
- package/dist/embedded-font.d.ts +2 -0
- package/dist/embedded-font.js +138 -0
- package/dist/encrypt.cjs +264 -0
- package/dist/encrypt.d.cts +11 -0
- package/dist/encrypt.d.ts +11 -0
- package/dist/encrypt.js +263 -0
- package/dist/filters.cjs +19 -1
- package/dist/filters.js +19 -1
- package/dist/font-registry-Cn7P9lAh.d.ts +38 -0
- package/dist/font-registry-yby02snZ.d.cts +38 -0
- package/dist/font-registry.cjs +152 -0
- package/dist/font-registry.d.cts +2 -0
- package/dist/font-registry.d.ts +2 -0
- package/dist/font-registry.js +150 -0
- package/dist/font-substitutes.cjs +13 -0
- package/dist/font-substitutes.d.cts +5 -0
- package/dist/font-substitutes.d.ts +5 -0
- package/dist/font-substitutes.js +12 -0
- package/dist/font-tables.cjs +127 -0
- package/dist/font-tables.d.cts +43 -0
- package/dist/font-tables.d.ts +43 -0
- package/dist/font-tables.js +122 -0
- package/dist/fonts.cjs +1 -0
- package/dist/fonts.d.cts +2 -1
- package/dist/fonts.d.ts +2 -1
- package/dist/fonts.js +1 -1
- package/dist/glyf.cjs +127 -0
- package/dist/glyf.d.cts +31 -0
- package/dist/glyf.d.ts +31 -0
- package/dist/glyf.js +125 -0
- package/dist/{math-hmtx.cjs → hmtx-table.cjs} +3 -3
- package/dist/{math-hmtx.d.ts → hmtx-table.d.cts} +2 -2
- package/dist/{math-hmtx.d.cts → hmtx-table.d.ts} +2 -2
- package/dist/{math-hmtx.js → hmtx-table.js} +3 -3
- package/dist/image/ccitt.cjs +488 -0
- package/dist/image/ccitt.d.cts +17 -0
- package/dist/image/ccitt.d.ts +17 -0
- package/dist/image/ccitt.js +487 -0
- package/dist/images-read.cjs +1 -4
- package/dist/images-read.js +2 -5
- package/dist/index.cjs +8 -0
- package/dist/index.d.cts +7 -4
- package/dist/index.d.ts +7 -4
- package/dist/index.js +5 -3
- package/dist/interpret.cjs +173 -38
- package/dist/interpret.d.cts +27 -10
- package/dist/interpret.d.ts +27 -10
- package/dist/interpret.js +174 -39
- package/dist/math-font-write.cjs +3 -32
- package/dist/math-font-write.js +2 -31
- package/dist/math-font.cjs +7 -5
- package/dist/math-font.js +5 -3
- package/dist/math-table.d.cts +1 -1
- package/dist/math-table.d.ts +1 -1
- package/dist/{matrix-B3c2_a2f.d.cts → matrix-BHzJESll.d.cts} +2 -1
- package/dist/{matrix-B3c2_a2f.d.ts → matrix-BHzJESll.d.ts} +2 -1
- package/dist/matrix.cjs +2 -0
- package/dist/matrix.d.cts +2 -2
- package/dist/matrix.d.ts +2 -2
- package/dist/matrix.js +2 -1
- package/dist/measure.cjs +65 -14
- package/dist/measure.d.cts +8 -1
- package/dist/measure.d.ts +8 -1
- package/dist/measure.js +64 -15
- package/dist/objects.cjs +4 -0
- package/dist/objects.d.cts +2 -1
- package/dist/objects.d.ts +2 -1
- package/dist/objects.js +4 -1
- package/dist/read.cjs +43 -4
- package/dist/read.d.cts +1 -1
- package/dist/read.d.ts +1 -1
- package/dist/read.js +43 -4
- package/dist/sfnt-V08bb5ut.d.cts +21 -0
- package/dist/sfnt-V08bb5ut.d.ts +21 -0
- package/dist/sfnt-subset.cjs +250 -0
- package/dist/sfnt-subset.d.cts +11 -0
- package/dist/sfnt-subset.d.ts +11 -0
- package/dist/sfnt-subset.js +249 -0
- package/dist/sfnt.cjs +50 -4
- package/dist/sfnt.d.cts +2 -17
- package/dist/sfnt.d.ts +2 -17
- package/dist/sfnt.js +47 -5
- package/dist/tounicode.cjs +34 -0
- package/dist/tounicode.d.cts +5 -0
- package/dist/tounicode.d.ts +5 -0
- package/dist/tounicode.js +33 -0
- package/dist/write.cjs +81 -11
- package/dist/write.d.cts +6 -0
- package/dist/write.d.ts +6 -0
- package/dist/write.js +82 -12
- package/package.json +1 -1
- package/dist/math-cmap.cjs +0 -81
- package/dist/math-cmap.d.cts +0 -6
- package/dist/math-cmap.d.ts +0 -6
- package/dist/math-cmap.js +0 -80
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
import { crc32 } from "./bytes/crc32.js";
|
|
2
|
+
import { pdfArray, pdfDict, pdfHexString, pdfName, pdfNum, pdfStream } from "./objects.js";
|
|
3
|
+
import { deflate } from "./bytes/flate.js";
|
|
4
|
+
import { buildToUnicodeCMap } from "./tounicode.js";
|
|
5
|
+
//#region src/embedded-font-write.ts
|
|
6
|
+
const FLAG_SERIF = 2;
|
|
7
|
+
const FLAG_NONSYMBOLIC = 32;
|
|
8
|
+
const FLAG_ITALIC = 64;
|
|
9
|
+
const NOMINAL_STEM_V = 80;
|
|
10
|
+
const DEFAULT_WIDTH = 1e3;
|
|
11
|
+
const SUBSET_TAG_LENGTH = 6;
|
|
12
|
+
const SUBSET_TAG_ALPHABET_SIZE = 26;
|
|
13
|
+
const SUBSET_TAG_CODE_SPACE = SUBSET_TAG_ALPHABET_SIZE ** SUBSET_TAG_LENGTH;
|
|
14
|
+
const UPPERCASE_A_CHAR_CODE = 65;
|
|
15
|
+
function embeddedSubsetTag(postScriptName, glyphIds) {
|
|
16
|
+
let value = crc32(new TextEncoder().encode(`${postScriptName} ${glyphIds.join(",")}`)) % SUBSET_TAG_CODE_SPACE;
|
|
17
|
+
let tag = "";
|
|
18
|
+
for (let i = 0; i < SUBSET_TAG_LENGTH; i++) {
|
|
19
|
+
tag = String.fromCharCode(UPPERCASE_A_CHAR_CODE + value % SUBSET_TAG_ALPHABET_SIZE) + tag;
|
|
20
|
+
value = Math.floor(value / SUBSET_TAG_ALPHABET_SIZE);
|
|
21
|
+
}
|
|
22
|
+
return tag;
|
|
23
|
+
}
|
|
24
|
+
function computeFlags(face) {
|
|
25
|
+
let flags = FLAG_NONSYMBOLIC;
|
|
26
|
+
if (face.metrics.serif) flags |= FLAG_SERIF;
|
|
27
|
+
if (face.metrics.italicAngleDegrees !== 0) flags |= FLAG_ITALIC;
|
|
28
|
+
return flags;
|
|
29
|
+
}
|
|
30
|
+
function buildWidthsArray(face, glyphIds) {
|
|
31
|
+
const entries = [];
|
|
32
|
+
for (const glyphId of glyphIds) entries.push(pdfNum(glyphId), pdfArray([pdfNum(face.glyphSpaceWidth(glyphId))]));
|
|
33
|
+
return pdfArray(entries);
|
|
34
|
+
}
|
|
35
|
+
function buildFontDescriptor(face, baseFont, fontFileRef) {
|
|
36
|
+
const m = face.metrics;
|
|
37
|
+
const entries = /* @__PURE__ */ new Map([
|
|
38
|
+
["Type", pdfName("FontDescriptor")],
|
|
39
|
+
["FontName", pdfName(baseFont)],
|
|
40
|
+
["Flags", pdfNum(computeFlags(face))],
|
|
41
|
+
["FontBBox", pdfArray(m.bboxGlyphSpace.map((n) => pdfNum(n)))],
|
|
42
|
+
["ItalicAngle", pdfNum(m.italicAngleDegrees)],
|
|
43
|
+
["Ascent", pdfNum(m.ascentGlyphSpace)],
|
|
44
|
+
["Descent", pdfNum(m.descentGlyphSpace)],
|
|
45
|
+
["CapHeight", pdfNum(m.capHeightGlyphSpace)],
|
|
46
|
+
["StemV", pdfNum(NOMINAL_STEM_V)]
|
|
47
|
+
]);
|
|
48
|
+
if (m.xHeightGlyphSpace !== void 0) entries.set("XHeight", pdfNum(m.xHeightGlyphSpace));
|
|
49
|
+
entries.set("FontFile2", fontFileRef);
|
|
50
|
+
return pdfDict(entries);
|
|
51
|
+
}
|
|
52
|
+
function buildFontFileStream(subsetBytes, compress) {
|
|
53
|
+
const entries = /* @__PURE__ */ new Map([["Length1", pdfNum(subsetBytes.length)]]);
|
|
54
|
+
if (compress) entries.set("Filter", pdfName("FlateDecode"));
|
|
55
|
+
return pdfStream(pdfDict(entries), compress ? deflate(subsetBytes) : subsetBytes);
|
|
56
|
+
}
|
|
57
|
+
function buildEmbeddedFontObjects(face, subset, usedGlyphs, refs, compress) {
|
|
58
|
+
const baseFont = `${embeddedSubsetTag(face.postScriptName, subset.glyphIds)}+${face.postScriptName}`;
|
|
59
|
+
const cidFont = pdfDict({
|
|
60
|
+
Type: pdfName("Font"),
|
|
61
|
+
Subtype: pdfName("CIDFontType2"),
|
|
62
|
+
BaseFont: pdfName(baseFont),
|
|
63
|
+
CIDSystemInfo: pdfDict({
|
|
64
|
+
Registry: pdfHexString(new TextEncoder().encode("Adobe")),
|
|
65
|
+
Ordering: pdfHexString(new TextEncoder().encode("Identity")),
|
|
66
|
+
Supplement: pdfNum(0)
|
|
67
|
+
}),
|
|
68
|
+
FontDescriptor: refs.descriptorRef,
|
|
69
|
+
DW: pdfNum(DEFAULT_WIDTH),
|
|
70
|
+
W: buildWidthsArray(face, subset.glyphIds),
|
|
71
|
+
CIDToGIDMap: pdfName("Identity")
|
|
72
|
+
});
|
|
73
|
+
return {
|
|
74
|
+
baseFont,
|
|
75
|
+
type0: pdfDict({
|
|
76
|
+
Type: pdfName("Font"),
|
|
77
|
+
Subtype: pdfName("Type0"),
|
|
78
|
+
BaseFont: pdfName(baseFont),
|
|
79
|
+
Encoding: pdfName("Identity-H"),
|
|
80
|
+
DescendantFonts: pdfArray([refs.cidFontRef]),
|
|
81
|
+
ToUnicode: refs.toUnicodeRef
|
|
82
|
+
}),
|
|
83
|
+
cidFont,
|
|
84
|
+
descriptor: buildFontDescriptor(face, baseFont, refs.fontFileRef),
|
|
85
|
+
fontFile: buildFontFileStream(subset.bytes, compress),
|
|
86
|
+
toUnicode: buildToUnicodeCMap(usedGlyphs)
|
|
87
|
+
};
|
|
88
|
+
}
|
|
89
|
+
//#endregion
|
|
90
|
+
export { buildEmbeddedFontObjects, embeddedSubsetTag };
|
|
@@ -0,0 +1,142 @@
|
|
|
1
|
+
Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
|
|
2
|
+
const require_sfnt = require("./sfnt.cjs");
|
|
3
|
+
const require_cmap_table = require("./cmap-table.cjs");
|
|
4
|
+
const require_font_tables = require("./font-tables.cjs");
|
|
5
|
+
const require_glyf = require("./glyf.cjs");
|
|
6
|
+
const require_hmtx_table = require("./hmtx-table.cjs");
|
|
7
|
+
//#region src/embedded-font.ts
|
|
8
|
+
const GLYPH_SPACE_UNITS_PER_EM = 1e3;
|
|
9
|
+
const NOTDEF_GLYPH_ID = 0;
|
|
10
|
+
const CAP_HEIGHT_REFERENCE_CODE_POINT = 72;
|
|
11
|
+
const PANOSE_FAMILY_KIND_OFFSET = 0;
|
|
12
|
+
const PANOSE_SERIF_STYLE_OFFSET = 1;
|
|
13
|
+
const PANOSE_FAMILY_KIND_LATIN_TEXT = 2;
|
|
14
|
+
const PANOSE_FIRST_SERIF_STYLE = 2;
|
|
15
|
+
const PANOSE_LAST_SERIF_STYLE = 10;
|
|
16
|
+
const cache = /* @__PURE__ */ new WeakMap();
|
|
17
|
+
function scaleToGlyphSpace(designUnits, unitsPerEm) {
|
|
18
|
+
return designUnits * GLYPH_SPACE_UNITS_PER_EM / unitsPerEm;
|
|
19
|
+
}
|
|
20
|
+
function resolveCapHeight(font, cmap, declared, ascent) {
|
|
21
|
+
if (declared !== void 0) return declared;
|
|
22
|
+
const head = require_font_tables.parseHead(font);
|
|
23
|
+
const maxp = require_font_tables.parseMaxp(font);
|
|
24
|
+
const glyphId = cmap(CAP_HEIGHT_REFERENCE_CODE_POINT);
|
|
25
|
+
if (head === void 0 || maxp === void 0 || glyphId === void 0) return ascent;
|
|
26
|
+
return require_glyf.parseGlyf(font, {
|
|
27
|
+
numGlyphs: maxp.numGlyphs,
|
|
28
|
+
indexToLocFormat: head.indexToLocFormat
|
|
29
|
+
})?.glyphHeader(glyphId)?.yMax ?? ascent;
|
|
30
|
+
}
|
|
31
|
+
function isSerifByPanose(panose) {
|
|
32
|
+
if (panose?.[PANOSE_FAMILY_KIND_OFFSET] !== PANOSE_FAMILY_KIND_LATIN_TEXT) return false;
|
|
33
|
+
const serifStyle = panose[PANOSE_SERIF_STYLE_OFFSET];
|
|
34
|
+
return serifStyle !== void 0 && serifStyle >= PANOSE_FIRST_SERIF_STYLE && serifStyle <= PANOSE_LAST_SERIF_STYLE;
|
|
35
|
+
}
|
|
36
|
+
function loadEmbeddedFace(font) {
|
|
37
|
+
const cached = cache.get(font.bytes);
|
|
38
|
+
if (cached !== void 0 || cache.has(font.bytes)) return cached;
|
|
39
|
+
const face = readEmbeddedFace(font);
|
|
40
|
+
cache.set(font.bytes, face);
|
|
41
|
+
return face;
|
|
42
|
+
}
|
|
43
|
+
function readEmbeddedFace(font) {
|
|
44
|
+
const head = require_font_tables.parseHead(font);
|
|
45
|
+
const maxp = require_font_tables.parseMaxp(font);
|
|
46
|
+
const name = require_font_tables.parseName(font);
|
|
47
|
+
const cmap = require_cmap_table.buildCmapLookup(font);
|
|
48
|
+
if (head === void 0 || maxp === void 0 || cmap === void 0 || name?.postScriptName === void 0) return;
|
|
49
|
+
const hhea = parseHhea(font);
|
|
50
|
+
const hmtxBytes = require_sfnt.sfntTableBytes(font, "hmtx");
|
|
51
|
+
if (hhea === void 0 || hmtxBytes === void 0 || !require_sfnt.hasBytes(hmtxBytes, 0, hhea.numberOfHMetrics * LONG_HOR_METRIC_SIZE)) return;
|
|
52
|
+
const hmtx = require_hmtx_table.parseHmtx(font);
|
|
53
|
+
const os2 = require_font_tables.parseOs2(font);
|
|
54
|
+
const post = require_font_tables.parsePost(font);
|
|
55
|
+
const { unitsPerEm } = head;
|
|
56
|
+
const scale = (designUnits) => scaleToGlyphSpace(designUnits, unitsPerEm);
|
|
57
|
+
const capHeight = resolveCapHeight(font, cmap, os2?.sCapHeight, hhea.ascent);
|
|
58
|
+
return {
|
|
59
|
+
font,
|
|
60
|
+
postScriptName: name.postScriptName,
|
|
61
|
+
numGlyphs: maxp.numGlyphs,
|
|
62
|
+
metrics: {
|
|
63
|
+
unitsPerEm,
|
|
64
|
+
ascentGlyphSpace: scale(hhea.ascent),
|
|
65
|
+
descentGlyphSpace: scale(hhea.descent),
|
|
66
|
+
lineGapGlyphSpace: scale(hhea.lineGap),
|
|
67
|
+
typoAscentGlyphSpace: os2 === void 0 ? void 0 : scale(os2.sTypoAscender),
|
|
68
|
+
typoDescentGlyphSpace: os2 === void 0 ? void 0 : scale(os2.sTypoDescender),
|
|
69
|
+
typoLineGapGlyphSpace: os2 === void 0 ? void 0 : scale(os2.sTypoLineGap),
|
|
70
|
+
winAscentGlyphSpace: os2 === void 0 ? void 0 : scale(os2.usWinAscent),
|
|
71
|
+
winDescentGlyphSpace: os2 === void 0 ? void 0 : -scale(os2.usWinDescent),
|
|
72
|
+
capHeightGlyphSpace: scale(capHeight),
|
|
73
|
+
xHeightGlyphSpace: os2?.sxHeight === void 0 ? void 0 : scale(os2.sxHeight),
|
|
74
|
+
bboxGlyphSpace: [
|
|
75
|
+
scale(head.xMin),
|
|
76
|
+
scale(head.yMin),
|
|
77
|
+
scale(head.xMax),
|
|
78
|
+
scale(head.yMax)
|
|
79
|
+
],
|
|
80
|
+
italicAngleDegrees: post?.italicAngle ?? 0,
|
|
81
|
+
underlinePositionGlyphSpace: scale(post?.underlinePosition ?? 0),
|
|
82
|
+
underlineThicknessGlyphSpace: scale(post?.underlineThickness ?? 0),
|
|
83
|
+
serif: isSerifByPanose(os2?.panose)
|
|
84
|
+
},
|
|
85
|
+
glyphId: (codePoint) => cmap(codePoint),
|
|
86
|
+
glyphSpaceWidth: (glyphId) => scale(hmtx.advanceWidth(glyphId))
|
|
87
|
+
};
|
|
88
|
+
}
|
|
89
|
+
const HHEA_TABLE_SIZE = 36;
|
|
90
|
+
const HHEA_ASCENDER_OFFSET = 4;
|
|
91
|
+
const HHEA_DESCENDER_OFFSET = 6;
|
|
92
|
+
const HHEA_LINE_GAP_OFFSET = 8;
|
|
93
|
+
const HHEA_NUMBER_OF_HMETRICS_OFFSET = 34;
|
|
94
|
+
const LONG_HOR_METRIC_SIZE = 4;
|
|
95
|
+
function parseHhea(font) {
|
|
96
|
+
const bytes = require_sfnt.sfntTableBytes(font, "hhea");
|
|
97
|
+
if (bytes === void 0 || !require_sfnt.hasBytes(bytes, 0, HHEA_TABLE_SIZE)) return;
|
|
98
|
+
const numberOfHMetrics = require_sfnt.u16(bytes, HHEA_NUMBER_OF_HMETRICS_OFFSET);
|
|
99
|
+
if (numberOfHMetrics === 0) return;
|
|
100
|
+
return {
|
|
101
|
+
ascent: require_sfnt.i16(bytes, HHEA_ASCENDER_OFFSET),
|
|
102
|
+
descent: require_sfnt.i16(bytes, HHEA_DESCENDER_OFFSET),
|
|
103
|
+
lineGap: require_sfnt.i16(bytes, HHEA_LINE_GAP_OFFSET),
|
|
104
|
+
numberOfHMetrics
|
|
105
|
+
};
|
|
106
|
+
}
|
|
107
|
+
function encodeForShowEmbedded(text, face) {
|
|
108
|
+
const glyphIds = [];
|
|
109
|
+
const substitutions = [];
|
|
110
|
+
let width1000 = 0;
|
|
111
|
+
for (const character of text) {
|
|
112
|
+
const glyphId = face.glyphId(character.codePointAt(0));
|
|
113
|
+
if (glyphId === void 0) substitutions.push({ from: character });
|
|
114
|
+
const shown = glyphId ?? 0;
|
|
115
|
+
glyphIds.push(shown);
|
|
116
|
+
width1000 += face.glyphSpaceWidth(shown);
|
|
117
|
+
}
|
|
118
|
+
const codes = new Uint8Array(glyphIds.length * 2);
|
|
119
|
+
glyphIds.forEach((glyphId, index) => {
|
|
120
|
+
codes[index * 2] = glyphId >> 8 & 255;
|
|
121
|
+
codes[index * 2 + 1] = glyphId & 255;
|
|
122
|
+
});
|
|
123
|
+
return {
|
|
124
|
+
codes,
|
|
125
|
+
width1000,
|
|
126
|
+
substitutions
|
|
127
|
+
};
|
|
128
|
+
}
|
|
129
|
+
function collectEmbeddedGlyphs(texts, face) {
|
|
130
|
+
const used = /* @__PURE__ */ new Map();
|
|
131
|
+
for (const text of texts) for (const character of text) {
|
|
132
|
+
const codePoint = character.codePointAt(0);
|
|
133
|
+
const glyphId = face.glyphId(codePoint);
|
|
134
|
+
if (glyphId !== void 0 && !used.has(glyphId)) used.set(glyphId, codePoint);
|
|
135
|
+
}
|
|
136
|
+
return used;
|
|
137
|
+
}
|
|
138
|
+
//#endregion
|
|
139
|
+
exports.NOTDEF_GLYPH_ID = NOTDEF_GLYPH_ID;
|
|
140
|
+
exports.collectEmbeddedGlyphs = collectEmbeddedGlyphs;
|
|
141
|
+
exports.encodeForShowEmbedded = encodeForShowEmbedded;
|
|
142
|
+
exports.loadEmbeddedFace = loadEmbeddedFace;
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
import { a as NOTDEF_GLYPH_ID, c as loadEmbeddedFace, i as EmbeddedShow, n as EmbeddedFaceMetrics, o as collectEmbeddedGlyphs, r as EmbeddedFaceSubstitution, s as encodeForShowEmbedded, t as EmbeddedFace } from "./embedded-font-CLedn-mT.cjs";
|
|
2
|
+
export { EmbeddedFace, EmbeddedFaceMetrics, EmbeddedFaceSubstitution, EmbeddedShow, NOTDEF_GLYPH_ID, collectEmbeddedGlyphs, encodeForShowEmbedded, loadEmbeddedFace };
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
import { a as NOTDEF_GLYPH_ID, c as loadEmbeddedFace, i as EmbeddedShow, n as EmbeddedFaceMetrics, o as collectEmbeddedGlyphs, r as EmbeddedFaceSubstitution, s as encodeForShowEmbedded, t as EmbeddedFace } from "./embedded-font-CbqkKgBl.js";
|
|
2
|
+
export { EmbeddedFace, EmbeddedFaceMetrics, EmbeddedFaceSubstitution, EmbeddedShow, NOTDEF_GLYPH_ID, collectEmbeddedGlyphs, encodeForShowEmbedded, loadEmbeddedFace };
|
|
@@ -0,0 +1,138 @@
|
|
|
1
|
+
import { hasBytes, i16, sfntTableBytes, u16 } from "./sfnt.js";
|
|
2
|
+
import { buildCmapLookup } from "./cmap-table.js";
|
|
3
|
+
import { parseHead, parseMaxp, parseName, parseOs2, parsePost } from "./font-tables.js";
|
|
4
|
+
import { parseGlyf } from "./glyf.js";
|
|
5
|
+
import { parseHmtx } from "./hmtx-table.js";
|
|
6
|
+
//#region src/embedded-font.ts
|
|
7
|
+
const GLYPH_SPACE_UNITS_PER_EM = 1e3;
|
|
8
|
+
const NOTDEF_GLYPH_ID = 0;
|
|
9
|
+
const CAP_HEIGHT_REFERENCE_CODE_POINT = 72;
|
|
10
|
+
const PANOSE_FAMILY_KIND_OFFSET = 0;
|
|
11
|
+
const PANOSE_SERIF_STYLE_OFFSET = 1;
|
|
12
|
+
const PANOSE_FAMILY_KIND_LATIN_TEXT = 2;
|
|
13
|
+
const PANOSE_FIRST_SERIF_STYLE = 2;
|
|
14
|
+
const PANOSE_LAST_SERIF_STYLE = 10;
|
|
15
|
+
const cache = /* @__PURE__ */ new WeakMap();
|
|
16
|
+
function scaleToGlyphSpace(designUnits, unitsPerEm) {
|
|
17
|
+
return designUnits * GLYPH_SPACE_UNITS_PER_EM / unitsPerEm;
|
|
18
|
+
}
|
|
19
|
+
function resolveCapHeight(font, cmap, declared, ascent) {
|
|
20
|
+
if (declared !== void 0) return declared;
|
|
21
|
+
const head = parseHead(font);
|
|
22
|
+
const maxp = parseMaxp(font);
|
|
23
|
+
const glyphId = cmap(CAP_HEIGHT_REFERENCE_CODE_POINT);
|
|
24
|
+
if (head === void 0 || maxp === void 0 || glyphId === void 0) return ascent;
|
|
25
|
+
return parseGlyf(font, {
|
|
26
|
+
numGlyphs: maxp.numGlyphs,
|
|
27
|
+
indexToLocFormat: head.indexToLocFormat
|
|
28
|
+
})?.glyphHeader(glyphId)?.yMax ?? ascent;
|
|
29
|
+
}
|
|
30
|
+
function isSerifByPanose(panose) {
|
|
31
|
+
if (panose?.[PANOSE_FAMILY_KIND_OFFSET] !== PANOSE_FAMILY_KIND_LATIN_TEXT) return false;
|
|
32
|
+
const serifStyle = panose[PANOSE_SERIF_STYLE_OFFSET];
|
|
33
|
+
return serifStyle !== void 0 && serifStyle >= PANOSE_FIRST_SERIF_STYLE && serifStyle <= PANOSE_LAST_SERIF_STYLE;
|
|
34
|
+
}
|
|
35
|
+
function loadEmbeddedFace(font) {
|
|
36
|
+
const cached = cache.get(font.bytes);
|
|
37
|
+
if (cached !== void 0 || cache.has(font.bytes)) return cached;
|
|
38
|
+
const face = readEmbeddedFace(font);
|
|
39
|
+
cache.set(font.bytes, face);
|
|
40
|
+
return face;
|
|
41
|
+
}
|
|
42
|
+
function readEmbeddedFace(font) {
|
|
43
|
+
const head = parseHead(font);
|
|
44
|
+
const maxp = parseMaxp(font);
|
|
45
|
+
const name = parseName(font);
|
|
46
|
+
const cmap = buildCmapLookup(font);
|
|
47
|
+
if (head === void 0 || maxp === void 0 || cmap === void 0 || name?.postScriptName === void 0) return;
|
|
48
|
+
const hhea = parseHhea(font);
|
|
49
|
+
const hmtxBytes = sfntTableBytes(font, "hmtx");
|
|
50
|
+
if (hhea === void 0 || hmtxBytes === void 0 || !hasBytes(hmtxBytes, 0, hhea.numberOfHMetrics * LONG_HOR_METRIC_SIZE)) return;
|
|
51
|
+
const hmtx = parseHmtx(font);
|
|
52
|
+
const os2 = parseOs2(font);
|
|
53
|
+
const post = parsePost(font);
|
|
54
|
+
const { unitsPerEm } = head;
|
|
55
|
+
const scale = (designUnits) => scaleToGlyphSpace(designUnits, unitsPerEm);
|
|
56
|
+
const capHeight = resolveCapHeight(font, cmap, os2?.sCapHeight, hhea.ascent);
|
|
57
|
+
return {
|
|
58
|
+
font,
|
|
59
|
+
postScriptName: name.postScriptName,
|
|
60
|
+
numGlyphs: maxp.numGlyphs,
|
|
61
|
+
metrics: {
|
|
62
|
+
unitsPerEm,
|
|
63
|
+
ascentGlyphSpace: scale(hhea.ascent),
|
|
64
|
+
descentGlyphSpace: scale(hhea.descent),
|
|
65
|
+
lineGapGlyphSpace: scale(hhea.lineGap),
|
|
66
|
+
typoAscentGlyphSpace: os2 === void 0 ? void 0 : scale(os2.sTypoAscender),
|
|
67
|
+
typoDescentGlyphSpace: os2 === void 0 ? void 0 : scale(os2.sTypoDescender),
|
|
68
|
+
typoLineGapGlyphSpace: os2 === void 0 ? void 0 : scale(os2.sTypoLineGap),
|
|
69
|
+
winAscentGlyphSpace: os2 === void 0 ? void 0 : scale(os2.usWinAscent),
|
|
70
|
+
winDescentGlyphSpace: os2 === void 0 ? void 0 : -scale(os2.usWinDescent),
|
|
71
|
+
capHeightGlyphSpace: scale(capHeight),
|
|
72
|
+
xHeightGlyphSpace: os2?.sxHeight === void 0 ? void 0 : scale(os2.sxHeight),
|
|
73
|
+
bboxGlyphSpace: [
|
|
74
|
+
scale(head.xMin),
|
|
75
|
+
scale(head.yMin),
|
|
76
|
+
scale(head.xMax),
|
|
77
|
+
scale(head.yMax)
|
|
78
|
+
],
|
|
79
|
+
italicAngleDegrees: post?.italicAngle ?? 0,
|
|
80
|
+
underlinePositionGlyphSpace: scale(post?.underlinePosition ?? 0),
|
|
81
|
+
underlineThicknessGlyphSpace: scale(post?.underlineThickness ?? 0),
|
|
82
|
+
serif: isSerifByPanose(os2?.panose)
|
|
83
|
+
},
|
|
84
|
+
glyphId: (codePoint) => cmap(codePoint),
|
|
85
|
+
glyphSpaceWidth: (glyphId) => scale(hmtx.advanceWidth(glyphId))
|
|
86
|
+
};
|
|
87
|
+
}
|
|
88
|
+
const HHEA_TABLE_SIZE = 36;
|
|
89
|
+
const HHEA_ASCENDER_OFFSET = 4;
|
|
90
|
+
const HHEA_DESCENDER_OFFSET = 6;
|
|
91
|
+
const HHEA_LINE_GAP_OFFSET = 8;
|
|
92
|
+
const HHEA_NUMBER_OF_HMETRICS_OFFSET = 34;
|
|
93
|
+
const LONG_HOR_METRIC_SIZE = 4;
|
|
94
|
+
function parseHhea(font) {
|
|
95
|
+
const bytes = sfntTableBytes(font, "hhea");
|
|
96
|
+
if (bytes === void 0 || !hasBytes(bytes, 0, HHEA_TABLE_SIZE)) return;
|
|
97
|
+
const numberOfHMetrics = u16(bytes, HHEA_NUMBER_OF_HMETRICS_OFFSET);
|
|
98
|
+
if (numberOfHMetrics === 0) return;
|
|
99
|
+
return {
|
|
100
|
+
ascent: i16(bytes, HHEA_ASCENDER_OFFSET),
|
|
101
|
+
descent: i16(bytes, HHEA_DESCENDER_OFFSET),
|
|
102
|
+
lineGap: i16(bytes, HHEA_LINE_GAP_OFFSET),
|
|
103
|
+
numberOfHMetrics
|
|
104
|
+
};
|
|
105
|
+
}
|
|
106
|
+
function encodeForShowEmbedded(text, face) {
|
|
107
|
+
const glyphIds = [];
|
|
108
|
+
const substitutions = [];
|
|
109
|
+
let width1000 = 0;
|
|
110
|
+
for (const character of text) {
|
|
111
|
+
const glyphId = face.glyphId(character.codePointAt(0));
|
|
112
|
+
if (glyphId === void 0) substitutions.push({ from: character });
|
|
113
|
+
const shown = glyphId ?? 0;
|
|
114
|
+
glyphIds.push(shown);
|
|
115
|
+
width1000 += face.glyphSpaceWidth(shown);
|
|
116
|
+
}
|
|
117
|
+
const codes = new Uint8Array(glyphIds.length * 2);
|
|
118
|
+
glyphIds.forEach((glyphId, index) => {
|
|
119
|
+
codes[index * 2] = glyphId >> 8 & 255;
|
|
120
|
+
codes[index * 2 + 1] = glyphId & 255;
|
|
121
|
+
});
|
|
122
|
+
return {
|
|
123
|
+
codes,
|
|
124
|
+
width1000,
|
|
125
|
+
substitutions
|
|
126
|
+
};
|
|
127
|
+
}
|
|
128
|
+
function collectEmbeddedGlyphs(texts, face) {
|
|
129
|
+
const used = /* @__PURE__ */ new Map();
|
|
130
|
+
for (const text of texts) for (const character of text) {
|
|
131
|
+
const codePoint = character.codePointAt(0);
|
|
132
|
+
const glyphId = face.glyphId(codePoint);
|
|
133
|
+
if (glyphId !== void 0 && !used.has(glyphId)) used.set(glyphId, codePoint);
|
|
134
|
+
}
|
|
135
|
+
return used;
|
|
136
|
+
}
|
|
137
|
+
//#endregion
|
|
138
|
+
export { NOTDEF_GLYPH_ID, collectEmbeddedGlyphs, encodeForShowEmbedded, loadEmbeddedFace };
|
package/dist/encrypt.cjs
ADDED
|
@@ -0,0 +1,264 @@
|
|
|
1
|
+
Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
|
|
2
|
+
const require_bytes_writer = require("./bytes/writer.cjs");
|
|
3
|
+
const require_diagnostics = require("./diagnostics.cjs");
|
|
4
|
+
const require_crypto_aes = require("./crypto/aes.cjs");
|
|
5
|
+
const require_crypto_md5 = require("./crypto/md5.cjs");
|
|
6
|
+
const require_crypto_rc4 = require("./crypto/rc4.cjs");
|
|
7
|
+
const require_crypto_sha2 = require("./crypto/sha2.cjs");
|
|
8
|
+
const require_objects = require("./objects.cjs");
|
|
9
|
+
//#region src/encrypt.ts
|
|
10
|
+
const PASSWORD_PADDING = new Uint8Array([
|
|
11
|
+
40,
|
|
12
|
+
191,
|
|
13
|
+
78,
|
|
14
|
+
94,
|
|
15
|
+
78,
|
|
16
|
+
117,
|
|
17
|
+
138,
|
|
18
|
+
65,
|
|
19
|
+
100,
|
|
20
|
+
0,
|
|
21
|
+
78,
|
|
22
|
+
86,
|
|
23
|
+
255,
|
|
24
|
+
250,
|
|
25
|
+
1,
|
|
26
|
+
8,
|
|
27
|
+
46,
|
|
28
|
+
46,
|
|
29
|
+
0,
|
|
30
|
+
182,
|
|
31
|
+
208,
|
|
32
|
+
104,
|
|
33
|
+
62,
|
|
34
|
+
128,
|
|
35
|
+
47,
|
|
36
|
+
12,
|
|
37
|
+
169,
|
|
38
|
+
254,
|
|
39
|
+
100,
|
|
40
|
+
83,
|
|
41
|
+
105,
|
|
42
|
+
122
|
|
43
|
+
]);
|
|
44
|
+
const AES_OBJECT_KEY_SALT = new Uint8Array([
|
|
45
|
+
115,
|
|
46
|
+
65,
|
|
47
|
+
108,
|
|
48
|
+
84
|
|
49
|
+
]);
|
|
50
|
+
const MD5_DIGEST_BYTES = 16;
|
|
51
|
+
const OBJECT_KEY_EXTRA_BYTES = 5;
|
|
52
|
+
const LEGACY_KEY_ITERATIONS = 50;
|
|
53
|
+
const ALGORITHM_5_ITERATIONS = 19;
|
|
54
|
+
const R6_MINIMUM_ROUNDS = 64;
|
|
55
|
+
const R6_ROUND_REPEATS = 64;
|
|
56
|
+
const R6_TERMINATION_MARGIN = 32;
|
|
57
|
+
const AESV3_KEY_BYTES = 32;
|
|
58
|
+
const RC4_40_KEY_BYTES = 5;
|
|
59
|
+
const AESV2_KEY_BYTES = 16;
|
|
60
|
+
const DEFAULT_KEY_BITS = 40;
|
|
61
|
+
const BITS_PER_BYTE = 8;
|
|
62
|
+
const MAX_RC4_KEY_BYTES = 16;
|
|
63
|
+
const ZERO_IV = /* @__PURE__ */ new Uint8Array(16);
|
|
64
|
+
function requireStringBytes(dict, key) {
|
|
65
|
+
const value = require_objects.dictGet(dict, key);
|
|
66
|
+
if (value?.kind !== "string") throw new require_diagnostics.PdfEncryptedError(`the /Encrypt dictionary's /${key} entry is missing or is not a direct string; this file's encryption cannot be read`);
|
|
67
|
+
return value.bytes;
|
|
68
|
+
}
|
|
69
|
+
function bytesEqual(a, b, length) {
|
|
70
|
+
if (a.length < length || b.length < length) return false;
|
|
71
|
+
for (let i = 0; i < length; i++) if (a[i] !== b[i]) return false;
|
|
72
|
+
return true;
|
|
73
|
+
}
|
|
74
|
+
function permissionsBytes(p) {
|
|
75
|
+
const unsigned = Math.trunc(p) >>> 0;
|
|
76
|
+
return new Uint8Array([
|
|
77
|
+
unsigned & 255,
|
|
78
|
+
unsigned >>> 8 & 255,
|
|
79
|
+
unsigned >>> 16 & 255,
|
|
80
|
+
unsigned >>> 24 & 255
|
|
81
|
+
]);
|
|
82
|
+
}
|
|
83
|
+
function computeLegacyFileKey(encryptDict, fileId, revision, keyBytes, encryptMetadata) {
|
|
84
|
+
const owner = requireStringBytes(encryptDict, "O");
|
|
85
|
+
const permissions = require_objects.asNumber(require_objects.dictGet(encryptDict, "P")) ?? 0;
|
|
86
|
+
const parts = [
|
|
87
|
+
PASSWORD_PADDING,
|
|
88
|
+
owner.subarray(0, PASSWORD_PADDING.length),
|
|
89
|
+
permissionsBytes(permissions),
|
|
90
|
+
fileId
|
|
91
|
+
];
|
|
92
|
+
if (revision >= 4 && !encryptMetadata) parts.push(new Uint8Array([
|
|
93
|
+
255,
|
|
94
|
+
255,
|
|
95
|
+
255,
|
|
96
|
+
255
|
|
97
|
+
]));
|
|
98
|
+
let digest = require_crypto_md5.md5(require_bytes_writer.concatBytes(parts));
|
|
99
|
+
if (revision >= 3) for (let i = 0; i < LEGACY_KEY_ITERATIONS; i++) digest = require_crypto_md5.md5(digest.subarray(0, keyBytes));
|
|
100
|
+
return digest.subarray(0, keyBytes);
|
|
101
|
+
}
|
|
102
|
+
function legacyUserPasswordVerifies(encryptDict, fileId, revision, fileKey) {
|
|
103
|
+
const storedUser = requireStringBytes(encryptDict, "U");
|
|
104
|
+
if (revision === 2) return bytesEqual(require_crypto_rc4.rc4(fileKey, PASSWORD_PADDING), storedUser, PASSWORD_PADDING.length);
|
|
105
|
+
let value = require_crypto_rc4.rc4(fileKey, require_crypto_md5.md5(require_bytes_writer.concatBytes([PASSWORD_PADDING, fileId])));
|
|
106
|
+
for (let i = 1; i <= ALGORITHM_5_ITERATIONS; i++) {
|
|
107
|
+
const roundKey = Uint8Array.from(fileKey, (byte) => byte ^ i);
|
|
108
|
+
value = require_crypto_rc4.rc4(roundKey, value);
|
|
109
|
+
}
|
|
110
|
+
return bytesEqual(value, storedUser, MD5_DIGEST_BYTES);
|
|
111
|
+
}
|
|
112
|
+
function hardenedHash(password, salt, userData, revision) {
|
|
113
|
+
let k = require_crypto_sha2.sha256(require_bytes_writer.concatBytes([
|
|
114
|
+
password,
|
|
115
|
+
salt,
|
|
116
|
+
userData
|
|
117
|
+
]));
|
|
118
|
+
if (revision === 5) return k;
|
|
119
|
+
let e = new Uint8Array([0]);
|
|
120
|
+
let round = 0;
|
|
121
|
+
while (round < R6_MINIMUM_ROUNDS || e[e.length - 1] > round - R6_TERMINATION_MARGIN) {
|
|
122
|
+
const roundInput = require_bytes_writer.concatBytes([
|
|
123
|
+
password,
|
|
124
|
+
k,
|
|
125
|
+
userData
|
|
126
|
+
]);
|
|
127
|
+
const k1 = new Uint8Array(roundInput.length * R6_ROUND_REPEATS);
|
|
128
|
+
for (let i = 0; i < R6_ROUND_REPEATS; i++) k1.set(roundInput, i * roundInput.length);
|
|
129
|
+
e = require_crypto_aes.aesCbcEncrypt(k.subarray(0, 16), k.subarray(16, 32), k1);
|
|
130
|
+
let sum = 0;
|
|
131
|
+
for (let i = 0; i < 16; i++) sum += e[i];
|
|
132
|
+
const selector = sum % 3;
|
|
133
|
+
k = selector === 0 ? require_crypto_sha2.sha256(e) : selector === 1 ? require_crypto_sha2.sha384(e) : require_crypto_sha2.sha512(e);
|
|
134
|
+
round++;
|
|
135
|
+
}
|
|
136
|
+
return k.subarray(0, AESV3_KEY_BYTES);
|
|
137
|
+
}
|
|
138
|
+
function computeAesV3FileKey(encryptDict, revision) {
|
|
139
|
+
const storedUser = requireStringBytes(encryptDict, "U");
|
|
140
|
+
const expectedLength = 48;
|
|
141
|
+
if (storedUser.length < expectedLength) throw new require_diagnostics.PdfEncryptedError(`the /Encrypt dictionary's /U entry is ${String(storedUser.length)} bytes; revision ${String(revision)} requires ${String(expectedLength)}`);
|
|
142
|
+
const empty = /* @__PURE__ */ new Uint8Array(0);
|
|
143
|
+
const validationSalt = storedUser.subarray(AESV3_KEY_BYTES, 40);
|
|
144
|
+
const keySalt = storedUser.subarray(40, expectedLength);
|
|
145
|
+
if (!bytesEqual(hardenedHash(empty, validationSalt, empty, revision), storedUser, AESV3_KEY_BYTES)) throw new require_diagnostics.PdfPasswordRequiredError();
|
|
146
|
+
const intermediateKey = hardenedHash(empty, keySalt, empty, revision);
|
|
147
|
+
const wrappedKey = requireStringBytes(encryptDict, "UE");
|
|
148
|
+
if (wrappedKey.length < AESV3_KEY_BYTES) throw new require_diagnostics.PdfEncryptedError(`the /Encrypt dictionary's /UE entry is ${String(wrappedKey.length)} bytes; revision ${String(revision)} requires ${String(AESV3_KEY_BYTES)}`);
|
|
149
|
+
return require_crypto_aes.aesCbcDecrypt(intermediateKey, ZERO_IV, wrappedKey.subarray(0, AESV3_KEY_BYTES));
|
|
150
|
+
}
|
|
151
|
+
function methodFromCryptFilterName(encryptDict, entryKey) {
|
|
152
|
+
const name = require_objects.asName(require_objects.dictGet(encryptDict, entryKey)) ?? "Identity";
|
|
153
|
+
if (name === "Identity") return "identity";
|
|
154
|
+
const filters = require_objects.asDict(require_objects.dictGet(encryptDict, "CF"));
|
|
155
|
+
const filter = filters !== void 0 ? require_objects.asDict(require_objects.dictGet(filters, name)) : void 0;
|
|
156
|
+
if (filter === void 0) throw new require_diagnostics.PdfEncryptedError(`the /Encrypt dictionary names a crypt filter /${name} for /${entryKey} that its own /CF dictionary does not define`);
|
|
157
|
+
const method = require_objects.asName(require_objects.dictGet(filter, "CFM"));
|
|
158
|
+
if (method === "None") return "identity";
|
|
159
|
+
if (method === "V2") return "rc4";
|
|
160
|
+
if (method === "AESV2" || method === "AESV3") return "aes";
|
|
161
|
+
throw new require_diagnostics.PdfEncryptedError(`crypt filter /${name} uses an unsupported /CFM ${method === void 0 ? "(absent)" : `/${method}`}`);
|
|
162
|
+
}
|
|
163
|
+
function cryptFilterKeyBytes(encryptDict, declaredBits) {
|
|
164
|
+
const streamFilterName = require_objects.asName(require_objects.dictGet(encryptDict, "StmF")) ?? "Identity";
|
|
165
|
+
const filters = require_objects.asDict(require_objects.dictGet(encryptDict, "CF"));
|
|
166
|
+
const filter = filters !== void 0 ? require_objects.asDict(require_objects.dictGet(filters, streamFilterName)) : void 0;
|
|
167
|
+
if ((filter !== void 0 ? require_objects.asName(require_objects.dictGet(filter, "CFM")) : void 0) === "AESV2") return AESV2_KEY_BYTES;
|
|
168
|
+
const declared = filter !== void 0 ? require_objects.asNumber(require_objects.dictGet(filter, "Length")) : void 0;
|
|
169
|
+
if (declared === void 0) return Math.floor(declaredBits / BITS_PER_BYTE);
|
|
170
|
+
return declared >= DEFAULT_KEY_BITS ? Math.floor(declared / BITS_PER_BYTE) : declared;
|
|
171
|
+
}
|
|
172
|
+
function setUpHandler(encryptDict, fileId) {
|
|
173
|
+
const version = require_objects.asNumber(require_objects.dictGet(encryptDict, "V")) ?? 0;
|
|
174
|
+
const revision = require_objects.asNumber(require_objects.dictGet(encryptDict, "R")) ?? 0;
|
|
175
|
+
const declaredBits = require_objects.asNumber(require_objects.dictGet(encryptDict, "Length")) ?? DEFAULT_KEY_BITS;
|
|
176
|
+
const encryptMetadataEntry = require_objects.dictGet(encryptDict, "EncryptMetadata");
|
|
177
|
+
const encryptMetadata = encryptMetadataEntry?.kind === "bool" ? encryptMetadataEntry.value : true;
|
|
178
|
+
if (version === 5) {
|
|
179
|
+
if (revision !== 5 && revision !== 6) throw new require_diagnostics.PdfEncryptedError(`/V 5 encryption with an unsupported revision /R ${String(revision)}`);
|
|
180
|
+
return {
|
|
181
|
+
fileKey: computeAesV3FileKey(encryptDict, revision),
|
|
182
|
+
streamMethod: methodFromCryptFilterName(encryptDict, "StmF"),
|
|
183
|
+
stringMethod: methodFromCryptFilterName(encryptDict, "StrF"),
|
|
184
|
+
perObjectKeys: false,
|
|
185
|
+
encryptMetadata
|
|
186
|
+
};
|
|
187
|
+
}
|
|
188
|
+
if (version !== 1 && version !== 2 && version !== 4) throw new require_diagnostics.PdfEncryptedError(`unsupported standard security handler version /V ${String(version)}`);
|
|
189
|
+
if (revision < 2 || revision > 4) throw new require_diagnostics.PdfEncryptedError(`unsupported standard security handler revision /R ${String(revision)}`);
|
|
190
|
+
const streamMethod = version === 4 ? methodFromCryptFilterName(encryptDict, "StmF") : "rc4";
|
|
191
|
+
const stringMethod = version === 4 ? methodFromCryptFilterName(encryptDict, "StrF") : "rc4";
|
|
192
|
+
const keyBytes = version === 1 ? RC4_40_KEY_BYTES : version === 2 ? Math.floor(declaredBits / BITS_PER_BYTE) : cryptFilterKeyBytes(encryptDict, declaredBits);
|
|
193
|
+
if (keyBytes < RC4_40_KEY_BYTES || keyBytes > MAX_RC4_KEY_BYTES) throw new require_diagnostics.PdfEncryptedError(`the /Encrypt dictionary declares a ${String(keyBytes * BITS_PER_BYTE)}-bit file key, outside the 40-128 bit range /V ${String(version)} permits`);
|
|
194
|
+
const fileKey = computeLegacyFileKey(encryptDict, fileId, revision, keyBytes, encryptMetadata);
|
|
195
|
+
if (!legacyUserPasswordVerifies(encryptDict, fileId, revision, fileKey)) throw new require_diagnostics.PdfPasswordRequiredError();
|
|
196
|
+
return {
|
|
197
|
+
fileKey,
|
|
198
|
+
streamMethod,
|
|
199
|
+
stringMethod,
|
|
200
|
+
perObjectKeys: true,
|
|
201
|
+
encryptMetadata
|
|
202
|
+
};
|
|
203
|
+
}
|
|
204
|
+
function objectKey(fileKey, num, gen, method) {
|
|
205
|
+
const extra = new Uint8Array(OBJECT_KEY_EXTRA_BYTES);
|
|
206
|
+
extra[0] = num & 255;
|
|
207
|
+
extra[1] = num >>> 8 & 255;
|
|
208
|
+
extra[2] = num >>> 16 & 255;
|
|
209
|
+
extra[3] = gen & 255;
|
|
210
|
+
extra[4] = gen >>> 8 & 255;
|
|
211
|
+
return require_crypto_md5.md5(require_bytes_writer.concatBytes(method === "aes" ? [
|
|
212
|
+
fileKey,
|
|
213
|
+
extra,
|
|
214
|
+
AES_OBJECT_KEY_SALT
|
|
215
|
+
] : [fileKey, extra])).subarray(0, Math.min(fileKey.length + OBJECT_KEY_EXTRA_BYTES, MD5_DIGEST_BYTES));
|
|
216
|
+
}
|
|
217
|
+
function decryptAes(key, data, sink) {
|
|
218
|
+
if (data.length <= 16) {
|
|
219
|
+
if (data.length !== 0 && data.length !== 16) sink({
|
|
220
|
+
code: "pdf/decrypt-truncated",
|
|
221
|
+
severity: "warning",
|
|
222
|
+
message: `an AES-encrypted value is ${String(data.length)} bytes, too short to carry even its own initialisation vector; treating it as empty`
|
|
223
|
+
});
|
|
224
|
+
return /* @__PURE__ */ new Uint8Array(0);
|
|
225
|
+
}
|
|
226
|
+
const body = data.subarray(16);
|
|
227
|
+
if (body.length % 16 !== 0) sink({
|
|
228
|
+
code: "pdf/decrypt-truncated",
|
|
229
|
+
severity: "warning",
|
|
230
|
+
message: `an AES-encrypted value's ciphertext is ${String(body.length)} bytes, not a whole number of 16-byte blocks; the trailing partial block is being dropped`
|
|
231
|
+
});
|
|
232
|
+
const plain = require_crypto_aes.aesCbcDecrypt(key, data.subarray(0, 16), body);
|
|
233
|
+
const padLength = plain[plain.length - 1] ?? 0;
|
|
234
|
+
if (padLength < 1 || padLength > 16 || padLength > plain.length) {
|
|
235
|
+
sink({
|
|
236
|
+
code: "pdf/decrypt-bad-padding",
|
|
237
|
+
severity: "warning",
|
|
238
|
+
message: "an AES-encrypted value did not end in valid PKCS#7 padding; keeping the decrypted bytes unstripped"
|
|
239
|
+
});
|
|
240
|
+
return plain;
|
|
241
|
+
}
|
|
242
|
+
return plain.subarray(0, plain.length - padLength);
|
|
243
|
+
}
|
|
244
|
+
function applyMethod(method, fileKey, perObjectKeys, bytes, num, gen, sink) {
|
|
245
|
+
if (method === "identity") return bytes;
|
|
246
|
+
const key = perObjectKeys ? objectKey(fileKey, num, gen, method) : fileKey;
|
|
247
|
+
return method === "rc4" ? require_crypto_rc4.rc4(key, bytes) : decryptAes(key, bytes, sink);
|
|
248
|
+
}
|
|
249
|
+
function createStandardDecryptor(encryptDict, fileId, sink) {
|
|
250
|
+
const filter = require_objects.asName(require_objects.dictGet(encryptDict, "Filter"));
|
|
251
|
+
if (filter !== "Standard") throw new require_diagnostics.PdfEncryptedError(`this PDF uses the ${filter === void 0 ? "unnamed" : `/${filter}`} security handler; only the standard security handler is supported`);
|
|
252
|
+
const setup = setUpHandler(encryptDict, fileId);
|
|
253
|
+
return {
|
|
254
|
+
decryptString(bytes, num, gen) {
|
|
255
|
+
return applyMethod(setup.stringMethod, setup.fileKey, setup.perObjectKeys, bytes, num, gen, sink);
|
|
256
|
+
},
|
|
257
|
+
decryptStream(bytes, dict, num, gen) {
|
|
258
|
+
if (!setup.encryptMetadata && require_objects.asName(require_objects.dictGet(dict, "Type")) === "Metadata") return bytes;
|
|
259
|
+
return applyMethod(setup.streamMethod, setup.fileKey, setup.perObjectKeys, bytes, num, gen, sink);
|
|
260
|
+
}
|
|
261
|
+
};
|
|
262
|
+
}
|
|
263
|
+
//#endregion
|
|
264
|
+
exports.createStandardDecryptor = createStandardDecryptor;
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import { PdfDiagnosticSink } from "./diagnostics.cjs";
|
|
2
|
+
import { PdfDict } from "./objects.cjs";
|
|
3
|
+
//#region src/encrypt.d.ts
|
|
4
|
+
type CipherMethod = 'identity' | 'rc4' | 'aes';
|
|
5
|
+
interface PdfDecryptor {
|
|
6
|
+
decryptString(bytes: Uint8Array<ArrayBuffer>, num: number, gen: number): Uint8Array<ArrayBuffer>;
|
|
7
|
+
decryptStream(bytes: Uint8Array<ArrayBuffer>, dict: PdfDict, num: number, gen: number): Uint8Array<ArrayBuffer>;
|
|
8
|
+
}
|
|
9
|
+
declare function createStandardDecryptor(encryptDict: PdfDict, fileId: Uint8Array<ArrayBuffer>, sink: PdfDiagnosticSink): PdfDecryptor;
|
|
10
|
+
//#endregion
|
|
11
|
+
export { CipherMethod, PdfDecryptor, createStandardDecryptor };
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import { PdfDiagnosticSink } from "./diagnostics.js";
|
|
2
|
+
import { PdfDict } from "./objects.js";
|
|
3
|
+
//#region src/encrypt.d.ts
|
|
4
|
+
type CipherMethod = 'identity' | 'rc4' | 'aes';
|
|
5
|
+
interface PdfDecryptor {
|
|
6
|
+
decryptString(bytes: Uint8Array<ArrayBuffer>, num: number, gen: number): Uint8Array<ArrayBuffer>;
|
|
7
|
+
decryptStream(bytes: Uint8Array<ArrayBuffer>, dict: PdfDict, num: number, gen: number): Uint8Array<ArrayBuffer>;
|
|
8
|
+
}
|
|
9
|
+
declare function createStandardDecryptor(encryptDict: PdfDict, fileId: Uint8Array<ArrayBuffer>, sink: PdfDiagnosticSink): PdfDecryptor;
|
|
10
|
+
//#endregion
|
|
11
|
+
export { CipherMethod, PdfDecryptor, createStandardDecryptor };
|