documents.js 1.33.4 → 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/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"]),
@@ -5793,424 +5794,96 @@ function runContentStream(bytes, resources, initialState, context, items, depth)
5793
5794
  }
5794
5795
  }
5795
5796
  //#endregion
5796
- //#region src/pdf/read.ts
5797
- const PDF_HEADER_BYTES = new TextEncoder().encode("%PDF-");
5798
- const HEADER_SEARCH_WINDOW = 1024;
5799
- const DEFAULT_PAGE_WIDTH_PT = 612;
5800
- const DEFAULT_PAGE_HEIGHT_PT = 792;
5801
- function hasPdfHeader(bytes) {
5802
- const window = bytes.subarray(0, Math.min(HEADER_SEARCH_WINDOW, bytes.length));
5803
- outer: for (let i = 0; i <= window.length - PDF_HEADER_BYTES.length; i++) {
5804
- for (let j = 0; j < PDF_HEADER_BYTES.length; j++) if (window[i + j] !== PDF_HEADER_BYTES[j]) continue outer;
5805
- return true;
5806
- }
5807
- return false;
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);
5808
5810
  }
5809
- function readPdf(bytes, options) {
5810
- const sink = options?.sink ?? NOOP_DIAGNOSTIC_SINK;
5811
- const signal = options?.signal;
5812
- 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");
5813
- const doc = openPdfDocument(bytes, sink);
5814
- const resolver = doc;
5815
- const fontResolver = createFontResolver({
5816
- resolver,
5817
- sink
5818
- });
5819
- const images = {};
5820
- const imageIdCache = /* @__PURE__ */ new Map();
5821
- const pages = doc.pages().map((pageDict) => {
5822
- throwIfAborted(signal);
5823
- return readPage(pageDict, resolver, fontResolver, images, imageIdCache, sink);
5824
- });
5825
- return {
5826
- formatVersion: 1,
5827
- metadata: readMetadata(doc.trailer, resolver),
5828
- pages,
5829
- images
5830
- };
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;
5831
5834
  }
