@embedpdf/web 3.0.0-next.15 → 3.0.0-next.16

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.cjs CHANGED
@@ -105,6 +105,263 @@ function browserClock() {
105
105
  return clock;
106
106
  }
107
107
  //#endregion
108
+ //#region src/printer.ts
109
+ /** The attribute that marks the sheets printed from the page itself. */
110
+ const PRINT_ROOT = "data-epdf-print";
111
+ /** The browser's printer: it prints PDFs where the browser's own PDF viewer does. */
112
+ function browserPrinter() {
113
+ const printer = {
114
+ needsTap: printsOnlyFromATap(),
115
+ stage: stageSheets
116
+ };
117
+ if (!printsPdfFiles()) return printer;
118
+ let removePrinted = null;
119
+ return {
120
+ ...printer,
121
+ async stagePdf(pdf, { name, signal }) {
122
+ removePrinted?.();
123
+ removePrinted = null;
124
+ const frame = await loadPdfFrame(pdf, name, signal);
125
+ let printed = false;
126
+ return {
127
+ async print() {
128
+ frame.view.focus();
129
+ frame.view.print();
130
+ printed = true;
131
+ },
132
+ dispose() {
133
+ if (printed) removePrinted = frame.remove;
134
+ else frame.remove();
135
+ }
136
+ };
137
+ }
138
+ };
139
+ }
140
+ /** The sheets staged where this browser prints them: from the page on iOS, else a frame. */
141
+ async function stageSheets(sheets, { name, signal }) {
142
+ const urls = sheets.map((sheet) => URL.createObjectURL(sheet.image));
143
+ const revoke = () => urls.forEach((url) => URL.revokeObjectURL(url));
144
+ try {
145
+ return printsFramesAsPictures() ? await stageInPage(sheets, urls, name, revoke, signal) : await stageInFrame(sheets, urls, name, revoke, signal);
146
+ } catch (error) {
147
+ revoke();
148
+ throw error;
149
+ }
150
+ }
151
+ /** `pdf` loaded in a hidden frame of its own, where the browser's PDF viewer shows it. */
152
+ async function loadPdfFrame(pdf, name, signal) {
153
+ const url = URL.createObjectURL(new Blob([pdf], { type: "application/pdf" }));
154
+ const frame = hiddenFrame();
155
+ frame.title = name;
156
+ frame.src = url;
157
+ const remove = () => {
158
+ frame.remove();
159
+ URL.revokeObjectURL(url);
160
+ };
161
+ const loaded = untilEvent(frame, "load", signal);
162
+ document.body.appendChild(frame);
163
+ try {
164
+ await loaded;
165
+ const view = frame.contentWindow;
166
+ if (!view) throw new Error("the print frame did not load");
167
+ return {
168
+ view,
169
+ remove
170
+ };
171
+ } catch (error) {
172
+ remove();
173
+ throw error;
174
+ }
175
+ }
176
+ /**
177
+ * How the sheets print: one named `@page` per page size, and a class per size that puts a sheet
178
+ * on that page, its picture fitted inside. A browser that prints on other paper keeps each sheet
179
+ * on one page: a sheet is never taller or wider than the page it prints on. That cap is a
180
+ * percentage of the page (`html` and `body` are the page's height): Safari resolves `vw` and
181
+ * `vh` against the screen even when it prints, so on a phone they would shrink every sheet to
182
+ * the phone's width. `sizes` is each sheet's class.
183
+ */
184
+ function printSheetsStyle(sheets) {
185
+ const named = [];
186
+ const sizes = sheets.map((sheet) => {
187
+ const index = named.findIndex((other) => other.widthPt === sheet.widthPt && other.heightPt === sheet.heightPt);
188
+ return `epdf-sheet-${index >= 0 ? index : named.push(sheet) - 1}`;
189
+ });
190
+ return {
191
+ css: [
192
+ "@page { margin: 0; }",
193
+ ...named.flatMap(({ widthPt, heightPt }, index) => [`@page epdf-sheet-${index} { size: ${widthPt}pt ${heightPt}pt; margin: 0; }`, `.epdf-sheet-${index} { page: epdf-sheet-${index}; width: ${widthPt}pt; height: ${heightPt}pt; }`]),
194
+ ".epdf-print-sheet { break-after: page; break-inside: avoid; max-width: 100%; max-height: 100%; overflow: hidden; }",
195
+ ".epdf-print-sheet:last-child { break-after: auto; }",
196
+ ".epdf-print-sheet img { display: block; width: 100%; height: 100%; object-fit: contain; }"
197
+ ].join("\n"),
198
+ sizes
199
+ };
200
+ }
201
+ /** The sheets, made in `owner` (the frame's document or the page's): one per page, its picture inside. */
202
+ function sheetElements(owner, urls, sizes) {
203
+ return urls.map((url, index) => {
204
+ const sheet = owner.createElement("div");
205
+ sheet.className = `epdf-print-sheet ${sizes[index]}`;
206
+ const image = owner.createElement("img");
207
+ image.alt = "";
208
+ image.src = url;
209
+ sheet.append(image);
210
+ return sheet;
211
+ });
212
+ }
213
+ /** A frame's document before the sheets go in: nothing of the print job's own is in it. */
214
+ const FRAME_DOCUMENT = "<!doctype html><html><head><meta charset=\"utf-8\"></head><body></body></html>";
215
+ /** A frame laid out but out of sight: a frame that is not laid out may never load. */
216
+ function hiddenFrame() {
217
+ const frame = document.createElement("iframe");
218
+ frame.setAttribute("aria-hidden", "true");
219
+ frame.tabIndex = -1;
220
+ Object.assign(frame.style, {
221
+ position: "fixed",
222
+ right: "0",
223
+ bottom: "0",
224
+ width: "1px",
225
+ height: "1px",
226
+ border: "0",
227
+ opacity: "0",
228
+ pointerEvents: "none"
229
+ });
230
+ return frame;
231
+ }
232
+ /** The sheets in a hidden frame of their own, so nothing of the host page prints with them. */
233
+ async function stageInFrame(sheets, urls, name, revoke, signal) {
234
+ const { css, sizes } = printSheetsStyle(sheets);
235
+ const frame = hiddenFrame();
236
+ frame.srcdoc = FRAME_DOCUMENT;
237
+ const loaded = untilEvent(frame, "load", signal);
238
+ document.body.appendChild(frame);
239
+ try {
240
+ await loaded;
241
+ const view = frame.contentWindow;
242
+ const frameDocument = frame.contentDocument;
243
+ if (!view || !frameDocument) throw new Error("the print frame did not load");
244
+ frameDocument.title = name;
245
+ const style = frameDocument.createElement("style");
246
+ style.textContent = `html, body { margin: 0; padding: 0; height: 100%; }\n${css}`;
247
+ frameDocument.head.append(style);
248
+ frameDocument.body.append(...sheetElements(frameDocument, urls, sizes));
249
+ await decodeAll(Array.from(frameDocument.querySelectorAll("img")), signal);
250
+ return {
251
+ print: () => printFrom(view),
252
+ dispose: () => {
253
+ frame.remove();
254
+ revoke();
255
+ }
256
+ };
257
+ } catch (error) {
258
+ frame.remove();
259
+ throw error;
260
+ }
261
+ }
262
+ /**
263
+ * The sheets in the page itself, shown only when it prints, and everything else hidden then.
264
+ * While it prints, `html` and `body` are plain blocks the page's height, whatever the host page
265
+ * makes them (an app often keeps its body to the window, which would clip the sheets after the
266
+ * first). The page's title is the print job's name while it prints.
267
+ */
268
+ async function stageInPage(sheets, urls, name, revoke, signal) {
269
+ const { css, sizes } = printSheetsStyle(sheets);
270
+ const style = document.createElement("style");
271
+ style.textContent = `${css}\n@media screen { [${PRINT_ROOT}] { display: none !important; } }\n@media print { html, body { display: block !important; height: 100% !important; margin: 0 !important; padding: 0 !important; overflow: visible !important; } body > :not([${PRINT_ROOT}]) { display: none !important; } [${PRINT_ROOT}] { height: 100%; } }`;
272
+ const root = document.createElement("div");
273
+ root.setAttribute(PRINT_ROOT, "");
274
+ root.append(...sheetElements(document, urls, sizes));
275
+ document.head.appendChild(style);
276
+ document.body.appendChild(root);
277
+ const remove = () => {
278
+ root.remove();
279
+ style.remove();
280
+ };
281
+ try {
282
+ await decodeAll(Array.from(root.querySelectorAll("img")), signal);
283
+ } catch (error) {
284
+ remove();
285
+ throw error;
286
+ }
287
+ return {
288
+ async print() {
289
+ const title = document.title;
290
+ document.title = name;
291
+ try {
292
+ await printFrom(window);
293
+ } finally {
294
+ document.title = title;
295
+ }
296
+ },
297
+ dispose: () => {
298
+ remove();
299
+ revoke();
300
+ }
301
+ };
302
+ }
303
+ /** Open `view`'s print dialog; resolves when the browser says printing finished. */
304
+ function printFrom(view) {
305
+ return new Promise((resolve, reject) => {
306
+ view.addEventListener("afterprint", () => resolve(), { once: true });
307
+ try {
308
+ view.focus();
309
+ view.print();
310
+ } catch (error) {
311
+ reject(error);
312
+ }
313
+ });
314
+ }
315
+ /** Wait until every picture is decoded, so the dialog never shows an empty sheet. */
316
+ async function decodeAll(images, signal) {
317
+ await untilSettled(Promise.all(images.map((image) => image.decode())), signal);
318
+ }
319
+ function untilEvent(target, type, signal) {
320
+ return untilSettled(new Promise((resolve) => target.addEventListener(type, () => resolve(), { once: true })), signal);
321
+ }
322
+ /** `work`, or the signal's reason as soon as it aborts. */
323
+ function untilSettled(work, signal) {
324
+ if (!signal) return work;
325
+ if (signal.aborted) return Promise.reject(signal.reason);
326
+ return new Promise((resolve, reject) => {
327
+ const onAbort = () => reject(signal.reason);
328
+ signal.addEventListener("abort", onAbort, { once: true });
329
+ work.then((value) => {
330
+ signal.removeEventListener("abort", onAbort);
331
+ resolve(value);
332
+ }, (error) => {
333
+ signal.removeEventListener("abort", onAbort);
334
+ reject(error);
335
+ });
336
+ });
337
+ }
338
+ /**
339
+ * Whether this browser prints a PDF file from a frame: its own PDF viewer is on (Chrome set to
340
+ * download PDFs has none, and would download the copy instead), and it is not iOS, which reports
341
+ * a viewer but prints a framed PDF as a picture of its first page, or Android.
342
+ */
343
+ function printsPdfFiles() {
344
+ if (typeof navigator === "undefined" || navigator.pdfViewerEnabled !== true) return false;
345
+ return !printsFramesAsPictures() && !/Android/i.test(navigator.userAgent);
346
+ }
347
+ /**
348
+ * Whether this browser prints only while a tap or click is being handled: Safari's engine, whose
349
+ * vendor is Apple. On iOS a print that waited shows "This website has been blocked from
350
+ * automatically printing"; Safari on the Mac opens its print dialog, but with blank pages.
351
+ */
352
+ function printsOnlyFromATap() {
353
+ if (typeof navigator === "undefined") return false;
354
+ return navigator.vendor === "Apple Computer, Inc." || printsFramesAsPictures();
355
+ }
356
+ /**
357
+ * Whether this browser prints a frame as a picture of the page around it: every browser on
358
+ * iOS and iPadOS (all use Safari's engine; iPadOS presents itself as a Mac with touch).
359
+ */
360
+ function printsFramesAsPictures() {
361
+ if (typeof navigator === "undefined") return false;
362
+ return /iPad|iPhone|iPod/.test(navigator.userAgent) || navigator.userAgent.includes("Macintosh") && navigator.maxTouchPoints > 1;
363
+ }
364
+ //#endregion
108
365
  //#region src/css-text.ts
