summaryrefslogtreecommitdiffstats
path: root/remote/shared/PDF.sys.mjs
diff options
context:
space:
mode:
Diffstat (limited to 'remote/shared/PDF.sys.mjs')
-rw-r--r--remote/shared/PDF.sys.mjs245
1 files changed, 245 insertions, 0 deletions
diff --git a/remote/shared/PDF.sys.mjs b/remote/shared/PDF.sys.mjs
new file mode 100644
index 0000000000..e0dd2ee060
--- /dev/null
+++ b/remote/shared/PDF.sys.mjs
@@ -0,0 +1,245 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this file,
+ * You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+import { XPCOMUtils } from "resource://gre/modules/XPCOMUtils.sys.mjs";
+
+const lazy = {};
+
+ChromeUtils.defineESModuleGetters(lazy, {
+ assert: "chrome://remote/content/shared/webdriver/Assert.sys.mjs",
+ Log: "chrome://remote/content/shared/Log.sys.mjs",
+});
+
+XPCOMUtils.defineLazyGetter(lazy, "logger", () => lazy.Log.get());
+
+export const print = {
+ maxScaleValue: 2.0,
+ minScaleValue: 0.1,
+};
+
+print.defaults = {
+ // The size of the page in centimeters.
+ page: {
+ width: 21.59,
+ height: 27.94,
+ },
+ margin: {
+ top: 1.0,
+ bottom: 1.0,
+ left: 1.0,
+ right: 1.0,
+ },
+ orientationValue: ["landscape", "portrait"],
+};
+
+print.addDefaultSettings = function (settings) {
+ const {
+ background = false,
+ margin = {},
+ orientation = "portrait",
+ page = {},
+ pageRanges = [],
+ scale = 1.0,
+ shrinkToFit = true,
+ } = settings;
+
+ lazy.assert.object(page, `Expected "page" to be a object, got ${page}`);
+ lazy.assert.object(margin, `Expected "margin" to be a object, got ${margin}`);
+
+ if (!("width" in page)) {
+ page.width = print.defaults.page.width;
+ }
+
+ if (!("height" in page)) {
+ page.height = print.defaults.page.height;
+ }
+
+ if (!("top" in margin)) {
+ margin.top = print.defaults.margin.top;
+ }
+
+ if (!("bottom" in margin)) {
+ margin.bottom = print.defaults.margin.bottom;
+ }
+
+ if (!("right" in margin)) {
+ margin.right = print.defaults.margin.right;
+ }
+
+ if (!("left" in margin)) {
+ margin.left = print.defaults.margin.left;
+ }
+
+ return {
+ background,
+ margin,
+ orientation,
+ page,
+ pageRanges,
+ scale,
+ shrinkToFit,
+ };
+};
+
+print.getPrintSettings = function (settings) {
+ const psService = Cc["@mozilla.org/gfx/printsettings-service;1"].getService(
+ Ci.nsIPrintSettingsService
+ );
+
+ let cmToInches = cm => cm / 2.54;
+ const printSettings = psService.createNewPrintSettings();
+ printSettings.isInitializedFromPrinter = true;
+ printSettings.isInitializedFromPrefs = true;
+ printSettings.outputFormat = Ci.nsIPrintSettings.kOutputFormatPDF;
+ printSettings.printerName = "marionette";
+ printSettings.printSilent = true;
+
+ // Setting the paperSizeUnit to kPaperSizeMillimeters doesn't work on mac
+ printSettings.paperSizeUnit = Ci.nsIPrintSettings.kPaperSizeInches;
+ printSettings.paperWidth = cmToInches(settings.page.width);
+ printSettings.paperHeight = cmToInches(settings.page.height);
+
+ printSettings.marginBottom = cmToInches(settings.margin.bottom);
+ printSettings.marginLeft = cmToInches(settings.margin.left);
+ printSettings.marginRight = cmToInches(settings.margin.right);
+ printSettings.marginTop = cmToInches(settings.margin.top);
+
+ printSettings.printBGColors = settings.background;
+ printSettings.printBGImages = settings.background;
+ printSettings.scaling = settings.scale;
+ printSettings.shrinkToFit = settings.shrinkToFit;
+
+ printSettings.headerStrCenter = "";
+ printSettings.headerStrLeft = "";
+ printSettings.headerStrRight = "";
+ printSettings.footerStrCenter = "";
+ printSettings.footerStrLeft = "";
+ printSettings.footerStrRight = "";
+
+ // Override any os-specific unwriteable margins
+ printSettings.unwriteableMarginTop = 0;
+ printSettings.unwriteableMarginLeft = 0;
+ printSettings.unwriteableMarginBottom = 0;
+ printSettings.unwriteableMarginRight = 0;
+
+ if (settings.orientation === "landscape") {
+ printSettings.orientation = Ci.nsIPrintSettings.kLandscapeOrientation;
+ }
+
+ if (settings.pageRanges?.length) {
+ printSettings.pageRanges = parseRanges(settings.pageRanges);
+ }
+
+ return printSettings;
+};
+
+/**
+ * Convert array of strings of the form ["1-3", "2-4", "7", "9-"] to an flat array of
+ * limits, like [1, 4, 7, 7, 9, 2**31 - 1] (meaning 1-4, 7, 9-end)
+ *
+ * @param {Array.<string|number>} ranges
+ * Page ranges to print, e.g., ['1-5', '8', '11-13'].
+ * Defaults to the empty string, which means print all pages.
+ *
+ * @returns {Array.<number>}
+ * Even-length array containing page range limits
+ */
+function parseRanges(ranges) {
+ const MAX_PAGES = 0x7fffffff;
+
+ if (ranges.length === 0) {
+ return [];
+ }
+
+ let allLimits = [];
+
+ for (let range of ranges) {
+ let limits;
+ if (typeof range !== "string") {
+ // We got a single integer so the limits are just that page
+ lazy.assert.positiveInteger(range);
+ limits = [range, range];
+ } else {
+ // We got a string presumably of the form <int> | <int>? "-" <int>?
+ const msg = `Expected a range of the form <int> or <int>-<int>, got ${range}`;
+
+ limits = range.split("-").map(x => x.trim());
+ lazy.assert.that(o => [1, 2].includes(o.length), msg)(limits);
+
+ // Single numbers map to a range with that page at the start and the end
+ if (limits.length == 1) {
+ limits.push(limits[0]);
+ }
+
+ // Need to check that both limits are strings conisting only of
+ // decimal digits (or empty strings)
+ const assertNumeric = lazy.assert.that(o => /^\d*$/.test(o), msg);
+ limits.every(x => assertNumeric(x));
+
+ // Convert from strings representing numbers to actual numbers
+ // If we don't have an upper bound, choose something very large;
+ // the print code will later truncate this to the number of pages
+ limits = limits.map((limitStr, i) => {
+ if (limitStr == "") {
+ return i == 0 ? 1 : MAX_PAGES;
+ }
+ return parseInt(limitStr);
+ });
+ }
+ lazy.assert.that(
+ x => x[0] <= x[1],
+ "Lower limit ${parts[0]} is higher than upper limit ${parts[1]}"
+ )(limits);
+
+ allLimits.push(limits);
+ }
+ // Order by lower limit
+ allLimits.sort((a, b) => a[0] - b[0]);
+ let parsedRanges = [allLimits.shift()];
+ for (let limits of allLimits) {
+ let prev = parsedRanges[parsedRanges.length - 1];
+ let prevMax = prev[1];
+ let [min, max] = limits;
+ if (min <= prevMax) {
+ // min is inside previous range, so extend the max if needed
+ if (max > prevMax) {
+ prev[1] = max;
+ }
+ } else {
+ // Otherwise we have a new range
+ parsedRanges.push(limits);
+ }
+ }
+
+ let rv = parsedRanges.flat();
+ lazy.logger.debug(`Got page ranges [${rv.join(", ")}]`);
+ return rv;
+}
+
+print.printToBinaryString = async function (browsingContext, printSettings) {
+ // Create a stream to write to.
+ const stream = Cc["@mozilla.org/storagestream;1"].createInstance(
+ Ci.nsIStorageStream
+ );
+ stream.init(4096, 0xffffffff);
+
+ printSettings.outputDestination =
+ Ci.nsIPrintSettings.kOutputDestinationStream;
+ printSettings.outputStream = stream.getOutputStream(0);
+
+ await browsingContext.print(printSettings);
+
+ const inputStream = Cc["@mozilla.org/binaryinputstream;1"].createInstance(
+ Ci.nsIBinaryInputStream
+ );
+
+ inputStream.setInputStream(stream.newInputStream(0));
+
+ const available = inputStream.available();
+ const bytes = inputStream.readBytes(available);
+
+ stream.close();
+
+ return bytes;
+};