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