@quillmark/quillify 0.3.1 → 0.4.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/NOTICE +1 -1
- package/dist/api.d.ts +1 -0
- package/dist/assemble.d.ts +1 -1
- package/dist/assemble.js +2 -13
- package/dist/display-list.js +10 -4
- package/dist/naming.js +2 -0
- package/dist/quillify.js +2 -2
- package/dist/types.d.ts +2 -3
- package/dist/xfa.js +2 -1
- package/package.json +2 -2
package/NOTICE
CHANGED
package/dist/api.d.ts
CHANGED
package/dist/assemble.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import type { InventoryWidget, QualifiedField, QualifiedForm, QuillTree } from './types.js';
|
|
2
|
-
/** What the assembly reads of a field, of the form and of a widget. Its tests hand it a title, which it does not read. */
|
|
2
|
+
/** What the assembly reads of a field, of the form and of a widget. Its tests hand it a quill title, which it does not read. */
|
|
3
3
|
type Named = Omit<QualifiedField, 'label'>;
|
|
4
4
|
type Qualified = {
|
|
5
5
|
quill: Pick<QualifiedForm['quill'], 'name' | 'description'> & {
|
package/dist/assemble.js
CHANGED
|
@@ -6,23 +6,15 @@ export const QUILL_VERSION = '0.1.0';
|
|
|
6
6
|
* (U+007F to U+009F, U+FFFE and U+FFFF) are written as `\u` escapes.
|
|
7
7
|
*/
|
|
8
8
|
const q = (s) => JSON.stringify(s.toWellFormed()).replace(/[\u007f-\u009f\ufffe\uffff]/g, (c) => `\\u${c.charCodeAt(0).toString(16).padStart(4, '0')}`);
|
|
9
|
-
/** A group id is snake_case; a radio group's PDF name is whatever its author typed. */
|
|
10
|
-
function groupId(name) {
|
|
11
|
-
const id = name
|
|
12
|
-
.toLowerCase()
|
|
13
|
-
.replace(/[^a-z0-9]+/g, '_')
|
|
14
|
-
.replace(/^_+|_+$/g, '');
|
|
15
|
-
return /^[a-z]/.test(id) ? id : `group_${id}`;
|
|
16
|
-
}
|
|
17
9
|
/**
|
|
18
10
|
* A `default:` says nobody need answer the field. A required field declares
|
|
19
11
|
* none, a boolean apart: it defaults to false either way.
|
|
20
12
|
*/
|
|
21
13
|
function fieldYaml(field) {
|
|
22
14
|
const lines = [` ${q(field.name)}:`];
|
|
15
|
+
if (field.title !== undefined)
|
|
16
|
+
lines.push(` title: ${q(field.title)}`);
|
|
23
17
|
const ui = [];
|
|
24
|
-
if (field.group)
|
|
25
|
-
ui.push(` group: ${q(groupId(field.group))}`);
|
|
26
18
|
switch (field.type) {
|
|
27
19
|
case 'string':
|
|
28
20
|
lines.push(' type: string');
|
|
@@ -74,9 +66,6 @@ export function quillYaml(form, fields) {
|
|
|
74
66
|
' body:',
|
|
75
67
|
' enabled: false'
|
|
76
68
|
];
|
|
77
|
-
const groups = [...new Set(fields.flatMap((f) => (f.group ? [groupId(f.group)] : [])))];
|
|
78
|
-
if (groups.length > 0)
|
|
79
|
-
lines.push(' ui:', ` groups: [${groups.map(q).join(', ')}]`);
|
|
80
69
|
if (fields.length > 0) {
|
|
81
70
|
lines.push(' fields:');
|
|
82
71
|
for (const field of fields)
|
package/dist/display-list.js
CHANGED
|
@@ -274,12 +274,13 @@ function svgOnly(tag) {
|
|
|
274
274
|
* One run per visual line, with the box the browser measured it into. Splitting
|
|
275
275
|
* where the rect top changes is what keeps a wrapped paragraph's second line at
|
|
276
276
|
* its own baseline. A tab ends a run too and is in none: the text after it
|
|
277
|
-
* starts a run at the box the layout moved it to.
|
|
277
|
+
* starts a run at the box the layout moved it to. A soft hyphen the layout
|
|
278
|
+
* breaks a line at is a hyphen in the run it ends, which is measured with it.
|
|
278
279
|
*/
|
|
279
280
|
function collectText(node, X, Y, drawn, turns) {
|
|
280
|
-
|
|
281
|
+
let text = node.nodeValue ?? '';
|
|
281
282
|
const parent = node.parentElement;
|
|
282
|
-
if (!text
|
|
283
|
+
if (!text.trim() || !parent)
|
|
283
284
|
return;
|
|
284
285
|
// A <select> is de-chromed above; only the option the form shows is drawn.
|
|
285
286
|
if (parent.tagName === 'OPTION' && !parent.selected)
|
|
@@ -347,10 +348,15 @@ function collectText(node, X, Y, drawn, turns) {
|
|
|
347
348
|
}
|
|
348
349
|
range.setStart(node, i);
|
|
349
350
|
range.setEnd(node, i + 1);
|
|
350
|
-
|
|
351
|
+
// At a break the soft hyphen has an empty rect and then the box of the
|
|
352
|
+
// hyphen shown, and the letter after it has that box and then its own.
|
|
353
|
+
const rects = range.getClientRects();
|
|
354
|
+
const rect = rects[rects.length - 1];
|
|
351
355
|
if (!rect)
|
|
352
356
|
continue;
|
|
353
357
|
const glyph = upright(rect);
|
|
358
|
+
if (text[i] === '\u00ad' && glyph.right > glyph.x)
|
|
359
|
+
text = `${text.slice(0, i)}-${text.slice(i + 1)}`;
|
|
354
360
|
if (top === null || Math.abs(glyph.top - top) < 0.6) {
|
|
355
361
|
top ??= glyph.top;
|
|
356
362
|
box = box
|
package/dist/naming.js
CHANGED
|
@@ -74,6 +74,8 @@ function shaped({ name, description, fields }) {
|
|
|
74
74
|
not(`${slot}.name`, 'a string');
|
|
75
75
|
if (typeof entry.description !== 'string')
|
|
76
76
|
not(`${slot}.description`, 'a string');
|
|
77
|
+
if (entry.title !== undefined && typeof entry.title !== 'string')
|
|
78
|
+
not(`${slot}.title`, 'a string');
|
|
77
79
|
if (entry.example !== undefined && typeof entry.example !== 'string')
|
|
78
80
|
not(`${slot}.example`, 'a string');
|
|
79
81
|
if (entry.text !== undefined && !TEXT.has(entry.text))
|
package/dist/quillify.js
CHANGED
|
@@ -50,12 +50,12 @@ export async function quillify(form, naming) {
|
|
|
50
50
|
return {
|
|
51
51
|
widget: entry.id,
|
|
52
52
|
name: entry.name,
|
|
53
|
+
...(entry.title === undefined ? {} : { title: entry.title }),
|
|
53
54
|
description: entry.description,
|
|
54
55
|
...typed(field, entry.text),
|
|
55
56
|
...(example === undefined ? {} : { example }),
|
|
56
57
|
keep: entry.keep && !field.readOnly,
|
|
57
|
-
required: field.required
|
|
58
|
-
...(field.kind === 'radio' ? { group: field.group } : {})
|
|
58
|
+
required: field.required
|
|
59
59
|
};
|
|
60
60
|
})
|
|
61
61
|
}, {
|
package/dist/types.d.ts
CHANGED
|
@@ -62,6 +62,7 @@ export interface QualifiedField {
|
|
|
62
62
|
/** snake_case schema field name */
|
|
63
63
|
name: string;
|
|
64
64
|
label: string;
|
|
65
|
+
title?: string;
|
|
65
66
|
description: string;
|
|
66
67
|
type: FieldType;
|
|
67
68
|
multiline: boolean;
|
|
@@ -70,8 +71,6 @@ export interface QualifiedField {
|
|
|
70
71
|
keep: boolean;
|
|
71
72
|
/** `/Ff` bit 2: the form says this one has to be filled. */
|
|
72
73
|
required: boolean;
|
|
73
|
-
/** The radio group this option belongs to, when it is one. */
|
|
74
|
-
group?: string;
|
|
75
74
|
}
|
|
76
75
|
export interface QualifiedForm {
|
|
77
76
|
quill: {
|
|
@@ -82,7 +81,7 @@ export interface QualifiedForm {
|
|
|
82
81
|
fields: QualifiedField[];
|
|
83
82
|
}
|
|
84
83
|
export declare const SNAKE_CASE: RegExp;
|
|
85
|
-
/** A quill as files, the shape `Quill.fromTree`
|
|
84
|
+
/** A quill as files, the shape `Quill.fromTree` takes. */
|
|
86
85
|
export type QuillTree = Map<string, Uint8Array>;
|
|
87
86
|
/** The largest file `sanitize` opens. It bounds the compressed input. */
|
|
88
87
|
export declare const MAX_BYTES: number;
|
package/dist/xfa.js
CHANGED
|
@@ -184,7 +184,8 @@ async function drawRun(page, pageNumber, run, H, get) {
|
|
|
184
184
|
// The standard-14 text fonts encode WinAnsi alone. U+2010 and U+2011 are
|
|
185
185
|
// drawn as its hyphen, and any other character outside it in the first of
|
|
186
186
|
// SYMBOLS that holds it. A soft hyphen is left out: WinAnsi has it as a
|
|
187
|
-
// hyphen, and the browser lays it out at no width and shows nothing.
|
|
187
|
+
// hyphen, and the browser lays it out at no width and shows nothing. The
|
|
188
|
+
// one it breaks a line at is a hyphen in the run already.
|
|
188
189
|
// Dropping the run would lose a line of the form without anything
|
|
189
190
|
// reporting it.
|
|
190
191
|
// pdf-lib kerns the width it measures and writes no kerning, so the width
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@quillmark/quillify",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.4.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.125.0",
|
|
52
52
|
"@types/node": "24.19.0",
|
|
53
53
|
"@vitest/browser-playwright": "5.0.2",
|
|
54
54
|
"fast-check": "4.10.2",
|