109
366
  /**
110
367
  * Style records as CSS text. This package hands styles out as records with
@@ -3213,8 +3470,54 @@ function chromeInPixels(nodes, page) {
3213
3470
  });
3214
3471
  }
3215
3472
  //#endregion
3473
+ //#region src/legible-text.ts
3474
+ /**
3475
+ * Editors laid out at a legible size and drawn at the page's. iOS Safari zooms the page in when
3476
+ * a field takes focus with text under 16 CSS pixels, to 16 over that size, and it reads the
3477
+ * field's own computed font size: a transform doesn't change that size (CSS `zoom` does). Text
3478
+ * on a page is drawn at the page's scale, so a form field or a text box on a phone often has
3479
+ * smaller text. Such an editor is laid out {@link legibleScaleOf} times bigger (its text, lines
3480
+ * and box alike), and a `scale()` from its top-left corner draws it back at the page's size: the
3481
+ * same picture, with text iOS reads as legible. Every other browser draws it the same way.
3482
+ */
3483
+ /** The smallest text, in CSS pixels, a field takes focus with on iOS without the page zooming in. */
3484
+ const LEGIBLE_FONT_PX = 16;
3485
+ /**
3486
+ * How many times bigger to lay out an editor whose text is `fontPx` CSS pixels, so its text is
3487
+ * at least {@link LEGIBLE_FONT_PX}: 1 when it already is.
3488
+ */
3489
+ function legibleScaleOf(fontPx) {
3490
+ return fontPx > 0 && fontPx < 16 ? 16 / fontPx : 1;
3491
+ }
3492
+ /**
3493
+ * The transform that draws an element laid out `legible` times bigger back at its size, from its
3494
+ * top-left corner (`transformOrigin: '0 0'`), or null when it is laid out at its size.
3495
+ */
3496
+ function legibleShrinkOf(legible) {
3497
+ return legible === 1 ? null : `scale(${1 / legible})`;
3498
+ }
3499
+ //#endregion
3216
3500
  //#region src/text-box-editor.ts
