@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 CHANGED
@@ -1,5 +1,5 @@
1
1
  quillify
2
- Copyright 2026 Nibs
2
+ Copyright 2026 Enrique Oti
3
3
 
4
4
  quillify redistributes eight files that are other projects' work. Everything
5
5
  else it depends on is installed by its consumer and is not in the package.
package/dist/api.d.ts CHANGED
@@ -41,6 +41,7 @@ export interface FieldNaming {
41
41
  /** A read-only field is left out whatever this says. */
42
42
  keep: boolean;
43
43
  name: string;
44
+ title?: string;
44
45
  description: string;
45
46
  example?: string;
46
47
  text?: 'line' | 'paragraph' | 'lines';
@@ -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)
@@ -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
- const text = node.nodeValue;
281
+ let text = node.nodeValue ?? '';
281
282
  const parent = node.parentElement;
282
- if (!text || !text.trim() || !parent)
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
- const rect = range.getClientRects()[0];
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` and a quiver both take. */
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.1",
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.124.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",