@ironsoftware/ironpdf
Version:
IronPDF for Node
309 lines • 13.4 kB
JavaScript
;
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
var desc = Object.getOwnPropertyDescriptor(m, k);
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
desc = { enumerable: true, get: function() { return m[k]; } };
}
Object.defineProperty(o, k2, desc);
}) : (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
o[k2] = m[k];
}));
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
Object.defineProperty(o, "default", { enumerable: true, value: v });
}) : function(o, v) {
o["default"] = v;
});
var __importStar = (this && this.__importStar) || function (mod) {
if (mod && mod.__esModule) return mod;
var result = {};
if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
__setModuleDefault(result, mod);
return result;
};
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
return new (P || (P = Promise))(function (resolve, reject) {
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
step((generator = generator.apply(thisArg, _arguments || [])).next());
});
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.saveAsLinearizedFromBytes = exports.linearizeCoreFromId = exports.linearizeCoreFromBytes = exports.linearizeInMemoryFromBytes = exports.linearizeInMemoryFromIdStream = exports.linearizeInMemoryFromId = exports.isLinearizedFromBytes = void 0;
const buffer_1 = require("buffer");
const fs = __importStar(require("fs"));
const os = __importStar(require("os"));
const path = __importStar(require("path"));
const stream_1 = require("stream");
const access_1 = require("../../access");
const render_1 = require("../../../public/render");
const util_1 = require("../util");
/**
* Check if the given PDF bytes represent a linearized ("Fast Web View") PDF.
*/
function isLinearizedFromBytes(pdfBytes, password = "") {
return __awaiter(this, void 0, void 0, function* () {
const client = yield access_1.Access.ensureConnection();
return new Promise((resolve, reject) => {
const stream = client.QPdf_Linearization_IsLinearized((err, value) => {
var _a;
if (err) {
reject(`${err.name}/n${err.message}`);
}
else if (value) {
if (value.exception) {
(0, util_1.handleRemoteException)(value.exception, reject);
return;
}
resolve((_a = value.result) !== null && _a !== void 0 ? _a : false);
}
else {
reject("No response from IronPdfEngine for isLinearized");
}
});
stream.write({ info: { password: password !== null && password !== void 0 ? password : "" } });
(0, util_1.chunkBuffer)(pdfBytes).forEach((chunk) => {
stream.write({ pdfBytesChunk: chunk });
});
stream.end();
});
});
}
exports.isLinearizedFromBytes = isLinearizedFromBytes;
/**
* Linearize a PDF held by the engine (by document id) and return the linearized bytes
* via the {@code QPdf_Linearization_LinearizeInMemoryFromId} unary-request/server-streaming RPC.
*/
function linearizeInMemoryFromId(id, password = "") {
return __awaiter(this, void 0, void 0, function* () {
const client = yield access_1.Access.ensureConnection();
return new Promise((resolve, reject) => {
const stream = client.QPdf_Linearization_LinearizeInMemoryFromId({
document: { documentId: id },
password: password !== null && password !== void 0 ? password : "",
});
const buffers = [];
stream.on("data", (data) => {
if (data.exception) {
(0, util_1.handleRemoteException)(data.exception, reject);
}
else if (data.resultChunk) {
buffers.push(data.resultChunk);
}
});
stream.on("error", (err) => {
reject(`${err.name}/n${err.message}`);
});
stream.on("end", () => {
resolve(buffer_1.Buffer.concat(buffers));
});
});
});
}
exports.linearizeInMemoryFromId = linearizeInMemoryFromId;
/**
* Linearize a PDF held by the engine (by document id) and stream the linearized bytes
* as a {@link Readable}. Useful for piping to HTTP responses or file streams without
* buffering the entire PDF in memory.
*/
function linearizeInMemoryFromIdStream(id, password = "") {
return __awaiter(this, void 0, void 0, function* () {
const client = yield access_1.Access.ensureConnection();
const passThrough = new stream_1.PassThrough();
const stream = client.QPdf_Linearization_LinearizeInMemoryFromId({
document: { documentId: id },
password: password !== null && password !== void 0 ? password : "",
});
stream.on("data", (data) => {
if (data.exception) {
passThrough.destroy(new Error(`${data.exception.message}/n${data.exception.remoteStackTrace}`));
}
else if (data.resultChunk) {
passThrough.write(data.resultChunk);
}
});
stream.on("error", (err) => {
passThrough.destroy(err);
});
stream.on("end", () => {
passThrough.end();
});
return passThrough;
});
}
exports.linearizeInMemoryFromIdStream = linearizeInMemoryFromIdStream;
/**
* Linearize a PDF provided as raw bytes and return the linearized bytes via the
* bidirectional streaming {@code QPdf_Linearization_LinearizeInMemory} RPC.
*/
function linearizeInMemoryFromBytes(pdfBytes, password = "") {
return __awaiter(this, void 0, void 0, function* () {
const client = yield access_1.Access.ensureConnection();
return new Promise((resolve, reject) => {
const stream = client.QPdf_Linearization_LinearizeInMemory();
const buffers = [];
stream.on("data", (data) => {
if (data.exception) {
(0, util_1.handleRemoteException)(data.exception, reject);
}
else if (data.resultChunk) {
buffers.push(data.resultChunk);
}
});
stream.on("error", (err) => {
reject(`${err.name}/n${err.message}`);
});
stream.on("end", () => {
resolve(buffer_1.Buffer.concat(buffers));
});
stream.write({ info: { password: password !== null && password !== void 0 ? password : "" } });
(0, util_1.chunkBuffer)(pdfBytes).forEach((chunk) => {
stream.write({ pdfBytesChunk: chunk });
});
stream.end();
});
});
}
exports.linearizeInMemoryFromBytes = linearizeInMemoryFromBytes;
/**
* Core linearization logic shared across all linearization entry points. Implements the
* {@link LinearizationMode} strategy pattern. Mirrors {@code PdfDocument.LinearizePdfCore}
* on the C# side.
*/
function linearizeCoreFromBytes(pdfBytes, password = "", mode = render_1.LinearizationMode.Automatic) {
return __awaiter(this, void 0, void 0, function* () {
if (!pdfBytes || pdfBytes.length === 0) {
throw new Error("The PDF bytes cannot be null or empty.");
}
if (mode === render_1.LinearizationMode.InMemory) {
return linearizeInMemoryFromBytes(pdfBytes, password);
}
if (mode === render_1.LinearizationMode.FileBased) {
// Explicit FileBased — let any disk error bubble up.
return linearizeViaTempFile(pdfBytes, password);
}
// Automatic mode
if (canWriteToTemp()) {
try {
return yield linearizeViaTempFile(pdfBytes, password);
}
catch (e) {
console.warn(`Automatic Linearization: Disk attempt failed (${e.message}). ` +
"Falling back to Memory linearization.");
return linearizeInMemoryFromBytes(pdfBytes, password);
}
}
console.warn("Automatic Linearization: No write permission detected. Using Memory linearization.");
return linearizeInMemoryFromBytes(pdfBytes, password);
});
}
exports.linearizeCoreFromBytes = linearizeCoreFromBytes;
/**
* Variant of {@link linearizeCoreFromBytes} that starts from an open document on the engine.
* For {@link LinearizationMode.InMemory} we use the cheap document-id RPC; for the disk-based
* paths we have to fetch the bytes once and delegate to {@link linearizeCoreFromBytes}.
*/
function linearizeCoreFromId(id, getBytes, password = "", mode = render_1.LinearizationMode.Automatic) {
return __awaiter(this, void 0, void 0, function* () {
if (mode === render_1.LinearizationMode.InMemory) {
return linearizeInMemoryFromId(id, password);
}
const pdfBytes = yield getBytes();
return linearizeCoreFromBytes(pdfBytes, password, mode);
});
}
exports.linearizeCoreFromId = linearizeCoreFromId;
/**
* Linearize via the engine's file-based RPC and persist the result through a client-side
* temporary file. The client-side disk write is the difference between this and
* {@link linearizeInMemoryFromBytes} — when the client filesystem is read-only,
* {@code FileBased} mode fails here so {@link LinearizationMode.Automatic} can fall back.
*
* Mirrors C# {@code PdfDocument.LinearizeViaTempFile}.
*/
function linearizeViaTempFile(pdfBytes, password) {
return __awaiter(this, void 0, void 0, function* () {
const linearized = yield saveAsLinearizedFromBytes(pdfBytes, "", password);
const tempPath = path.join(os.tmpdir(), `ironpdf-linearize-${process.pid}-${Date.now()}-${Math.random().toString(36).slice(2, 10)}.pdf`);
try {
fs.writeFileSync(tempPath, linearized);
return fs.readFileSync(tempPath);
}
finally {
try {
if (fs.existsSync(tempPath)) {
fs.unlinkSync(tempPath);
}
}
catch (_a) {
// best-effort cleanup
}
}
});
}
/**
* Probe whether the current process can create files in the system temp directory.
*/
function canWriteToTemp() {
const probePath = path.join(os.tmpdir(), `ironpdf-probe-${process.pid}-${Date.now()}-${Math.random().toString(36).slice(2, 10)}.tmp`);
try {
fs.writeFileSync(probePath, "");
return true;
}
catch (_a) {
return false;
}
finally {
try {
if (fs.existsSync(probePath)) {
fs.unlinkSync(probePath);
}
}
catch (_b) {
// best-effort cleanup
}
}
}
/**
* Linearize a PDF provided as raw bytes and save the result to disk via the file-based
* streaming {@code QPdf_Linearization_SaveAsLinearizedFromBytes} RPC.
*
* Mirrors the in-memory behavior used by {@link compressInMemory} in {@code compress.ts}:
* the engine streams the linearized bytes back, and we concatenate and persist them
* locally at {@code outputPath}.
*/
function saveAsLinearizedFromBytes(pdfBytes, outputPath, password = "") {
return __awaiter(this, void 0, void 0, function* () {
const client = yield access_1.Access.ensureConnection();
return new Promise((resolve, reject) => {
const stream = client.QPdf_Linearization_SaveAsLinearizedFromBytes();
const buffers = [];
stream.on("data", (data) => {
if (data.exception) {
(0, util_1.handleRemoteException)(data.exception, reject);
}
else if (data.resultChunk) {
buffers.push(data.resultChunk);
}
});
stream.on("error", (err) => {
reject(`${err.name}/n${err.message}`);
});
stream.on("end", () => {
resolve(buffer_1.Buffer.concat(buffers));
});
stream.write({
info: { outputPath: outputPath, password: password !== null && password !== void 0 ? password : "" },
});
(0, util_1.chunkBuffer)(pdfBytes).forEach((chunk) => {
stream.write({ pdfBytesChunk: chunk });
});
stream.end();
});
});
}
exports.saveAsLinearizedFromBytes = saveAsLinearizedFromBytes;
//# sourceMappingURL=linearize.js.map