@embedpdf/core 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
@@ -1,5 +1,5 @@
1
1
  Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
2
- const require_settings = require("./settings-maKtf237.cjs");
2
+ const require_settings = require("./settings-BS97q3tq.cjs");
3
3
  let _embedpdf_engine_core_runtime = require("@embedpdf/engine-core/runtime");
4
4
  //#region src/order.ts
5
5
  /**
@@ -97,7 +97,8 @@ function layoutFromEvent(event) {
97
97
  case "pages.reordered":
98
98
  case "pages.rotated":
99
99
  case "pages.deleted":
100
- case "pages.inserted": return event.layout;
100
+ case "pages.inserted":
101
+ case "pages.restored": return event.layout;
101
102
  default: return null;
102
103
  }
103
104
  }
@@ -147,6 +148,16 @@ async function fetchUrlSource(source, signal) {
147
148
  }
148
149
  const isUrlSource = (source) => source.kind === "url";
149
150
  const noLayer = () => new require_settings.PluginError("unsupported", "documents", "only the local engine gives a layer to download");
151
+ /** How a print comes out at each tier, one object each, so readers can compare them with `Object.is`. */
152
+ const PRINT_STANDARD = Object.freeze({
153
+ kind: "images",
154
+ dpi: 150
155
+ });
156
+ const PRINT_HIGH = Object.freeze({
157
+ kind: "images",
158
+ dpi: 300
159
+ });
160
+ const PRINT_PDF = Object.freeze({ kind: "pdf" });
150
161
  /**
151
162
  * The documents capability as seen from inside one document's scope: every call whose document
152
163
  * is optional uses that document instead of the active one. The rest is the registry itself.
@@ -157,6 +168,9 @@ const documentsInScope = (documents, documentId) => ({
157
168
  download: (id = documentId, options) => documents.download(id, options),
158
169
  downloadLayer: (id = documentId, options) => documents.downloadLayer(id, options),
159
170
  canDownload: (id = documentId) => documents.canDownload(id),
171
+ preparePrint: (id = documentId, options) => documents.preparePrint(id, options),
172
+ print: (id = documentId, options) => documents.print(id, options),
173
+ getPrintQuality: (id = documentId) => documents.getPrintQuality(id),
160
174
  canPrint: (id = documentId) => documents.canPrint(id),
161
175
  listPages: (id = documentId) => documents.listPages(id),
162
176
  getPage: (page, id = documentId) => documents.getPage(page, id),
@@ -294,12 +308,14 @@ function createKernel(config) {
294
308
  leases: /* @__PURE__ */ new Map(),
295
309
  settleFlushes: /* @__PURE__ */ new Set(),
296
310
  downloadWraps: [],
311
+ printWraps: [],
297
312
  changes: require_settings.createDocumentChanges({
298
313
  handle: () => session.handle,
299
314
  notify: () => store.notify(),
300
315
  report
301
316
  }),
302
317
  changeCount: 0,
318
+ changesSeen: 0,
303
319
  close: () => closeSession(session)
304
320
  };
305
321
  session.settleFlushes.add(() => session.changes.settle());
@@ -518,13 +534,202 @@ function createKernel(config) {
518
534
  readAt = session.changeCount;
519
535
  return require_settings.cancellable("documents", signal, read(handle));
520
536
  };
521
- const bytes = await require_settings.readWrapped(session.downloadWraps, readFile);
537
+ const bytes = await require_settings.runWrapped(session.downloadWraps, readFile);
522
538
  if (readAt === session.changeCount) setUnsavedChanges(session, false);
523
539
  return bytes;
524
540
  } catch (error) {
525
541
  throw require_settings.toPluginError("documents", error);
526
542
  }
527
543
  }
