blob: d0a737699c88dfc9371401f2bb0bee41bfb02f22 [file] [log] [blame]
// Copyright (c) 2012 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
cr.define('print_preview', function() {
'use strict';
/**
* Interface to the Chromium print preview generator.
* @param {!print_preview.DestinationStore} destinationStore Used to get the
* currently selected destination.
* @param {!print_preview.PrintTicketStore} printTicketStore Used to read the
* state of the ticket and write document information.
* @param {!print_preview.NativeLayer} nativeLayer Used to communicate to
* Chromium's preview rendering system.
* @param {!print_preview.DocumentInfo} documentInfo Document data model.
* @constructor
* @extends {cr.EventTarget}
*/
function PreviewGenerator(
destinationStore, printTicketStore, nativeLayer, documentInfo) {
cr.EventTarget.call(this);
/**
* Used to get the currently selected destination.
* @type {!print_preview.DestinationStore}
* @private
*/
this.destinationStore_ = destinationStore;
/**
* Used to read the state of the ticket and write document information.
* @type {!print_preview.PrintTicketStore}
* @private
*/
this.printTicketStore_ = printTicketStore;
/**
* Interface to the Chromium native layer.
* @type {!print_preview.NativeLayer}
* @private
*/
this.nativeLayer_ = nativeLayer;
/**
* Document data model.
* @type {!print_preview.DocumentInfo}
* @private
*/
this.documentInfo_ = documentInfo;
/**
* ID of current in-flight request. Requests that do not share this ID will
* be ignored.
* @type {number}
* @private
*/
this.inFlightRequestId_ = -1;
/**
* Whether the previews are being generated in landscape mode.
* @type {boolean}
* @private
*/
this.isLandscapeEnabled_ = false;
/**
* Whether the previews are being generated with a header and footer.
* @type {boolean}
* @private
*/
this.isHeaderFooterEnabled_ = false;
/**
* Whether the previews are being generated in color.
* @type {boolean}
* @private
*/
this.colorValue_ = false;
/**
* Whether the document should be fitted to the page.
* @type {boolean}
* @private
*/
this.isFitToPageEnabled_ = false;
/**
* Page ranges setting used used to generate the last preview.
* @type {!Array.<object.<{from: number, to: number}>>}
* @private
*/
this.pageRanges_ = null;
/**
* Margins type used to generate the last preview.
* @type {!print_preview.ticket_items.MarginsType.Value}
* @private
*/
this.marginsType_ = print_preview.ticket_items.MarginsType.Value.DEFAULT;
/**
* Whether the document should have element CSS backgrounds printed.
* @type {boolean}
* @private
*/
this.isCssBackgroundEnabled_ = false;
/**
* Destination that was selected for the last preview.
* @type {print_preview.Destination}
* @private
*/
this.selectedDestination_ = null;
/**
* Event tracker used to keep track of native layer events.
* @type {!EventTracker}
* @private
*/
this.tracker_ = new EventTracker();
this.addEventListeners_();
};
/**
* Event types dispatched by the preview generator.
* @enum {string}
*/
PreviewGenerator.EventType = {
// Dispatched when the document can be printed.
DOCUMENT_READY: 'print_preview.PreviewGenerator.DOCUMENT_READY',
// Dispatched when a page preview is ready. The previewIndex field of the
// event is the index of the page in the modified document, not the
// original. So page 4 of the original document might be previewIndex = 0 of
// the modified document.
PAGE_READY: 'print_preview.PreviewGenerator.PAGE_READY',
// Dispatched when the document preview starts to be generated.
PREVIEW_START: 'print_preview.PreviewGenerator.PREVIEW_START',
// Dispatched when the current print preview request fails.
FAIL: 'print_preview.PreviewGenerator.FAIL'
};
PreviewGenerator.prototype = {
__proto__: cr.EventTarget.prototype,
/**
* Request that new preview be generated. A preview request will not be
* generated if the print ticket has not changed sufficiently.
* @return {boolean} Whether a new preview was actually requested.
*/
requestPreview: function() {
if (!this.printTicketStore_.isTicketValidForPreview() ||
!this.printTicketStore_.isInitialized) {
return false;
}
if (!this.hasPreviewChanged_()) {
// Changes to these ticket items might not trigger a new preview, but
// they still need to be recorded.
this.marginsType_ = this.printTicketStore_.marginsType.getValue();
return false;
}
this.isLandscapeEnabled_ = this.printTicketStore_.landscape.getValue();
this.isHeaderFooterEnabled_ =
this.printTicketStore_.headerFooter.getValue();
this.colorValue_ = this.printTicketStore_.color.getValue();
this.isFitToPageEnabled_ = this.printTicketStore_.fitToPage.getValue();
this.pageRanges_ = this.printTicketStore_.pageRange.getPageRanges();
this.marginsType_ = this.printTicketStore_.marginsType.getValue();
this.isCssBackgroundEnabled_ =
this.printTicketStore_.cssBackground.getValue();
this.isSelectionOnlyEnabled_ =
this.printTicketStore_.selectionOnly.getValue();
this.selectedDestination_ = this.destinationStore_.selectedDestination;
this.inFlightRequestId_++;
this.nativeLayer_.startGetPreview(
this.destinationStore_.selectedDestination,
this.printTicketStore_,
this.documentInfo_,
this.inFlightRequestId_);
return true;
},
/** Removes all event listeners that the preview generator has attached. */
removeEventListeners: function() {
this.tracker_.removeAll();
},
/**
* Adds event listeners to the relevant native layer events.
* @private
*/
addEventListeners_: function() {
this.tracker_.add(
this.nativeLayer_,
print_preview.NativeLayer.EventType.PAGE_LAYOUT_READY,
this.onPageLayoutReady_.bind(this));
this.tracker_.add(
this.nativeLayer_,
print_preview.NativeLayer.EventType.PAGE_COUNT_READY,
this.onPageCountReady_.bind(this));
this.tracker_.add(
this.nativeLayer_,
print_preview.NativeLayer.EventType.PREVIEW_RELOAD,
this.onPreviewReload_.bind(this));
this.tracker_.add(
this.nativeLayer_,
print_preview.NativeLayer.EventType.PAGE_PREVIEW_READY,
this.onPagePreviewReady_.bind(this));
this.tracker_.add(
this.nativeLayer_,
print_preview.NativeLayer.EventType.PREVIEW_GENERATION_DONE,
this.onPreviewGenerationDone_.bind(this));
this.tracker_.add(
this.nativeLayer_,
print_preview.NativeLayer.EventType.PREVIEW_GENERATION_FAIL,
this.onPreviewGenerationFail_.bind(this));
},
/**
* Dispatches a PAGE_READY event to signal that a page preview is ready.
* @param {number} previewIndex Index of the page with respect to the pages
* shown in the preview. E.g an index of 0 is the first displayed page,
* but not necessarily the first original document page.
* @param {number} pageNumber Number of the page with respect to the
* document. A value of 3 means it's the third page of the original
* document.
* @param {number} previewUid Unique identifier of the preview.
* @private
*/
dispatchPageReadyEvent_: function(previewIndex, pageNumber, previewUid) {
var pageGenEvent = new Event(PreviewGenerator.EventType.PAGE_READY);
pageGenEvent.previewIndex = previewIndex;
pageGenEvent.previewUrl = 'chrome://print/' + previewUid.toString() +
'/' + (pageNumber - 1) + '/print.pdf';
this.dispatchEvent(pageGenEvent);
},
/**
* Dispatches a PREVIEW_START event. Signals that the preview should be
* reloaded.
* @param {number} previewUid Unique identifier of the preview.
* @param {number} index Index of the first page of the preview.
* @private
*/
dispatchPreviewStartEvent_: function(previewUid, index) {
var previewStartEvent = new Event(
PreviewGenerator.EventType.PREVIEW_START);
if (!this.documentInfo_.isModifiable) {
index = -1;
}
previewStartEvent.previewUrl = 'chrome://print/' +
previewUid.toString() + '/' + index + '/print.pdf';
this.dispatchEvent(previewStartEvent);
},
/**
* @return {boolean} Whether the print ticket has changed sufficiently to
* determine whether a new preview request should be issued.
* @private
*/
hasPreviewChanged_: function() {
var ticketStore = this.printTicketStore_;
return this.inFlightRequestId_ == -1 ||
!ticketStore.landscape.isValueEqual(this.isLandscapeEnabled_) ||
!ticketStore.headerFooter.isValueEqual(this.isHeaderFooterEnabled_) ||
!ticketStore.color.isValueEqual(this.colorValue_) ||
!ticketStore.fitToPage.isValueEqual(this.isFitToPageEnabled_) ||
this.pageRanges_ == null ||
!areRangesEqual(ticketStore.pageRange.getPageRanges(),
this.pageRanges_) ||
(!ticketStore.marginsType.isValueEqual(this.marginsType_) &&
!ticketStore.marginsType.isValueEqual(
print_preview.ticket_items.MarginsType.Value.CUSTOM)) ||
(ticketStore.marginsType.isValueEqual(
print_preview.ticket_items.MarginsType.Value.CUSTOM) &&
!ticketStore.customMargins.isValueEqual(
this.documentInfo_.margins)) ||
!ticketStore.cssBackground.isValueEqual(
this.isCssBackgroundEnabled_) ||
!ticketStore.selectionOnly.isValueEqual(
this.isSelectionOnlyEnabled_) ||
(this.selectedDestination_ !=
this.destinationStore_.selectedDestination);
},
/**
* Called when the page layout of the document is ready. Always occurs
* as a result of a preview request.
* @param {Event} event Contains layout info about the document.
* @private
*/
onPageLayoutReady_: function(event) {
// NOTE: A request ID is not specified, so assuming its for the current
// in-flight request.
var origin = new print_preview.Coordinate2d(
event.pageLayout.printableAreaX,
event.pageLayout.printableAreaY);
var size = new print_preview.Size(
event.pageLayout.printableAreaWidth,
event.pageLayout.printableAreaHeight);
var margins = new print_preview.Margins(
Math.round(event.pageLayout.marginTop),
Math.round(event.pageLayout.marginRight),
Math.round(event.pageLayout.marginBottom),
Math.round(event.pageLayout.marginLeft));
var o = print_preview.ticket_items.CustomMargins.Orientation;
var pageSize = new print_preview.Size(
event.pageLayout.contentWidth +
margins.get(o.LEFT) + margins.get(o.RIGHT),
event.pageLayout.contentHeight +
margins.get(o.TOP) + margins.get(o.BOTTOM));
this.documentInfo_.updatePageInfo(
new print_preview.PrintableArea(origin, size),
pageSize,
event.hasCustomPageSizeStyle,
margins);
},
/**
* Called when the document page count is received from the native layer.
* Always occurs as a result of a preview request.
* @param {Event} event Contains the document's page count.
* @private
*/
onPageCountReady_: function(event) {
if (this.inFlightRequestId_ != event.previewResponseId) {
return; // Ignore old response.
}
this.documentInfo_.updatePageCount(event.pageCount);
this.pageRanges_ = this.printTicketStore_.pageRange.getPageRanges();
},
/**
* Called when the print preview should be reloaded.
* @param {Event} event Contains the preview UID and request ID.
* @private
*/
onPreviewReload_: function(event) {
if (this.inFlightRequestId_ != event.previewResponseId) {
return; // Ignore old response.
}
var pageNumberSet = this.printTicketStore_.pageRange.getPageNumberSet();
this.dispatchPreviewStartEvent_(
event.previewUid, pageNumberSet.getPageNumberAt(0) - 1);
for (var i = 0; i < pageNumberSet.size; i++) {
var pageNumber = pageNumberSet.getPageNumberAt(i);
this.dispatchPageReadyEvent_(i, pageNumber, event.previewUid);
}
cr.dispatchSimpleEvent(this, PreviewGenerator.EventType.DOCUMENT_READY);
},
/**
* Called when a page's preview has been generated. Dispatches a
* PAGE_READY event.
* @param {Event} event Contains the page index and preview UID.
* @private
*/
onPagePreviewReady_: function(event) {
if (this.inFlightRequestId_ != event.previewResponseId) {
return; // Ignore old response.
}
var pageNumber = event.pageIndex + 1;
var pageNumberSet = this.printTicketStore_.pageRange.getPageNumberSet();
if (pageNumberSet.hasPageNumber(pageNumber)) {
var previewIndex = pageNumberSet.getPageNumberIndex(pageNumber);
if (previewIndex == 0) {
this.dispatchPreviewStartEvent_(event.previewUid, event.pageIndex);
}
this.dispatchPageReadyEvent_(
previewIndex, pageNumber, event.previewUid);
}
},
/**
* Called when the preview generation is complete. Dispatches a
* DOCUMENT_READY event.
* @param {Event} event Contains the preview UID and response ID.
* @private
*/
onPreviewGenerationDone_: function(event) {
if (this.inFlightRequestId_ != event.previewResponseId) {
return; // Ignore old response.
}
// Dispatch a PREVIEW_START event since non-modifiable documents don't
// trigger PAGE_READY events.
if (!this.documentInfo_.isModifiable) {
this.dispatchPreviewStartEvent_(event.previewUid, 0);
}
cr.dispatchSimpleEvent(this, PreviewGenerator.EventType.DOCUMENT_READY);
},
/**
* Called when the preview generation fails.
* @private
*/
onPreviewGenerationFail_: function() {
// NOTE: No request ID is returned from Chromium so its assumed its the
// current one.
cr.dispatchSimpleEvent(this, PreviewGenerator.EventType.FAIL);
}
};
// Export
return {
PreviewGenerator: PreviewGenerator
};
});