@quillmark/quillify 0.4.0 → 0.5.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/dist/assemble.js +10 -13
- package/dist/xfa-widgets.d.ts +1 -1
- package/dist/xfa.js +26 -2
- package/package.json +3 -3
package/dist/assemble.js
CHANGED
|
@@ -1,11 +1,12 @@
|
|
|
1
1
|
/** Every quill the library builds is a first version. */
|
|
2
2
|
export const QUILL_VERSION = '0.1.0';
|
|
3
3
|
/**
|
|
4
|
-
*
|
|
5
|
-
*
|
|
6
|
-
* (U+007F to U+009F, U+FFFE and U+FFFF) are written as `\u` escapes.
|
|
4
|
+
* JSON as YAML the engine reads: the characters its reader refuses and JSON
|
|
5
|
+
* leaves raw (U+007F to U+009F, U+FFFE and U+FFFF) are written as `\u` escapes.
|
|
7
6
|
*/
|
|
8
|
-
const
|
|
7
|
+
const escaped = (json) => json.replace(/[\u007f-\u009f\ufffe\uffff]/g, (c) => `\\u${c.charCodeAt(0).toString(16).padStart(4, '0')}`);
|
|
8
|
+
/** A string as a YAML double-quoted scalar, which JSON writes from a well formed string. */
|
|
9
|
+
const q = (s) => escaped(JSON.stringify(s.toWellFormed()));
|
|
9
10
|
/**
|
|
10
11
|
* A `default:` says nobody need answer the field. A required field declares
|
|
11
12
|
* none, a boolean apart: it defaults to false either way.
|
|
@@ -14,14 +15,13 @@ function fieldYaml(field) {
|
|
|
14
15
|
const lines = [` ${q(field.name)}:`];
|
|
15
16
|
if (field.title !== undefined)
|
|
16
17
|
lines.push(` title: ${q(field.title)}`);
|
|
17
|
-
const ui = [];
|
|
18
18
|
switch (field.type) {
|
|
19
19
|
case 'string':
|
|
20
|
-
lines.push(' type:
|
|
20
|
+
lines.push(' type: text');
|
|
21
21
|
if (!field.required)
|
|
22
22
|
lines.push(' default: ""');
|
|
23
23
|
if (field.multiline)
|
|
24
|
-
|
|
24
|
+
lines.push(' multiline: true');
|
|
25
25
|
break;
|
|
26
26
|
case 'boolean':
|
|
27
27
|
lines.push(' type: boolean', ' default: false');
|
|
@@ -32,7 +32,7 @@ function fieldYaml(field) {
|
|
|
32
32
|
// It refuses ";", ">" and "|" in a value too, so a dropdown with one is a text box.
|
|
33
33
|
const values = (field.options ?? []).filter((option) => option !== '');
|
|
34
34
|
if (values.length === 0 || values.some((value) => /[;>|]/.test(value)))
|
|
35
|
-
lines.push(' type:
|
|
35
|
+
lines.push(' type: text');
|
|
36
36
|
else {
|
|
37
37
|
lines.push(' type: enum', ' values:');
|
|
38
38
|
for (const value of values)
|
|
@@ -43,14 +43,11 @@ function fieldYaml(field) {
|
|
|
43
43
|
break;
|
|
44
44
|
}
|
|
45
45
|
case 'array':
|
|
46
|
-
lines.push(' type: array', ' items:', ' type:
|
|
46
|
+
lines.push(' type: array', ' items:', ' type: text');
|
|
47
47
|
if (!field.required)
|
|
48
48
|
lines.push(' default: []');
|
|
49
|
-
ui.push(' multiline: true');
|
|
50
49
|
break;
|
|
51
50
|
}
|
|
52
|
-
if (ui.length > 0)
|
|
53
|
-
lines.push(' ui:', ...ui);
|
|
54
51
|
lines.push(` description: ${q(field.description)}`);
|
|
55
52
|
return lines;
|
|
56
53
|
}
|
|
@@ -131,7 +128,7 @@ export function assembleQuill(form, inventory, formPdf) {
|
|
|
131
128
|
const tree = new Map([
|
|
132
129
|
['Quill.yaml', encoder.encode(quillYaml(form, bound))],
|
|
133
130
|
['form.pdf', formPdf],
|
|
134
|
-
['form.
|
|
131
|
+
['form.yaml', encoder.encode(escaped(JSON.stringify(formJson, null, 2)) + '\n')]
|
|
135
132
|
]);
|
|
136
133
|
const example = exampleMarkdown(form, bound);
|
|
137
134
|
if (example)
|
package/dist/xfa-widgets.d.ts
CHANGED
|
@@ -8,7 +8,7 @@ export interface XfaWidget {
|
|
|
8
8
|
name: string;
|
|
9
9
|
kind: XfaKind;
|
|
10
10
|
page: number;
|
|
11
|
-
/** Points, top-left origin, page-relative: the rect `form.
|
|
11
|
+
/** Points, top-left origin, page-relative: the rect `form.yaml` wants. */
|
|
12
12
|
x: number;
|
|
13
13
|
y: number;
|
|
14
14
|
w: number;
|
package/dist/xfa.js
CHANGED
|
@@ -54,9 +54,11 @@ export async function xfaToAcroForm(bytes, signal) {
|
|
|
54
54
|
construct: 'image',
|
|
55
55
|
detail: 'pixels'
|
|
56
56
|
});
|
|
57
|
-
|
|
57
|
+
const harvested = harvestWidgets(laid, number);
|
|
58
|
+
for (const widget of harvested)
|
|
58
59
|
widgets.push(widget);
|
|
59
|
-
|
|
60
|
+
const shown = whiten(list, harvested);
|
|
61
|
+
await paint(out.addPage([list.width, list.height]), number, shown, async (name) => {
|
|
60
62
|
let font = fonts.get(name);
|
|
61
63
|
if (!font)
|
|
62
64
|
fonts.set(name, (font = await out.embedFont(name)));
|
|
@@ -71,6 +73,28 @@ export async function xfaToAcroForm(bytes, signal) {
|
|
|
71
73
|
await opened?.close();
|
|
72
74
|
}
|
|
73
75
|
}
|
|
76
|
+
/**
|
|
77
|
+
* The engine fills every field in black, whatever colour the form gives its
|
|
78
|
+
* text. A field whose middle lies on a fill that black reads on at under 4.5:1,
|
|
79
|
+
* WCAG's floor for text, gets a white box drawn just over that fill.
|
|
80
|
+
*/
|
|
81
|
+
function whiten(list, widgets) {
|
|
82
|
+
const drawn = [...list.drawn];
|
|
83
|
+
const filled = (item) => 'edges' in item && item.fill !== null;
|
|
84
|
+
for (const { x, y, w, h } of widgets) {
|
|
85
|
+
const [u, v] = [x + w / 2, y + h / 2];
|
|
86
|
+
const at = drawn.findLastIndex((item) => filled(item) && item.x <= u && u <= item.x + item.w && item.y <= v && v <= item.y + item.h);
|
|
87
|
+
const under = drawn[at];
|
|
88
|
+
if (at >= 0 && filled(under) && (luminance(under.fill) + 0.05) / 0.05 < 4.5)
|
|
89
|
+
drawn.splice(at + 1, 0, { x, y, w, h, fill: [1, 1, 1], edges: [null, null, null, null] });
|
|
90
|
+
}
|
|
91
|
+
return { ...list, drawn };
|
|
92
|
+
}
|
|
93
|
+
/** The relative luminance WCAG gives a colour in sRGB. */
|
|
94
|
+
function luminance(colour) {
|
|
95
|
+
const [r, g, b] = colour.map((c) => (c <= 0.04045 ? c / 12.92 : ((c + 0.055) / 1.055) ** 2.4));
|
|
96
|
+
return 0.2126 * r + 0.7152 * g + 0.0722 * b;
|
|
97
|
+
}
|
|
74
98
|
/** PDF's origin is bottom-left; every primitive arrives top-left. */
|
|
75
99
|
async function paint(page, pageNumber, list, font) {
|
|
76
100
|
const H = list.height;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@quillmark/quillify",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.5.0",
|
|
4
4
|
"description": "Turn a published PDF form (AcroForm or XFA) into a Quillmark quill, in the browser.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"license": "Apache-2.0",
|
|
@@ -48,7 +48,7 @@
|
|
|
48
48
|
},
|
|
49
49
|
"devDependencies": {
|
|
50
50
|
"@neslinesli93/qpdf-wasm": "0.3.0",
|
|
51
|
-
"@quillmark/wasm": "0.
|
|
51
|
+
"@quillmark/wasm": "0.126.0",
|
|
52
52
|
"@types/node": "24.19.0",
|
|
53
53
|
"@vitest/browser-playwright": "5.0.2",
|
|
54
54
|
"fast-check": "4.10.2",
|
|
@@ -63,7 +63,7 @@
|
|
|
63
63
|
"pdfjs-dist": "6.4.299"
|
|
64
64
|
},
|
|
65
65
|
"peerDependencies": {
|
|
66
|
-
"@quillmark/wasm": ">=0.
|
|
66
|
+
"@quillmark/wasm": ">=0.126.0"
|
|
67
67
|
},
|
|
68
68
|
"publishConfig": {
|
|
69
69
|
"access": "public"
|