5832
- function readMediaBox(page) {
5833
- const arr = asArray(dictGet(page, "MediaBox"));
5834
- if (arr === void 0) return {
5835
- llx: 0,
5836
- lly: 0,
5837
- urx: DEFAULT_PAGE_WIDTH_PT,
5838
- ury: DEFAULT_PAGE_HEIGHT_PT
5839
- };
5840
- const a = asNumber(arr[0]) ?? 0;
5841
- const b = asNumber(arr[1]) ?? 0;
5842
- const c = asNumber(arr[2]) ?? DEFAULT_PAGE_WIDTH_PT;
5843
- const d = asNumber(arr[3]) ?? DEFAULT_PAGE_HEIGHT_PT;
5835
+ function parseIhdr(data) {
5836
+ const view = requireDataView(data);
5844
5837
  return {
5845
- llx: Math.min(a, c),
5846
- lly: Math.min(b, d),
5847
- urx: Math.max(a, c),
5848
- ury: Math.max(b, d)
5838
+ width: view.getUint32(0),
5839
+ height: view.getUint32(4),
5840
+ bitDepth: data[8],
5841
+ colorType: data[9],
5842
+ interlace: data[12]
5849
5843
  };
5850
5844
  }
5851
- function normalizeRotation(rotate) {
5852
- if (rotate === void 0) return 0;
5853
- const normalized = (Math.round(rotate / 90) * 90 % 360 + 360) % 360;
5854
- return normalized === 90 || normalized === 180 || normalized === 270 ? normalized : 0;
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}`);
5855
5852
  }
5856
- function pageRotationTransform(rotation, w, h) {
5857
- if (rotation === 90) return {
5858
- matrix: [
5859
- 0,
5860
- -1,
5861
- 1,
5862
- 0,
5863
- 0,
5864
- w
5865
- ],
5866
- widthPt: h,
5867
- heightPt: w
5868
- };
5869
- if (rotation === 180) return {
5870
- matrix: [
5871
- -1,
5872
- 0,
5873
- 0,
5874
- -1,
5875
- w,
5876
- h
5877
- ],
5878
- widthPt: w,
5879
- heightPt: h
5880
- };
5881
- if (rotation === 270) return {
5882
- matrix: [
5883
- 0,
5884
- 1,
5885
- -1,
5886
- 0,
5887
- h,
5888
- 0
5889
- ],
5890
- widthPt: h,
5891
- heightPt: w
5892
- };
5893
- return {
5894
- matrix: [
5895
- 1,
5896
- 0,
5897
- 0,
5898
- 1,
5899
- 0,
5900
- 0
5901
- ],
5902
- widthPt: w,
5903
- heightPt: h
5904
- };
5853
+ function filterBpp(bitDepth, channels) {
5854
+ return Math.max(1, Math.ceil(bitDepth * channels / 8));
5905
5855
  }
5906
- function readPageContentBytes(page, resolver, sink) {
5907
- const contentsObj = resolver.resolve(dictGet(page, "Contents"));
5908
- if (contentsObj?.kind === "stream") return decodeStream(contentsObj.raw, contentsObj.dict, sink).bytes;
5909
- if (contentsObj?.kind === "array") {
5910
- const chunks = [];
5911
- for (const item of contentsObj.items) {
5912
- const streamObj = resolver.resolve(item);
5913
- if (streamObj?.kind === "stream") chunks.push(decodeStream(streamObj.raw, streamObj.dict, sink).bytes, new Uint8Array([10]));
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;
5914
5868
  }
5915
- return concatBytes(chunks);
5916
5869
  }
5917
- return /* @__PURE__ */ new Uint8Array(0);
5870
+ return samples;
5918
5871
  }
5919
- function readPage(page, resolver, fontResolver, images, imageIdCache, sink) {
5920
- const resources = resolver.resolveDict(dictGet(page, "Resources"));
5921
- const mediaBox = readMediaBox(page);
5922
- const rotationResult = pageRotationTransform(normalizeRotation(asNumber(dictGet(page, "Rotate"))), mediaBox.urx - mediaBox.llx, mediaBox.ury - mediaBox.lly);
5923
- const pageMatrix = multiplyMatrices(translationMatrix(-mediaBox.llx, -mediaBox.lly), rotationResult.matrix);
5924
- const items = [];
5925
- if (resources !== void 0) {
5926
- const extracted = interpretContentStream(readPageContentBytes(page, resolver, sink), resources, {
5927
- fontMetrics: fontResolver.metrics,
5928
- resolver,
5929
- sink
5930
- });
5931
- for (const item of extracted) {
5932
- const converted = convertExtractedItem(item, pageMatrix, fontResolver, images, imageIdCache, resolver, sink);
5933
- if (converted !== void 0) items.push(converted);
5934
- }
5935
- } else sink({
5936
- code: "pdf/object-missing-value",
5937
- severity: "warning",
5938
- message: "page has no /Resources dict; its content stream cannot be interpreted"
5939
- });
5940
- items.push(...readLinkAnnotations(page, pageMatrix, resolver));
5941
- return {
5942
- widthPt: rotationResult.widthPt,
5943
- heightPt: rotationResult.heightPt,
5944
- items
5945
- };
5872
+ function readTrnsGrayValue(trns) {
5873
+ return requireDataView(trns).getUint16(0);
5946
5874
  }
5947
- function convertExtractedItem(item, pageMatrix, fontResolver, images, imageIdCache, resolver, sink) {
5948
- if (item.kind === "text") return convertText(item, pageMatrix, fontResolver);
5949
- if (item.kind === "rect") return convertRect(item, pageMatrix);
5950
- if (item.kind === "image") return convertImage(item, pageMatrix, images, imageIdCache, resolver, sink);
5951
- return convertInlineImage(item, pageMatrix, images, resolver, sink);
5875
+ function readTrnsRgbKey(trns) {
5876
+ const view = requireDataView(trns);
5877
+ return [
5878
+ view.getUint16(0),
5879
+ view.getUint16(2),
5880
+ view.getUint16(4)
5881
+ ];
5952
5882
  }
5953
- function convertText(item, pageMatrix, fontResolver) {
5954
- const font = fontResolver.resolve(item.fontResourceName, item.resources);
5955
- const text = font?.decodeToUnicode(item.codes) ?? "";
5956
- if (text.length === 0) return;
5957
- const startTrm = multiplyMatrices(item.startMatrix, pageMatrix);
5958
- const endTrm = multiplyMatrices(item.endMatrix, pageMatrix);
5959
- const widthPt = Math.hypot(endTrm[4] - startTrm[4], endTrm[5] - startTrm[5]);
5960
- const sizePt = matrixScaleX(startTrm);
5961
- const rotationDeg = matrixRotationDegrees(startTrm);
5962
- const layoutFont = {
5963
- family: font?.family ?? "Helvetica",
5964
- weight: font?.bold === true ? "bold" : "normal",
5965
- style: font?.italic === true ? "italic" : "normal"
5966
- };
5967
- return {
5968
- kind: "text",
5969
- text,
5970
- xPt: startTrm[4],
5971
- yPt: startTrm[5],
5972
- font: layoutFont,
5973
- sizePt: sizePt > 0 ? sizePt : item.sizePt,
5974
- color: item.color,
5975
- widthPt,
5976
- rotationDeg: rotationDeg !== 0 ? rotationDeg : void 0
5977
- };
5978
- }
5979
- function convertRect(item, pageMatrix) {
5980
- const p1 = applyMatrix(pageMatrix, {
5981
- x: item.xPt,
5982
- y: item.yPt
5983
- });
5984
- const p2 = applyMatrix(pageMatrix, {
5985
- x: item.xPt + item.widthPt,
5986
- y: item.yPt + item.heightPt
5987
- });
5988
- return {
5989
- kind: "rect",
5990
- xPt: Math.min(p1.x, p2.x),
5991
- yPt: Math.min(p1.y, p2.y),
5992
- widthPt: Math.abs(p2.x - p1.x),
5993
- heightPt: Math.abs(p2.y - p1.y),
5994
- fill: item.color
5995
- };
5996
- }
5997
- function imagePlacementFrom(matrix) {
5998
- const rotationDeg = matrixRotationDegrees(matrix);
5999
- return {
6000
- xPt: matrix[4],
6001
- yPt: matrix[5],
6002
- widthPt: matrixScaleX(matrix),
6003
- heightPt: matrixScaleY(matrix),
6004
- rotationDeg: rotationDeg !== 0 ? rotationDeg : void 0
6005
- };
6006
- }
6007
- function registerExtractedImage(format, bytes, widthPx, heightPx, images) {
6008
- const imageId = `img${crc32(bytes).toString(16)}`;
6009
- if (!(imageId in images)) images[imageId] = {
6010
- format,
6011
- base64: (0, ooxml_js.bytesToBase64)(bytes),
6012
- widthPx,
6013
- heightPx
6014
- };
6015
- return imageId;
6016
- }
6017
- function resolveCachedImageId(dict, raw, images, cache, resolver, sink) {
6018
- if (cache.has(dict)) return cache.get(dict) ?? void 0;
6019
- const decoded = readImageXObject(dict, raw, resolver, sink);
6020
- if (decoded === void 0) {
6021
- cache.set(dict, null);
6022
- return;
6023
- }
6024
- const imageId = registerExtractedImage(decoded.format, decoded.bytes, decoded.widthPx, decoded.heightPx, images);
6025
- cache.set(dict, imageId);
6026
- return imageId;
6027
- }
6028
- function convertImage(item, pageMatrix, images, cache, resolver, sink) {
6029
- const xobjects = resolver.resolveDict(dictGet(item.resources, "XObject"));
6030
- const xobj = xobjects !== void 0 ? resolver.resolve(dictGet(xobjects, item.resourceName)) : void 0;
6031
- if (xobj?.kind !== "stream") return;
6032
- const imageId = resolveCachedImageId(xobj.dict, xobj.raw, images, cache, resolver, sink);
6033
- if (imageId === void 0) return;
6034
- return {
6035
- kind: "image",
6036
- imageId,
6037
- ...imagePlacementFrom(multiplyMatrices(item.matrix, pageMatrix))
6038
- };
6039
- }
6040
- function convertInlineImage(item, pageMatrix, images, resolver, sink) {
6041
- const decoded = readImageXObject(item.dict, item.data, resolver, sink);
6042
- if (decoded === void 0) return;
6043
- return {
6044
- kind: "image",
6045
- imageId: registerExtractedImage(decoded.format, decoded.bytes, decoded.widthPx, decoded.heightPx, images),
6046
- ...imagePlacementFrom(multiplyMatrices(item.matrix, pageMatrix))
6047
- };
6048
- }
6049
- function readLinkAnnotations(page, pageMatrix, resolver) {
6050
- const annotsArr = asArray(dictGet(page, "Annots"));
6051
- if (annotsArr === void 0) return [];
6052
- const links = [];
6053
- for (const annotRef of annotsArr) {
6054
- const annot = resolver.resolveDict(annotRef);
6055
- if (annot === void 0 || asName(dictGet(annot, "Subtype")) !== "Link") continue;
6056
- const uri = readLinkUri(annot, resolver);
6057
- const rectArr = asArray(dictGet(annot, "Rect"));
6058
- if (uri === void 0 || rectArr === void 0) continue;
6059
- const x1 = asNumber(rectArr[0]) ?? 0;
6060
- const y1 = asNumber(rectArr[1]) ?? 0;
6061
- const x2 = asNumber(rectArr[2]) ?? 0;
6062
- const y2 = asNumber(rectArr[3]) ?? 0;
6063
- const p1 = applyMatrix(pageMatrix, {
6064
- x: Math.min(x1, x2),
6065
- y: Math.min(y1, y2)
6066
- });
6067
- const p2 = applyMatrix(pageMatrix, {
6068
- x: Math.max(x1, x2),
6069
- y: Math.max(y1, y2)
6070
- });
6071
- links.push({
6072
- kind: "link",
6073
- uri,
6074
- xPt: Math.min(p1.x, p2.x),
6075
- yPt: Math.min(p1.y, p2.y),
6076
- widthPt: Math.abs(p2.x - p1.x),
6077
- heightPt: Math.abs(p2.y - p1.y)
6078
- });
6079
- }
6080
- return links;
6081
- }
6082
- function readLinkUri(annot, resolver) {
6083
- const action = resolver.resolveDict(dictGet(annot, "A"));
6084
- if (action === void 0 || asName(dictGet(action, "S")) !== "URI") return;
6085
- const uriObj = dictGet(action, "URI");
6086
- return uriObj?.kind === "string" ? decodePdfString(uriObj.bytes) : void 0;
6087
- }
6088
- function decodePdfString(bytes) {
6089
- if (bytes.length >= 2 && bytes[0] === 254 && bytes[1] === 255) {
6090
- let out = "";
6091
- for (let i = 2; i + 1 < bytes.length; i += 2) out += String.fromCharCode((bytes[i] ?? 0) << 8 | (bytes[i + 1] ?? 0));
6092
- return out;
6093
- }
6094
- return Array.from(bytes, (b) => String.fromCharCode(b)).join("");
6095
- }
6096
- const PDF_DATE_PATTERN = /^D:(\d{4})(\d{2})?(\d{2})?(\d{2})?(\d{2})?(\d{2})?([+\-Z])?(\d{2})?'?(\d{2})?'?$/;
6097
- function parsePdfDate(raw) {
6098
- if (raw === void 0) return;
6099
- const match = PDF_DATE_PATTERN.exec(raw);
6100
- if (match === null) return;
6101
- const [, year, month = "01", day = "01", hour = "00", minute = "00", second = "00", tzSign, tzHour = "00", tzMinute = "00"] = match;
6102
- return `${year}-${month}-${day}T${hour}:${minute}:${second}${tzSign === void 0 || tzSign === "Z" ? "Z" : `${tzSign}${tzHour}:${tzMinute}`}`;
6103
- }
6104
- function readMetadata(trailer, resolver) {
6105
- const info = resolver.resolveDict(dictGet(trailer, "Info"));
6106
- if (info === void 0) return {};
6107
- const stringField = (key) => {
6108
- const obj = dictGet(info, key);
6109
- return obj?.kind === "string" ? decodePdfString(obj.bytes) : void 0;
6110
- };
6111
- const keywords = stringField("Keywords")?.split(",").map((k) => k.trim()).filter((k) => k.length > 0);
6112
- return {
6113
- title: stringField("Title"),
6114
- author: stringField("Author"),
6115
- subject: stringField("Subject"),
6116
- keywords: keywords !== void 0 && keywords.length > 0 ? keywords : void 0,
6117
- creator: stringField("Creator"),
6118
- producer: stringField("Producer"),
6119
- createdIso: parsePdfDate(stringField("CreationDate")),
6120
- modifiedIso: parsePdfDate(stringField("ModDate"))
6121
- };
6122
- }
6123
- //#endregion
6124
- //#region src/image/png-decode.ts
6125
- const PNG_SIGNATURE = [
6126
- 137,
6127
- 80,
6128
- 78,
6129
- 71,
6130
- 13,
6131
- 10,
6132
- 26,
6133
- 10
6134
- ];
6135
- function requireDataView(bytes) {
6136
- return new DataView(bytes.buffer, bytes.byteOffset, bytes.byteLength);
6137
- }
6138
- function readChunks(bytes, onWarning) {
6139
- const chunks = [];
6140
- const view = requireDataView(bytes);
6141
- let offset = PNG_SIGNATURE.length;
6142
- while (offset + 8 <= bytes.length) {
6143
- const length = view.getUint32(offset);
6144
- const typeBytes = bytes.subarray(offset + 4, offset + 8);
6145
- const type = new TextDecoder("latin1").decode(typeBytes);
6146
- const dataStart = offset + 8;
6147
- const dataEnd = dataStart + length;
6148
- if (dataEnd + 4 > bytes.length) throw new Error(`PNG chunk '${type}' declares a length that runs past the end of the file`);
6149
- const data = bytes.subarray(dataStart, dataEnd);
6150
- if (onWarning !== void 0) {
6151
- if (view.getUint32(dataEnd) !== crc32(concatBytes([typeBytes, data]))) onWarning(`PNG chunk '${type}' failed its CRC32 check`);
6152
- }
6153
- chunks.push({
6154
- type,
6155
- data
6156
- });
6157
- offset = dataEnd + 4;
6158
- if (type === "IEND") break;
6159
- }
6160
- return chunks;
6161
- }
6162
- function parseIhdr(data) {
6163
- const view = requireDataView(data);
6164
- return {
6165
- width: view.getUint32(0),
6166
- height: view.getUint32(4),
6167
- bitDepth: data[8],
6168
- colorType: data[9],
6169
- interlace: data[12]
6170
- };
6171
- }
6172
- function channelsForColorType(colorType) {
6173
- if (colorType === 0) return 1;
6174
- if (colorType === 2) return 3;
6175
- if (colorType === 3) return 1;
6176
- if (colorType === 4) return 2;
6177
- if (colorType === 6) return 4;
6178
- throw new Error(`unsupported PNG colour type: ${colorType}`);
6179
- }
6180
- function filterBpp(bitDepth, channels) {
6181
- return Math.max(1, Math.ceil(bitDepth * channels / 8));
6182
- }
6183
- function unpackRow(rowBytes, width, channels, bitDepth) {
6184
- const sampleCount = width * channels;
6185
- const samples = new Array(sampleCount);
6186
- if (bitDepth === 8) for (let i = 0; i < sampleCount; i++) samples[i] = rowBytes[i];
6187
- else if (bitDepth === 16) for (let i = 0; i < sampleCount; i++) samples[i] = rowBytes[i * 2];
6188
- else {
6189
- const mask = (1 << bitDepth) - 1;
6190
- for (let i = 0; i < sampleCount; i++) {
6191
- const bitOffset = i * bitDepth;
6192
- const byteIndex = bitOffset >> 3;
6193
- const shift = 8 - bitDepth - (bitOffset & 7);
6194
- samples[i] = rowBytes[byteIndex] >> shift & mask;
6195
- }
6196
- }
6197
- return samples;
6198
- }
6199
- function readTrnsGrayValue(trns) {
6200
- return requireDataView(trns).getUint16(0);
6201
- }
6202
- function readTrnsRgbKey(trns) {
6203
- const view = requireDataView(trns);
6204
- return [
6205
- view.getUint16(0),
6206
- view.getUint16(2),
6207
- view.getUint16(4)
6208
- ];
6209
- }
6210
- function scaleToByte(sample, bitDepth) {
6211
- if (bitDepth === 16) return sample;
6212
- const maxSample = (1 << bitDepth) - 1;
6213
- return Math.round(sample * 255 / maxSample);
5883
+ function scaleToByte(sample, bitDepth) {
5884
+ if (bitDepth === 16) return sample;
5885
+ const maxSample = (1 << bitDepth) - 1;
5886
+ return Math.round(sample * 255 / maxSample);
6214
5887
  }
6215
5888
  function decodePng(bytes, options = {}) {
6216
5889
  for (let i = 0; i < PNG_SIGNATURE.length; i++) if (bytes[i] !== PNG_SIGNATURE[i]) throw new Error("not a valid PNG file: bad signature");
@@ -6513,334 +6186,694 @@ function createStandardFontMeasurer(options = {}) {
6513
6186
  }
6514
6187
  };
6515
6188
  }
6516
- //#endregion
6517
- //#region src/pdf/write.ts
6518
- const FIRST_CHAR = 32;
6519
- const LAST_CHAR = 255;
6520
- const NOMINAL_STEM_V_REGULAR = 80;
6521
- const NOMINAL_STEM_V_BOLD = 120;
6522
- const FLAG_FIXED_PITCH = 1;
6523
- const FLAG_SERIF = 2;
6524
- const FLAG_NONSYMBOLIC = 32;
6525
- const FLAG_ITALIC = 64;
6526
- const FLAG_FORCE_BOLD = 262144;
6527
- function textToPdfString(text) {
6528
- const bytes = new Uint8Array(2 + text.length * 2);
6529
- bytes[0] = 254;
6530
- bytes[1] = 255;
6531
- for (let i = 0; i < text.length; i++) {
6532
- const code = text.charCodeAt(i);
6533
- bytes[2 + i * 2] = code >> 8 & 255;
6534
- bytes[2 + i * 2 + 1] = code & 255;
6189
+ //#endregion
6190
+ //#region src/pdf/write.ts
6191
+ const FIRST_CHAR = 32;
6192
+ const LAST_CHAR = 255;
6193
+ const NOMINAL_STEM_V_REGULAR = 80;
6194
+ const NOMINAL_STEM_V_BOLD = 120;
6195
+ const FLAG_FIXED_PITCH = 1;
6196
+ const FLAG_SERIF = 2;
6197
+ const FLAG_NONSYMBOLIC = 32;
6198
+ const FLAG_ITALIC = 64;
6199
+ const FLAG_FORCE_BOLD = 262144;
6200
+ function textToPdfString(text) {
6201
+ const bytes = new Uint8Array(2 + text.length * 2);
6202
+ bytes[0] = 254;
6203
+ bytes[1] = 255;
6204
+ for (let i = 0; i < text.length; i++) {
6205
+ const code = text.charCodeAt(i);
6206
+ bytes[2 + i * 2] = code >> 8 & 255;
6207
+ bytes[2 + i * 2 + 1] = code & 255;
6208
+ }
6209
+ return pdfHexString(bytes);
6210
+ }
6211
+ function pad2(n) {
6212
+ return n.toString().padStart(2, "0");
6213
+ }
6214
+ function formatPdfDate(iso) {
6215
+ const date = new Date(iso);
6216
+ return `D:${date.getUTCFullYear()}${pad2(date.getUTCMonth() + 1)}${pad2(date.getUTCDate())}${pad2(date.getUTCHours())}${pad2(date.getUTCMinutes())}${pad2(date.getUTCSeconds())}Z`;
6217
+ }
6218
+ function buildInfoDict(doc) {
6219
+ const entries = /* @__PURE__ */ new Map();
6220
+ entries.set("Producer", textToPdfString("documents.js"));
6221
+ if (doc.metadata.title !== void 0) entries.set("Title", textToPdfString(doc.metadata.title));
6222
+ if (doc.metadata.author !== void 0) entries.set("Author", textToPdfString(doc.metadata.author));
6223
+ if (doc.metadata.subject !== void 0) entries.set("Subject", textToPdfString(doc.metadata.subject));
6224
+ if (doc.metadata.keywords !== void 0) entries.set("Keywords", textToPdfString(doc.metadata.keywords.join(", ")));
6225
+ if (doc.metadata.creator !== void 0) entries.set("Creator", textToPdfString(doc.metadata.creator));
6226
+ if (doc.metadata.createdIso !== void 0) entries.set("CreationDate", textToPdfString(formatPdfDate(doc.metadata.createdIso)));
6227
+ if (doc.metadata.modifiedIso !== void 0) entries.set("ModDate", textToPdfString(formatPdfDate(doc.metadata.modifiedIso)));
6228
+ return pdfDict(entries);
6229
+ }
6230
+ function computeFontFlags(standardName, metrics) {
6231
+ let flags = FLAG_NONSYMBOLIC;
6232
+ if (standardName.startsWith("Courier")) flags |= FLAG_FIXED_PITCH;
6233
+ if (standardName.startsWith("Times")) flags |= FLAG_SERIF;
6234
+ if (metrics.italicAngle !== 0) flags |= FLAG_ITALIC;
6235
+ if (standardName.includes("Bold")) flags |= FLAG_FORCE_BOLD;
6236
+ return flags;
6237
+ }
6238
+ function widthForWidthsArray(standardName, code) {
6239
+ if (STANDARD_METRICS[standardName].fixedWidth === void 0 && winAnsiGlyphName(code) === void 0) return 0;
6240
+ return widthOfCode(standardName, code);
6241
+ }
6242
+ function buildFontObjects(standardName, descriptorRef) {
6243
+ const metrics = STANDARD_METRICS[standardName];
6244
+ const widths = [];
6245
+ for (let code = FIRST_CHAR; code <= LAST_CHAR; code++) widths.push(pdfNum(widthForWidthsArray(standardName, code)));
6246
+ return {
6247
+ font: pdfDict({
6248
+ Type: pdfName("Font"),
6249
+ Subtype: pdfName("Type1"),
6250
+ BaseFont: pdfName(standardName),
6251
+ Encoding: pdfName("WinAnsiEncoding"),
6252
+ FirstChar: pdfNum(FIRST_CHAR),
6253
+ LastChar: pdfNum(LAST_CHAR),
6254
+ Widths: pdfArray(widths),
6255
+ FontDescriptor: descriptorRef
6256
+ }),
6257
+ descriptor: pdfDict({
6258
+ Type: pdfName("FontDescriptor"),
6259
+ FontName: pdfName(standardName),
6260
+ Flags: pdfNum(computeFontFlags(standardName, metrics)),
6261
+ FontBBox: pdfArray(metrics.fontBBox.map((n) => pdfNum(n))),
6262
+ ItalicAngle: pdfNum(metrics.italicAngle),
6263
+ Ascent: pdfNum(metrics.ascender),
6264
+ Descent: pdfNum(metrics.descender),
6265
+ CapHeight: pdfNum(metrics.capHeight),
6266
+ XHeight: pdfNum(metrics.xHeight),
6267
+ StemV: pdfNum(standardName.includes("Bold") ? NOMINAL_STEM_V_BOLD : NOMINAL_STEM_V_REGULAR)
6268
+ })
6269
+ };
6270
+ }
6271
+ function prepareJpegImage(bytes) {
6272
+ const info = readJpegInfo(bytes);
6273
+ const colorSpace = info.components === 1 ? "DeviceGray" : info.components === 4 ? "DeviceCMYK" : "DeviceRGB";
6274
+ const entries = /* @__PURE__ */ new Map([
6275
+ ["Type", pdfName("XObject")],
6276
+ ["Subtype", pdfName("Image")],
6277
+ ["Width", pdfNum(info.width)],
6278
+ ["Height", pdfNum(info.height)],
6279
+ ["ColorSpace", pdfName(colorSpace)],
6280
+ ["BitsPerComponent", pdfNum(info.precision)],
6281
+ ["Filter", pdfName("DCTDecode")]
6282
+ ]);
6283
+ if (info.components === 4 && (info.adobeTransform === 2 || info.adobeTransform === void 0)) entries.set("Decode", pdfArray([
6284
+ 1,
6285
+ 0,
6286
+ 1,
6287
+ 0,
6288
+ 1,
6289
+ 0,
6290
+ 1,
6291
+ 0
6292
+ ].map((n) => pdfNum(n))));
6293
+ return {
6294
+ dict: pdfDict(entries),
6295
+ raw: bytes
6296
+ };
6297
+ }
6298
+ function pngImageDict(width, height, colorSpace, compress) {
6299
+ const entries = /* @__PURE__ */ new Map([
6300
+ ["Type", pdfName("XObject")],
6301
+ ["Subtype", pdfName("Image")],
6302
+ ["Width", pdfNum(width)],
6303
+ ["Height", pdfNum(height)],
6304
+ ["ColorSpace", pdfName(colorSpace)],
6305
+ ["BitsPerComponent", pdfNum(8)]
6306
+ ]);
6307
+ if (compress) entries.set("Filter", pdfName("FlateDecode"));
6308
+ return pdfDict(entries);
6309
+ }
6310
+ function preparePngImage(bytes, compress) {
6311
+ const raw = decodePng(bytes);
6312
+ const colorSpace = raw.channels === 1 ? "DeviceGray" : "DeviceRGB";
6313
+ return {
6314
+ dict: pngImageDict(raw.width, raw.height, colorSpace, compress),
6315
+ raw: compress ? deflate(raw.data) : raw.data,
6316
+ alpha: raw.alpha === void 0 ? void 0 : {
6317
+ dict: pngImageDict(raw.width, raw.height, "DeviceGray", compress),
6318
+ raw: compress ? deflate(raw.alpha) : raw.alpha
6319
+ }
6320
+ };
6321
+ }
6322
+ function prepareImage(asset, compress) {
6323
+ const bytes = (0, ooxml_js.base64ToBytes)(asset.base64);
6324
+ return asset.format === "jpeg" ? prepareJpegImage(bytes) : preparePngImage(bytes, compress);
6325
+ }
6326
+ function buildLinkAnnotDict(link) {
6327
+ return pdfDict({
6328
+ Type: pdfName("Annot"),
6329
+ Subtype: pdfName("Link"),
6330
+ Rect: pdfArray([
6331
+ link.xPt,
6332
+ link.yPt,
6333
+ link.xPt + link.widthPt,
6334
+ link.yPt + link.heightPt
6335
+ ].map((n) => pdfNum(n))),
6336
+ Border: pdfArray([
6337
+ 0,
6338
+ 0,
6339
+ 0
6340
+ ].map((n) => pdfNum(n))),
6341
+ A: pdfDict({
6342
+ Type: pdfName("Action"),
6343
+ S: pdfName("URI"),
6344
+ URI: pdfHexString(new TextEncoder().encode(link.uri))
6345
+ })
6346
+ });
6347
+ }
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);
6535
6657
  }
6536
- return pdfHexString(bytes);
6537
- }
6538
- function pad2(n) {
6539
- return n.toString().padStart(2, "0");
6540
- }
6541
- function formatPdfDate(iso) {
6542
- const date = new Date(iso);
6543
- return `D:${date.getUTCFullYear()}${pad2(date.getUTCMonth() + 1)}${pad2(date.getUTCDate())}${pad2(date.getUTCHours())}${pad2(date.getUTCMinutes())}${pad2(date.getUTCSeconds())}Z`;
6544
- }
6545
- function buildInfoDict(doc) {
6546
- const entries = /* @__PURE__ */ new Map();
6547
- entries.set("Producer", textToPdfString("documents.js"));
6548
- if (doc.metadata.title !== void 0) entries.set("Title", textToPdfString(doc.metadata.title));
6549
- if (doc.metadata.author !== void 0) entries.set("Author", textToPdfString(doc.metadata.author));
6550
- if (doc.metadata.subject !== void 0) entries.set("Subject", textToPdfString(doc.metadata.subject));
6551
- if (doc.metadata.keywords !== void 0) entries.set("Keywords", textToPdfString(doc.metadata.keywords.join(", ")));
6552
- if (doc.metadata.creator !== void 0) entries.set("Creator", textToPdfString(doc.metadata.creator));
6553
- if (doc.metadata.createdIso !== void 0) entries.set("CreationDate", textToPdfString(formatPdfDate(doc.metadata.createdIso)));
6554
- if (doc.metadata.modifiedIso !== void 0) entries.set("ModDate", textToPdfString(formatPdfDate(doc.metadata.modifiedIso)));
6555
- return pdfDict(entries);
6658
+ return /* @__PURE__ */ new Uint8Array(0);
6556
6659
  }
6557
- function computeFontFlags(standardName, metrics) {
6558
- let flags = FLAG_NONSYMBOLIC;
6559
- if (standardName.startsWith("Courier")) flags |= FLAG_FIXED_PITCH;
6560
- if (standardName.startsWith("Times")) flags |= FLAG_SERIF;
6561
- if (metrics.italicAngle !== 0) flags |= FLAG_ITALIC;
6562
- if (standardName.includes("Bold")) flags |= FLAG_FORCE_BOLD;
6563
- return flags;
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
+ };
6564
6689
  }
6565
- function widthForWidthsArray(standardName, code) {
6566
- if (STANDARD_METRICS[standardName].fixedWidth === void 0 && winAnsiGlyphName(code) === void 0) return 0;
6567
- return widthOfCode(standardName, code);
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);
6568
6695
  }
6569
- function buildFontObjects(standardName, descriptorRef) {
6570
- const metrics = STANDARD_METRICS[standardName];
6571
- const widths = [];
6572
- for (let code = FIRST_CHAR; code <= LAST_CHAR; code++) widths.push(pdfNum(widthForWidthsArray(standardName, code)));
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
+ };
6573
6710
  return {
6574
- font: pdfDict({
6575
- Type: pdfName("Font"),
6576
- Subtype: pdfName("Type1"),
6577
- BaseFont: pdfName(standardName),
6578
- Encoding: pdfName("WinAnsiEncoding"),
6579
- FirstChar: pdfNum(FIRST_CHAR),
6580
- LastChar: pdfNum(LAST_CHAR),
6581
- Widths: pdfArray(widths),
6582
- FontDescriptor: descriptorRef
6583
- }),
6584
- descriptor: pdfDict({
6585
- Type: pdfName("FontDescriptor"),
6586
- FontName: pdfName(standardName),
6587
- Flags: pdfNum(computeFontFlags(standardName, metrics)),
6588
- FontBBox: pdfArray(metrics.fontBBox.map((n) => pdfNum(n))),
6589
- ItalicAngle: pdfNum(metrics.italicAngle),
6590
- Ascent: pdfNum(metrics.ascender),
6591
- Descent: pdfNum(metrics.descender),
6592
- CapHeight: pdfNum(metrics.capHeight),
6593
- XHeight: pdfNum(metrics.xHeight),
6594
- StemV: pdfNum(standardName.includes("Bold") ? NOMINAL_STEM_V_BOLD : NOMINAL_STEM_V_REGULAR)
6595
- })
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
6596
6720
  };
6597
6721
  }
6598
- function prepareJpegImage(bytes) {
6599
- const info = readJpegInfo(bytes);
6600
- const colorSpace = info.components === 1 ? "DeviceGray" : info.components === 4 ? "DeviceCMYK" : "DeviceRGB";
6601
- const entries = /* @__PURE__ */ new Map([
6602
- ["Type", pdfName("XObject")],
6603
- ["Subtype", pdfName("Image")],
6604
- ["Width", pdfNum(info.width)],
6605
- ["Height", pdfNum(info.height)],
6606
- ["ColorSpace", pdfName(colorSpace)],
6607
- ["BitsPerComponent", pdfNum(info.precision)],
6608
- ["Filter", pdfName("DCTDecode")]
6609
- ]);
6610
- if (info.components === 4 && (info.adobeTransform === 2 || info.adobeTransform === void 0)) entries.set("Decode", pdfArray([
6611
- 1,
6612
- 0,
6613
- 1,
6614
- 0,
6615
- 1,
6616
- 0,
6617
- 1,
6618
- 0
6619
- ].map((n) => pdfNum(n))));
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
+ });
6620
6731
  return {
6621
- dict: pdfDict(entries),
6622
- raw: bytes
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
6623
6738
  };
6624
6739
  }
6625
- function pngImageDict(width, height, colorSpace, compress) {
6626
- const entries = /* @__PURE__ */ new Map([
6627
- ["Type", pdfName("XObject")],
6628
- ["Subtype", pdfName("Image")],
6629
- ["Width", pdfNum(width)],
6630
- ["Height", pdfNum(height)],
6631
- ["ColorSpace", pdfName(colorSpace)],
6632
- ["BitsPerComponent", pdfNum(8)]
6633
- ]);
6634
- if (compress) entries.set("Filter", pdfName("FlateDecode"));
6635
- return pdfDict(entries);
6636
- }
6637
- function preparePngImage(bytes, compress) {
6638
- const raw = decodePng(bytes);
6639
- const colorSpace = raw.channels === 1 ? "DeviceGray" : "DeviceRGB";
6740
+ function imagePlacementFrom(matrix) {
6741
+ const rotationDeg = matrixRotationDegrees(matrix);
6640
6742
  return {
6641
- dict: pngImageDict(raw.width, raw.height, colorSpace, compress),
6642
- raw: compress ? deflate(raw.data) : raw.data,
6643
- alpha: raw.alpha === void 0 ? void 0 : {
6644
- dict: pngImageDict(raw.width, raw.height, "DeviceGray", compress),
6645
- raw: compress ? deflate(raw.alpha) : raw.alpha
6646
- }
6743
+ xPt: matrix[4],
6744
+ yPt: matrix[5],
6745
+ widthPt: matrixScaleX(matrix),
6746
+ heightPt: matrixScaleY(matrix),
6747
+ rotationDeg: rotationDeg !== 0 ? rotationDeg : void 0
6647
6748
  };
6648
6749
  }
6649
- function prepareImage(asset, compress) {
6650
- const bytes = (0, ooxml_js.base64ToBytes)(asset.base64);
6651
- return asset.format === "jpeg" ? prepareJpegImage(bytes) : preparePngImage(bytes, compress);
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;
6652
6759
  }
6653
- function buildLinkAnnotDict(link) {
6654
- return pdfDict({
6655
- Type: pdfName("Annot"),
6656
- Subtype: pdfName("Link"),
6657
- Rect: pdfArray([
6658
- link.xPt,
6659
- link.yPt,
6660
- link.xPt + link.widthPt,
6661
- link.yPt + link.heightPt
6662
- ].map((n) => pdfNum(n))),
6663
- Border: pdfArray([
6664
- 0,
6665
- 0,
6666
- 0
6667
- ].map((n) => pdfNum(n))),
6668
- A: pdfDict({
6669
- Type: pdfName("Action"),
6670
- S: pdfName("URI"),
6671
- URI: pdfHexString(new TextEncoder().encode(link.uri))
6672
- })
6673
- });
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;
6674
6770
  }
6675
- function isLinkItem(item) {
6676
- return item.kind === "link";
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
+ };
6677
6782
  }
6678
- function xrefEntry(offset, generation, inUse) {
6679
- return `${offset.toString().padStart(10, "0")} ${generation.toString().padStart(5, "0")} ${inUse ? "n" : "f"} \n`;
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
+ };
6680
6791
  }
6681
- function writePdf(doc, options = {}) {
6682
- const compress = options.compress ?? true;
6683
- const measurer = createStandardFontMeasurer();
6684
- let nextObjNum = 1;
6685
- const catalogNum = nextObjNum++;
6686
- const pagesNum = nextObjNum++;
6687
- const infoNum = nextObjNum++;
6688
- const fontNames = /* @__PURE__ */ new Set();
6689
- const imageIds = /* @__PURE__ */ new Set();
6690
- 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);
6691
- else if (item.kind === "image") imageIds.add(item.imageId);
6692
- const fontAllocs = /* @__PURE__ */ new Map();
6693
- for (const [index, name] of [...fontNames].sort().entries()) {
6694
- const fontNum = nextObjNum++;
6695
- const descNum = nextObjNum++;
6696
- fontAllocs.set(name, {
6697
- fontNum,
6698
- descNum,
6699
- resourceName: `F${index + 1}`
6700
- });
6701
- }
6702
- const imageAllocs = /* @__PURE__ */ new Map();
6703
- for (const [index, imageId] of [...imageIds].sort().entries()) {
6704
- const asset = doc.images[imageId];
6705
- if (asset === void 0) throw new Error(`LayoutDocument references image "${imageId}" but it is not present in images`);
6706
- const prepared = prepareImage(asset, compress);
6707
- const imageNum = nextObjNum++;
6708
- const smaskNum = prepared.alpha === void 0 ? void 0 : nextObjNum++;
6709
- imageAllocs.set(imageId, {
6710
- imageNum,
6711
- smaskNum,
6712
- resourceName: `Im${index + 1}`,
6713
- prepared
6714
- });
6715
- }
6716
- const pageAllocs = doc.pages.map(() => ({
6717
- pageNum: nextObjNum++,
6718
- contentsNum: nextObjNum++
6719
- }));
6720
- const objects = [];
6721
- objects.push({
6722
- num: catalogNum,
6723
- value: pdfDict({
6724
- Type: pdfName("Catalog"),
6725
- Pages: pdfRef(pagesNum, 0)
6726
- })
6727
- });
6728
- objects.push({
6729
- num: pagesNum,
6730
- value: pdfDict({
6731
- Type: pdfName("Pages"),
6732
- Kids: pdfArray(pageAllocs.map((p) => pdfRef(p.pageNum, 0))),
6733
- Count: pdfNum(doc.pages.length)
6734
- })
6735
- });
6736
- objects.push({
6737
- num: infoNum,
6738
- value: buildInfoDict(doc)
6739
- });
6740
- for (const [standardName, alloc] of fontAllocs) {
6741
- const { font, descriptor } = buildFontObjects(standardName, pdfRef(alloc.descNum, 0));
6742
- objects.push({
6743
- num: alloc.fontNum,
6744
- 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)
6745
6809
  });
6746
- objects.push({
6747
- num: alloc.descNum,
6748
- value: descriptor
6810
+ const p2 = applyMatrix(pageMatrix, {
6811
+ x: Math.max(x1, x2),
6812
+ y: Math.max(y1, y2)
6749
6813
  });
6750
- }
6751
- for (const alloc of imageAllocs.values()) {
6752
- if (alloc.smaskNum !== void 0 && alloc.prepared.alpha !== void 0) {
6753
- alloc.prepared.dict.entries.set("SMask", pdfRef(alloc.smaskNum, 0));
6754
- objects.push({
6755
- num: alloc.smaskNum,
6756
- value: pdfStream(alloc.prepared.alpha.dict, alloc.prepared.alpha.raw)
6757
- });
6758
- }
6759
- objects.push({
6760
- num: alloc.imageNum,
6761
- 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)
6762
6821
  });
6763
6822
  }
6764
- const resourceEntries = /* @__PURE__ */ new Map();
6765
- if (fontAllocs.size > 0) resourceEntries.set("Font", pdfDict(new Map([...fontAllocs.values()].map((alloc) => [alloc.resourceName, pdfRef(alloc.fontNum, 0)]))));
6766
- if (imageAllocs.size > 0) resourceEntries.set("XObject", pdfDict(new Map([...imageAllocs.values()].map((alloc) => [alloc.resourceName, pdfRef(alloc.imageNum, 0)]))));
6767
- const resourcesDict = pdfDict(resourceEntries);
6768
- const context = {
6769
- measurer,
6770
- resolveFont: (font) => {
6771
- const standardName = resolveStandardFont(font.family, font.weight === "bold", font.style === "italic").standardName;
6772
- const alloc = fontAllocs.get(standardName);
6773
- if (alloc === void 0) throw new Error(`font "${standardName}" was not pre-allocated -- this is a writePdf internal invariant violation`);
6774
- return {
6775
- resourceName: alloc.resourceName,
6776
- standardName
6777
- };
6778
- },
6779
- resolveImage: (imageId) => {
6780
- const alloc = imageAllocs.get(imageId);
6781
- if (alloc === void 0) throw new Error(`image "${imageId}" was not pre-allocated -- this is a writePdf internal invariant violation`);
6782
- return { resourceName: alloc.resourceName };
6783
- }
6784
- };
6785
- doc.pages.forEach((page, pageIndex) => {
6786
- throwIfAborted(options.signal);
6787
- const { pageNum, contentsNum } = pageAllocs[pageIndex];
6788
- const { bytes: contentBytes, substitutions } = writeContentStream(page.items, context);
6789
- for (const substitution of substitutions) options.onSubstitution?.(substitution, { pageIndex });
6790
- const finalContentBytes = compress ? deflate(contentBytes) : contentBytes;
6791
- const contentsDict = pdfDict(compress ? { Filter: pdfName("FlateDecode") } : {});
6792
- objects.push({
6793
- num: contentsNum,
6794
- value: pdfStream(contentsDict, finalContentBytes)
6795
- });
6796
- const annots = page.items.filter(isLinkItem).map((link) => buildLinkAnnotDict(link));
6797
- const pageEntries = /* @__PURE__ */ new Map([
6798
- ["Type", pdfName("Page")],
6799
- ["Parent", pdfRef(pagesNum, 0)],
6800
- ["MediaBox", pdfArray([
6801
- 0,
6802
- 0,
6803
- page.widthPt,
6804
- page.heightPt
6805
- ].map((n) => pdfNum(n)))],
6806
- ["Resources", resourcesDict],
6807
- ["Contents", pdfRef(contentsNum, 0)]
6808
- ]);
6809
- if (annots.length > 0) pageEntries.set("Annots", pdfArray(annots));
6810
- objects.push({
6811
- num: pageNum,
6812
- value: pdfDict(pageEntries)
6813
- });
6814
- });
6815
- const writer = new ByteWriter();
6816
- writer.writeAscii("%PDF-1.7\n");
6817
- const offsets = /* @__PURE__ */ new Map();
6818
- for (const { num, value } of objects) {
6819
- offsets.set(num, writer.length);
6820
- writer.writeAscii(`${num} 0 obj\n`);
6821
- writeObject(writer, value);
6822
- 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);
6823
6841
  }
6824
- const maxObjNum = nextObjNum - 1;
6825
- const xrefOffset = writer.length;
6826
- writer.writeAscii("xref\n");
6827
- writer.writeAscii(`0 ${maxObjNum + 1}\n`);
6828
- writer.writeAscii(xrefEntry(0, 65535, false));
6829
- for (let num = 1; num <= maxObjNum; num++) {
6830
- const offset = offsets.get(num);
6831
- if (offset === void 0) throw new Error(`object ${num} was allocated but never written -- this is a writePdf internal invariant violation`);
6832
- 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;
6833
6848
  }
6834
- writer.writeAscii("trailer\n");
6835
- writeObject(writer, pdfDict({
6836
- Size: pdfNum(maxObjNum + 1),
6837
- Root: pdfRef(catalogNum, 0),
6838
- Info: pdfRef(infoNum, 0)
6839
- }));
6840
- writer.writeAscii("\nstartxref\n");
6841
- writer.writeAscii(`${xrefOffset}\n`);
6842
- writer.writeAscii("%%EOF");
6843
- return writer.toBytes();
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
+ };
6844
6877
  }
6845
6878
  //#endregion
6846
6879
  //#region src/pdf/codec.ts
@@ -7472,7 +7505,8 @@ function convertSlide(slide, measurer, images) {
7472
7505
  return {
7473
7506
  widthPt: slide.size.widthPt,
7474
7507
  heightPt: slide.size.heightPt,
7475
- items
7508
+ items,
7509
+ ...slide.notes.length > 0 ? { notes: slide.notes } : {}
7476
7510
  };
7477
7511
  }
7478
7512
  function convertPresentationToLayout(doc, options) {
@@ -7710,7 +7744,7 @@ function reconstructSlide(page, images) {
7710
7744
  heightPt: page.heightPt
7711
7745
  },
7712
7746
  shapes: [...imageShapes, ...textShapes],
7713
- notes: ""
7747
+ notes: page.notes ?? ""
7714
7748
  };
7715
7749
  }
7716
7750
  function splitLineByLargeGaps(line) {