<?xml version="1.0" encoding="UTF-8" standalone="yes" ?>
<!DOCTYPE bugzilla SYSTEM "https://www.w3.org/Bugs/Public/page.cgi?id=bugzilla.dtd">

<bugzilla version="5.0.4"
          urlbase="https://www.w3.org/Bugs/Public/"
          
          maintainer="sysbot+bugzilla@w3.org"
>

    <bug>
          <bug_id>22698</bug_id>
          
          <creation_ts>2013-07-16 17:37:25 +0000</creation_ts>
          <short_desc>&lt;canvas&gt;: API to make &lt;canvas&gt; prettier when printing</short_desc>
          <delta_ts>2017-07-21 10:51:28 +0000</delta_ts>
          <reporter_accessible>1</reporter_accessible>
          <cclist_accessible>1</cclist_accessible>
          <classification_id>1</classification_id>
          <classification>Unclassified</classification>
          <product>WHATWG</product>
          <component>HTML</component>
          <version>unspecified</version>
          <rep_platform>Other</rep_platform>
          <op_sys>All</op_sys>
          <bug_status>RESOLVED</bug_status>
          <resolution>WONTFIX</resolution>
          
          
          <bug_file_loc></bug_file_loc>
          <status_whiteboard></status_whiteboard>
          <keywords></keywords>
          <priority>P3</priority>
          <bug_severity>enhancement</bug_severity>
          <target_milestone>Needs Impl Interest</target_milestone>
          
          
          <everconfirmed>1</everconfirmed>
          <reporter name="Ian &apos;Hixie&apos; Hickson">ian</reporter>
          <assigned_to name="Ian &apos;Hixie&apos; Hickson">ian</assigned_to>
          <cc>annevk</cc>
    
    <cc>esprehn</cc>
    
    <cc>ian</cc>
    
    <cc>ikilpatrick</cc>
    
    <cc>junov</cc>
    
    <cc>jviereck.dev</cc>
    
    <cc>mike</cc>
    
    <cc>roc</cc>
          
          <qa_contact>contributor</qa_contact>

      

      

      

          <comment_sort_order>oldest_to_newest</comment_sort_order>  
          <long_desc isprivate="0" >
    <commentid>90808</commentid>
    <comment_count>0</comment_count>
    <who name="Ian &apos;Hixie&apos; Hickson">ian</who>
    <bug_when>2013-07-16 17:37:25 +0000</bug_when>
    <thetext>On Wed, 26 Sep 2012, Julian Viereck wrote:
