Skip to content
Snippets Groups Projects

Compare revisions

Changes are shown as if the source revision was being merged into the target revision. Learn more about comparing revisions.

Source

Select target project
No results found

Target

Select target project
  • pagedjs/pagedjs-cli
  • math712b/pagedjs-cli
  • valentin.schabschneider/pagedjs-cli
3 results
Show changes
Commits on Source (10)
module.exports = {
"env": {
"commonjs": true,
"es6": true,
"node": true,
"browser": true
},
"extends": "eslint:recommended",
"globals": {
"Atomics": "readonly",
"SharedArrayBuffer": "readonly"
},
"parserOptions": {
"ecmaVersion": 2018
},
"rules": {
"linebreak-style": [
"error",
"unix"
],
"quotes": [
"warn",
"double"
],
"semi": [
"error",
"always"
],
"no-unused-vars" : ["warn"],
"no-console" : ["error", { allow: ["log", "warn", "error"] }],
"no-unused-vars": [
"error",
{ "vars": "all", "args": "none" }
],
"no-mixed-spaces-and-tabs": ["error", "smart-tabs"],
"valid-jsdoc": ["warn"]
}
};
#!/usr/bin/env node
const program = require('commander');
const ora = require('ora');
const program = require("commander");
const ora = require("ora");
const Printer = require("../");
const path = require('path');
const fs = require('fs');
const { promisify } = require('util');
const writeFileAsync = promisify(fs.writeFile);
const replaceExt = require('replace-ext');
const path = require("path");
const fs = require("fs");
// const { promisify } = require("util");
// const writeFileAsync = promisify(fs.writeFile);
const replaceExt = require("replace-ext");
program
.version(require('../package.json').version)
.arguments('[inputPath]')
.option('-i, --inputs [inputs]', 'Inputs')
.option('-o, --output [output]', 'Output')
.option('-d, --debug', 'Debug')
.option('-l, --landscape', 'Landscape printing', false)
.option('-s, --page-size [size]', 'Print to Page Size [size]')
.option('-w, --width [size]', 'Print to Page Width [width] in MM')
.option('-h --height [size]', 'Print to Page Height [weight] in MM')
// .option('-m, --page-margin [margin]', 'Print with margin [margin]')
// .option('-n, --hyphenate [lang]', 'Hyphenate with language [language], defaults to "en-us"')
// .option('-hi, --hypher_ignore [str]', 'Ignore passed element selectors, such as ".class_to_ignore, h1"')
// .option('-ho, --hypher_only [str]', 'Only hyphenate passed elements selector, such as ".hyphenate, aside"')
// .option('-e, --encoding [type]', 'Set the encoding of the input html, defaults to "utf-8"')
.option('-t, --timeout [ms]', 'Set a max timeout of [ms]')
.option('-x, --html', 'output html file')
.option('-b, --blockLocal', 'Disallow access to filesystem for local files')
.option('--outline-tags [tags]', 'Specifies that an outline should be ' +
'generated for the resulting PDF document. [tags] specifies which ' +
'HTML tags should be considered for that outline. ' +
'"h1,h2" will trigger an outline with "h1" tags as root elements ' +
'and "h2" elements as their childs.')
.version(require("../package.json").version)
.arguments("[inputPath]")
.option("-i, --inputs [inputs]", "Inputs")
.option("-o, --output [output]", "Output")
.option("-d, --debug", "Debug")
.option("-l, --landscape", "Landscape printing", false)
.option("-s, --page-size [size]", "Print to Page Size [size]")
.option("-w, --width [size]", "Print to Page Width [width] in MM")
.option("-h --height [size]", "Print to Page Height [weight] in MM")
// .option("-m, --page-margin [margin]", "Print with margin [margin]")
// .option("-n, --hyphenate [lang]", "Hyphenate with language [language], defaults to "en-us"")
// .option("-hi, --hypher_ignore [str]", "Ignore passed element selectors, such as ".class_to_ignore, h1"")
// .option("-ho, --hypher_only [str]", "Only hyphenate passed elements selector, such as ".hyphenate, aside"")
// .option("-e, --encoding [type]", "Set the encoding of the input html, defaults to "utf-8"")
.option("-t, --timeout [ms]", "Set a max timeout of [ms]")
.option("-x, --html", "output html file")
.option("-b, --blockLocal", "Disallow access to filesystem for local files")
.option("--outline-tags [tags]", "Specifies that an outline should be " +
"generated for the resulting PDF document. [tags] specifies which " +
"HTML tags should be considered for that outline. " +
"\"h1,h2\" will trigger an outline with \"h1\" tags as root elements " +
"and \"h2\" elements as their childs.")
.parse(process.argv);
......@@ -38,19 +38,17 @@ let input = program.inputs || program.args[0];
let dir = process.cwd();
let url;
let relativePath;
let allowLocal;
try {
url = new URL(input);
new URL(input);
allowLocal = false;
} catch {
} catch (error) {
relativePath = path.resolve(dir, input);
allowLocal = !program.blockLocal;
}
let output;
let tmpFile, tmpPath;
let headless = typeof program.debug === "undefined";
......@@ -64,7 +62,7 @@ if (!input) {
if (relativePath) {
if (['.html', '.xhtml'].indexOf(path.extname(relativePath)) === -1) {
if ([".html", ".xhtml"].indexOf(path.extname(relativePath)) === -1) {
console.error("Must pass a html or xhtml file as input");
return process.exit(1);
}
......@@ -80,7 +78,7 @@ if (relativePath) {
if (typeof(program.output) === "string") {
output = path.resolve(dir, program.output);
} else if (typeof(program.output) !== "undefined") {
output = './' + replaceExt(path.basename(input), '.pdf');
output = "./" + replaceExt(path.basename(input), ".pdf");
} else {
output = "output.pdf";
}
......@@ -88,7 +86,7 @@ if (typeof(program.output) === "string") {
const spinner = ora({
spinner: "circleQuarters"
})
});
if (typeof input === "string") {
......@@ -106,7 +104,7 @@ if (typeof input === "string") {
spinner.start("Rendering: Page " + (page.position + 1));
} else {
spinner.text = "Rendering: Page " + (page.position + 1)
spinner.text = "Rendering: Page " + (page.position + 1);
}
});
......@@ -125,9 +123,9 @@ if (typeof input === "string") {
let options = {};
if (program.html) {
file = await printer.html(input, options);
output = replaceExt(output, '.html');
output = replaceExt(output, ".html");
} else {
options.outlineTags = !program.outlineTags ? [] : program.outlineTags.split(',');
options.outlineTags = !program.outlineTags ? [] : program.outlineTags.split(",");
file = await printer.pdf(input, options);
}
} else {
......
This diff is collapsed.
{
"name": "pagedjs-cli",
"version": "0.0.7",
"version": "0.0.9",
"author": "Fred Chasen",
"license": "MIT",
"homepage": "https://pagedmedia.org",
......@@ -11,24 +11,25 @@
"bin": "./bin/paged",
"scripts": {
"start": "./bin/paged",
"build": "pkg ."
"build": "pkg .",
"lint": "./node_modules/.bin/eslint src bin/paged"
},
"main": "index.js",
"dependencies": {
"commander": "^2.20.0",
"express": "^4.16.4",
"hyphenopoly": "^3.0.1",
"katex": "^0.10.1",
"lodash": "^4.17.11",
"mathjax": "^2.7.5",
"node-fetch": "^2.4.1",
"ora": "^3.4.0",
"pagedjs": "^0.1.34",
"pdf-lib": "^0.6.1",
"puppeteer": "^1.15.0",
"commander": "^3.0.2",
"express": "^4.17.1",
"hyphenopoly": "^3.2.1",
"katex": "^0.11.1",
"lodash": "^4.17.15",
"mathjax": "^3.0.0",
"node-fetch": "^2.6.0",
"ora": "^4.0.2",
"pagedjs": "0.1.34",
"pdf-lib": "0.6.4",
"puppeteer": "^1.20.0",
"replace-ext": "^1.0.0"
},
"devDependencies": {
"eslint": "^5.16.0"
"eslint": "^6.5.1"
}
}
const PDFLib = require("pdf-lib");
const EventEmitter = require('events');
const EventEmitter = require("events");
const PDFDocumentWriter = require('./writer');
const PDFDocumentWriter = require("./writer");
class PostProcesser extends EventEmitter {
constructor(pdf) {
super();
if (!pdf) {
throw "Must pass a PDF Buffer to PostProcesser"
throw "Must pass a PDF Buffer to PostProcesser";
}
this.pdf = pdf
this.pdf = pdf;
this.pdfDoc = PDFLib.PDFDocumentFactory.load(pdf);
}
......@@ -58,7 +58,7 @@ class PostProcesser extends EventEmitter {
modDate: info.getMaybe("ModDate") && info.getMaybe("ModDate").string,
creator: info.getMaybe("Creator") && info.getMaybe("Creator").string,
producer: info.getMaybe("Producer") && info.getMaybe("Producer").string
}
};
}
updateInfoDict(meta) {
......@@ -100,9 +100,9 @@ class PostProcesser extends EventEmitter {
}
addXmpMetadata(meta) {
const charCodes = (str) => str.split('').map((c) => c.charCodeAt(0));
const charCodes = (str) => str.split("").map((c) => c.charCodeAt(0));
const typedArrayFor = (str) => new Uint8Array(charCodes(str));
const whitespacePadding = new Array(20).fill(' '.repeat(100)).join('\n');
const whitespacePadding = new Array(20).fill(" ".repeat(100)).join("\n");
const metadataXML = `
<?xpacket begin="" id="W5M0MpCehiHzreSzNTczkc9d"?>
<x:xmpmeta xmlns:x="adobe:ns:meta/" x:xmptk="Adobe XMP Core 5.2-c001 63.139439, 2010/09/27-13:37:26">
......@@ -124,7 +124,7 @@ class PostProcesser extends EventEmitter {
<rdf:Bag>
${meta.keywords
.map((keyword) => `<rdf:li>${keyword}</rdf:li>`)
.join('\n')}
.join("\n")}
</rdf:Bag>
</dc:subject>
</rdf:Description>
......@@ -149,8 +149,8 @@ class PostProcesser extends EventEmitter {
const metadataStreamDict = PDFLib.PDFDictionary.from(
{
Type: PDFLib.PDFName.from('Metadata'),
Subtype: PDFLib.PDFName.from('XML'),
Type: PDFLib.PDFName.from("Metadata"),
Subtype: PDFLib.PDFName.from("XML"),
Length: PDFLib.PDFNumber.fromNumber(metadataXML.length),
},
this.pdfDoc.index,
......@@ -163,8 +163,8 @@ class PostProcesser extends EventEmitter {
const metadataStreamRef = this.pdfDoc.register(metadataStream);
this.pdfDoc.catalog.set('Metadata', metadataStreamRef);
};
this.pdfDoc.catalog.set("Metadata", metadataStreamRef);
}
boxes(pages) {
const pdfPages = this.pdfDoc.getPages();
......@@ -184,10 +184,10 @@ class PostProcesser extends EventEmitter {
const rectangle = PDFLib.PDFArray.fromArray(
[
PDFLib.PDFNumber.fromNumber(boxes.crop.x * 2),
PDFLib.PDFNumber.fromNumber(boxes.crop.y * 2),
PDFLib.PDFNumber.fromNumber(boxes.crop.width),
PDFLib.PDFNumber.fromNumber(boxes.crop.height),
PDFLib.PDFNumber.fromNumber(boxes.crop.x),
PDFLib.PDFNumber.fromNumber(boxes.crop.y),
PDFLib.PDFNumber.fromNumber(boxes.crop.width + boxes.crop.x),
PDFLib.PDFNumber.fromNumber(boxes.crop.height + boxes.crop.y),
],
pdfPage.index,
);
......@@ -205,17 +205,17 @@ class PostProcesser extends EventEmitter {
/**
* Adds a table of content to the generated PDF
*
*
* Ideally this would not be required if Chromium would add this directly.
* So if these bugs are closed this can probably be removed again:
* - https://bugs.chromium.org/p/chromium/issues/detail?id=840455
* - https://github.com/GoogleChrome/puppeteer/issues/1778
*
*
* This code is heavily based on @Hopding's comment at:
* https://github.com/Hopding/pdf-lib/issues/127#issuecomment-502450179
*/
addOutline(outlineSpec) {
const outline = JSON.parse(JSON.stringify(outlineSpec))
const outline = JSON.parse(JSON.stringify(outlineSpec));
const pageRefs = [];
......@@ -234,7 +234,7 @@ class PostProcesser extends EventEmitter {
count += countOutlineLayer(outlineEntry.children);
}
return count;
}
};
const createItemsForOutlineLayer = (layer, parent) => {
layer.forEach((outlineItem, i) => {
......@@ -243,13 +243,13 @@ class PostProcesser extends EventEmitter {
const pdfItem = createOutlineItem(outlineItem, prev, next, parent);
index.assign(outlineItem.ref, pdfItem);
});
}
};
const createOutlineItem = (outlineItem, prev, next, parent) => {
if (!outlineItem.id) {
throw new Error(`Cannot generate outline item with title '${outlineItem.title} ` +
`without any target anchor. Please specify an 'id' attribute for ` +
`the relevant HTML element`);
"without any target anchor. Please specify an 'id' attribute for " +
"the relevant HTML element");
}
const item = {
Title: PDFLib.PDFString.fromString(outlineItem.title),
......@@ -277,14 +277,14 @@ class PostProcesser extends EventEmitter {
for (const child of outlineEntry.children) {
createOutlineReferences(child);
}
}
};
for (const outlineItem of outline) {
createOutlineReferences(outlineItem);
}
createItemsForOutlineLayer(outline, outlineReference);
const pdfOutline = PDFLib.PDFDictionary.from(
{
First: outline[0].ref,
......@@ -294,7 +294,7 @@ class PostProcesser extends EventEmitter {
index,
);
index.assign(outlineReference, pdfOutline);
this.pdfDoc.catalog.set('Outlines', outlineReference);
this.pdfDoc.catalog.set("Outlines", outlineReference);
}
save() {
......
const Paged = require('pagedjs');
const EventEmitter = require('events');
const puppeteer = require('puppeteer');
const util = require('util');
const fs = require('fs');
const EventEmitter = require("events");
const puppeteer = require("puppeteer");
const fetch = require("node-fetch");
const path = require('path');
const path = require("path");
let dir = process.cwd();
......@@ -14,19 +11,7 @@ let pagedjsLocation = require.resolve("pagedjs/dist/paged.polyfill.js");
let paths = pagedjsLocation.split("node_modules");
let scriptPath = paths[0] + "node_modules" + paths[paths.length-1];
const PostProcesser = require('./postprocesser');
const PDF_SETTINGS = {
printBackground: true,
displayHeaderFooter: false,
preferCSSPageSize: true,
margin: {
top: 0,
right: 0,
bottom: 0,
left: 0,
}
};
const PostProcesser = require("./postprocesser");
class Printer extends EventEmitter {
constructor(headless, allowLocal) {
......@@ -39,7 +24,7 @@ class Printer extends EventEmitter {
async setup() {
const browser = await puppeteer.launch({
headless: this.headless,
args: this.allowLocal ? ['--allow-file-access-from-files', '--disable-dev-shm-usage'] : ['--disable-dev-shm-usage'],
args: this.allowLocal ? ["--allow-file-access-from-files", "--disable-dev-shm-usage"] : ["--disable-dev-shm-usage"],
ignoreHTTPSErrors: true
});
......@@ -66,10 +51,10 @@ class Printer extends EventEmitter {
uri = new URL(input);
if (uri.protocol === "https:") {
html = await fetch(input)
.then(res => res.text())
.then(res => res.text());
}
url = input;
} catch {
} catch (error) {
let relativePath = path.resolve(dir, input);
url = "file://" + relativePath;
}
......@@ -113,15 +98,15 @@ class Printer extends EventEmitter {
path: scriptPath
});
// await page.exposeFunction('PuppeteerLogger', (msg) => {
// await page.exposeFunction("PuppeteerLogger", (msg) => {
// console.log(msg);
// });
await page.exposeFunction('onSize', (size) => {
await page.exposeFunction("onSize", (size) => {
this.emit("size", size);
});
await page.exposeFunction('onPage', (page) => {
await page.exposeFunction("onPage", (page) => {
// console.log("page", page.position + 1);
this.pages.push(page);
......@@ -129,7 +114,7 @@ class Printer extends EventEmitter {
this.emit("page", page);
});
await page.exposeFunction('onRendered', (msg, width, height, orientation) => {
await page.exposeFunction("onRendered", (msg, width, height, orientation) => {
this.emit("rendered", msg, width, height, orientation);
resolver({msg, width, height, orientation});
});
......@@ -158,7 +143,7 @@ class Printer extends EventEmitter {
x: getPointsValue(cropbox.x) - getPointsValue(mediabox.x),
y: getPointsValue(cropbox.y) - getPointsValue(mediabox.y)
}
}
};
window.onPage({ id, width, height, startToken, endToken, breakAfter, breakBefore, position, boxes });
});
......@@ -185,7 +170,7 @@ class Printer extends EventEmitter {
async _parseOutline(page, tags) {
return await page.evaluate((tags) => {
const tagsToProcess = [];
for (const node of document.querySelectorAll(tags.join(','))) {
for (const node of document.querySelectorAll(tags.join(","))) {
tagsToProcess.push(node);
}
tagsToProcess.reverse();
......@@ -224,8 +209,8 @@ class Printer extends EventEmitter {
for (const child of node.children) {
stripParentProperty(child);
}
}
stripParentProperty(root)
};
stripParentProperty(root);
return root.children;
}, tags);
}
......@@ -245,7 +230,7 @@ class Printer extends EventEmitter {
if (tag.name) {
meta[tag.name] = tag.content;
}
})
});
return meta;
});
......@@ -264,9 +249,9 @@ class Printer extends EventEmitter {
bottom: 0,
left: 0,
}
}
};
let pdf = await page.pdf(PDF_SETTINGS)
let pdf = await page.pdf(settings)
.catch((e) => {
console.error(e);
});
......
const PDFLib = require("pdf-lib");
const isFunction = require( 'lodash/isFunction' );
const last = require( 'lodash/last' );
const sortBy = require( 'lodash/sortBy' );
const PDFXRefTableFactory = require( 'pdf-lib/lib/core/pdf-structures/factories/PDFXRefTableFactory' ).default;
const isFunction = require( "lodash/isFunction" );
const last = require( "lodash/last" );
const sortBy = require( "lodash/sortBy" );
const PDFXRefTableFactory = require( "pdf-lib/lib/core/pdf-structures/factories/PDFXRefTableFactory" ).default;
const createIndirectObjectsFromIndex = ({ index }) => {
let catalogRef;
......@@ -45,7 +45,7 @@ class PDFDocumentWriter extends PDFLib.PDFDocumentWriter {
nonStreamObjects,
} = createIndirectObjectsFromIndex(pdfDoc.index);
if (!catalogRef) error('Missing PDFCatalog');
if (!catalogRef) console.error("Missing PDFCatalog");
streamObjects.forEach((streamObj) => {
if (isFunction(streamObj.pdfObject.encode)) streamObj.pdfObject.encode();
});
......@@ -53,7 +53,7 @@ class PDFDocumentWriter extends PDFLib.PDFDocumentWriter {
const merged = [...streamObjects, ...nonStreamObjects];
const offsets = computeOffsets(pdfDoc.header.bytesSize(), merged);
const sortedOffsets = sortBy(offsets, 'objectNumber');
const sortedOffsets = sortBy(offsets, "objectNumber");
/* ===== (2) Create XRefTable and Trailer ===== */
const table = PDFXRefTableFactory.forOffsets(sortedOffsets);
......@@ -74,6 +74,8 @@ class PDFDocumentWriter extends PDFLib.PDFDocumentWriter {
const bufferSize = tableOffset + table.bytesSize() + trailer.bytesSize();
const buffer = new Uint8Array(bufferSize);
/* eslint-disable no-unused-vars */
// TODO: how is remaining used?
let remaining = pdfDoc.header.copyBytesInto(buffer);
remaining = merged.reduce(
(remBytes, indirectObj) => indirectObj.copyBytesInto(remBytes),
......@@ -81,6 +83,7 @@ class PDFDocumentWriter extends PDFLib.PDFDocumentWriter {
);
remaining = table.copyBytesInto(remaining);
remaining = trailer.copyBytesInto(remaining);
/* eslint-enable no-unused-vars */
return buffer;
}
......
<!DOCTYPE html PUBLIC>
<html lang="en" lang="en">
<head>
<meta http-equiv="Content-Type" content="text/html;charset=utf-8" />
<meta http-equiv="Content-Style-Type" content="text/css" />
<title>
Auroræ: Their Characters and Spectra
</title>
<meta name="author" content="J. Rand Capron"/>
<meta name="subject" content="A Project Gutenberg eBook"/>
<meta name="keywords" content="aurorae, spectra, characters"/>
<meta name="creator" content="Editoria"/>
<meta name="modDate" content="2009-04-01"/>
<link rel="coverpage" href="images/cover.jpg" />
<link href="aurorae/book.css" rel="stylesheet" type='text/css'>
<body>
<section class="frontmatter" data-type="half-title">
<h1>Auroræ<br />
<span class="smaller">Their characters and spectra</span></h1>
</section>
</body>
</html>
This diff is collapsed.