// 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.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 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 }; });