&gt; 
&gt; This email is about proposing a new attribute &quot;printCallback&quot; on the 
&gt; HTMLCanvasElement (in the following called &quot;Canvas&quot;). This new API 
&gt; allows to:
&gt; 
&gt; * define the content of a canvas element during the printing progress
&gt; * send the canvas&apos; content without rasterization to the printer
&gt; 
&gt; The basic API was implemented in [1] and is available in Firefox Nightly 
&gt; 18.
&gt; 
&gt; # Motivation And Use-Case
&gt; 
&gt; The motivation for designing and implementing the API was to add proper 
&gt; printing support for the PDF.JS project. The PDF.JS project is an 
&gt; implementation of a PDF viewer using only web technologies. Without this 
&gt; API it is not possible to:
&gt; 
&gt; * render only the pages needed for printing. A webpage is printed with 
&gt; the content visible at the moment the print action is started. For 
&gt; PDF.JS this means that all pages are required to be rendered before 
&gt; printing. Rendering all the pages takes quite some time for complex and 
&gt; huge documents (&gt; 100 pages). But the user might only want to print the 
&gt; first page. That means, the user waits for unnecessary computation to 
&gt; finish.
&gt;
&gt; * print the content of a canvas element without rasterization artifacts 
&gt; on the printout. One could increase the size of the canvas such that the 
&gt; rasterization doesn&apos;t becomes visible, but this is not possible due to 
&gt; the large usage of memory going with this. Using a different way to 
&gt; render the pages than using canvas (e.g. SVG) is not possible, due to 
&gt; memory and performance issues.
&gt; 
&gt; Although not directly relevant to PDF.JS - it&apos;s also not possible to
&gt; 
&gt; * define the content of a printed page that looks exactly the same cross 
&gt; all user agents. There are small variations, that cause breaks and 
&gt; styles to look slightly different between user agents. Using CSS it&apos;s 
&gt; possible to make one canvas element take up one physical page and then 
&gt; precisely layout content on the canvas.
&gt; 
&gt; (I will later describe briefly how the API was used to solve these 
&gt; issues.)
&gt; 
&gt; # Actual API
&gt; 
&gt; The actual API looks like this:
&gt; 
&gt; The &quot;printCallback&quot; attribute on a Canvas takes a callback. This 
&gt; callback is invoked when the canvas with a printCallback is printed. A 
&gt; &quot;printState&quot; object is passed as argument to the callback. The 
&gt; printState object has a &quot;context&quot; property, which points to a 
&gt; CanvasRenderingContext2D object. Against this context all known 
&gt; operations of the CanvasRenderingContext2D are executable. However, the 
&gt; CanvasRenderingContext2D doesn&apos;t rasterize the operations but instead 
&gt; forward them directly to the printer - instead of drawing to a pixel 
&gt; surface it&apos;s more like drawing to a vector surface. The result of the 
&gt; operations show up on the canvas when printed, but are not visible on 
&gt; the screen. The &quot;printState.done()&quot; function must be called once all 
&gt; drawing operations for the canvas are done and the printing should 
&gt; progress. This was added to allow the printCallback to perform 
&gt; asynchronous tasks.
&gt; 
&gt; A simple example of the API looks like this:
&gt; 
&gt; var canvas = document.getElementById(&apos;canvas&apos;);
&gt; var ctx = canvas.getContext(&apos;2d&apos;);
&gt; ctx.fillText(&apos;Hi there.&apos;, 50, 50);
&gt; 
&gt; canvas.printCallback = function(printState) {
&gt;   var printCtx = printState.context;
&gt;   printCtx.fillText(&apos;I\&apos;m only visible when printed.&apos;, 50, 50);
&gt;   printState.done();
&gt; };
&gt; 
&gt; You can try this example out in [2] using Firefox Nighlty and see the 
&gt; results as a PDF in [3]. Notice that you can select the text in the PDF 
&gt; linked in [3]. (Note: in the linked example [2], the callback is called 
&gt; &quot;mozPrintCallback&quot; as the API is currently prefixed in Gecko. The canvas 
&gt; output is rasterized on Windows and Linux due to a bug in Gecko at the 
&gt; moment.)
&gt; 
&gt; Some more details on the behavior of the API:
&gt; 
&gt; * the printCallback function is only invoked on the canvases that will 
&gt; be visible in the print output.
&gt; 
&gt; * there is only one printCallback called at the time. After the 
&gt; &quot;printState.done()&quot; function is called, the next printCallback function 
&gt; gets invoked assuming there is another canvas that gets printed and has 
&gt; a printCallback specified.
&gt; 
&gt; * the order the printCallbacks of the canvases are called follows the 
&gt; output order of the canvases in the printout.
&gt; 
&gt; * the resolution on the printContext is the same as when drawing to the 
&gt; canvas on the page. E.g. if a canvas has the attribute &quot;width&quot; set to 
&gt; &quot;100&quot; and by using CSS the canvas takes 10 cm in width on the printout, 
&gt; then 1 unit on the context corresponds to 0.1 cm.
&gt; 
&gt; * the putImageData and getImageData functions on the 
&gt; CanvasRenderingContext2D use the same pixel resolution (width/height) as 
&gt; the canvas on the page (this results in the data of the getImageData 
&gt; function to be rasterized).
&gt; 
&gt; * the &quot;canvas&quot; property on the printContext points to the canvas on the 
&gt; page and not the canvas element that is printed. Otherwise it&apos;s possible 
&gt; to change the layout of the printing while printing. As the canvas on 
&gt; the page might not be available anymore (e.g. the canvas was removed and 
&gt; garbage collected from the document before the printCallback gets 
&gt; invoked), the &quot;canvas&quot; property might be &quot;undefined&quot; or &quot;null&quot;.
&gt; 
&gt; * the window.onafterprint event is called either
&gt;   1. after all printCallbacks are done and the page is ready for 
&gt;      printing
&gt;   2. the printing got aborted using the print dialog.
&gt; 
&gt; * the printContext holds no content when passed to the callback and 
&gt; takes the default values (for transformation matrix, styles etc.) of a 
&gt; CanvasRenderingContext2D
&gt; 
&gt; * the printContext is a CanvasRenderingContext2D but instead of using a 
&gt; pixel map to store the drawing operations result, the operations are 
&gt; forwarded to the printer without rasterization.
&gt; 
&gt; * the API does not change the layout of the canvas element on the page.
&gt; 
&gt; # Open Discussion:
&gt; 
&gt; * There is no way to abort in case something goes wrong. E.g. printing 
&gt; to the canvas might require a successful network request, but the 
&gt; request failed.
&gt; 
&gt; * The printState.done() function gets eventually never called and 
&gt; therefore printing the document might never finish.
&gt; 
&gt; # How The PrintCallback-API Solved The Problems For PDF.JS
&gt; 
&gt; * using CSS all content except a single div is hidden during printing
&gt; 
&gt; * when the beforePrint event is fired it is checked if the webpage is 
&gt; setup for printing or not. If it is not, the webpage is setup and the 
&gt; print action is canceled. Otherwise, the printing is not prevented and 
&gt; happens as regular
&gt; 
&gt; * the &quot;setup webpage for printing&quot; consists of the following steps
&gt; 1. for each page of the PDF document, a canvas element is created and 
&gt; insert to the div that is visible during printing
&gt; 2. using CSS, the canvas inside the print-visible div take up an entire 
&gt; page in the later printout
&gt; 3. for each canvas the `mozPrintCallback` is set. If the callback 
&gt; function is called, the pdf page corresponding to the canvas is loaded 
&gt; and drawn on the canvas. Once finished, the `printState.done()` function 
&gt; is called
&gt; 
&gt; * after the user set the print settings in the print dialog, the webpage 
&gt; is printed
&gt; 
&gt; * for the pages that are required for the printout, the mozPrintCallback 
&gt; is called on the canvas for these pages
&gt; 
&gt; * after the printing finished (detected by listening to the afterPrint 
&gt; event), the created canvases are removed again to save memory.
&gt; 
&gt; [1]: Mozilla Bug 745025 - Implement CanvasElement.mozPrintCallback: 
&gt;      https://bugzilla.mozilla.org/show_bug.cgi?id=745025
&gt; [2]: http://jsfiddle.net/FPNMM/2/embedded/js%2Cresult/
&gt; [3]: http://n.ethz.ch/~jviereck/drop/mozPrintCallback_output.pdf

