@uurtech/jdf-cli 0.1.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.
- package/dist/index.js +331 -0
- package/dist/jdf-schema.json +298 -0
- package/package.json +57 -0
package/dist/index.js
ADDED
|
@@ -0,0 +1,331 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import fs from 'fs';
|
|
3
|
+
import path from 'path';
|
|
4
|
+
import { fileURLToPath } from 'url';
|
|
5
|
+
import Ajv from 'ajv';
|
|
6
|
+
import addFormats from 'ajv-formats';
|
|
7
|
+
|
|
8
|
+
var __dirname$1 = path.dirname(fileURLToPath(import.meta.url));
|
|
9
|
+
function resolveSchemaPath() {
|
|
10
|
+
const bundled = path.resolve(__dirname$1, "jdf-schema.json");
|
|
11
|
+
if (fs.existsSync(bundled)) return bundled;
|
|
12
|
+
const dev = path.resolve(__dirname$1, "../../../../spec/jdf-schema.json");
|
|
13
|
+
return dev;
|
|
14
|
+
}
|
|
15
|
+
var SCHEMA_PATH = resolveSchemaPath();
|
|
16
|
+
async function validate(file) {
|
|
17
|
+
const filePath = path.resolve(file);
|
|
18
|
+
if (!fs.existsSync(filePath)) {
|
|
19
|
+
console.error(`File not found: ${filePath}`);
|
|
20
|
+
return false;
|
|
21
|
+
}
|
|
22
|
+
let doc;
|
|
23
|
+
try {
|
|
24
|
+
doc = JSON.parse(fs.readFileSync(filePath, "utf-8"));
|
|
25
|
+
} catch (e) {
|
|
26
|
+
console.error(`Invalid JSON: ${e.message}`);
|
|
27
|
+
return false;
|
|
28
|
+
}
|
|
29
|
+
if (!fs.existsSync(SCHEMA_PATH)) {
|
|
30
|
+
console.error(`Schema not found at ${SCHEMA_PATH}`);
|
|
31
|
+
return false;
|
|
32
|
+
}
|
|
33
|
+
const schema = JSON.parse(fs.readFileSync(SCHEMA_PATH, "utf-8"));
|
|
34
|
+
const ajv = new Ajv({ allErrors: true, strict: false });
|
|
35
|
+
addFormats(ajv);
|
|
36
|
+
const validateFn = ajv.compile(schema);
|
|
37
|
+
const ok = validateFn(doc);
|
|
38
|
+
if (ok) {
|
|
39
|
+
const d = doc;
|
|
40
|
+
const pageCount = Array.isArray(d.pages) ? d.pages.length : 0;
|
|
41
|
+
const elCount = Array.isArray(d.pages) ? d.pages.reduce((acc, p) => acc + (Array.isArray(p?.elements) ? p.elements.length : 0), 0) : 0;
|
|
42
|
+
console.log(`\u2713 Valid: ${path.basename(filePath)}`);
|
|
43
|
+
console.log(` Format: ${d.$jdf}`);
|
|
44
|
+
console.log(` Title: ${d.meta?.title}`);
|
|
45
|
+
console.log(` Pages: ${pageCount}`);
|
|
46
|
+
console.log(` Elements: ${elCount}`);
|
|
47
|
+
return true;
|
|
48
|
+
}
|
|
49
|
+
console.error(`\u2717 Invalid: ${path.basename(filePath)}`);
|
|
50
|
+
for (const err of validateFn.errors || []) {
|
|
51
|
+
const loc = err.instancePath || "(root)";
|
|
52
|
+
console.error(` ${loc} \u2014 ${err.message}`);
|
|
53
|
+
}
|
|
54
|
+
return false;
|
|
55
|
+
}
|
|
56
|
+
async function importMarkdown(inputPath, outputPath) {
|
|
57
|
+
const input = path.resolve(inputPath);
|
|
58
|
+
const output = outputPath ? path.resolve(outputPath) : input.replace(/\.(md|markdown)$/i, ".jdf");
|
|
59
|
+
console.log(`Importing: ${input}`);
|
|
60
|
+
console.log(`Output: ${output}`);
|
|
61
|
+
const content = fs.readFileSync(input, "utf-8");
|
|
62
|
+
const doc = convertMarkdownToJdf(content, path.basename(input, path.extname(input)));
|
|
63
|
+
fs.writeFileSync(output, JSON.stringify(doc, null, 2));
|
|
64
|
+
console.log(`
|
|
65
|
+
Done! Created ${doc.pages.length} page(s)`);
|
|
66
|
+
console.log(`Open with: open -a "JDF Reader" "${output}"`);
|
|
67
|
+
}
|
|
68
|
+
function convertMarkdownToJdf(md, title) {
|
|
69
|
+
const maxY = 247;
|
|
70
|
+
const contentWidth = 166;
|
|
71
|
+
const pages = [];
|
|
72
|
+
let elements = [];
|
|
73
|
+
let y = 5;
|
|
74
|
+
let pageNum = 1;
|
|
75
|
+
const lines = md.split("\n");
|
|
76
|
+
let i = 0;
|
|
77
|
+
while (i < lines.length) {
|
|
78
|
+
const line = lines[i];
|
|
79
|
+
const headingMatch = line.match(/^(#{1,6})\s+(.+)/);
|
|
80
|
+
if (headingMatch) {
|
|
81
|
+
const level = headingMatch[1].length;
|
|
82
|
+
const text = headingMatch[2].trim();
|
|
83
|
+
const fontSize = level === 1 ? 22 : level === 2 ? 16 : level === 3 ? 13 : 11;
|
|
84
|
+
const height2 = fontSize * 0.6;
|
|
85
|
+
if (y + height2 > maxY) {
|
|
86
|
+
pages.push({ id: `page-${pageNum}`, elements });
|
|
87
|
+
elements = [];
|
|
88
|
+
y = 5;
|
|
89
|
+
pageNum++;
|
|
90
|
+
}
|
|
91
|
+
if (y > 10 && level <= 2) {
|
|
92
|
+
elements.push({ type: "shape", shape: "rect", position: { x: 0, y }, width: contentWidth, height: 0.3, fill: "#e2e8f0" });
|
|
93
|
+
y += 4;
|
|
94
|
+
}
|
|
95
|
+
elements.push({
|
|
96
|
+
type: "text",
|
|
97
|
+
content: text,
|
|
98
|
+
position: { x: 0, y },
|
|
99
|
+
width: contentWidth,
|
|
100
|
+
heading: true,
|
|
101
|
+
tocEntry: text,
|
|
102
|
+
style: { fontFamily: "Inter", fontSize, fontWeight: "bold", color: level <= 2 ? "#0f172a" : "#1e293b" }
|
|
103
|
+
});
|
|
104
|
+
y += height2 + 4;
|
|
105
|
+
i++;
|
|
106
|
+
continue;
|
|
107
|
+
}
|
|
108
|
+
if (line.match(/^\s*[-*+]\s/)) {
|
|
109
|
+
const items = [];
|
|
110
|
+
while (i < lines.length && lines[i].match(/^\s*[-*+]\s/)) {
|
|
111
|
+
items.push({ content: lines[i].replace(/^\s*[-*+]\s+/, "").trim() });
|
|
112
|
+
i++;
|
|
113
|
+
}
|
|
114
|
+
const height2 = items.length * 5 + 3;
|
|
115
|
+
if (y + height2 > maxY) {
|
|
116
|
+
pages.push({ id: `page-${pageNum}`, elements });
|
|
117
|
+
elements = [];
|
|
118
|
+
y = 5;
|
|
119
|
+
pageNum++;
|
|
120
|
+
}
|
|
121
|
+
elements.push({
|
|
122
|
+
type: "list",
|
|
123
|
+
listType: "unordered",
|
|
124
|
+
position: { x: 0, y },
|
|
125
|
+
width: contentWidth,
|
|
126
|
+
style: { fontFamily: "Inter", fontSize: 10, lineHeight: 1.6, color: "#334155" },
|
|
127
|
+
items
|
|
128
|
+
});
|
|
129
|
+
y += height2;
|
|
130
|
+
continue;
|
|
131
|
+
}
|
|
132
|
+
if (line.match(/^\s*\d+\.\s/)) {
|
|
133
|
+
const items = [];
|
|
134
|
+
while (i < lines.length && lines[i].match(/^\s*\d+\.\s/)) {
|
|
135
|
+
items.push({ content: lines[i].replace(/^\s*\d+\.\s+/, "").trim() });
|
|
136
|
+
i++;
|
|
137
|
+
}
|
|
138
|
+
const height2 = items.length * 5 + 3;
|
|
139
|
+
if (y + height2 > maxY) {
|
|
140
|
+
pages.push({ id: `page-${pageNum}`, elements });
|
|
141
|
+
elements = [];
|
|
142
|
+
y = 5;
|
|
143
|
+
pageNum++;
|
|
144
|
+
}
|
|
145
|
+
elements.push({
|
|
146
|
+
type: "list",
|
|
147
|
+
listType: "ordered",
|
|
148
|
+
position: { x: 0, y },
|
|
149
|
+
width: contentWidth,
|
|
150
|
+
style: { fontFamily: "Inter", fontSize: 10, lineHeight: 1.6, color: "#334155" },
|
|
151
|
+
items
|
|
152
|
+
});
|
|
153
|
+
y += height2;
|
|
154
|
+
continue;
|
|
155
|
+
}
|
|
156
|
+
if (line.startsWith("```")) {
|
|
157
|
+
const codeLines = [];
|
|
158
|
+
i++;
|
|
159
|
+
while (i < lines.length && !lines[i].startsWith("```")) {
|
|
160
|
+
codeLines.push(lines[i]);
|
|
161
|
+
i++;
|
|
162
|
+
}
|
|
163
|
+
i++;
|
|
164
|
+
const code = codeLines.join("\n");
|
|
165
|
+
const height2 = codeLines.length * 4 + 8;
|
|
166
|
+
if (y + height2 > maxY) {
|
|
167
|
+
pages.push({ id: `page-${pageNum}`, elements });
|
|
168
|
+
elements = [];
|
|
169
|
+
y = 5;
|
|
170
|
+
pageNum++;
|
|
171
|
+
}
|
|
172
|
+
elements.push({
|
|
173
|
+
type: "text",
|
|
174
|
+
content: code,
|
|
175
|
+
position: { x: 0, y },
|
|
176
|
+
width: contentWidth,
|
|
177
|
+
style: { fontFamily: "JetBrains Mono", fontSize: 9, color: "#1e293b", backgroundColor: "#f1f5f9", padding: 8, borderRadius: 4, lineHeight: 1.5 }
|
|
178
|
+
});
|
|
179
|
+
y += height2;
|
|
180
|
+
continue;
|
|
181
|
+
}
|
|
182
|
+
if (line.trim() === "") {
|
|
183
|
+
y += 3;
|
|
184
|
+
i++;
|
|
185
|
+
continue;
|
|
186
|
+
}
|
|
187
|
+
let para = "";
|
|
188
|
+
while (i < lines.length && lines[i].trim() !== "" && !lines[i].match(/^#{1,6}\s/) && !lines[i].match(/^\s*[-*+]\s/) && !lines[i].startsWith("```")) {
|
|
189
|
+
para += (para ? " " : "") + lines[i].trim();
|
|
190
|
+
i++;
|
|
191
|
+
}
|
|
192
|
+
const paraLines = Math.ceil(para.length / 80);
|
|
193
|
+
const height = paraLines * 4.5 + 3;
|
|
194
|
+
if (y + height > maxY) {
|
|
195
|
+
pages.push({ id: `page-${pageNum}`, elements });
|
|
196
|
+
elements = [];
|
|
197
|
+
y = 5;
|
|
198
|
+
pageNum++;
|
|
199
|
+
}
|
|
200
|
+
elements.push({
|
|
201
|
+
type: "text",
|
|
202
|
+
content: para,
|
|
203
|
+
position: { x: 0, y },
|
|
204
|
+
width: contentWidth,
|
|
205
|
+
style: { fontFamily: "Inter", fontSize: 10, lineHeight: 1.6, color: "#334155" }
|
|
206
|
+
});
|
|
207
|
+
y += height;
|
|
208
|
+
}
|
|
209
|
+
if (elements.length > 0) {
|
|
210
|
+
pages.push({ id: `page-${pageNum}`, elements });
|
|
211
|
+
}
|
|
212
|
+
return {
|
|
213
|
+
$jdf: "1.0.0",
|
|
214
|
+
meta: {
|
|
215
|
+
title,
|
|
216
|
+
pageSize: "A4",
|
|
217
|
+
unit: "mm",
|
|
218
|
+
margins: { top: 25, right: 22, bottom: 25, left: 22 }
|
|
219
|
+
},
|
|
220
|
+
styles: {
|
|
221
|
+
heading: { fontFamily: "Inter", fontSize: 22, fontWeight: "bold", color: "#0f172a" },
|
|
222
|
+
body: { fontFamily: "Inter", fontSize: 10, lineHeight: 1.6, color: "#334155" }
|
|
223
|
+
},
|
|
224
|
+
pages
|
|
225
|
+
};
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
// src/commands/import-pdf.ts
|
|
229
|
+
async function importPdfPlaceholder(_inputPath, _outputPath) {
|
|
230
|
+
console.error("PDF import via CLI is not yet wired up \u2014 run the JDF Reader app and use Open / drag-drop, which uses the Rust pdf-extract pipeline.");
|
|
231
|
+
console.error("Track progress at: https://github.com/uurtech/jdf");
|
|
232
|
+
process.exit(2);
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
// src/index.ts
|
|
236
|
+
var HELP = `jdf \u2014 JSON Document Format CLI
|
|
237
|
+
|
|
238
|
+
Usage:
|
|
239
|
+
jdf validate <file.jdf>
|
|
240
|
+
jdf import <file.{md,pdf}> [-o output.jdf]
|
|
241
|
+
jdf --help
|
|
242
|
+
|
|
243
|
+
Commands:
|
|
244
|
+
validate Validate a .jdf file against the JDF schema
|
|
245
|
+
import Convert a markdown or PDF file to JDF
|
|
246
|
+
|
|
247
|
+
Examples:
|
|
248
|
+
jdf validate spec/examples/hello-world.jdf
|
|
249
|
+
jdf import README.md
|
|
250
|
+
jdf import paper.pdf -o paper.jdf
|
|
251
|
+
`;
|
|
252
|
+
function parseArgs(argv) {
|
|
253
|
+
const positional = [];
|
|
254
|
+
const flags = {};
|
|
255
|
+
let command;
|
|
256
|
+
for (let i = 0; i < argv.length; i++) {
|
|
257
|
+
const a = argv[i];
|
|
258
|
+
if (i === 0 && !a.startsWith("-")) {
|
|
259
|
+
command = a;
|
|
260
|
+
continue;
|
|
261
|
+
}
|
|
262
|
+
if (a === "--help" || a === "-h") {
|
|
263
|
+
flags["help"] = true;
|
|
264
|
+
continue;
|
|
265
|
+
}
|
|
266
|
+
if (a.startsWith("--")) {
|
|
267
|
+
const k = a.slice(2);
|
|
268
|
+
const next = argv[i + 1];
|
|
269
|
+
if (next && !next.startsWith("-")) {
|
|
270
|
+
flags[k] = next;
|
|
271
|
+
i++;
|
|
272
|
+
} else flags[k] = true;
|
|
273
|
+
continue;
|
|
274
|
+
}
|
|
275
|
+
if (a === "-o" || a === "--output") {
|
|
276
|
+
const next = argv[i + 1];
|
|
277
|
+
if (next) {
|
|
278
|
+
flags["output"] = next;
|
|
279
|
+
i++;
|
|
280
|
+
}
|
|
281
|
+
continue;
|
|
282
|
+
}
|
|
283
|
+
positional.push(a);
|
|
284
|
+
}
|
|
285
|
+
return { command, positional, flags };
|
|
286
|
+
}
|
|
287
|
+
async function main() {
|
|
288
|
+
const { command, positional, flags } = parseArgs(process.argv.slice(2));
|
|
289
|
+
if (!command || flags.help) {
|
|
290
|
+
console.log(HELP);
|
|
291
|
+
process.exit(command ? 0 : 1);
|
|
292
|
+
}
|
|
293
|
+
try {
|
|
294
|
+
switch (command) {
|
|
295
|
+
case "validate": {
|
|
296
|
+
if (!positional[0]) {
|
|
297
|
+
console.error("Usage: jdf validate <file.jdf>");
|
|
298
|
+
process.exit(1);
|
|
299
|
+
}
|
|
300
|
+
const ok = await validate(positional[0]);
|
|
301
|
+
process.exit(ok ? 0 : 1);
|
|
302
|
+
}
|
|
303
|
+
case "import": {
|
|
304
|
+
const input = positional[0];
|
|
305
|
+
if (!input) {
|
|
306
|
+
console.error("Usage: jdf import <file.{md,pdf}> [-o output.jdf]");
|
|
307
|
+
process.exit(1);
|
|
308
|
+
}
|
|
309
|
+
const output = typeof flags.output === "string" ? flags.output : void 0;
|
|
310
|
+
const lower = input.toLowerCase();
|
|
311
|
+
if (lower.endsWith(".md") || lower.endsWith(".markdown")) {
|
|
312
|
+
await importMarkdown(input, output);
|
|
313
|
+
} else if (lower.endsWith(".pdf")) {
|
|
314
|
+
await importPdfPlaceholder(input, output);
|
|
315
|
+
} else {
|
|
316
|
+
console.error(`Unsupported file type: ${input}`);
|
|
317
|
+
process.exit(1);
|
|
318
|
+
}
|
|
319
|
+
break;
|
|
320
|
+
}
|
|
321
|
+
default:
|
|
322
|
+
console.error(`Unknown command: ${command}`);
|
|
323
|
+
console.log(HELP);
|
|
324
|
+
process.exit(1);
|
|
325
|
+
}
|
|
326
|
+
} catch (e) {
|
|
327
|
+
console.error(`Error: ${e.message || e}`);
|
|
328
|
+
process.exit(1);
|
|
329
|
+
}
|
|
330
|
+
}
|
|
331
|
+
main();
|
|
@@ -0,0 +1,298 @@
|
|
|
1
|
+
{
|
|
2
|
+
"$schema": "http://json-schema.org/draft-07/schema#",
|
|
3
|
+
"$id": "https://jdf.dev/schema/1.0.0/jdf-schema.json",
|
|
4
|
+
"title": "JDF Document",
|
|
5
|
+
"description": "JSON Document Format — a human-readable alternative to PDF",
|
|
6
|
+
"type": "object",
|
|
7
|
+
"required": ["$jdf", "meta", "pages"],
|
|
8
|
+
"additionalProperties": false,
|
|
9
|
+
"properties": {
|
|
10
|
+
"$jdf": {
|
|
11
|
+
"type": "string",
|
|
12
|
+
"description": "JDF format version (semver)",
|
|
13
|
+
"pattern": "^\\d+\\.\\d+\\.\\d+$",
|
|
14
|
+
"examples": ["1.0.0"]
|
|
15
|
+
},
|
|
16
|
+
"meta": { "$ref": "#/definitions/Meta" },
|
|
17
|
+
"styles": {
|
|
18
|
+
"type": "object",
|
|
19
|
+
"description": "Named reusable style definitions",
|
|
20
|
+
"additionalProperties": { "$ref": "#/definitions/Style" }
|
|
21
|
+
},
|
|
22
|
+
"resources": { "$ref": "#/definitions/Resources" },
|
|
23
|
+
"header": { "$ref": "#/definitions/HeaderFooter" },
|
|
24
|
+
"footer": { "$ref": "#/definitions/HeaderFooter" },
|
|
25
|
+
"pages": {
|
|
26
|
+
"type": "array",
|
|
27
|
+
"minItems": 1,
|
|
28
|
+
"items": { "$ref": "#/definitions/Page" }
|
|
29
|
+
}
|
|
30
|
+
},
|
|
31
|
+
"definitions": {
|
|
32
|
+
"Meta": {
|
|
33
|
+
"type": "object",
|
|
34
|
+
"required": ["title"],
|
|
35
|
+
"properties": {
|
|
36
|
+
"title": { "type": "string" },
|
|
37
|
+
"author": { "type": "string" },
|
|
38
|
+
"created": { "type": "string", "format": "date-time" },
|
|
39
|
+
"modified": { "type": "string", "format": "date-time" },
|
|
40
|
+
"language": { "type": "string" },
|
|
41
|
+
"keywords": { "type": "array", "items": { "type": "string" } },
|
|
42
|
+
"pageSize": { "$ref": "#/definitions/PageSize" },
|
|
43
|
+
"pageOrientation": { "$ref": "#/definitions/PageOrientation" },
|
|
44
|
+
"margins": { "$ref": "#/definitions/Margins" },
|
|
45
|
+
"unit": { "$ref": "#/definitions/Unit" }
|
|
46
|
+
}
|
|
47
|
+
},
|
|
48
|
+
"PageSize": {
|
|
49
|
+
"oneOf": [
|
|
50
|
+
{ "type": "string", "enum": ["A4", "A3", "A5", "Letter", "Legal", "Tabloid"] },
|
|
51
|
+
{ "type": "object", "required": ["width", "height"], "properties": { "width": { "type": "number" }, "height": { "type": "number" } } }
|
|
52
|
+
]
|
|
53
|
+
},
|
|
54
|
+
"PageOrientation": { "type": "string", "enum": ["portrait", "landscape"] },
|
|
55
|
+
"Unit": { "type": "string", "enum": ["mm", "in", "pt", "px"] },
|
|
56
|
+
"Margins": {
|
|
57
|
+
"type": "object",
|
|
58
|
+
"properties": {
|
|
59
|
+
"top": { "type": "number" }, "right": { "type": "number" },
|
|
60
|
+
"bottom": { "type": "number" }, "left": { "type": "number" }
|
|
61
|
+
}
|
|
62
|
+
},
|
|
63
|
+
"Position": {
|
|
64
|
+
"type": "object",
|
|
65
|
+
"required": ["x", "y"],
|
|
66
|
+
"properties": { "x": { "type": "number" }, "y": { "type": "number" } }
|
|
67
|
+
},
|
|
68
|
+
"Style": {
|
|
69
|
+
"type": "object",
|
|
70
|
+
"properties": {
|
|
71
|
+
"fontFamily": { "type": "string" },
|
|
72
|
+
"fontSize": { "type": "number" },
|
|
73
|
+
"fontWeight": { "oneOf": [ { "type": "string", "enum": ["normal","bold","100","200","300","400","500","600","700","800","900"] }, { "type": "number" } ] },
|
|
74
|
+
"fontStyle": { "type": "string", "enum": ["normal", "italic"] },
|
|
75
|
+
"textDecoration": { "type": "string", "enum": ["none","underline","strikethrough","line-through","underline strikethrough"] },
|
|
76
|
+
"color": { "type": "string" },
|
|
77
|
+
"backgroundColor": { "type": "string" },
|
|
78
|
+
"textAlign": { "type": "string", "enum": ["left","center","right","justify"] },
|
|
79
|
+
"lineHeight": { "type": "number" },
|
|
80
|
+
"letterSpacing": { "oneOf": [{ "type": "number" }, { "type": "string" }] },
|
|
81
|
+
"padding": { "oneOf": [{ "type": "number" }, { "type": "string" }, { "$ref": "#/definitions/Margins" }] },
|
|
82
|
+
"margin": { "oneOf": [{ "type": "number" }, { "type": "string" }, { "$ref": "#/definitions/Margins" }] },
|
|
83
|
+
"marginTop": { "type": "number" },
|
|
84
|
+
"marginBottom": { "type": "number" },
|
|
85
|
+
"border": { "type": "string" },
|
|
86
|
+
"borderRadius": { "oneOf": [{ "type": "number" }, { "type": "string" }] },
|
|
87
|
+
"opacity": { "type": "number", "minimum": 0, "maximum": 1 }
|
|
88
|
+
}
|
|
89
|
+
},
|
|
90
|
+
"StyleRef": {
|
|
91
|
+
"oneOf": [
|
|
92
|
+
{ "type": "string" },
|
|
93
|
+
{ "type": "array", "items": { "type": "string" } },
|
|
94
|
+
{ "$ref": "#/definitions/Style" }
|
|
95
|
+
]
|
|
96
|
+
},
|
|
97
|
+
"Link": {
|
|
98
|
+
"oneOf": [
|
|
99
|
+
{ "type": "string" },
|
|
100
|
+
{ "type": "object", "required": ["target"], "properties": { "type": { "type": "string", "enum": ["internal","external"] }, "target": { "type": "string" } } }
|
|
101
|
+
]
|
|
102
|
+
},
|
|
103
|
+
"Resources": {
|
|
104
|
+
"type": "object",
|
|
105
|
+
"properties": {
|
|
106
|
+
"fonts": { "type": "array", "items": { "$ref": "#/definitions/FontResource" } },
|
|
107
|
+
"images": { "type": "object", "additionalProperties": { "$ref": "#/definitions/ImageResource" } }
|
|
108
|
+
},
|
|
109
|
+
"additionalProperties": { "$ref": "#/definitions/ImageResource" }
|
|
110
|
+
},
|
|
111
|
+
"FontResource": {
|
|
112
|
+
"type": "object",
|
|
113
|
+
"required": ["family", "src"],
|
|
114
|
+
"properties": {
|
|
115
|
+
"family": { "type": "string" },
|
|
116
|
+
"src": { "type": "string", "enum": ["embedded","file","system"] },
|
|
117
|
+
"data": { "type": "string" },
|
|
118
|
+
"path": { "type": "string" },
|
|
119
|
+
"weight": { "type": "string" },
|
|
120
|
+
"style": { "type": "string" }
|
|
121
|
+
}
|
|
122
|
+
},
|
|
123
|
+
"ImageResource": {
|
|
124
|
+
"type": "object",
|
|
125
|
+
"properties": {
|
|
126
|
+
"src": { "type": "string", "enum": ["embedded","file"] },
|
|
127
|
+
"mimeType": { "type": "string" },
|
|
128
|
+
"data": { "type": "string" },
|
|
129
|
+
"path": { "type": "string" }
|
|
130
|
+
}
|
|
131
|
+
},
|
|
132
|
+
"HeaderFooter": {
|
|
133
|
+
"type": "object",
|
|
134
|
+
"properties": {
|
|
135
|
+
"height": { "type": "number" },
|
|
136
|
+
"elements": { "type": "array", "items": { "$ref": "#/definitions/Element" } },
|
|
137
|
+
"content": { "type": "string", "description": "Template string with {{pageNumber}}, {{totalPages}}, {{title}}, {{author}}" },
|
|
138
|
+
"style": { "$ref": "#/definitions/StyleRef" }
|
|
139
|
+
}
|
|
140
|
+
},
|
|
141
|
+
"Page": {
|
|
142
|
+
"type": "object",
|
|
143
|
+
"required": ["elements"],
|
|
144
|
+
"properties": {
|
|
145
|
+
"id": { "type": "string" },
|
|
146
|
+
"pageSize": { "$ref": "#/definitions/PageSize" },
|
|
147
|
+
"pageOrientation": { "$ref": "#/definitions/PageOrientation" },
|
|
148
|
+
"margins": { "$ref": "#/definitions/Margins" },
|
|
149
|
+
"background": { "type": "string" },
|
|
150
|
+
"flow": { "type": "boolean" },
|
|
151
|
+
"header": { "$ref": "#/definitions/HeaderFooter" },
|
|
152
|
+
"footer": { "$ref": "#/definitions/HeaderFooter" },
|
|
153
|
+
"elements": { "type": "array", "items": { "$ref": "#/definitions/Element" } }
|
|
154
|
+
}
|
|
155
|
+
},
|
|
156
|
+
"Element": {
|
|
157
|
+
"oneOf": [
|
|
158
|
+
{ "$ref": "#/definitions/TextElement" },
|
|
159
|
+
{ "$ref": "#/definitions/RichTextElement" },
|
|
160
|
+
{ "$ref": "#/definitions/ImageElement" },
|
|
161
|
+
{ "$ref": "#/definitions/TableElement" },
|
|
162
|
+
{ "$ref": "#/definitions/ListElement" },
|
|
163
|
+
{ "$ref": "#/definitions/ShapeElement" },
|
|
164
|
+
{ "$ref": "#/definitions/CollapsibleElement" },
|
|
165
|
+
{ "$ref": "#/definitions/TocElement" }
|
|
166
|
+
]
|
|
167
|
+
},
|
|
168
|
+
"TextElement": {
|
|
169
|
+
"type": "object",
|
|
170
|
+
"required": ["type", "content"],
|
|
171
|
+
"properties": {
|
|
172
|
+
"type": { "const": "text" },
|
|
173
|
+
"content": { "type": "string" },
|
|
174
|
+
"style": { "$ref": "#/definitions/StyleRef" },
|
|
175
|
+
"position": { "$ref": "#/definitions/Position" },
|
|
176
|
+
"width": { "type": "number" },
|
|
177
|
+
"height": { "type": "number" },
|
|
178
|
+
"align": { "type": "string", "enum": ["left","center","right","justify"] },
|
|
179
|
+
"heading": { "oneOf": [{ "type": "boolean" }, { "type": "integer", "minimum": 1, "maximum": 6 }] },
|
|
180
|
+
"tocEntry": { "type": "string" },
|
|
181
|
+
"tocLevel": { "type": "integer", "minimum": 1, "maximum": 6 },
|
|
182
|
+
"link": { "$ref": "#/definitions/Link" }
|
|
183
|
+
}
|
|
184
|
+
},
|
|
185
|
+
"RichTextElement": {
|
|
186
|
+
"type": "object",
|
|
187
|
+
"required": ["type", "runs"],
|
|
188
|
+
"properties": {
|
|
189
|
+
"type": { "const": "richtext" },
|
|
190
|
+
"runs": {
|
|
191
|
+
"type": "array",
|
|
192
|
+
"items": {
|
|
193
|
+
"type": "object",
|
|
194
|
+
"required": ["text"],
|
|
195
|
+
"properties": {
|
|
196
|
+
"text": { "type": "string" },
|
|
197
|
+
"bold": { "type": "boolean" }, "italic": { "type": "boolean" },
|
|
198
|
+
"underline": { "type": "boolean" }, "strikethrough": { "type": "boolean" },
|
|
199
|
+
"color": { "type": "string" }, "fontSize": { "type": "number" }, "fontFamily": { "type": "string" },
|
|
200
|
+
"style": { "$ref": "#/definitions/StyleRef" }, "link": { "$ref": "#/definitions/Link" }
|
|
201
|
+
}
|
|
202
|
+
}
|
|
203
|
+
},
|
|
204
|
+
"style": { "$ref": "#/definitions/StyleRef" },
|
|
205
|
+
"position": { "$ref": "#/definitions/Position" },
|
|
206
|
+
"width": { "type": "number" }, "height": { "type": "number" }
|
|
207
|
+
}
|
|
208
|
+
},
|
|
209
|
+
"ImageElement": {
|
|
210
|
+
"type": "object",
|
|
211
|
+
"required": ["type"],
|
|
212
|
+
"anyOf": [{ "required": ["resource"] }, { "required": ["src"] }],
|
|
213
|
+
"properties": {
|
|
214
|
+
"type": { "const": "image" },
|
|
215
|
+
"resource": { "type": "string" }, "src": { "type": "string" }, "alt": { "type": "string" },
|
|
216
|
+
"position": { "$ref": "#/definitions/Position" },
|
|
217
|
+
"width": { "type": "number" }, "height": { "type": "number" },
|
|
218
|
+
"fit": { "type": "string", "enum": ["contain","cover","fill","none"] },
|
|
219
|
+
"link": { "$ref": "#/definitions/Link" },
|
|
220
|
+
"style": { "$ref": "#/definitions/StyleRef" }
|
|
221
|
+
}
|
|
222
|
+
},
|
|
223
|
+
"TableElement": {
|
|
224
|
+
"type": "object",
|
|
225
|
+
"required": ["type", "rows"],
|
|
226
|
+
"properties": {
|
|
227
|
+
"type": { "const": "table" },
|
|
228
|
+
"columns": {
|
|
229
|
+
"type": "array",
|
|
230
|
+
"items": { "type": "object", "properties": { "width": { "oneOf": [{ "type": "string" }, { "type": "number" }] }, "header": { "type": "string" }, "align": { "type": "string", "enum": ["left","center","right","justify"] } } }
|
|
231
|
+
},
|
|
232
|
+
"headers": { "type": "array", "items": { "type": "string" } },
|
|
233
|
+
"rows": {
|
|
234
|
+
"type": "array",
|
|
235
|
+
"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" } } }] } }
|
|
236
|
+
},
|
|
237
|
+
"position": { "$ref": "#/definitions/Position" }, "width": { "type": "number" },
|
|
238
|
+
"headerStyle": { "$ref": "#/definitions/StyleRef" },
|
|
239
|
+
"rowStyle": { "$ref": "#/definitions/StyleRef" },
|
|
240
|
+
"alternateRowStyle": { "$ref": "#/definitions/StyleRef" },
|
|
241
|
+
"alternatingRowColor": { "type": "string" },
|
|
242
|
+
"borders": { "oneOf": [{ "type": "boolean" }, { "type": "object", "properties": { "outer": { "type": "boolean" }, "inner": { "type": "boolean" }, "color": { "type": "string" }, "width": { "type": "number" } } }] },
|
|
243
|
+
"style": { "$ref": "#/definitions/StyleRef" }
|
|
244
|
+
}
|
|
245
|
+
},
|
|
246
|
+
"ListElement": {
|
|
247
|
+
"type": "object",
|
|
248
|
+
"required": ["type", "items"],
|
|
249
|
+
"properties": {
|
|
250
|
+
"type": { "const": "list" },
|
|
251
|
+
"items": {
|
|
252
|
+
"type": "array",
|
|
253
|
+
"items": { "type": "object", "required": ["content"], "properties": { "content": { "type": "string" }, "style": { "$ref": "#/definitions/StyleRef" }, "children": { "type": "array", "items": { "$ref": "#/definitions/ListElement/properties/items/items" } }, "listType": { "type": "string", "enum": ["ordered","unordered"] } } }
|
|
254
|
+
},
|
|
255
|
+
"listType": { "type": "string", "enum": ["ordered","unordered"] },
|
|
256
|
+
"ordered": { "type": "boolean" },
|
|
257
|
+
"position": { "$ref": "#/definitions/Position" }, "width": { "type": "number" },
|
|
258
|
+
"style": { "$ref": "#/definitions/StyleRef" }
|
|
259
|
+
}
|
|
260
|
+
},
|
|
261
|
+
"ShapeElement": {
|
|
262
|
+
"type": "object",
|
|
263
|
+
"required": ["type", "shape"],
|
|
264
|
+
"properties": {
|
|
265
|
+
"type": { "const": "shape" },
|
|
266
|
+
"shape": { "type": "string", "enum": ["rect","circle","ellipse","line","path"] },
|
|
267
|
+
"position": { "$ref": "#/definitions/Position" }, "width": { "type": "number" }, "height": { "type": "number" },
|
|
268
|
+
"fill": { "type": "string" },
|
|
269
|
+
"stroke": { "oneOf": [{ "type": "string" }, { "type": "object", "properties": { "color": { "type": "string" }, "width": { "type": "number" } } }] },
|
|
270
|
+
"strokeWidth": { "type": "number" }, "borderRadius": { "type": "number" }, "path": { "type": "string" },
|
|
271
|
+
"points": { "type": "array", "items": { "$ref": "#/definitions/Position" } },
|
|
272
|
+
"style": { "$ref": "#/definitions/StyleRef" }
|
|
273
|
+
}
|
|
274
|
+
},
|
|
275
|
+
"CollapsibleElement": {
|
|
276
|
+
"type": "object",
|
|
277
|
+
"required": ["type", "title", "elements"],
|
|
278
|
+
"properties": {
|
|
279
|
+
"type": { "const": "collapsible" },
|
|
280
|
+
"title": { "type": "string" },
|
|
281
|
+
"elements": { "type": "array", "items": { "$ref": "#/definitions/Element" } },
|
|
282
|
+
"expanded": { "type": "boolean" },
|
|
283
|
+
"position": { "$ref": "#/definitions/Position" }, "width": { "type": "number" },
|
|
284
|
+
"style": { "$ref": "#/definitions/StyleRef" }
|
|
285
|
+
}
|
|
286
|
+
},
|
|
287
|
+
"TocElement": {
|
|
288
|
+
"type": "object",
|
|
289
|
+
"required": ["type"],
|
|
290
|
+
"properties": {
|
|
291
|
+
"type": { "const": "toc" }, "title": { "type": "string" },
|
|
292
|
+
"position": { "$ref": "#/definitions/Position" }, "width": { "type": "number" },
|
|
293
|
+
"depth": { "type": "integer", "minimum": 1, "maximum": 6 },
|
|
294
|
+
"style": { "$ref": "#/definitions/StyleRef" }
|
|
295
|
+
}
|
|
296
|
+
}
|
|
297
|
+
}
|
|
298
|
+
}
|
package/package.json
ADDED
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@uurtech/jdf-cli",
|
|
3
|
+
"version": "0.1.1",
|
|
4
|
+
"description": "Command-line tool for the JDF (JSON Document Format) — validate and convert documents.",
|
|
5
|
+
"license": "MIT",
|
|
6
|
+
"author": "Ugur Kazdal",
|
|
7
|
+
"homepage": "https://github.com/uurtech/jdf#readme",
|
|
8
|
+
"repository": {
|
|
9
|
+
"type": "git",
|
|
10
|
+
"url": "git+https://github.com/uurtech/jdf.git",
|
|
11
|
+
"directory": "tools/jdf-cli"
|
|
12
|
+
},
|
|
13
|
+
"bugs": {
|
|
14
|
+
"url": "https://github.com/uurtech/jdf/issues"
|
|
15
|
+
},
|
|
16
|
+
"keywords": [
|
|
17
|
+
"jdf",
|
|
18
|
+
"json",
|
|
19
|
+
"document",
|
|
20
|
+
"cli",
|
|
21
|
+
"validator",
|
|
22
|
+
"ajv",
|
|
23
|
+
"json-schema",
|
|
24
|
+
"pdf-alternative"
|
|
25
|
+
],
|
|
26
|
+
"type": "module",
|
|
27
|
+
"main": "./dist/index.js",
|
|
28
|
+
"bin": {
|
|
29
|
+
"jdf": "./dist/index.js"
|
|
30
|
+
},
|
|
31
|
+
"files": [
|
|
32
|
+
"dist",
|
|
33
|
+
"README.md",
|
|
34
|
+
"LICENSE"
|
|
35
|
+
],
|
|
36
|
+
"engines": {
|
|
37
|
+
"node": ">=18"
|
|
38
|
+
},
|
|
39
|
+
"scripts": {
|
|
40
|
+
"build": "tsup",
|
|
41
|
+
"dev": "tsup --watch",
|
|
42
|
+
"start": "tsx src/index.ts",
|
|
43
|
+
"typecheck": "tsc --noEmit",
|
|
44
|
+
"prepublishOnly": "pnpm build"
|
|
45
|
+
},
|
|
46
|
+
"dependencies": {
|
|
47
|
+
"ajv": "^8.17.1",
|
|
48
|
+
"ajv-formats": "^3.0.1"
|
|
49
|
+
},
|
|
50
|
+
"devDependencies": {
|
|
51
|
+
"@jdf/core": "workspace:*",
|
|
52
|
+
"@types/node": "^22.10.0",
|
|
53
|
+
"tsup": "^8.3.5",
|
|
54
|
+
"tsx": "^4.19.2",
|
|
55
|
+
"typescript": "^5.5.0"
|
|
56
|
+
}
|
|
57
|
+
}
|