зеркало из https://github.com/mozilla/gecko-dev.git
Bug 1311586 - Implement method |PPB_PDF::Print|. r=brsun
From eb817ace782f0c7deb420e61c5d9d6185c665e28 Mon Sep 17 00:00:00 2001 --- .../mortar/host/common/ppapi-runtime.jsm | 205 +++++++++++++++++++++ .../mortar/host/pdf/chrome/js/toolbar.js | 4 + .../mortar/host/pdf/chrome/js/viewport.js | 6 + .../mortar/host/pdf/ppapi-content-sandbox.js | 70 +++++++ 4 files changed, 285 insertions(+)
This commit is contained in:
Родитель
4b7b8ff6a2
Коммит
79448e55da
|
@ -53,6 +53,13 @@ const PP_ERROR_ADDRESS_IN_USE = -108;
|
|||
const PP_ERROR_MESSAGE_TOO_BIG = -109;
|
||||
const PP_ERROR_NAME_NOT_RESOLVED = -110;
|
||||
|
||||
// Point is defined as 1/72 of an inch (25.4mm)
|
||||
const POINT_PER_INCH = 72;
|
||||
const POINT_PER_MILLIMETER = POINT_PER_INCH / 25.4;
|
||||
|
||||
const PRINT_FILE_NAME = "print.pdf";
|
||||
const PRINT_CONTENT_TEMP_KEY =
|
||||
(Services.appinfo.OS == "Linux") ? "TmpD" : "ContentTmpD";
|
||||
|
||||
const PP_Bool = {
|
||||
PP_FALSE: 0,
|
||||
|
@ -262,6 +269,26 @@ const PP_NetworkList_Type = {
|
|||
PP_NETWORKLIST_TYPE_CELLULAR: 3
|
||||
};
|
||||
|
||||
const PP_PrintOrientation_Dev = {
|
||||
PP_PRINTORIENTATION_NORMAL: 0,
|
||||
PP_PRINTORIENTATION_ROTATED_90_CW: 1,
|
||||
PP_PRINTORIENTATION_ROTATED_180: 2,
|
||||
PP_PRINTORIENTATION_ROTATED_90_CCW: 3
|
||||
};
|
||||
|
||||
const PP_PrintOutputFormat_Dev = {
|
||||
PP_PRINTOUTPUTFORMAT_RASTER: 1 << 0,
|
||||
PP_PRINTOUTPUTFORMAT_PDF: 1 << 1,
|
||||
PP_PRINTOUTPUTFORMAT_POSTSCRIPT: 1 << 2,
|
||||
PP_PRINTOUTPUTFORMAT_EMF: 1 << 3
|
||||
};
|
||||
|
||||
const PP_PrintScalingOption_Dev = {
|
||||
PP_PRINTSCALINGOPTION_NONE: 0,
|
||||
PP_PRINTSCALINGOPTION_FIT_TO_PRINTABLE_AREA: 1,
|
||||
PP_PRINTSCALINGOPTION_SOURCE_SIZE: 2
|
||||
};
|
||||
|
||||
const PP_TextInput_Type_Dev = {
|
||||
PP_TEXTINPUT_TYPE_DEV_NONE: 0,
|
||||
PP_TEXTINPUT_TYPE_DEV_TEXT: 1,
|
||||
|
@ -330,6 +357,19 @@ const PR_TRUNCATE = 0x20;
|
|||
const PR_SYNC = 0x40;
|
||||
const PR_EXCL = 0x80;
|
||||
|
||||
/* File mode bits */
|
||||
const PR_IRWXU = 0o700; /* read, write, execute/search by owner */
|
||||
const PR_IRUSR = 0o400; /* read permission, owner */
|
||||
const PR_IWUSR = 0o200; /* write permission, owner */
|
||||
const PR_IXUSR = 0o100; /* execute/search permission, owner */
|
||||
const PR_IRWXG = 0o070; /* read, write, execute/search by group */
|
||||
const PR_IRGRP = 0o040; /* read permission, group */
|
||||
const PR_IWGRP = 0o020; /* write permission, group */
|
||||
const PR_IXGRP = 0o010; /* execute/search permission, group */
|
||||
const PR_IRWXO = 0o007; /* read, write, execute/search by others */
|
||||
const PR_IROTH = 0o004; /* read permission, others */
|
||||
const PR_IWOTH = 0o002; /* write permission, others */
|
||||
const PR_IXOTH = 0o001; /* execute/search permission, others */
|
||||
|
||||
class InterfaceMemberCall {
|
||||
constructor(interfaceName, memberName, args) {
|
||||
|
@ -1541,6 +1581,8 @@ class PPAPIInstance {
|
|||
this.cachedImageData = null;
|
||||
this.viewport = new PPAPIViewport(this);
|
||||
this.selectedText = "";
|
||||
this.ppapiPrintSettings = {};
|
||||
this.pageRangeInfo = {};
|
||||
|
||||
this.notifyHashChange(info.url);
|
||||
|
||||
|
@ -1561,6 +1603,76 @@ class PPAPIInstance {
|
|||
name: evt.data.name
|
||||
});
|
||||
});
|
||||
|
||||
this.mm.addMessageListener("ppapipdf.js:printsettingschanged", (evt) => {
|
||||
// Convert trim print settings to ppapi print settings
|
||||
let ps = evt.data.trimPrintSettings;
|
||||
let point = (ps.paperSizeUnit == Ci.nsIPrintSettings.kPaperSizeInches)
|
||||
? POINT_PER_INCH : POINT_PER_MILLIMETER;
|
||||
// Unit of margins are in inches but paper size could be inches or mm
|
||||
this.ppapiPrintSettings.printable_area = {
|
||||
point: {
|
||||
x: ps.unwriteableMarginLeft * POINT_PER_INCH,
|
||||
y: ps.unwriteableMarginTop * POINT_PER_INCH
|
||||
},
|
||||
size: {
|
||||
width: ps.paperWidth * point - (ps.unwriteableMarginLeft +
|
||||
ps.unwriteableMarginRight) * POINT_PER_INCH,
|
||||
height: ps.paperHeight * point - (ps.unwriteableMarginTop +
|
||||
ps.unwriteableMarginBottom) * POINT_PER_INCH
|
||||
}
|
||||
};
|
||||
this.ppapiPrintSettings.content_area = {
|
||||
point: {
|
||||
x: ps.marginLeft * POINT_PER_INCH,
|
||||
y: ps.marginTop * POINT_PER_INCH
|
||||
},
|
||||
size: {
|
||||
width: ps.paperWidth * point - (ps.marginLeft +
|
||||
ps.marginRight) * POINT_PER_INCH,
|
||||
height: ps.paperHeight * point - (ps.marginTop +
|
||||
ps.marginBottom) * POINT_PER_INCH
|
||||
}
|
||||
};
|
||||
this.ppapiPrintSettings.paper_size = {
|
||||
width: ps.paperWidth * point,
|
||||
height: ps.paperHeight * point
|
||||
};
|
||||
// XXX The print dialog does not provide a way for user to set resolution
|
||||
// which causes us not able to access the resolution. So here we
|
||||
// workaround by setting resolution to a default value 0 and pass it
|
||||
// to PDFium for getting PDF file. It is fine to pass a unmeaningful
|
||||
// value of resolution when print as PDF.
|
||||
this.ppapiPrintSettings.dpi = 0;
|
||||
// XXX We only support kPortraitOrientation and kLandscapeOreintation,
|
||||
// otherwise we return directly.
|
||||
switch (ps.orientation) {
|
||||
case Ci.nsIPrintSettings.kPortraitOrientation:
|
||||
this.ppapiPrintSettings.orientation =
|
||||
PP_PrintOrientation_Dev.PP_PRINTORIENTATION_NORMAL;
|
||||
break;
|
||||
case Ci.nsIPrintSettings.kLandscapeOrientation:
|
||||
this.ppapiPrintSettings.orientation =
|
||||
PP_PrintOrientation_Dev.PP_PRINTORIENTATION_ROTATED_90_CW;
|
||||
break;
|
||||
default:
|
||||
return;
|
||||
}
|
||||
this.ppapiPrintSettings.print_scaling_option = ps.shrinkToFit ?
|
||||
PP_PrintScalingOption_Dev.PP_PRINTSCALINGOPTION_FIT_TO_PRINTABLE_AREA :
|
||||
PP_PrintScalingOption_Dev.PP_PRINTSCALINGOPTION_NONE;
|
||||
this.ppapiPrintSettings.grayscale = !ps.printInColor;
|
||||
this.ppapiPrintSettings.format =
|
||||
PP_PrintOutputFormat_Dev.PP_PRINTOUTPUTFORMAT_PDF;
|
||||
|
||||
// Store page range information
|
||||
this.pageRangeInfo.printRange = ps.printRange;
|
||||
this.pageRangeInfo.startPageRange = ps.startPageRange;
|
||||
this.pageRangeInfo.endPageRange = ps.endPageRange;
|
||||
|
||||
let message = {type: 'print'};
|
||||
this.viewportActionHandler(message);
|
||||
});
|
||||
}
|
||||
|
||||
notifyHashChange(url) {
|
||||
|
@ -1704,6 +1816,10 @@ class PPAPIInstance {
|
|||
case 'setHash':
|
||||
this.mm.sendAsyncMessage("ppapipdf.js:setHash", message.hash);
|
||||
break;
|
||||
case 'startPrint':
|
||||
// We need permission for showing print dialog to get print settings
|
||||
this.mm.sendAsyncMessage("ppapipdf.js:getPrintSettings");
|
||||
break;
|
||||
case 'viewport':
|
||||
case 'rotateClockwise':
|
||||
case 'rotateCounterclockwise':
|
||||
|
@ -1711,6 +1827,7 @@ class PPAPIInstance {
|
|||
case 'getSelectedText':
|
||||
case 'getNamedDestination':
|
||||
case 'getPasswordComplete':
|
||||
case 'print':
|
||||
let data = PP_Var.fromJSValue(new Dictionary(message), this);
|
||||
this.rt.call(new InterfaceMemberCall("PPP_Messaging;1.0", "HandleMessage",
|
||||
{ instance: this, var: data }));
|
||||
|
@ -4810,6 +4927,94 @@ dump(`callFromJSON: < ${JSON.stringify(call)}\n`);
|
|||
return 0;
|
||||
},
|
||||
|
||||
/**
|
||||
* void Print(
|
||||
* [in] PP_Instance instance);
|
||||
*/
|
||||
PPB_PDF_Print: function(json) {
|
||||
let instance = this.instances[json.instance];
|
||||
let pageRangeInfo = instance.pageRangeInfo;
|
||||
|
||||
// Query supported formats
|
||||
let supportedFormats = instance.rt.call(new InterfaceMemberCall(
|
||||
"PPP_Printing(Dev);0.6", "QuerySupportedFormats", { instance }), true);
|
||||
// XXX We only support PDF output format now.
|
||||
if (!(PP_PrintOutputFormat_Dev.PP_PRINTOUTPUTFORMAT_PDF &
|
||||
supportedFormats)) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Begin of printing
|
||||
let totalPageNumber = instance.rt.call(new InterfaceMemberCall(
|
||||
"PPP_Printing(Dev);0.6", "Begin", { instance, print_settings:
|
||||
instance.ppapiPrintSettings }), true);
|
||||
if (totalPageNumber <= 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Get PDF file
|
||||
if (pageRangeInfo.printRange == Ci.nsIPrintSettings.kRangeAllPages) {
|
||||
pageRangeInfo.startPageRange = 1;
|
||||
pageRangeInfo.endPageRange = totalPageNumber;
|
||||
} else {
|
||||
if (pageRangeInfo.startPageRange < 1) {
|
||||
pageRanges.startPageRange = 1;
|
||||
}
|
||||
if (pageRangeInfo.endPageRange > totalPageNumber) {
|
||||
pageRangeInfo.endPageRange = totalPageNumber;
|
||||
}
|
||||
if (pageRangeInfo.startPageRange > pageRangeInfo.endPageRange) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
// XXX We only support one page range now:
|
||||
// Gecko is using OS native print dialog now. And the print dialog
|
||||
// on Linux do support multiple page ranges. However, there is no
|
||||
// existing XPCOM interface for us to get the multiple page ranges
|
||||
// information. In addition, the print dialog on Mac and Windows only
|
||||
// support one page range.
|
||||
//
|
||||
// The behavior of printing muliple page ranges on Linux now is we
|
||||
// will print out the pages from the minimum start page to the
|
||||
// maximum last page of the page ranges.
|
||||
let pageRanges = [{ from: pageRangeInfo.startPageRange - 1,
|
||||
to: pageRangeInfo.endPageRange - 1 }];
|
||||
let pageRangeCount = pageRanges.length;
|
||||
let bufferId = instance.rt.call(new InterfaceMemberCall(
|
||||
"PPP_Printing(Dev);0.6", "PrintPages", {
|
||||
instance, page_ranges: pageRanges, page_range_count: pageRangeCount
|
||||
}), true);
|
||||
if (!bufferId) {
|
||||
return;
|
||||
}
|
||||
let buffer = PP_Resource.lookup(bufferId);
|
||||
|
||||
// Save PDF to file
|
||||
let file = Services.dirsvc.get(PRINT_CONTENT_TEMP_KEY, Ci.nsIFile);
|
||||
file.append(PRINT_FILE_NAME);
|
||||
file.createUnique(Ci.nsIFile.NORMAL_FILE_TYPE, PR_IRUSR | PR_IWUSR);
|
||||
let stream = Cc["@mozilla.org/network/file-output-stream;1"].
|
||||
createInstance(Ci.nsIFileOutputStream);
|
||||
stream.init(file, PR_RDWR | PR_CREATE_FILE | PR_TRUNCATE,
|
||||
PR_IRUSR | PR_IWUSR, 0);
|
||||
let binaryStream = Cc["@mozilla.org/binaryoutputstream;1"].
|
||||
createInstance(Ci.nsIBinaryOutputStream);
|
||||
binaryStream.setOutputStream(stream);
|
||||
let data = new Uint8ClampedArray(instance.rt.getCachedBuffer(buffer.mem));
|
||||
binaryStream.writeByteArray(Array.from(data), buffer.size);
|
||||
|
||||
// End of printing
|
||||
stream.flush();
|
||||
stream.close();
|
||||
buffer.unmap();
|
||||
buffer.release();
|
||||
instance.rt.call(new InterfaceMemberCall(
|
||||
"PPP_Printing(Dev);0.6", "End", { instance }), true);
|
||||
// We need permission for printing PDF file
|
||||
instance.mm.sendAsyncMessage("ppapipdf.js:printPDF", {
|
||||
contentTempKey: PRINT_CONTENT_TEMP_KEY, fileName: PRINT_FILE_NAME });
|
||||
},
|
||||
|
||||
/**
|
||||
* void SearchString(
|
||||
* [in] PP_Instance instance,
|
||||
|
|
|
@ -197,6 +197,10 @@ class Toolbar {
|
|||
case 'secondaryDownload':
|
||||
this._viewport.save();
|
||||
break;
|
||||
case 'print':
|
||||
case 'secondaryPrint':
|
||||
this._viewport.print();
|
||||
break;
|
||||
case 'pageRotateCw':
|
||||
this._viewport.rotateClockwise();
|
||||
break;
|
||||
|
|
|
@ -659,6 +659,12 @@ class Viewport {
|
|||
});
|
||||
}
|
||||
|
||||
print() {
|
||||
this._doAction({
|
||||
type: 'startPrint'
|
||||
});
|
||||
}
|
||||
|
||||
// A handler for delivering messages to runtime.
|
||||
registerActionHandler(handler) {
|
||||
if (typeof handler === 'function') {
|
||||
|
|
|
@ -12,6 +12,7 @@
|
|||
const { classes: Cc, interfaces: Ci, results: Cr, utils: Cu } = Components;
|
||||
|
||||
Cu.import("resource://gre/modules/XPCOMUtils.jsm");
|
||||
Cu.import("resource://gre/modules/Services.jsm");
|
||||
|
||||
XPCOMUtils.defineLazyModuleGetter(this, "NetUtil",
|
||||
"resource://gre/modules/NetUtil.jsm");
|
||||
|
@ -20,6 +21,10 @@ XPCOMUtils.defineLazyModuleGetter(this, "PrivateBrowsingUtils",
|
|||
|
||||
let mm = pluginElement.frameLoader.messageManager;
|
||||
let containerWindow = pluginElement.ownerGlobal;
|
||||
// We have to cache the print settings for printing PDF file here, since in
|
||||
// general we are not able to send XPCOM object across processes.
|
||||
let printSettings = {};
|
||||
|
||||
// Prevent the drag event's default action on the element, to avoid dragging of
|
||||
// that element while the user selects text.
|
||||
pluginElement.addEventListener("dragstart",
|
||||
|
@ -110,6 +115,71 @@ mm.addMessageListener("ppapipdf.js:setHash", ({ data }) => {
|
|||
}
|
||||
});
|
||||
|
||||
mm.addMessageListener("ppapipdf.js:getPrintSettings", () => {
|
||||
let PSSVC = Cc["@mozilla.org/gfx/printsettings-service;1"].
|
||||
getService(Ci.nsIPrintSettingsService);
|
||||
printSettings = PSSVC.globalPrintSettings;
|
||||
let webBrowserPrint =
|
||||
containerWindow.QueryInterface(Ci.nsIInterfaceRequestor).
|
||||
getInterface(Ci.nsIWebBrowserPrint);
|
||||
let PPSVC = Cc["@mozilla.org/embedcomp/printingprompt-service;1"].
|
||||
getService(Ci.nsIPrintingPromptService);
|
||||
PPSVC.showPrintDialog(containerWindow, webBrowserPrint, printSettings);
|
||||
|
||||
// XPCOM object should not in general be sent across processes. So we have to
|
||||
// copy out only the info PDFium needed and send it back to jsplugin process
|
||||
// for getting PDF file.
|
||||
let trimPrintSettings = {};
|
||||
trimPrintSettings.marginTop = printSettings.marginTop;
|
||||
trimPrintSettings.marginLeft = printSettings.marginLeft;
|
||||
trimPrintSettings.marginBottom = printSettings.marginBottom;
|
||||
trimPrintSettings.marginRight = printSettings.marginRight;
|
||||
trimPrintSettings.unwriteableMarginLeft =
|
||||
printSettings.unwriteableMarginLeft;
|
||||
trimPrintSettings.unwriteableMarginTop =
|
||||
printSettings.unwriteableMarginTop;
|
||||
trimPrintSettings.unwriteableMarginRight =
|
||||
printSettings.unwriteableMarginRight;
|
||||
trimPrintSettings.unwriteableMarginBottom =
|
||||
printSettings.unwriteableMarginBottom;
|
||||
trimPrintSettings.paperWidth = printSettings.paperWidth;
|
||||
trimPrintSettings.paperHeight = printSettings.paperHeight;
|
||||
trimPrintSettings.paperSizeUnit = printSettings.paperSizeUnit;
|
||||
trimPrintSettings.orientation = printSettings.orientation;
|
||||
trimPrintSettings.shrinkToFit = printSettings.shrinkToFit;
|
||||
trimPrintSettings.printInColor = printSettings.printInColor;
|
||||
trimPrintSettings.printRange = printSettings.printRange;
|
||||
trimPrintSettings.startPageRange = printSettings.startPageRange;
|
||||
trimPrintSettings.endPageRange = printSettings.endPageRange;
|
||||
|
||||
mm.sendAsyncMessage("ppapipdf.js:printsettingschanged", {
|
||||
trimPrintSettings });
|
||||
});
|
||||
|
||||
mm.addMessageListener("ppapipdf.js:printPDF", ({ data }) => {
|
||||
let file = Services.dirsvc.get(data.contentTempKey, Ci.nsIFile);
|
||||
file.append(data.fileName);
|
||||
if (!file.exists()) {
|
||||
return;
|
||||
}
|
||||
|
||||
let webBrowserPrint =
|
||||
containerWindow.QueryInterface(Ci.nsIInterfaceRequestor).
|
||||
getInterface(Ci.nsIWebBrowserPrint);
|
||||
if (!webBrowserPrint || !webBrowserPrint.printPDF) {
|
||||
file.remove(false);
|
||||
return;
|
||||
}
|
||||
|
||||
webBrowserPrint.printPDF(file.path, printSettings)
|
||||
.then(() => {
|
||||
file.remove(false);
|
||||
})
|
||||
.catch(() => {
|
||||
file.remove(false);
|
||||
});
|
||||
});
|
||||
|
||||
mm.addMessageListener("ppapipdf.js:save", () => {
|
||||
let url = containerWindow.document.location;
|
||||
let filename = "document.pdf";
|
||||
|
|
Загрузка…
Ссылка в новой задаче