documents.js 1.33.3 → 1.34.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 +1 -0
- package/dist/index.cjs +660 -570
- package/dist/index.d.cts +3 -0
- package/dist/index.d.ts +3 -0
- package/dist/index.js +660 -570
- package/package.json +4 -2
package/dist/index.cjs
CHANGED
|
@@ -99,7 +99,8 @@ const LayoutItemSchema = zod.z.discriminatedUnion("kind", [
|
|
|
99
99
|
const LayoutPageSchema = zod.z.object({
|
|
100
100
|
widthPt: zod.z.number().positive(),
|
|
101
101
|
heightPt: zod.z.number().positive(),
|
|
102
|
-
items: zod.z.array(LayoutItemSchema)
|
|
102
|
+
items: zod.z.array(LayoutItemSchema),
|
|
103
|
+
notes: zod.z.string().optional()
|
|
103
104
|
});
|
|
104
105
|
const LayoutImageAssetSchema = zod.z.object({
|
|
105
106
|
format: zod.z.enum(["png", "jpeg"]),
|
|
@@ -1308,16 +1309,21 @@ const DML_NS = "http://schemas.openxmlformats.org/drawingml/2006/main";
|
|
|
1308
1309
|
const R_NS = "http://schemas.openxmlformats.org/officeDocument/2006/relationships";
|
|
1309
1310
|
const DEFAULT_SLIDE_WIDTH_EMU = "12192000";
|
|
1310
1311
|
const DEFAULT_SLIDE_HEIGHT_EMU = "6858000";
|
|
1312
|
+
const DEFAULT_NOTES_WIDTH_EMU = "6858000";
|
|
1313
|
+
const DEFAULT_NOTES_HEIGHT_EMU = "9144000";
|
|
1311
1314
|
const SLIDE_MASTER_PART_PATH = "ppt/slideMasters/slideMaster1.xml";
|
|
1312
1315
|
const SLIDE_LAYOUT_PART_PATH = "ppt/slideLayouts/slideLayout1.xml";
|
|
1313
1316
|
const THEME_PART_PATH = "ppt/theme/theme1.xml";
|
|
1314
1317
|
const PRESENTATION_PART_PATH$1 = "ppt/presentation.xml";
|
|
1318
|
+
const NOTES_MASTER_PART_PATH = "ppt/notesMasters/notesMaster1.xml";
|
|
1315
1319
|
const SLIDE_MASTER_CONTENT_TYPE = "application/vnd.openxmlformats-officedocument.presentationml.slideMaster+xml";
|
|
1316
1320
|
const SLIDE_LAYOUT_CONTENT_TYPE = "application/vnd.openxmlformats-officedocument.presentationml.slideLayout+xml";
|
|
1317
1321
|
const THEME_CONTENT_TYPE = "application/vnd.openxmlformats-officedocument.theme+xml";
|
|
1322
|
+
const NOTES_MASTER_CONTENT_TYPE = "application/vnd.openxmlformats-officedocument.presentationml.notesMaster+xml";
|
|
1318
1323
|
const SLIDE_MASTER_REL_TYPE = "http://schemas.openxmlformats.org/officeDocument/2006/relationships/slideMaster";
|
|
1319
1324
|
const SLIDE_LAYOUT_REL_TYPE = "http://schemas.openxmlformats.org/officeDocument/2006/relationships/slideLayout";
|
|
1320
1325
|
const THEME_REL_TYPE = "http://schemas.openxmlformats.org/officeDocument/2006/relationships/theme";
|
|
1326
|
+
const NOTES_MASTER_REL_TYPE = "http://schemas.openxmlformats.org/officeDocument/2006/relationships/notesMaster";
|
|
1321
1327
|
const MIN_MASTER_OR_LAYOUT_ID = 2147483648;
|
|
1322
1328
|
function declaration() {
|
|
1323
1329
|
return {
|
|
@@ -1454,6 +1460,37 @@ function buildSlideLayout() {
|
|
|
1454
1460
|
preserve: "1"
|
|
1455
1461
|
}, [el("p:cSld", { name: "Blank" }, [buildEmptyGroupSpTree()]), el("p:clrMapOvr", {}, [el("a:masterClrMapping")])]);
|
|
1456
1462
|
}
|
|
1463
|
+
function buildNotesMaster() {
|
|
1464
|
+
return el("p:notesMaster", {
|
|
1465
|
+
"xmlns:p": PML_NS,
|
|
1466
|
+
"xmlns:a": DML_NS
|
|
1467
|
+
}, [el("p:cSld", {}, [buildWhiteBackground(), buildEmptyGroupSpTree()]), buildIdentityColorMap()]);
|
|
1468
|
+
}
|
|
1469
|
+
function ensureNotesMaster(pkg) {
|
|
1470
|
+
if (pkg.parts[NOTES_MASTER_PART_PATH] !== void 0) return NOTES_MASTER_PART_PATH;
|
|
1471
|
+
const masterToThemeTarget = buildRelativeTarget(NOTES_MASTER_PART_PATH, THEME_PART_PATH);
|
|
1472
|
+
addRelationship(pkg, NOTES_MASTER_PART_PATH, {
|
|
1473
|
+
type: THEME_REL_TYPE,
|
|
1474
|
+
target: masterToThemeTarget
|
|
1475
|
+
});
|
|
1476
|
+
pkg.parts[NOTES_MASTER_PART_PATH] = {
|
|
1477
|
+
kind: "xml",
|
|
1478
|
+
nodes: [declaration(), buildNotesMaster()]
|
|
1479
|
+
};
|
|
1480
|
+
ensureContentTypeOverride(pkg, NOTES_MASTER_PART_PATH, NOTES_MASTER_CONTENT_TYPE);
|
|
1481
|
+
const presentationToNotesMasterTarget = buildRelativeTarget(PRESENTATION_PART_PATH$1, NOTES_MASTER_PART_PATH);
|
|
1482
|
+
const relId = addRelationship(pkg, PRESENTATION_PART_PATH$1, {
|
|
1483
|
+
type: NOTES_MASTER_REL_TYPE,
|
|
1484
|
+
target: presentationToNotesMasterTarget
|
|
1485
|
+
});
|
|
1486
|
+
const presentationPart = pkg.parts[PRESENTATION_PART_PATH$1];
|
|
1487
|
+
const presentationElement = presentationPart?.kind === "xml" ? presentationPart.nodes.find((n) => n.type === "element") : void 0;
|
|
1488
|
+
if (presentationElement === void 0) throw new Error("ensureNotesMaster: package has no ppt/presentation.xml element");
|
|
1489
|
+
const sldMasterIdLstIndex = presentationElement.children.findIndex((c) => c.type === "element" && c.tag === "p:sldMasterIdLst");
|
|
1490
|
+
const notesMasterIdLst = el("p:notesMasterIdLst", {}, [el("p:notesMasterId", { "r:id": relId })]);
|
|
1491
|
+
presentationElement.children.splice(sldMasterIdLstIndex === -1 ? 0 : sldMasterIdLstIndex + 1, 0, notesMasterIdLst);
|
|
1492
|
+
return NOTES_MASTER_PART_PATH;
|
|
1493
|
+
}
|
|
1457
1494
|
function createEmptyPptxPackage() {
|
|
1458
1495
|
const contentTypes = el("Types", { xmlns: CONTENT_TYPES_NS }, [
|
|
1459
1496
|
el("Default", {
|
|
@@ -1535,6 +1572,9 @@ function createEmptyPptxPackage() {
|
|
|
1535
1572
|
})]), el("p:sldIdLst"), el("p:sldSz", {
|
|
1536
1573
|
cx: DEFAULT_SLIDE_WIDTH_EMU,
|
|
1537
1574
|
cy: DEFAULT_SLIDE_HEIGHT_EMU
|
|
1575
|
+
}), el("p:notesSz", {
|
|
1576
|
+
cx: DEFAULT_NOTES_WIDTH_EMU,
|
|
1577
|
+
cy: DEFAULT_NOTES_HEIGHT_EMU
|
|
1538
1578
|
}));
|
|
1539
1579
|
return pkg;
|
|
1540
1580
|
}
|
|
@@ -1757,7 +1797,7 @@ function nextIdIn(root) {
|
|
|
1757
1797
|
const NOTES_SLIDE_RELATIONSHIP_TYPE = "http://schemas.openxmlformats.org/officeDocument/2006/relationships/notesSlide";
|
|
1758
1798
|
const NOTES_SLIDE_CONTENT_TYPE = "application/vnd.openxmlformats-officedocument.presentationml.notesSlide+xml";
|
|
1759
1799
|
function buildMinimalNotesSlide(text) {
|
|
1760
|
-
|
|
1800
|
+
const body = el("p:sp", {}, [
|
|
1761
1801
|
el("p:nvSpPr", {}, [
|
|
1762
1802
|
el("p:cNvPr", {
|
|
1763
1803
|
id: "2",
|
|
@@ -1769,13 +1809,25 @@ function buildMinimalNotesSlide(text) {
|
|
|
1769
1809
|
idx: "1"
|
|
1770
1810
|
})])
|
|
1771
1811
|
]),
|
|
1772
|
-
el("p:spPr"
|
|
1812
|
+
el("p:spPr", {}, [el("a:xfrm", {}, [el("a:off", {
|
|
1813
|
+
x: "685800",
|
|
1814
|
+
y: "4400550"
|
|
1815
|
+
}), el("a:ext", {
|
|
1816
|
+
cx: "5486400",
|
|
1817
|
+
cy: "4200525"
|
|
1818
|
+
})])]),
|
|
1773
1819
|
el("p:txBody", {}, [
|
|
1774
1820
|
el("a:bodyPr"),
|
|
1775
1821
|
el("a:lstStyle"),
|
|
1776
1822
|
el("a:p", {}, [el("a:r", {}, [el("a:t", {}, [txt(encodeXmlText(text))])])])
|
|
1777
1823
|
])
|
|
1778
|
-
])
|
|
1824
|
+
]);
|
|
1825
|
+
const spTree = buildEmptyGroupSpTree();
|
|
1826
|
+
spTree.children.push(body);
|
|
1827
|
+
return el("p:notes", {
|
|
1828
|
+
"xmlns:p": PML_NS,
|
|
1829
|
+
"xmlns:a": DML_NS
|
|
1830
|
+
}, [el("p:cSld", {}, [spTree]), el("p:clrMapOvr", {}, [el("a:masterClrMapping")])]);
|
|
1779
1831
|
}
|
|
1780
1832
|
var PptxSlide = class {
|
|
1781
1833
|
container;
|
|
@@ -1826,12 +1878,17 @@ var PptxSlide = class {
|
|
|
1826
1878
|
const existingRel = [...(0, ooxml_js.resolveRelationships)(pkg, slidePartPath).values()].find((r) => r.type === NOTES_SLIDE_RELATIONSHIP_TYPE);
|
|
1827
1879
|
let notesPartPath;
|
|
1828
1880
|
if (existingRel === void 0) {
|
|
1829
|
-
notesPartPath = `ppt/notesSlides/notesSlide${Object.keys(pkg.parts).filter((p) =>
|
|
1881
|
+
notesPartPath = `ppt/notesSlides/notesSlide${Object.keys(pkg.parts).filter((p) => /^ppt\/notesSlides\/notesSlide\d+\.xml$/.test(p)).length + 1}.xml`;
|
|
1830
1882
|
ensureContentTypeOverride(pkg, notesPartPath, NOTES_SLIDE_CONTENT_TYPE);
|
|
1831
1883
|
addRelationship(pkg, slidePartPath, {
|
|
1832
1884
|
type: NOTES_SLIDE_RELATIONSHIP_TYPE,
|
|
1833
1885
|
target: buildRelativeTarget(slidePartPath, notesPartPath)
|
|
1834
1886
|
});
|
|
1887
|
+
const notesMasterPartPath = ensureNotesMaster(pkg);
|
|
1888
|
+
addRelationship(pkg, notesPartPath, {
|
|
1889
|
+
type: NOTES_MASTER_REL_TYPE,
|
|
1890
|
+
target: buildRelativeTarget(notesPartPath, notesMasterPartPath)
|
|
1891
|
+
});
|
|
1835
1892
|
} else notesPartPath = existingRel.target;
|
|
1836
1893
|
pkg.parts[notesPartPath] = {
|
|
1837
1894
|
kind: "xml",
|
|
@@ -5737,419 +5794,91 @@ function runContentStream(bytes, resources, initialState, context, items, depth)
|
|
|
5737
5794
|
}
|
|
5738
5795
|
}
|
|
5739
5796
|
//#endregion
|
|
5740
|
-
//#region src/
|
|
5741
|
-
const
|
|
5742
|
-
|
|
5743
|
-
|
|
5744
|
-
|
|
5745
|
-
|
|
5746
|
-
|
|
5747
|
-
|
|
5748
|
-
|
|
5749
|
-
|
|
5750
|
-
|
|
5751
|
-
|
|
5797
|
+
//#region src/image/png-decode.ts
|
|
5798
|
+
const PNG_SIGNATURE = [
|
|
5799
|
+
137,
|
|
5800
|
+
80,
|
|
5801
|
+
78,
|
|
5802
|
+
71,
|
|
5803
|
+
13,
|
|
5804
|
+
10,
|
|
5805
|
+
26,
|
|
5806
|
+
10
|
|
5807
|
+
];
|
|
5808
|
+
function requireDataView(bytes) {
|
|
5809
|
+
return new DataView(bytes.buffer, bytes.byteOffset, bytes.byteLength);
|
|
5752
5810
|
}
|
|
5753
|
-
function
|
|
5754
|
-
const
|
|
5755
|
-
const
|
|
5756
|
-
|
|
5757
|
-
|
|
5758
|
-
|
|
5759
|
-
|
|
5760
|
-
|
|
5761
|
-
|
|
5762
|
-
|
|
5763
|
-
|
|
5764
|
-
|
|
5765
|
-
|
|
5766
|
-
|
|
5767
|
-
|
|
5768
|
-
|
|
5769
|
-
|
|
5770
|
-
|
|
5771
|
-
|
|
5772
|
-
|
|
5773
|
-
|
|
5774
|
-
}
|
|
5811
|
+
function readChunks(bytes, onWarning) {
|
|
5812
|
+
const chunks = [];
|
|
5813
|
+
const view = requireDataView(bytes);
|
|
5814
|
+
let offset = PNG_SIGNATURE.length;
|
|
5815
|
+
while (offset + 8 <= bytes.length) {
|
|
5816
|
+
const length = view.getUint32(offset);
|
|
5817
|
+
const typeBytes = bytes.subarray(offset + 4, offset + 8);
|
|
5818
|
+
const type = new TextDecoder("latin1").decode(typeBytes);
|
|
5819
|
+
const dataStart = offset + 8;
|
|
5820
|
+
const dataEnd = dataStart + length;
|
|
5821
|
+
if (dataEnd + 4 > bytes.length) throw new Error(`PNG chunk '${type}' declares a length that runs past the end of the file`);
|
|
5822
|
+
const data = bytes.subarray(dataStart, dataEnd);
|
|
5823
|
+
if (onWarning !== void 0) {
|
|
5824
|
+
if (view.getUint32(dataEnd) !== crc32(concatBytes([typeBytes, data]))) onWarning(`PNG chunk '${type}' failed its CRC32 check`);
|
|
5825
|
+
}
|
|
5826
|
+
chunks.push({
|
|
5827
|
+
type,
|
|
5828
|
+
data
|
|
5829
|
+
});
|
|
5830
|
+
offset = dataEnd + 4;
|
|
5831
|
+
if (type === "IEND") break;
|
|
5832
|
+
}
|
|
5833
|
+
return chunks;
|
|
5775
5834
|
}
|
|
5776
|
-
function
|
|
5777
|
-
const
|
|
5778
|
-
if (arr === void 0) return {
|
|
5779
|
-
llx: 0,
|
|
5780
|
-
lly: 0,
|
|
5781
|
-
urx: DEFAULT_PAGE_WIDTH_PT,
|
|
5782
|
-
ury: DEFAULT_PAGE_HEIGHT_PT
|
|
5783
|
-
};
|
|
5784
|
-
const a = asNumber(arr[0]) ?? 0;
|
|
5785
|
-
const b = asNumber(arr[1]) ?? 0;
|
|
5786
|
-
const c = asNumber(arr[2]) ?? DEFAULT_PAGE_WIDTH_PT;
|
|
5787
|
-
const d = asNumber(arr[3]) ?? DEFAULT_PAGE_HEIGHT_PT;
|
|
5835
|
+
function parseIhdr(data) {
|
|
5836
|
+
const view = requireDataView(data);
|
|
5788
5837
|
return {
|
|
5789
|
-
|
|
5790
|
-
|
|
5791
|
-
|
|
5792
|
-
|
|
5838
|
+
width: view.getUint32(0),
|
|
5839
|
+
height: view.getUint32(4),
|
|
5840
|
+
bitDepth: data[8],
|
|
5841
|
+
colorType: data[9],
|
|
5842
|
+
interlace: data[12]
|
|
5793
5843
|
};
|
|
5794
5844
|
}
|
|
5795
|
-
function
|
|
5796
|
-
if (
|
|
5797
|
-
|
|
5798
|
-
|
|
5845
|
+
function channelsForColorType(colorType) {
|
|
5846
|
+
if (colorType === 0) return 1;
|
|
5847
|
+
if (colorType === 2) return 3;
|
|
5848
|
+
if (colorType === 3) return 1;
|
|
5849
|
+
if (colorType === 4) return 2;
|
|
5850
|
+
if (colorType === 6) return 4;
|
|
5851
|
+
throw new Error(`unsupported PNG colour type: ${colorType}`);
|
|
5799
5852
|
}
|
|
5800
|
-
function
|
|
5801
|
-
|
|
5802
|
-
matrix: [
|
|
5803
|
-
0,
|
|
5804
|
-
-1,
|
|
5805
|
-
1,
|
|
5806
|
-
0,
|
|
5807
|
-
0,
|
|
5808
|
-
w
|
|
5809
|
-
],
|
|
5810
|
-
widthPt: h,
|
|
5811
|
-
heightPt: w
|
|
5812
|
-
};
|
|
5813
|
-
if (rotation === 180) return {
|
|
5814
|
-
matrix: [
|
|
5815
|
-
-1,
|
|
5816
|
-
0,
|
|
5817
|
-
0,
|
|
5818
|
-
-1,
|
|
5819
|
-
w,
|
|
5820
|
-
h
|
|
5821
|
-
],
|
|
5822
|
-
widthPt: w,
|
|
5823
|
-
heightPt: h
|
|
5824
|
-
};
|
|
5825
|
-
if (rotation === 270) return {
|
|
5826
|
-
matrix: [
|
|
5827
|
-
0,
|
|
5828
|
-
1,
|
|
5829
|
-
-1,
|
|
5830
|
-
0,
|
|
5831
|
-
h,
|
|
5832
|
-
0
|
|
5833
|
-
],
|
|
5834
|
-
widthPt: h,
|
|
5835
|
-
heightPt: w
|
|
5836
|
-
};
|
|
5837
|
-
return {
|
|
5838
|
-
matrix: [
|
|
5839
|
-
1,
|
|
5840
|
-
0,
|
|
5841
|
-
0,
|
|
5842
|
-
1,
|
|
5843
|
-
0,
|
|
5844
|
-
0
|
|
5845
|
-
],
|
|
5846
|
-
widthPt: w,
|
|
5847
|
-
heightPt: h
|
|
5848
|
-
};
|
|
5853
|
+
function filterBpp(bitDepth, channels) {
|
|
5854
|
+
return Math.max(1, Math.ceil(bitDepth * channels / 8));
|
|
5849
5855
|
}
|
|
5850
|
-
function
|
|
5851
|
-
const
|
|
5852
|
-
|
|
5853
|
-
if (
|
|
5854
|
-
|
|
5855
|
-
|
|
5856
|
-
|
|
5857
|
-
|
|
5856
|
+
function unpackRow(rowBytes, width, channels, bitDepth) {
|
|
5857
|
+
const sampleCount = width * channels;
|
|
5858
|
+
const samples = new Array(sampleCount);
|
|
5859
|
+
if (bitDepth === 8) for (let i = 0; i < sampleCount; i++) samples[i] = rowBytes[i];
|
|
5860
|
+
else if (bitDepth === 16) for (let i = 0; i < sampleCount; i++) samples[i] = rowBytes[i * 2];
|
|
5861
|
+
else {
|
|
5862
|
+
const mask = (1 << bitDepth) - 1;
|
|
5863
|
+
for (let i = 0; i < sampleCount; i++) {
|
|
5864
|
+
const bitOffset = i * bitDepth;
|
|
5865
|
+
const byteIndex = bitOffset >> 3;
|
|
5866
|
+
const shift = 8 - bitDepth - (bitOffset & 7);
|
|
5867
|
+
samples[i] = rowBytes[byteIndex] >> shift & mask;
|
|
5858
5868
|
}
|
|
5859
|
-
return concatBytes(chunks);
|
|
5860
5869
|
}
|
|
5861
|
-
return
|
|
5870
|
+
return samples;
|
|
5862
5871
|
}
|
|
5863
|
-
function
|
|
5864
|
-
|
|
5865
|
-
const mediaBox = readMediaBox(page);
|
|
5866
|
-
const rotationResult = pageRotationTransform(normalizeRotation(asNumber(dictGet(page, "Rotate"))), mediaBox.urx - mediaBox.llx, mediaBox.ury - mediaBox.lly);
|
|
5867
|
-
const pageMatrix = multiplyMatrices(translationMatrix(-mediaBox.llx, -mediaBox.lly), rotationResult.matrix);
|
|
5868
|
-
const items = [];
|
|
5869
|
-
if (resources !== void 0) {
|
|
5870
|
-
const extracted = interpretContentStream(readPageContentBytes(page, resolver, sink), resources, {
|
|
5871
|
-
fontMetrics: fontResolver.metrics,
|
|
5872
|
-
resolver,
|
|
5873
|
-
sink
|
|
5874
|
-
});
|
|
5875
|
-
for (const item of extracted) {
|
|
5876
|
-
const converted = convertExtractedItem(item, pageMatrix, fontResolver, images, imageIdCache, resolver, sink);
|
|
5877
|
-
if (converted !== void 0) items.push(converted);
|
|
5878
|
-
}
|
|
5879
|
-
} else sink({
|
|
5880
|
-
code: "pdf/object-missing-value",
|
|
5881
|
-
severity: "warning",
|
|
5882
|
-
message: "page has no /Resources dict; its content stream cannot be interpreted"
|
|
5883
|
-
});
|
|
5884
|
-
items.push(...readLinkAnnotations(page, pageMatrix, resolver));
|
|
5885
|
-
return {
|
|
5886
|
-
widthPt: rotationResult.widthPt,
|
|
5887
|
-
heightPt: rotationResult.heightPt,
|
|
5888
|
-
items
|
|
5889
|
-
};
|
|
5872
|
+
function readTrnsGrayValue(trns) {
|
|
5873
|
+
return requireDataView(trns).getUint16(0);
|
|
5890
5874
|
}
|
|
5891
|
-
function
|
|
5892
|
-
|
|
5893
|
-
|
|
5894
|
-
|
|
5895
|
-
|
|
5896
|
-
|
|
5897
|
-
|
|
5898
|
-
const font = fontResolver.resolve(item.fontResourceName, item.resources);
|
|
5899
|
-
const text = font?.decodeToUnicode(item.codes) ?? "";
|
|
5900
|
-
if (text.length === 0) return;
|
|
5901
|
-
const startTrm = multiplyMatrices(item.startMatrix, pageMatrix);
|
|
5902
|
-
const endTrm = multiplyMatrices(item.endMatrix, pageMatrix);
|
|
5903
|
-
const widthPt = Math.hypot(endTrm[4] - startTrm[4], endTrm[5] - startTrm[5]);
|
|
5904
|
-
const sizePt = matrixScaleX(startTrm);
|
|
5905
|
-
const rotationDeg = matrixRotationDegrees(startTrm);
|
|
5906
|
-
const layoutFont = {
|
|
5907
|
-
family: font?.family ?? "Helvetica",
|
|
5908
|
-
weight: font?.bold === true ? "bold" : "normal",
|
|
5909
|
-
style: font?.italic === true ? "italic" : "normal"
|
|
5910
|
-
};
|
|
5911
|
-
return {
|
|
5912
|
-
kind: "text",
|
|
5913
|
-
text,
|
|
5914
|
-
xPt: startTrm[4],
|
|
5915
|
-
yPt: startTrm[5],
|
|
5916
|
-
font: layoutFont,
|
|
5917
|
-
sizePt: sizePt > 0 ? sizePt : item.sizePt,
|
|
5918
|
-
color: item.color,
|
|
5919
|
-
widthPt,
|
|
5920
|
-
rotationDeg: rotationDeg !== 0 ? rotationDeg : void 0
|
|
5921
|
-
};
|
|
5922
|
-
}
|
|
5923
|
-
function convertRect(item, pageMatrix) {
|
|
5924
|
-
const p1 = applyMatrix(pageMatrix, {
|
|
5925
|
-
x: item.xPt,
|
|
5926
|
-
y: item.yPt
|
|
5927
|
-
});
|
|
5928
|
-
const p2 = applyMatrix(pageMatrix, {
|
|
5929
|
-
x: item.xPt + item.widthPt,
|
|
5930
|
-
y: item.yPt + item.heightPt
|
|
5931
|
-
});
|
|
5932
|
-
return {
|
|
5933
|
-
kind: "rect",
|
|
5934
|
-
xPt: Math.min(p1.x, p2.x),
|
|
5935
|
-
yPt: Math.min(p1.y, p2.y),
|
|
5936
|
-
widthPt: Math.abs(p2.x - p1.x),
|
|
5937
|
-
heightPt: Math.abs(p2.y - p1.y),
|
|
5938
|
-
fill: item.color
|
|
5939
|
-
};
|
|
5940
|
-
}
|
|
5941
|
-
function imagePlacementFrom(matrix) {
|
|
5942
|
-
const rotationDeg = matrixRotationDegrees(matrix);
|
|
5943
|
-
return {
|
|
5944
|
-
xPt: matrix[4],
|
|
5945
|
-
yPt: matrix[5],
|
|
5946
|
-
widthPt: matrixScaleX(matrix),
|
|
5947
|
-
heightPt: matrixScaleY(matrix),
|
|
5948
|
-
rotationDeg: rotationDeg !== 0 ? rotationDeg : void 0
|
|
5949
|
-
};
|
|
5950
|
-
}
|
|
5951
|
-
function registerExtractedImage(format, bytes, widthPx, heightPx, images) {
|
|
5952
|
-
const imageId = `img${crc32(bytes).toString(16)}`;
|
|
5953
|
-
if (!(imageId in images)) images[imageId] = {
|
|
5954
|
-
format,
|
|
5955
|
-
base64: (0, ooxml_js.bytesToBase64)(bytes),
|
|
5956
|
-
widthPx,
|
|
5957
|
-
heightPx
|
|
5958
|
-
};
|
|
5959
|
-
return imageId;
|
|
5960
|
-
}
|
|
5961
|
-
function resolveCachedImageId(dict, raw, images, cache, resolver, sink) {
|
|
5962
|
-
if (cache.has(dict)) return cache.get(dict) ?? void 0;
|
|
5963
|
-
const decoded = readImageXObject(dict, raw, resolver, sink);
|
|
5964
|
-
if (decoded === void 0) {
|
|
5965
|
-
cache.set(dict, null);
|
|
5966
|
-
return;
|
|
5967
|
-
}
|
|
5968
|
-
const imageId = registerExtractedImage(decoded.format, decoded.bytes, decoded.widthPx, decoded.heightPx, images);
|
|
5969
|
-
cache.set(dict, imageId);
|
|
5970
|
-
return imageId;
|
|
5971
|
-
}
|
|
5972
|
-
function convertImage(item, pageMatrix, images, cache, resolver, sink) {
|
|
5973
|
-
const xobjects = resolver.resolveDict(dictGet(item.resources, "XObject"));
|
|
5974
|
-
const xobj = xobjects !== void 0 ? resolver.resolve(dictGet(xobjects, item.resourceName)) : void 0;
|
|
5975
|
-
if (xobj?.kind !== "stream") return;
|
|
5976
|
-
const imageId = resolveCachedImageId(xobj.dict, xobj.raw, images, cache, resolver, sink);
|
|
5977
|
-
if (imageId === void 0) return;
|
|
5978
|
-
return {
|
|
5979
|
-
kind: "image",
|
|
5980
|
-
imageId,
|
|
5981
|
-
...imagePlacementFrom(multiplyMatrices(item.matrix, pageMatrix))
|
|
5982
|
-
};
|
|
5983
|
-
}
|
|
5984
|
-
function convertInlineImage(item, pageMatrix, images, resolver, sink) {
|
|
5985
|
-
const decoded = readImageXObject(item.dict, item.data, resolver, sink);
|
|
5986
|
-
if (decoded === void 0) return;
|
|
5987
|
-
return {
|
|
5988
|
-
kind: "image",
|
|
5989
|
-
imageId: registerExtractedImage(decoded.format, decoded.bytes, decoded.widthPx, decoded.heightPx, images),
|
|
5990
|
-
...imagePlacementFrom(multiplyMatrices(item.matrix, pageMatrix))
|
|
5991
|
-
};
|
|
5992
|
-
}
|
|
5993
|
-
function readLinkAnnotations(page, pageMatrix, resolver) {
|
|
5994
|
-
const annotsArr = asArray(dictGet(page, "Annots"));
|
|
5995
|
-
if (annotsArr === void 0) return [];
|
|
5996
|
-
const links = [];
|
|
5997
|
-
for (const annotRef of annotsArr) {
|
|
5998
|
-
const annot = resolver.resolveDict(annotRef);
|
|
5999
|
-
if (annot === void 0 || asName(dictGet(annot, "Subtype")) !== "Link") continue;
|
|
6000
|
-
const uri = readLinkUri(annot, resolver);
|
|
6001
|
-
const rectArr = asArray(dictGet(annot, "Rect"));
|
|
6002
|
-
if (uri === void 0 || rectArr === void 0) continue;
|
|
6003
|
-
const x1 = asNumber(rectArr[0]) ?? 0;
|
|
6004
|
-
const y1 = asNumber(rectArr[1]) ?? 0;
|
|
6005
|
-
const x2 = asNumber(rectArr[2]) ?? 0;
|
|
6006
|
-
const y2 = asNumber(rectArr[3]) ?? 0;
|
|
6007
|
-
const p1 = applyMatrix(pageMatrix, {
|
|
6008
|
-
x: Math.min(x1, x2),
|
|
6009
|
-
y: Math.min(y1, y2)
|
|
6010
|
-
});
|
|
6011
|
-
const p2 = applyMatrix(pageMatrix, {
|
|
6012
|
-
x: Math.max(x1, x2),
|
|
6013
|
-
y: Math.max(y1, y2)
|
|
6014
|
-
});
|
|
6015
|
-
links.push({
|
|
6016
|
-
kind: "link",
|
|
6017
|
-
uri,
|
|
6018
|
-
xPt: Math.min(p1.x, p2.x),
|
|
6019
|
-
yPt: Math.min(p1.y, p2.y),
|
|
6020
|
-
widthPt: Math.abs(p2.x - p1.x),
|
|
6021
|
-
heightPt: Math.abs(p2.y - p1.y)
|
|
6022
|
-
});
|
|
6023
|
-
}
|
|
6024
|
-
return links;
|
|
6025
|
-
}
|
|
6026
|
-
function readLinkUri(annot, resolver) {
|
|
6027
|
-
const action = resolver.resolveDict(dictGet(annot, "A"));
|
|
6028
|
-
if (action === void 0 || asName(dictGet(action, "S")) !== "URI") return;
|
|
6029
|
-
const uriObj = dictGet(action, "URI");
|
|
6030
|
-
return uriObj?.kind === "string" ? decodePdfString(uriObj.bytes) : void 0;
|
|
6031
|
-
}
|
|
6032
|
-
function decodePdfString(bytes) {
|
|
6033
|
-
if (bytes.length >= 2 && bytes[0] === 254 && bytes[1] === 255) {
|
|
6034
|
-
let out = "";
|
|
6035
|
-
for (let i = 2; i + 1 < bytes.length; i += 2) out += String.fromCharCode((bytes[i] ?? 0) << 8 | (bytes[i + 1] ?? 0));
|
|
6036
|
-
return out;
|
|
6037
|
-
}
|
|
6038
|
-
return Array.from(bytes, (b) => String.fromCharCode(b)).join("");
|
|
6039
|
-
}
|
|
6040
|
-
const PDF_DATE_PATTERN = /^D:(\d{4})(\d{2})?(\d{2})?(\d{2})?(\d{2})?(\d{2})?([+\-Z])?(\d{2})?'?(\d{2})?'?$/;
|
|
6041
|
-
function parsePdfDate(raw) {
|
|
6042
|
-
if (raw === void 0) return;
|
|
6043
|
-
const match = PDF_DATE_PATTERN.exec(raw);
|
|
6044
|
-
if (match === null) return;
|
|
6045
|
-
const [, year, month = "01", day = "01", hour = "00", minute = "00", second = "00", tzSign, tzHour = "00", tzMinute = "00"] = match;
|
|
6046
|
-
return `${year}-${month}-${day}T${hour}:${minute}:${second}${tzSign === void 0 || tzSign === "Z" ? "Z" : `${tzSign}${tzHour}:${tzMinute}`}`;
|
|
6047
|
-
}
|
|
6048
|
-
function readMetadata(trailer, resolver) {
|
|
6049
|
-
const info = resolver.resolveDict(dictGet(trailer, "Info"));
|
|
6050
|
-
if (info === void 0) return {};
|
|
6051
|
-
const stringField = (key) => {
|
|
6052
|
-
const obj = dictGet(info, key);
|
|
6053
|
-
return obj?.kind === "string" ? decodePdfString(obj.bytes) : void 0;
|
|
6054
|
-
};
|
|
6055
|
-
const keywords = stringField("Keywords")?.split(",").map((k) => k.trim()).filter((k) => k.length > 0);
|
|
6056
|
-
return {
|
|
6057
|
-
title: stringField("Title"),
|
|
6058
|
-
author: stringField("Author"),
|
|
6059
|
-
subject: stringField("Subject"),
|
|
6060
|
-
keywords: keywords !== void 0 && keywords.length > 0 ? keywords : void 0,
|
|
6061
|
-
creator: stringField("Creator"),
|
|
6062
|
-
producer: stringField("Producer"),
|
|
6063
|
-
createdIso: parsePdfDate(stringField("CreationDate")),
|
|
6064
|
-
modifiedIso: parsePdfDate(stringField("ModDate"))
|
|
6065
|
-
};
|
|
6066
|
-
}
|
|
6067
|
-
//#endregion
|
|
6068
|
-
//#region src/image/png-decode.ts
|
|
6069
|
-
const PNG_SIGNATURE = [
|
|
6070
|
-
137,
|
|
6071
|
-
80,
|
|
6072
|
-
78,
|
|
6073
|
-
71,
|
|
6074
|
-
13,
|
|
6075
|
-
10,
|
|
6076
|
-
26,
|
|
6077
|
-
10
|
|
6078
|
-
];
|
|
6079
|
-
function requireDataView(bytes) {
|
|
6080
|
-
return new DataView(bytes.buffer, bytes.byteOffset, bytes.byteLength);
|
|
6081
|
-
}
|
|
6082
|
-
function readChunks(bytes, onWarning) {
|
|
6083
|
-
const chunks = [];
|
|
6084
|
-
const view = requireDataView(bytes);
|
|
6085
|
-
let offset = PNG_SIGNATURE.length;
|
|
6086
|
-
while (offset + 8 <= bytes.length) {
|
|
6087
|
-
const length = view.getUint32(offset);
|
|
6088
|
-
const typeBytes = bytes.subarray(offset + 4, offset + 8);
|
|
6089
|
-
const type = new TextDecoder("latin1").decode(typeBytes);
|
|
6090
|
-
const dataStart = offset + 8;
|
|
6091
|
-
const dataEnd = dataStart + length;
|
|
6092
|
-
if (dataEnd + 4 > bytes.length) throw new Error(`PNG chunk '${type}' declares a length that runs past the end of the file`);
|
|
6093
|
-
const data = bytes.subarray(dataStart, dataEnd);
|
|
6094
|
-
if (onWarning !== void 0) {
|
|
6095
|
-
if (view.getUint32(dataEnd) !== crc32(concatBytes([typeBytes, data]))) onWarning(`PNG chunk '${type}' failed its CRC32 check`);
|
|
6096
|
-
}
|
|
6097
|
-
chunks.push({
|
|
6098
|
-
type,
|
|
6099
|
-
data
|
|
6100
|
-
});
|
|
6101
|
-
offset = dataEnd + 4;
|
|
6102
|
-
if (type === "IEND") break;
|
|
6103
|
-
}
|
|
6104
|
-
return chunks;
|
|
6105
|
-
}
|
|
6106
|
-
function parseIhdr(data) {
|
|
6107
|
-
const view = requireDataView(data);
|
|
6108
|
-
return {
|
|
6109
|
-
width: view.getUint32(0),
|
|
6110
|
-
height: view.getUint32(4),
|
|
6111
|
-
bitDepth: data[8],
|
|
6112
|
-
colorType: data[9],
|
|
6113
|
-
interlace: data[12]
|
|
6114
|
-
};
|
|
6115
|
-
}
|
|
6116
|
-
function channelsForColorType(colorType) {
|
|
6117
|
-
if (colorType === 0) return 1;
|
|
6118
|
-
if (colorType === 2) return 3;
|
|
6119
|
-
if (colorType === 3) return 1;
|
|
6120
|
-
if (colorType === 4) return 2;
|
|
6121
|
-
if (colorType === 6) return 4;
|
|
6122
|
-
throw new Error(`unsupported PNG colour type: ${colorType}`);
|
|
6123
|
-
}
|
|
6124
|
-
function filterBpp(bitDepth, channels) {
|
|
6125
|
-
return Math.max(1, Math.ceil(bitDepth * channels / 8));
|
|
6126
|
-
}
|
|
6127
|
-
function unpackRow(rowBytes, width, channels, bitDepth) {
|
|
6128
|
-
const sampleCount = width * channels;
|
|
6129
|
-
const samples = new Array(sampleCount);
|
|
6130
|
-
if (bitDepth === 8) for (let i = 0; i < sampleCount; i++) samples[i] = rowBytes[i];
|
|
6131
|
-
else if (bitDepth === 16) for (let i = 0; i < sampleCount; i++) samples[i] = rowBytes[i * 2];
|
|
6132
|
-
else {
|
|
6133
|
-
const mask = (1 << bitDepth) - 1;
|
|
6134
|
-
for (let i = 0; i < sampleCount; i++) {
|
|
6135
|
-
const bitOffset = i * bitDepth;
|
|
6136
|
-
const byteIndex = bitOffset >> 3;
|
|
6137
|
-
const shift = 8 - bitDepth - (bitOffset & 7);
|
|
6138
|
-
samples[i] = rowBytes[byteIndex] >> shift & mask;
|
|
6139
|
-
}
|
|
6140
|
-
}
|
|
6141
|
-
return samples;
|
|
6142
|
-
}
|
|
6143
|
-
function readTrnsGrayValue(trns) {
|
|
6144
|
-
return requireDataView(trns).getUint16(0);
|
|
6145
|
-
}
|
|
6146
|
-
function readTrnsRgbKey(trns) {
|
|
6147
|
-
const view = requireDataView(trns);
|
|
6148
|
-
return [
|
|
6149
|
-
view.getUint16(0),
|
|
6150
|
-
view.getUint16(2),
|
|
6151
|
-
view.getUint16(4)
|
|
6152
|
-
];
|
|
5875
|
+
function readTrnsRgbKey(trns) {
|
|
5876
|
+
const view = requireDataView(trns);
|
|
5877
|
+
return [
|
|
5878
|
+
view.getUint16(0),
|
|
5879
|
+
view.getUint16(2),
|
|
5880
|
+
view.getUint16(4)
|
|
5881
|
+
];
|
|
6153
5882
|
}
|
|
6154
5883
|
function scaleToByte(sample, bitDepth) {
|
|
6155
5884
|
if (bitDepth === 16) return sample;
|
|
@@ -6616,175 +6345,535 @@ function buildLinkAnnotDict(link) {
|
|
|
6616
6345
|
})
|
|
6617
6346
|
});
|
|
6618
6347
|
}
|
|
6619
|
-
function isLinkItem(item) {
|
|
6620
|
-
return item.kind === "link";
|
|
6348
|
+
function isLinkItem(item) {
|
|
6349
|
+
return item.kind === "link";
|
|
6350
|
+
}
|
|
6351
|
+
const NOTES_ANNOTATION_HIDDEN_FLAG = 2;
|
|
6352
|
+
const NOTES_ANNOTATION_AUTHOR = "documents.js:notes";
|
|
6353
|
+
function buildNotesAnnotDict(notes) {
|
|
6354
|
+
return pdfDict({
|
|
6355
|
+
Type: pdfName("Annot"),
|
|
6356
|
+
Subtype: pdfName("Text"),
|
|
6357
|
+
Rect: pdfArray([
|
|
6358
|
+
0,
|
|
6359
|
+
0,
|
|
6360
|
+
0,
|
|
6361
|
+
0
|
|
6362
|
+
].map((n) => pdfNum(n))),
|
|
6363
|
+
Contents: textToPdfString(notes),
|
|
6364
|
+
T: textToPdfString(NOTES_ANNOTATION_AUTHOR),
|
|
6365
|
+
F: pdfNum(NOTES_ANNOTATION_HIDDEN_FLAG)
|
|
6366
|
+
});
|
|
6367
|
+
}
|
|
6368
|
+
function xrefEntry(offset, generation, inUse) {
|
|
6369
|
+
return `${offset.toString().padStart(10, "0")} ${generation.toString().padStart(5, "0")} ${inUse ? "n" : "f"} \n`;
|
|
6370
|
+
}
|
|
6371
|
+
function writePdf(doc, options = {}) {
|
|
6372
|
+
const compress = options.compress ?? true;
|
|
6373
|
+
const measurer = createStandardFontMeasurer();
|
|
6374
|
+
let nextObjNum = 1;
|
|
6375
|
+
const catalogNum = nextObjNum++;
|
|
6376
|
+
const pagesNum = nextObjNum++;
|
|
6377
|
+
const infoNum = nextObjNum++;
|
|
6378
|
+
const fontNames = /* @__PURE__ */ new Set();
|
|
6379
|
+
const imageIds = /* @__PURE__ */ new Set();
|
|
6380
|
+
for (const page of doc.pages) for (const item of page.items) if (item.kind === "text") fontNames.add(resolveStandardFont(item.font.family, item.font.weight === "bold", item.font.style === "italic").standardName);
|
|
6381
|
+
else if (item.kind === "image") imageIds.add(item.imageId);
|
|
6382
|
+
const fontAllocs = /* @__PURE__ */ new Map();
|
|
6383
|
+
for (const [index, name] of [...fontNames].sort().entries()) {
|
|
6384
|
+
const fontNum = nextObjNum++;
|
|
6385
|
+
const descNum = nextObjNum++;
|
|
6386
|
+
fontAllocs.set(name, {
|
|
6387
|
+
fontNum,
|
|
6388
|
+
descNum,
|
|
6389
|
+
resourceName: `F${index + 1}`
|
|
6390
|
+
});
|
|
6391
|
+
}
|
|
6392
|
+
const imageAllocs = /* @__PURE__ */ new Map();
|
|
6393
|
+
for (const [index, imageId] of [...imageIds].sort().entries()) {
|
|
6394
|
+
const asset = doc.images[imageId];
|
|
6395
|
+
if (asset === void 0) throw new Error(`LayoutDocument references image "${imageId}" but it is not present in images`);
|
|
6396
|
+
const prepared = prepareImage(asset, compress);
|
|
6397
|
+
const imageNum = nextObjNum++;
|
|
6398
|
+
const smaskNum = prepared.alpha === void 0 ? void 0 : nextObjNum++;
|
|
6399
|
+
imageAllocs.set(imageId, {
|
|
6400
|
+
imageNum,
|
|
6401
|
+
smaskNum,
|
|
6402
|
+
resourceName: `Im${index + 1}`,
|
|
6403
|
+
prepared
|
|
6404
|
+
});
|
|
6405
|
+
}
|
|
6406
|
+
const pageAllocs = doc.pages.map(() => ({
|
|
6407
|
+
pageNum: nextObjNum++,
|
|
6408
|
+
contentsNum: nextObjNum++
|
|
6409
|
+
}));
|
|
6410
|
+
const objects = [];
|
|
6411
|
+
objects.push({
|
|
6412
|
+
num: catalogNum,
|
|
6413
|
+
value: pdfDict({
|
|
6414
|
+
Type: pdfName("Catalog"),
|
|
6415
|
+
Pages: pdfRef(pagesNum, 0)
|
|
6416
|
+
})
|
|
6417
|
+
});
|
|
6418
|
+
objects.push({
|
|
6419
|
+
num: pagesNum,
|
|
6420
|
+
value: pdfDict({
|
|
6421
|
+
Type: pdfName("Pages"),
|
|
6422
|
+
Kids: pdfArray(pageAllocs.map((p) => pdfRef(p.pageNum, 0))),
|
|
6423
|
+
Count: pdfNum(doc.pages.length)
|
|
6424
|
+
})
|
|
6425
|
+
});
|
|
6426
|
+
objects.push({
|
|
6427
|
+
num: infoNum,
|
|
6428
|
+
value: buildInfoDict(doc)
|
|
6429
|
+
});
|
|
6430
|
+
for (const [standardName, alloc] of fontAllocs) {
|
|
6431
|
+
const { font, descriptor } = buildFontObjects(standardName, pdfRef(alloc.descNum, 0));
|
|
6432
|
+
objects.push({
|
|
6433
|
+
num: alloc.fontNum,
|
|
6434
|
+
value: font
|
|
6435
|
+
});
|
|
6436
|
+
objects.push({
|
|
6437
|
+
num: alloc.descNum,
|
|
6438
|
+
value: descriptor
|
|
6439
|
+
});
|
|
6440
|
+
}
|
|
6441
|
+
for (const alloc of imageAllocs.values()) {
|
|
6442
|
+
if (alloc.smaskNum !== void 0 && alloc.prepared.alpha !== void 0) {
|
|
6443
|
+
alloc.prepared.dict.entries.set("SMask", pdfRef(alloc.smaskNum, 0));
|
|
6444
|
+
objects.push({
|
|
6445
|
+
num: alloc.smaskNum,
|
|
6446
|
+
value: pdfStream(alloc.prepared.alpha.dict, alloc.prepared.alpha.raw)
|
|
6447
|
+
});
|
|
6448
|
+
}
|
|
6449
|
+
objects.push({
|
|
6450
|
+
num: alloc.imageNum,
|
|
6451
|
+
value: pdfStream(alloc.prepared.dict, alloc.prepared.raw)
|
|
6452
|
+
});
|
|
6453
|
+
}
|
|
6454
|
+
const resourceEntries = /* @__PURE__ */ new Map();
|
|
6455
|
+
if (fontAllocs.size > 0) resourceEntries.set("Font", pdfDict(new Map([...fontAllocs.values()].map((alloc) => [alloc.resourceName, pdfRef(alloc.fontNum, 0)]))));
|
|
6456
|
+
if (imageAllocs.size > 0) resourceEntries.set("XObject", pdfDict(new Map([...imageAllocs.values()].map((alloc) => [alloc.resourceName, pdfRef(alloc.imageNum, 0)]))));
|
|
6457
|
+
const resourcesDict = pdfDict(resourceEntries);
|
|
6458
|
+
const context = {
|
|
6459
|
+
measurer,
|
|
6460
|
+
resolveFont: (font) => {
|
|
6461
|
+
const standardName = resolveStandardFont(font.family, font.weight === "bold", font.style === "italic").standardName;
|
|
6462
|
+
const alloc = fontAllocs.get(standardName);
|
|
6463
|
+
if (alloc === void 0) throw new Error(`font "${standardName}" was not pre-allocated -- this is a writePdf internal invariant violation`);
|
|
6464
|
+
return {
|
|
6465
|
+
resourceName: alloc.resourceName,
|
|
6466
|
+
standardName
|
|
6467
|
+
};
|
|
6468
|
+
},
|
|
6469
|
+
resolveImage: (imageId) => {
|
|
6470
|
+
const alloc = imageAllocs.get(imageId);
|
|
6471
|
+
if (alloc === void 0) throw new Error(`image "${imageId}" was not pre-allocated -- this is a writePdf internal invariant violation`);
|
|
6472
|
+
return { resourceName: alloc.resourceName };
|
|
6473
|
+
}
|
|
6474
|
+
};
|
|
6475
|
+
doc.pages.forEach((page, pageIndex) => {
|
|
6476
|
+
throwIfAborted(options.signal);
|
|
6477
|
+
const { pageNum, contentsNum } = pageAllocs[pageIndex];
|
|
6478
|
+
const { bytes: contentBytes, substitutions } = writeContentStream(page.items, context);
|
|
6479
|
+
for (const substitution of substitutions) options.onSubstitution?.(substitution, { pageIndex });
|
|
6480
|
+
const finalContentBytes = compress ? deflate(contentBytes) : contentBytes;
|
|
6481
|
+
const contentsDict = pdfDict(compress ? { Filter: pdfName("FlateDecode") } : {});
|
|
6482
|
+
objects.push({
|
|
6483
|
+
num: contentsNum,
|
|
6484
|
+
value: pdfStream(contentsDict, finalContentBytes)
|
|
6485
|
+
});
|
|
6486
|
+
const annots = page.items.filter(isLinkItem).map((link) => buildLinkAnnotDict(link));
|
|
6487
|
+
if (page.notes !== void 0 && page.notes.length > 0) annots.push(buildNotesAnnotDict(page.notes));
|
|
6488
|
+
const pageEntries = /* @__PURE__ */ new Map([
|
|
6489
|
+
["Type", pdfName("Page")],
|
|
6490
|
+
["Parent", pdfRef(pagesNum, 0)],
|
|
6491
|
+
["MediaBox", pdfArray([
|
|
6492
|
+
0,
|
|
6493
|
+
0,
|
|
6494
|
+
page.widthPt,
|
|
6495
|
+
page.heightPt
|
|
6496
|
+
].map((n) => pdfNum(n)))],
|
|
6497
|
+
["Resources", resourcesDict],
|
|
6498
|
+
["Contents", pdfRef(contentsNum, 0)]
|
|
6499
|
+
]);
|
|
6500
|
+
if (annots.length > 0) pageEntries.set("Annots", pdfArray(annots));
|
|
6501
|
+
objects.push({
|
|
6502
|
+
num: pageNum,
|
|
6503
|
+
value: pdfDict(pageEntries)
|
|
6504
|
+
});
|
|
6505
|
+
});
|
|
6506
|
+
const writer = new ByteWriter();
|
|
6507
|
+
writer.writeAscii("%PDF-1.7\n");
|
|
6508
|
+
const offsets = /* @__PURE__ */ new Map();
|
|
6509
|
+
for (const { num, value } of objects) {
|
|
6510
|
+
offsets.set(num, writer.length);
|
|
6511
|
+
writer.writeAscii(`${num} 0 obj\n`);
|
|
6512
|
+
writeObject(writer, value);
|
|
6513
|
+
writer.writeAscii("\nendobj\n");
|
|
6514
|
+
}
|
|
6515
|
+
const maxObjNum = nextObjNum - 1;
|
|
6516
|
+
const xrefOffset = writer.length;
|
|
6517
|
+
writer.writeAscii("xref\n");
|
|
6518
|
+
writer.writeAscii(`0 ${maxObjNum + 1}\n`);
|
|
6519
|
+
writer.writeAscii(xrefEntry(0, 65535, false));
|
|
6520
|
+
for (let num = 1; num <= maxObjNum; num++) {
|
|
6521
|
+
const offset = offsets.get(num);
|
|
6522
|
+
if (offset === void 0) throw new Error(`object ${num} was allocated but never written -- this is a writePdf internal invariant violation`);
|
|
6523
|
+
writer.writeAscii(xrefEntry(offset, 0, true));
|
|
6524
|
+
}
|
|
6525
|
+
writer.writeAscii("trailer\n");
|
|
6526
|
+
writeObject(writer, pdfDict({
|
|
6527
|
+
Size: pdfNum(maxObjNum + 1),
|
|
6528
|
+
Root: pdfRef(catalogNum, 0),
|
|
6529
|
+
Info: pdfRef(infoNum, 0)
|
|
6530
|
+
}));
|
|
6531
|
+
writer.writeAscii("\nstartxref\n");
|
|
6532
|
+
writer.writeAscii(`${xrefOffset}\n`);
|
|
6533
|
+
writer.writeAscii("%%EOF");
|
|
6534
|
+
return writer.toBytes();
|
|
6535
|
+
}
|
|
6536
|
+
//#endregion
|
|
6537
|
+
//#region src/pdf/read.ts
|
|
6538
|
+
const PDF_HEADER_BYTES = new TextEncoder().encode("%PDF-");
|
|
6539
|
+
const HEADER_SEARCH_WINDOW = 1024;
|
|
6540
|
+
const DEFAULT_PAGE_WIDTH_PT = 612;
|
|
6541
|
+
const DEFAULT_PAGE_HEIGHT_PT = 792;
|
|
6542
|
+
function hasPdfHeader(bytes) {
|
|
6543
|
+
const window = bytes.subarray(0, Math.min(HEADER_SEARCH_WINDOW, bytes.length));
|
|
6544
|
+
outer: for (let i = 0; i <= window.length - PDF_HEADER_BYTES.length; i++) {
|
|
6545
|
+
for (let j = 0; j < PDF_HEADER_BYTES.length; j++) if (window[i + j] !== PDF_HEADER_BYTES[j]) continue outer;
|
|
6546
|
+
return true;
|
|
6547
|
+
}
|
|
6548
|
+
return false;
|
|
6549
|
+
}
|
|
6550
|
+
function readPdf(bytes, options) {
|
|
6551
|
+
const sink = options?.sink ?? NOOP_DIAGNOSTIC_SINK;
|
|
6552
|
+
const signal = options?.signal;
|
|
6553
|
+
if (!hasPdfHeader(bytes)) throw new PdfParseError("pdf/no-header", "no \"%PDF-\" header found within the first bytes of the file; this does not look like a PDF at all");
|
|
6554
|
+
const doc = openPdfDocument(bytes, sink);
|
|
6555
|
+
const resolver = doc;
|
|
6556
|
+
const fontResolver = createFontResolver({
|
|
6557
|
+
resolver,
|
|
6558
|
+
sink
|
|
6559
|
+
});
|
|
6560
|
+
const images = {};
|
|
6561
|
+
const imageIdCache = /* @__PURE__ */ new Map();
|
|
6562
|
+
const pages = doc.pages().map((pageDict) => {
|
|
6563
|
+
throwIfAborted(signal);
|
|
6564
|
+
return readPage(pageDict, resolver, fontResolver, images, imageIdCache, sink);
|
|
6565
|
+
});
|
|
6566
|
+
return {
|
|
6567
|
+
formatVersion: 1,
|
|
6568
|
+
metadata: readMetadata(doc.trailer, resolver),
|
|
6569
|
+
pages,
|
|
6570
|
+
images
|
|
6571
|
+
};
|
|
6572
|
+
}
|
|
6573
|
+
function readMediaBox(page) {
|
|
6574
|
+
const arr = asArray(dictGet(page, "MediaBox"));
|
|
6575
|
+
if (arr === void 0) return {
|
|
6576
|
+
llx: 0,
|
|
6577
|
+
lly: 0,
|
|
6578
|
+
urx: DEFAULT_PAGE_WIDTH_PT,
|
|
6579
|
+
ury: DEFAULT_PAGE_HEIGHT_PT
|
|
6580
|
+
};
|
|
6581
|
+
const a = asNumber(arr[0]) ?? 0;
|
|
6582
|
+
const b = asNumber(arr[1]) ?? 0;
|
|
6583
|
+
const c = asNumber(arr[2]) ?? DEFAULT_PAGE_WIDTH_PT;
|
|
6584
|
+
const d = asNumber(arr[3]) ?? DEFAULT_PAGE_HEIGHT_PT;
|
|
6585
|
+
return {
|
|
6586
|
+
llx: Math.min(a, c),
|
|
6587
|
+
lly: Math.min(b, d),
|
|
6588
|
+
urx: Math.max(a, c),
|
|
6589
|
+
ury: Math.max(b, d)
|
|
6590
|
+
};
|
|
6591
|
+
}
|
|
6592
|
+
function normalizeRotation(rotate) {
|
|
6593
|
+
if (rotate === void 0) return 0;
|
|
6594
|
+
const normalized = (Math.round(rotate / 90) * 90 % 360 + 360) % 360;
|
|
6595
|
+
return normalized === 90 || normalized === 180 || normalized === 270 ? normalized : 0;
|
|
6596
|
+
}
|
|
6597
|
+
function pageRotationTransform(rotation, w, h) {
|
|
6598
|
+
if (rotation === 90) return {
|
|
6599
|
+
matrix: [
|
|
6600
|
+
0,
|
|
6601
|
+
-1,
|
|
6602
|
+
1,
|
|
6603
|
+
0,
|
|
6604
|
+
0,
|
|
6605
|
+
w
|
|
6606
|
+
],
|
|
6607
|
+
widthPt: h,
|
|
6608
|
+
heightPt: w
|
|
6609
|
+
};
|
|
6610
|
+
if (rotation === 180) return {
|
|
6611
|
+
matrix: [
|
|
6612
|
+
-1,
|
|
6613
|
+
0,
|
|
6614
|
+
0,
|
|
6615
|
+
-1,
|
|
6616
|
+
w,
|
|
6617
|
+
h
|
|
6618
|
+
],
|
|
6619
|
+
widthPt: w,
|
|
6620
|
+
heightPt: h
|
|
6621
|
+
};
|
|
6622
|
+
if (rotation === 270) return {
|
|
6623
|
+
matrix: [
|
|
6624
|
+
0,
|
|
6625
|
+
1,
|
|
6626
|
+
-1,
|
|
6627
|
+
0,
|
|
6628
|
+
h,
|
|
6629
|
+
0
|
|
6630
|
+
],
|
|
6631
|
+
widthPt: h,
|
|
6632
|
+
heightPt: w
|
|
6633
|
+
};
|
|
6634
|
+
return {
|
|
6635
|
+
matrix: [
|
|
6636
|
+
1,
|
|
6637
|
+
0,
|
|
6638
|
+
0,
|
|
6639
|
+
1,
|
|
6640
|
+
0,
|
|
6641
|
+
0
|
|
6642
|
+
],
|
|
6643
|
+
widthPt: w,
|
|
6644
|
+
heightPt: h
|
|
6645
|
+
};
|
|
6646
|
+
}
|
|
6647
|
+
function readPageContentBytes(page, resolver, sink) {
|
|
6648
|
+
const contentsObj = resolver.resolve(dictGet(page, "Contents"));
|
|
6649
|
+
if (contentsObj?.kind === "stream") return decodeStream(contentsObj.raw, contentsObj.dict, sink).bytes;
|
|
6650
|
+
if (contentsObj?.kind === "array") {
|
|
6651
|
+
const chunks = [];
|
|
6652
|
+
for (const item of contentsObj.items) {
|
|
6653
|
+
const streamObj = resolver.resolve(item);
|
|
6654
|
+
if (streamObj?.kind === "stream") chunks.push(decodeStream(streamObj.raw, streamObj.dict, sink).bytes, new Uint8Array([10]));
|
|
6655
|
+
}
|
|
6656
|
+
return concatBytes(chunks);
|
|
6657
|
+
}
|
|
6658
|
+
return /* @__PURE__ */ new Uint8Array(0);
|
|
6659
|
+
}
|
|
6660
|
+
function readPage(page, resolver, fontResolver, images, imageIdCache, sink) {
|
|
6661
|
+
const resources = resolver.resolveDict(dictGet(page, "Resources"));
|
|
6662
|
+
const mediaBox = readMediaBox(page);
|
|
6663
|
+
const rotationResult = pageRotationTransform(normalizeRotation(asNumber(dictGet(page, "Rotate"))), mediaBox.urx - mediaBox.llx, mediaBox.ury - mediaBox.lly);
|
|
6664
|
+
const pageMatrix = multiplyMatrices(translationMatrix(-mediaBox.llx, -mediaBox.lly), rotationResult.matrix);
|
|
6665
|
+
const items = [];
|
|
6666
|
+
if (resources !== void 0) {
|
|
6667
|
+
const extracted = interpretContentStream(readPageContentBytes(page, resolver, sink), resources, {
|
|
6668
|
+
fontMetrics: fontResolver.metrics,
|
|
6669
|
+
resolver,
|
|
6670
|
+
sink
|
|
6671
|
+
});
|
|
6672
|
+
for (const item of extracted) {
|
|
6673
|
+
const converted = convertExtractedItem(item, pageMatrix, fontResolver, images, imageIdCache, resolver, sink);
|
|
6674
|
+
if (converted !== void 0) items.push(converted);
|
|
6675
|
+
}
|
|
6676
|
+
} else sink({
|
|
6677
|
+
code: "pdf/object-missing-value",
|
|
6678
|
+
severity: "warning",
|
|
6679
|
+
message: "page has no /Resources dict; its content stream cannot be interpreted"
|
|
6680
|
+
});
|
|
6681
|
+
items.push(...readLinkAnnotations(page, pageMatrix, resolver));
|
|
6682
|
+
const notes = readPageNotes(page, resolver);
|
|
6683
|
+
return {
|
|
6684
|
+
widthPt: rotationResult.widthPt,
|
|
6685
|
+
heightPt: rotationResult.heightPt,
|
|
6686
|
+
items,
|
|
6687
|
+
...notes !== void 0 ? { notes } : {}
|
|
6688
|
+
};
|
|
6689
|
+
}
|
|
6690
|
+
function convertExtractedItem(item, pageMatrix, fontResolver, images, imageIdCache, resolver, sink) {
|
|
6691
|
+
if (item.kind === "text") return convertText(item, pageMatrix, fontResolver);
|
|
6692
|
+
if (item.kind === "rect") return convertRect(item, pageMatrix);
|
|
6693
|
+
if (item.kind === "image") return convertImage(item, pageMatrix, images, imageIdCache, resolver, sink);
|
|
6694
|
+
return convertInlineImage(item, pageMatrix, images, resolver, sink);
|
|
6695
|
+
}
|
|
6696
|
+
function convertText(item, pageMatrix, fontResolver) {
|
|
6697
|
+
const font = fontResolver.resolve(item.fontResourceName, item.resources);
|
|
6698
|
+
const text = font?.decodeToUnicode(item.codes) ?? "";
|
|
6699
|
+
if (text.length === 0) return;
|
|
6700
|
+
const startTrm = multiplyMatrices(item.startMatrix, pageMatrix);
|
|
6701
|
+
const endTrm = multiplyMatrices(item.endMatrix, pageMatrix);
|
|
6702
|
+
const widthPt = Math.hypot(endTrm[4] - startTrm[4], endTrm[5] - startTrm[5]);
|
|
6703
|
+
const sizePt = matrixScaleX(startTrm);
|
|
6704
|
+
const rotationDeg = matrixRotationDegrees(startTrm);
|
|
6705
|
+
const layoutFont = {
|
|
6706
|
+
family: font?.family ?? "Helvetica",
|
|
6707
|
+
weight: font?.bold === true ? "bold" : "normal",
|
|
6708
|
+
style: font?.italic === true ? "italic" : "normal"
|
|
6709
|
+
};
|
|
6710
|
+
return {
|
|
6711
|
+
kind: "text",
|
|
6712
|
+
text,
|
|
6713
|
+
xPt: startTrm[4],
|
|
6714
|
+
yPt: startTrm[5],
|
|
6715
|
+
font: layoutFont,
|
|
6716
|
+
sizePt: sizePt > 0 ? sizePt : item.sizePt,
|
|
6717
|
+
color: item.color,
|
|
6718
|
+
widthPt,
|
|
6719
|
+
rotationDeg: rotationDeg !== 0 ? rotationDeg : void 0
|
|
6720
|
+
};
|
|
6721
|
+
}
|
|
6722
|
+
function convertRect(item, pageMatrix) {
|
|
6723
|
+
const p1 = applyMatrix(pageMatrix, {
|
|
6724
|
+
x: item.xPt,
|
|
6725
|
+
y: item.yPt
|
|
6726
|
+
});
|
|
6727
|
+
const p2 = applyMatrix(pageMatrix, {
|
|
6728
|
+
x: item.xPt + item.widthPt,
|
|
6729
|
+
y: item.yPt + item.heightPt
|
|
6730
|
+
});
|
|
6731
|
+
return {
|
|
6732
|
+
kind: "rect",
|
|
6733
|
+
xPt: Math.min(p1.x, p2.x),
|
|
6734
|
+
yPt: Math.min(p1.y, p2.y),
|
|
6735
|
+
widthPt: Math.abs(p2.x - p1.x),
|
|
6736
|
+
heightPt: Math.abs(p2.y - p1.y),
|
|
6737
|
+
fill: item.color
|
|
6738
|
+
};
|
|
6739
|
+
}
|
|
6740
|
+
function imagePlacementFrom(matrix) {
|
|
6741
|
+
const rotationDeg = matrixRotationDegrees(matrix);
|
|
6742
|
+
return {
|
|
6743
|
+
xPt: matrix[4],
|
|
6744
|
+
yPt: matrix[5],
|
|
6745
|
+
widthPt: matrixScaleX(matrix),
|
|
6746
|
+
heightPt: matrixScaleY(matrix),
|
|
6747
|
+
rotationDeg: rotationDeg !== 0 ? rotationDeg : void 0
|
|
6748
|
+
};
|
|
6749
|
+
}
|
|
6750
|
+
function registerExtractedImage(format, bytes, widthPx, heightPx, images) {
|
|
6751
|
+
const imageId = `img${crc32(bytes).toString(16)}`;
|
|
6752
|
+
if (!(imageId in images)) images[imageId] = {
|
|
6753
|
+
format,
|
|
6754
|
+
base64: (0, ooxml_js.bytesToBase64)(bytes),
|
|
6755
|
+
widthPx,
|
|
6756
|
+
heightPx
|
|
6757
|
+
};
|
|
6758
|
+
return imageId;
|
|
6759
|
+
}
|
|
6760
|
+
function resolveCachedImageId(dict, raw, images, cache, resolver, sink) {
|
|
6761
|
+
if (cache.has(dict)) return cache.get(dict) ?? void 0;
|
|
6762
|
+
const decoded = readImageXObject(dict, raw, resolver, sink);
|
|
6763
|
+
if (decoded === void 0) {
|
|
6764
|
+
cache.set(dict, null);
|
|
6765
|
+
return;
|
|
6766
|
+
}
|
|
6767
|
+
const imageId = registerExtractedImage(decoded.format, decoded.bytes, decoded.widthPx, decoded.heightPx, images);
|
|
6768
|
+
cache.set(dict, imageId);
|
|
6769
|
+
return imageId;
|
|
6770
|
+
}
|
|
6771
|
+
function convertImage(item, pageMatrix, images, cache, resolver, sink) {
|
|
6772
|
+
const xobjects = resolver.resolveDict(dictGet(item.resources, "XObject"));
|
|
6773
|
+
const xobj = xobjects !== void 0 ? resolver.resolve(dictGet(xobjects, item.resourceName)) : void 0;
|
|
6774
|
+
if (xobj?.kind !== "stream") return;
|
|
6775
|
+
const imageId = resolveCachedImageId(xobj.dict, xobj.raw, images, cache, resolver, sink);
|
|
6776
|
+
if (imageId === void 0) return;
|
|
6777
|
+
return {
|
|
6778
|
+
kind: "image",
|
|
6779
|
+
imageId,
|
|
6780
|
+
...imagePlacementFrom(multiplyMatrices(item.matrix, pageMatrix))
|
|
6781
|
+
};
|
|
6621
6782
|
}
|
|
6622
|
-
function
|
|
6623
|
-
|
|
6783
|
+
function convertInlineImage(item, pageMatrix, images, resolver, sink) {
|
|
6784
|
+
const decoded = readImageXObject(item.dict, item.data, resolver, sink);
|
|
6785
|
+
if (decoded === void 0) return;
|
|
6786
|
+
return {
|
|
6787
|
+
kind: "image",
|
|
6788
|
+
imageId: registerExtractedImage(decoded.format, decoded.bytes, decoded.widthPx, decoded.heightPx, images),
|
|
6789
|
+
...imagePlacementFrom(multiplyMatrices(item.matrix, pageMatrix))
|
|
6790
|
+
};
|
|
6624
6791
|
}
|
|
6625
|
-
function
|
|
6626
|
-
const
|
|
6627
|
-
|
|
6628
|
-
|
|
6629
|
-
const
|
|
6630
|
-
|
|
6631
|
-
|
|
6632
|
-
|
|
6633
|
-
|
|
6634
|
-
|
|
6635
|
-
|
|
6636
|
-
|
|
6637
|
-
|
|
6638
|
-
const
|
|
6639
|
-
const
|
|
6640
|
-
|
|
6641
|
-
|
|
6642
|
-
descNum,
|
|
6643
|
-
resourceName: `F${index + 1}`
|
|
6644
|
-
});
|
|
6645
|
-
}
|
|
6646
|
-
const imageAllocs = /* @__PURE__ */ new Map();
|
|
6647
|
-
for (const [index, imageId] of [...imageIds].sort().entries()) {
|
|
6648
|
-
const asset = doc.images[imageId];
|
|
6649
|
-
if (asset === void 0) throw new Error(`LayoutDocument references image "${imageId}" but it is not present in images`);
|
|
6650
|
-
const prepared = prepareImage(asset, compress);
|
|
6651
|
-
const imageNum = nextObjNum++;
|
|
6652
|
-
const smaskNum = prepared.alpha === void 0 ? void 0 : nextObjNum++;
|
|
6653
|
-
imageAllocs.set(imageId, {
|
|
6654
|
-
imageNum,
|
|
6655
|
-
smaskNum,
|
|
6656
|
-
resourceName: `Im${index + 1}`,
|
|
6657
|
-
prepared
|
|
6658
|
-
});
|
|
6659
|
-
}
|
|
6660
|
-
const pageAllocs = doc.pages.map(() => ({
|
|
6661
|
-
pageNum: nextObjNum++,
|
|
6662
|
-
contentsNum: nextObjNum++
|
|
6663
|
-
}));
|
|
6664
|
-
const objects = [];
|
|
6665
|
-
objects.push({
|
|
6666
|
-
num: catalogNum,
|
|
6667
|
-
value: pdfDict({
|
|
6668
|
-
Type: pdfName("Catalog"),
|
|
6669
|
-
Pages: pdfRef(pagesNum, 0)
|
|
6670
|
-
})
|
|
6671
|
-
});
|
|
6672
|
-
objects.push({
|
|
6673
|
-
num: pagesNum,
|
|
6674
|
-
value: pdfDict({
|
|
6675
|
-
Type: pdfName("Pages"),
|
|
6676
|
-
Kids: pdfArray(pageAllocs.map((p) => pdfRef(p.pageNum, 0))),
|
|
6677
|
-
Count: pdfNum(doc.pages.length)
|
|
6678
|
-
})
|
|
6679
|
-
});
|
|
6680
|
-
objects.push({
|
|
6681
|
-
num: infoNum,
|
|
6682
|
-
value: buildInfoDict(doc)
|
|
6683
|
-
});
|
|
6684
|
-
for (const [standardName, alloc] of fontAllocs) {
|
|
6685
|
-
const { font, descriptor } = buildFontObjects(standardName, pdfRef(alloc.descNum, 0));
|
|
6686
|
-
objects.push({
|
|
6687
|
-
num: alloc.fontNum,
|
|
6688
|
-
value: font
|
|
6792
|
+
function readLinkAnnotations(page, pageMatrix, resolver) {
|
|
6793
|
+
const annotsArr = asArray(dictGet(page, "Annots"));
|
|
6794
|
+
if (annotsArr === void 0) return [];
|
|
6795
|
+
const links = [];
|
|
6796
|
+
for (const annotRef of annotsArr) {
|
|
6797
|
+
const annot = resolver.resolveDict(annotRef);
|
|
6798
|
+
if (annot === void 0 || asName(dictGet(annot, "Subtype")) !== "Link") continue;
|
|
6799
|
+
const uri = readLinkUri(annot, resolver);
|
|
6800
|
+
const rectArr = asArray(dictGet(annot, "Rect"));
|
|
6801
|
+
if (uri === void 0 || rectArr === void 0) continue;
|
|
6802
|
+
const x1 = asNumber(rectArr[0]) ?? 0;
|
|
6803
|
+
const y1 = asNumber(rectArr[1]) ?? 0;
|
|
6804
|
+
const x2 = asNumber(rectArr[2]) ?? 0;
|
|
6805
|
+
const y2 = asNumber(rectArr[3]) ?? 0;
|
|
6806
|
+
const p1 = applyMatrix(pageMatrix, {
|
|
6807
|
+
x: Math.min(x1, x2),
|
|
6808
|
+
y: Math.min(y1, y2)
|
|
6689
6809
|
});
|
|
6690
|
-
|
|
6691
|
-
|
|
6692
|
-
|
|
6810
|
+
const p2 = applyMatrix(pageMatrix, {
|
|
6811
|
+
x: Math.max(x1, x2),
|
|
6812
|
+
y: Math.max(y1, y2)
|
|
6693
6813
|
});
|
|
6694
|
-
|
|
6695
|
-
|
|
6696
|
-
|
|
6697
|
-
|
|
6698
|
-
|
|
6699
|
-
|
|
6700
|
-
|
|
6701
|
-
});
|
|
6702
|
-
}
|
|
6703
|
-
objects.push({
|
|
6704
|
-
num: alloc.imageNum,
|
|
6705
|
-
value: pdfStream(alloc.prepared.dict, alloc.prepared.raw)
|
|
6814
|
+
links.push({
|
|
6815
|
+
kind: "link",
|
|
6816
|
+
uri,
|
|
6817
|
+
xPt: Math.min(p1.x, p2.x),
|
|
6818
|
+
yPt: Math.min(p1.y, p2.y),
|
|
6819
|
+
widthPt: Math.abs(p2.x - p1.x),
|
|
6820
|
+
heightPt: Math.abs(p2.y - p1.y)
|
|
6706
6821
|
});
|
|
6707
6822
|
}
|
|
6708
|
-
|
|
6709
|
-
|
|
6710
|
-
|
|
6711
|
-
const
|
|
6712
|
-
|
|
6713
|
-
|
|
6714
|
-
|
|
6715
|
-
|
|
6716
|
-
|
|
6717
|
-
|
|
6718
|
-
|
|
6719
|
-
|
|
6720
|
-
|
|
6721
|
-
|
|
6722
|
-
|
|
6723
|
-
|
|
6724
|
-
|
|
6725
|
-
|
|
6726
|
-
return { resourceName: alloc.resourceName };
|
|
6727
|
-
}
|
|
6728
|
-
};
|
|
6729
|
-
doc.pages.forEach((page, pageIndex) => {
|
|
6730
|
-
throwIfAborted(options.signal);
|
|
6731
|
-
const { pageNum, contentsNum } = pageAllocs[pageIndex];
|
|
6732
|
-
const { bytes: contentBytes, substitutions } = writeContentStream(page.items, context);
|
|
6733
|
-
for (const substitution of substitutions) options.onSubstitution?.(substitution, { pageIndex });
|
|
6734
|
-
const finalContentBytes = compress ? deflate(contentBytes) : contentBytes;
|
|
6735
|
-
const contentsDict = pdfDict(compress ? { Filter: pdfName("FlateDecode") } : {});
|
|
6736
|
-
objects.push({
|
|
6737
|
-
num: contentsNum,
|
|
6738
|
-
value: pdfStream(contentsDict, finalContentBytes)
|
|
6739
|
-
});
|
|
6740
|
-
const annots = page.items.filter(isLinkItem).map((link) => buildLinkAnnotDict(link));
|
|
6741
|
-
const pageEntries = /* @__PURE__ */ new Map([
|
|
6742
|
-
["Type", pdfName("Page")],
|
|
6743
|
-
["Parent", pdfRef(pagesNum, 0)],
|
|
6744
|
-
["MediaBox", pdfArray([
|
|
6745
|
-
0,
|
|
6746
|
-
0,
|
|
6747
|
-
page.widthPt,
|
|
6748
|
-
page.heightPt
|
|
6749
|
-
].map((n) => pdfNum(n)))],
|
|
6750
|
-
["Resources", resourcesDict],
|
|
6751
|
-
["Contents", pdfRef(contentsNum, 0)]
|
|
6752
|
-
]);
|
|
6753
|
-
if (annots.length > 0) pageEntries.set("Annots", pdfArray(annots));
|
|
6754
|
-
objects.push({
|
|
6755
|
-
num: pageNum,
|
|
6756
|
-
value: pdfDict(pageEntries)
|
|
6757
|
-
});
|
|
6758
|
-
});
|
|
6759
|
-
const writer = new ByteWriter();
|
|
6760
|
-
writer.writeAscii("%PDF-1.7\n");
|
|
6761
|
-
const offsets = /* @__PURE__ */ new Map();
|
|
6762
|
-
for (const { num, value } of objects) {
|
|
6763
|
-
offsets.set(num, writer.length);
|
|
6764
|
-
writer.writeAscii(`${num} 0 obj\n`);
|
|
6765
|
-
writeObject(writer, value);
|
|
6766
|
-
writer.writeAscii("\nendobj\n");
|
|
6823
|
+
return links;
|
|
6824
|
+
}
|
|
6825
|
+
function readLinkUri(annot, resolver) {
|
|
6826
|
+
const action = resolver.resolveDict(dictGet(annot, "A"));
|
|
6827
|
+
if (action === void 0 || asName(dictGet(action, "S")) !== "URI") return;
|
|
6828
|
+
const uriObj = dictGet(action, "URI");
|
|
6829
|
+
return uriObj?.kind === "string" ? decodePdfString(uriObj.bytes) : void 0;
|
|
6830
|
+
}
|
|
6831
|
+
function readPageNotes(page, resolver) {
|
|
6832
|
+
const annotsArr = asArray(dictGet(page, "Annots"));
|
|
6833
|
+
if (annotsArr === void 0) return;
|
|
6834
|
+
for (const annotRef of annotsArr) {
|
|
6835
|
+
const annot = resolver.resolveDict(annotRef);
|
|
6836
|
+
if (annot === void 0 || asName(dictGet(annot, "Subtype")) !== "Text") continue;
|
|
6837
|
+
const titleObj = dictGet(annot, "T");
|
|
6838
|
+
if ((titleObj?.kind === "string" ? decodePdfString(titleObj.bytes) : void 0) !== "documents.js:notes") continue;
|
|
6839
|
+
const contentsObj = dictGet(annot, "Contents");
|
|
6840
|
+
if (contentsObj?.kind === "string") return decodePdfString(contentsObj.bytes);
|
|
6767
6841
|
}
|
|
6768
|
-
|
|
6769
|
-
|
|
6770
|
-
|
|
6771
|
-
|
|
6772
|
-
|
|
6773
|
-
|
|
6774
|
-
const offset = offsets.get(num);
|
|
6775
|
-
if (offset === void 0) throw new Error(`object ${num} was allocated but never written -- this is a writePdf internal invariant violation`);
|
|
6776
|
-
writer.writeAscii(xrefEntry(offset, 0, true));
|
|
6842
|
+
}
|
|
6843
|
+
function decodePdfString(bytes) {
|
|
6844
|
+
if (bytes.length >= 2 && bytes[0] === 254 && bytes[1] === 255) {
|
|
6845
|
+
let out = "";
|
|
6846
|
+
for (let i = 2; i + 1 < bytes.length; i += 2) out += String.fromCharCode((bytes[i] ?? 0) << 8 | (bytes[i + 1] ?? 0));
|
|
6847
|
+
return out;
|
|
6777
6848
|
}
|
|
6778
|
-
|
|
6779
|
-
|
|
6780
|
-
|
|
6781
|
-
|
|
6782
|
-
|
|
6783
|
-
|
|
6784
|
-
|
|
6785
|
-
|
|
6786
|
-
|
|
6787
|
-
|
|
6849
|
+
return Array.from(bytes, (b) => String.fromCharCode(b)).join("");
|
|
6850
|
+
}
|
|
6851
|
+
const PDF_DATE_PATTERN = /^D:(\d{4})(\d{2})?(\d{2})?(\d{2})?(\d{2})?(\d{2})?([+\-Z])?(\d{2})?'?(\d{2})?'?$/;
|
|
6852
|
+
function parsePdfDate(raw) {
|
|
6853
|
+
if (raw === void 0) return;
|
|
6854
|
+
const match = PDF_DATE_PATTERN.exec(raw);
|
|
6855
|
+
if (match === null) return;
|
|
6856
|
+
const [, year, month = "01", day = "01", hour = "00", minute = "00", second = "00", tzSign, tzHour = "00", tzMinute = "00"] = match;
|
|
6857
|
+
return `${year}-${month}-${day}T${hour}:${minute}:${second}${tzSign === void 0 || tzSign === "Z" ? "Z" : `${tzSign}${tzHour}:${tzMinute}`}`;
|
|
6858
|
+
}
|
|
6859
|
+
function readMetadata(trailer, resolver) {
|
|
6860
|
+
const info = resolver.resolveDict(dictGet(trailer, "Info"));
|
|
6861
|
+
if (info === void 0) return {};
|
|
6862
|
+
const stringField = (key) => {
|
|
6863
|
+
const obj = dictGet(info, key);
|
|
6864
|
+
return obj?.kind === "string" ? decodePdfString(obj.bytes) : void 0;
|
|
6865
|
+
};
|
|
6866
|
+
const keywords = stringField("Keywords")?.split(",").map((k) => k.trim()).filter((k) => k.length > 0);
|
|
6867
|
+
return {
|
|
6868
|
+
title: stringField("Title"),
|
|
6869
|
+
author: stringField("Author"),
|
|
6870
|
+
subject: stringField("Subject"),
|
|
6871
|
+
keywords: keywords !== void 0 && keywords.length > 0 ? keywords : void 0,
|
|
6872
|
+
creator: stringField("Creator"),
|
|
6873
|
+
producer: stringField("Producer"),
|
|
6874
|
+
createdIso: parsePdfDate(stringField("CreationDate")),
|
|
6875
|
+
modifiedIso: parsePdfDate(stringField("ModDate"))
|
|
6876
|
+
};
|
|
6788
6877
|
}
|
|
6789
6878
|
//#endregion
|
|
6790
6879
|
//#region src/pdf/codec.ts
|
|
@@ -7416,7 +7505,8 @@ function convertSlide(slide, measurer, images) {
|
|
|
7416
7505
|
return {
|
|
7417
7506
|
widthPt: slide.size.widthPt,
|
|
7418
7507
|
heightPt: slide.size.heightPt,
|
|
7419
|
-
items
|
|
7508
|
+
items,
|
|
7509
|
+
...slide.notes.length > 0 ? { notes: slide.notes } : {}
|
|
7420
7510
|
};
|
|
7421
7511
|
}
|
|
7422
7512
|
function convertPresentationToLayout(doc, options) {
|
|
@@ -7654,7 +7744,7 @@ function reconstructSlide(page, images) {
|
|
|
7654
7744
|
heightPt: page.heightPt
|
|
7655
7745
|
},
|
|
7656
7746
|
shapes: [...imageShapes, ...textShapes],
|
|
7657
|
-
notes: ""
|
|
7747
|
+
notes: page.notes ?? ""
|
|
7658
7748
|
};
|
|
7659
7749
|
}
|
|
7660
7750
|
function splitLineByLargeGaps(line) {
|