Skip to content

Instantly share code, notes, and snippets.

@florianbepunkt
Created December 16, 2019 16:37
Show Gist options
  • Save florianbepunkt/a28acf772b5afa9f0841903d8589cd31 to your computer and use it in GitHub Desktop.
Save florianbepunkt/a28acf772b5afa9f0841903d8589cd31 to your computer and use it in GitHub Desktop.
Flatten pdf with poppler utils pdftocairo
import { EventEmitter } from "events";
import { spawn, ChildProcess } from "child_process";
import { Writable } from "stream";
import fs from "fs";
/**
* from: https://github.com/rauschma/stringio/
*/
function streamPromiseHelper(
emitter: EventEmitter,
operation: (callback: () => void) => void
): Promise<void> {
return new Promise((resolve, reject) => {
const errListener = (err: Error) => {
emitter.removeListener("error", errListener);
reject(err);
};
emitter.addListener("error", errListener);
const callback = () => {
emitter.removeListener("error", errListener);
resolve(undefined);
};
operation(callback);
});
}
/**
* from: https://github.com/rauschma/stringio/
*/
function streamWrite(
stream: Writable,
chunk: string | Buffer | Uint8Array,
encoding = "utf8"
): Promise<void> {
// Get notified via callback when it’s “safe” to write again.
// The alternatives are:
// – 'drain' event waits until buffering is below “high water mark”
// – callback waits until written content is unbuffered
return streamPromiseHelper(stream, callback =>
stream.write(chunk, encoding, callback)
);
}
/**
* from: https://github.com/rauschma/stringio/
*/
function streamEnd(stream: Writable): Promise<void> {
return streamPromiseHelper(stream, callback => stream.end(callback));
}
async function streamStdOutToBuffer(
childProcess: ChildProcess
): Promise<Buffer> {
return new Promise((resolve, reject) => {
let buffer = Buffer.from("");
childProcess.stdout.on("data", data => {
buffer = Buffer.concat([buffer, data]);
});
childProcess.once("exit", (code, signal) => {
if (code === 0) {
resolve(buffer);
} else {
reject(new Error("Exit with error code: " + code));
}
});
childProcess.once("error", error => {
reject(error);
});
});
}
async function flattenPdf(pdf: Buffer): Promise<Buffer> {
const childProcess = spawn("pdftocairo", ["-pdf", "-", "-"]);
await streamWrite(childProcess.stdin, pdf);
await streamEnd(childProcess.stdin);
return await streamStdOutToBuffer(childProcess);
}
/**
* Example
*/
const main = async () => {
const pdf = fs.readFileSync("./invoice-gb.pdf");
const flattenedPdf = await flattenPdf(pdf);
fs.writeFileSync("./foobar.pdf", flattenedPdf);
};
main();
@qwertynik
Copy link

Thanks for attaching this.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment