book(E1): vendor pdf-lib + book PDF export skeleton

First slice of Phase E. The Pages overview now has an Export PDF…
button that walks the physical-page sequence, renders each page at
print DPI, and stamps them into a single PDF via pdf-lib.

Library:
- Vendored [email protected].1 as src/vendor/pdf-lib.min.js (~513 KB,
  MIT). Lazy-loaded through loadPdfLibOnce() so the cost is only
  paid when an author actually exports.

Pipeline:
- exportBookToPdf({ dpi, includeCover }) walks
  enumerateBookPhysicalPages(book), rendering each slot at the
  resolved DPI (default 200) onto a trim-sized canvas:
    * Designed pages → master underlay + page layers via
      drawLayerToCanvas, with token substitution applied to master
      text layers (book.spec.textStyles tokens resolve per page).
    * Reflow physical pages → master underlay (with text-frame
      outline stripped) + the pagination engine's block output drawn
      via fillText at the frame's coordinates.
    * Cover → wide spread canvas, all book-cover-* / book-spine
      layers drawn in place.
- Each canvas converts to PNG via canvas.toBlob → Uint8Array, then
  pdf-lib's embedPng + addPage at trimW×trimH points (cover spread
  is its own landscape page at the end).

UI:
- Export PDF… button in the Pages overview toolbar with a status
  line that surfaces progress + warnings (failed image embeds,
  per-page render errors).
- Filename = `<title-slug>.pdf`.

Known v1 limitations (handled in later E phases):
- Raster-only: every page is a PNG inside the PDF. Real PDF text
  for embeddable-allowlist fonts comes in E3.
- No bleed-inclusive output / crop marks / page-range / two-file
  modes yet (E2).
- Linked-design layers + premium-gated content not exercised.

Co-Authored-By: Claude Opus 4.7 <[email protected]>
This commit is contained in:
thePR0M3TH3ANandClaude Opus 4.7 committed 2026-06-03 13:32:20 -04:00
1 parent 55f53aeb49
commit f4243887cc
2 files changed
+293 -1

No files matched your search