544
+ const printer = config.printer ?? null;
545
+ const canPrintOn = (documentId) => printer !== null && allowsOn(documentId, "doc.print");
546
+ /**
547
+ * How a print comes out: the PDF copy with `doc.print.high`, a right to keep the copy (every
548
+ * print dialog can save it as a file) and a printer that prints a PDF; else pictures, at the
549
+ * resolution `doc.print.high` or `doc.print` allows.
550
+ */
551
+ const printQualityOf = (documentId) => {
552
+ if (!canPrintOn(documentId)) return null;
553
+ if (!allowsOn(documentId, "doc.print.high")) return PRINT_STANDARD;
554
+ return (allowsOn(documentId, "doc.download") || allowsOn(documentId, "doc.download.flattened")) && printer?.stagePdf ? PRINT_PDF : PRINT_HIGH;
555
+ };
556
+ /** The pages a print asks for, in its order: every page when it names none. */
557
+ function printPagesOf(session, pages) {
558
+ const all = metaOf(session.id)?.pages ?? EMPTY_PAGES;
559
+ if (pages === void 0) return all;
560
+ if (pages.length === 0) throw new require_settings.PluginError("invalid-input", "documents", "a print needs at least one page");
561
+ const found = pages.map((ref) => {
562
+ const page = require_settings.findPage(all, ref);
563
+ if (!page) throw new require_settings.PluginError("not-found", "documents", `page ${ref.objectNumber} is not in this document`);
564
+ return page;
565
+ });
566
+ if (new Set(found).size !== found.length) throw new require_settings.PluginError("invalid-input", "documents", "a print names a page twice");
567
+ return found;
568
+ }
569
+ /**
570
+ * What prints beside the page content: the annotations as the options ask, by default when
571
+ * the user may read them; the form fields whenever the user may read the form.
572
+ */
573
+ const printLayersOf = (documentId, options) => ({
574
+ includeAnnotations: options.includeAnnotations ?? allowsOn(documentId, "doc.annotate.read"),
575
+ includeFormFields: allowsOn(documentId, "doc.forms.read")
576
+ });
577
+ /**
578
+ * Make the pages as they print and hand them to the printer, once the document settled: the
579
+ * PDF copy, or a picture of each page. The pages are looked up again each time, so one deleted
580
+ * meanwhile refuses `not-found`. Returns the staged print, how it came out, and the change it
581
+ * saw last, so a print can tell it went stale.
582
+ */
583
+ async function stagePrint(session, activePrinter, quality, options) {
584
+ const { signal } = options;
585
+ await require_settings.settle(session.settleFlushes, [signal, session.signal], report);
586
+ const handle = documentHandle(session.id);
587
+ if (!handle) throw new require_settings.PluginError("not-ready", "documents", "no open document to print");
588
+ const changesSeen = session.changesSeen;
589
+ const job = {
590
+ handle,
591
+ pages: printPagesOf(session, options.pages),
592
+ layers: printLayersOf(session.id, options),
593
+ name: session.name ?? "document",
594
+ options
595
+ };
596
+ options.onProgress?.({
597
+ pagesRendered: 0,
598
+ pageCount: job.pages.length
599
+ });
600
+ if (quality.kind === "images") return {
601
+ staged: await stagePictures(job, activePrinter, quality.dpi),
602
+ quality,
603
+ changesSeen
604
+ };
605
+ try {
606
+ return {
607
+ staged: await stagePdfCopy(job, activePrinter),
608
+ quality,
609
+ changesSeen
610
+ };
611
+ } catch (error) {
612
+ if (signal?.aborted) throw error;
613
+ return {
614
+ staged: await stagePictures(job, activePrinter, PRINT_HIGH.dpi),
615
+ quality: PRINT_HIGH,
616
+ changesSeen
617
+ };
618
+ }
619
+ }
620
+ /** The PDF copy of the pages as they print, staged with the printer. */
621
+ async function stagePdfCopy(job, activePrinter) {
622
+ const { handle, pages, layers, name, options } = job;
623
+ const { signal } = options;
624
+ if (!activePrinter.stagePdf) throw new require_settings.PluginError("unsupported", "documents", "this printer prints no PDF");
625
+ const pdf = await require_settings.cancellable("documents", signal, handle.pages.extract(pages.map((page) => page.ref), {
626
+ flatten: { usage: "print" },
627
+ ...layers
628
+ }));
629
+ const staged = await activePrinter.stagePdf(pdf, {
630
+ name,
631
+ ...signal ? { signal } : {}
632
+ });
633
+ options.onProgress?.({
634
+ pagesRendered: pages.length,
635
+ pageCount: pages.length
636
+ });
637
+ return staged;
638
+ }
639
+ /** A picture of each page, drawn by the print rules at `dpi`, staged with the printer. */
640
+ async function stagePictures(job, activePrinter, dpi) {
641
+ const { handle, pages, layers, name, options } = job;
642
+ const { signal } = options;
643
+ const sheets = [];
644
+ for (const page of pages) {
645
+ const blob = await require_settings.cancellable("documents", signal, (await require_settings.cancellable("documents", signal, handle.page(page.ref).render.image({
646
+ usage: "print",
647
+ viewport: {
648
+ kind: "dpi",
649
+ dpi
650
+ },
651
+ format: "png",
652
+ ...layers
653
+ }))).blob());
654
+ sheets.push({
655
+ image: blob,
656
+ widthPt: page.size.width,
657
+ heightPt: page.size.height
658
+ });
659
+ options.onProgress?.({
660
+ pagesRendered: sheets.length,
661
+ pageCount: pages.length
662
+ });
663
+ }
664
+ return activePrinter.stage(sheets, {
665
+ name,
666
+ ...signal ? { signal } : {}
667
+ });
668
+ }
669
+ /**
670
+ * Get a print ready. Refused before anything runs without `doc.print` or a printer, or for
671
+ * pages the document doesn't have. Its `print()` runs inside every `aroundPrint` wrap (the
672
+ * document's own print actions), and prepares again first when the document changed since:
673
+ * a will-print action that fills in a date is on the paper. When the printer turns the PDF
674
+ * copy down, preparing or printing, the same pages print as pictures, and `quality` says so.
675
+ */
676
+ async function preparePrint(documentId, options = {}) {
677
+ const session = readySession(documentId, "preparePrint");
678
+ if (!allowsOn(session.id, "doc.print")) throw require_settings.permissionDenied("documents", "doc.print", "documents.preparePrint");
679
+ const quality = printQualityOf(session.id);
680
+ if (!printer || !quality) throw new require_settings.PluginError("unsupported", "documents", "no printer: pass one to createKernel({ printer }), such as browserPrinter() from @embedpdf/web");
681
+ const pageCount = printPagesOf(session, options.pages).length;
682
+ try {
683
+ let { staged, changesSeen, quality: printed } = await stagePrint(session, printer, quality, options);
684
+ let disposed = false;
685
+ const dispose = () => {
686
+ if (disposed) return;
687
+ disposed = true;
688
+ staged.dispose();
689
+ };
690
+ session.scope.defer(dispose);
691
+ /** Stage the print again, as `as`, in place of what was staged. */
692
+ const restage = async (as, signal) => {
693
+ const fresh = await stagePrint(session, printer, as, {
694
+ ...options.pages ? { pages: options.pages } : {},
695
+ ...options.includeAnnotations !== void 0 ? { includeAnnotations: options.includeAnnotations } : {},
696
+ ...signal ? { signal } : {}
697
+ });
698
+ staged.dispose();
699
+ ({staged, changesSeen, quality: printed} = fresh);
700
+ };
701
+ return {
702
+ get quality() {
703
+ return printed;
704
+ },
705
+ pageCount,
706
+ needsTap: printer.needsTap === true,
707
+ async print(printOptions) {
708
+ if (disposed) throw new require_settings.PluginError("invalid-input", "documents", "this print was disposed");
709
+ const signal = printOptions?.signal;
710
+ const run = async () => {
711
+ if (session.changesSeen !== changesSeen) await restage(quality, signal);
712
+ if (signal?.aborted) throw new require_settings.PluginError("operation-cancelled", "documents", "print cancelled", { cause: signal.reason });
713
+ try {
714
+ await staged.print();
715
+ } catch (error) {
716
+ if (printed.kind !== "pdf") throw error;
717
+ await restage(PRINT_HIGH, signal);
718
+ await staged.print();
719
+ }
720
+ };
721
+ try {
722
+ await require_settings.runWrapped(session.printWraps, run);
723
+ } catch (error) {
724
+ throw require_settings.toPluginError("documents", error);
725
+ }
726
+ },
727
+ dispose
728
+ };
729
+ } catch (error) {
730
+ throw require_settings.toPluginError("documents", error);
731
+ }
732
+ }
528
733
  /** Whether the plugin's `create()` is running for this document: it has no capability yet. */