3217
3501
  /**
3502
+ * A free text box's editor on an element: the controller behind the
3503
+ * annotation layer's own text boxes and behind a renderer's element that
3504
+ * draws one itself. The shared rich-text binding ({@link attachRichTextEditor})
3505
+ * renders the runs as inline-styled spans, turns typing back into runs, maps
3506
+ * the selection to flat offsets and keeps the caret through a restyle; the
3507
+ * browser owns layout, the caret, input methods and the clipboard; the
3508
+ * annotation plugin owns the text, the range routing and the write after a
3509
+ * pause. This module adds what makes the element a text box of the layer:
3510
+ * focus and `contentEditable` follow the plugin's `editing`, never the other
3511
+ * way, and a press inside the box stays inside it. A framework binding keeps
3512
+ * one {@link createTextBoxEditorFollower} per element and hands it what
3513
+ * changed. {@link freeTextBoxOf} is the layer's own editor element and
3514
+ * {@link ownTextBoxOf} one a renderer draws itself, both laid out at a legible
3515
+ * size (`legible-text.ts`).
3516
+ *
3517
+ * The plugin's types are mirrored structurally, so this package stays free of
3518
+ * EmbedPDF imports.
3519
+ */
3520
+ /**
3218
3521
  * Make `element` the editor of a text box. Typing shows at once and reaches
3219
3522
  * the engine in one write after a pause (or when the edit ends), not one
3220
3523
  * write per keystroke.
@@ -3324,22 +3627,67 @@ function textBoxStyleOf(css, scale) {
3324
3627
  };
3325
3628
  }
3326
3629
  /**
3327
- * The editor element of a free text box in the page layer's pixels: the
3328
- * engine's text plate (`SetPlateRect` and its `re W n` clip), the box inset
3329
- * by its padding. Place it there with no CSS padding, so its edge is the
3330
- * plate's: CSS padding doesn't clip overflow, and scrolled lines would paint
3331
- * over the border band. The box never grows with its text.
3630
+ * The layer's own editor element for a free text box: the engine's text
3631
+ * plate (`SetPlateRect` and its `re W n` clip), the box inset by its padding,
3632
+ * with no CSS padding, so its edge is the plate's: CSS padding doesn't clip
3633
+ * overflow, and scrolled lines would paint over the border band. The box
3634
+ * never grows with its text. It is laid out at a legible size and drawn back
3635
+ * at the plate's (`legible-text.ts`), and a turned box turns about its
3636
+ * centre, as the baked appearance does. While someone types it takes the
3637
+ * pointer and shows an `outline` of that color.
3332
3638
  */
