@vivliostyle/cli
Version:
Save the pdf file via headless browser and Vivliostyle.
1,229 lines (1,218 loc) • 38 kB
JavaScript
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