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.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/
|
|
5796
|
-
const
|
|
5797
|
-
|
|
5798
|
-
|
|
5799
|
-
|
|
5800
|
-
|
|
5801
|
-
|
|
5802
|
-
|
|
5803
|
-
|
|
5804
|
-
|
|
5805
|
-
|
|
5806
|
-
|
|
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
|
|
5809
|
-
const
|
|
5810
|
-
const
|
|
5811
|
-
|
|
5812
|
-
|
|
5813
|
-
|
|
5814
|
-
|
|
5815
|
-
|
|
5816
|
-
|
|
5817
|
-
|
|
5818
|
-
|
|
5819
|
-
|
|
5820
|
-
|
|
5821
|
-
|
|
5822
|
-
|
|
5823
|
-
|
|
5824
|
-
|
|
5825
|
-
|
|
5826
|
-
|
|
5827
|
-
|
|
5828
|
-
|
|
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
|
|
5832
|
-
const
|
|
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
|
-
|
|
5845
|
-
|
|
5846
|
-
|
|
5847
|
-
|
|
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
|
|
5851
|
-
if (
|
|
5852
|
-
|
|
5853
|
-
|
|
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
|
|
5856
|
-
|
|
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
|
|
5906
|
-
const
|
|
5907
|
-
|
|
5908
|
-
if (
|
|
5909
|
-
|
|
5910
|
-
|
|
5911
|
-
|
|
5912
|
-
|
|
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
|
|
5869
|
+
return samples;
|
|
5917
5870
|
}
|
|
5918
|
-
function
|
|
5919
|
-
|
|
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
|
|
5947
|
-
|
|
5948
|
-
|
|
5949
|
-
|
|
5950
|
-
|
|
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
|
|
5953
|
-
|
|
5954
|
-
const
|
|
5955
|
-
|
|
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
|
|
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
|
|
6557
|
-
|
|
6558
|
-
|
|
6559
|
-
|
|
6560
|
-
|
|
6561
|
-
|
|
6562
|
-
|
|
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
|
|
6565
|
-
if (
|
|
6566
|
-
return
|
|
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
|
|
6569
|
-
const
|
|
6570
|
-
const
|
|
6571
|
-
|
|
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
|
-
|
|
6574
|
-
|
|
6575
|
-
|
|
6576
|
-
|
|
6577
|
-
|
|
6578
|
-
|
|
6579
|
-
|
|
6580
|
-
|
|
6581
|
-
|
|
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
|
|
6598
|
-
const
|
|
6599
|
-
|
|
6600
|
-
|
|
6601
|
-
|
|
6602
|
-
|
|
6603
|
-
|
|
6604
|
-
|
|
6605
|
-
|
|
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
|
-
|
|
6621
|
-
|
|
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
|
|
6625
|
-
const
|
|
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
|
-
|
|
6641
|
-
|
|
6642
|
-
|
|
6643
|
-
|
|
6644
|
-
|
|
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
|
|
6649
|
-
const
|
|
6650
|
-
|
|
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
|
|
6653
|
-
return
|
|
6654
|
-
|
|
6655
|
-
|
|
6656
|
-
|
|
6657
|
-
|
|
6658
|
-
|
|
6659
|
-
|
|
6660
|
-
|
|
6661
|
-
|
|
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
|
|
6675
|
-
|
|
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
|
|
6678
|
-
|
|
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
|
|
6681
|
-
const
|
|
6682
|
-
|
|
6683
|
-
|
|
6684
|
-
const
|
|
6685
|
-
|
|
6686
|
-
|
|
6687
|
-
|
|
6688
|
-
|
|
6689
|
-
|
|
6690
|
-
|
|
6691
|
-
|
|
6692
|
-
|
|
6693
|
-
const
|
|
6694
|
-
const
|
|
6695
|
-
|
|
6696
|
-
|
|
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
|
-
|
|
6746
|
-
|
|
6747
|
-
|
|
6809
|
+
const p2 = applyMatrix(pageMatrix, {
|
|
6810
|
+
x: Math.max(x1, x2),
|
|
6811
|
+
y: Math.max(y1, y2)
|
|
6748
6812
|
});
|
|
6749
|
-
|
|
6750
|
-
|
|
6751
|
-
|
|
6752
|
-
|
|
6753
|
-
|
|
6754
|
-
|
|
6755
|
-
|
|
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
|
-
|
|
6764
|
-
|
|
6765
|
-
|
|
6766
|
-
const
|
|
6767
|
-
|
|
6768
|
-
|
|
6769
|
-
|
|
6770
|
-
|
|
6771
|
-
|
|
6772
|
-
|
|
6773
|
-
|
|
6774
|
-
|
|
6775
|
-
|
|
6776
|
-
|
|
6777
|
-
|
|
6778
|
-
|
|
6779
|
-
|
|
6780
|
-
|
|
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
|
-
|
|
6824
|
-
|
|
6825
|
-
|
|
6826
|
-
|
|
6827
|
-
|
|
6828
|
-
|
|
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
|
-
|
|
6834
|
-
|
|
6835
|
-
|
|
6836
|
-
|
|
6837
|
-
|
|
6838
|
-
|
|
6839
|
-
|
|
6840
|
-
|
|
6841
|
-
|
|
6842
|
-
|
|
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) {
|