3333
- function textPlateInPixels(item, page) {
3639
+ function freeTextBoxOf(item, page, outline) {
3334
3640
  const box = rectInPixels(item.box, page);
3335
- const scale = item.box.width > 0 ? box.width / item.box.width : 1;
3336
- const inset = item.css.padding * scale;
3641
+ const onPage = item.box.width > 0 ? box.width / item.box.width : 1;
3642
+ const inset = item.css.padding * onPage;
3643
+ const width = Math.max(0, box.width - 2 * inset);
3644
+ const height = Math.max(0, box.height - 2 * inset);
3645
+ const legible = legibleScaleOf(item.css.fontSize * onPage);
3646
+ const scale = onPage * legible;
3647
+ const turn = item.rot ? `translate(${width / 2}px, ${height / 2}px) rotate(${item.rot}deg) translate(${-width / 2}px, ${-height / 2}px)` : null;
3648
+ return {
3649
+ scale,
3650
+ style: {
3651
+ position: "absolute",
3652
+ left: `${box.left + inset}px`,
3653
+ top: `${box.top + inset}px`,
3654
+ width: `${width * legible}px`,
3655
+ height: `${height * legible}px`,
3656
+ ...textBoxStyleOf(item.css, scale),
3657
+ boxSizing: "border-box",
3658
+ background: "transparent",
3659
+ whiteSpace: "pre-wrap",
3660
+ overflowWrap: "break-word",
3661
+ overflowX: "hidden",
3662
+ overflowY: item.editing ? "auto" : "hidden",
3663
+ outline: item.editing ? `${legible}px solid ${outline}` : "none",
3664
+ cursor: item.editing ? "text" : "default",
3665
+ transform: [turn, legibleShrinkOf(legible)].filter(Boolean).join(" ") || "none",
3666
+ transformOrigin: "0 0",
3667
+ pointerEvents: item.editing ? "auto" : "none"
3668
+ }
3669
+ };
3670
+ }
3671
+ /**
3672
+ * Your own element as the editor of a text box you draw yourself, at `scale`
3673
+ * pixels per point where it is ({@link textBoxEditorScaleOf}): the box's body
3674
+ * style, filling the element's parent, laid out at a legible size and drawn
3675
+ * back at the parent's (`legible-text.ts`). Draw the box's frame (its fill,
3676
+ * border and padding) on the parent: what's on the element scales with it.
3677
+ */
3678
+ function ownTextBoxOf(item, scale) {
3679
+ const legible = legibleScaleOf(item.css.fontSize * scale);
3337
3680
  return {
3338
- left: box.left + inset,
3339
- top: box.top + inset,
3340
- width: Math.max(0, box.width - 2 * inset),
3341
- height: Math.max(0, box.height - 2 * inset),
3342
- scale
3681
+ scale: scale * legible,
3682
+ style: {
3683
+ ...textBoxStyleOf(item.css, scale * legible),
3684
+ width: `${100 * legible}%`,
3685
+ height: `${100 * legible}%`,
3686
+ boxSizing: "border-box",
3687
+ transform: legibleShrinkOf(legible) ?? "none",
3688
+ transformOrigin: "0 0",
3689
+ ...item.editing ? { pointerEvents: "auto" } : {}
3690
+ }
3343
3691
  };
3344
3692
  }
3345
3693
  //#endregion
@@ -3626,19 +3974,25 @@ function cssFontOf(pdfFont) {
3626
3974
  /** Pixels per page point where the widget is drawn. */
3627
3975
  const scaleOf = (field, frame) => field.box.width > 0 ? frame.width / field.box.width : 1;
3628
3976
  /**
3629
- * A text field's editor style. A font size of 0 means "fit the box" in PDF:
3630
- * the box height for one line, Acrobat's 12 pt for several.
3977
+ * The size of a text field's text in pixels at `frame`. A font size of 0 means "fit the box" in
3978
+ * PDF: the box height for one line, Acrobat's 12 pt for several.
3631
3979
  */
3632
- function textFieldStyleOf(field, frame, colors) {
3980
+ function textFieldFontPxOf(field, frame) {
3633
3981
  const scale = scaleOf(field, frame);
3634
- const fontSize = field.look.fontSize ? field.look.fontSize * scale : field.multiline ? 12 * scale : Math.max(6, frame.height * .72);
3982
+ return field.look.fontSize ? field.look.fontSize * scale : field.multiline ? 12 * scale : Math.max(6, frame.height * .72);
3983
+ }
3984
+ /**
3985
+ * A text field's editor style at `frame`, with its lengths `legible` times bigger for an editor
3986
+ * laid out at a legible size (`legible-text.ts`).
3987
+ */
3988
+ function textFieldStyleOf(field, frame, colors, legible = 1) {
3635
3989
  return {
3636
3990
  ...cssFontOf(field.look.fontFamily),
3637
- fontSize: `${fontSize}px`,
3991
+ fontSize: `${textFieldFontPxOf(field, frame) * legible}px`,
3638
3992
  textAlign: field.look.textAlign,
3639
3993
  color: field.look.color ?? colors.text,
3640
3994
  background: field.look.background ?? colors.background,
3641
- ...field.comb && field.maxLength ? { letterSpacing: `${frame.width / field.maxLength / 2}px` } : {}
3995
+ ...field.comb && field.maxLength ? { letterSpacing: `${frame.width / field.maxLength / 2 * legible}px` } : {}
3642
3996
  };
3643
3997
  }
3644
3998
  /** A list box's style: the field's own look, the settings where it has none (12 pt for a fitted size). */
@@ -3858,6 +4212,9 @@ function widgetBoxStyleOf(widget, frame, colors, { edge = true } = {}) {
3858
4212
  * control would swallow clicks, and the box below runs the widget's action
3859
4213
  * (the read-only "button" pattern), so a disabled one lets them through.
3860
4214
  *
4215
+ * It fills the widget's box, laid out at a legible size and drawn back at the
4216
+ * box's (`legible-text.ts`), so iOS doesn't zoom the page in when it takes focus.
4217
+ *
3861
4218
  * With the engine's `layout` of the text being typed, the editor writes it
3862
4219
  * where the engine will draw it: in the engine's face (mounted), at the size
3863
4220
  * it draws (a fitted size resolved), in the box inside the border, a line of
@@ -3865,14 +4222,24 @@ function widgetBoxStyleOf(widget, frame, colors, { edge = true } = {}) {
3865
4222
  */
3866
4223
  function textFieldEditorStyleOf(field, frame, colors, focused, layout = null) {
3867
4224
  const scale = scaleOf(field, frame);
3868
- const pixels = (points) => `${points * scale}px`;
4225
+ const legible = legibleScaleOf(layout ? layout.fontSize * scale : textFieldFontPxOf(field, frame));
4226
+ const px = (pixels) => `${pixels * legible}px`;
4227
+ const pixels = (points) => px(points * scale);
3869
4228
  return {
3870
- ...FORM_CONTROL_FILL,
4229
+ position: "absolute",
4230
+ left: "0",
4231
+ top: "0",
4232
+ width: `${100 * legible}%`,
4233
+ height: `${100 * legible}%`,
4234
+ boxSizing: "border-box",
4235
+ margin: "0",
4236
+ transform: legibleShrinkOf(legible) ?? "none",
4237
+ transformOrigin: "0 0",
3871
4238
  border: "none",
3872
- outline: focused ? `2px solid ${colors.focus}` : "none",
3873
- outlineOffset: "-2px",
3874
- padding: "0 2px",
3875
- ...textFieldStyleOf(field, frame, colors),
4239
+ outline: focused ? `${px(2)} solid ${colors.focus}` : "none",
4240
+ outlineOffset: px(-2),
4241
+ padding: `0 ${px(2)}`,
4242
+ ...textFieldStyleOf(field, frame, colors, legible),
3876
4243
  ...layout ? {
3877
4244
  fontFamily: layout.fontFamily,
3878
4245
  fontWeight: layout.fontWeight,
@@ -5086,18 +5453,19 @@ function createSurfaceFocusFollower() {
5086
5453
  //#region src/standard-commands-platform.ts
5087
5454
  /**
5088
5455
  * The browser's part of the standard commands (`createStandardCommands` in
5089
- * `@embedpdf/plugin-commands/standard`): copying the selected text, handing
5090
- * the document to the user as a download, and the print dialog. Every
5091
- * framework adapter builds its `standardCommands` from this one object.
5456
+ * `@embedpdf/plugin-commands/standard`): copying the selected text, and
5457
+ * handing the document to the user as a download. Every framework adapter
5458
+ * builds its `standardCommands` from this one object. Printing is the
5459
+ * kernel's, with `browserPrinter()`.
5092
5460
  */
5093
5461
  const standardCommandsBrowser = {
5094
5462
  copySelection,
5095
- saveFile: (bytes, fileName) => saveFile(bytes, fileName, "application/pdf"),
5096
- print: () => window.print()
5463
+ saveFile: (bytes, fileName) => saveFile(bytes, fileName, "application/pdf")
5097
5464
  };
5098
5465
  //#endregion
5099
5466
  exports.EPDF_VARIABLES = EPDF_VARIABLES;
5100
5467
  exports.FORM_CONTROL_FILL = FORM_CONTROL_FILL;
5468
+ exports.LEGIBLE_FONT_PX = LEGIBLE_FONT_PX;
5101
5469
  exports.TOOLBAR_ITEM_ATTRIBUTE = TOOLBAR_ITEM_ATTRIBUTE;
5102
5470
  exports.anchoredViewOf = anchoredViewOf;
5103
5471
  exports.annotationChromePaint = annotationChromePaint;
@@ -5116,6 +5484,7 @@ exports.bindCommandShortcuts = bindCommandShortcuts;
5116
5484
  exports.bindPaintedImage = bindPaintedImage;
5117
5485
  exports.bindWidgetEvents = bindWidgetEvents;
5118
5486
  exports.browserClock = browserClock;
5487
+ exports.browserPrinter = browserPrinter;
5119
5488
  exports.canTakeFocus = canTakeFocus;
5120
5489
  exports.chromeInPixels = chromeInPixels;
5121
5490
  exports.clientPageProjector = clientPageProjector;
@@ -5149,6 +5518,7 @@ exports.fontProgramsOf = fontProgramsOf;
5149
5518
  exports.formColorsOf = formColorsOf;
5150
5519
  exports.formFocusRingStyleOf = formFocusRingStyleOf;
5151
5520
  exports.frameInPixels = frameInPixels;
5521
+ exports.freeTextBoxOf = freeTextBoxOf;
5152
5522
  exports.ghostOpacity = ghostOpacity;
5153
5523
  exports.hoverLink = hoverLink;
5154
5524
  exports.indexedDbByteStore = indexedDbByteStore;
@@ -5161,6 +5531,8 @@ exports.isUprightQuad = isUprightQuad;
5161
5531
  exports.isolatePointerDown = isolatePointerDown;
5162
5532
  exports.isolateWheel = isolateWheel;
5163
5533
  exports.layerTextBoxesOf = layerTextBoxesOf;
5534
+ exports.legibleScaleOf = legibleScaleOf;
5535
+ exports.legibleShrinkOf = legibleShrinkOf;
5164
5536
  exports.lineModelFor = lineModelFor;
5165
5537
  exports.linkActivateContextOf = linkActivateContextOf;
5166
5538
  exports.linkAnchorOf = linkAnchorOf;
@@ -5191,6 +5563,7 @@ exports.observeOutsidePress = observeOutsidePress;
5191
5563
  exports.observeWidth = observeWidth;
5192
5564
  exports.offsetOfPosition = offsetOfPosition;
5193
5565
  exports.openExternalUri = openExternalUri;
5566
+ exports.ownTextBoxOf = ownTextBoxOf;
5194
5567
  exports.pageClientSpace = pageClientSpace;
5195
5568
  exports.pageLayersOf = pageLayersOf;
5196
5569
  exports.pageSurfaceLayout = pageSurfaceLayout;
@@ -5240,10 +5613,9 @@ exports.svgPoints = svgPoints;
5240
5613
  exports.svgShapesOf = svgShapesOf;
5241
5614
  exports.tabbablesIn = tabbablesIn;
5242
5615
  exports.textBoxEditorScaleOf = textBoxEditorScaleOf;
5243
- exports.textBoxStyleOf = textBoxStyleOf;
5244
5616
  exports.textFieldEditorStyleOf = textFieldEditorStyleOf;
5617
+ exports.textFieldFontPxOf = textFieldFontPxOf;
5245
5618
  exports.textFieldStyleOf = textFieldStyleOf;
5246
- exports.textPlateInPixels = textPlateInPixels;
5247
5619
  exports.toolCursorsOf = toolCursorsOf;
5248
5620
  exports.toolbarMeasureKey = toolbarMeasureKey;
5249
5621
  exports.toolbarStopsOf = toolbarStopsOf;