@uurtech/jdf-cli 0.1.10 → 0.1.22
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 +206 -54
- package/dist/jdf-schema.json +3 -2
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -240,6 +240,58 @@ function shouldUseJdfx(doc) {
|
|
|
240
240
|
}
|
|
241
241
|
|
|
242
242
|
// src/commands/import-md.ts
|
|
243
|
+
function parseInline(text) {
|
|
244
|
+
const runs = [];
|
|
245
|
+
let i = 0;
|
|
246
|
+
let buf = "";
|
|
247
|
+
const flush = (extra) => {
|
|
248
|
+
if (buf) {
|
|
249
|
+
runs.push({ text: buf, ...extra });
|
|
250
|
+
buf = "";
|
|
251
|
+
}
|
|
252
|
+
};
|
|
253
|
+
while (i < text.length) {
|
|
254
|
+
const rest = text.slice(i);
|
|
255
|
+
const link = rest.match(/^\[([^\]]+)\]\(([^)\s]+)[^)]*\)/);
|
|
256
|
+
if (link) {
|
|
257
|
+
flush();
|
|
258
|
+
runs.push({ text: link[1], link: link[2] });
|
|
259
|
+
i += link[0].length;
|
|
260
|
+
continue;
|
|
261
|
+
}
|
|
262
|
+
const code = rest.match(/^`([^`]+)`/);
|
|
263
|
+
if (code) {
|
|
264
|
+
flush();
|
|
265
|
+
runs.push({ text: code[1], fontFamily: "JetBrains Mono", color: "#be185d" });
|
|
266
|
+
i += code[0].length;
|
|
267
|
+
continue;
|
|
268
|
+
}
|
|
269
|
+
const bold = rest.match(/^(\*\*|__)([^]+?)\1/);
|
|
270
|
+
if (bold) {
|
|
271
|
+
flush();
|
|
272
|
+
runs.push(...parseInline(bold[2]).map((r) => ({ ...r, bold: true })));
|
|
273
|
+
i += bold[0].length;
|
|
274
|
+
continue;
|
|
275
|
+
}
|
|
276
|
+
const ital = rest.match(/^(\*|_)([^]+?)\1/);
|
|
277
|
+
if (ital) {
|
|
278
|
+
flush();
|
|
279
|
+
runs.push(...parseInline(ital[2]).map((r) => ({ ...r, italic: true })));
|
|
280
|
+
i += ital[0].length;
|
|
281
|
+
continue;
|
|
282
|
+
}
|
|
283
|
+
buf += text[i];
|
|
284
|
+
i++;
|
|
285
|
+
}
|
|
286
|
+
flush();
|
|
287
|
+
return runs.length ? runs : [{ text }];
|
|
288
|
+
}
|
|
289
|
+
function hasFormatting(runs) {
|
|
290
|
+
return runs.some((r) => r.bold || r.italic || r.link || r.fontFamily || r.color);
|
|
291
|
+
}
|
|
292
|
+
function stripInline(text) {
|
|
293
|
+
return parseInline(text).map((r) => r.text).join("");
|
|
294
|
+
}
|
|
243
295
|
async function importMarkdown(inputPath, outputPath) {
|
|
244
296
|
const input = path2.resolve(inputPath);
|
|
245
297
|
console.log(`Importing: ${input}`);
|
|
@@ -330,11 +382,28 @@ function convertMarkdownToJdf(md, title, baseDir = process.cwd()) {
|
|
|
330
382
|
i++;
|
|
331
383
|
continue;
|
|
332
384
|
}
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
385
|
+
if (line.match(/^\s*([-*_])(\s*\1){2,}\s*$/)) {
|
|
386
|
+
if (y + 6 > maxY) {
|
|
387
|
+
pages.push({ id: `page-${pageNum}`, elements });
|
|
388
|
+
elements = [];
|
|
389
|
+
y = 5;
|
|
390
|
+
pageNum++;
|
|
391
|
+
}
|
|
392
|
+
elements.push({ type: "shape", shape: "rect", position: { x: 0, y: y + 2 }, width: contentWidth, height: 0.3, fill: "#cbd5e1" });
|
|
393
|
+
y += 6;
|
|
394
|
+
i++;
|
|
395
|
+
continue;
|
|
396
|
+
}
|
|
397
|
+
if (line.includes("|") && i + 1 < lines.length && lines[i + 1].match(/^\s*\|?[\s:|-]+\|?\s*$/) && lines[i + 1].includes("-")) {
|
|
398
|
+
const splitRow = (r) => r.replace(/^\s*\|/, "").replace(/\|\s*$/, "").split("|").map((c) => stripInline(c.trim()));
|
|
399
|
+
const headers = splitRow(line);
|
|
400
|
+
i += 2;
|
|
401
|
+
const rows = [];
|
|
402
|
+
while (i < lines.length && lines[i].includes("|") && lines[i].trim() !== "") {
|
|
403
|
+
rows.push(splitRow(lines[i]));
|
|
404
|
+
i++;
|
|
405
|
+
}
|
|
406
|
+
const height2 = (rows.length + 1) * 7 + 3;
|
|
338
407
|
if (y + height2 > maxY) {
|
|
339
408
|
pages.push({ id: `page-${pageNum}`, elements });
|
|
340
409
|
elements = [];
|
|
@@ -342,25 +411,27 @@ function convertMarkdownToJdf(md, title, baseDir = process.cwd()) {
|
|
|
342
411
|
pageNum++;
|
|
343
412
|
}
|
|
344
413
|
elements.push({
|
|
345
|
-
type: "
|
|
346
|
-
|
|
347
|
-
|
|
414
|
+
type: "table",
|
|
415
|
+
headers,
|
|
416
|
+
rows,
|
|
348
417
|
position: { x: 0, y },
|
|
349
418
|
width: contentWidth,
|
|
350
|
-
|
|
351
|
-
|
|
419
|
+
borders: true,
|
|
420
|
+
headerStyle: { backgroundColor: "#f1f5f9", fontWeight: "bold", fontSize: 10, color: "#0f172a" },
|
|
421
|
+
rowStyle: { color: "#334155", fontSize: 10 },
|
|
422
|
+
style: { fontFamily: "Inter", fontSize: 10 }
|
|
352
423
|
});
|
|
353
|
-
y += height2
|
|
354
|
-
i++;
|
|
424
|
+
y += height2;
|
|
355
425
|
continue;
|
|
356
426
|
}
|
|
357
|
-
if (line.match(/^\s
|
|
358
|
-
const
|
|
359
|
-
while (i < lines.length && lines[i].match(/^\s
|
|
360
|
-
|
|
427
|
+
if (line.match(/^\s*>\s?/)) {
|
|
428
|
+
const quoteLines = [];
|
|
429
|
+
while (i < lines.length && lines[i].match(/^\s*>\s?/)) {
|
|
430
|
+
quoteLines.push(lines[i].replace(/^\s*>\s?/, ""));
|
|
361
431
|
i++;
|
|
362
432
|
}
|
|
363
|
-
const
|
|
433
|
+
const quote = quoteLines.join(" ").trim();
|
|
434
|
+
const height2 = Math.ceil(quote.length / 70) * 4.5 + 6;
|
|
364
435
|
if (y + height2 > maxY) {
|
|
365
436
|
pages.push({ id: `page-${pageNum}`, elements });
|
|
366
437
|
elements = [];
|
|
@@ -368,23 +439,66 @@ function convertMarkdownToJdf(md, title, baseDir = process.cwd()) {
|
|
|
368
439
|
pageNum++;
|
|
369
440
|
}
|
|
370
441
|
elements.push({
|
|
371
|
-
type: "
|
|
372
|
-
|
|
442
|
+
type: "text",
|
|
443
|
+
content: quote,
|
|
373
444
|
position: { x: 0, y },
|
|
374
445
|
width: contentWidth,
|
|
375
|
-
style: { fontFamily: "Inter", fontSize: 10, lineHeight: 1.6, color: "#
|
|
376
|
-
items
|
|
446
|
+
style: { fontFamily: "Inter", fontSize: 10, lineHeight: 1.6, color: "#475569", fontStyle: "italic", backgroundColor: "#f8fafc", padding: 10, borderRadius: 6, marginTop: 4 }
|
|
377
447
|
});
|
|
378
448
|
y += height2;
|
|
379
449
|
continue;
|
|
380
450
|
}
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
451
|
+
const imageMatch = line.match(/^\s*!\[([^\]]*)\]\(\s*([^)\s]+)[^)]*\)\s*$/);
|
|
452
|
+
if (imageMatch) {
|
|
453
|
+
const alt = imageMatch[1];
|
|
454
|
+
const src = resolveImageSrc(imageMatch[2], baseDir);
|
|
455
|
+
const height2 = 60;
|
|
456
|
+
if (y + height2 > maxY) {
|
|
457
|
+
pages.push({ id: `page-${pageNum}`, elements });
|
|
458
|
+
elements = [];
|
|
459
|
+
y = 5;
|
|
460
|
+
pageNum++;
|
|
461
|
+
}
|
|
462
|
+
elements.push({
|
|
463
|
+
type: "image",
|
|
464
|
+
src,
|
|
465
|
+
alt,
|
|
466
|
+
position: { x: 0, y },
|
|
467
|
+
width: contentWidth,
|
|
468
|
+
height: height2,
|
|
469
|
+
fit: "contain"
|
|
470
|
+
});
|
|
471
|
+
y += height2 + 4;
|
|
472
|
+
i++;
|
|
473
|
+
continue;
|
|
474
|
+
}
|
|
475
|
+
const listMatch = line.match(/^(\s*)([-*+]|\d+\.)\s/);
|
|
476
|
+
if (listMatch) {
|
|
477
|
+
const raw = [];
|
|
478
|
+
while (i < lines.length) {
|
|
479
|
+
const m = lines[i].match(/^(\s*)([-*+]|\d+\.)\s+(.*)$/);
|
|
480
|
+
if (!m) break;
|
|
481
|
+
raw.push({ indent: m[1].length, ordered: /\d/.test(m[2]), content: m[3].trim() });
|
|
385
482
|
i++;
|
|
386
483
|
}
|
|
387
|
-
const
|
|
484
|
+
const rootOrdered = raw[0].ordered;
|
|
485
|
+
const root = [];
|
|
486
|
+
const stack = [{ indent: raw[0].indent, items: root }];
|
|
487
|
+
for (const r of raw) {
|
|
488
|
+
while (stack.length > 1 && r.indent < stack[stack.length - 1].indent) stack.pop();
|
|
489
|
+
if (r.indent > stack[stack.length - 1].indent) {
|
|
490
|
+
const parentItems = stack[stack.length - 1].items;
|
|
491
|
+
const parent = parentItems[parentItems.length - 1];
|
|
492
|
+
if (parent) {
|
|
493
|
+
parent.children = parent.children || [];
|
|
494
|
+
parent.listType = r.ordered ? "ordered" : "unordered";
|
|
495
|
+
stack.push({ indent: r.indent, items: parent.children });
|
|
496
|
+
}
|
|
497
|
+
}
|
|
498
|
+
stack[stack.length - 1].items.push({ content: stripInline(r.content) });
|
|
499
|
+
}
|
|
500
|
+
const count = raw.length;
|
|
501
|
+
const height2 = count * 5 + 3;
|
|
388
502
|
if (y + height2 > maxY) {
|
|
389
503
|
pages.push({ id: `page-${pageNum}`, elements });
|
|
390
504
|
elements = [];
|
|
@@ -393,11 +507,11 @@ function convertMarkdownToJdf(md, title, baseDir = process.cwd()) {
|
|
|
393
507
|
}
|
|
394
508
|
elements.push({
|
|
395
509
|
type: "list",
|
|
396
|
-
listType: "ordered",
|
|
510
|
+
listType: rootOrdered ? "ordered" : "unordered",
|
|
397
511
|
position: { x: 0, y },
|
|
398
512
|
width: contentWidth,
|
|
399
513
|
style: { fontFamily: "Inter", fontSize: 10, lineHeight: 1.6, color: "#334155" },
|
|
400
|
-
items
|
|
514
|
+
items: root
|
|
401
515
|
});
|
|
402
516
|
y += height2;
|
|
403
517
|
continue;
|
|
@@ -442,10 +556,15 @@ function convertMarkdownToJdf(md, title, baseDir = process.cwd()) {
|
|
|
442
556
|
continue;
|
|
443
557
|
}
|
|
444
558
|
let para = "";
|
|
445
|
-
while (i < lines.length && lines[i].trim() !== "" && !lines[i].match(/^#{1,6}\s/) && !lines[i].match(/^\s*[-*+]\s/) && !lines[i].match(/^\s*\
|
|
559
|
+
while (i < lines.length && lines[i].trim() !== "" && !lines[i].match(/^#{1,6}\s/) && !lines[i].match(/^\s*([-*+]|\d+\.)\s/) && !lines[i].startsWith("```") && !lines[i].match(/^\s*>\s?/) && !lines[i].match(/^\s*([-*_])(\s*\1){2,}\s*$/) && !lines[i].includes("|")) {
|
|
446
560
|
para += (para ? " " : "") + lines[i].trim();
|
|
447
561
|
i++;
|
|
448
562
|
}
|
|
563
|
+
if (!para) {
|
|
564
|
+
para = lines[i].trim();
|
|
565
|
+
i++;
|
|
566
|
+
if (!para) continue;
|
|
567
|
+
}
|
|
449
568
|
const paraLines = Math.ceil(para.length / 80);
|
|
450
569
|
const height = paraLines * 4.5 + 3;
|
|
451
570
|
if (y + height > maxY) {
|
|
@@ -454,13 +573,24 @@ function convertMarkdownToJdf(md, title, baseDir = process.cwd()) {
|
|
|
454
573
|
y = 5;
|
|
455
574
|
pageNum++;
|
|
456
575
|
}
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
576
|
+
const runs = parseInline(para);
|
|
577
|
+
if (hasFormatting(runs)) {
|
|
578
|
+
elements.push({
|
|
579
|
+
type: "richtext",
|
|
580
|
+
runs,
|
|
581
|
+
position: { x: 0, y },
|
|
582
|
+
width: contentWidth,
|
|
583
|
+
style: { fontFamily: "Inter", fontSize: 10, lineHeight: 1.6, color: "#334155" }
|
|
584
|
+
});
|
|
585
|
+
} else {
|
|
586
|
+
elements.push({
|
|
587
|
+
type: "text",
|
|
588
|
+
content: para,
|
|
589
|
+
position: { x: 0, y },
|
|
590
|
+
width: contentWidth,
|
|
591
|
+
style: { fontFamily: "Inter", fontSize: 10, lineHeight: 1.6, color: "#334155" }
|
|
592
|
+
});
|
|
593
|
+
}
|
|
464
594
|
y += height;
|
|
465
595
|
}
|
|
466
596
|
if (elements.length > 0) {
|
|
@@ -582,17 +712,35 @@ async function walkOps(page, OPS, viewport) {
|
|
|
582
712
|
const y1Local = (va.y - minY) * PT_TO_MM;
|
|
583
713
|
const x2Local = (vb.x - minX) * PT_TO_MM;
|
|
584
714
|
const y2Local = (vb.y - minY) * PT_TO_MM;
|
|
585
|
-
|
|
586
|
-
|
|
587
|
-
|
|
588
|
-
|
|
589
|
-
|
|
590
|
-
|
|
591
|
-
|
|
592
|
-
|
|
593
|
-
|
|
594
|
-
|
|
595
|
-
|
|
715
|
+
const wLocal = Math.max(0.05, (maxX - minX) * PT_TO_MM);
|
|
716
|
+
const hLocal = Math.max(0.05, (maxY - minY) * PT_TO_MM);
|
|
717
|
+
const dx = Math.abs(va.x - vb.x);
|
|
718
|
+
const dy = Math.abs(va.y - vb.y);
|
|
719
|
+
const axisAligned = dx < 0.5 || dy < 0.5;
|
|
720
|
+
if (axisAligned) {
|
|
721
|
+
shapes.push({
|
|
722
|
+
kind: "line",
|
|
723
|
+
x: minX * PT_TO_MM,
|
|
724
|
+
y: minY * PT_TO_MM,
|
|
725
|
+
width: wLocal,
|
|
726
|
+
height: hLocal,
|
|
727
|
+
stroke: isStroke ? gs.stroke : void 0,
|
|
728
|
+
strokeWidth: isStroke ? gs.lineWidth * PT_TO_MM : void 0,
|
|
729
|
+
opacity: gs.strokeAlpha
|
|
730
|
+
});
|
|
731
|
+
} else {
|
|
732
|
+
shapes.push({
|
|
733
|
+
kind: "path",
|
|
734
|
+
x: minX * PT_TO_MM,
|
|
735
|
+
y: minY * PT_TO_MM,
|
|
736
|
+
width: wLocal,
|
|
737
|
+
height: hLocal,
|
|
738
|
+
stroke: isStroke ? gs.stroke : void 0,
|
|
739
|
+
strokeWidth: isStroke ? gs.lineWidth * PT_TO_MM : void 0,
|
|
740
|
+
opacity: gs.strokeAlpha,
|
|
741
|
+
path: `M ${x1Local.toFixed(2)} ${y1Local.toFixed(2)} L ${x2Local.toFixed(2)} ${y2Local.toFixed(2)}`
|
|
742
|
+
});
|
|
743
|
+
}
|
|
596
744
|
} else if (pathSegments.length > 0) {
|
|
597
745
|
const vpSegments = pathSegments.map((seg) => {
|
|
598
746
|
if (seg.type === "Z") return seg;
|
|
@@ -1455,12 +1603,13 @@ The CLI exists for two workflows:
|
|
|
1455
1603
|
|
|
1456
1604
|
Usage:
|
|
1457
1605
|
jdf validate <file.jdf>
|
|
1458
|
-
jdf
|
|
1606
|
+
jdf convert <file.{pdf,json,md}> [-o output.{jdf,jdfx}] [--json]
|
|
1459
1607
|
jdf --help
|
|
1460
1608
|
|
|
1461
1609
|
Commands:
|
|
1462
1610
|
validate Validate a .jdf / .jdfx file against the JDF schema
|
|
1463
|
-
|
|
1611
|
+
convert Convert a PDF, JSON, or Markdown file into JDF
|
|
1612
|
+
(alias: import)
|
|
1464
1613
|
|
|
1465
1614
|
Flags:
|
|
1466
1615
|
-o, --output <path> Explicit output path (extension picks .jdf vs .jdfx)
|
|
@@ -1471,10 +1620,10 @@ Flags:
|
|
|
1471
1620
|
|
|
1472
1621
|
Examples:
|
|
1473
1622
|
jdf validate spec/examples/hello-world.jdf
|
|
1474
|
-
jdf
|
|
1475
|
-
jdf
|
|
1476
|
-
jdf
|
|
1477
|
-
jdf
|
|
1623
|
+
jdf convert paper.pdf # PDF \u2192 JDF (or .jdfx for images)
|
|
1624
|
+
jdf convert contract.pdf --json | jq . # PDF \u2192 pure JSON, pipe-friendly
|
|
1625
|
+
jdf convert response.json -o response.jdf # LLM JSON output \u2192 validated JDF
|
|
1626
|
+
jdf convert README.md
|
|
1478
1627
|
`;
|
|
1479
1628
|
var BOOLEAN_FLAGS = /* @__PURE__ */ new Set(["help", "h", "json", "verbose", "skip-validate"]);
|
|
1480
1629
|
function parseArgs(argv) {
|
|
@@ -1540,10 +1689,13 @@ async function main() {
|
|
|
1540
1689
|
const ok = await validate(positional[0]);
|
|
1541
1690
|
process.exit(ok ? 0 : 1);
|
|
1542
1691
|
}
|
|
1692
|
+
// `convert` is the headline verb; `import` stays as a back-compat alias
|
|
1693
|
+
// so existing scripts and docs keep working.
|
|
1694
|
+
case "convert":
|
|
1543
1695
|
case "import": {
|
|
1544
1696
|
const input = positional[0];
|
|
1545
1697
|
if (!input) {
|
|
1546
|
-
console.error("Usage: jdf
|
|
1698
|
+
console.error("Usage: jdf convert <file.{pdf,json,md}> [-o output.jdf] [--json]");
|
|
1547
1699
|
process.exit(1);
|
|
1548
1700
|
}
|
|
1549
1701
|
const output = typeof flags.output === "string" ? flags.output : void 0;
|
package/dist/jdf-schema.json
CHANGED
|
@@ -42,7 +42,8 @@
|
|
|
42
42
|
"pageSize": { "$ref": "#/definitions/PageSize" },
|
|
43
43
|
"pageOrientation": { "$ref": "#/definitions/PageOrientation" },
|
|
44
44
|
"margins": { "$ref": "#/definitions/Margins" },
|
|
45
|
-
"unit": { "$ref": "#/definitions/Unit" }
|
|
45
|
+
"unit": { "$ref": "#/definitions/Unit" },
|
|
46
|
+
"flow": { "type": "boolean", "description": "Document-level default: lay elements out top-to-bottom and auto-paginate overflow in PDF export. Per-page `flow` overrides." }
|
|
46
47
|
}
|
|
47
48
|
},
|
|
48
49
|
"PageSize": {
|
|
@@ -335,7 +336,7 @@
|
|
|
335
336
|
"headers": { "type": "array", "items": { "type": "string" } },
|
|
336
337
|
"rows": {
|
|
337
338
|
"type": "array",
|
|
338
|
-
"items": { "type": "array", "items": { "oneOf": [{ "type": "string" }, { "type": "object", "required": ["content"], "properties": { "content": { "type": "string" }, "style": { "$ref": "#/definitions/StyleRef" }, "colspan": { "type": "integer" }, "rowspan": { "type": "integer" } } }] } }
|
|
339
|
+
"items": { "type": "array", "items": { "oneOf": [{ "type": "string" }, { "type": "object", "required": ["content"], "properties": { "content": { "type": "string" }, "style": { "$ref": "#/definitions/StyleRef" }, "align": { "type": "string", "enum": ["left","center","right","justify"] }, "colspan": { "type": "integer" }, "rowspan": { "type": "integer" } } }] } }
|
|
339
340
|
},
|
|
340
341
|
"position": { "$ref": "#/definitions/Position" }, "width": { "type": "number" },
|
|
341
342
|
"headerStyle": { "$ref": "#/definitions/StyleRef" },
|