@inkbar/core 0.6.0 → 0.8.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/README.md CHANGED
@@ -185,20 +185,22 @@ Most creation tools are one-shot: `shape`, `freetext`, `note`, `redact-area` and
185
185
 
186
186
  A host tool says which it is with `kind: 'one-shot' | 'sticky'` (no `kind` is sticky). A one-shot tool adds and selects what it makes, then calls `ctx.made(ids)`; `ctx.done()` clears the selection and switches to `select-text` itself. Under a sticky tool, `ctx.made` does nothing. The controller's `active` readable reports every switch, including one a one-shot tool makes.
187
187
 
188
- | Where focus is | Key | Effect |
189
- | ---------------------------- | ----------------------------------- | --------------------------------------------------------------------------------------------------- |
190
- | Viewport, `highlight` tool | `Enter` | Turns the text selection into a highlight |
191
- | Viewport, `redact-area` tool | `Enter` | Turns the text selection into text redactions |
192
- | Viewport, `note` tool | `Enter` | Places a note at the centre of the current page and focuses it |
193
- | Viewport, `signature` tool | `Enter` | Places the chosen signature at the centre of the current page |
194
- | Viewport | `+` or `=`, `-` | Zoom in, zoom out |
195
- | Viewport | `Home`, `End`, `PageUp`, `PageDown` | First, last, previous, next page |
196
- | Annotation | `Enter` or `Space` | Selects and activates it |
197
- | Annotation | `ArrowUp`, `ArrowDown` | Previous or next annotation on the page, in reading order |
198
- | Annotation | `Shift` + arrow | Moves it 1 pt (`Shift` + `Alt` + arrow: 10 pt) |
199
- | Annotation | `Ctrl` or `Cmd` + `Shift` + arrow | Resizes an area redaction, text box or shape by 1 pt. Right and Down grow it, Left and Up shrink it |
200
- | Annotation | `Delete` or `Backspace` | Removes it |
201
- | Annotation | `Escape` | Clears the selection |
188
+ | Where focus is | Key | Effect |
189
+ | ---------------------------- | --------------------------------------------- | --------------------------------------------------------------------------------------------------- |
190
+ | Viewport, `highlight` tool | `Enter` | Turns the text selection into a highlight |
191
+ | Viewport, `redact-area` tool | `Enter` | Turns the text selection into text redactions |
192
+ | Viewport, `note` tool | `Enter` | Places a note at the centre of the current page and focuses it |
193
+ | Viewport, `signature` tool | `Enter` | Places the chosen signature at the centre of the current page |
194
+ | Viewport | `+` or `=`, `-` | Zoom in, zoom out |
195
+ | Viewport | `Home`, `End`, `PageUp`, `PageDown` | First, last, previous, next page |
196
+ | Viewer root, toolbars too | `Ctrl` or `Cmd` + `Z` | Undo |
197
+ | Viewer root, toolbars too | `Ctrl` or `Cmd` + `Shift` + `Z`, `Ctrl` + `Y` | Redo |
198
+ | Annotation | `Enter` or `Space` | Selects and activates it |
199
+ | Annotation | `ArrowUp`, `ArrowDown` | Previous or next annotation on the page, in reading order |
200
+ | Annotation | `Shift` + arrow | Moves it 1 pt (`Shift` + `Alt` + arrow: 10 pt) |
201
+ | Annotation | `Ctrl` or `Cmd` + `Shift` + arrow | Resizes an area redaction, text box or shape by 1 pt. Right and Down grow it, Left and Up shrink it |
202
+ | Annotation | `Delete` or `Backspace` | Removes it |
203
+ | Annotation | `Escape` | Clears the selection |
202
204
 
203
205
  The tool controller and the overlay handle the tool and annotation keys. The zoom and page keys (`+`, `=`, `-`, `Home`, `End`, `PageUp`, `PageDown`) come from `attachViewportKeys`, which the tool controller does not install. Attach it to the same container:
204
206
 
