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/README.md +1 -0
- package/dist/index.cjs +748 -714
- package/dist/index.d.cts +15 -12
- package/dist/index.d.ts +15 -12
- package/dist/index.js +748 -714
- package/package.json +4 -2
package/dist/index.cjs
CHANGED
|
@@ -99,7 +99,8 @@ const LayoutItemSchema = zod.z.discriminatedUnion("kind", [
|
|
|
99
99
|
const LayoutPageSchema = zod.z.object({
|
|
100
100
|
widthPt: zod.z.number().positive(),
|
|
101
101
|
heightPt: zod.z.number().positive(),
|
|
102
|
-
items: zod.z.array(LayoutItemSchema)
|
|
102
|
+
items: zod.z.array(LayoutItemSchema),
|
|
103
|
+
notes: zod.z.string().optional()
|
|
103
104
|
});
|
|
104
105
|
const LayoutImageAssetSchema = zod.z.object({
|
|
105
106
|
format: zod.z.enum(["png", "jpeg"]),
|
|
@@ -5793,424 +5794,96 @@ function runContentStream(bytes, resources, initialState, context, items, depth)
|
|
|
5793
5794
|
}
|
|
5794
5795
|
}
|
|
5795
5796
|
//#endregion
|
|
5796
|
-
//#region src/
|
|
5797
|
-
const
|
|
5798
|
-
|
|
5799
|
-
|
|
5800
|
-
|
|
5801
|
-
|
|
5802
|
-
|
|
5803
|
-
|
|
5804
|
-
|
|
5805
|
-
|
|
5806
|
-
|
|
5807
|
-
|
|
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
|
|
5810
|
-
const
|
|
5811
|
-
const
|
|
5812
|
-
|
|
5813
|
-
|
|
5814
|
-
|
|
5815
|
-
|
|
5816
|
-
|
|
5817
|
-
|
|
5818
|
-
|
|
5819
|
-
|
|
5820
|
-
|
|
5821
|
-
|
|
5822
|
-
|
|
5823
|
-
|
|
5824
|
-
|
|
5825
|
-
|
|
5826
|
-
|
|
5827
|
-
|
|
5828
|
-
|
|
5829
|
-
|
|
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
|
|
5833
|
-
const
|
|
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
|
-
|
|
5846
|
-
|
|
5847
|
-
|
|
5848
|
-
|
|
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
|
|
5852
|
-
if (
|
|
5853
|
-
|
|
5854
|
-
|
|
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
|
|
5857
|
-
|
|
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
|
|
5907
|
-
const
|
|
5908
|
-
|
|
5909
|
-
if (
|
|
5910
|
-
|
|
5911
|
-
|
|
5912
|
-
|
|
5913
|
-
|
|
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
|
|
5870
|
+
return samples;
|
|
5918
5871
|
}
|
|
5919
|
-
function
|
|
5920
|
-
|
|
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
|
|
5948
|
-
|
|
5949
|
-
|
|
5950
|
-
|
|
5951
|
-
|
|
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
|
|
5954
|
-
|
|
5955
|
-
const
|
|
5956
|
-
|
|
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
|
|
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
|
|
6558
|
-
|
|
6559
|
-
|
|
6560
|
-
|
|
6561
|
-
|
|
6562
|
-
|
|
6563
|
-
|
|
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
|
|
6566
|
-
if (
|
|
6567
|
-
return
|
|
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
|
|
6570
|
-
const
|
|
6571
|
-
const
|
|
6572
|
-
|
|
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
|
-
|
|
6575
|
-
|
|
6576
|
-
|
|
6577
|
-
|
|
6578
|
-
|
|
6579
|
-
|
|
6580
|
-
|
|
6581
|
-
|
|
6582
|
-
|
|
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
|
|
6599
|
-
const
|
|
6600
|
-
|
|
6601
|
-
|
|
6602
|
-
|
|
6603
|
-
|
|
6604
|
-
|
|
6605
|
-
|
|
6606
|
-
|
|
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
|
-
|
|
6622
|
-
|
|
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
|
|
6626
|
-
const
|
|
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
|
-
|
|
6642
|
-
|
|
6643
|
-
|
|
6644
|
-
|
|
6645
|
-
|
|
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
|
|
6650
|
-
const
|
|
6651
|
-
|
|
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
|
|
6654
|
-
return
|
|
6655
|
-
|
|
6656
|
-
|
|
6657
|
-
|
|
6658
|
-
|
|
6659
|
-
|
|
6660
|
-
|
|
6661
|
-
|
|
6662
|
-
|
|
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
|
|
6676
|
-
|
|
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
|
|
6679
|
-
|
|
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
|
|
6682
|
-
const
|
|
6683
|
-
|
|
6684
|
-
|
|
6685
|
-
const
|
|
6686
|
-
|
|
6687
|
-
|
|
6688
|
-
|
|
6689
|
-
|
|
6690
|
-
|
|
6691
|
-
|
|
6692
|
-
|
|
6693
|
-
|
|
6694
|
-
const
|
|
6695
|
-
const
|
|
6696
|
-
|
|
6697
|
-
|
|
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
|
-
|
|
6747
|
-
|
|
6748
|
-
|
|
6810
|
+
const p2 = applyMatrix(pageMatrix, {
|
|
6811
|
+
x: Math.max(x1, x2),
|
|
6812
|
+
y: Math.max(y1, y2)
|
|
6749
6813
|
});
|
|
6750
|
-
|
|
6751
|
-
|
|
6752
|
-
|
|
6753
|
-
|
|
6754
|
-
|
|
6755
|
-
|
|
6756
|
-
|
|
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
|
-
|
|
6765
|
-
|
|
6766
|
-
|
|
6767
|
-
const
|
|
6768
|
-
|
|
6769
|
-
|
|
6770
|
-
|
|
6771
|
-
|
|
6772
|
-
|
|
6773
|
-
|
|
6774
|
-
|
|
6775
|
-
|
|
6776
|
-
|
|
6777
|
-
|
|
6778
|
-
|
|
6779
|
-
|
|
6780
|
-
|
|
6781
|
-
|
|
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
|
-
|
|
6825
|
-
|
|
6826
|
-
|
|
6827
|
-
|
|
6828
|
-
|
|
6829
|
-
|
|
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
|
-
|
|
6835
|
-
|
|
6836
|
-
|
|
6837
|
-
|
|
6838
|
-
|
|
6839
|
-
|
|
6840
|
-
|
|
6841
|
-
|
|
6842
|
-
|
|
6843
|
-
|
|
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) {
|