1 /* This Source Code Form is subject to the terms of the Mozilla Public
2 * License, v. 2.0. If a copy of the MPL was not distributed with this file,
3 * You can obtain one at http://mozilla.org/MPL/2.0/. */
7 ChromeUtils.defineESModuleGetters(lazy, {
8 assert: "chrome://remote/content/shared/webdriver/Assert.sys.mjs",
9 error: "chrome://remote/content/shared/webdriver/Errors.sys.mjs",
10 Log: "chrome://remote/content/shared/Log.sys.mjs",
11 pprint: "chrome://remote/content/shared/Format.sys.mjs",
14 ChromeUtils.defineLazyGetter(lazy, "logger", () => lazy.Log.get());
16 export const print = {
21 export const MIN_PAGE_SIZE = 0.0352;
24 // The size of the page in centimeters.
35 orientationValue: ["landscape", "portrait"],
38 print.addDefaultSettings = function (settings) {
42 orientation = "portrait",
51 lazy.pprint`Expected "page" to be an object, got ${page}`
55 lazy.pprint`Expected "margin" to be an object, got ${margin}`
58 if (!("width" in page)) {
59 page.width = print.defaults.page.width;
62 if (!("height" in page)) {
63 page.height = print.defaults.page.height;
66 if (page.width < MIN_PAGE_SIZE) {
67 throw new lazy.error.InvalidArgumentError(
68 `Expected "page.width" to be greater than or equal to ${MIN_PAGE_SIZE}cm, got ${page.width}cm.`
72 if (page.height < MIN_PAGE_SIZE) {
73 throw new lazy.error.InvalidArgumentError(
74 `Expected "page.height" to be greater than or equal to ${MIN_PAGE_SIZE}cm, got ${page.height}cm.`
78 if (!("top" in margin)) {
79 margin.top = print.defaults.margin.top;
82 if (!("bottom" in margin)) {
83 margin.bottom = print.defaults.margin.bottom;
86 if (!("right" in margin)) {
87 margin.right = print.defaults.margin.right;
90 if (!("left" in margin)) {
91 margin.left = print.defaults.margin.left;
105 print.getPrintSettings = function (settings) {
106 const psService = Cc["@mozilla.org/gfx/printsettings-service;1"].getService(
107 Ci.nsIPrintSettingsService
110 let cmToInches = cm => cm / 2.54;
111 const printSettings = psService.createNewPrintSettings();
112 printSettings.isInitializedFromPrinter = true;
113 printSettings.isInitializedFromPrefs = true;
114 printSettings.outputFormat = Ci.nsIPrintSettings.kOutputFormatPDF;
115 printSettings.printerName = "marionette";
116 printSettings.printSilent = true;
118 // Setting the paperSizeUnit to kPaperSizeMillimeters doesn't work on mac
119 printSettings.paperSizeUnit = Ci.nsIPrintSettings.kPaperSizeInches;
120 printSettings.paperWidth = cmToInches(settings.page.width);
121 printSettings.paperHeight = cmToInches(settings.page.height);
122 printSettings.usePageRuleSizeAsPaperSize = true;
124 printSettings.marginBottom = cmToInches(settings.margin.bottom);
125 printSettings.marginLeft = cmToInches(settings.margin.left);
126 printSettings.marginRight = cmToInches(settings.margin.right);
127 printSettings.marginTop = cmToInches(settings.margin.top);
129 printSettings.printBGColors = settings.background;
130 printSettings.printBGImages = settings.background;
131 printSettings.scaling = settings.scale;
132 printSettings.shrinkToFit = settings.shrinkToFit;
134 printSettings.headerStrCenter = "";
135 printSettings.headerStrLeft = "";
136 printSettings.headerStrRight = "";
137 printSettings.footerStrCenter = "";
138 printSettings.footerStrLeft = "";
139 printSettings.footerStrRight = "";
141 // Override any os-specific unwriteable margins
142 printSettings.unwriteableMarginTop = 0;
143 printSettings.unwriteableMarginLeft = 0;
144 printSettings.unwriteableMarginBottom = 0;
145 printSettings.unwriteableMarginRight = 0;
147 if (settings.orientation === "landscape") {
148 printSettings.orientation = Ci.nsIPrintSettings.kLandscapeOrientation;
151 if (settings.pageRanges?.length) {
152 printSettings.pageRanges = parseRanges(settings.pageRanges);
155 return printSettings;
159 * Convert array of strings of the form ["1-3", "2-4", "7", "9-"] to an flat array of
160 * limits, like [1, 4, 7, 7, 9, 2**31 - 1] (meaning 1-4, 7, 9-end)
162 * @param {Array.<string|number>} ranges
163 * Page ranges to print, e.g., ['1-5', '8', '11-13'].
164 * Defaults to the empty string, which means print all pages.
166 * @returns {Array.<number>}
167 * Even-length array containing page range limits
169 function parseRanges(ranges) {
170 const MAX_PAGES = 0x7fffffff;
172 if (ranges.length === 0) {
178 for (let range of ranges) {
180 if (typeof range !== "string") {
181 // We got a single integer so the limits are just that page
182 lazy.assert.positiveInteger(
184 lazy.pprint`Expected "range" to be a string or a positive integer, got ${range}`
186 limits = [range, range];
188 // We got a string presumably of the form <int> | <int>? "-" <int>?
189 const msg = lazy.pprint`Expected "range" to be of the form <int> or <int>-<int>, got ${range}`;
191 limits = range.split("-").map(x => x.trim());
192 lazy.assert.that(o => [1, 2].includes(o.length), msg)(limits);
194 // Single numbers map to a range with that page at the start and the end
195 if (limits.length == 1) {
196 limits.push(limits[0]);
199 // Need to check that both limits are strings consisting only of
200 // decimal digits (or empty strings)
201 const assertNumeric = lazy.assert.that(o => /^\d*$/.test(o), msg);
202 limits.every(x => assertNumeric(x));
204 // Convert from strings representing numbers to actual numbers
205 // If we don't have an upper bound, choose something very large;
206 // the print code will later truncate this to the number of pages
207 limits = limits.map((limitStr, i) => {
208 if (limitStr == "") {
209 return i == 0 ? 1 : MAX_PAGES;
211 return parseInt(limitStr);
216 lazy.pprint`Expected "range" lower limit to be less than the upper limit, got ${range}`
219 allLimits.push(limits);
221 // Order by lower limit
222 allLimits.sort((a, b) => a[0] - b[0]);
223 let parsedRanges = [allLimits.shift()];
224 for (let limits of allLimits) {
225 let prev = parsedRanges[parsedRanges.length - 1];
226 let prevMax = prev[1];
227 let [min, max] = limits;
228 if (min <= prevMax) {
229 // min is inside previous range, so extend the max if needed
234 // Otherwise we have a new range
235 parsedRanges.push(limits);
239 let rv = parsedRanges.flat();
240 lazy.logger.debug(`Got page ranges [${rv.join(", ")}]`);
244 print.printToBinaryString = async function (browsingContext, printSettings) {
245 // Create a stream to write to.
246 const stream = Cc["@mozilla.org/storagestream;1"].createInstance(
249 stream.init(4096, 0xffffffff);
251 printSettings.outputDestination =
252 Ci.nsIPrintSettings.kOutputDestinationStream;
253 printSettings.outputStream = stream.getOutputStream(0);
255 await browsingContext.print(printSettings);
257 const inputStream = Cc["@mozilla.org/binaryinputstream;1"].createInstance(
258 Ci.nsIBinaryInputStream
261 inputStream.setInputStream(stream.newInputStream(0));
263 const available = inputStream.available();
264 const bytes = inputStream.readBytes(available);