On Wed, 23 Jan 2013, Julian Viereck wrote:
&gt;
&gt; Discussing the proposal with Robert &quot;roc&quot; O&apos;Callahan, we came up with 
&gt; the following adjustments. They are targeting the &quot;Some more details on 
&gt; the behavior of the API&quot; from before:
&gt; 
&gt; (1) With the previous proposal, printCallbacks are executed even after 
&gt; the window is unloaded. We think this is not a good idea. There 
&gt; shouldn&apos;t be any JavaScript execution after a window is unloaded. 
&gt; Therefore let&apos;s add the following:
&gt; 
&gt; Encourage the UA to prevent closing the window while print callbacks
&gt; are pending. If the window is nevertheless closed and while some
&gt; printCallbacks have not completed yet, all printCallbacks are
&gt; canceled, the JavaScript execution is stopped and the print job is
&gt; aborted. Canceling the printCallbacks is done to prevent any
&gt; JavaScript execution after the window is unloaded.
&gt; 
&gt; (2) Given the change in (1), we can now change the following point:
&gt; 
&gt; &gt; * the &quot;canvas&quot; property on the printContext points to the canvas on 
&gt; &gt; the page and not the canvas element that is printed. Otherwise it&apos;s 
&gt; &gt; possible to change the layout of the printing while printing. As the 
&gt; &gt; canvas on the page might not be available anymore (e.g. the canvas was 
&gt; &gt; removed and garbage collected from the document before the 
&gt; &gt; printCallback gets invoked), the &quot;canvas&quot; property might be 
&gt; &gt; &quot;undefined&quot; or &quot;null&quot;.
&gt; 
&gt; to:
&gt; 
&gt; * the &quot;canvas&quot; property on the printContext points to the canvas on the 
&gt; page and not the canvas element that is printed. Otherwise it&apos;s possible 
&gt; to change the layout of the printing while printing.
&gt; 
&gt; Simply saying, if the window object is always alive while the 
&gt; printCallbacks are happening (thanks to (1)), the &quot;canvas&quot; property on 
&gt; the printContext can always point to the canvas on the page.

