@uurtech/jdf-cli 0.2.2 → 0.2.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.js +251 -69
- package/dist/jdf-schema.json +1 -0
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -712,7 +712,7 @@ function columnBands(rows) {
|
|
|
712
712
|
return bands.map(({ x0, x1 }) => ({ x0, x1 }));
|
|
713
713
|
}
|
|
714
714
|
var numeric = (s) => /^[\s$€£¥+\-−–]*[\d.,]+\s*(%|ms|s|k|m|b|M|K|B|x|×)?\s*(\/\w+)?$/i.test(s.trim()) || /^[+\-−]?\d/.test(s.trim()) && /\d$/.test(s.trim().replace(/[%)]$/, ""));
|
|
715
|
-
function detectTables(runs, shapes, pageWidthMm) {
|
|
715
|
+
function detectTables(runs, shapes, pageWidthMm, gutters = []) {
|
|
716
716
|
const out = [];
|
|
717
717
|
const used = /* @__PURE__ */ new Set();
|
|
718
718
|
const rows = groupRows(runs, () => false);
|
|
@@ -761,6 +761,14 @@ function detectTables(runs, shapes, pageWidthMm) {
|
|
|
761
761
|
i++;
|
|
762
762
|
continue;
|
|
763
763
|
}
|
|
764
|
+
if (gutters.length && !hasLattice) {
|
|
765
|
+
const b = best.bands;
|
|
766
|
+
const straddles = b.some((band, k) => k < b.length - 1 && gutters.some((g) => band.x1 <= g.x1 + 1 && b[k + 1].x0 >= g.x0 - 1) && band.x1 - band.x0 > 30 && b[k + 1].x1 - b[k + 1].x0 > 30);
|
|
767
|
+
if (straddles) {
|
|
768
|
+
i++;
|
|
769
|
+
continue;
|
|
770
|
+
}
|
|
771
|
+
}
|
|
764
772
|
const bands = best.bands;
|
|
765
773
|
const cellText = (row, b) => row.cells.filter((c) => c.x0 < bands[b].x1 - 0.2 && c.x1 > bands[b].x0 + 0.2).map((c) => c.run.text.trim()).join(" ").trim();
|
|
766
774
|
const grid = [];
|
|
@@ -848,8 +856,165 @@ function detectTables(runs, shapes, pageWidthMm) {
|
|
|
848
856
|
return out;
|
|
849
857
|
}
|
|
850
858
|
|
|
851
|
-
// ../../packages/jdf-pdf-import/src/
|
|
859
|
+
// ../../packages/jdf-pdf-import/src/columns.ts
|
|
860
|
+
function detectGutters(lines, bodyFontSize, pageWmm) {
|
|
861
|
+
const body = lines.filter((l) => l.text.trim().length > 0 && Math.abs(l.fontSize - bodyFontSize) <= 1.5 && l.width < pageWmm * 0.6 && l.width > 0);
|
|
862
|
+
if (body.length < 12) return [];
|
|
863
|
+
const minX = Math.min(...body.map((l) => l.x));
|
|
864
|
+
const maxX = Math.max(...body.map((l) => l.x + l.width));
|
|
865
|
+
const span = maxX - minX;
|
|
866
|
+
if (span < pageWmm * 0.4) return [];
|
|
867
|
+
const BIN = 0.5;
|
|
868
|
+
const n = Math.ceil(span / BIN) + 1;
|
|
869
|
+
const cov = new Array(n).fill(0);
|
|
870
|
+
for (const l of body) {
|
|
871
|
+
const a = Math.max(0, Math.floor((l.x - minX) / BIN)), b = Math.min(n, Math.ceil((l.x + l.width - minX) / BIN));
|
|
872
|
+
for (let i2 = a; i2 < b; i2++) cov[i2]++;
|
|
873
|
+
}
|
|
874
|
+
const noise = Math.max(2, Math.round(body.length * 0.05));
|
|
875
|
+
const gutters = [];
|
|
876
|
+
let i = 0;
|
|
877
|
+
while (i < n) {
|
|
878
|
+
if (cov[i] > noise) {
|
|
879
|
+
i++;
|
|
880
|
+
continue;
|
|
881
|
+
}
|
|
882
|
+
let j = i;
|
|
883
|
+
while (j < n && cov[j] <= noise) j++;
|
|
884
|
+
const g0 = minX + i * BIN, g1 = minX + j * BIN;
|
|
885
|
+
if (g1 - g0 >= 3 && g0 > minX + span * 0.15 && g1 < maxX - span * 0.15) gutters.push({ x0: g0, x1: g1 });
|
|
886
|
+
i = j;
|
|
887
|
+
}
|
|
888
|
+
const solid = gutters.every((g) => body.filter((l) => l.x + l.width <= g.x0 + 0.5).length >= 4 && body.filter((l) => l.x >= g.x1 - 0.5).length >= 4);
|
|
889
|
+
return solid ? gutters : [];
|
|
890
|
+
}
|
|
891
|
+
function orderByColumns(els, gutters, pageWmm) {
|
|
892
|
+
if (!gutters.length || els.length < 2) return els;
|
|
893
|
+
const bands = [];
|
|
894
|
+
let left = 0;
|
|
895
|
+
for (const g of gutters) {
|
|
896
|
+
bands.push({ x0: left, x1: g.x0 });
|
|
897
|
+
left = g.x1;
|
|
898
|
+
}
|
|
899
|
+
bands.push({ x0: left, x1: pageWmm });
|
|
900
|
+
const colOf = (e) => {
|
|
901
|
+
const x = e.position?.x ?? 0;
|
|
902
|
+
const k = bands.findIndex((b) => x >= b.x0 && x < b.x1);
|
|
903
|
+
if (k >= 0) return k;
|
|
904
|
+
let best = 0, d = Infinity;
|
|
905
|
+
bands.forEach((b, i) => {
|
|
906
|
+
const dd = Math.min(Math.abs(x - b.x0), Math.abs(x - b.x1));
|
|
907
|
+
if (dd < d) {
|
|
908
|
+
d = dd;
|
|
909
|
+
best = i;
|
|
910
|
+
}
|
|
911
|
+
});
|
|
912
|
+
return best;
|
|
913
|
+
};
|
|
914
|
+
const spans = (e) => {
|
|
915
|
+
const c = colOf(e);
|
|
916
|
+
if (c >= bands.length - 1) return false;
|
|
917
|
+
const right = (e.position?.x ?? 0) + (e.width ?? 0);
|
|
918
|
+
const next = bands[c + 1];
|
|
919
|
+
return right > (next.x0 + next.x1) / 2;
|
|
920
|
+
};
|
|
921
|
+
const sorted = els.slice().sort((a, b) => (a.position?.y ?? 0) - (b.position?.y ?? 0) || (a.position?.x ?? 0) - (b.position?.x ?? 0));
|
|
922
|
+
const out = [];
|
|
923
|
+
let band = [];
|
|
924
|
+
const flush = () => {
|
|
925
|
+
for (let c = 0; c < bands.length; c++) for (const e of band) if (colOf(e) === c) out.push(e);
|
|
926
|
+
band = [];
|
|
927
|
+
};
|
|
928
|
+
for (const e of sorted) {
|
|
929
|
+
if (spans(e)) {
|
|
930
|
+
flush();
|
|
931
|
+
out.push(e);
|
|
932
|
+
} else band.push(e);
|
|
933
|
+
}
|
|
934
|
+
flush();
|
|
935
|
+
return out;
|
|
936
|
+
}
|
|
937
|
+
|
|
938
|
+
// ../../packages/jdf-pdf-import/src/paragraphs.ts
|
|
852
939
|
var PT_TO_MM2 = 0.352778;
|
|
940
|
+
var LIST_START = /^\s*(?:[•◦▪●■\-–—*]\s|\(?\d{1,3}[.)]\s|\(?[a-zA-Z][.)]\s|[ivx]{1,4}[.)]\s)/;
|
|
941
|
+
var TERMINAL = /[.!?:;]["'”’)\]]*$/;
|
|
942
|
+
function textOf(e) {
|
|
943
|
+
if (e.type === "text") return String(e.content ?? "");
|
|
944
|
+
return (e.runs ?? []).map((r) => String(r.text ?? "")).join("");
|
|
945
|
+
}
|
|
946
|
+
function foldParagraphs(elements, meta, pageWmm) {
|
|
947
|
+
const foldable = (e) => !!meta.get(e) && !!e.position && (e.type === "text" && !e.heading || e.type === "richtext");
|
|
948
|
+
const out = [];
|
|
949
|
+
let i = 0;
|
|
950
|
+
while (i < elements.length) {
|
|
951
|
+
const first = elements[i];
|
|
952
|
+
if (!foldable(first)) {
|
|
953
|
+
out.push(first);
|
|
954
|
+
i++;
|
|
955
|
+
continue;
|
|
956
|
+
}
|
|
957
|
+
const para = [first];
|
|
958
|
+
let j = i + 1;
|
|
959
|
+
while (j < elements.length) {
|
|
960
|
+
const prev = para[para.length - 1], next = elements[j];
|
|
961
|
+
if (!foldable(next) || !continues(para, prev, next, meta)) break;
|
|
962
|
+
para.push(next);
|
|
963
|
+
j++;
|
|
964
|
+
}
|
|
965
|
+
out.push(para.length > 1 ? merge(para, meta, pageWmm) : first);
|
|
966
|
+
i = j;
|
|
967
|
+
}
|
|
968
|
+
return out;
|
|
969
|
+
}
|
|
970
|
+
function continues(para, prev, next, meta) {
|
|
971
|
+
const mp = meta.get(prev), mn = meta.get(next); meta.get(para[0]);
|
|
972
|
+
if (mp.face !== mn.face || Math.abs(mp.size - mn.size) >= 0.5) return false;
|
|
973
|
+
const lineH = mp.size * PT_TO_MM2;
|
|
974
|
+
const pitch = next.position.y - prev.position.y;
|
|
975
|
+
if (pitch < lineH * 0.9 || pitch > lineH * 1.75) return false;
|
|
976
|
+
const dx = next.position.x - (para.length === 1 ? prev.position.x : para[0].position.x);
|
|
977
|
+
if (para.length === 1 ? Math.abs(dx) > 6 : Math.abs(dx) > 1.2) return false;
|
|
978
|
+
if (para.length >= 2 && Math.abs(next.position.x - para[1].position.x) > 1.2) return false;
|
|
979
|
+
const nText = textOf(next), pText = textOf(prev).trimEnd();
|
|
980
|
+
if (LIST_START.test(nText) || !nText.trim()) return false;
|
|
981
|
+
const maxW = Math.max(mp.w, mn.w, ...para.map((e) => meta.get(e).w));
|
|
982
|
+
const full = mp.w >= maxW * 0.85;
|
|
983
|
+
const endsHyphen = /[-‐‑]$/.test(pText);
|
|
984
|
+
const softEnd = !TERMINAL.test(pText);
|
|
985
|
+
const startsLower = /^[a-z(\[]/.test(nText.trimStart());
|
|
986
|
+
return full || endsHyphen || softEnd && (startsLower || mp.w >= maxW * 0.6);
|
|
987
|
+
}
|
|
988
|
+
function merge(para, meta, pageWmm) {
|
|
989
|
+
const first = para[0], last = para[para.length - 1];
|
|
990
|
+
const m0 = meta.get(first);
|
|
991
|
+
const lineH = m0.size * PT_TO_MM2;
|
|
992
|
+
const x = Math.min(...para.map((e) => e.position.x));
|
|
993
|
+
const indent = first.position.x - x;
|
|
994
|
+
const right = Math.max(...para.map((e) => e.position.x + meta.get(e).w));
|
|
995
|
+
const pitch = (last.position.y - first.position.y) / (para.length - 1);
|
|
996
|
+
const width = Math.min(pageWmm - x, (right - x) * 1.2 + lineH * 0.4);
|
|
997
|
+
const joiner = () => "\n";
|
|
998
|
+
const style = { ...first.style ?? {}, lineHeight: Math.round(pitch / lineH * 100) / 100, ...indent > 0.5 ? { textIndent: Math.round(indent * 100) / 100 } : {} };
|
|
999
|
+
const allText = para.every((e) => e.type === "text" && !e.link);
|
|
1000
|
+
const base = { position: { x, y: first.position.y }, width: Math.round(width * 100) / 100, style };
|
|
1001
|
+
if (allText) {
|
|
1002
|
+
const content = para.map((e) => String(e.content ?? "").trim().replace(/[ \t]+/g, " ")).join(joiner());
|
|
1003
|
+
const { height: _hh2, ...firstRest } = first;
|
|
1004
|
+
return { ...firstRest, ...base, content };
|
|
1005
|
+
}
|
|
1006
|
+
const runs = [];
|
|
1007
|
+
for (const e of para) {
|
|
1008
|
+
const lineRuns = e.type === "text" ? [{ text: String(e.content ?? "").trim(), ...e.style?.fontWeight === "bold" ? { bold: true } : {}, ...e.style?.fontStyle === "italic" ? { italic: true } : {}, ...e.style?.color && e.style.color !== "#000000" ? { color: e.style.color } : {}, ...e.link ? { link: e.link } : {} }] : (e.runs ?? []).map((r) => ({ ...r }));
|
|
1009
|
+
if (runs.length) runs[runs.length - 1].text = `${String(runs[runs.length - 1].text).trimEnd()}${joiner()}`;
|
|
1010
|
+
runs.push(...lineRuns);
|
|
1011
|
+
}
|
|
1012
|
+
const { content: _c, heading: _h, link: _l, height: _hh, ...rest } = first;
|
|
1013
|
+
return { ...rest, ...base, type: "richtext", runs, style: { fontSize: style.fontSize, fontFamily: style.fontFamily, lineHeight: style.lineHeight, ...style.textIndent ? { textIndent: style.textIndent } : {}, ...style.opacity != null ? { opacity: style.opacity } : {} } };
|
|
1014
|
+
}
|
|
1015
|
+
|
|
1016
|
+
// ../../packages/jdf-pdf-import/src/core.ts
|
|
1017
|
+
var PT_TO_MM3 = 0.352778;
|
|
853
1018
|
function classifyFont(name) {
|
|
854
1019
|
const n = (name || "").toLowerCase();
|
|
855
1020
|
const bold = /bold|black|heavy|semibold|demibold|extrabold/.test(n);
|
|
@@ -959,10 +1124,10 @@ async function walkOps(page, OPS, viewport) {
|
|
|
959
1124
|
const minY = Math.min(...ys), maxY = Math.max(...ys);
|
|
960
1125
|
imagePositions.push({
|
|
961
1126
|
name,
|
|
962
|
-
x: minX *
|
|
963
|
-
y: minY *
|
|
964
|
-
w: (maxX - minX) *
|
|
965
|
-
h: (maxY - minY) *
|
|
1127
|
+
x: minX * PT_TO_MM3,
|
|
1128
|
+
y: minY * PT_TO_MM3,
|
|
1129
|
+
w: (maxX - minX) * PT_TO_MM3,
|
|
1130
|
+
h: (maxY - minY) * PT_TO_MM3,
|
|
966
1131
|
inline,
|
|
967
1132
|
maskFill
|
|
968
1133
|
});
|
|
@@ -978,13 +1143,13 @@ async function walkOps(page, OPS, viewport) {
|
|
|
978
1143
|
const br = toViewport(r.x + r.w, r.y);
|
|
979
1144
|
shapes.push({
|
|
980
1145
|
kind: "rect",
|
|
981
|
-
x: Math.min(tl.x, br.x) *
|
|
982
|
-
y: Math.min(tl.y, br.y) *
|
|
983
|
-
width: Math.abs(br.x - tl.x) *
|
|
984
|
-
height: Math.abs(br.y - tl.y) *
|
|
1146
|
+
x: Math.min(tl.x, br.x) * PT_TO_MM3,
|
|
1147
|
+
y: Math.min(tl.y, br.y) * PT_TO_MM3,
|
|
1148
|
+
width: Math.abs(br.x - tl.x) * PT_TO_MM3,
|
|
1149
|
+
height: Math.abs(br.y - tl.y) * PT_TO_MM3,
|
|
985
1150
|
fill: isFill ? gs.fill : void 0,
|
|
986
1151
|
stroke: isStroke ? gs.stroke : void 0,
|
|
987
|
-
strokeWidth: isStroke ? gs.lineWidth *
|
|
1152
|
+
strokeWidth: isStroke ? gs.lineWidth * PT_TO_MM3 : void 0,
|
|
988
1153
|
opacity: isFill ? gs.fillAlpha : gs.strokeAlpha
|
|
989
1154
|
});
|
|
990
1155
|
}
|
|
@@ -998,13 +1163,13 @@ async function walkOps(page, OPS, viewport) {
|
|
|
998
1163
|
const h = Math.abs(br.y - tl.y);
|
|
999
1164
|
shapes.push({
|
|
1000
1165
|
kind: "rect",
|
|
1001
|
-
x: x *
|
|
1002
|
-
y: y *
|
|
1003
|
-
width: w *
|
|
1004
|
-
height: h *
|
|
1166
|
+
x: x * PT_TO_MM3,
|
|
1167
|
+
y: y * PT_TO_MM3,
|
|
1168
|
+
width: w * PT_TO_MM3,
|
|
1169
|
+
height: h * PT_TO_MM3,
|
|
1005
1170
|
fill: isFill ? gs.fill : void 0,
|
|
1006
1171
|
stroke: isStroke ? gs.stroke : void 0,
|
|
1007
|
-
strokeWidth: isStroke ? gs.lineWidth *
|
|
1172
|
+
strokeWidth: isStroke ? gs.lineWidth * PT_TO_MM3 : void 0,
|
|
1008
1173
|
opacity: isFill ? gs.fillAlpha : gs.strokeAlpha
|
|
1009
1174
|
});
|
|
1010
1175
|
} else if (pathSegments.length === 2 && pathSegments[0].type === "M" && pathSegments[1].type === "L") {
|
|
@@ -1016,35 +1181,35 @@ async function walkOps(page, OPS, viewport) {
|
|
|
1016
1181
|
const minY = Math.min(va.y, vb.y);
|
|
1017
1182
|
const maxX = Math.max(va.x, vb.x);
|
|
1018
1183
|
const maxY = Math.max(va.y, vb.y);
|
|
1019
|
-
const x1Local = (va.x - minX) *
|
|
1020
|
-
const y1Local = (va.y - minY) *
|
|
1021
|
-
const x2Local = (vb.x - minX) *
|
|
1022
|
-
const y2Local = (vb.y - minY) *
|
|
1023
|
-
const wLocal = Math.max(0.05, (maxX - minX) *
|
|
1024
|
-
const hLocal = Math.max(0.05, (maxY - minY) *
|
|
1184
|
+
const x1Local = (va.x - minX) * PT_TO_MM3;
|
|
1185
|
+
const y1Local = (va.y - minY) * PT_TO_MM3;
|
|
1186
|
+
const x2Local = (vb.x - minX) * PT_TO_MM3;
|
|
1187
|
+
const y2Local = (vb.y - minY) * PT_TO_MM3;
|
|
1188
|
+
const wLocal = Math.max(0.05, (maxX - minX) * PT_TO_MM3);
|
|
1189
|
+
const hLocal = Math.max(0.05, (maxY - minY) * PT_TO_MM3);
|
|
1025
1190
|
const dx = Math.abs(va.x - vb.x);
|
|
1026
1191
|
const dy = Math.abs(va.y - vb.y);
|
|
1027
1192
|
const axisAligned = dx < 0.5 || dy < 0.5;
|
|
1028
1193
|
if (axisAligned) {
|
|
1029
1194
|
shapes.push({
|
|
1030
1195
|
kind: "line",
|
|
1031
|
-
x: minX *
|
|
1032
|
-
y: minY *
|
|
1196
|
+
x: minX * PT_TO_MM3,
|
|
1197
|
+
y: minY * PT_TO_MM3,
|
|
1033
1198
|
width: wLocal,
|
|
1034
1199
|
height: hLocal,
|
|
1035
1200
|
stroke: isStroke ? gs.stroke : void 0,
|
|
1036
|
-
strokeWidth: isStroke ? gs.lineWidth *
|
|
1201
|
+
strokeWidth: isStroke ? gs.lineWidth * PT_TO_MM3 : void 0,
|
|
1037
1202
|
opacity: gs.strokeAlpha
|
|
1038
1203
|
});
|
|
1039
1204
|
} else {
|
|
1040
1205
|
shapes.push({
|
|
1041
1206
|
kind: "path",
|
|
1042
|
-
x: minX *
|
|
1043
|
-
y: minY *
|
|
1207
|
+
x: minX * PT_TO_MM3,
|
|
1208
|
+
y: minY * PT_TO_MM3,
|
|
1044
1209
|
width: wLocal,
|
|
1045
1210
|
height: hLocal,
|
|
1046
1211
|
stroke: isStroke ? gs.stroke : void 0,
|
|
1047
|
-
strokeWidth: isStroke ? gs.lineWidth *
|
|
1212
|
+
strokeWidth: isStroke ? gs.lineWidth * PT_TO_MM3 : void 0,
|
|
1048
1213
|
opacity: gs.strokeAlpha,
|
|
1049
1214
|
path: `M ${x1Local.toFixed(2)} ${y1Local.toFixed(2)} L ${x2Local.toFixed(2)} ${y2Local.toFixed(2)}`
|
|
1050
1215
|
});
|
|
@@ -1077,20 +1242,20 @@ async function walkOps(page, OPS, viewport) {
|
|
|
1077
1242
|
if (seg.type === "Z") return "Z";
|
|
1078
1243
|
const p = [];
|
|
1079
1244
|
for (let i = 0; i < seg.pts.length; i += 2) {
|
|
1080
|
-
p.push(((seg.pts[i] - minX) *
|
|
1081
|
-
p.push(((seg.pts[i + 1] - minY) *
|
|
1245
|
+
p.push(((seg.pts[i] - minX) * PT_TO_MM3).toFixed(2));
|
|
1246
|
+
p.push(((seg.pts[i + 1] - minY) * PT_TO_MM3).toFixed(2));
|
|
1082
1247
|
}
|
|
1083
1248
|
return `${seg.type} ${p.join(" ")}`;
|
|
1084
1249
|
}).join(" ");
|
|
1085
1250
|
shapes.push({
|
|
1086
1251
|
kind: "path",
|
|
1087
|
-
x: minX *
|
|
1088
|
-
y: minY *
|
|
1089
|
-
width: bw *
|
|
1090
|
-
height: bh *
|
|
1252
|
+
x: minX * PT_TO_MM3,
|
|
1253
|
+
y: minY * PT_TO_MM3,
|
|
1254
|
+
width: bw * PT_TO_MM3,
|
|
1255
|
+
height: bh * PT_TO_MM3,
|
|
1091
1256
|
fill: isFill ? gs.fill : void 0,
|
|
1092
1257
|
stroke: isStroke ? gs.stroke : void 0,
|
|
1093
|
-
strokeWidth: isStroke ? gs.lineWidth *
|
|
1258
|
+
strokeWidth: isStroke ? gs.lineWidth * PT_TO_MM3 : void 0,
|
|
1094
1259
|
opacity: isFill ? gs.fillAlpha : gs.strokeAlpha,
|
|
1095
1260
|
path: d
|
|
1096
1261
|
});
|
|
@@ -1431,10 +1596,10 @@ async function extractLinks(doc, page, viewport) {
|
|
|
1431
1596
|
const xMax = Math.max(c1.x, c2.x);
|
|
1432
1597
|
const yMax = Math.max(c1.y, c2.y);
|
|
1433
1598
|
const rectMm = {
|
|
1434
|
-
x: xMin *
|
|
1435
|
-
y: yMin *
|
|
1436
|
-
w: (xMax - xMin) *
|
|
1437
|
-
h: (yMax - yMin) *
|
|
1599
|
+
x: xMin * PT_TO_MM3,
|
|
1600
|
+
y: yMin * PT_TO_MM3,
|
|
1601
|
+
w: (xMax - xMin) * PT_TO_MM3,
|
|
1602
|
+
h: (yMax - yMin) * PT_TO_MM3
|
|
1438
1603
|
};
|
|
1439
1604
|
const url = a.url || a.unsafeUrl;
|
|
1440
1605
|
const destPage = url ? void 0 : await resolveDestPage(doc, a.dest);
|
|
@@ -1472,10 +1637,10 @@ async function extractFormWidgets(page, viewport) {
|
|
|
1472
1637
|
})).filter((o) => o.value !== "") : [];
|
|
1473
1638
|
out.push({
|
|
1474
1639
|
rectMm: {
|
|
1475
|
-
x: xMin *
|
|
1476
|
-
y: yMin *
|
|
1477
|
-
w: (xMax - xMin) *
|
|
1478
|
-
h: (yMax - yMin) *
|
|
1640
|
+
x: xMin * PT_TO_MM3,
|
|
1641
|
+
y: yMin * PT_TO_MM3,
|
|
1642
|
+
w: (xMax - xMin) * PT_TO_MM3,
|
|
1643
|
+
h: (yMax - yMin) * PT_TO_MM3
|
|
1479
1644
|
},
|
|
1480
1645
|
fieldType: a.fieldType || "",
|
|
1481
1646
|
fieldName: a.fieldName || `field-${out.length + 1}`,
|
|
@@ -1764,12 +1929,12 @@ async function importPdfToJdf(source, title, runtime, options = {}) {
|
|
|
1764
1929
|
const w = safeNum(it.width, 0);
|
|
1765
1930
|
runs.push({
|
|
1766
1931
|
text: it.str,
|
|
1767
|
-
x: safeNum(vx *
|
|
1768
|
-
y: safeNum(yTop *
|
|
1932
|
+
x: safeNum(vx * PT_TO_MM3, 0),
|
|
1933
|
+
y: safeNum(yTop * PT_TO_MM3, 0),
|
|
1769
1934
|
fontSize: safeNum(fontSize, 10),
|
|
1770
1935
|
fontName: it.fontName,
|
|
1771
|
-
width: safeNum(w *
|
|
1772
|
-
height: safeNum((it.height || fontSize) *
|
|
1936
|
+
width: safeNum(w * PT_TO_MM3, 0),
|
|
1937
|
+
height: safeNum((it.height || fontSize) * PT_TO_MM3, fontSize * PT_TO_MM3),
|
|
1773
1938
|
color: op?.fill || "#000000",
|
|
1774
1939
|
opacity: invisible ? 0 : safeNum(op?.alpha, 1)
|
|
1775
1940
|
});
|
|
@@ -1792,10 +1957,10 @@ async function importPdfToJdf(source, title, runtime, options = {}) {
|
|
|
1792
1957
|
}
|
|
1793
1958
|
const sameLine = Math.abs(last.y - r.y) <= Y_TOL;
|
|
1794
1959
|
const sameStyle = Math.abs(last.fontSize - r.fontSize) < 0.4 && (last.fontName === r.fontName || fontKey(last.fontName) === fontKey(r.fontName)) && last.color === r.color && Math.abs(last.opacity - r.opacity) < 0.05;
|
|
1795
|
-
const emMm = r.fontSize *
|
|
1960
|
+
const emMm = r.fontSize * PT_TO_MM3;
|
|
1796
1961
|
const extent = (t) => {
|
|
1797
1962
|
if (!/\s$/.test(t.text)) return t.width;
|
|
1798
|
-
const em = t.fontSize *
|
|
1963
|
+
const em = t.fontSize * PT_TO_MM3;
|
|
1799
1964
|
const est = Math.max(1, t.text.trim().length) * em * kGlyph + em * 0.25;
|
|
1800
1965
|
if (stretchedSpaces) return Math.min(t.width, est);
|
|
1801
1966
|
return t.width > est * 1.4 ? est : t.width;
|
|
@@ -1814,11 +1979,19 @@ async function importPdfToJdf(source, title, runtime, options = {}) {
|
|
|
1814
1979
|
}
|
|
1815
1980
|
}
|
|
1816
1981
|
const elements = [];
|
|
1982
|
+
const lineMeta = /* @__PURE__ */ new WeakMap();
|
|
1817
1983
|
const tRuns = lines.map((l) => {
|
|
1818
1984
|
const cls = fontMap.get(l.fontName) || classifyFont(l.fontName || "");
|
|
1819
1985
|
return { text: l.text, x: l.x, y: l.y, width: l.width, height: l.height, fontSize: l.fontSize, fontName: l.fontName, color: l.color, bold: cls.weight === "bold" };
|
|
1820
1986
|
});
|
|
1821
|
-
const
|
|
1987
|
+
const sizeChars = /* @__PURE__ */ new Map();
|
|
1988
|
+
for (const l of lines) {
|
|
1989
|
+
const k = Math.round(l.fontSize * 2) / 2;
|
|
1990
|
+
sizeChars.set(k, (sizeChars.get(k) ?? 0) + l.text.length);
|
|
1991
|
+
}
|
|
1992
|
+
const bodyFontSize = [...sizeChars.entries()].sort((a, b) => b[1] - a[1])[0]?.[0] ?? 0;
|
|
1993
|
+
const gutters = options.readingOrder === false ? [] : detectGutters(lines.map((l) => ({ text: l.text, x: l.x, y: l.y, width: l.width, fontSize: l.fontSize })), bodyFontSize, pageW * PT_TO_MM3);
|
|
1994
|
+
const detected = options.detectTables === false ? [] : detectTables(tRuns, ops.shapes, pageW * PT_TO_MM3, gutters);
|
|
1822
1995
|
const consumedLines = /* @__PURE__ */ new Set();
|
|
1823
1996
|
const consumedShapes = /* @__PURE__ */ new Set();
|
|
1824
1997
|
const tableAtLine = /* @__PURE__ */ new Map();
|
|
@@ -1827,7 +2000,7 @@ async function importPdfToJdf(source, title, runtime, options = {}) {
|
|
|
1827
2000
|
for (const k of t.shapeIdx) consumedShapes.add(k);
|
|
1828
2001
|
tableAtLine.set(Math.min(...t.lineIdx), t.element);
|
|
1829
2002
|
}
|
|
1830
|
-
const pageWmm = pageW *
|
|
2003
|
+
const pageWmm = pageW * PT_TO_MM3, pageHmm = pageH * PT_TO_MM3;
|
|
1831
2004
|
ops.shapes.forEach((sh, shapeIdx) => {
|
|
1832
2005
|
if (consumedShapes.has(shapeIdx)) return;
|
|
1833
2006
|
if (sh.width < 0.3 && sh.height < 0.3) return;
|
|
@@ -1871,12 +2044,6 @@ async function importPdfToJdf(source, title, runtime, options = {}) {
|
|
|
1871
2044
|
fit: "fill"
|
|
1872
2045
|
});
|
|
1873
2046
|
}
|
|
1874
|
-
const sizeChars = /* @__PURE__ */ new Map();
|
|
1875
|
-
for (const l of lines) {
|
|
1876
|
-
const k = Math.round(l.fontSize * 2) / 2;
|
|
1877
|
-
sizeChars.set(k, (sizeChars.get(k) ?? 0) + l.text.length);
|
|
1878
|
-
}
|
|
1879
|
-
const bodyFontSize = [...sizeChars.entries()].sort((a, b) => b[1] - a[1])[0]?.[0] ?? 0;
|
|
1880
2047
|
const rowOf = /* @__PURE__ */ new Map();
|
|
1881
2048
|
const rowStartOf = /* @__PURE__ */ new Map();
|
|
1882
2049
|
const nextOnRow = /* @__PURE__ */ new Map();
|
|
@@ -1884,10 +2051,10 @@ async function importPdfToJdf(source, title, runtime, options = {}) {
|
|
|
1884
2051
|
const order = lines.map((_, i) => i).filter((i) => !consumedLines.has(i));
|
|
1885
2052
|
for (let a = 0; a < order.length; a++) {
|
|
1886
2053
|
const i = order[a], li = lines[i];
|
|
1887
|
-
const tolY = Math.max(0.6, li.fontSize * PT_TO_MM2 * 0.35);
|
|
1888
2054
|
let bestNext = -1, bestX = Infinity;
|
|
1889
2055
|
for (let b = 0; b < order.length; b++) {
|
|
1890
2056
|
const j = order[b], lj = lines[j];
|
|
2057
|
+
const tolY = Math.max(0.6, Math.max(li.fontSize, lj.fontSize) * PT_TO_MM3 * 0.5);
|
|
1891
2058
|
if (j === i || Math.abs(lj.y - li.y) > tolY || lj.x <= li.x) continue;
|
|
1892
2059
|
if (lj.x < bestX) {
|
|
1893
2060
|
bestX = lj.x;
|
|
@@ -1904,7 +2071,7 @@ async function importPdfToJdf(source, title, runtime, options = {}) {
|
|
|
1904
2071
|
let cur = i;
|
|
1905
2072
|
while (nextOnRow.has(cur)) {
|
|
1906
2073
|
const j = nextOnRow.get(cur), lc = lines[cur], lj = lines[j];
|
|
1907
|
-
const em = Math.min(lc.fontSize, lj.fontSize) *
|
|
2074
|
+
const em = Math.min(lc.fontSize, lj.fontSize) * PT_TO_MM3;
|
|
1908
2075
|
const gap = lj.x - (lc.x + lc.width);
|
|
1909
2076
|
if (gap < -em * 0.3 || gap > em * 0.6) break;
|
|
1910
2077
|
row.push(j);
|
|
@@ -1929,9 +2096,9 @@ async function importPdfToJdf(source, title, runtime, options = {}) {
|
|
|
1929
2096
|
const first = lines[row[0]], last = lines[row[row.length - 1]];
|
|
1930
2097
|
const base = runStyle(first);
|
|
1931
2098
|
const rowEnd = last.x + last.width;
|
|
1932
|
-
const measuredW = Math.max((rowEnd - first.x) * 1.2 + first.fontSize *
|
|
2099
|
+
const measuredW = Math.max((rowEnd - first.x) * 1.2 + first.fontSize * PT_TO_MM3 * 0.4, first.fontSize * PT_TO_MM3);
|
|
1933
2100
|
const nextIdx2 = nextOnRow.get(row[row.length - 1]);
|
|
1934
|
-
const cap2 = nextIdx2 != null ? lines[nextIdx2].x - first.x - first.fontSize *
|
|
2101
|
+
const cap2 = nextIdx2 != null ? lines[nextIdx2].x - first.x - first.fontSize * PT_TO_MM3 * 0.3 : pageWmm - first.x;
|
|
1935
2102
|
const runs2 = [];
|
|
1936
2103
|
row.forEach((idx, k) => {
|
|
1937
2104
|
const r = lines[idx];
|
|
@@ -1940,7 +2107,7 @@ async function importPdfToJdf(source, title, runtime, options = {}) {
|
|
|
1940
2107
|
if (k > 0) {
|
|
1941
2108
|
const prev2 = lines[row[k - 1]];
|
|
1942
2109
|
const gap = r.x - (prev2.x + prev2.width);
|
|
1943
|
-
if (gap > r.fontSize *
|
|
2110
|
+
if (gap > r.fontSize * PT_TO_MM3 * 0.08 && !/\s$/.test(prev2.text) && !/^\s/.test(text2)) text2 = " " + text2;
|
|
1944
2111
|
}
|
|
1945
2112
|
const run = { text: text2 };
|
|
1946
2113
|
if (st.bold) run.bold = true;
|
|
@@ -1954,13 +2121,16 @@ async function importPdfToJdf(source, title, runtime, options = {}) {
|
|
|
1954
2121
|
});
|
|
1955
2122
|
const style2 = { fontSize: Math.round(first.fontSize * 10) / 10, fontFamily: base.cls.family };
|
|
1956
2123
|
if (first.opacity < 0.999) style2.opacity = Math.round(first.opacity * 100) / 100;
|
|
1957
|
-
|
|
2124
|
+
const rt = {
|
|
1958
2125
|
type: "richtext",
|
|
1959
2126
|
runs: runs2,
|
|
1960
2127
|
position: { x: Math.max(0, Math.round(first.x * 100) / 100), y: Math.max(0, Math.round(Math.min(...row.map((i) => lines[i].y)) * 100) / 100) },
|
|
1961
|
-
width: Math.max(2, Math.round(Math.max(first.fontSize *
|
|
2128
|
+
width: Math.max(2, Math.round(Math.max(first.fontSize * PT_TO_MM3, Math.min(measuredW, cap2)) * 100) / 100),
|
|
1962
2129
|
style: style2
|
|
1963
|
-
}
|
|
2130
|
+
};
|
|
2131
|
+
const dominant = row.map((i) => lines[i]).sort((a, b) => b.text.trim().length - a.text.trim().length)[0];
|
|
2132
|
+
lineMeta.set(rt, { w: Math.max(first.fontSize * PT_TO_MM3, rowEnd - first.x), size: dominant.fontSize, face: fontKey(dominant.fontName) });
|
|
2133
|
+
elements.push(rt);
|
|
1964
2134
|
return;
|
|
1965
2135
|
}
|
|
1966
2136
|
const cls = fontMap.get(l.fontName) || classifyFont(l.fontName || "");
|
|
@@ -1973,10 +2143,10 @@ async function importPdfToJdf(source, title, runtime, options = {}) {
|
|
|
1973
2143
|
if (l.color !== "#000000") style.color = l.color;
|
|
1974
2144
|
if (l.opacity < 0.999) style.opacity = Math.round(l.opacity * 100) / 100;
|
|
1975
2145
|
const link = findLinkForRun2(l);
|
|
1976
|
-
const measured = Math.max(l.width * 1.2 + l.fontSize *
|
|
2146
|
+
const measured = Math.max(l.width * 1.2 + l.fontSize * PT_TO_MM3 * 0.4, l.fontSize * PT_TO_MM3);
|
|
1977
2147
|
const remaining = Math.max(measured, pageWmm - l.x);
|
|
1978
2148
|
const nextIdx = nextOnRow.get(lineIdx);
|
|
1979
|
-
const cap = nextIdx != null ? Math.max(l.fontSize *
|
|
2149
|
+
const cap = nextIdx != null ? Math.max(l.fontSize * PT_TO_MM3, lines[nextIdx].x - l.x - l.fontSize * PT_TO_MM3 * 0.3) : Infinity;
|
|
1980
2150
|
const elWidth = Math.min(measured, remaining, cap);
|
|
1981
2151
|
const text = {
|
|
1982
2152
|
type: "text",
|
|
@@ -1997,14 +2167,26 @@ async function importPdfToJdf(source, title, runtime, options = {}) {
|
|
|
1997
2167
|
else if (link.destPage != null) text.link = { type: "internal", target: `#page-${link.destPage + 1}` };
|
|
1998
2168
|
}
|
|
1999
2169
|
const prev = elements[elements.length - 1];
|
|
2000
|
-
if (text.heading && prev && prev.type === "text" && prev.heading === text.heading && !link && !prev.link && Math.abs(prev.style?.fontSize - style.fontSize) < 0.5 && Math.abs(prev.position.x - text.position.x) < 1 && text.position.y - prev.position.y < l.fontSize *
|
|
2170
|
+
if (text.heading && prev && prev.type === "text" && prev.heading === text.heading && !link && !prev.link && Math.abs(prev.style?.fontSize - style.fontSize) < 0.5 && Math.abs(prev.position.x - text.position.x) < 1 && text.position.y - prev.position.y < l.fontSize * PT_TO_MM3 * 2.2 && text.position.y > prev.position.y) {
|
|
2001
2171
|
prev.content = `${prev.content} ${text.content}`.replace(/\s+/g, " ");
|
|
2002
2172
|
prev.tocEntry = prev.content;
|
|
2003
2173
|
prev.width = Math.max(prev.width ?? 0, text.width ?? 0);
|
|
2004
2174
|
return;
|
|
2005
2175
|
}
|
|
2176
|
+
lineMeta.set(text, { w: Math.max(l.fontSize * PT_TO_MM3, l.width), size: l.fontSize, face: fontKey(l.fontName) });
|
|
2006
2177
|
elements.push(text);
|
|
2007
2178
|
});
|
|
2179
|
+
if (options.readingOrder !== false) {
|
|
2180
|
+
if (gutters.length) {
|
|
2181
|
+
const isFlow = (e) => e.type === "text" || e.type === "richtext" || e.type === "table";
|
|
2182
|
+
const flow = elements.filter(isFlow), rest = elements.filter((e) => !isFlow(e));
|
|
2183
|
+
elements.splice(0, elements.length, ...rest, ...orderByColumns(flow, gutters, pageWmm));
|
|
2184
|
+
}
|
|
2185
|
+
}
|
|
2186
|
+
if (options.foldParagraphs !== false) {
|
|
2187
|
+
const folded = foldParagraphs(elements, lineMeta, pageWmm);
|
|
2188
|
+
elements.splice(0, elements.length, ...folded);
|
|
2189
|
+
}
|
|
2008
2190
|
for (const w of formWidgets) {
|
|
2009
2191
|
if (w.pushButton) continue;
|
|
2010
2192
|
const baseEl = {
|
|
@@ -2039,7 +2221,7 @@ async function importPdfToJdf(source, title, runtime, options = {}) {
|
|
|
2039
2221
|
}
|
|
2040
2222
|
pages.push({
|
|
2041
2223
|
id: `page-${pi}`,
|
|
2042
|
-
pageSize: { width: Math.round(pageW *
|
|
2224
|
+
pageSize: { width: Math.round(pageW * PT_TO_MM3 * 100) / 100, height: Math.round(pageH * PT_TO_MM3 * 100) / 100 },
|
|
2043
2225
|
margins: { top: 0, right: 0, bottom: 0, left: 0 },
|
|
2044
2226
|
elements
|
|
2045
2227
|
});
|
package/dist/jdf-schema.json
CHANGED
|
@@ -103,6 +103,7 @@
|
|
|
103
103
|
"backgroundColor": { "type": "string" },
|
|
104
104
|
"textAlign": { "type": "string", "enum": ["left","center","right","justify"] },
|
|
105
105
|
"lineHeight": { "type": "number" },
|
|
106
|
+
"textIndent": { "type": "number", "description": "First-line indent in the document unit." },
|
|
106
107
|
"letterSpacing": { "oneOf": [{ "type": "number" }, { "type": "string" }] },
|
|
107
108
|
"padding": { "oneOf": [{ "type": "number" }, { "type": "string" }, { "$ref": "#/definitions/Margins" }] },
|
|
108
109
|
"margin": { "oneOf": [{ "type": "number" }, { "type": "string" }, { "$ref": "#/definitions/Margins" }] },
|