@forgeroute/ui 0.3.10 → 0.3.12

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.
@@ -1,6 +1,6 @@
1
1
  import { isPlatformBrowser } from "@angular/common";
2
2
  import * as i0 from "@angular/core";
3
- import { ChangeDetectionStrategy, Component, DestroyRef, ElementRef, InjectionToken, Injector, PLATFORM_ID, afterNextRender, computed, effect, inject, input, output, signal, untracked } from "@angular/core";
3
+ import { ChangeDetectionStrategy, Component, DestroyRef, ElementRef, InjectionToken, Injector, PLATFORM_ID, afterNextRender, computed, effect, inject, input, makeEnvironmentProviders, output, signal, untracked } from "@angular/core";
4
4
  const PDF_VIEWER_WORKER_SRC = new InjectionToken("PDF_VIEWER_WORKER_SRC", {
5
5
  providedIn: "root",
6
6
  factory: () => ""
@@ -10,6 +10,34 @@ const DEFAULT_FIELD_HEIGHT = 14;
10
10
  const MAX_CANVAS_PIXELS = 16e6;
11
11
  const MAX_TOTAL_CANVAS_PIXELS = 48e6;
12
12
  const WORKER_MISSING = "PDF worker is not configured.";
13
+ const PDF_VIEWER_REQUIRED_FEATURES = [
14
+ "Promise.withResolvers",
15
+ "Promise.try",
16
+ "Map.prototype.getOrInsertComputed",
17
+ "Math.sumPrecise",
18
+ "Iterator",
19
+ "URL.parse"
20
+ ];
21
+ function findMissingPdfFeatures(scope = globalThis, needsUrl = true) {
22
+ const g = scope;
23
+ const has = {
24
+ "Promise.withResolvers": () => typeof g["Promise"]?.withResolvers === "function",
25
+ "Promise.try": () => typeof g["Promise"]?.try === "function",
26
+ "Map.prototype.getOrInsertComputed": () => typeof g["Map"]?.prototype?.getOrInsertComputed === "function",
27
+ "Math.sumPrecise": () => typeof g["Math"]?.sumPrecise === "function",
28
+ "Iterator": () => typeof g["Iterator"] === "function" && typeof g["Iterator"].prototype === "object",
29
+ "URL.parse": () => !needsUrl || typeof g["URL"]?.parse === "function"
30
+ };
31
+ return PDF_VIEWER_REQUIRED_FEATURES.filter((f) => !has[f]());
32
+ }
33
+ var PdfViewerUnsupportedError = class extends Error {
34
+ constructor(missing) {
35
+ super("This browser cannot run pdf.js; missing: " + missing.join(", "));
36
+ this.missing = missing;
37
+ this.reason = "unsupported-browser";
38
+ this.name = "PdfViewerUnsupportedError";
39
+ }
40
+ };
13
41
  var PdfViewerComponent = class PdfViewerComponent {
14
42
  constructor() {
15
43
  this.src = input(null, ...ngDevMode ? [{ debugName: "src" }] : /* istanbul ignore next */ []);
@@ -23,6 +51,9 @@ var PdfViewerComponent = class PdfViewerComponent {
23
51
  this.ariaLabel = input("", ...ngDevMode ? [{ debugName: "ariaLabel" }] : /* istanbul ignore next */ []);
24
52
  this.loadingText = input("Loading document…", ...ngDevMode ? [{ debugName: "loadingText" }] : /* istanbul ignore next */ []);
25
53
  this.errorText = input("Failed to load document.", ...ngDevMode ? [{ debugName: "errorText" }] : /* istanbul ignore next */ []);
54
+ this.unsupportedText = input("This browser can't show the document preview.", ...ngDevMode ? [{ debugName: "unsupportedText" }] : /* istanbul ignore next */ []);
55
+ this.openText = input("Open the document", ...ngDevMode ? [{ debugName: "openText" }] : /* istanbul ignore next */ []);
56
+ this.checkBrowserSupport = input(true, ...ngDevMode ? [{ debugName: "checkBrowserSupport" }] : /* istanbul ignore next */ []);
26
57
  this.loaded = output();
27
58
  this.loadFailed = output();
28
59
  this.pagesRendered = output();
@@ -30,7 +61,12 @@ var PdfViewerComponent = class PdfViewerComponent {
30
61
  this.pageSizes = signal([], ...ngDevMode ? [{ debugName: "pageSizes" }] : /* istanbul ignore next */ []);
31
62
  this.containerWidth = signal(0, ...ngDevMode ? [{ debugName: "containerWidth" }] : /* istanbul ignore next */ []);
32
63
  this.customError = signal("", ...ngDevMode ? [{ debugName: "customError" }] : /* istanbul ignore next */ []);
64
+ this.openUrl = computed(() => this.data() == null ? this.src() || "" : "", ...ngDevMode ? [{ debugName: "openUrl" }] : /* istanbul ignore next */ []);
33
65
  this.errorMessage = computed(() => this.customError() || this.errorText(), ...ngDevMode ? [{ debugName: "errorMessage" }] : /* istanbul ignore next */ []);
66
+ this.liveText = computed(() => {
67
+ const status = this.status();
68
+ return status === "loading" ? this.loadingText() : status === "unsupported" ? this.unsupportedText() : "";
69
+ }, ...ngDevMode ? [{ debugName: "liveText" }] : /* istanbul ignore next */ []);
34
70
  this.drawKey = computed(() => this.views().map((v) => v.canvasWidth + "x" + v.canvasHeight).join("|"), ...ngDevMode ? [{ debugName: "drawKey" }] : /* istanbul ignore next */ []);
35
71
  this.effectiveWidth = computed(() => this.widthFor(this.containerWidth()), ...ngDevMode ? [{ debugName: "effectiveWidth" }] : /* istanbul ignore next */ []);
36
72
  this.views = computed(() => {
@@ -115,6 +151,7 @@ var PdfViewerComponent = class PdfViewerComponent {
115
151
  this.headersKey();
116
152
  this.withCredentials();
117
153
  this.workerSrc();
154
+ this.checkBrowserSupport();
118
155
  if (!this.isBrowser) return;
119
156
  untracked(() => void this.startLoad());
120
157
  });
@@ -139,6 +176,9 @@ var PdfViewerComponent = class PdfViewerComponent {
139
176
  loadPdfJs() {
140
177
  return import("pdfjs-dist");
141
178
  }
179
+ missingFeatures(needsUrl) {
180
+ return findMissingPdfFeatures(globalThis, needsUrl);
181
+ }
142
182
  resolvedRatio() {
143
183
  const explicit = this.pixelRatio();
144
184
  if (explicit != null && explicit > 0) return explicit;
@@ -161,6 +201,16 @@ var PdfViewerComponent = class PdfViewerComponent {
161
201
  this.status.set("idle");
162
202
  return;
163
203
  }
204
+ if (this.checkBrowserSupport()) {
205
+ const missing = this.missingFeatures(data == null);
206
+ if (missing.length > 0) {
207
+ this.loadToken++;
208
+ this.pageSizes.set([]);
209
+ this.status.set("unsupported");
210
+ this.loadFailed.emit(new PdfViewerUnsupportedError(missing));
211
+ return;
212
+ }
213
+ }
164
214
  this.containerWidth.set(this.host.nativeElement.clientWidth);
165
215
  this.status.set("loading");
166
216
  try {
@@ -391,6 +441,27 @@ var PdfViewerComponent = class PdfViewerComponent {
391
441
  isSignal: true,
392
442
  isRequired: false,
393
443
  transformFunction: null
444
+ },
445
+ unsupportedText: {
446
+ classPropertyName: "unsupportedText",
447
+ publicName: "unsupportedText",
448
+ isSignal: true,
449
+ isRequired: false,
450
+ transformFunction: null
451
+ },
452
+ openText: {
453
+ classPropertyName: "openText",
454
+ publicName: "openText",
455
+ isSignal: true,
456
+ isRequired: false,
457
+ transformFunction: null
458
+ },
459
+ checkBrowserSupport: {
460
+ classPropertyName: "checkBrowserSupport",
461
+ publicName: "checkBrowserSupport",
462
+ isSignal: true,
463
+ isRequired: false,
464
+ transformFunction: null
394
465
  }
395
466
  },
396
467
  outputs: {
@@ -402,10 +473,18 @@ var PdfViewerComponent = class PdfViewerComponent {
402
473
  template: `
403
474
  <div class="pv-root" [attr.role]="ariaLabel() ? 'region' : null" [attr.aria-label]="ariaLabel() || null"
404
475
  [attr.aria-busy]="status() === 'loading'">
405
- <div class="pv-live" role="status" aria-live="polite">{{ status() === 'loading' ? loadingText() : '' }}</div>
476
+ <div class="pv-live" role="status" aria-live="polite">{{ liveText() }}</div>
406
477
  @switch (status()) {
407
478
  @case ('loading') { <div class="pv-state pv-loading" aria-hidden="true">{{ loadingText() }}</div> }
408
479
  @case ('error') { <div class="pv-state pv-error" role="alert">{{ errorMessage() }}</div> }
480
+ @case ('unsupported') {
481
+ <div class="pv-state pv-unsupported">
482
+ <p class="pv-unsupported-text">{{ unsupportedText() }}</p>
483
+ @if (openUrl(); as url) {
484
+ <a class="pv-open" [href]="url" target="_blank" rel="noopener">{{ openText() }}<span class="pv-sr"> (opens in a new tab)</span></a>
485
+ }
486
+ </div>
487
+ }
409
488
  @case ('ready') {
410
489
  <div class="pv-pages">
411
490
  @for (p of views(); track p.number) {
@@ -427,7 +506,7 @@ var PdfViewerComponent = class PdfViewerComponent {
427
506
  </div>
428
507
  `,
429
508
  isInline: true,
430
- styles: [":host{display:block;width:100%}.pv-root{position:relative}.pv-live{position:absolute;width:1px;height:1px;margin:-1px;padding:0;overflow:hidden;clip:rect(0,0,0,0);white-space:nowrap;border:0}.pv-state{display:flex;align-items:center;justify-content:center;width:100%;min-height:12rem;padding:0 16px;text-align:center;font-size:.8125rem;line-height:1.4;overflow-wrap:anywhere;box-sizing:border-box}.pv-loading{color:#64748b}.pv-error{color:#dc2626}.pv-pages{display:flex;flex-direction:column;gap:16px;width:100%;max-width:100%}.pv-page{position:relative;margin:0 auto;max-width:100%}.pv-canvas{display:block;width:100%;height:auto;background:#fff;border-radius:2px;box-shadow:0 10px 15px -3px #0000001a,0 4px 6px -4px #0000001a}.pv-field{position:absolute;display:flex;align-items:center;box-sizing:border-box;padding:0 4px;overflow:hidden;pointer-events:none;border:1px solid;border-radius:2px;-webkit-user-select:none;user-select:none}.pv-field-label{min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;line-height:1}.pv-tone-blue{background:#dbeafe;border-color:#93c5fd;color:#1d4ed8}.pv-tone-green{background:#dcfce7;border-color:#86efac;color:#15803d}.pv-tone-amber{background:#fef3c7;border-color:#fcd34d;color:#92400e}.pv-tone-red{background:#fee2e2;border-color:#fca5a5;color:#b91c1c}.pv-tone-slate{background:#f1f5f9;border-color:#cbd5e1;color:#334155}\n"],
509
+ styles: [":host{display:block;width:100%}.pv-root{position:relative}.pv-live,.pv-sr{position:absolute;width:1px;height:1px;margin:-1px;padding:0;overflow:hidden;clip:rect(0,0,0,0);white-space:nowrap;border:0}.pv-state{display:flex;align-items:center;justify-content:center;width:100%;min-height:12rem;padding:0 16px;text-align:center;font-size:.8125rem;line-height:1.4;overflow-wrap:anywhere;box-sizing:border-box}.pv-loading{color:#64748b}.pv-error{color:#dc2626}.pv-unsupported{flex-direction:column;gap:8px;color:#334155}.pv-unsupported-text{margin:0}.pv-open{display:inline-flex;align-items:center;justify-content:center;position:relative;min-height:44px;min-width:44px;padding:0 16px;box-sizing:border-box;color:#1d4ed8;font-weight:600;text-decoration:underline;border-radius:6px}.pv-open:focus-visible{outline:2px solid #1d4ed8;outline-offset:2px}.pv-pages{display:flex;flex-direction:column;gap:16px;width:100%;max-width:100%}.pv-page{position:relative;margin:0 auto;max-width:100%}.pv-canvas{display:block;width:100%;height:auto;background:#fff;border-radius:2px;box-shadow:0 10px 15px -3px #0000001a,0 4px 6px -4px #0000001a}.pv-field{position:absolute;display:flex;align-items:center;box-sizing:border-box;padding:0 4px;overflow:hidden;pointer-events:none;border:1px solid;border-radius:2px;-webkit-user-select:none;user-select:none}.pv-field-label{min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;line-height:1}.pv-tone-blue{background:#dbeafe;border-color:#93c5fd;color:#1d4ed8}.pv-tone-green{background:#dcfce7;border-color:#86efac;color:#15803d}.pv-tone-amber{background:#fef3c7;border-color:#fcd34d;color:#92400e}.pv-tone-red{background:#fee2e2;border-color:#fca5a5;color:#b91c1c}.pv-tone-slate{background:#f1f5f9;border-color:#cbd5e1;color:#334155}\n"],
431
510
  changeDetection: i0.ChangeDetectionStrategy.OnPush
432
511
  });
433
512
  }
@@ -446,10 +525,18 @@ i0.ɵɵngDeclareClassMetadata({
446
525
  template: `
447
526
  <div class="pv-root" [attr.role]="ariaLabel() ? 'region' : null" [attr.aria-label]="ariaLabel() || null"
448
527
  [attr.aria-busy]="status() === 'loading'">
449
- <div class="pv-live" role="status" aria-live="polite">{{ status() === 'loading' ? loadingText() : '' }}</div>
528
+ <div class="pv-live" role="status" aria-live="polite">{{ liveText() }}</div>
450
529
  @switch (status()) {
451
530
  @case ('loading') { <div class="pv-state pv-loading" aria-hidden="true">{{ loadingText() }}</div> }
452
531
  @case ('error') { <div class="pv-state pv-error" role="alert">{{ errorMessage() }}</div> }
532
+ @case ('unsupported') {
533
+ <div class="pv-state pv-unsupported">
534
+ <p class="pv-unsupported-text">{{ unsupportedText() }}</p>
535
+ @if (openUrl(); as url) {
536
+ <a class="pv-open" [href]="url" target="_blank" rel="noopener">{{ openText() }}<span class="pv-sr"> (opens in a new tab)</span></a>
537
+ }
538
+ </div>
539
+ }
453
540
  @case ('ready') {
454
541
  <div class="pv-pages">
455
542
  @for (p of views(); track p.number) {
@@ -470,7 +557,7 @@ i0.ɵɵngDeclareClassMetadata({
470
557
  }
471
558
  </div>
472
559
  `,
473
- styles: [":host{display:block;width:100%}.pv-root{position:relative}.pv-live{position:absolute;width:1px;height:1px;margin:-1px;padding:0;overflow:hidden;clip:rect(0,0,0,0);white-space:nowrap;border:0}.pv-state{display:flex;align-items:center;justify-content:center;width:100%;min-height:12rem;padding:0 16px;text-align:center;font-size:.8125rem;line-height:1.4;overflow-wrap:anywhere;box-sizing:border-box}.pv-loading{color:#64748b}.pv-error{color:#dc2626}.pv-pages{display:flex;flex-direction:column;gap:16px;width:100%;max-width:100%}.pv-page{position:relative;margin:0 auto;max-width:100%}.pv-canvas{display:block;width:100%;height:auto;background:#fff;border-radius:2px;box-shadow:0 10px 15px -3px #0000001a,0 4px 6px -4px #0000001a}.pv-field{position:absolute;display:flex;align-items:center;box-sizing:border-box;padding:0 4px;overflow:hidden;pointer-events:none;border:1px solid;border-radius:2px;-webkit-user-select:none;user-select:none}.pv-field-label{min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;line-height:1}.pv-tone-blue{background:#dbeafe;border-color:#93c5fd;color:#1d4ed8}.pv-tone-green{background:#dcfce7;border-color:#86efac;color:#15803d}.pv-tone-amber{background:#fef3c7;border-color:#fcd34d;color:#92400e}.pv-tone-red{background:#fee2e2;border-color:#fca5a5;color:#b91c1c}.pv-tone-slate{background:#f1f5f9;border-color:#cbd5e1;color:#334155}\n"]
560
+ styles: [":host{display:block;width:100%}.pv-root{position:relative}.pv-live,.pv-sr{position:absolute;width:1px;height:1px;margin:-1px;padding:0;overflow:hidden;clip:rect(0,0,0,0);white-space:nowrap;border:0}.pv-state{display:flex;align-items:center;justify-content:center;width:100%;min-height:12rem;padding:0 16px;text-align:center;font-size:.8125rem;line-height:1.4;overflow-wrap:anywhere;box-sizing:border-box}.pv-loading{color:#64748b}.pv-error{color:#dc2626}.pv-unsupported{flex-direction:column;gap:8px;color:#334155}.pv-unsupported-text{margin:0}.pv-open{display:inline-flex;align-items:center;justify-content:center;position:relative;min-height:44px;min-width:44px;padding:0 16px;box-sizing:border-box;color:#1d4ed8;font-weight:600;text-decoration:underline;border-radius:6px}.pv-open:focus-visible{outline:2px solid #1d4ed8;outline-offset:2px}.pv-pages{display:flex;flex-direction:column;gap:16px;width:100%;max-width:100%}.pv-page{position:relative;margin:0 auto;max-width:100%}.pv-canvas{display:block;width:100%;height:auto;background:#fff;border-radius:2px;box-shadow:0 10px 15px -3px #0000001a,0 4px 6px -4px #0000001a}.pv-field{position:absolute;display:flex;align-items:center;box-sizing:border-box;padding:0 4px;overflow:hidden;pointer-events:none;border:1px solid;border-radius:2px;-webkit-user-select:none;user-select:none}.pv-field-label{min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;line-height:1}.pv-tone-blue{background:#dbeafe;border-color:#93c5fd;color:#1d4ed8}.pv-tone-green{background:#dcfce7;border-color:#86efac;color:#15803d}.pv-tone-amber{background:#fef3c7;border-color:#fcd34d;color:#92400e}.pv-tone-red{background:#fee2e2;border-color:#fca5a5;color:#b91c1c}.pv-tone-slate{background:#f1f5f9;border-color:#cbd5e1;color:#334155}\n"]
474
561
  }]
475
562
  }],
476
563
  ctorParameters: () => [],
@@ -563,6 +650,30 @@ i0.ɵɵngDeclareClassMetadata({
563
650
  required: false
564
651
  }]
565
652
  }],
653
+ unsupportedText: [{
654
+ type: i0.Input,
655
+ args: [{
656
+ isSignal: true,
657
+ alias: "unsupportedText",
658
+ required: false
659
+ }]
660
+ }],
661
+ openText: [{
662
+ type: i0.Input,
663
+ args: [{
664
+ isSignal: true,
665
+ alias: "openText",
666
+ required: false
667
+ }]
668
+ }],
669
+ checkBrowserSupport: [{
670
+ type: i0.Input,
671
+ args: [{
672
+ isSignal: true,
673
+ alias: "checkBrowserSupport",
674
+ required: false
675
+ }]
676
+ }],
566
677
  loaded: [{
567
678
  type: i0.Output,
568
679
  args: ["loaded"]
@@ -577,6 +688,14 @@ i0.ɵɵngDeclareClassMetadata({
577
688
  }]
578
689
  }
579
690
  });
580
- export { PDF_VIEWER_WORKER_SRC, PdfViewerComponent };
691
+ function provideForgeroutePdf(options) {
692
+ const workerSrc = typeof options?.workerSrc === "string" ? options.workerSrc.trim() : "";
693
+ if (!workerSrc) throw new Error("provideForgeroutePdf: \"workerSrc\" must be a non-empty URL of the pdf.js worker script (for example \"/pdf.worker.min.mjs\").");
694
+ return makeEnvironmentProviders([{
695
+ provide: PDF_VIEWER_WORKER_SRC,
696
+ useValue: workerSrc
697
+ }]);
698
+ }
699
+ export { PDF_VIEWER_REQUIRED_FEATURES, PDF_VIEWER_WORKER_SRC, PdfViewerComponent, PdfViewerUnsupportedError, findMissingPdfFeatures, provideForgeroutePdf };
581
700
 
582
701
  //# sourceMappingURL=forgeroute-ui-pdf.mjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"forgeroute-ui-pdf.mjs","names":[],"sources":["../../../projects/forgeroute-ui/pdf/src/pdf-viewer/pdf-viewer.component.ts"],"sourcesContent":["import { isPlatformBrowser } from '@angular/common';\nimport {\n PLATFORM_ID, Component, ChangeDetectionStrategy, DestroyRef, ElementRef, InjectionToken, Injector, afterNextRender, computed, effect,\n inject, input, output, signal, untracked,\n} from '@angular/core';\nimport type { PDFDocumentProxy, PDFPageProxy } from 'pdfjs-dist';\n\n/** Where pdf.js finds its worker script. Provide it once in the app (`{ provide: PDF_VIEWER_WORKER_SRC, useValue: '/assets/pdf.worker.min.mjs' }`). */\nexport const PDF_VIEWER_WORKER_SRC = new InjectionToken<string>('PDF_VIEWER_WORKER_SRC', { providedIn: 'root', factory: () => '' });\n\n/** Colour of an overlay badge. */\nexport type PdfViewerTone = 'blue' | 'green' | 'amber' | 'red' | 'slate';\n\n/** A labelled box drawn over a page. Positions are PDF points with the origin at the page's bottom-left corner. */\nexport interface PdfViewerField {\n id: string;\n /** 1-based page number; a field on a page that does not exist is ignored. */\n page: number;\n /** Bottom-left corner of the box, in PDF points. */\n x: number;\n y: number;\n /** Size in PDF points. Without a width the box fits its label; the default height is 14. */\n width?: number;\n height?: number;\n label: string;\n tone?: PdfViewerTone;\n}\n\nexport interface PdfViewerLoadedEvent {\n pageCount: number;\n /** Every page's size in PDF points at scale 1. */\n pages: { width: number; height: number }[];\n /** The loaded pdf.js document, so the host can read text content. The viewer destroys it when it is replaced or the viewer goes away. */\n document: PDFDocumentProxy;\n}\n\nexport interface PdfViewerRenderedEvent {\n pageCount: number;\n /** CSS px per PDF point of the first page. */\n scale: number;\n /** Displayed (CSS px) width of every page. */\n pageWidths: number[];\n}\n\ntype PdfJsModule = typeof import('pdfjs-dist');\ntype Status = 'idle' | 'loading' | 'ready' | 'error';\n\ninterface PageView {\n number: number;\n cssWidth: number;\n cssHeight: number;\n canvasWidth: number;\n canvasHeight: number;\n fields: FieldView[];\n}\ninterface FieldView {\n id: string; label: string; tone: PdfViewerTone;\n left: number; top: number; width: number | null; maxWidth: number; height: number; fontPx: number;\n}\n\nconst DEFAULT_MAX_WIDTH = 700;\nconst DEFAULT_FIELD_HEIGHT = 14;\n/** Most pixels one page's canvas may have; Safari (iOS especially) refuses or blanks bigger ones. */\nconst MAX_CANVAS_PIXELS = 16_000_000;\n/** Most pixels all canvases together may have; iOS Safari limits total canvas memory. */\nconst MAX_TOTAL_CANVAS_PIXELS = 48_000_000;\nconst WORKER_MISSING = 'PDF worker is not configured.';\n\n/**\n * Renders every page of a PDF into a canvas (crisp on high-density screens), with optional labelled boxes overlaid in PDF coordinates.\n * Needs `pdfjs-dist` (loaded on first use, so it stays out of bundles that never show a PDF).\n *\n * <ui-pdf-viewer [data]=\"buffer()\" [fields]=\"badges()\" ariaLabel=\"Contract preview\" (loaded)=\"onLoaded($event)\" />\n * <ui-pdf-viewer src=\"/files/contract.pdf\" [httpHeaders]=\"{ Authorization: 'Bearer ' + token }\" />\n *\n * - `src` / `data`: a URL, or the bytes (`data` wins). The bytes are copied, so your buffer stays usable.\n * - `workerSrc` or the `PDF_VIEWER_WORKER_SRC` token: the pdf.js worker URL. Without either the viewer shows \"PDF worker is not configured.\"\n * - `maxPageWidth` (default 700): pages are drawn this wide, or the container's width when narrower; never larger.\n * - `pixelRatio`: canvas pixels per CSS pixel; default is the screen's ratio between 1 and 3.\n * - `fields`: badges positioned in PDF points (origin bottom-left); they scale with the page.\n * - `loaded` / `pagesRendered` / `loadFailed`: see the event types. A new `src` / `data` replaces the document; a slow older load never paints.\n * A page that fails to draw after `loaded` also emits `loadFailed`, shows the error state and destroys the document that `loaded` handed to the host.\n * - A page's canvas never exceeds 16,000,000 pixels (Safari limit); the pixel ratio is lowered for such a page, its CSS size is unchanged.\n * - All canvases together never exceed 48,000,000 pixels (iOS Safari limits total canvas memory): a long document is drawn at a lower\n * density, the same for every page and never below 1 pixel per CSS pixel. CSS sizes are unchanged.\n * - `httpHeaders` and `withCredentials` apply to `src` only; they are not used when `data` is given.\n * - `fields[].id` is only a display / test hook; duplicates are tolerated. `httpHeaders` is compared by content, but a stable reference is still preferred.\n * - The worker-missing message is fixed text and ignores `errorText`, because it is a setup problem for the developer.\n * - Needs pdf.js 5 (`pdfjs-dist` >= 5), which needs Safari 17.4+; the legacy pdf.js build is not supported by this component.\n * - The public typings reference `pdfjs-dist` types: apps that type-check the library without `pdfjs-dist` installed need `skipLibCheck` or the\n * package (the same trade-off as `@forgeroute/ui/charts` and chart.js).\n */\n@Component({\n selector: 'ui-pdf-viewer',\n standalone: true,\n changeDetection: ChangeDetectionStrategy.OnPush,\n template: `\n <div class=\"pv-root\" [attr.role]=\"ariaLabel() ? 'region' : null\" [attr.aria-label]=\"ariaLabel() || null\"\n [attr.aria-busy]=\"status() === 'loading'\">\n <div class=\"pv-live\" role=\"status\" aria-live=\"polite\">{{ status() === 'loading' ? loadingText() : '' }}</div>\n @switch (status()) {\n @case ('loading') { <div class=\"pv-state pv-loading\" aria-hidden=\"true\">{{ loadingText() }}</div> }\n @case ('error') { <div class=\"pv-state pv-error\" role=\"alert\">{{ errorMessage() }}</div> }\n @case ('ready') {\n <div class=\"pv-pages\">\n @for (p of views(); track p.number) {\n <div class=\"pv-page\" [style.width.px]=\"p.cssWidth\">\n <canvas class=\"pv-canvas\" role=\"img\" [attr.aria-label]=\"'Page ' + p.number + ' of ' + views().length\"\n [attr.width]=\"p.canvasWidth\" [attr.height]=\"p.canvasHeight\"></canvas>\n @for (f of p.fields; track $index) {\n <div class=\"pv-field pv-tone-{{ f.tone }}\"\n [style.left.%]=\"f.left\" [style.top.%]=\"f.top\" [style.width.%]=\"f.width\" [style.max-width.%]=\"f.maxWidth\"\n [style.height.%]=\"f.height\" [style.font-size.px]=\"f.fontPx\">\n <span class=\"pv-field-label\">{{ f.label }}</span>\n </div>\n }\n </div>\n }\n </div>\n }\n }\n </div>\n `,\n styles: [`\n :host { display: block; width: 100%; }\n .pv-root { position: relative; }\n .pv-live { position: absolute; width: 1px; height: 1px; margin: -1px; padding: 0; overflow: hidden; clip: rect(0, 0, 0, 0); white-space: nowrap; border: 0; }\n .pv-state { display: flex; align-items: center; justify-content: center; width: 100%; min-height: 12rem; padding: 0 16px; text-align: center;\n font-size: 0.8125rem; line-height: 1.4; overflow-wrap: anywhere; box-sizing: border-box; }\n .pv-loading { color: #64748b; }\n .pv-error { color: #dc2626; }\n .pv-pages { display: flex; flex-direction: column; gap: 16px; width: 100%; max-width: 100%; }\n .pv-page { position: relative; margin: 0 auto; max-width: 100%; }\n .pv-canvas { display: block; width: 100%; height: auto; background: #fff; border-radius: 2px;\n box-shadow: 0 10px 15px -3px rgba(0, 0, 0, 0.1), 0 4px 6px -4px rgba(0, 0, 0, 0.1); }\n .pv-field { position: absolute; display: flex; align-items: center; box-sizing: border-box; padding: 0 4px; overflow: hidden;\n pointer-events: none; border: 1px solid; border-radius: 2px; -webkit-user-select: none; user-select: none; }\n .pv-field-label { min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; line-height: 1; }\n .pv-tone-blue { background: #dbeafe; border-color: #93c5fd; color: #1d4ed8; }\n .pv-tone-green { background: #dcfce7; border-color: #86efac; color: #15803d; }\n .pv-tone-amber { background: #fef3c7; border-color: #fcd34d; color: #92400e; }\n .pv-tone-red { background: #fee2e2; border-color: #fca5a5; color: #b91c1c; }\n .pv-tone-slate { background: #f1f5f9; border-color: #cbd5e1; color: #334155; }\n `],\n})\nexport class PdfViewerComponent {\n /** PDF URL. Ignored while `data` is set. */\n readonly src = input<string | null>(null);\n /** PDF bytes. Wins over `src`. */\n readonly data = input<ArrayBuffer | Uint8Array | null>(null);\n /** Request headers for `src`. Changes are detected by content, but pass a stable reference when you can. */\n readonly httpHeaders = input<Record<string, string>>({});\n readonly withCredentials = input(false);\n /** pdf.js worker URL; falls back to the `PDF_VIEWER_WORKER_SRC` token. */\n readonly workerSrc = input('');\n /** Widest a page is drawn, in CSS px. */\n readonly maxPageWidth = input(DEFAULT_MAX_WIDTH);\n /** Canvas pixels per CSS pixel; null = the screen's ratio, limited to 1..3. */\n readonly pixelRatio = input<number | null>(null);\n readonly fields = input<PdfViewerField[]>([]);\n /** Accessible name of the viewer. */\n readonly ariaLabel = input('');\n readonly loadingText = input('Loading document…');\n readonly errorText = input('Failed to load document.');\n\n readonly loaded = output<PdfViewerLoadedEvent>();\n readonly loadFailed = output<unknown>();\n readonly pagesRendered = output<PdfViewerRenderedEvent>();\n\n protected readonly status = signal<Status>('idle');\n private readonly pageSizes = signal<{ width: number; height: number }[]>([]);\n private readonly containerWidth = signal(0);\n private readonly customError = signal('');\n protected readonly errorMessage = computed(() => this.customError() || this.errorText());\n\n /** Changes exactly when some canvas bitmap changes size (and so is cleared). */\n private readonly drawKey = computed(() => this.views().map(v => v.canvasWidth + 'x' + v.canvasHeight).join('|'));\n private readonly effectiveWidth = computed(() => this.widthFor(this.containerWidth()));\n protected readonly views = computed((): PageView[] => {\n const sizes = this.pageSizes();\n const cssWidth = this.effectiveWidth();\n const ratio = this.resolvedRatio();\n const byPage = new Map<number, PdfViewerField[]>();\n for (const f of this.fields()) {\n if (!Number.isInteger(f.page) || f.page < 1 || f.page > sizes.length) continue;\n const list = byPage.get(f.page) ?? [];\n list.push(f);\n byPage.set(f.page, list);\n }\n const base = sizes.map(size => {\n const scale = cssWidth / size.width;\n const cssHeight = size.height * scale;\n // lower the density for a page whose bitmap would pass the canvas limit; the CSS size stays\n const pageRatio = Math.min(ratio, Math.sqrt(MAX_CANVAS_PIXELS / (cssWidth * cssHeight)));\n return { scale, cssHeight, pageRatio };\n });\n // a total budget for long documents: shrink every page's density by the same factor, but never below 1 (or below a cap-lowered ratio)\n let factor = 1;\n const bitmaps = () => base.map(b => {\n const r = Math.max(Math.min(b.pageRatio, 1), b.pageRatio * factor);\n return { w: Math.floor(cssWidth * r), h: Math.floor(b.cssHeight * r) };\n });\n let sized = bitmaps();\n for (let i = 0; i < 6; i++) {\n const total = sized.reduce((sum, b) => sum + b.w * b.h, 0);\n if (total <= MAX_TOTAL_CANVAS_PIXELS) break;\n factor *= Math.sqrt(MAX_TOTAL_CANVAS_PIXELS / total);\n sized = bitmaps();\n }\n return sizes.map((size, i) => {\n const { scale, cssHeight } = base[i];\n const fields = (byPage.get(i + 1) ?? []).map((f): FieldView => {\n const h = f.height ?? DEFAULT_FIELD_HEIGHT;\n const left = (f.x / size.width) * 100;\n return {\n id: f.id, label: f.label, tone: f.tone ?? 'blue',\n left, top: ((size.height - f.y - h) / size.height) * 100,\n width: f.width == null ? null : (f.width / size.width) * 100,\n maxWidth: Math.max(0, 100 - left),\n height: (h / size.height) * 100,\n fontPx: Math.max(9, h * scale * 0.7),\n };\n });\n return { number: i + 1, cssWidth, cssHeight, canvasWidth: sized[i].w, canvasHeight: sized[i].h, fields };\n });\n });\n\n private readonly host = inject<ElementRef<HTMLElement>>(ElementRef);\n private readonly injector = inject(Injector);\n private readonly tokenWorkerSrc = inject(PDF_VIEWER_WORKER_SRC);\n private readonly isBrowser = isPlatformBrowser(inject(PLATFORM_ID));\n private readonly headersKey = computed(() => JSON.stringify(this.httpHeaders()));\n\n /** Bumped by every new load, replacement and destroy; an async step whose token no longer matches stops. */\n private loadToken = 0;\n /** Bumped by every (re)draw so only the newest draw paints. */\n private drawToken = 0;\n private doc: PDFDocumentProxy | null = null;\n private loadingTask: { destroy?: () => unknown } | null = null;\n private pageProxies: PDFPageProxy[] = [];\n private renderTasks: { cancel?: () => void }[] = [];\n private observer: ResizeObserver | null = null;\n private frame = 0;\n private lastDrawKey = '';\n\n constructor() {\n effect(() => {\n this.src(); this.data(); this.headersKey(); this.withCredentials(); this.workerSrc();\n if (!this.isBrowser) return; // nothing to load or draw on the server\n untracked(() => void this.startLoad());\n });\n // Whatever resizes the canvases (maxPageWidth, pixelRatio) clears them, so a changed bitmap size needs a redraw. Draws the code\n // already scheduled record their key in scheduleDraw(), so this only fires for changes nothing else handled.\n effect(() => {\n const key = this.drawKey();\n untracked(() => { if (this.status() === 'ready' && key !== this.lastDrawKey) this.scheduleDraw(); });\n });\n if (this.isBrowser && typeof ResizeObserver !== 'undefined') {\n this.observer = new ResizeObserver(() => this.onResize());\n this.observer.observe(this.host.nativeElement);\n }\n inject(DestroyRef).onDestroy(() => {\n this.loadToken++;\n this.drawToken++;\n this.observer?.disconnect();\n if (this.frame && typeof cancelAnimationFrame === 'function') cancelAnimationFrame(this.frame);\n this.teardown();\n });\n }\n\n /** Test seam: loads pdf.js on first use so apps that never show a PDF do not bundle or fetch it. */\n protected loadPdfJs(): Promise<PdfJsModule> {\n return import('pdfjs-dist');\n }\n\n private resolvedRatio(): number {\n const explicit = this.pixelRatio();\n if (explicit != null && explicit > 0) return explicit;\n const dpr = typeof window !== 'undefined' ? window.devicePixelRatio || 1 : 1;\n return Math.min(Math.max(dpr, 1), 3);\n }\n\n private widthFor(container: number): number {\n const max = this.maxPageWidth() > 0 ? this.maxPageWidth() : DEFAULT_MAX_WIDTH;\n return container > 0 ? Math.min(max, container) : max;\n }\n\n private async startLoad(): Promise<void> {\n const token = ++this.loadToken;\n this.drawToken++;\n this.teardown();\n this.pageSizes.set([]);\n this.customError.set('');\n const data = this.data();\n const src = this.src();\n if (data == null && !src) { this.status.set('idle'); return; }\n this.containerWidth.set(this.host.nativeElement.clientWidth);\n this.status.set('loading');\n try {\n const pdfjs = await this.loadPdfJs();\n if (token !== this.loadToken) return;\n const worker = this.workerSrc() || this.tokenWorkerSrc;\n if (worker) pdfjs.GlobalWorkerOptions.workerSrc = worker;\n else if (!pdfjs.GlobalWorkerOptions.workerSrc) { this.fail(new Error(WORKER_MISSING), WORKER_MISSING); return; }\n\n const params: Record<string, unknown> = data != null\n ? { data: data instanceof Uint8Array ? data.slice() : new Uint8Array(data.slice(0)) } // pdf.js detaches what it is given\n : { url: src };\n if (data == null) { // request options only make sense for a URL\n const headers = this.httpHeaders();\n if (Object.keys(headers).length > 0) params['httpHeaders'] = headers;\n if (this.withCredentials()) params['withCredentials'] = true;\n }\n const task = pdfjs.getDocument(params as unknown as Parameters<PdfJsModule['getDocument']>[0]);\n this.loadingTask = task;\n const doc = await task.promise;\n if (token !== this.loadToken) { this.destroyDoc(doc); return; }\n this.doc = doc;\n\n const proxies: PDFPageProxy[] = [];\n const sizes: { width: number; height: number }[] = [];\n for (let i = 1; i <= doc.numPages; i++) {\n const page = await doc.getPage(i);\n if (token !== this.loadToken) return;\n const vp = page.getViewport({ scale: 1 });\n if (!(Number.isFinite(vp.width) && Number.isFinite(vp.height) && vp.width > 0 && vp.height > 0)) throw new Error('invalid page size');\n proxies.push(page);\n sizes.push({ width: vp.width, height: vp.height });\n }\n this.pageProxies = proxies;\n this.containerWidth.set(this.host.nativeElement.clientWidth); // the container may have changed while the file loaded\n this.pageSizes.set(sizes);\n this.status.set('ready');\n this.loaded.emit({ pageCount: sizes.length, pages: sizes.map(s => ({ ...s })), document: doc });\n this.scheduleDraw();\n } catch (err) {\n if (token !== this.loadToken) return;\n this.fail(err);\n }\n }\n\n private fail(err: unknown, message = ''): void {\n this.loadToken++; // nothing still in flight may paint over the error\n this.drawToken++;\n this.teardown();\n this.pageSizes.set([]);\n this.customError.set(message);\n this.status.set('error');\n this.loadFailed.emit(err);\n }\n\n private scheduleDraw(): void {\n const id = ++this.drawToken;\n this.lastDrawKey = this.drawKey();\n this.cancelRenders(); // a still-running paint must not land in canvases about to be resized\n afterNextRender(() => void this.draw(id), { injector: this.injector });\n }\n\n private async draw(id: number): Promise<void> {\n const token = this.loadToken;\n const stale = () => id !== this.drawToken || token !== this.loadToken;\n this.cancelRenders();\n const views = this.views();\n const canvases = Array.from(this.host.nativeElement.querySelectorAll<HTMLCanvasElement>('canvas.pv-canvas'));\n const sizes = this.pageSizes();\n const scales: number[] = [];\n try {\n for (let i = 0; i < views.length; i++) {\n if (stale()) return;\n const canvas = canvases[i];\n const page = this.pageProxies[i];\n if (!canvas || !page || !canvas.getContext('2d')) throw new Error('canvas unavailable');\n const cssScale = views[i].cssWidth / sizes[i].width;\n scales.push(cssScale);\n const viewport = page.getViewport({ scale: (views[i].canvasWidth / sizes[i].width) });\n const task = page.render({ canvas, viewport });\n this.renderTasks.push(task);\n await task.promise;\n }\n } catch (err) {\n if (stale() || (err as { name?: string } | null)?.name === 'RenderingCancelledException') return;\n this.fail(err);\n return;\n }\n if (stale()) return;\n this.pagesRendered.emit({ pageCount: views.length, scale: scales[0] ?? 1, pageWidths: views.map(v => v.cssWidth) });\n }\n\n private onResize(): void {\n if (this.frame || typeof requestAnimationFrame !== 'function') return;\n this.frame = requestAnimationFrame(() => {\n this.frame = 0;\n const before = this.effectiveWidth();\n this.containerWidth.set(this.host.nativeElement.clientWidth);\n if (this.effectiveWidth() !== before && this.status() === 'ready') this.scheduleDraw();\n });\n }\n\n private cancelRenders(): void {\n for (const t of this.renderTasks) { try { t.cancel?.(); } catch { /* already finished */ } }\n this.renderTasks = [];\n }\n\n private teardown(): void {\n this.cancelRenders();\n const task = this.loadingTask;\n this.loadingTask = null;\n if (task) { try { Promise.resolve(task.destroy?.()).catch(() => undefined); } catch { /* ignore */ } }\n const doc = this.doc;\n this.doc = null;\n this.pageProxies = [];\n if (doc) this.destroyDoc(doc);\n }\n\n private destroyDoc(doc: PDFDocumentProxy): void {\n try { Promise.resolve(doc.destroy()).catch(() => undefined); } catch { /* ignore */ }\n }\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"forgeroute-ui-pdf.mjs","names":[],"sources":["../../../projects/forgeroute-ui/pdf/src/pdf-viewer/pdf-viewer.component.ts","../../../projects/forgeroute-ui/pdf/src/provide-forgeroute-pdf.ts"],"sourcesContent":["import { isPlatformBrowser } from '@angular/common';\nimport {\n PLATFORM_ID, Component, ChangeDetectionStrategy, DestroyRef, ElementRef, InjectionToken, Injector, afterNextRender, computed, effect,\n inject, input, output, signal, untracked,\n} from '@angular/core';\nimport type { PDFDocumentProxy, PDFPageProxy } from 'pdfjs-dist';\n\n/** Where pdf.js finds its worker script. Provide it once in the app (`{ provide: PDF_VIEWER_WORKER_SRC, useValue: '/assets/pdf.worker.min.mjs' }`). */\nexport const PDF_VIEWER_WORKER_SRC = new InjectionToken<string>('PDF_VIEWER_WORKER_SRC', { providedIn: 'root', factory: () => '' });\n\n/** Colour of an overlay badge. */\nexport type PdfViewerTone = 'blue' | 'green' | 'amber' | 'red' | 'slate';\n\n/** A labelled box drawn over a page. Positions are PDF points with the origin at the page's bottom-left corner. */\nexport interface PdfViewerField {\n id: string;\n /** 1-based page number; a field on a page that does not exist is ignored. */\n page: number;\n /** Bottom-left corner of the box, in PDF points. */\n x: number;\n y: number;\n /** Size in PDF points. Without a width the box fits its label; the default height is 14. */\n width?: number;\n height?: number;\n label: string;\n tone?: PdfViewerTone;\n}\n\nexport interface PdfViewerLoadedEvent {\n pageCount: number;\n /** Every page's size in PDF points at scale 1. */\n pages: { width: number; height: number }[];\n /** The loaded pdf.js document, so the host can read text content. The viewer destroys it when it is replaced or the viewer goes away. */\n document: PDFDocumentProxy;\n}\n\nexport interface PdfViewerRenderedEvent {\n pageCount: number;\n /** CSS px per PDF point of the first page. */\n scale: number;\n /** Displayed (CSS px) width of every page. */\n pageWidths: number[];\n}\n\ntype PdfJsModule = typeof import('pdfjs-dist');\ntype Status = 'idle' | 'loading' | 'ready' | 'error' | 'unsupported';\n\ninterface PageView {\n number: number;\n cssWidth: number;\n cssHeight: number;\n canvasWidth: number;\n canvasHeight: number;\n fields: FieldView[];\n}\ninterface FieldView {\n id: string; label: string; tone: PdfViewerTone;\n left: number; top: number; width: number | null; maxWidth: number; height: number; fontPx: number;\n}\n\nconst DEFAULT_MAX_WIDTH = 700;\nconst DEFAULT_FIELD_HEIGHT = 14;\n/** Most pixels one page's canvas may have; Safari (iOS especially) refuses or blanks bigger ones. */\nconst MAX_CANVAS_PIXELS = 16_000_000;\n/** Most pixels all canvases together may have; iOS Safari limits total canvas memory. */\nconst MAX_TOTAL_CANVAS_PIXELS = 48_000_000;\nconst WORKER_MISSING = 'PDF worker is not configured.';\n\n/**\n * Language features the pdf.js modern build calls WITHOUT a polyfill or guard on the path every document takes, found by reading\n * `pdfjs-dist/build/pdf.mjs` + `pdf.worker.mjs` of 5.7.284 (this library's dev copy) and 6.2.108 (the apps' copy). The `legacy/` build\n * polyfills them; this component imports the modern one. Details and the evidence per feature: docs/pdf-viewer.md.\n *\n * - `Promise.withResolvers`, `Promise.try`: the main-thread API and the worker message handler (every message).\n * - `Map.prototype.getOrInsertComputed`: per-page intent state and the object store on every render (6.x also in the message handler).\n * - `Math.sumPrecise`: the worker, when it builds a font's name / glyf tables (every document with text).\n * - `Iterator`: pdf.js 6.x touches `Iterator.prototype` while the module loads, so a browser without the global cannot even import it.\n * - `URL.parse`: opening a document by URL (the stream choice and the worker origin check).\n *\n * Left out on purpose: `Worker` and `structuredClone` (pdf.js falls back to a main-thread worker that only needs one of them),\n * `OffscreenCanvas` / `Float16Array` (feature-tested by pdf.js), `Uint8Array.fromBase64` / `toBase64` and `Iterator` helpers (XFA / editor / text-layer paths only).\n */\nexport const PDF_VIEWER_REQUIRED_FEATURES = [\n 'Promise.withResolvers', 'Promise.try', 'Map.prototype.getOrInsertComputed', 'Math.sumPrecise', 'Iterator', 'URL.parse',\n] as const;\nexport type PdfViewerRequiredFeature = typeof PDF_VIEWER_REQUIRED_FEATURES[number];\n\n/**\n * Names of the required features `scope` (default: the global object) lacks; [] = pdf.js can run. Synchronous and\n * side-effect free. `URL.parse` is only needed to open a URL, so it is skipped when `needsUrl` is false (bytes are given).\n */\nexport function findMissingPdfFeatures(scope: object = globalThis, needsUrl = true): PdfViewerRequiredFeature[] {\n const g = scope as Record<string, any>;\n const has: Record<PdfViewerRequiredFeature, () => boolean> = {\n 'Promise.withResolvers': () => typeof g['Promise']?.withResolvers === 'function',\n 'Promise.try': () => typeof g['Promise']?.try === 'function',\n 'Map.prototype.getOrInsertComputed': () => typeof g['Map']?.prototype?.getOrInsertComputed === 'function',\n 'Math.sumPrecise': () => typeof g['Math']?.sumPrecise === 'function',\n 'Iterator': () => typeof g['Iterator'] === 'function' && typeof g['Iterator'].prototype === 'object',\n 'URL.parse': () => !needsUrl || typeof g['URL']?.parse === 'function',\n };\n return PDF_VIEWER_REQUIRED_FEATURES.filter(f => !has[f]());\n}\n\n/** Emitted by `loadFailed` when the browser lacks a feature pdf.js needs (nothing was loaded). */\nexport class PdfViewerUnsupportedError extends Error {\n readonly reason = 'unsupported-browser' as const;\n constructor(readonly missing: readonly PdfViewerRequiredFeature[]) {\n super('This browser cannot run pdf.js; missing: ' + missing.join(', '));\n this.name = 'PdfViewerUnsupportedError';\n }\n}\n\n/**\n * Renders every page of a PDF into a canvas (crisp on high-density screens), with optional labelled boxes overlaid in PDF coordinates.\n * Needs `pdfjs-dist` (loaded on first use, so it stays out of bundles that never show a PDF).\n *\n * <ui-pdf-viewer [data]=\"buffer()\" [fields]=\"badges()\" ariaLabel=\"Contract preview\" (loaded)=\"onLoaded($event)\" />\n * <ui-pdf-viewer src=\"/files/contract.pdf\" [httpHeaders]=\"{ Authorization: 'Bearer ' + token }\" />\n *\n * - `src` / `data`: a URL, or the bytes (`data` wins). The bytes are copied, so your buffer stays usable.\n * - `workerSrc` or the `PDF_VIEWER_WORKER_SRC` token: the pdf.js worker URL. Without either the viewer shows \"PDF worker is not configured.\"\n * - `maxPageWidth` (default 700): pages are drawn this wide, or the container's width when narrower; never larger.\n * - `pixelRatio`: canvas pixels per CSS pixel; default is the screen's ratio between 1 and 3.\n * - `fields`: badges positioned in PDF points (origin bottom-left); they scale with the page.\n * - `loaded` / `pagesRendered` / `loadFailed`: see the event types. A new `src` / `data` replaces the document; a slow older load never paints.\n * A page that fails to draw after `loaded` also emits `loadFailed`, shows the error state and destroys the document that `loaded` handed to the host.\n * - A page's canvas never exceeds 16,000,000 pixels (Safari limit); the pixel ratio is lowered for such a page, its CSS size is unchanged.\n * - All canvases together never exceed 48,000,000 pixels (iOS Safari limits total canvas memory): a long document is drawn at a lower\n * density, the same for every page and never below 1 pixel per CSS pixel. CSS sizes are unchanged.\n * - `httpHeaders` and `withCredentials` apply to `src` only; they are not used when `data` is given.\n * - `fields[].id` is only a display / test hook; duplicates are tolerated. `httpHeaders` is compared by content, but a stable reference is still preferred.\n * - The worker-missing message is fixed text and ignores `errorText`, because it is a setup problem for the developer.\n * - Needs pdf.js 5 (`pdfjs-dist` >= 5); the legacy pdf.js build is not supported by this component.\n * - Browser check: before pdf.js is imported, the viewer checks (once per load, synchronously) that the browser has the features\n * `PDF_VIEWER_REQUIRED_FEATURES` lists. Without them it shows `unsupportedText` (role=\"status\") and, when `src` is a URL, an\n * \"Open the document\" link (new tab); `loadFailed` emits a `PdfViewerUnsupportedError` (`reason: 'unsupported-browser'`, `missing`).\n * `[checkBrowserSupport]=\"false\"` skips the check. See docs/pdf-viewer.md.\n * - The public typings reference `pdfjs-dist` types: apps that type-check the library without `pdfjs-dist` installed need `skipLibCheck` or the\n * package (the same trade-off as `@forgeroute/ui/charts` and chart.js).\n */\n@Component({\n selector: 'ui-pdf-viewer',\n standalone: true,\n changeDetection: ChangeDetectionStrategy.OnPush,\n template: `\n <div class=\"pv-root\" [attr.role]=\"ariaLabel() ? 'region' : null\" [attr.aria-label]=\"ariaLabel() || null\"\n [attr.aria-busy]=\"status() === 'loading'\">\n <div class=\"pv-live\" role=\"status\" aria-live=\"polite\">{{ liveText() }}</div>\n @switch (status()) {\n @case ('loading') { <div class=\"pv-state pv-loading\" aria-hidden=\"true\">{{ loadingText() }}</div> }\n @case ('error') { <div class=\"pv-state pv-error\" role=\"alert\">{{ errorMessage() }}</div> }\n @case ('unsupported') {\n <div class=\"pv-state pv-unsupported\">\n <p class=\"pv-unsupported-text\">{{ unsupportedText() }}</p>\n @if (openUrl(); as url) {\n <a class=\"pv-open\" [href]=\"url\" target=\"_blank\" rel=\"noopener\">{{ openText() }}<span class=\"pv-sr\"> (opens in a new tab)</span></a>\n }\n </div>\n }\n @case ('ready') {\n <div class=\"pv-pages\">\n @for (p of views(); track p.number) {\n <div class=\"pv-page\" [style.width.px]=\"p.cssWidth\">\n <canvas class=\"pv-canvas\" role=\"img\" [attr.aria-label]=\"'Page ' + p.number + ' of ' + views().length\"\n [attr.width]=\"p.canvasWidth\" [attr.height]=\"p.canvasHeight\"></canvas>\n @for (f of p.fields; track $index) {\n <div class=\"pv-field pv-tone-{{ f.tone }}\"\n [style.left.%]=\"f.left\" [style.top.%]=\"f.top\" [style.width.%]=\"f.width\" [style.max-width.%]=\"f.maxWidth\"\n [style.height.%]=\"f.height\" [style.font-size.px]=\"f.fontPx\">\n <span class=\"pv-field-label\">{{ f.label }}</span>\n </div>\n }\n </div>\n }\n </div>\n }\n }\n </div>\n `,\n styles: [`\n :host { display: block; width: 100%; }\n .pv-root { position: relative; }\n .pv-live, .pv-sr { position: absolute; width: 1px; height: 1px; margin: -1px; padding: 0; overflow: hidden; clip: rect(0, 0, 0, 0); white-space: nowrap; border: 0; }\n .pv-state { display: flex; align-items: center; justify-content: center; width: 100%; min-height: 12rem; padding: 0 16px; text-align: center;\n font-size: 0.8125rem; line-height: 1.4; overflow-wrap: anywhere; box-sizing: border-box; }\n .pv-loading { color: #64748b; }\n .pv-error { color: #dc2626; }\n .pv-unsupported { flex-direction: column; gap: 8px; color: #334155; }\n .pv-unsupported-text { margin: 0; }\n .pv-open { display: inline-flex; align-items: center; justify-content: center; position: relative; min-height: 44px; min-width: 44px; padding: 0 16px;\n box-sizing: border-box; color: #1d4ed8; font-weight: 600; text-decoration: underline; border-radius: 6px; }\n .pv-open:focus-visible { outline: 2px solid #1d4ed8; outline-offset: 2px; }\n .pv-pages { display: flex; flex-direction: column; gap: 16px; width: 100%; max-width: 100%; }\n .pv-page { position: relative; margin: 0 auto; max-width: 100%; }\n .pv-canvas { display: block; width: 100%; height: auto; background: #fff; border-radius: 2px;\n box-shadow: 0 10px 15px -3px rgba(0, 0, 0, 0.1), 0 4px 6px -4px rgba(0, 0, 0, 0.1); }\n .pv-field { position: absolute; display: flex; align-items: center; box-sizing: border-box; padding: 0 4px; overflow: hidden;\n pointer-events: none; border: 1px solid; border-radius: 2px; -webkit-user-select: none; user-select: none; }\n .pv-field-label { min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; line-height: 1; }\n .pv-tone-blue { background: #dbeafe; border-color: #93c5fd; color: #1d4ed8; }\n .pv-tone-green { background: #dcfce7; border-color: #86efac; color: #15803d; }\n .pv-tone-amber { background: #fef3c7; border-color: #fcd34d; color: #92400e; }\n .pv-tone-red { background: #fee2e2; border-color: #fca5a5; color: #b91c1c; }\n .pv-tone-slate { background: #f1f5f9; border-color: #cbd5e1; color: #334155; }\n `],\n})\nexport class PdfViewerComponent {\n /** PDF URL. Ignored while `data` is set. */\n readonly src = input<string | null>(null);\n /** PDF bytes. Wins over `src`. */\n readonly data = input<ArrayBuffer | Uint8Array | null>(null);\n /** Request headers for `src`. Changes are detected by content, but pass a stable reference when you can. */\n readonly httpHeaders = input<Record<string, string>>({});\n readonly withCredentials = input(false);\n /** pdf.js worker URL; falls back to the `PDF_VIEWER_WORKER_SRC` token. */\n readonly workerSrc = input('');\n /** Widest a page is drawn, in CSS px. */\n readonly maxPageWidth = input(DEFAULT_MAX_WIDTH);\n /** Canvas pixels per CSS pixel; null = the screen's ratio, limited to 1..3. */\n readonly pixelRatio = input<number | null>(null);\n readonly fields = input<PdfViewerField[]>([]);\n /** Accessible name of the viewer. */\n readonly ariaLabel = input('');\n readonly loadingText = input('Loading document…');\n readonly errorText = input('Failed to load document.');\n /** Shown when the browser cannot run pdf.js (see `checkBrowserSupport`). */\n readonly unsupportedText = input(\"This browser can't show the document preview.\");\n /** Text of the link that opens `src` in a new tab on such a browser. */\n readonly openText = input('Open the document');\n /** Check for the browser features pdf.js needs before loading it; false = always try to load. */\n readonly checkBrowserSupport = input(true);\n\n readonly loaded = output<PdfViewerLoadedEvent>();\n readonly loadFailed = output<unknown>();\n readonly pagesRendered = output<PdfViewerRenderedEvent>();\n\n protected readonly status = signal<Status>('idle');\n private readonly pageSizes = signal<{ width: number; height: number }[]>([]);\n private readonly containerWidth = signal(0);\n private readonly customError = signal('');\n /** The URL the \"Open the document\" link points at: `src` when no bytes are given. */\n protected readonly openUrl = computed(() => (this.data() == null ? this.src() || '' : ''));\n protected readonly errorMessage = computed(() => this.customError() || this.errorText());\n /** What the always-present live region announces: loading progress, or why no preview is shown (a live region that already exists is announced reliably; one inserted with its text is not). */\n protected readonly liveText = computed(() => {\n const status = this.status();\n return status === 'loading' ? this.loadingText() : status === 'unsupported' ? this.unsupportedText() : '';\n });\n\n /** Changes exactly when some canvas bitmap changes size (and so is cleared). */\n private readonly drawKey = computed(() => this.views().map(v => v.canvasWidth + 'x' + v.canvasHeight).join('|'));\n private readonly effectiveWidth = computed(() => this.widthFor(this.containerWidth()));\n protected readonly views = computed((): PageView[] => {\n const sizes = this.pageSizes();\n const cssWidth = this.effectiveWidth();\n const ratio = this.resolvedRatio();\n const byPage = new Map<number, PdfViewerField[]>();\n for (const f of this.fields()) {\n if (!Number.isInteger(f.page) || f.page < 1 || f.page > sizes.length) continue;\n const list = byPage.get(f.page) ?? [];\n list.push(f);\n byPage.set(f.page, list);\n }\n const base = sizes.map(size => {\n const scale = cssWidth / size.width;\n const cssHeight = size.height * scale;\n // lower the density for a page whose bitmap would pass the canvas limit; the CSS size stays\n const pageRatio = Math.min(ratio, Math.sqrt(MAX_CANVAS_PIXELS / (cssWidth * cssHeight)));\n return { scale, cssHeight, pageRatio };\n });\n // a total budget for long documents: shrink every page's density by the same factor, but never below 1 (or below a cap-lowered ratio)\n let factor = 1;\n const bitmaps = () => base.map(b => {\n const r = Math.max(Math.min(b.pageRatio, 1), b.pageRatio * factor);\n return { w: Math.floor(cssWidth * r), h: Math.floor(b.cssHeight * r) };\n });\n let sized = bitmaps();\n for (let i = 0; i < 6; i++) {\n const total = sized.reduce((sum, b) => sum + b.w * b.h, 0);\n if (total <= MAX_TOTAL_CANVAS_PIXELS) break;\n factor *= Math.sqrt(MAX_TOTAL_CANVAS_PIXELS / total);\n sized = bitmaps();\n }\n return sizes.map((size, i) => {\n const { scale, cssHeight } = base[i];\n const fields = (byPage.get(i + 1) ?? []).map((f): FieldView => {\n const h = f.height ?? DEFAULT_FIELD_HEIGHT;\n const left = (f.x / size.width) * 100;\n return {\n id: f.id, label: f.label, tone: f.tone ?? 'blue',\n left, top: ((size.height - f.y - h) / size.height) * 100,\n width: f.width == null ? null : (f.width / size.width) * 100,\n maxWidth: Math.max(0, 100 - left),\n height: (h / size.height) * 100,\n fontPx: Math.max(9, h * scale * 0.7),\n };\n });\n return { number: i + 1, cssWidth, cssHeight, canvasWidth: sized[i].w, canvasHeight: sized[i].h, fields };\n });\n });\n\n private readonly host = inject<ElementRef<HTMLElement>>(ElementRef);\n private readonly injector = inject(Injector);\n private readonly tokenWorkerSrc = inject(PDF_VIEWER_WORKER_SRC);\n private readonly isBrowser = isPlatformBrowser(inject(PLATFORM_ID));\n private readonly headersKey = computed(() => JSON.stringify(this.httpHeaders()));\n\n /** Bumped by every new load, replacement and destroy; an async step whose token no longer matches stops. */\n private loadToken = 0;\n /** Bumped by every (re)draw so only the newest draw paints. */\n private drawToken = 0;\n private doc: PDFDocumentProxy | null = null;\n private loadingTask: { destroy?: () => unknown } | null = null;\n private pageProxies: PDFPageProxy[] = [];\n private renderTasks: { cancel?: () => void }[] = [];\n private observer: ResizeObserver | null = null;\n private frame = 0;\n private lastDrawKey = '';\n\n constructor() {\n effect(() => {\n this.src(); this.data(); this.headersKey(); this.withCredentials(); this.workerSrc(); this.checkBrowserSupport();\n if (!this.isBrowser) return; // nothing to load or draw on the server\n untracked(() => void this.startLoad());\n });\n // Whatever resizes the canvases (maxPageWidth, pixelRatio) clears them, so a changed bitmap size needs a redraw. Draws the code\n // already scheduled record their key in scheduleDraw(), so this only fires for changes nothing else handled.\n effect(() => {\n const key = this.drawKey();\n untracked(() => { if (this.status() === 'ready' && key !== this.lastDrawKey) this.scheduleDraw(); });\n });\n if (this.isBrowser && typeof ResizeObserver !== 'undefined') {\n this.observer = new ResizeObserver(() => this.onResize());\n this.observer.observe(this.host.nativeElement);\n }\n inject(DestroyRef).onDestroy(() => {\n this.loadToken++;\n this.drawToken++;\n this.observer?.disconnect();\n if (this.frame && typeof cancelAnimationFrame === 'function') cancelAnimationFrame(this.frame);\n this.teardown();\n });\n }\n\n /** Test seam: loads pdf.js on first use so apps that never show a PDF do not bundle or fetch it. */\n protected loadPdfJs(): Promise<PdfJsModule> {\n return import('pdfjs-dist');\n }\n\n /** Test seam: the required features this browser lacks. */\n protected missingFeatures(needsUrl: boolean): readonly PdfViewerRequiredFeature[] {\n return findMissingPdfFeatures(globalThis, needsUrl);\n }\n\n private resolvedRatio(): number {\n const explicit = this.pixelRatio();\n if (explicit != null && explicit > 0) return explicit;\n const dpr = typeof window !== 'undefined' ? window.devicePixelRatio || 1 : 1;\n return Math.min(Math.max(dpr, 1), 3);\n }\n\n private widthFor(container: number): number {\n const max = this.maxPageWidth() > 0 ? this.maxPageWidth() : DEFAULT_MAX_WIDTH;\n return container > 0 ? Math.min(max, container) : max;\n }\n\n private async startLoad(): Promise<void> {\n const token = ++this.loadToken;\n this.drawToken++;\n this.teardown();\n this.pageSizes.set([]);\n this.customError.set('');\n const data = this.data();\n const src = this.src();\n if (data == null && !src) { this.status.set('idle'); return; }\n if (this.checkBrowserSupport()) {\n const missing = this.missingFeatures(data == null);\n if (missing.length > 0) { // pdf.js would throw on first use: do not even import it\n this.loadToken++;\n this.pageSizes.set([]);\n this.status.set('unsupported');\n this.loadFailed.emit(new PdfViewerUnsupportedError(missing));\n return;\n }\n }\n this.containerWidth.set(this.host.nativeElement.clientWidth);\n this.status.set('loading');\n try {\n const pdfjs = await this.loadPdfJs();\n if (token !== this.loadToken) return;\n const worker = this.workerSrc() || this.tokenWorkerSrc;\n if (worker) pdfjs.GlobalWorkerOptions.workerSrc = worker;\n else if (!pdfjs.GlobalWorkerOptions.workerSrc) { this.fail(new Error(WORKER_MISSING), WORKER_MISSING); return; }\n\n const params: Record<string, unknown> = data != null\n ? { data: data instanceof Uint8Array ? data.slice() : new Uint8Array(data.slice(0)) } // pdf.js detaches what it is given\n : { url: src };\n if (data == null) { // request options only make sense for a URL\n const headers = this.httpHeaders();\n if (Object.keys(headers).length > 0) params['httpHeaders'] = headers;\n if (this.withCredentials()) params['withCredentials'] = true;\n }\n const task = pdfjs.getDocument(params as unknown as Parameters<PdfJsModule['getDocument']>[0]);\n this.loadingTask = task;\n const doc = await task.promise;\n if (token !== this.loadToken) { this.destroyDoc(doc); return; }\n this.doc = doc;\n\n const proxies: PDFPageProxy[] = [];\n const sizes: { width: number; height: number }[] = [];\n for (let i = 1; i <= doc.numPages; i++) {\n const page = await doc.getPage(i);\n if (token !== this.loadToken) return;\n const vp = page.getViewport({ scale: 1 });\n if (!(Number.isFinite(vp.width) && Number.isFinite(vp.height) && vp.width > 0 && vp.height > 0)) throw new Error('invalid page size');\n proxies.push(page);\n sizes.push({ width: vp.width, height: vp.height });\n }\n this.pageProxies = proxies;\n this.containerWidth.set(this.host.nativeElement.clientWidth); // the container may have changed while the file loaded\n this.pageSizes.set(sizes);\n this.status.set('ready');\n this.loaded.emit({ pageCount: sizes.length, pages: sizes.map(s => ({ ...s })), document: doc });\n this.scheduleDraw();\n } catch (err) {\n if (token !== this.loadToken) return;\n this.fail(err);\n }\n }\n\n private fail(err: unknown, message = ''): void {\n this.loadToken++; // nothing still in flight may paint over the error\n this.drawToken++;\n this.teardown();\n this.pageSizes.set([]);\n this.customError.set(message);\n this.status.set('error');\n this.loadFailed.emit(err);\n }\n\n private scheduleDraw(): void {\n const id = ++this.drawToken;\n this.lastDrawKey = this.drawKey();\n this.cancelRenders(); // a still-running paint must not land in canvases about to be resized\n afterNextRender(() => void this.draw(id), { injector: this.injector });\n }\n\n private async draw(id: number): Promise<void> {\n const token = this.loadToken;\n const stale = () => id !== this.drawToken || token !== this.loadToken;\n this.cancelRenders();\n const views = this.views();\n const canvases = Array.from(this.host.nativeElement.querySelectorAll<HTMLCanvasElement>('canvas.pv-canvas'));\n const sizes = this.pageSizes();\n const scales: number[] = [];\n try {\n for (let i = 0; i < views.length; i++) {\n if (stale()) return;\n const canvas = canvases[i];\n const page = this.pageProxies[i];\n if (!canvas || !page || !canvas.getContext('2d')) throw new Error('canvas unavailable');\n const cssScale = views[i].cssWidth / sizes[i].width;\n scales.push(cssScale);\n const viewport = page.getViewport({ scale: (views[i].canvasWidth / sizes[i].width) });\n const task = page.render({ canvas, viewport });\n this.renderTasks.push(task);\n await task.promise;\n }\n } catch (err) {\n if (stale() || (err as { name?: string } | null)?.name === 'RenderingCancelledException') return;\n this.fail(err);\n return;\n }\n if (stale()) return;\n this.pagesRendered.emit({ pageCount: views.length, scale: scales[0] ?? 1, pageWidths: views.map(v => v.cssWidth) });\n }\n\n private onResize(): void {\n if (this.frame || typeof requestAnimationFrame !== 'function') return;\n this.frame = requestAnimationFrame(() => {\n this.frame = 0;\n const before = this.effectiveWidth();\n this.containerWidth.set(this.host.nativeElement.clientWidth);\n if (this.effectiveWidth() !== before && this.status() === 'ready') this.scheduleDraw();\n });\n }\n\n private cancelRenders(): void {\n for (const t of this.renderTasks) { try { t.cancel?.(); } catch { /* already finished */ } }\n this.renderTasks = [];\n }\n\n private teardown(): void {\n this.cancelRenders();\n const task = this.loadingTask;\n this.loadingTask = null;\n if (task) { try { Promise.resolve(task.destroy?.()).catch(() => undefined); } catch { /* ignore */ } }\n const doc = this.doc;\n this.doc = null;\n this.pageProxies = [];\n if (doc) this.destroyDoc(doc);\n }\n\n private destroyDoc(doc: PDFDocumentProxy): void {\n try { Promise.resolve(doc.destroy()).catch(() => undefined); } catch { /* ignore */ }\n }\n}\n","import { EnvironmentProviders, makeEnvironmentProviders } from '@angular/core';\nimport { PDF_VIEWER_WORKER_SRC } from './pdf-viewer/pdf-viewer.component';\n\nexport interface ForgeroutePdfOptions {\n /** URL of the pdf.js worker script every `ui-pdf-viewer` in the app uses, e.g. `'/pdf.worker.min.mjs'`. Must not be blank. */\n workerSrc: string;\n}\n\n/**\n * Sets the pdf.js worker location once for the whole app (provides `PDF_VIEWER_WORKER_SRC`):\n *\n * providers: [provideForgeroutePdf({ workerSrc: '/pdf.worker.min.mjs' })]\n *\n * A `workerSrc` input on a viewer still wins over this value. Throws at provide time when `workerSrc` is blank, because a viewer\n * without a worker can only show \"PDF worker is not configured.\".\n */\nexport function provideForgeroutePdf(options: ForgeroutePdfOptions): EnvironmentProviders {\n const workerSrc = typeof options?.workerSrc === 'string' ? options.workerSrc.trim() : '';\n if (!workerSrc) {\n throw new Error('provideForgeroutePdf: \"workerSrc\" must be a non-empty URL of the pdf.js worker script (for example \"/pdf.worker.min.mjs\").');\n }\n return makeEnvironmentProviders([{ provide: PDF_VIEWER_WORKER_SRC, useValue: workerSrc }]);\n}\n"],"mappings":"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"}