On Mon, 28 Jan 2013, Elliott Sprehn wrote:
&gt;
&gt; 1) I feel like this should probably be an event. I don&apos;t know why we&apos;re 
&gt; inventing new callback facilities everywhere.
&gt; 
&gt; canvas.onprintcanvas = function(e) { e.printState ... }

On Thu, 7 Feb 2013, Julian Viereck wrote:
&gt; 
&gt; A event might be dispatched to multiple listeners. In the scenario with 
&gt; the print callback it makes only sense to have one function that defines 
&gt; the look of the canvas during printing. Therefore I&apos;ve choosen to not 
&gt; use the event-listener pattern but instead a single callback function.

On Thu, 7 Feb 2013, Elliott Sprehn wrote:
&gt;
&gt; It should be an event. There are legitimate reasons for multiple 
&gt; listeners, for instance I might have a listener on body that adds 
&gt; watermarks and a listener on the canvas that does just the drawing.
&gt; 
&gt; Not using events also limits my ability to globally hook the facility. 
&gt; For instance I&apos;d like to do:
&gt; 
&gt; $(document).printcanvas(function(e) {
&gt;   if (drawingFunctions[e.target.id])
&gt;     drawingFunctions[e.target.id](e.target);
&gt; });

On Mon, 28 Jan 2013, Elliott Sprehn wrote:
&gt; 
&gt; 2) What does &quot;send the canvas&apos; content without rasterization to the 
&gt; printer&quot; mean? How are blending and overlapping images handled? Your 
&gt; current description makes it sound like if I did two drawImage() calls 
&gt; it would make my printer print the images on top of each other.

On Thu, 7 Feb 2013, Elliott Sprehn wrote:
&gt; 
&gt; [...] you don&apos;t mean sent to the physical printer without rasterization 
&gt; then, you mean it&apos;s just spooled to the printer driver which may 
&gt; rasterize it to send it to the device. That seems to run into operating 
&gt; system and implementation limitations. I don&apos;t know if the spec should 
&gt; require that behavior.

On Mon, 28 Jan 2013, Elliott Sprehn wrote:
&gt; 
&gt; 3) If we&apos;re advocating that developers &quot;put a canvas on every page that 
&gt; covers the whole page&quot; as the standard way to handle large document 
&gt; printing why not have a handler that gets given a canvas for every page 
&gt; automatically instead of requiring the developer to insert it 
&gt; themselves. This seems much easier, and handles the memory management 
&gt; for since we can drop the backing buffer between events for each page.
&gt; 
&gt; document.onprintpage = function(e) {
&gt;   e.index // number of the page
&gt;   e.range // range that encompasses all the nodes to print
&gt;   e.canvas // canvas to send drawing commands to for printing
&gt; };

