UNPKG

@vivliostyle/cli

Version:

Save the pdf file via headless browser and Vivliostyle.

1,229 lines (1,218 loc) 38 kB
import { resolveViteConfig } from "./chunk-MU7JCDMK.js"; import { buildWebPublication, cleanupWorkspace, compile, createViteServer, getSourceUrl, getViewerFullUrl, isWebPubConfig, launchPreview, loadVivliostyleConfig, mergeConfig, mergeInlineConfig, prepareThemeDirectory, resolveTaskConfig, warnDeprecatedConfig } from "./chunk-C4HQHRXQ.js"; import { importNodeModule } from "./chunk-ECEGM36O.js"; import { Logger, copyAssets, cwd, exec, isInContainer, isUnicodeSupported, isValidUri, pathEquals, randomBookSymbol, runExitHandlers, setupConfigFromFlags } from "./chunk-DBK27BAR.js"; import { CONTAINER_LOCAL_HOSTNAME, CONTAINER_ROOT_DIR, coreVersion } from "./chunk-OAFXM4ES.js"; import { __callDispose, __using } from "./chunk-I7BWSAN6.js"; // src/core/build.ts import { pathToFileURL as pathToFileURL2 } from "node:url"; import terminalLink2 from "terminal-link"; import upath4 from "upath"; import { build as viteBuild } from "vite"; import { cyan as cyan2, gray as gray2 } from "yoctocolors"; // src/container.ts import process2 from "node:process"; import { fileURLToPath, pathToFileURL } from "node:url"; import { x } from "tinyexec"; import upath from "upath"; function toContainerPath(urlOrAbsPath) { if (isValidUri(urlOrAbsPath)) { if (urlOrAbsPath.toLowerCase().startsWith("file")) { return pathToFileURL( upath.posix.join( CONTAINER_ROOT_DIR, upath.toUnix(fileURLToPath(urlOrAbsPath)).replace(/^\w:/, "") ) ).href; } else { return urlOrAbsPath; } } return upath.posix.join( CONTAINER_ROOT_DIR, upath.toUnix(urlOrAbsPath).replace(/^\w:/, "") ); } function collectVolumeArgs(mountPoints) { return mountPoints.filter((p, i, array) => { if (i !== array.indexOf(p)) { return false; } let parent = p; while (!pathEquals(parent, upath.dirname(parent))) { parent = upath.dirname(parent); if (array.includes(parent)) { return false; } } return true; }).map((p) => `${p}:${toContainerPath(p)}`); } async function runContainer({ image, userVolumeArgs, commandArgs, entrypoint, env, workdir }) { const { default: commandExists } = await importNodeModule("command-exists"); if (!await commandExists("docker")) { throw new Error( `Docker isn't be installed. To use this feature, you'll need to install Docker.` ); } const version = (await exec("docker", ["version", "--format", "{{.Server.Version}}"])).stdout; const [major, minor] = version.split(".").map(Number); if (major < 20 || major === 20 && minor < 10) { throw new Error( `Docker version ${version} is not supported. Please upgrade to Docker 20.10.0 or later.` ); } try { var _stack = []; try { const _2 = __using(_stack, Logger.suspendLogging("Launching docker container")); const args = [ "run", ...Logger.isInteractive ? ["-it"] : [], "--rm", ...entrypoint ? ["--entrypoint", entrypoint] : [], ...env ? env.flatMap(([k, v]) => ["-e", `${k}=${v}`]) : [], ...process2.env.DEBUG ? ["-e", `DEBUG=${process2.env.DEBUG}`] : [], ...userVolumeArgs.flatMap((arg) => ["-v", arg]), ...workdir ? ["-w", workdir] : [], image, ...commandArgs ]; Logger.debug(`docker ${args.join(" ")}`); const proc = x("docker", args, { throwOnError: true, nodeOptions: { stdio: Logger.isInteractive ? "inherit" : void 0 } }); if (Logger.isInteractive) { await proc; } else { for await (const line of proc) { Logger.log(line); } } } catch (_) { var _error = _, _hasError = true; } finally { __callDispose(_stack, _error, _hasError); } } catch (error) { throw new Error( "An error occurred on the running container. Please see logs above." ); } } async function buildPDFWithContainer({ target, config, inlineConfig }) { const sourceUrl = new URL(await getSourceUrl(config)); if (sourceUrl.origin === config.rootUrl) { sourceUrl.hostname = CONTAINER_LOCAL_HOSTNAME; } const bypassedOption = { ...inlineConfig, input: { format: "webbook", entry: sourceUrl.href }, output: [ { ...target, path: toContainerPath(target.path) } ], host: CONTAINER_LOCAL_HOSTNAME }; await runContainer({ image: config.image, userVolumeArgs: collectVolumeArgs([ ...typeof config.serverRootDir === "string" ? [config.serverRootDir] : [], upath.dirname(target.path) ]), env: [["VS_CLI_BUILD_PDF_OPTIONS", JSON.stringify(bypassedOption)]], commandArgs: ["build"], workdir: typeof config.serverRootDir === "string" ? toContainerPath(config.serverRootDir) : void 0 }); return target.path; } // src/output/pdf.ts import fs3 from "node:fs"; import { URL as URL2 } from "node:url"; import terminalLink from "terminal-link"; import upath3 from "upath"; import { cyan, gray, green, red } from "yoctocolors"; // src/output/pdf-postprocess.ts import decamelize from "decamelize"; import fs2 from "node:fs"; import os from "node:os"; import upath2 from "upath"; import { v1 as uuid } from "uuid"; // src/output/pdf-stream.ts var SRGB_MAX = 1e4; var CMYK_MAX = 1e4; function* tokenize(content) { let i = 0; const len = content.length; while (i < len) { while (i < len && /\s/.test(content[i])) i++; if (i >= len) break; const c = content[i]; if (c === "%") { const start = i; while (i < len && content[i] !== "\n" && content[i] !== "\r") i++; yield { type: "other", raw: content.slice(start, i) }; continue; } if (c === "(") { let depth = 1; let str = "("; i++; while (i < len && depth > 0) { if (content[i] === "\\" && i + 1 < len) { str += content[i] + content[i + 1]; i += 2; } else { if (content[i] === "(") depth++; else if (content[i] === ")") depth--; str += content[i]; i++; } } yield { type: "other", raw: str }; continue; } if (c === "<" && content[i + 1] !== "<") { let str = "<"; i++; while (i < len && content[i] !== ">") { str += content[i]; i++; } if (i < len) { str += ">"; i++; } yield { type: "other", raw: str }; continue; } if (c === "[" || c === "]" || c === "{" || c === "}") { yield { type: "other", raw: c }; i++; continue; } if (c === "<" && content[i + 1] === "<") { yield { type: "other", raw: "<<" }; i += 2; continue; } if (c === ">" && content[i + 1] === ">") { yield { type: "other", raw: ">>" }; i += 2; continue; } if (c === "/") { let name = "/"; i++; while (i < len && /[^\s\[\]()<>{}/%]/.test(content[i])) { name += content[i]; i++; } yield { type: "other", raw: name }; continue; } let token = ""; while (i < len && /[^\s\[\]()<>{}/%]/.test(content[i])) { token += content[i]; i++; } if (/^[+-]?(\d+\.?\d*|\.\d+)$/.test(token)) { yield { type: "number", value: parseFloat(token), raw: token }; } else if (token === "ID") { yield { type: "operator", value: "ID", raw: "ID" }; const dataStart = i; while (i < len) { if (/\s/.test(content[i]) && content[i + 1] === "E" && content[i + 2] === "I" && (i + 3 >= len || /\s/.test(content[i + 3]))) { yield { type: "other", raw: content.slice(dataStart, i + 1) }; yield { type: "operator", value: "EI", raw: "EI" }; i += 3; break; } i++; } } else if (token.length > 0) { yield { type: "operator", value: token, raw: token }; } } } function formatRgbKey(r, g, b) { const ri = Math.round(r * SRGB_MAX); const gi = Math.round(g * SRGB_MAX); const bi = Math.round(b * SRGB_MAX); return JSON.stringify([ri, gi, bi]); } function formatRgbKeyForWarning(r, g, b) { const ri = Math.round(r * SRGB_MAX); const gi = Math.round(g * SRGB_MAX); const bi = Math.round(b * SRGB_MAX); return JSON.stringify({ r: ri, g: gi, b: bi }); } function convertStreamColors(content, colorMap, warnUnmapped, warnedColors) { const result = []; const pendingNumbers = []; const flushPendingNumbers = () => { for (const num of pendingNumbers) { result.push(num.raw); } pendingNumbers.length = 0; }; for (const token of tokenize(content)) { if (token.type === "number") { pendingNumbers.push({ value: token.value, raw: token.raw }); } else if (token.type === "operator") { const op = token.value; const cmykOp = op === "rg" ? "k" : op === "RG" ? "K" : null; if (cmykOp && pendingNumbers.length >= 3) { const b = pendingNumbers.pop(); const g = pendingNumbers.pop(); const r = pendingNumbers.pop(); flushPendingNumbers(); const key = formatRgbKey(r.value, g.value, b.value); const cmyk = colorMap[key]; if (cmyk) { const c = (cmyk.c / CMYK_MAX).toString(); const m = (cmyk.m / CMYK_MAX).toString(); const y = (cmyk.y / CMYK_MAX).toString(); const k = (cmyk.k / CMYK_MAX).toString(); result.push(`${c} ${m} ${y} ${k} ${cmykOp}`); } else { result.push(r.raw, g.raw, b.raw, token.raw); if (warnUnmapped) { const warnKey = formatRgbKeyForWarning(r.value, g.value, b.value); if (!warnedColors.has(warnKey)) { warnedColors.add(warnKey); Logger.logWarn(`RGB color not mapped to CMYK: ${warnKey}`); } } } } else { flushPendingNumbers(); result.push(token.raw); } } else { flushPendingNumbers(); result.push(token.raw); } } flushPendingNumbers(); return result.join(" "); } // src/output/cmyk.ts function disposable(obj) { return Object.assign(obj, { [Symbol.dispose]() { obj.destroy(); } }); } function processStream(stream, colorMap, warnUnmapped, warnedColors, mupdf) { const buffer = stream.readStream(); const content = buffer.asString(); const converted = convertStreamColors( content, colorMap, warnUnmapped, warnedColors ); stream.writeStream(new mupdf.Buffer(converted)); } function processFormXObjects(resources, colorMap, warnUnmapped, warnedColors, mupdf, processed) { const xobjects = resources.get("XObject"); if (!xobjects || !xobjects.isDictionary()) { return; } xobjects.forEach((xobj) => { if (!xobj || !xobj.isStream()) { return; } const objNum = xobj.asIndirect(); if (objNum && processed.has(objNum)) { return; } if (objNum) { processed.add(objNum); } const subtype = xobj.get("Subtype"); if (!subtype || subtype.toString() !== "/Form") { return; } processStream(xobj, colorMap, warnUnmapped, warnedColors, mupdf); const nestedResources = xobj.get("Resources"); if (nestedResources && nestedResources.isDictionary()) { processFormXObjects( nestedResources, colorMap, warnUnmapped, warnedColors, mupdf, processed ); } }); } function processContents(contents, colorMap, warnUnmapped, warnedColors, mupdf) { if (contents.isArray()) { for (let i = 0; i < contents.length; i++) { const streamObj = contents.get(i); if (streamObj && streamObj.isStream()) { processStream(streamObj, colorMap, warnUnmapped, warnedColors, mupdf); } } } else if (contents.isStream()) { processStream(contents, colorMap, warnUnmapped, warnedColors, mupdf); } } async function convertCmykColors({ pdf, colorMap, warnUnmapped }) { var _stack = []; try { const mupdf = await importNodeModule("mupdf"); const warnedColors = /* @__PURE__ */ new Set(); const processedXObjects = /* @__PURE__ */ new Set(); const doc = __using(_stack, disposable( mupdf.PDFDocument.openDocument( pdf, "application/pdf" ) )); const pageCount = doc.countPages(); for (let i = 0; i < pageCount; i++) { const page = doc.loadPage(i); const pageObj = page.getObject().resolve(); const contents = pageObj.get("Contents"); if (contents) { processContents(contents, colorMap, warnUnmapped, warnedColors, mupdf); } const resources = pageObj.get("Resources"); if (resources && resources.isDictionary()) { processFormXObjects( resources, colorMap, warnUnmapped, warnedColors, mupdf, processedXObjects ); } const annots = pageObj.get("Annots"); if (!annots?.isArray()) { continue; } for (let j = 0; j < annots.length; j++) { const annot = annots.get(j); if (!annot) { continue; } const ap = annot.resolve().get("AP"); if (!ap?.isDictionary()) { continue; } const n = ap.get("N"); if (!n) { continue; } if (n.isStream()) { processStream(n, colorMap, warnUnmapped, warnedColors, mupdf); } else if (n.isDictionary()) { n.forEach((val) => { if (val?.isStream()) { processStream(val, colorMap, warnUnmapped, warnedColors, mupdf); } }); } } } const outputBuffer = __using(_stack, disposable(doc.saveToBuffer("compress"))); return new Uint8Array(outputBuffer.asUint8Array()); } catch (_) { var _error = _, _hasError = true; } finally { __callDispose(_stack, _error, _hasError); } } // src/output/image.ts import fs from "node:fs"; function disposable2(obj) { return Object.assign(obj, { [Symbol.dispose]() { obj.destroy(); } }); } function imagesEqual(a, b) { if (a.getWidth() !== b.getWidth() || a.getHeight() !== b.getHeight()) { return false; } const pixmapA = a.toPixmap(); const pixmapB = b.toPixmap(); const typeA = pixmapA.getColorSpace(); const typeB = pixmapB.getColorSpace(); if (typeA === null || typeB === null || !(typeA.isRGB() && typeB.isRGB() || typeA.isCMYK() && typeB.isCMYK() || typeA.isGray() && typeB.isGray())) { return false; } const pixelsA = pixmapA.getPixels(); const pixelsB = pixmapB.getPixels(); return pixelsA.length === pixelsB.length && Buffer.compare(Buffer.from(pixelsA), Buffer.from(pixelsB)) === 0; } function replaceImagesInDocument(doc, imagePairs) { let replaced = 0; let total = 0; const pageCount = doc.countPages(); for (let i = 0; i < pageCount; i++) { const page = doc.loadPage(i); const pageObj = page.getObject().resolve(); const res = pageObj.get("Resources"); if (!res || !res.isDictionary()) continue; const xobjects = res.get("XObject"); if (!xobjects || !xobjects.isDictionary()) continue; const entries = []; xobjects.forEach((value, key) => { entries.push({ key, value }); }); for (const { key, value } of entries) { const resolved = value.resolve(); const subtype = resolved.get("Subtype"); if (subtype && subtype.toString() === "/Image") { total++; const pdfImage = doc.loadImage(value); for (const pair of imagePairs) { if (imagesEqual(pdfImage, pair.srcImage)) { const newImageRef = doc.addImage(pair.destImage); xobjects.put(key, newImageRef); replaced++; Logger.debug( ` Page ${i + 1}, ref "${key}": ${pair.sourcePath} -> ${pair.replacementPath}` ); break; } } } } res.put("XObject", xobjects); pageObj.put("Resources", res); } return { replaced, total }; } async function replaceImages({ pdf, replaceImageConfig }) { var _stack = []; try { if (replaceImageConfig.length === 0) { return pdf; } const mupdf = await importNodeModule("mupdf"); const imagePairs = []; for (const { source, replacement } of replaceImageConfig) { let srcImage; let destImage; try { const srcBuffer = fs.readFileSync(source); srcImage = new mupdf.Image(srcBuffer); Logger.debug( `Loaded source image: ${source} (${srcImage.getWidth()}x${srcImage.getHeight()})` ); } catch (error) { Logger.logWarn(`Failed to load source image: ${source}: ${error}`); continue; } try { const destBuffer = fs.readFileSync(replacement); destImage = new mupdf.Image(destBuffer); Logger.debug( `Loaded replacement image: ${replacement} (${destImage.getWidth()}x${destImage.getHeight()})` ); } catch (error) { Logger.logWarn( `Failed to load replacement image: ${replacement}: ${error}` ); continue; } imagePairs.push({ srcImage, destImage, sourcePath: source, replacementPath: replacement }); } if (imagePairs.length === 0) { return pdf; } const doc = __using(_stack, disposable2( mupdf.PDFDocument.openDocument( pdf, "application/pdf" ) )); const stats = replaceImagesInDocument(doc, imagePairs); Logger.debug(`Replaced ${stats.replaced} of ${stats.total} images`); const outputBuffer = __using(_stack, disposable2(doc.saveToBuffer("compress"))); return new Uint8Array(outputBuffer.asUint8Array()); } catch (_) { var _error = _, _hasError = true; } finally { __callDispose(_stack, _error, _hasError); } } // src/output/pdf-postprocess.ts var prefixes = { dcterms: "http://purl.org/dc/terms/", meta: "http://idpf.org/epub/vocab/package/meta/#" }; var metaTerms = { title: `${prefixes.dcterms}title`, creator: `${prefixes.dcterms}creator`, description: `${prefixes.dcterms}description`, subject: `${prefixes.dcterms}subject`, contributor: `${prefixes.dcterms}contributor`, language: `${prefixes.dcterms}language`, role: `${prefixes.meta}role`, created: `${prefixes.meta}created`, date: `${prefixes.meta}date` }; async function pressReadyWithContainer({ input, output, preflightOption, image }) { await runContainer({ image, entrypoint: "press-ready", userVolumeArgs: collectVolumeArgs([ upath2.dirname(input), upath2.dirname(output) ]), commandArgs: [ "build", "-i", toContainerPath(input), "-o", toContainerPath(output), ...preflightOption.map((opt) => `--${decamelize(opt, { separator: "-" })}`).filter((str) => /^[\w-]+/.test(str)) ] }); } var PostProcess = class _PostProcess { document; static async load(pdf) { const { PDFDocument } = await importNodeModule("pdf-lib"); const document2 = await PDFDocument.load(pdf, { updateMetadata: false }); return new _PostProcess(document2); } constructor(document2) { this.document = document2; } async save(output, { preflight, preflightOption, image, cmyk, cmykMap, replaceImage }) { let pdf = await this.document.save(); if (cmyk) { const mergedMap = { ...cmykMap }; for (const [rgb, cmykValue] of cmyk.overrideMap) { const key = JSON.stringify([rgb.r, rgb.g, rgb.b]); mergedMap[key] = cmykValue; } Logger.logInfo("Converting CMYK colors"); pdf = await convertCmykColors({ pdf, colorMap: mergedMap, warnUnmapped: cmyk.warnUnmapped }); if (cmyk.mapOutput) { const mapOutputDir = upath2.dirname(cmyk.mapOutput); fs2.mkdirSync(mapOutputDir, { recursive: true }); await fs2.promises.writeFile( cmyk.mapOutput, JSON.stringify(mergedMap, null, 2) ); Logger.logInfo(`CMYK color map saved to ${cmyk.mapOutput}`); } } if (replaceImage.length > 0) { Logger.logInfo("Replacing images"); pdf = await replaceImages({ pdf, replaceImageConfig: replaceImage }); } if (preflight) { const input = upath2.join(os.tmpdir(), `vivliostyle-cli-${uuid()}.pdf`); await fs2.promises.writeFile(input, pdf); if (preflight === "press-ready-local" || preflight === "press-ready" && isInContainer()) { var _stack = []; try { const _3 = __using(_stack, Logger.suspendLogging("Running press-ready")); const { build: build2 } = await importNodeModule("press-ready"); await build2({ ...preflightOption.reduce((acc, opt) => { const optName = decamelize(opt, { separator: "-" }); return optName.startsWith("no-") ? { ...acc, [optName.slice(3)]: false } : { ...acc, [optName]: true }; }, {}), input, output }); } catch (_) { var _error = _, _hasError = true; } finally { __callDispose(_stack, _error, _hasError); } } else if (preflight === "press-ready") { var _stack2 = []; try { const _3 = __using(_stack2, Logger.suspendLogging("Running press-ready")); await pressReadyWithContainer({ input, output, preflightOption, image }); } catch (_2) { var _error2 = _2, _hasError2 = true; } finally { __callDispose(_stack2, _error2, _hasError2); } } } else { await fs2.promises.writeFile(output, pdf); } } async metadata(tree, { pageProgression, browserVersion, viewerCoreVersion, disableCreatorOption } = {}) { const { ReadingDirection } = await importNodeModule("pdf-lib"); const title = tree[metaTerms.title]?.[0].v; if (title) { this.document.setTitle(title); } const author = tree[metaTerms.creator]?.map((item) => item.v)?.join("; "); if (author) { this.document.setAuthor(author); } const subject = tree[metaTerms.description]?.[0].v; if (subject) { this.document.setSubject(subject); } const keywords = tree[metaTerms.subject]?.map((item) => item.v); if (keywords) { this.document.setKeywords(keywords); } let creatorOpt = `Vivliostyle.js ${viewerCoreVersion ?? coreVersion}`; if (browserVersion) { creatorOpt += `; ${browserVersion}`; } this.document.setCreator( disableCreatorOption ? "Vivliostyle" : `Vivliostyle (${creatorOpt})` ); const language = tree[metaTerms.language]?.[0].v; if (language) { this.document.setLanguage(language); } const creation = (tree[metaTerms.created] || tree[metaTerms.date])?.[0].v; const creationDate = creation && new Date(creation); if (creationDate) { this.document.setCreationDate(creationDate); } if (pageProgression === "rtl") { const viewerPrefs = this.document.catalog.getOrCreateViewerPreferences(); viewerPrefs.setReadingDirection(ReadingDirection.R2L); } } async toc(items) { const { PDFDict, PDFHexString, PDFName, PDFNumber } = await importNodeModule("pdf-lib"); if (!items || !items.length) { return; } const addRefs = (items2, parentRef) => items2.map((item) => { const ref = this.document.context.nextRef(); return { ...item, parentRef, ref, children: addRefs(item.children, ref) }; }); const countAll = (items2) => items2.reduce((sum, item) => sum + countAll(item.children), items2.length); const addObjectsToPDF = (items2) => { for (const [i, item] of items2.entries()) { const child = PDFDict.withContext(this.document.context); child.set(PDFName.of("Title"), PDFHexString.fromText(item.title)); child.set(PDFName.of("Dest"), PDFName.of(item.id)); child.set(PDFName.of("Parent"), item.parentRef); const prev = items2[i - 1]; if (prev) { child.set(PDFName.of("Prev"), prev.ref); } const next = items2[i + 1]; if (next) { child.set(PDFName.of("Next"), next.ref); } if (item.children.length) { child.set(PDFName.of("First"), item.children[0].ref); child.set( PDFName.of("Last"), item.children[item.children.length - 1].ref ); child.set(PDFName.of("Count"), PDFNumber.of(countAll(item.children))); } this.document.context.assign(item.ref, child); addObjectsToPDF(item.children); } }; const outlineRef = this.document.context.nextRef(); const itemsWithRefs = addRefs(items, outlineRef); addObjectsToPDF(itemsWithRefs); const outline = PDFDict.withContext(this.document.context); outline.set(PDFName.of("First"), itemsWithRefs[0].ref); outline.set( PDFName.of("Last"), itemsWithRefs[itemsWithRefs.length - 1].ref ); outline.set(PDFName.of("Count"), PDFNumber.of(countAll(itemsWithRefs))); this.document.context.assign(outlineRef, outline); this.document.catalog.set(PDFName.of("Outlines"), outlineRef); } async setPageBoxes(pageSizeData) { if (pageSizeData.length + 1 === this.document.getPageCount()) { this.document.removePage(pageSizeData.length); } if (pageSizeData.length !== this.document.getPageCount()) { return; } for (let i = 0; i < pageSizeData.length; i++) { const page = this.document.getPage(i); const sizeData = pageSizeData[i]; if (!sizeData.mediaWidth || !sizeData.mediaHeight || isNaN(sizeData.bleedOffset) || isNaN(sizeData.bleedSize)) { continue; } const yOffset = page.getHeight() - sizeData.mediaHeight; page.setMediaBox(0, yOffset, sizeData.mediaWidth, sizeData.mediaHeight); if (!sizeData.bleedOffset && !sizeData.bleedSize) { continue; } page.setBleedBox( sizeData.bleedOffset, yOffset + sizeData.bleedOffset, sizeData.mediaWidth - sizeData.bleedOffset * 2, sizeData.mediaHeight - sizeData.bleedOffset * 2 ); const trimOffset = sizeData.bleedOffset + sizeData.bleedSize; page.setTrimBox( trimOffset, yOffset + trimOffset, sizeData.mediaWidth - trimOffset * 2, sizeData.mediaHeight - trimOffset * 2 ); } } }; // src/output/pdf.ts async function buildPDF({ target, config }) { Logger.logUpdate(`Launching PDF build environment`); const viewerFullUrl = await getViewerFullUrl(config); Logger.debug("viewerFullUrl", viewerFullUrl); let lastEntry; function stringifyEntry(entry) { const formattedSourcePath = cyan( entry.source.type === "file" ? upath3.relative(config.entryContextDir, entry.source.pathname) : entry.source.href ); return `${terminalLink( formattedSourcePath, entry.source.type === "file" ? `file://${entry.source.pathname}` : entry.source.href, { fallback: () => formattedSourcePath } )} ${entry.title ? gray(entry.title) : ""}`; } function handleEntry(response) { const entry = config.entries.find((entry2) => { if (!("source" in entry2)) { return false; } const url = new URL2(response.url()); return url.protocol === "file:" ? pathEquals(entry2.target, url.pathname) : pathEquals( upath3.relative(config.workspaceDir, entry2.target), url.pathname.substring(1) ); }); if (entry) { if (!lastEntry) { lastEntry = entry; Logger.logUpdate(stringifyEntry(entry)); return; } Logger.logSuccess(stringifyEntry(lastEntry)); Logger.startLogging(stringifyEntry(entry)); lastEntry = entry; } } const { browser, page } = await launchPreview({ mode: "build", url: viewerFullUrl, config, onBrowserOpen: () => { Logger.logUpdate("Building pages"); }, onPageOpen: async (page2) => { page2.on("pageerror", (error) => { Logger.logError(red(error.message)); }); page2.on("console", (msg) => { switch (msg.type()) { case "error": if (/\/vivliostyle-viewer\.js$/.test(msg.location().url ?? "")) { Logger.logError(msg.text()); throw msg.text(); } return; case "debug": if (/time slice/.test(msg.text())) { return; } break; } if (msg.type() === "error") { Logger.logVerbose(red("console.error()"), msg.text()); } else { Logger.logVerbose(gray(`console.${msg.type()}()`), msg.text()); } }); page2.on("response", (response) => { Logger.debug( gray("viewer:response"), green(response.status().toString()), response.url() ); handleEntry(response); if (400 > response.status() && 200 <= response.status()) return; if (response.url().startsWith("file://") && response.ok()) return; Logger.logError(red(`${response.status()}`), response.url()); }); await page2.setDefaultTimeout(config.timeout); } }); const browserVersion = await browser.version(); Logger.debug(green("success"), `browserVersion=${browserVersion}`); let remainTime = config.timeout; const startTime = Date.now(); await page.waitForNetworkIdle(); await page.waitForFunction(() => !!window.coreViewer); const { protocol } = browser; if (protocol === "cdp") { await page.emulateMediaType("print"); } await page.waitForFunction( /* v8 ignore next */ () => window.coreViewer.readyState === "complete", { polling: 1e3 } ); if (lastEntry) { Logger.logSuccess(stringifyEntry(lastEntry)); } const pageProgression = await page.evaluate( () => ( /* v8 ignore next 5 */ document.querySelector("#vivliostyle-viewer-viewport")?.getAttribute("data-vivliostyle-page-progression") === "rtl" ? "rtl" : "ltr" ) ); const viewerCoreVersion = await page.evaluate( () => ( /* v8 ignore next 3 */ document.querySelector("#vivliostyle-menu_settings .version")?.textContent?.replace(/^.*?: (\d[-+.\w]+).*$/, "$1") ) ); const metadata = await loadMetadata(page); const toc = await loadTOC(page); const pageSizeData = await loadPageSizeData(page); const cmykMap = target.cmyk ? await loadCmykMap(page) : {}; remainTime -= Date.now() - startTime; if (remainTime <= 0) { throw new Error("Typesetting process timed out"); } Logger.debug("Remaining timeout:", remainTime); Logger.logUpdate("Building PDF"); const dimensionSizeForWebDriverBiDi = parseInt(process.env.VS_CLI_PDF_BUILD_PDF_PAGE_SIZE || "", 10) || 3780; const pdf = await page.pdf({ margin: { top: 0, bottom: 0, right: 0, left: 0 }, printBackground: true, tagged: true, // timeout: remainTime, ...protocol === "webDriverBiDi" ? { width: dimensionSizeForWebDriverBiDi, height: dimensionSizeForWebDriverBiDi } : { preferCSSPageSize: true } }); await browser.close(); Logger.logUpdate("Processing PDF"); fs3.mkdirSync(upath3.dirname(target.path), { recursive: true }); const post = await PostProcess.load(pdf); await post.metadata(metadata, { pageProgression, browserVersion, viewerCoreVersion, // If custom viewer is set and its version info is not available, // there is no guarantee that the default creator option is correct. disableCreatorOption: !!config.viewer && !viewerCoreVersion }); await post.toc(toc); await post.setPageBoxes(pageSizeData); await post.save(target.path, { preflight: target.preflight, preflightOption: target.preflightOption, image: config.image, cmyk: target.cmyk, cmykMap, replaceImage: target.replaceImage }); return target.path; } async function loadMetadata(page) { return page.evaluate(() => window.coreViewer.getMetadata()); } async function loadTOC(page) { return page.evaluate( () => new Promise((resolve) => { function listener(payload) { if (payload.a !== "toc") { return; } window.coreViewer.removeListener("done", listener); window.coreViewer.showTOC(false); resolve(window.coreViewer.getTOC()); } window.coreViewer.addListener("done", listener); window.coreViewer.showTOC(true); }) ); } async function loadPageSizeData(page) { return page.evaluate(() => { const sizeData = []; const pageContainers = document.querySelectorAll( "#vivliostyle-viewer-viewport > div > div > div[data-vivliostyle-page-container]" ); for (const pageContainer of pageContainers) { const bleedBox = pageContainer.querySelector( "div[data-vivliostyle-bleed-box]" ); sizeData.push({ mediaWidth: parseFloat(pageContainer.style.width) * 0.75, mediaHeight: parseFloat(pageContainer.style.height) * 0.75, bleedOffset: parseFloat(bleedBox?.style.left) * 0.75, bleedSize: parseFloat(bleedBox?.style.paddingLeft) * 0.75 }); } return sizeData; }); } async function loadCmykMap(page) { return page.evaluate(() => window.coreViewer.getCmykMap?.() ?? {}); } // src/core/build.ts async function build(inlineConfig, { containerForkMode = false } = {}) { Logger.setLogOptions(inlineConfig); if (containerForkMode) { Logger.setLogPrefix(gray2("[Docker]")); } Logger.debug("build > inlineConfig %O", inlineConfig); let vivliostyleConfig = await loadVivliostyleConfig(inlineConfig) ?? setupConfigFromFlags(inlineConfig); warnDeprecatedConfig(vivliostyleConfig); vivliostyleConfig = mergeInlineConfig(vivliostyleConfig, { ...inlineConfig, quick: false }); const { inlineOptions } = vivliostyleConfig; Logger.debug("build > vivliostyleConfig %O", vivliostyleConfig); for (let [i, task] of vivliostyleConfig.tasks.entries()) { var _stack2 = []; try { const _3 = __using(_stack2, Logger.startLogging("Start building")); let config = resolveTaskConfig(task, inlineOptions); Logger.debug("build > config %O", config); const viteConfig = await resolveViteConfig({ ...config, mode: "build" }); let server; if (!containerForkMode) { Logger.debug("build > viteConfig.configFile %s", viteConfig.configFile); if (viteConfig.configFile && typeof config.serverRootDir === "string") { var _stack = []; try { const _4 = __using(_stack, Logger.suspendLogging("Building Vite project")); await viteBuild({ configFile: viteConfig.configFile, root: config.serverRootDir }); } catch (_) { var _error = _, _hasError = true; } finally { __callDispose(_stack, _error, _hasError); } } if (!inlineConfig.disableServerStartup) { server = await createViteServer({ config, viteConfig, inlineConfig, mode: "build" }); if (server.httpServer) { const addressInfo = server.httpServer.address(); if (addressInfo && typeof addressInfo !== "string") { const actualPort = addressInfo.port; vivliostyleConfig = mergeConfig(vivliostyleConfig, { temporaryFilePrefix: config.temporaryFilePrefix, server: { ...server.config.preview, port: actualPort } }); config = resolveTaskConfig( vivliostyleConfig.tasks[i], vivliostyleConfig.inlineOptions ); } } } if (isWebPubConfig(config)) { await cleanupWorkspace(config); await prepareThemeDirectory(config); await compile(config); await copyAssets(config); } } for (const target of config.outputs) { let output = null; const { format } = target; if (format === "pdf") { if (!containerForkMode && target.renderMode === "docker") { output = await buildPDFWithContainer({ target, config, inlineConfig }); } else { output = await buildPDF({ target, config }); } } else if (format === "webpub" || format === "epub") { output = await buildWebPublication({ target, config }); } if (output && !containerForkMode) { const formattedOutput = cyan2( upath4.relative(inlineConfig.cwd ?? cwd, output) ); Logger.logSuccess( `Finished building ${terminalLink2( formattedOutput, pathToFileURL2(output).href, { fallback: () => formattedOutput } )}` ); } } await server?.close(); } catch (_2) { var _error2 = _2, _hasError2 = true; } finally { __callDispose(_stack2, _error2, _hasError2); } } runExitHandlers(); if (!containerForkMode) { const num = vivliostyleConfig.tasks.flatMap((t) => t.output ?? []).length; const symbol = isUnicodeSupported ? `${num > 1 ? "\u{1F4DA}" : randomBookSymbol} ` : ""; Logger.log(`${symbol}Built successfully!`); } } export { build }; //# sourceMappingURL=chunk-ERDN47XG.js.map