529
734
  const isBuilding = (plugin, session) => session.building.has(plugin) && !session.capabilities.has(plugin);
530
735
  function buildDocumentCapability(plugin, session) {
@@ -666,9 +871,12 @@ function createKernel(config) {
666
871
  session.scope.defer(bursts.close);
667
872
  const unsubscribeEvents = session.handle.events.subscribe((event) => {
668
873
  bursts.first(event);
669
- if (tracksChanges && changesDocument(event)) {
670
- session.changeCount += 1;
671
- setUnsavedChanges(session, true);
874
+ if (changesDocument(event)) {
875
+ session.changesSeen += 1;
876
+ if (tracksChanges) {
877
+ session.changeCount += 1;
878
+ setUnsavedChanges(session, true);
879
+ }
672
880
  }
673
881
  const layout = layoutFromEvent(event);
674
882
  if (!layout) return;
@@ -955,8 +1163,18 @@ function createKernel(config) {
955
1163
  },
956
1164
  download: (id, options) => downloadFile(id, options?.signal, "download", (handle) => handle.download(options?.mode !== void 0 ? { mode: options.mode } : void 0)),
957
1165
  downloadLayer: (id, options) => downloadFile(id, options?.signal, "downloadLayer", (handle) => (0, _embedpdf_engine_core_runtime.isLocalDocument)(handle) ? handle.downloadLayer() : Promise.reject(noLayer()), (handle) => (0, _embedpdf_engine_core_runtime.isLocalDocument)(handle) ? null : noLayer()),
1166
+ preparePrint: (id, options) => preparePrint(id, options),
1167
+ async print(id, options = {}) {
1168
+ const prepared = await preparePrint(id, options);
1169
+ try {
1170
+ await prepared.print(options.signal ? { signal: options.signal } : void 0);
1171
+ } finally {
1172
+ prepared.dispose();
1173
+ }
1174
+ },
1175
+ getPrintQuality: (id) => printQualityOf(id),
958
1176
  canDownload: (id) => allowsOn(id, "doc.download"),
959
- canPrint: (id) => allowsOn(id, "doc.print"),
1177
+ canPrint: (id) => canPrintOn(id),
960
1178
  listPages: (id) => metaOf(id)?.pages ?? EMPTY_PAGES,
961
1179
  getPage: (page, id) => require_settings.findPage(metaOf(id)?.pages ?? EMPTY_PAGES, page),
962
1180
  getPageIndex: (ref, id) => metaOf(id)?.pages.findIndex((pageInfo) => (0, _embedpdf_engine_core_runtime.pageRefsEqual)(pageInfo.ref, ref)) ?? -1,