On Tue, 29 Jan 2013, Robert O&apos;Callahan wrote:
&gt;
&gt; Another use-case is when you have a document that&apos;s regular HTML and 
&gt; just happens to contain one or more &lt;canvas&gt; images that should be 
&gt; beautiful when printed.

On Tue, 29 Jan 2013, Elliott Sprehn wrote:
&gt; 
&gt; Why can&apos;t I do this for &lt;video&gt; or &lt;object&gt; or any number of other 
&gt; things then? All those things want to be beautiful when printed too. :)
&gt; 
&gt; In that world I really think we want something closer to 
&gt; Element#printCallback.

On Thu, 7 Feb 2013, Elliott Sprehn wrote:
&gt;
&gt; [...] Why can&apos;t I do the same thing for &lt;img&gt; ? Cocoa lets me hook
&gt; NSView drawRect: and draw whatever I want at print time.

On Tue, 29 Jan 2013, Robert O&apos;Callahan wrote:
&gt; 
&gt; That doesn&apos;t make sense to me. &lt;canvas&gt; is special because we have APIs 
&gt; to draw into it, and authors can use the same code that draws a screen 
&gt; canvas to draw a beautiful print canvas with very few changes. The 
&gt; contents of &lt;video&gt; and &lt;object&gt; aren&apos;t rendered by author JS code, so 
&gt; it&apos;s not natural to provide an API that lets them be rendered by author 
&gt; JS code just for the print case. However it is possible with our 
&gt; proposal for authors to use some clever @media CSS to replace any 
&gt; element with a pretty &lt;canvas&gt; version if they need to.
&gt; 
&gt; Also, &lt;video&gt; and &lt;object&gt; (and &lt;img&gt;) have features that could be used 
&gt; to have them print nicely without author script having to take over 
&gt; their drawing. For example, on some platforms a plugin in an &lt;object&gt; 
&gt; can define custom printing behavior.

Presumably &lt;img srcset&gt; also provides a solution for printing.

On Mon, 28 Jan 2013, Elliott Sprehn wrote:
&gt; 
&gt; 4) SVG is for vector graphics, not canvas. Why can&apos;t I replace an entire 
&gt; page with an &lt;svg&gt; instead of drawing to a canvas? :)

On Tue, 29 Jan 2013, Robert O&apos;Callahan wrote:
&gt;
&gt; SVG doesn&apos;t fit some use-cases very well, for example, when you generate 
&gt; graphics from some underlying (large) data model.

On Tue, 29 Jan 2013, Elliott Sprehn wrote:
&gt; 
&gt; Vector graphics is exactly what you want when the data model is really 
&gt; big though since it lets you prune out things you know can&apos;t be seen and 
&gt; stream the commands directly to hardware without rasterization as 
&gt; they&apos;re attempting to do with &lt;canvas&gt; magic in this proposal.
&gt; 
&gt; If SVG is broken for this use case we should fix that, it seems really 
&gt; sad to &quot;give up&quot; on SVG and bolt vector graphics onto &lt;canvas&gt;.