@@ -212,6 +214,16 @@ const detach = attachViewportKeys(container, {
212
214
  // later: detach()
213
215
  ```
214
216
 
217
+ `createViewer` and each binding's `InkRoot` put the undo and redo keys on the root element, so they work from the toolbars inside it as well as from the viewport. A key does what the Undo or Redo button does, and acts only while that button is enabled. `Ctrl` and `Cmd` both work on every platform, and so does `Ctrl` + `Y`. In a text field or editable content the keys stay with the element, so a field keeps its own text undo. In a `<select>` they are left to the browser, which does nothing with them. With `mountViewer` or `createToolController`, attach them yourself:
218
+
219
+ ```ts
220
+ import { attachHistoryKeys } from '@inkbar/core/dom'
221
+
222
+ const detach = attachHistoryKeys(root, () => doc.history)
223
+ ```
224
+
225
+ An Undo or Redo button that undoes or redoes the last step disables itself, and the browser would move focus to `<body>`, out of reach of the keys. `createUndo`, `createRedo` and the binding buttons move it to the scroll element instead. For your own buttons, mark them `data-ink-undo` or `data-ink-redo` and call `keepHistoryFocus(root, () => scrollElement)`.
226
+
215
227
  On a focused annotation, `Enter` or `Space` selects it and calls `onActivate`; what activation opens is up to the host.
216
228
 
217
229
  Locked annotations, such as those embedded in the file, ignore move, resize and delete. Without a text selection, `Enter` in the `highlight` and `redact-area` tools does nothing.
@@ -283,7 +295,7 @@ Marks redraw when their records change, undo and redo included, and keep their e
283
295
 
284
296
  `suggestions.annotates('redact', { from, onRemoved })` links records to the annotations made from them. Its `add(record)` adds an annotation from `from(record)`, stores the link at `custom.<plugin>.<records name>` (here `custom.review.suggestions`) and fills the type's default colour when `from` gives none. `of(record)` returns the linked annotations. `onRemoved(record, annotation)` and `onChanged(record, annotation)` run inside the user's undo step, never on undo or redo. Call it in `setup`. The link travels with the annotation through a Gloss export and import.
285
297
 
286
- `quadsFromRect(rect)` and `rectFromQuads(quads)` from `@inkbar/core` convert between a box and the quads that text markup takes, and `doc.pageText(page)` resolves to a page's text joined with single spaces.
298
+ `quadsFromRect(rect)` and `rectFromQuads(quads)` from `@inkbar/core` convert between a box and the quads that text markup takes, and `doc.pageText(page)` resolves to a page's text joined the way the page reads: nothing between touching items, a space at line ends and at wide gaps.
287
299
 
288
300
  ### Going further
289
301
 
@@ -300,6 +312,60 @@ A command added with `ctx.commands.add` is disabled until a document is ready, u
300
312
  - Decorations from `ctx.decorate` re-apply when the annotations change. Call `ctx.redecorate()` when one reads state of your own that changed. Marks from `plugin.marks` take `refresh()` for the same job.
301
313
  - The framework packages run `setup` once the viewer has mounted in the browser, never while rendering on the server. Until then the plugin's commands are disabled, and anything `setup` creates, such as a store, doesn't exist yet.
302
314
 
315
+ ## Search
316
+
317
+ `doc.search(query, options?)` finds text across the document. The query is a string or a `RegExp`. It returns `SearchResults`: iterate it with `for await` to get hits as pages are scanned, or call `all()` for the lot.
318
+
319
+ ```ts
320
+ for await (const hit of doc.search('invoice')) {
321
+ console.log(hit.page, hit.text, `${hit.before}[${hit.text}]${hit.after}`)
322
+ }
323
+
324
+ const refs = await doc.search(/\b[A-Z]{2}\d{6}[A-D]\b/, { signal }).all()
325
+ ```
326
+
327
+ Options: `matchCase` (default false; a `RegExp` keeps its own `i` flag), `wholeWord` (a hit must not have a letter, digit or underscore on either side), `pages` (indices to search, in the order given; all pages in order by default) and `signal`, which stops the scan. A string matches the way the page reads: it finds text across line breaks and a hyphenated line end, and ignores accents, ligatures and curly quotes.
328
+
329
+ Each `SearchHit` has:
330
+
331
+ - `page`: 0-based page index.
332
+ - `start`, `end`: offsets into `doc.pageText(page)`.
333
+ - `text`: the page text the hit covers.
334
+ - `quads`, `rect`: where it is, in PDF user space. `quads` has one entry per run of text the hit touches, and can go straight into a text markup annotation. Each quad is cut where the page draws its glyphs and spans the font's full height, so it covers ascenders and descenders.
335
+ - `exact`: `true` when every quad comes from where the page draws its glyphs. `false` when some of the text could not be matched to them (right-to-left or vertical text, or an unusual file) and its quad is cut at estimated widths, which can fall short. Pad those before using them to cover text, as a redaction does.
336
+ - `before`, `after`: up to 40 characters of page text either side.
337
+
338
+ `viewer.search` is the state and actions behind the viewer's find bar: `state` (a readable of `open`, `query`, `matchCase`, `hits`, `current` and `scanning`), `open(query?)`, `setQuery`, `setMatchCase`, `next`, `previous` and `close`. `open()` runs the search and draws the highlights even with no find bar on the page. Plugins get the same object as `ctx.search`.
339
+
340
+ ```ts
341
+ import { createFindBar, createSearchButton } from '@inkbar/core/dom'
342
+
343
+ toolbar.append(createSearchButton(viewer))
344
+ viewer.element.querySelector('[data-ink-viewport-root]')!.append(createFindBar(viewer))
345
+ ```
346
+
347
+ `createSearchButton(viewer)` opens the bar. `createFindBar(viewer)` is a field with a match count, previous and next, a match-case toggle and close. Put it inside `[data-ink-viewport-root]`; the theme floats it over the top-right of the pages. Enter steps to the next match, `Shift+Enter` to the previous, and `Escape` closes and returns focus to where it was.
348
+
349
+ Ctrl/Cmd+F opens the bar when one is mounted and the key comes from inside the viewer, with the page selection (up to 200 characters) as the query. Without a bar, or from outside the viewer, the browser keeps the key.
350
+
351
+ Hits are drawn as a layer over the page that multiplies with it, so the text shows through. `@inkbar/theme` styles them with `--ink-search-hit` and, for the current hit, `--ink-search-current`.
352
+
353
+ ## Forms
354
+
355
+ Inkbar fills the AcroForm fields in a PDF and runs the form's scripts. `doc.form` reads and sets the values:
356
+
357
+ ```ts
358
+ doc.form.values.get() // { name: 'Ada', agree: 'Yes', ... }
359
+ doc.form.set({ name: 'Grace' }) // runs the scripts; one undo step
360
+ doc.form.reset() // every field back to the PDF's default
361
+ ```
362
+
363
+ Pass `fields: { mode: 'controlled', initial, onChange }` to `openDocument` or `createViewer` to hold the values in your own state. The form never submits, opens a link, sends mail or prints. It hands each request to you: through `onFormAction` on `createViewer` or `mountViewer`, or through `doc.form.on('action', fn)` on a document you opened with `openDocument`.
364
+
365
+ Each fillable field has a light tint. `formHighlight: false` turns it off when the viewer opens. To change it later, call `viewer.setFormHighlight(on)` on a `createViewer` viewer, or `engine.setFormHighlight(on)` on a `mountViewer` engine. Neither renders a page again, and a focused field keeps its focus and typed text.
366
+
367
+ The [forms guide](https://inkbar.dev/docs/guides/forms/) covers controlled fields, form actions, scripts, signature fields and saving values as Gloss.
368
+
303
369
  ## Signatures
304
370
 
305
371
  A signature here is a mark on the page, not a cryptographic signature of the file. Draw one, type your name in a handwriting font, or upload a scan, and it is saved as a template. The `signature` tool places the chosen template: a click puts it about 150 pt wide, centred on the press, and `Enter` puts it at the centre of the current page. It is turned against the page's `/Rotate`, so it reads upright on the page; a view rotation turns it with the page. A drawn signature becomes `ink` and the others a `stamp` (an image), both with `subject: 'Signature'`. Both keep their aspect ratio when resized.