@uurtech/jdf-cli 0.2.0 → 0.2.1

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.
Files changed (2) hide show
  1. package/dist/index.js +95 -4
  2. package/package.json +1 -1
package/dist/index.js CHANGED
@@ -1699,6 +1699,10 @@ async function importPdfToJdf(source, title, runtime, options = {}) {
1699
1699
  if (arr) arr.push(op);
1700
1700
  else opBins.set(key, [op]);
1701
1701
  }
1702
+ const sizePenalty = (op, fontSize) => {
1703
+ if (!op.fontSize || !fontSize) return 0;
1704
+ return Math.abs(Math.log(op.fontSize / fontSize)) * 6;
1705
+ };
1702
1706
  const findOp = (x, y, fontSize) => {
1703
1707
  let best = null;
1704
1708
  let bestD = Infinity;
@@ -1708,7 +1712,7 @@ async function importPdfToJdf(source, title, runtime, options = {}) {
1708
1712
  const arr = opBins.get(`${bx + dx},${by + dy}`);
1709
1713
  if (!arr) continue;
1710
1714
  for (const op of arr) {
1711
- const d = Math.hypot(op.x - x, op.y - y);
1715
+ const d = Math.hypot(op.x - x, op.y - y) + sizePenalty(op, fontSize);
1712
1716
  if (d < bestD) {
1713
1717
  bestD = d;
1714
1718
  best = op;
@@ -1718,8 +1722,9 @@ async function importPdfToJdf(source, title, runtime, options = {}) {
1718
1722
  }
1719
1723
  if (best) return best;
1720
1724
  const tol = Math.max(2, fontSize * 0.6);
1725
+ const sizeOk = (op) => !op.fontSize || !fontSize || op.fontSize / fontSize > 0.6 && op.fontSize / fontSize < 1.7;
1721
1726
  for (const op of ops.textOps) {
1722
- if (Math.abs(op.y - y) > tol) continue;
1727
+ if (!sizeOk(op) || Math.abs(op.y - y) > tol) continue;
1723
1728
  const d = Math.abs(op.x - x) + Math.abs(op.y - y) * 4;
1724
1729
  if (d < bestD) {
1725
1730
  bestD = d;
@@ -1728,6 +1733,7 @@ async function importPdfToJdf(source, title, runtime, options = {}) {
1728
1733
  }
1729
1734
  if (best) return best;
1730
1735
  for (const op of ops.textOps) {
1736
+ if (!sizeOk(op)) continue;
1731
1737
  const d = Math.hypot(op.x - x, op.y - y);
1732
1738
  if (d < bestD) {
1733
1739
  bestD = d;
@@ -1746,6 +1752,7 @@ async function importPdfToJdf(source, title, runtime, options = {}) {
1746
1752
  const conv = viewport.convertToViewportPoint(baseX, baseY);
1747
1753
  const vx = safeNum(conv?.[0], 0);
1748
1754
  const vy = safeNum(conv?.[1], 0);
1755
+ if (fontSize < 1.5) return;
1749
1756
  const op = findOp(vx, vy, fontSize);
1750
1757
  const mode = op?.mode ?? 0;
1751
1758
  if (mode === 7) return;
@@ -1869,10 +1876,92 @@ async function importPdfToJdf(source, title, runtime, options = {}) {
1869
1876
  sizeChars.set(k, (sizeChars.get(k) ?? 0) + l.text.length);
1870
1877
  }
1871
1878
  const bodyFontSize = [...sizeChars.entries()].sort((a, b) => b[1] - a[1])[0]?.[0] ?? 0;
1879
+ const rowOf = /* @__PURE__ */ new Map();
1880
+ const rowStartOf = /* @__PURE__ */ new Map();
1881
+ const nextOnRow = /* @__PURE__ */ new Map();
1882
+ {
1883
+ const order = lines.map((_, i) => i).filter((i) => !consumedLines.has(i));
1884
+ for (let a = 0; a < order.length; a++) {
1885
+ const i = order[a], li = lines[i];
1886
+ const tolY = Math.max(0.6, li.fontSize * PT_TO_MM2 * 0.35);
1887
+ let bestNext = -1, bestX = Infinity;
1888
+ for (let b = 0; b < order.length; b++) {
1889
+ const j = order[b], lj = lines[j];
1890
+ if (j === i || Math.abs(lj.y - li.y) > tolY || lj.x <= li.x) continue;
1891
+ if (lj.x < bestX) {
1892
+ bestX = lj.x;
1893
+ bestNext = j;
1894
+ }
1895
+ }
1896
+ if (bestNext >= 0) nextOnRow.set(i, bestNext);
1897
+ }
1898
+ const seen = /* @__PURE__ */ new Set();
1899
+ for (const i of order) {
1900
+ if (seen.has(i)) continue;
1901
+ const row = [i];
1902
+ seen.add(i);
1903
+ let cur = i;
1904
+ while (nextOnRow.has(cur)) {
1905
+ const j = nextOnRow.get(cur), lc = lines[cur], lj = lines[j];
1906
+ const em = Math.min(lc.fontSize, lj.fontSize) * PT_TO_MM2;
1907
+ const gap = lj.x - (lc.x + lc.width);
1908
+ if (gap < -em * 0.3 || gap > em * 0.6) break;
1909
+ row.push(j);
1910
+ seen.add(j);
1911
+ cur = j;
1912
+ }
1913
+ rowOf.set(i, row);
1914
+ for (const j of row) rowStartOf.set(j, i);
1915
+ }
1916
+ }
1917
+ const runStyle = (l) => {
1918
+ const cls = fontMap.get(l.fontName) || classifyFont(l.fontName || "");
1919
+ return { cls, bold: cls.weight === "bold", italic: cls.style === "italic" };
1920
+ };
1872
1921
  lines.forEach((l, lineIdx) => {
1873
1922
  const tableEl = tableAtLine.get(lineIdx);
1874
1923
  if (tableEl) elements.push(tableEl);
1875
1924
  if (consumedLines.has(lineIdx)) return;
1925
+ const row = rowOf.get(lineIdx);
1926
+ if (!row) return;
1927
+ if (row.length > 1) {
1928
+ const first = lines[row[0]], last = lines[row[row.length - 1]];
1929
+ const base = runStyle(first);
1930
+ const rowEnd = last.x + last.width;
1931
+ const measuredW = Math.max((rowEnd - first.x) * 1.2 + first.fontSize * PT_TO_MM2 * 0.4, first.fontSize * PT_TO_MM2);
1932
+ const nextIdx2 = nextOnRow.get(row[row.length - 1]);
1933
+ const cap2 = nextIdx2 != null ? lines[nextIdx2].x - first.x - first.fontSize * PT_TO_MM2 * 0.3 : pageWmm - first.x;
1934
+ const runs2 = [];
1935
+ row.forEach((idx, k) => {
1936
+ const r = lines[idx];
1937
+ const st = runStyle(r);
1938
+ let text2 = r.text;
1939
+ if (k > 0) {
1940
+ const prev2 = lines[row[k - 1]];
1941
+ const gap = r.x - (prev2.x + prev2.width);
1942
+ if (gap > r.fontSize * PT_TO_MM2 * 0.08 && !/\s$/.test(prev2.text) && !/^\s/.test(text2)) text2 = " " + text2;
1943
+ }
1944
+ const run = { text: text2 };
1945
+ if (st.bold) run.bold = true;
1946
+ if (st.italic) run.italic = true;
1947
+ if (r.color !== "#000000") run.color = r.color;
1948
+ if (Math.abs(r.fontSize - first.fontSize) >= 0.5) run.fontSize = Math.round(r.fontSize * 10) / 10;
1949
+ if (st.cls.family !== base.cls.family) run.fontFamily = st.cls.family;
1950
+ const lk = findLinkForRun2(r);
1951
+ if (lk) run.link = lk.url ? lk.url : lk.destPage != null ? { type: "internal", target: `#page-${lk.destPage + 1}` } : void 0;
1952
+ runs2.push(run);
1953
+ });
1954
+ const style2 = { fontSize: Math.round(first.fontSize * 10) / 10, fontFamily: base.cls.family };
1955
+ if (first.opacity < 0.999) style2.opacity = Math.round(first.opacity * 100) / 100;
1956
+ elements.push({
1957
+ type: "richtext",
1958
+ runs: runs2,
1959
+ 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) },
1960
+ width: Math.max(2, Math.round(Math.max(first.fontSize * PT_TO_MM2, Math.min(measuredW, cap2)) * 100) / 100),
1961
+ style: style2
1962
+ });
1963
+ return;
1964
+ }
1876
1965
  const cls = fontMap.get(l.fontName) || classifyFont(l.fontName || "");
1877
1966
  const style = {
1878
1967
  fontSize: Math.round(l.fontSize * 10) / 10,
@@ -1883,9 +1972,11 @@ async function importPdfToJdf(source, title, runtime, options = {}) {
1883
1972
  if (l.color !== "#000000") style.color = l.color;
1884
1973
  if (l.opacity < 0.999) style.opacity = Math.round(l.opacity * 100) / 100;
1885
1974
  const link = findLinkForRun2(l);
1886
- const measured = Math.max(l.width + l.fontSize * PT_TO_MM2 * 0.4, l.fontSize * PT_TO_MM2);
1975
+ const measured = Math.max(l.width * 1.2 + l.fontSize * PT_TO_MM2 * 0.4, l.fontSize * PT_TO_MM2);
1887
1976
  const remaining = Math.max(measured, pageWmm - l.x);
1888
- const elWidth = Math.min(measured, remaining);
1977
+ const nextIdx = nextOnRow.get(lineIdx);
1978
+ const cap = nextIdx != null ? Math.max(l.fontSize * PT_TO_MM2, lines[nextIdx].x - l.x - l.fontSize * PT_TO_MM2 * 0.3) : Infinity;
1979
+ const elWidth = Math.min(measured, remaining, cap);
1889
1980
  const text = {
1890
1981
  type: "text",
1891
1982
  content: l.text,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@uurtech/jdf-cli",
3
- "version": "0.2.0",
3
+ "version": "0.2.1",
4
4
  "description": "Command-line tool for the JDF (JSON Document Format) — validate and convert documents.",
5
5
  "license": "MIT",
6
6
  "author": "Ugur Kazdal",