On Tue, 29 Jan 2013, Robert O&apos;Callahan wrote:
&gt; 
&gt; In our prototype implementation in Firefox, when drawing in a 
&gt; printCallback and printing to a PDF, if the author&apos;s canvas drawing 
&gt; calls can be directly supported by PDF then they are emitted into the 
&gt; PDF and not rasterized at all.
&gt; 
&gt; If you have a very large data model to render, converting directly from 
&gt; the data model to (GL or 2D) drawing commands is a lot more efficient 
&gt; than building a persistent CSS-styled DOM as an intermediate step. 
&gt; Having the browser optimize away that intermediate step doesn&apos;t seem 
&gt; feasible.</thetext>
  </long_desc><long_desc isprivate="0" >
    <commentid>109451</commentid>
    <comment_count>1</comment_count>
    <who name="Ian &apos;Hixie&apos; Hickson">ian</who>
    <bug_when>2014-07-29 00:11:19 +0000</bug_when>
    <thetext>roc, esprehn: I assume Mozilla and Chrome are both on board here? Do either of you have an implementation of this yet that I should reverse-engineer?</thetext>
  </long_desc><long_desc isprivate="0" >
    <commentid>109454</commentid>
    <comment_count>2</comment_count>
    <who name="Robert O&apos;Callahan (Mozilla)">roc</who>
    <bug_when>2014-07-29 00:20:15 +0000</bug_when>
    <thetext>We have an implementation.</thetext>
  </long_desc><long_desc isprivate="0" >
    <commentid>109459</commentid>
    <comment_count>3</comment_count>
    <who name="Elliott Sprehn">esprehn</who>
    <bug_when>2014-07-29 05:09:16 +0000</bug_when>
    <thetext>(In reply to Ian &apos;Hixie&apos; Hickson from comment #1)
&gt; roc, esprehn: I assume Mozilla and Chrome are both on board here? Do either
&gt; of you have an implementation of this yet that I should reverse-engineer?

We haven&apos;t implemented anything like this, but I think some kind of &quot;print&quot; event for &lt;canvas&gt; would be reasonable given the use cases here. I don&apos;t think we should add a callback though.</thetext>
  </long_desc><long_desc isprivate="0" >
    <commentid>111110</commentid>
    <comment_count>4</comment_count>
    <who name="Ian &apos;Hixie&apos; Hickson">ian</who>
    <bug_when>2014-09-05 22:16:30 +0000</bug_when>
    <thetext>Not sure what to do here. There doesn&apos;t appear to be a proposal with multiple vendors supporting it.</thetext>
  </long_desc><long_desc isprivate="0" >
    <commentid>111121</commentid>
    <comment_count>5</comment_count>
    <who name="Robert O&apos;Callahan (Mozilla)">roc</who>
    <bug_when>2014-09-06 04:13:40 +0000</bug_when>
    <thetext>I&apos;m happy to consider any counter-proposal here that meets the needs of applications like pdf.js. &quot;Just use SVG&quot; is not acceptable for performance reasons for the reasons quoted from me in comment #0.

I&apos;m happy to change the callback function to an event as per Elliott&apos;s comments. I don&apos;t think having multiple receivers working together to paint the canvas makes sense, but I also don&apos;t think it&apos;s worth blocking the feature on that point.

I don&apos;t know what other objections Elliott (or anyone else) has, that haven&apos;t already been adequately addressed.</thetext>
  </long_desc><long_desc isprivate="0" >
    <commentid>125997</commentid>
    <comment_count>6</comment_count>
    <who name="Justin Novosad">junov</who>
    <bug_when>2016-04-21 17:01:28 +0000</bug_when>
    <thetext>I believe this issue is being addressed indirectly by the css paint api:
https://drafts.css-houdini.org/css-paint-api/

For a project like PDF.js, there is one big gap in the css paint feature though: it does not do text rendering.</thetext>
  </long_desc><long_desc isprivate="0" >
    <commentid>126007</commentid>
    <comment_count>7</comment_count>
    <who name="Ian Kilpatrick">ikilpatrick</who>
    <bug_when>2016-04-21 17:37:32 +0000</bug_when>
    <thetext>+1 CSS Paint API Should be able to handle this nicely.

CSS Paint API doesn&apos;t assume the resolution which the image will be rastered at, so the UA can upscale / downscale as they see fit for printing / scaling / etc.

We stripped out Fonts for CSS Paint API initially, but they should be a small add-on once we figure out the font resolution behavior in paint.

 - Ian</thetext>
  </long_desc><long_desc isprivate="0" >
    <commentid>128750</commentid>
    <comment_count>8</comment_count>
    <who name="Anne">annevk</who>
    <bug_when>2017-07-21 10:51:28 +0000</bug_when>
    <thetext>If this is still desired, please file a new issue at https://github.com/whatwg/html/issues/new. (Seems like it isn&apos;t from the last couple of comments.)</thetext>
  </long_desc>
      
      

    </bug>

</bugzilla>