+277 -1
View File
@@ -4417,10 +4417,12 @@
<button type="button" id="bookAddPageBtn" class="primary">+ Add page</button>
<button type="button" id="bookAddTenBtn">+ Add 10 pages</button>
<button type="button" id="bookAddChapterBtn">+ Add chapter</button>
<button type="button" id="bookExportPdfBtn" title="Export this book as a PDF (interior pages + cover spread)">Export PDF…</button>
<span id="bookExportPdfStatus" class="hint" style="margin-left:6px;font-size:.74rem"></span>
</div>
</div>
<div class="deckSlideGrid" id="bookPagesGrid"></div>
<p class="hint" id="bookPagesHint" style="margin:8px 0 0">Click a designed-page tile to edit it on the canvas. Click a chapter tile to edit its markdown. Per-page Nostr publish (per-event sidecars + manifest) lands in Phase D1.</p>
<p class="hint" id="bookPagesHint" style="margin:8px 0 0">Click a designed-page tile to edit it on the canvas. Click a chapter tile to edit its markdown. PDF export is raster-only in v1 (real-text mode for embeddable fonts lands in a follow-up slice).</p>
</div>
<div id="bookCoverView" style="display:none">
<div class="bookCoverSummary" id="bookCoverSummary"></div>
@@ -25047,6 +25049,279 @@
// Slide / Custom Art / Pixel Art standalone: one flat rectangle of
// layers on a background colour. Slides share the customart payload
// shape under `state.customArt`.
// ---- Book PDF export (Phase E1) -------------------------------------
// pdf-lib lazy-load. Vendored as ~513 KB so we don't pay the
// cost on every page-load; the export modal triggers this.
let _pdfLibPromise = null;
function loadPdfLibOnce() {
if (window.PDFLib) return Promise.resolve(window.PDFLib);
if (_pdfLibPromise) return _pdfLibPromise;
_pdfLibPromise = loadScriptOnce('vendor/pdf-lib.min.js').then(() => {
if (!window.PDFLib) throw new Error('pdf-lib loaded but PDFLib global is missing');
return window.PDFLib;
}).catch(err => {
_pdfLibPromise = null;
throw err;
});
return _pdfLibPromise;
}
// Render one designed page (master underlay + page layers) to a
// print-DPI canvas. Token substitution is applied to master text
// layers so `{page-number}` etc resolve correctly per page.
async function renderBookDesignedPageToCanvas(entry, dpi, ctxToken) {
const book = state.book || {};
const spec = book.spec || {};
const trimW = Number(spec.trimW) || 6;
const trimH = Number(spec.trimH) || 9;
const canvas = document.createElement('canvas');
const ctx = setupExportCanvas(canvas, trimW, trimH, dpi);
const bg = entry.background || spec.background || '#ffffff';
if (bg && bg.toLowerCase() !== '#ffffff' && bg.toLowerCase() !== '#fff') {
ctx.fillStyle = bg;
ctx.fillRect(0, 0, canvas.width, canvas.height);
}
// Page coords on the editor have x/y relative to the bleed-
// inclusive artboard origin; for PDF export at trim-only size
// we offset by -bleed so the page renders at (0,0).
const bleed = Number(spec.bleed) || 0;
const offX = -bleed * dpi;
const offY = -bleed * dpi;
// Master underlay first.
const masterId = effectiveMasterIdForPage(entry);
const masterTarget = masterId ? `book-master:${masterId}` : null;
const masterLayers = masterTarget
? (state.layers || [])
.filter(l => l.target === masterTarget && !l.hidden
&& !(l.type === 'shape' && l.shape?.kind === 'text-frame'))
.slice().sort((a, b) => (a.z || 0) - (b.z || 0))
: [];
const subMaster = applyBookTokensToLayers(masterLayers, ctxToken);
for (const l of subMaster) await drawLayerToCanvas(ctx, l, dpi, offX, offY);
// Page layers on top.
const pageTarget = `book-page:${entry.id}`;
const pageLayers = (state.layers || [])
.filter(l => l.target === pageTarget && !l.hidden)
.slice().sort((a, b) => (a.z || 0) - (b.z || 0));
const subPage = applyBookTokensToLayers(pageLayers, ctxToken);
for (const l of subPage) await drawLayerToCanvas(ctx, l, dpi, offX, offY);
return canvas;
}
// Render one physical page of a reflow segment: master underlay
// (with the text-frame outline stripped) + paginated text blocks
// drawn via fillText at the frame's coordinates.
async function renderBookReflowPhysicalPageToCanvas(entry, paginated, indexInEntry, dpi, ctxToken) {
const book = state.book || {};
const spec = book.spec || {};
const trimW = Number(spec.trimW) || 6;
const trimH = Number(spec.trimH) || 9;
const bleed = Number(spec.bleed) || 0;
const canvas = document.createElement('canvas');
const ctx = setupExportCanvas(canvas, trimW, trimH, dpi);
const bg = spec.background || '#ffffff';
if (bg && bg.toLowerCase() !== '#ffffff' && bg.toLowerCase() !== '#fff') {
ctx.fillStyle = bg;
ctx.fillRect(0, 0, canvas.width, canvas.height);
}
// Master layers (no text-frame outline).
const masterId = effectiveMasterIdForPage(entry);
if (masterId) {
const masterTarget = `book-master:${masterId}`;
const masterLayers = (state.layers || [])
.filter(l => l.target === masterTarget && !l.hidden
&& !(l.type === 'shape' && l.shape?.kind === 'text-frame'))
.slice().sort((a, b) => (a.z || 0) - (b.z || 0));
const subMaster = applyBookTokensToLayers(masterLayers, ctxToken);
const offX = -bleed * dpi;
const offY = -bleed * dpi;
for (const l of subMaster) await drawLayerToCanvas(ctx, l, dpi, offX, offY);
}
// Paint the paginated text blocks for this physical page.
const page = paginated.pages[indexInEntry];
const frame = paginated.frame;
if (!page || !frame) return canvas;
const padding = Number(frame.padding) || 0;
const innerX = (frame.x + padding) - bleed; // trim-relative inches
const innerY = (frame.y + padding) - bleed;
for (const block of (page.blocks || [])) {
const style = block.style || {};
const fontSizePt = Number(style.fontSize) || 11;
const fontPx = fontSizePt * (dpi / 72);
const family = style.fontFamily || 'Georgia, serif';
const weight = style.fontWeight || 400;
const styleAttr = style.fontStyle || 'normal';
ctx.font = `${styleAttr} ${weight} ${fontPx}px ${family}`;
ctx.fillStyle = style.color || '#111';
ctx.textBaseline = 'top';
const align = style.alignment || 'left';
ctx.textAlign = align === 'center' ? 'center' : align === 'right' ? 'right' : 'left';
const blockYPx = (innerY * dpi) + (Number(block.y) || 0) * dpi;
const marginLeftIn = Number(style.marginLeft) || 0;
const marginRightIn = Number(style.marginRight) || 0;
const xStartIn = innerX + marginLeftIn;
const lineH = fontPx * (Number(style.lineHeight) || 1.5);
for (let i = 0; i < (block.lines || []).length; i++) {
let xPx;
if (align === 'center') {
const widthIn = (frame.w - 2 * padding) - marginLeftIn - marginRightIn;
xPx = (xStartIn + widthIn / 2) * dpi;
} else if (align === 'right') {
const rightIn = innerX + (frame.w - 2 * padding) - marginRightIn;
xPx = rightIn * dpi;
} else {
xPx = xStartIn * dpi;
}
ctx.fillText(block.lines[i], xPx, blockYPx + i * lineH);
}
}
return canvas;
}
// Render the cover spread to one wide canvas. Layers carry their
// own book-cover-front / book-cover-back / book-spine targets
// already positioned in inch-space across the full spread.
async function renderBookCoverToCanvas(dpi) {
const spec = state.book?.spec || {};
const trimW = Number(spec.trimW) || 6;
const trimH = Number(spec.trimH) || 9;
const bleed = Number(spec.bleed) || 0;
const hasBack = !!spec.coverHasBack;
const hasSpine = !!spec.coverHasSpine;
const spineW = hasSpine ? bookSpineWidthInches() : 0;
const spreadW = (hasBack ? trimW : 0) + spineW + trimW + bleed * 2;
const spreadH = trimH + bleed * 2;
const canvas = document.createElement('canvas');
const ctx = setupExportCanvas(canvas, spreadW, spreadH, dpi);
const bg = spec.coverBackground || '#ffffff';
if (bg && bg.toLowerCase() !== '#ffffff' && bg.toLowerCase() !== '#fff') {
ctx.fillStyle = bg;
ctx.fillRect(0, 0, canvas.width, canvas.height);
}
const coverLayers = (state.layers || [])
.filter(l => /^book-cover-|^book-spine$/.test(String(l.target || '')) && !l.hidden)
.slice().sort((a, b) => (a.z || 0) - (b.z || 0));
for (const l of coverLayers) await drawLayerToCanvas(ctx, l, dpi);
return canvas;
}
// Helper: canvas → PNG Uint8Array (pdf-lib's embedPng wants raw bytes).
function _canvasToPngBytes(canvas) {
return new Promise((resolve, reject) => {
canvas.toBlob(async (blob) => {
if (!blob) return reject(new Error('Canvas toBlob returned null'));
try {
const buf = await blob.arrayBuffer();
resolve(new Uint8Array(buf));
} catch (err) { reject(err); }
}, 'image/png');
});
}
// Phase E1 orchestrator. Walks the physical-page sequence, renders
// each to canvas, embeds as PDF pages. Returns { blob, warnings }.
async function exportBookToPdf(opts = {}) {
const PDFLib = await loadPdfLibOnce();
const book = state.book;
if (!book) throw new Error('No book to export.');
const dpi = Math.max(72, Math.min(450, Number(opts.dpi) || 200));
const includeCover = opts.includeCover !== false;
const seq = enumerateBookPhysicalPages(book);
if (!seq.length && !(includeCover && book.cover)) {
throw new Error('Nothing to export — add at least one page or chapter first.');
}
const pdfDoc = await PDFLib.PDFDocument.create();
const warnings = [];
_exportFailedImages.clear();
// Per physical page.
const reflowCache = new Map(); // entryId → paginateChapter result
const spec = book.spec || {};
const trimWpt = (Number(spec.trimW) || 6) * 72;
const trimHpt = (Number(spec.trimH) || 9) * 72;
for (const slot of seq) {
const entry = book.pages.find(p => p.id === slot.entryId);
if (!entry) continue;
const ctxToken = bookTokenContext(book, slot.entryId, slot.indexInEntry);
let canvas;
try {
if (slot.type === 'reflow') {
let pag = reflowCache.get(slot.entryId);
if (!pag) { pag = paginateChapter(entry, book); reflowCache.set(slot.entryId, pag); }
canvas = await renderBookReflowPhysicalPageToCanvas(entry, pag, slot.indexInEntry, dpi, ctxToken);
} else {
canvas = await renderBookDesignedPageToCanvas(entry, dpi, ctxToken);
}
} catch (err) {
warnings.push(`Page ${(slot.pageNumber != null) ? slot.pageNumber : '?'}: ${err.message || err}`);
continue;
}
const png = await _canvasToPngBytes(canvas);
const embedded = await pdfDoc.embedPng(png);
const page = pdfDoc.addPage([trimWpt, trimHpt]);
page.drawImage(embedded, { x: 0, y: 0, width: trimWpt, height: trimHpt });
}
// Cover spread on its own landscape page at the end.
if (includeCover && book.cover) {
try {
const coverCanvas = await renderBookCoverToCanvas(dpi);
const png = await _canvasToPngBytes(coverCanvas);
const embedded = await pdfDoc.embedPng(png);
const spec2 = book.spec || {};
const trimW = Number(spec2.trimW) || 6;
const trimH = Number(spec2.trimH) || 9;
const bleed = Number(spec2.bleed) || 0;
const hasBack = !!spec2.coverHasBack;
const hasSpine = !!spec2.coverHasSpine;
const spineW = hasSpine ? bookSpineWidthInches() : 0;
const spreadW = (hasBack ? trimW : 0) + spineW + trimW + bleed * 2;
const spreadH = trimH + bleed * 2;
const page = pdfDoc.addPage([spreadW * 72, spreadH * 72]);
page.drawImage(embedded, { x: 0, y: 0, width: spreadW * 72, height: spreadH * 72 });
} catch (err) {
warnings.push(`Cover: ${err.message || err}`);
}
}
if (_exportFailedImages.size) {
warnings.push(`${_exportFailedImages.size} image${_exportFailedImages.size === 1 ? '' : 's'} could not be embedded (CORS / missing).`);
}
const bytes = await pdfDoc.save();
const blob = new Blob([bytes], { type: 'application/pdf' });
return { blob, warnings };
}
async function downloadBookPdf() {
const book = state.book;
if (!book) return;
const btn = document.getElementById('bookExportPdfBtn');
const status = document.getElementById('bookExportPdfStatus');
const setStatus = (text, kind) => {
if (!status) return;
status.textContent = text || '';
status.style.color = kind === 'error' ? '#b91c1c'
: kind === 'ok' ? '#15803d' : 'var(--muted)';
};
if (btn) btn.disabled = true;
setStatus('Rendering PDF…');
try {
const { blob, warnings } = await exportBookToPdf();
const a = document.createElement('a');
const url = URL.createObjectURL(blob);
const slug = (state.meta?.title || 'book')
.toLowerCase().replace(/[^a-z0-9]+/g, '-').replace(/^-+|-+$/g, '').slice(0, 64);
a.href = url;
a.download = `${slug || 'book'}.pdf`;
document.body.appendChild(a);
a.click();
document.body.removeChild(a);
setTimeout(() => URL.revokeObjectURL(url), 4000);
if (warnings.length) {
setStatus(`Exported with ${warnings.length} warning${warnings.length === 1 ? '' : 's'}: ${warnings[0]}`, 'error');
} else {
setStatus(`Exported ${(blob.size / 1024).toFixed(0)} KB.`, 'ok');
}
} catch (err) {
console.warn('PDF export failed', err);
setStatus('Export failed: ' + (err?.message || 'unknown'), 'error');
} finally {
if (btn) btn.disabled = false;
}
}
// ---- end Book PDF export --------------------------------------------
async function renderCustomArtToExportCanvas(dpi) {
const ca = state.customArt || { width: 1920, height: 1080, background: '#ffffff' };
const wIn = (Number(ca.width) || 1920) / 96;
@@ -34461,6 +34736,7 @@
on('bookViewTogglePages', 'click', () => setBookSubView('pages'));
on('bookViewToggleCover', 'click', () => setBookSubView('cover'));
on('bookCoverEditBtn', 'click', openBookCover);
on('bookExportPdfBtn', 'click', downloadBookPdf);
on('bookPageEditBack', 'click', () => {
// Same back-button serves per-page, per-master, and cover modes.
if (state.book?.editingMasterId) closeBookMaster();
+16
View File
@@ -0,0 +1,16 @@
!function(t,e){"object"==typeof exports&&"undefined"!=typeof module?e(exports):"function"==typeof define&&define.amd?define(["exports"],e):e((t=t||self).PDFLib={})}(this,(function(t){"use strict";
/*! *****************************************************************************
Copyright (c) Microsoft Corporation. All rights reserved.
Licensed under the Apache License, Version 2.0 (the "License"); you may not use
this file except in compliance with the License. You may obtain a copy of the
License at http://www.apache.org/licenses/LICENSE-2.0
THIS CODE IS PROVIDED ON AN *AS IS* BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
KIND, EITHER EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION ANY IMPLIED
WARRANTIES OR CONDITIONS OF TITLE, FITNESS FOR A PARTICULAR PURPOSE,
MERCHANTABLITY OR NON-INFRINGEMENT.
See the Apache Version 2.0 License for specific language governing permissions
and limitations under the License.
***************************************************************************** */var e=function(t,n){return(e=Object.setPrototypeOf||{__proto__:[]}instanceof Array&&function(t,e){t.__proto__=e}||function(t,e){for(var n in e)e.hasOwnProperty(n)&&(t[n]=e[n])})(t,n)};function n(t,n){function r(){this.constructor=t}e(t,n),t.prototype=null===n?Object.create(n):(r.prototype=n.prototype,new r)}var r=function(){return(r=Object.assign||function(t){for(var e,n=1,r=arguments.length;n<r;n++)for(var i in e=arguments[n])Object.prototype.hasOwnProperty.call(e,i)&&(t[i]=e[i]);return t}).apply(this,arguments)};function i(t,e,n,r){return new(n||(n=Promise))((function(i,o){function a(t){try{l(r.next(t))}catch(t){o(t)}}function s(t){try{l(r.throw(t))}catch(t){o(t)}}function l(t){var e;t.done?i(t.value):(e=t.value,e instanceof n?e:new n((function(t){t(e)}))).then(a,s)}l((r=r.apply(t,e||[])).next())}))}function o(t,e){var n,r,i,o,a={label:0,sent:function(){if(1&i[0])throw i[1];return i[1]},trys:[],ops:[]};return o={next:s(0),throw:s(1),return:s(2)},"function"==typeof Symbol&&(o[Symbol.iterator]=function(){return this}),o;function s(o){return function(s){return function(o){if(n)throw new TypeError("Generator is already executing.");for(;a;)try{if(n=1,r&&(i=2&o[0]?r.return:o[0]?r.throw||((i=r.return)&&i.call(r),0):r.next)&&!(i=i.call(r,o[1])).done)return i;switch(r=0,i&&(o=[2&o[0],i.value]),o[0]){case 0:case 1:i=o;break;case 4:return a.label++,{value:o[1],done:!1};case 5:a.label++,r=o[1],o=[0];continue;case 7:o=a.ops.pop(),a.trys.pop();continue;default:if(!(i=a.trys,(i=i.length>0&&i[i.length-1])||6!==o[0]&&2!==o[0])){a=0;continue}if(3===o[0]&&(!i||o[1]>i[0]&&o[1]<i[3])){a.label=o[1];break}if(6===o[0]&&a.label<i[1]){a.label=i[1],i=o;break}if(i&&a.label<i[2]){a.label=i[2],a.ops.push(o);break}i[2]&&a.ops.pop(),a.trys.pop();continue}o=e.call(t,a)}catch(t){o=[6,t],r=0}finally{n=i=0}if(5&o[0])throw o[1];return{value:o[0]?o[1]:void 0,done:!0}}([o,s])}}}function a(){for(var t=0,e=0,n=arguments.length;e<n;e++)t+=arguments[e].length;var r=Array(t),i=0;for(e=0;e<n;e++)for(var o=arguments[e],a=0,s=o.length;a<s;a++,i++)r[i]=o[a];return r}for(var s="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/",l=new Uint8Array(256),h=0;h<s.length;h++)l[s.charCodeAt(h)]=h;var u,c=function(t){for(var e="",n=t.length,r=0;r<n;r+=3)e+=s[t[r]>>2],e+=s[(3&t[r])<<4|t[r+1]>>4],e+=s[(15&t[r+1])<<2|t[r+2]>>6],e+=s[63&t[r+2]];return n%3==2?e=e.substring(0,e.length-1)+"=":n%3==1&&(e=e.substring(0,e.length-2)+"=="),e},d=function(t){var e,n,r,i,o,a=.75*t.length,s=t.length,h=0;"="===t[t.length-1]&&(a--,"="===t[t.length-2]&&a--);var u=new Uint8Array(a);for(e=0;e<s;e+=4)n=l[t.charCodeAt(e)],r=l[t.charCodeAt(e+1)],i=l[t.charCodeAt(e+2)],o=l[t.charCodeAt(e+3)],u[h++]=n<<2|r>>4,u[h++]=(15&r)<<4|i>>2,u[h++]=(3&i)<<6|63&o;return u},f=/^(data)?:?([\w\/\+]+)?;?(charset=[\w-]+|base64)?.*,/i,p=function(t){var e=t.trim(),n=e.substring(0,100).match(f);if(!n)return d(e);var r=n[0],i=e.substring(r.length);return d(i)},g=function(t){return t.charCodeAt(0)},v=function(t){return t.codePointAt(0)},y=function(t,e){return x(t.toString(16),e,"0").toUpperCase()},m=function(t){return y(t,2)},b=function(t){return String.fromCharCode(t)},w=function(t){return b(parseInt(t,16))},x=function(t,e,n){for(var r="",i=0,o=e-t.length;i<o;i++)r+=n;return r+t},k=function(t,e,n){for(var r=t.length,i=0;i<r;i++)e[n++]=t.charCodeAt(i);return r},F=function(t){return t.replace(/[.*+?^${}()|[\]\\]/g,"\\$&")},S=function(t){return t.replace(/\t|\u0085|\u2028|\u2029/g," ").replace(/[\b\v]/g,"")},C=["\\n","\\f","\\r","\\u000B"],A=function(t){return/^[\n\f\r\u000B]$/.test(t)},T=function(t){return t.split(/[\n\f\r\u000B]/)},P=function(t){return t.replace(/[\n\f\r\u000B]/g," ")},z=function(t,e){var n,r=t.charCodeAt(e),i=e+1,o=1;return r>=55296&&r<=56319&&t.length>i&&(n=t.charCodeAt(i))>=56320&&n<=57343&&(o=2),[t.slice(e,e+o),o]},R=function(t){for(var e=[],n=0,r=t.length;n<r;){var i=z(t,n),o=i[0],a=i[1];e.push(o),n+=a}return e},O=function(t,e,n,r){for(var i=function(t){for(var e=C.join("|"),n=["$"],r=0,i=t.length;r<i;r++){var o=t[r];if(A(o))throw new TypeError("`wordBreak` must not include "+e);n.push(""===o?".":F(o))}var a=n.join("|");return new RegExp("("+e+")|((.*?)("+a+"))","gm")}(e),o=S(t).match(i),a="",s=0,l=[],h=function(){""!==a&&l.push(a),a="",s=0},u=0,c=o.length;u<c;u++){var d=o[u];if(A(d))h();else{var f=r(d);s+f>n&&h(),a+=d,s+=f}}return h(),l},D=/^D:(\d\d\d\d)(\d\d)?(\d\d)?(\d\d)?(\d\d)?(\d\d)?([+\-Z])?(\d\d)?'?(\d\d)?'?$/,B=function(t){var e=t.match(D);if(e){var n=e[1],r=e[2],i=void 0===r?"01":r,o=e[3],a=void 0===o?"01":o,s=e[4],l=void 0===s?"00":s,h=e[5],u=void 0===h?"00":h,c=e[6],d=void 0===c?"00":c,f=e[7],p=void 0===f?"Z":f,g=e[8],v=void 0===g?"00":g,y=e[9];return new Date(n+"-"+i+"-"+a+"T"+l+":"+u+":"+d+("Z"===p?"Z":""+p+v+":"+(void 0===y?"00":y)))}},N=function(t,e){for(var n,r,i=0;i<t.length;){var o=t.substring(i).match(e);if(!o)return{match:r,pos:i};r=o,i+=(null!==(n=o.index)&&void 0Line truncated
//# sourceMappingURL=pdf-lib.min.js.map