WebPrintAPI Proposal

243 views
Skip to first unread message

Julian Viereck

unread,
Mar 14, 2012, 5:02:08 PM3/14/12
to dev-tec...@lists.mozilla.org
Hi,

the PDF.JS team is dealing with printing support on the web. I looked at what's currently possible with the browser and shared some thoughts about it here:

https://gist.github.com/1953505#file_gistfile1.md

There are some comments on this G+ post:

https://plus.google.com/u/0/115228539776721208421/posts/LbVDmfALeey

Based on that feedback, I've started on a new WebPrint API proposal, that you can find here:

https://wiki.mozilla.org/WebAPI/WebPrintAPI

There isn't too much yet. So far I have scratched out the basic API and started some very basic implementation work. However, I hope this is enough to get some discussion about this proposed API going and I'd like to get some feedback what people think about it.

Best,

Julian

PS: This API was proposed on the WebAPI mailing list. I was told to share the API proposal on this list as well as some layout people might have an opinion on this as well and don't follow the WebAPI mailing list.

Robert O'Callahan

unread,
Mar 14, 2012, 6:26:00 PM3/14/12
to Julian Viereck, dev-tec...@lists.mozilla.org
On Wed, Mar 14, 2012 at 9:02 PM, Julian Viereck <
julian....@googlemail.com> wrote:

> the PDF.JS team is dealing with printing support on the web. I looked at
> what's currently possible with the browser and shared some thoughts about
> it here:
>
> https://gist.github.com/1953505#file_gistfile1.md
>
> There are some comments on this G+ post:
>
> https://plus.google.com/u/0/115228539776721208421/posts/LbVDmfALeey
>
> Based on that feedback, I've started on a new WebPrint API proposal, that
> you can find here:
>
> https://wiki.mozilla.org/WebAPI/WebPrintAPI
>
> There isn't too much yet. So far I have scratched out the basic API and
> started some very basic implementation work. However, I hope this is enough
> to get some discussion about this proposed API going and I'd like to get
> some feedback what people think about it.
>

We need to implement the CSS3 stuff anyway. How about:
-- Implement CSS3 Paged Media sufficient to make headers/footers and
margins go away, select page orientation and size. That shouldn't be too
hard.
-- Add an event to canvas that fires per-page during printing to render
that canvas content while we're printing the page it's on. This event would
provide a special canvas context that's compatible with the 2d interface
but internally renders at higher resolution (or possibly emits PS/PDF
directly).
Then you could create a print document that's just a series of canvases,
with the appropriate CSS to remove headers/footers/margins etc, and as we
print each page we'll run the callback on each canvas to paint it more
accurately. Any temporary resources could be thrown away after painting
that page.

Rob
--
“You have heard that it was said, ‘Love your neighbor and hate your enemy.’
But I tell you, love your enemies and pray for those who persecute you,
that you may be children of your Father in heaven. ... If you love those
who love you, what reward will you get? Are not even the tax collectors
doing that? And if you greet only your own people, what are you doing more
than others?" [Matthew 5:43-47]

Zack Weinberg

unread,
Mar 14, 2012, 8:24:15 PM3/14/12
to
On 03/14/2012 02:02 PM, Julian Viereck wrote:
>
> https://wiki.mozilla.org/WebAPI/WebPrintAPI
>
> There isn't too much yet. So far I have scratched out the basic API
> and started some very basic implementation work. However, I hope this
> is enough to get some discussion about this proposed API going and
> I'd like to get some feedback what people think about it.

A few small comments:

* Instead of forbidding px, define 1px = 1/96in
* similarly, ctx.putImageData should scale to 1px = 1/96in, unless the
image object has embedded resolution information, in which case it
should be honored
* .getImageData should, if possible, produce a raster at the true
printable resolution
* (0,0) should be the top left hand corner of the print area, not the paper
* CMYK opens a huge can of process worms (which the GCPM language you
linked to, doesn't even *begin* to cover); avoid for now
* No matter what the document does, Print Preview needs to be capable of
showing an as-accurate-as-possible WYSIWYG rendition; it's not clear to
me whether this is true in your proposal

zw

Zack Weinberg

unread,
Mar 14, 2012, 8:25:03 PM3/14/12
to
That's clever, I like it.

zw

Jet Villegas

unread,
Mar 14, 2012, 8:56:22 PM3/14/12
to rob...@ocallahan.org, Julian Viereck, dev-tec...@lists.mozilla.org
IIUC you're asking that we implement the default behavior for CSS3 Paged Media margin boxes:

Margin boxes have an initial value of zero for padding, border and margin. The default height of top boxes is the value of the page box's top margin. The default height of the bottom boxes is the value of the page box's bottom margin.
The initial value for 'content' is 'none'. The initial 'width' and 'height' of margin boxes is 'auto'.

http://www.w3.org/TR/2006/WD-css3-page-20061010/#populating-margin-boxes

In other words, we won't be rendering any content for headers or footers.

You're also asking that we implement page size, and portrait vs. landscape:
http://www.w3.org/TR/2006/WD-css3-page-20061010/#page-size

Will that do the trick for the pdf.js use cases?

-- Jet

Julian Viereck

unread,
Mar 15, 2012, 10:31:51 AM3/15/12
to dev-tec...@lists.mozilla.org, rob...@ocallahan.org
(I've forgotten to do a reply-all, such that some of the conversation between me and Rob didn't made it to the mailing list. I include my reply + Robs reply + my new reply to rob in this post. Sorry about this.)

>> We need to implement the CSS3 stuff anyway. How about:
>> -- Implement CSS3 Paged Media sufficient to make headers/footers and margins go away, select page orientation and size. That shouldn't be too hard.
>
> For the PDF.JS one could code around the missing page orientation feature by rotating the content in the canvas used for printing.
>
> In addition, there need to be a way to scratch an element onto an entire print page. E.g. a canvas should take up the entire page if the margin is set to zero. Can that be done using something similar to style="width:100%;height:100%"?
>
> Yes.
>
>
>> -- Add an event to canvas that fires per-page during printing to render that canvas content while we're printing the page it's on. This event would provide a special canvas context that's compatible with the 2d interface but internally renders at higher resolution (or possibly emits PS/PDF directly).
>
> The output should get passed to the printing backend conserving the vector information. That way, if the user selects "Save as PDF", s/he can still select text and scale the PDF to higher resolutions. Also, the printout will look better if the data is kept in a vector format.
>
> Right, so what you would do is pass a special 2D context object with the event, where the methods on that context object generate PDF directly. This wouldn't be terribly hard to implement and it would be really easy to use for Web developers since they could use their existing "paint the canvas" code. This context object should support the full canvas API (although getImageData/putImageData need not produce great results).
>
> The rendering event handler may need to generate temporary canvases that are good for printing, so maybe we'd need to provide a way to do that.
>
> Assume the "drawForPrinting" callback is called and the page renders to a special context object. It's not unusual that rendering a single page can take several seconds (very graphic intense PDFs…). Therefore, there should be a way to continue the printing after a setTimeout(). The setTimout is necessary to let the browser become responsive. For the WebPrintAPI I proposed to add a new `endPage()` function on the print rendering context. Would "pausing the rendering" be possible here as well?
>
> Hmm. Not sure.

Is the browser blocked during rendering all the pages as things are implemented right now or does the browser UI gets responsive during rendering the individual pages?

> While calling the canvas rendering function, this function might have some side effect that removes an element from the document, such that the print layout changes. Is the layout required to update then? What should happen if a canvas gets removed from the document that was not rendered yet?
>
> We can specify that printing works on a snapshot of the document (with the events fired at the corresponding canvases in the real document) so that isn't an issue. That is in fact how Gecko works.
>
> The idea described here seems to enable most of what is described in the WebPrintAPI proposal. The WebPrintAPI proposal is superior to the idea described here as it doesn't require to build up some "fake" canvas that are only used for printing and makes it easier to implement page preview. Also, the maximum number of pages is determined using a JS function, while this HTML/CSS idea requires canvas for all print pages to be added to the DOM before the actual print out which might get very memory intense for large documents (> 1000 pages).
>
> I guess one question is, is it possible to do effective print preview of documents with > 1000 pages without using too much memory --- with any solution? If not, what goals should we be aiming for?

I'm sorry - I don't understand what you mean by "--- with any solution"?

Julian

julian....@googlemail.com

unread,
Mar 15, 2012, 10:43:07 AM3/15/12
to
> * (0,0) should be the top left hand corner of the print area, not the paper
> * CMYK opens a huge can of process worms (which the GCPM language you
> linked to, doesn't even *begin* to cover); avoid for now

Okay.

> * No matter what the document does, Print Preview needs to be capable of
> showing an as-accurate-as-possible WYSIWYG rendition; it's not clear to
> me whether this is true in your proposal

The idea is to give developers the choice of render parts of the page with less details. E.g. if there is a complex graphic but it doesn't take up too much space in the preview, it might be worth showing a placeholder graphic compared to render the complex graphic and take a few seconds for that afford.

Does this answer your question?

Julian

julian....@googlemail.com

unread,
Mar 15, 2012, 10:52:26 AM3/15/12
to mozilla.dev...@googlegroups.com, Jet Villegas, Julian Viereck, rob...@ocallahan.org, dev-tec...@lists.mozilla.org
> IIUC you're asking that we implement the default behavior for CSS3 Paged Media margin boxes:
>
> [...]
>
> Will that do the trick for the pdf.js use cases?

Together with a new callback for printing canvas graphics, this looks very promising.

I'm still a little bit concerned about blocking the UI for printing (esp. for large documents which is common if you print some PDF), the corresponding memory usage and if page preview will scale for large documents.

Julian

julian....@googlemail.com

unread,
Mar 15, 2012, 10:52:26 AM3/15/12
to rob...@ocallahan.org, Julian Viereck, dev-tec...@lists.mozilla.org, Jet Villegas
> IIUC you're asking that we implement the default behavior for CSS3 Paged Media margin boxes:
>
> [...]
>
> Will that do the trick for the pdf.js use cases?

Zack Weinberg

unread,
Mar 15, 2012, 11:54:58 AM3/15/12
to
On 03/15/2012 07:52 AM, julian....@googlemail.com wrote:
>> IIUC you're asking that we implement the default behavior for CSS3
>> Paged Media margin boxes:
>>
>> [...]
>>
>> Will that do the trick for the pdf.js use cases?
>
> Together with a new callback for printing canvas graphics, this
> looks very promising.

Agree.

> I'm still a little bit concerned about blocking the UI for printing
> (esp. for large documents which is common if you print some PDF),

The current printing logic (start with layout/printing/nsPrintEngine) is
a two-phase operation. First we reflow the entire document with
pagination, uninterruptibly; second we paint each page in succession,
allowing the UI to process events but only in between pages.

This is already a cause of significant jank, and it would be great to do
something about it; but it might be quite a lot of work. I had a
conversation with Boris about this the other week, I think in
dev-platform; you might find it instructive.

Right now, execution of page JS is suspended for the course of the print
operation. (Within the cloned document, that is. Although there are
still comments within layout/printing that suggest that the
document-cloning project is incomplete.) If we want to allow the UI to
process events *during* the painting of one page, your "paint this
canvas now" API is going to need some sort of interruptibility. Off the
top of my head I don't know what that ought to look like.

The current print preview arrangement is sort of an afterthought to
normal printing. I don't know what its memory requirements are like.

Basically what I'm saying is there's a lot of work to be done here but
it's work we need to do anyway :)

zw

julian....@googlemail.com

unread,
Mar 15, 2012, 3:08:50 PM3/15/12
to
> Right now, execution of page JS is suspended for the course of the print
> operation. (Within the cloned document, that is. Although there are
> still comments within layout/printing that suggest that the
> document-cloning project is incomplete.) If we want to allow the UI to
> process events *during* the painting of one page, your "paint this
> canvas now" API is going to need some sort of interruptibility. Off the
> top of my head I don't know what that ought to look like.

If you say the JS is suspended, is it still able to call a callback on the canvas element that does the drawing while the print is taking place?

Zack Weinberg

unread,
Mar 15, 2012, 5:27:53 PM3/15/12
to
On 03/15/2012 12:08 PM, julian....@googlemail.com wrote:
>> Right now, execution of page JS is suspended for the course of the
>> print operation. (Within the cloned document, that is. Although
>> there are still comments within layout/printing that suggest that
>> the document-cloning project is incomplete.)
>
> If you say the JS is suspended, is it still able to call a callback
> on the canvas element that does the drawing while the print is taking
> place?

Nope. No page JS can execute while we're in print layout mode (if I
have understood what nsPrintEngine is doing correctly). That would have
to change.

zw

smaug

unread,
Mar 18, 2012, 1:57:17 PM3/18/12
to

On 03/15/2012 05:54 PM, Zack Weinberg wrote:
> On 03/15/2012 07:52 AM, julian....@googlemail.com wrote:
>>> IIUC you're asking that we implement the default behavior for CSS3
>>> Paged Media margin boxes:
>>>
>>> [...]
>>>
>>> Will that do the trick for the pdf.js use cases?
>>
>> Together with a new callback for printing canvas graphics, this
>> looks very promising.
>
> Agree.
>
>> I'm still a little bit concerned about blocking the UI for printing
>> (esp. for large documents which is common if you print some PDF),
>
> The current printing logic (start with layout/printing/nsPrintEngine) is
> a two-phase operation. First we reflow the entire document with
> pagination, uninterruptibly; second we paint each page in succession,
> allowing the UI to process events but only in between pages.
>
> This is already a cause of significant jank, and it would be great to do
> something about it; but it might be quite a lot of work. I had a
> conversation with Boris about this the other week, I think in
> dev-platform; you might find it instructive.
>
> Right now, execution of page JS is suspended for the course of the print
> operation.
Well, it is not expected that any script runs in the cloned document.

> (Within the cloned document, that is. Although there are
> still comments within layout/printing that suggest that the
> document-cloning project is incomplete.)

IIRC, the only thing that is incomplete is that plugin handling could
be better.

> If we want to allow the UI to
> process events *during* the painting of one page, your "paint this
> canvas now" API is going to need some sort of interruptibility. Off the
> top of my head I don't know what that ought to look like.
>
> The current print preview arrangement is sort of an afterthought to
> normal printing. I don't know what its memory requirements are like.
print preview is pretty simple. You just create a paginated
prescontext/layout for the cloned document, and show it otherwise like
a normal document.

smaug

unread,
Mar 18, 2012, 1:59:14 PM3/18/12
to julian....@googlemail.com
JS in the cloned document is suspended. JS in the cloned document never
runs. The callback would run in the original document, and possibly
update the cloned document (if needed).



julian....@googlemail.com

unread,
Mar 18, 2012, 5:03:56 PM3/18/12
to julian....@googlemail.com
If I get that picture right, here is what would happen during printing:

1) the document is cloned, printing is started on the cloned document
2) while printing the cloned document, a canvas element is detected
3) for this canvas in the cloned document, the canvas object in the original document is looked up
4) on the original canvas, it's checked if the canvas has a callback specified doing the special print drawing
5a) If true: this callback is called on the original document passing over a context that knows how to render directly to the printer
5b) If false: the canvas data as available in the cloned document is printed (like it is done right now)

For step 3, is it possible to find the corresponding "original" canvas based on the canvas in the cloned document? Is it possible to call a JS function on the original object like required in step 5a)?

How much is this possible if the original document structure has changed already?

Also, the callback is called within the original document, that might have changed already compared to the cloned document. This might be confusing for developers. Is this a problem or just something to tell the developers that this might be the case and it should be all fine?

smaug

unread,
Mar 18, 2012, 7:45:08 PM3/18/12
to

On 03/18/2012 11:03 PM, julian....@googlemail.com wrote:
> On Sunday, March 18, 2012 6:59:14 PM UTC+1, smaug wrote:
>> On 03/15/2012 09:08 PM, julian....@googlemail.com wrote:
>>> > Right now, execution of page JS is suspended for the course of the print
>>>> operation. (Within the cloned document, that is. Although there are
>>>> still comments within layout/printing that suggest that the
>>>> document-cloning project is incomplete.) If we want to allow the UI to
>>>> process events *during* the painting of one page, your "paint this
>>>> canvas now" API is going to need some sort of interruptibility. Off the
>>>> top of my head I don't know what that ought to look like.
>>>
>>> If you say the JS is suspended, is it still able to call a callback on the canvas element that does the drawing while the print is taking place?
>>>
>>
>> JS in the cloned document is suspended. JS in the cloned document never
>> runs. The callback would run in the original document, and possibly
>> update the cloned document (if needed).
>
> If I get that picture right, here is what would happen during printing:
>
> 1) the document is cloned, printing is started on the cloned document
> 2) while printing the cloned document, a canvas element is detected
> 3) for this canvas in the cloned document, the canvas object in the original document is looked up
> 4) on the original canvas, it's checked if the canvas has a callback specified doing the special print drawing
> 5a) If true: this callback is called on the original document passing over a context that knows how to render directly to the printer
> 5b) If false: the canvas data as available in the cloned document is printed (like it is done right now)
>
> For step 3, is it possible to find the corresponding "original" canvas based on the canvas in the cloned document?
Right now no, but that would be easy to add. Just add a member variable
to nsHTMLCanvasElement and make it point to the original element in
nsHTMLCanvasElement::CopyInnerTo

> Is it possible to call a JS function on the original object like required in step 5a)?
That should be easy.

>
> How much is this possible if the original document structure has changed already?
In general cloned document doesn't really care about the original
document. That was the reason for cloning; original document can
continue to execute JS etc, and clone will be printed.
So, if there is a strong reference from cloned canvas to the original,
everything should just work fine.


>
> Also, the callback is called within the original document, that might have changed already compared to the cloned document.
> This might be confusing for developers. Is this a problem or just something to tell the developers that this might be the case and it should be all fine?

We may want to add some new events, since afterprint fires too early
for this stuff. There could be some asyncronous afterprintprocessed event.

Or, hmm, could we add some mapping from canvas to callback, but not
really register the callback to canvas?
Something like:
window.setAlternativePrintHandler(somecanvasElement, callback);
Then define that so that right after beforeprint event the callback is
stored in a list which will be called during printing, regardless
whether the original canvas is in the document anymore.


julian....@googlemail.com

unread,
Mar 19, 2012, 8:00:10 AM3/19/12
to
> window.setAlternativePrintHandler(somecanvasElement, callback);
> Then define that so that right after beforeprint event the callback is
> stored in a list which will be called during printing, regardless
> whether the original canvas is in the document anymore.

The special print handler belongs to the canvas element. Therefore it should be defined directly on it I think.

> Right now no, but that would be easy to add. Just add a member variable
> to nsHTMLCanvasElement and make it point to the original element in
> nsHTMLCanvasElement::CopyInnerTo

Actually, we are not interested in anything more then keeping a reference to a JS function that was defined on the canvas for the print callback. A link to such a function could be created during the CopyInnerTo function call. Then we can be sure it's there when calling the function while printing, although the canvas element itself might got removed from the original document already.

Does that make sense/is possible to implement?

smaug

unread,
Mar 19, 2012, 8:40:14 AM3/19/12
to julian....@googlemail.com

On 03/19/2012 02:00 PM, julian....@googlemail.com wrote:
>> window.setAlternativePrintHandler(somecanvasElement, callback);
>> Then define that so that right after beforeprint event the callback
>> is stored in a list which will be called during printing,
>> regardless whether the original canvas is in the document anymore.
>
> The special print handler belongs to the canvas element. Therefore it
> should be defined directly on it I think.
>
>> Right now no, but that would be easy to add. Just add a member
>> variable to nsHTMLCanvasElement and make it point to the original
>> element in nsHTMLCanvasElement::CopyInnerTo
>
> Actually, we are not interested in anything more then keeping a
> reference to a JS function that was defined on the canvas for the
> print callback.
In practice you need to keep the element alive too, in which case the
callback stays alive.

julian....@googlemail.com

unread,
Mar 20, 2012, 6:53:32 PM3/20/12
to mozilla.dev...@googlegroups.com, dev-tec...@lists.mozilla.org, rob...@ocallahan.org
After thinking about the proposed ideas in Robert's first reply, based on the feedback to my raised questions (you guys are awesome, thanks!) and talking to some other people, I think it's best to drop the WebPrintAPI proposal and go with what Robert proposed.

That requires two things to get implemented in Gecko:

i) Implement a subset of CSS3PagedMedia to set the margin (at least the case = 0 to hide all the header/footer) and the page size.
ii) Implement a new callback on the canvas element that is called during the actual printing to print directly to the printing backend.

What's the best way to get the implementation going?

For i), there seems to be a bug already: https://bugzilla.mozilla.org/show_bug.cgi?id=115199

I tried to understand what the patches are doing and couldn't figure it out for most parts. For i), is the way to implement this by continue working on bug 115199, or should there be a new bug that tries to implement only a very dump subset of the CSS3PagedMedia, such that one can set the margin to 0 and the page-size and that's it?

>From my intuition, the second part ii) might be "easier" to implement then the first part, as it is more contained and doesn't require to deal with new CSS rules. I'm therefore wondering if you think this is something I could try to implement.

So far I have some basic understanding about how the printing stuff works, how the CanvasReneringContext is defined in the IDL and cpp file etc. Could someone give me some pointers where the actual printing of a canvas element to the printing context happens, such that I could try to implement the proposed callback?

Best,

Julian

julian....@googlemail.com

unread,
Mar 20, 2012, 6:53:32 PM3/20/12
to dev-tec...@lists.mozilla.org, rob...@ocallahan.org

Robert O'Callahan

unread,
Mar 20, 2012, 10:13:16 PM3/20/12
to julian....@googlemail.com, mozilla.dev...@googlegroups.com, dev-tec...@lists.mozilla.org
On Wed, Mar 21, 2012 at 11:53 AM, <julian....@googlemail.com> wrote:

> i) Implement a subset of CSS3PagedMedia to set the margin (at least the
> case = 0 to hide all the header/footer) and the page size.
> ii) Implement a new callback on the canvas element that is called during
> the actual printing to print directly to the printing backend.
>
> What's the best way to get the implementation going?
>
> For i), there seems to be a bug already:
> https://bugzilla.mozilla.org/show_bug.cgi?id=115199
>
> I tried to understand what the patches are doing and couldn't figure it
> out for most parts. For i), is the way to implement this by continue
> working on bug 115199, or should there be a new bug that tries to implement
> only a very dump subset of the CSS3PagedMedia, such that one can set the
> margin to 0 and the page-size and that's it?
>

I think starting with the patch there is the way to go. You'll need to add
code to nsSimplePageSequenceFrame::Reflow to set up margins using the style
data from a (the first) nsPageFrame child.

>From my intuition, the second part ii) might be "easier" to implement then
> the first part, as it is more contained and doesn't require to deal with
> new CSS rules. I'm therefore wondering if you think this is something I
> could try to implement.
>
> So far I have some basic understanding about how the printing stuff works,
> how the CanvasReneringContext is defined in the IDL and cpp file etc. Could
> someone give me some pointers where the actual printing of a canvas element
> to the printing context happens, such that I could try to implement the
> proposed callback?
>

Basically we just render the canvas like normal, but we're rendering it to
a print context.

What I think you need to do is:
-- in nsHTMLCanvasElement::CopyInnerTo in the IsStaticDocument case, set up
a reference from the cloned-for-print canvas to the original canvas
-- in nsSimplePageSequenceFrame::PrintNextPage, before you render the page
via nsLayoutUtils::PaintFrame, traverse the child page frame's frame
subtree (see FrameChildListIterator and its users) looking for
nsHTMLCanvasFrames (via do_QueryFrame). For each one, look up the original
canvas, see if it has an "onprint" (or whatever we call it) event handler
set on it. For those that do:
-- Poke the printed nsHTMLCanvasElement's 2D rendering context
(nsCanvasRenderingContext2D for now) to tell it to change its surface to a
new surface created via
renderingContext->GetThebes()->CurrentSurface()->CreateSimilarSurface()
-- Synchronously dispatch the "print" event to the original canvas.
-- That event should use a custom DOM event class (see for example
nsDOMNotifyPaintEvent) with an extra field that's the 2D rendering context
for the printed nsHTMLCanvasElement
-- To save memory, after calling PaintFrame for the page, go back through
the canvases where you fired the callbacks and clear out their surfaces.

If you're really lucky, that will be enough for printing :-). For print
preview:
-- Add an mIsReadyToPrint flag on nsHTMLCanvasElement, initially false,
-- Add an IsReadyToPrint() method on nsPageFrame that scans the frame
subtree (see FrameChildListIterator) looking for nsHTMLCanvasFrames (via
do_QueryFrame). If any such canvases have their mIsReadyToPrint flag not
set, and the original canvas has an "onprint" callback, the page is not
ready to print.
-- When painting an nsPageFrame in print preview, if !IsReadyToPrint(), for
each not-ready print canvas, clear out its surface and fire an asynchronous
event that dispatches a "print" event to the original canvas, passing the
2D rendering context for the print canvas's context, and when that event
has finished set the mIsReadyToPrint flag on the print canvas.
-- To save memory, periodically check for pages that are offscreen and
clear out the surfaces for the print canvases in them.

At some point, hopefully soon, nsCanvasRenderingContext2D will go away and
you'll need to make these changes to nsCanvasRenderingContext2DAzure
instead.

fantasai

unread,
Mar 21, 2012, 3:28:18 AM3/21/12
to
On 03/14/2012 05:24 PM, Zack Weinberg wrote:
> On 03/14/2012 02:02 PM, Julian Viereck wrote:
>>
>> https://wiki.mozilla.org/WebAPI/WebPrintAPI
>>
>> There isn't too much yet. So far I have scratched out the basic API
>> and started some very basic implementation work. However, I hope this
>> is enough to get some discussion about this proposed API going and
>> I'd like to get some feedback what people think about it.
>
> A few small comments:
>
> * Instead of forbidding px, define 1px = 1/96in

On that topic, I would suggest some close reading of the absolute units
section here:
http://www.w3.org/TR/CSS21/syndata.html#length-units

~fantasai

fantasai

unread,
Mar 21, 2012, 3:38:45 AM3/21/12
to
On 03/14/2012 05:56 PM, Jet Villegas wrote:
> IIUC you're asking that we implement the default behavior for CSS3 Paged Media margin boxes:
>
> Margin boxes have an initial value of zero for padding, border and margin. The default height of top boxes is the value of the page box's top margin. The default height of the bottom boxes is the value of the page box's bottom margin.
> The initial value for 'content' is 'none'. The initial 'width' and 'height' of margin boxes is 'auto'.
>
> http://www.w3.org/TR/2006/WD-css3-page-20061010/#populating-margin-boxes
>
> In other words, we won't be rendering any content for headers or footers.

That depends on what's in our UA and user style sheet levels. Presumably
the default for web pages will include the title/URL/timestamp/etc. like
we do today.

> You're also asking that we implement page size, and portrait vs. landscape:
> http://www.w3.org/TR/2006/WD-css3-page-20061010/#page-size

I would strongly recommend referring to the editor's draft for now.
http://dev.w3.org/csswg/css3-page/
For various reasons (that have little to do with W3C and more to
do with the printer industry and me being switched to other tasks
like CSS2.1), the draft you link to is very out-of-date...

We've also gotten some significant comments on the margin box sizing
rules from Simon Sapin on www-style; if you're going to implement
margin boxes, you'll want to track those discussions.

http://lists.w3.org/Archives/Public/www-style/

The CSS3 Paged Media discussions on www-style are tagged with [css3-page].
Feel free to poke me with any questions; I'm the spec editor. And please
send comments about things that are stupid or broken to www-style so we
can fix them...

~fantasai

julian....@googlemail.com

unread,
Mar 21, 2012, 5:57:01 PM3/21/12
to mozilla.dev...@googlegroups.com, julian....@googlemail.com, dev-tec...@lists.mozilla.org, rob...@ocallahan.org
Rob, thanks a lot for your step points on how to get this implemented - this is way more detailed then I've expected it to be :)

> For each one, look up the original
> canvas, see if it has an "onprint" (or whatever we call it) event handler
> set on it. For those that do:

If we use events here, there could be two callbacks listening for the same event. To keep it easy, I propose to define a new `printCallback` attribute on the nsHTMLCanvasElement, that takes a JSFunction as argument.

Are there advantages in using an event instead of a simple callback that I don't realize?

> -- Synchronously dispatch the "print" event to the original canvas.
> -- That event should use a custom DOM event class (see for example
> nsDOMNotifyPaintEvent) with an extra field that's the 2D rendering context
> for the printed nsHTMLCanvasElement

With the idea of the `printCallback` from above, the first argument would be the new created RenderingContext.

julian....@googlemail.com

unread,
Mar 21, 2012, 5:57:01 PM3/21/12
to julian....@googlemail.com, dev-tec...@lists.mozilla.org, rob...@ocallahan.org
Rob, thanks a lot for your step points on how to get this implemented - this is way more detailed then I've expected it to be :)

> For each one, look up the original
> canvas, see if it has an "onprint" (or whatever we call it) event handler
> set on it. For those that do:

If we use events here, there could be two callbacks listening for the same event. To keep it easy, I propose to define a new `printCallback` attribute on the nsHTMLCanvasElement, that takes a JSFunction as argument.

Are there advantages in using an event instead of a simple callback that I don't realize?

> -- Synchronously dispatch the "print" event to the original canvas.
> -- That event should use a custom DOM event class (see for example
> nsDOMNotifyPaintEvent) with an extra field that's the 2D rendering context
> for the printed nsHTMLCanvasElement

Robert O'Callahan

unread,
Mar 22, 2012, 4:29:31 AM3/22/12
to julian....@googlemail.com, mozilla.dev...@googlegroups.com, dev-tec...@lists.mozilla.org
On Thu, Mar 22, 2012 at 10:57 AM, <julian....@googlemail.com> wrote:

> > For each one, look up the original
> > canvas, see if it has an "onprint" (or whatever we call it) event handler
> > set on it. For those that do:
>
> If we use events here, there could be two callbacks listening for the same
> event. To keep it easy, I propose to define a new `printCallback` attribute
> on the nsHTMLCanvasElement, that takes a JSFunction as argument.
>
> Are there advantages in using an event instead of a simple callback that I
> don't realize?
>

Events are more the standard pattern, that's all. And I think events are a
bit more evolvable; it's easier to add fields to an event object without
breaking things than to add parameters to a callback function. A callback
is OK if you feel strongly about it.

julian....@googlemail.com

unread,
Mar 28, 2012, 4:59:03 PM3/28/12
to mozilla.dev...@googlegroups.com, julian....@googlemail.com, dev-tec...@lists.mozilla.org, rob...@ocallahan.org
> What I think you need to do is:
> -- in nsHTMLCanvasElement::CopyInnerTo in the IsStaticDocument case, set up
> a reference from the cloned-for-print canvas to the original canvas
> -- in nsSimplePageSequenceFrame::PrintNextPage, before you render the page
> via nsLayoutUtils::PaintFrame, traverse the child page frame's frame
> subtree (see FrameChildListIterator and its users) looking for
> nsHTMLCanvasFrames (via do_QueryFrame). For each one, look up the original
> canvas, see if it has an "onprint" (or whatever we call it) event handler
> set on it. For those that do:
> -- Poke the printed nsHTMLCanvasElement's 2D rendering context
> (nsCanvasRenderingContext2D for now) to tell it to change its surface to a
> new surface created via
> renderingContext->GetThebes()->CurrentSurface()->CreateSimilarSurface()
> -- Synchronously dispatch the "print" event to the original canvas.
> -- That event should use a custom DOM event class (see for example
> nsDOMNotifyPaintEvent) with an extra field that's the 2D rendering context
> for the printed nsHTMLCanvasElement

The points above seem to work in a very hacked up first version :)

Instead of the event I use callback style for now. The first argument gets the rendering context and the second argument is an object, that contains additional data.

You can find my current "test" file here:

https://gist.github.com/2230187

This is what the print output looks like (used "Save as PDF" option):

http://n.ethz.ch/~jviereck/drop/Preview_print_test.pdf

There are some questions now arising:

-- What should be the unit system for the context used in the mozPrintCallback? Right now, one unit corresponds to the length that one pixel of the canvas gets mapped to in the actual printout. That has nothing to do with the 1/72 inch or 1/92 inch mentioned in this thread. However, the way it's right now is what people might expect. If they write a rendering function like I did in the gist file, then they just can reuse it during rendering as well and don't have to adjust the unit system depending on printing to screen or printer.

-- Is there a way to figure out the DPI resolution of the printout in C++?

-- ctx.getImageData and ctx.putImageData work as expected. The second rectangle you see in the example PDF on the bottom right was created using getImageData and putImageData. During this process, the vector information gets rastered and therefore the text quality of the second rectangle is poor compared to the original one. The resolution stays the same during the rastering process as it would in the "normal" DOM case. For the printing case, it might be useful to request imageData with a higher resolution. Should there be a new parameter for the getImageData and putImageData to specify the resolution? E.g getImageData(x, y, width, height, 100) returns imageData where each "normal" pixel corresponds to 100 pixel.

-- How can one figure out the actual size of the canvas when printed to paper? I guess I can get some size-information from the CanvasFrame, but is that the actual/resulting size on paper?

>> In addition, there need to be a way to scratch an element onto an entire print page. E.g. a canvas should take up the entire page if the margin is set to zero. Can that be done using something similar to style="width:100%;height:100%"?
>
> Yes.

Rob, what was your idea to use for this?

julian....@googlemail.com

unread,
Mar 28, 2012, 4:59:03 PM3/28/12
to julian....@googlemail.com, dev-tec...@lists.mozilla.org, rob...@ocallahan.org
> What I think you need to do is:
> -- in nsHTMLCanvasElement::CopyInnerTo in the IsStaticDocument case, set up
> a reference from the cloned-for-print canvas to the original canvas
> -- in nsSimplePageSequenceFrame::PrintNextPage, before you render the page
> via nsLayoutUtils::PaintFrame, traverse the child page frame's frame
> subtree (see FrameChildListIterator and its users) looking for
> nsHTMLCanvasFrames (via do_QueryFrame). For each one, look up the original
> canvas, see if it has an "onprint" (or whatever we call it) event handler
> set on it. For those that do:
> -- Poke the printed nsHTMLCanvasElement's 2D rendering context
> (nsCanvasRenderingContext2D for now) to tell it to change its surface to a
> new surface created via
> renderingContext->GetThebes()->CurrentSurface()->CreateSimilarSurface()
> -- Synchronously dispatch the "print" event to the original canvas.
> -- That event should use a custom DOM event class (see for example
> nsDOMNotifyPaintEvent) with an extra field that's the 2D rendering context
> for the printed nsHTMLCanvasElement

The points above seem to work in a very hacked up first version :)

Instead of the event I use callback style for now. The first argument gets the rendering context and the second argument is an object, that contains additional data.

You can find my current "test" file here:

https://gist.github.com/2230187

This is what the print output looks like (used "Save as PDF" option):

http://n.ethz.ch/~jviereck/drop/Preview_print_test.pdf

There are some questions now arising:

-- What should be the unit system for the context used in the mozPrintCallback? Right now, one unit corresponds to the length that one pixel of the canvas gets mapped to in the actual printout. That has nothing to do with the 1/72 inch or 1/92 inch mentioned in this thread. However, the way it's right now is what people might expect. If they write a rendering function like I did in the gist file, then they just can reuse it during rendering as well and don't have to adjust the unit system depending on printing to screen or printer.

-- Is there a way to figure out the DPI resolution of the printout in C++?

-- ctx.getImageData and ctx.putImageData work as expected. The second rectangle you see in the example PDF on the bottom right was created using getImageData and putImageData. During this process, the vector information gets rastered and therefore the text quality of the second rectangle is poor compared to the original one. The resolution stays the same during the rastering process as it would in the "normal" DOM case. For the printing case, it might be useful to request imageData with a higher resolution. Should there be a new parameter for the getImageData and putImageData to specify the resolution? E.g getImageData(x, y, width, height, 100) returns imageData where each "normal" pixel corresponds to 100 pixel.

-- How can one figure out the actual size of the canvas when printed to paper? I guess I can get some size-information from the CanvasFrame, but is that the actual/resulting size on paper?

>> In addition, there need to be a way to scratch an element onto an entire print page. E.g. a canvas should take up the entire page if the margin is set to zero. Can that be done using something similar to style="width:100%;height:100%"?
>
> Yes.

Robert O'Callahan

unread,
Mar 29, 2012, 7:41:58 PM3/29/12
to julian....@googlemail.com, mozilla.dev...@googlegroups.com, dev-tec...@lists.mozilla.org
On Thu, Mar 29, 2012 at 9:59 AM, <julian....@googlemail.com> wrote:

> The points above seem to work in a very hacked up first version :)
>

Impressive!!!


> Instead of the event I use callback style for now. The first argument gets
> the rendering context and the second argument is an object, that contains
> additional data.
>

What does the print engine pass as the second argument?

You shouldn't really need to use a second argument to carry around data for
rendering. You can use a JS closure for that.

-- What should be the unit system for the context used in the
> mozPrintCallback? Right now, one unit corresponds to the length that one
> pixel of the canvas gets mapped to in the actual printout. That has nothing
> to do with the 1/72 inch or 1/92 inch mentioned in this thread. However,
> the way it's right now is what people might expect. If they write a
> rendering function like I did in the gist file, then they just can reuse it
> during rendering as well and don't have to adjust the unit system depending
> on printing to screen or printer.
>

I think that's definitely the right thing to do.

-- Is there a way to figure out the DPI resolution of the printout in C++?
>

Yes, cairo_surface_get_fallback_resolution. You should add a wrapper to
gfxContext to return it.

-- ctx.getImageData and ctx.putImageData work as expected. The second
> rectangle you see in the example PDF on the bottom right was created using
> getImageData and putImageData. During this process, the vector information
> gets rastered and therefore the text quality of the second rectangle is
> poor compared to the original one. The resolution stays the same during the
> rastering process as it would in the "normal" DOM case. For the printing
> case, it might be useful to request imageData with a higher resolution.
> Should there be a new parameter for the getImageData and putImageData to
> specify the resolution? E.g getImageData(x, y, width, height, 100) returns
> imageData where each "normal" pixel corresponds to 100 pixel.
>

getImageData/putImageData were originally designed to be able to return
high-res versions of the canvas data. However, Web authors don't use them
as designed so we'll probably need new APIs for this. See the recent thread
http://lists.whatwg.org/htdig.cgi/whatwg-whatwg.org/2012-March/035112.html.

-- How can one figure out the actual size of the canvas when printed to
> paper? I guess I can get some size-information from the CanvasFrame, but is
> that the actual/resulting size on paper?
>

When printing, CSS units are rendered as physical units if the zoom factor
is 100%. Each CSS px is 1/96 of an inch. Changing the zoom factor will
change this of course.

What do you need this for?

>> In addition, there need to be a way to scratch an element onto an entire
> print page. E.g. a canvas should take up the entire page if the margin is
> set to zero. Can that be done using something similar to
> style="width:100%;height:100%"?
> >
> > Yes.
>
> Rob, what was your idea to use for this?
>

Easiest way is probably to make the canvas position:absolute; top:0;
left:0; width:100%; height:100%. Of course you'll need to zero out margins
as well.

Julian Viereck

unread,
Mar 31, 2012, 2:16:45 PM3/31/12
to rob...@ocallahan.org, Julian Viereck, dev-tec...@lists.mozilla.org
> Impressive!!!

Thanks :)

> Instead of the event I use callback style for now. The first argument gets the rendering context and the second argument is an object, that contains additional data.
>
> What does the print engine pass as the second argument?
>
> You shouldn't really need to use a second argument to carry around data for rendering. You can use a JS closure for that.

The second argument might look like this:

sndArgument = {
DPI: 200,
preview: false
};

It's intended to expose more print related information to the user.

> -- Is there a way to figure out the DPI resolution of the printout in C++?
>
> Yes, cairo_surface_get_fallback_resolution. You should add a wrapper to gfxContext to return it.


cairo_surface_get_fallback_resolution requires a cairo surface. Therefore I've added a new `GetFallbackResolution` on the gfxASurface class.

Calling this function always return 300 by 300 pixels per inch. That's independent of the print quality I set on my printer or wether I choose "Save as PDF". The cairo documentation states

"This function returns the previous fallback resolution set by cairo_surface_set_fallback_resolution(), or default fallback resolution if never set."

Therefore I'm wondering if this is really related to the actual DPI of the printer or just picks up some default value.

> -- How can one figure out the actual size of the canvas when printed to paper? I guess I can get some size-information from the CanvasFrame, but is that the actual/resulting size on paper?
>
> When printing, CSS units are rendered as physical units if the zoom factor is 100%. Each CSS px is 1/96 of an inch. Changing the zoom factor will change this of course.
>
> What do you need this for?


Imagine you print a canvas that contains some computed image data. If you know the actual size of the canvas on the paper and the DPI resolution, one can compute what dimension the image data has to be in order to look good on the printout (e.g. if you don't compute a high enough resolution, the user will see pixels in the printout).

Even in the case that the DPI of the printer might not be determinable, using 300DPI as the default value and multiply it by the size would still give a good guess what the image data size has to be.

> >> In addition, there need to be a way to scratch an element onto an entire print page. E.g. a canvas should take up the entire page if the margin is set to zero. Can that be done using something similar to style="width:100%;height:100%"?
> >
> > Yes.
>
> Rob, what was your idea to use for this?
>
> Easiest way is probably to make the canvas position:absolute; top:0; left:0; width:100%; height:100%. Of course you'll need to zero out margins as well.

Using "position: relative" works like expected and scales the canvas to fit on a page each.

I've played around with some CSS units (code in [1], resulting PDF in [2]). The "mm"-size used for the ctx.font has nothing to do with the actual printout size. This feels consistent to me and therefore right, as it renders to the same size/way the DOM canvas does, but it can confuse people that expect to get really 5mm on the printout.

What's the way it should be?

Julian

[1]: https://gist.github.com/2267257
[2]: http://n.ethz.ch/~jviereck/drop/Preview_print_test_page.pdf

Robert O'Callahan

unread,
Apr 1, 2012, 6:26:07 PM4/1/12
to Julian Viereck, dev-tec...@lists.mozilla.org
On Sun, Apr 1, 2012 at 6:16 AM, Julian Viereck <
julian....@googlemail.com> wrote:

> The second argument might look like this:
>
> sndArgument = {
> DPI: 200,
> preview: false
> };
>
> It's intended to expose more print related information to the user.
>

Hmm. Can you explain this a bit more?


> -- Is there a way to figure out the DPI resolution of the printout in C++?
>>
>
> Yes, cairo_surface_get_fallback_resolution. You should add a wrapper to
> gfxContext to return it.
>
>
> cairo_surface_get_fallback_resolution requires a cairo surface. Therefore
> I've added a new `GetFallbackResolution` on the gfxASurface class.
>
> Calling this function always return 300 by 300 pixels per inch. That's
> independent of the print quality I set on my printer or wether I choose
> "Save as PDF". The cairo documentation states
>
> "This function returns the previous fallback resolution set
> by cairo_surface_set_fallback_resolution(), or default fallback resolution
> if never set."
>
> Therefore I'm wondering if this is really related to the actual DPI of the
> printer or just picks up some default value.
>

We probably need to set it somewhere.


> -- How can one figure out the actual size of the canvas when printed to
>> paper? I guess I can get some size-information from the CanvasFrame, but is
>> that the actual/resulting size on paper?
>>
>
> When printing, CSS units are rendered as physical units if the zoom factor
> is 100%. Each CSS px is 1/96 of an inch. Changing the zoom factor will
> change this of course.
>
> What do you need this for?
>
>
> Imagine you print a canvas that contains some computed image data. If you
> know the actual size of the canvas on the paper and the DPI resolution, one
> can compute what dimension the image data has to be in order to look good
> on the printout (e.g. if you don't compute a high enough resolution, the
> user will see pixels in the printout).
>
> Even in the case that the DPI of the printer might not be determinable,
> using 300DPI as the default value and multiply it by the size would still
> give a good guess what the image data size has to be.
>

I see. What sort of image data computation are we talking about? Probably
the proposed getImageData extensions, or some other API to get the actual
DPI of the canvas, would cover this.

I've played around with some CSS units (code in [1], resulting PDF in [2]).
> The "mm"-size used for the ctx.font has nothing to do with the actual
> printout size. This feels consistent to me and therefore right, as it
> renders to the same size/way the DOM canvas does, but it can confuse people
> that expect to get really 5mm on the printout.
>
> What's the way it should be?
>

I see what you mean. The problem is that setting the CSS width and height
of the canvas scales "canvas units", and the font size you specify is in
canvas units.

Changing this would make printing behave differently from normal drawing,
so we probably shouldn't change anything by default.

I looked into this today. I moved the code to swap the surface and call the
> mozPrintCallback function from the nsSimplePageSequence::
> PrintNextPage() into the nsPageFrame::paintPageContent function. Within
> preview the mozPrintCallback gets called now as well.
>
> However, the drawing looks pixeled (like it does without calling the
> callback/printing a normal canvas) and not like the print/"Save as PDF" at
> high/vector resolution. I did a
>
> std::cout << "SurfaceType: " << printSurface->GetType();
>
> and it states the type is the same for printing and print preview.
>

We shouldn't switch the canvas type here, that's just wasteful (since
internally we'd render to a PDF surface or something and then render that
to a raster surface). Print preview should use the same canvas type as
normal screen drawing.

What we really need to do here is render to a higher-resolution backing
surface of the screen type. That's going to require a bit more canvas work.

julian....@googlemail.com

unread,
Apr 16, 2012, 5:00:46 PM4/16/12
to Julian Viereck, dev-tec...@lists.mozilla.org, rob...@ocallahan.org
While thinking about what a proposal to WHATWG about the printCallback-API should look like, I've came across the following edgecases.

* the context passed to the mozPrintCallback function has a `canvas` property. I think this `canvas` property should not exist on the mozPrintCallback passed in `ctx` and should return `undefined.

What should this canvas be? If it's the canvas of the print-document, performing operations like `appendChild` to the HTML tree, that gets printed, can cause issues, as it's assumed that this tree is static and can't change. On the other side, the `canvas` might refer to the canvas in the DOM, but this canvas was removed in the meantime (= during the time frame the printing starts and the callback is called) maybe .

Instead, I propose the second argument passed to the callback should contain the `width`, `height`, `id` and `name` of the canvas as at the beginning of the printing process.

The usage of the mozPrintCallback might then look like this:

canvas.mozPrintCallback = function(ctx, obj) {
console.log(obj.width, obj.height, obj.id, obj.name);
// ...
}

* The current implementation sets the size of the rendering context to the size of the actual canvas on the screen if in preview. The canvas context is then scaled, such that the behavior of the canvas context "feels" the same in normal DOM usage. While this works, this might cause problems when using `getImageData` on the canvas.

Imagine the print preview has a zoom set to 10%. If the canvas has normally a width of 100px, it might now only have 10px in preview. The internal used context of the canvas is then only 10px in width, but if the user requests imageData with width 50px, this gets mapped down to 5px (due to internal scaling). But then the 5px gets scaled by factor 10 to get the 50px of the imageData size. As a result, the imageData the user gets might not be of the quality it is used to be.

To solve this, should the canvas context size be at least the size of the canvas.width/canvas.height as specified in the DOM? If the size is larger, there might still be some loose in quality, as the data needs to be down-scaled for the requested imageData (imagine the canvas has a width of 100px in the DOM and 200px in preview - then the canvas context has a width of 200px as well and the width is scaled by a factor of 2 when a getImageData requiest is done). But I don't expect the image quality loose too be as bad as when the canvas context gets very tight compared to the expected canvas size.

Does these changes make sense?

Best,

Julian

julian....@googlemail.com

unread,
Apr 16, 2012, 5:00:46 PM4/16/12
to mozilla.dev...@googlegroups.com, Julian Viereck, dev-tec...@lists.mozilla.org, rob...@ocallahan.org

Robert O'Callahan

unread,
Apr 17, 2012, 8:35:37 AM4/17/12
to julian....@googlemail.com, mozilla.dev...@googlegroups.com, dev-tec...@lists.mozilla.org
On Tue, Apr 17, 2012 at 9:00 AM, <julian....@googlemail.com> wrote:

> * the context passed to the mozPrintCallback function has a `canvas`
> property. I think this `canvas` property should not exist on the
> mozPrintCallback passed in `ctx` and should return `undefined.
>
> What should this canvas be? If it's the canvas of the print-document,
> performing operations like `appendChild` to the HTML tree, that gets
> printed, can cause issues, as it's assumed that this tree is static and
> can't change. On the other side, the `canvas` might refer to the canvas in
> the DOM, but this canvas was removed in the meantime (= during the time
> frame the printing starts and the callback is called) maybe .
>

Good thought, but I think it's fine for it to be the canvas in the page
DOM. If that canvas has been removed from the document, that's OK.

* The current implementation sets the size of the rendering context to the
> size of the actual canvas on the screen if in preview. The canvas context
> is then scaled, such that the behavior of the canvas context "feels" the
> same in normal DOM usage. While this works, this might cause problems when
> using `getImageData` on the canvas.
>
> Imagine the print preview has a zoom set to 10%. If the canvas has
> normally a width of 100px, it might now only have 10px in preview. The
> internal used context of the canvas is then only 10px in width, but if the
> user requests imageData with width 50px, this gets mapped down to 5px (due
> to internal scaling). But then the 5px gets scaled by factor 10 to get the
> 50px of the imageData size. As a result, the imageData the user gets might
> not be of the quality it is used to be.
>

For high quality, we need an extended version of get/putImageData that can
work at higher resolution.

To solve this, should the canvas context size be at least the size of the
> canvas.width/canvas.height as specified in the DOM?


I don't think we need to do that.

julian....@googlemail.com

unread,
Apr 17, 2012, 5:11:08 PM4/17/12
to
> The CSS3 Paged Media discussions on www-style are tagged with [css3-page].
> Feel free to poke me with any questions; I'm the spec editor. And please
> send comments about things that are stupid or broken to www-style so we
> can fix them...

One part of the spec that might need to get implemented for PDF.JS is the ability to specify the size of individual pages. If I get that right from the spec, this can be done like this:

@page narrow { size: 4cm 4cm }
div.foo { page: narrow }

Can you please help me, what the expected behavior is in the following case:

Assume the default page size is A4 and a div matching "div.foo" is at the very bottom of the first printed page. As it has a `page` property defined, the `@page narrow` properties get applied to the page the "div.foo" is on. But the page-size required the A4 page to shrink. As a consequence of the shrink process, the "div.foo" element is no longer on the same page as before, as it was at the very bottom and the page got smaller. In a consequence, the page that now contains the "div.foo" should get the narrow-page-size applied. But as the "div.foo" is no longer on the first page, the "@page narrow" doesn't apply to the first page anymore, and it size should be A4 again. But then the "div.foo" is on the first page again, the "@page narrow" get applied and ...

Do you see the endless loop? I haven't read the entire spec, but from what I read the behavior in this case is not clear to me.

Also, what should happen if there are two divs on the page, both referring to "@page" definitions but have different page-sizes applied. Should the first div's page-size win over the second?

Best,

Julian

Julian Viereck

unread,
Apr 24, 2012, 5:31:37 PM4/24/12
to rob...@ocallahan.org, mozilla.dev...@googlegroups.com, Julian Viereck, dev-tec...@lists.mozilla.org
I'm pretty close to have `async` support done for the mozPrintCallback, but I'm stuck with a problem that I don't know how to solve best.

Here is some very basic background to understand my problem:

- I've implemented a new function `nsSimplePageSequenceFrame::PrePrintNextPage()` that is called from the nsPrintEngine/nsPagePrintTimer to deter if the current page is ready for printing. If the function returns `true`, nsSimplePageSequenceFrame::PrintNextPage() is executed that does the actual printing to the page.
- PrePrintNextPage does the canvas-surface-replacement to replace the pixel based surface by a vector one that is used for printing. If there are no canvas with a mozPrintCallback or the page doesn't get printed, it returns true. Otherwise, it waits until on all canvas with a mozPrintCallback the `done()` function is called.

[I have the two current function PrePrintNextPage() and PrintNextPage() here: https://gist.github.com/2483955#L611]

The problem is now in the "canvas-surface-replacement": The "renderingSurface->CreateSimilarSurface()" function call only returns a vector-based surface iff "dc->BeginPage()" was called at least once before. However, for the first page, this is not the case (as "PrePrintNextPage()" is called before "PrintNextPage()", which does the call to dc->BeginPage()).

An easy solution might be to "move all the code from the beginning of `PrintNextPage` until the dc->BeginPage() into the "PrePrintNextPage()" function. Then, before a similar surface is created, a dc->BeginPage() call already happened. To do this, I need some more clarification on this code:

http://mxr.mozilla.org/mozilla-central/source/layout/generic/nsSimplePageSequence.cpp#620

* can PresContext()->IsRootPaginatedDocument() be false at any time? The `PrintNextPage()` function is only called for printing to paper - it's not called for preview. Therefore, the PresContext is always a paginatedDocument?
* the function might call `dc->BeginPage()` multiple times. Shouldn't this function add only one page to the output? Is this a bug or a feature?
* was this code in "PrintNextPage()" used once for rendering the preview window output as well (which is longer the case if I read the code right)? Are the two points I've listed above left over from sharing the same function and can be removed now?

Best,

Julian

Daniel Holbert

unread,
Apr 24, 2012, 6:20:14 PM4/24/12
to Julian Viereck, rob...@ocallahan.org, dev-tec...@lists.mozilla.org
On 04/24/2012 02:31 PM, Julian Viereck wrote:
> * the function might call `dc->BeginPage()` multiple times.
> Shouldn't this function add only one page to the output?
> Is this a bug or a feature?

AFAICT, that code only triggers multiple BeginPage calls if we're doing
Print Selection.

Print Selection is special. Rather than explicitly paginating our
content into separate page frames, as we normally do, we instead render
the entire selected region onto one giant page frame, and then we paint
that onto multiple physical pages. (This mostly works, but it can cause
problems if we pick a bad spot to split[1].)

So -- when we're doing Print Selection, we end up only printing a single
(tall) page _frame_, so we only need one call to PrintNextPage(). But we
print that frame onto multiple *physical* pages, so we end up calling
BeginPage multiple times.

(I'm not sure about your other questions, offhand.)

~Daniel

[1] e.g. https://bugzilla.mozilla.org/show_bug.cgi?id=240625

Robert O'Callahan

unread,
Apr 25, 2012, 1:47:44 AM4/25/12
to Julian Viereck, mozilla.dev...@googlegroups.com, dev-tec...@lists.mozilla.org
On Wed, Apr 25, 2012 at 9:31 AM, Julian Viereck <
julian....@googlemail.com> wrote:

> An easy solution might be to "move all the code from the beginning of
> `PrintNextPage` until the dc->BeginPage() into the "PrePrintNextPage()"
> function.


That sounds right.


> Then, before a similar surface is created, a dc->BeginPage() call already
> happened. To do this, I need some more clarification on this code:
>
>
> http://mxr.mozilla.org/mozilla-central/source/layout/generic/nsSimplePageSequence.cpp#620
>
> * can PresContext()->IsRootPaginatedDocument() be false at any time? The
> `PrintNextPage()` function is only called for printing to paper - it's not
> called for preview. Therefore, the PresContext is always a
> paginatedDocument?
>

Yes. (It is for print preview too.)


> * the function might call `dc->BeginPage()` multiple times. Shouldn't this
> function add only one page to the output? Is this a bug or a feature?
>

What Daniel said. I'm not sure how you should handle print-selection ---
probably just disable the callbacks and print whatever's already in the
canvases, or refactor print-selection so we don't do this loop here.

Julian Viereck

unread,
Apr 26, 2012, 2:10:22 PM4/26/12
to rob...@ocallahan.org, mozilla.dev...@googlegroups.com, Julian Viereck, dev-tec...@lists.mozilla.org
Can someone give me pointers what writing the unit test for the mozPrintCallback should look like (very roughly what's expected to be tested)? Are there already some tests for printing that I could use as a "template"?

Best,

Julian

fantasai

unread,
Apr 26, 2012, 9:19:29 PM4/26/12
to
On 04/17/2012 02:11 PM, julian....@googlemail.com wrote:
>> The CSS3 Paged Media discussions on www-style are tagged with [css3-page].
>> Feel free to poke me with any questions; I'm the spec editor. And please
>> send comments about things that are stupid or broken to www-style so we
>> can fix them...
>
> One part of the spec that might need to get implemented for PDF.JS is the ability to specify the size of individual pages. If I get that right from the spec, this can be done like this:
>
> @page narrow { size: 4cm 4cm }
> div.foo { page: narrow }
>
> Can you please help me, what the expected behavior is in the following case:
>
> Assume the default page size is A4 and a div matching "div.foo" is at the very bottom of the first printed page. As it has a `page` property defined, the `@page narrow` properties get applied to the page the "div.foo" is on. But the page-size required the A4 page to shrink. As a consequence of the shrink process, the "div.foo" element is no longer on the same page as before, as it was at the very bottom and the page got smaller. In a consequence, the page that now contains the "div.foo" should get the narrow-page-size applied. But as the "div.foo" is no longer on the first page, the "@page narrow" doesn't apply to the first page anymore, and it size should be A4 again. But then the "div.foo" is on the first page again, the "@page narrow" get applied and ...
>
> Do you see the endless loop? I haven't read the entire spec, but from what I read the behavior in this case is not clear to me.

If div.foo is assigned a different 'page' value than its previous sibling,
it forces a page break. So the first page remains unaffected, and the
next page is 4cm x 4cm.

http://dev.w3.org/csswg/css3-page/#using-named-pages

~fantasai

Julian Viereck

unread,
May 1, 2012, 4:06:54 PM5/1/12
to Julian Viereck, mozilla.dev...@googlegroups.com, dev-tec...@lists.mozilla.org
The async version of the mozPrintCallback API is working for printing output (it still has very rough edges, but it "works").

However, things don't work correctly for print-preview: As I open print-preview, I see the calls on the canvas, that are done immediately when the mozPrintCallback is called. The drawings performed against the canvas context with some delay don't show up in preview. If I scroll the preview window, the repainted area of the canvas show the updated/right content. Also, if the print preview loses focus, the canvas shows the actual content.

It looks like the "redraw this (part of the) canvas, as something in it changed" doesn't work.

I tried to figure out what's going wrong here, but I know to little about the Gecko layout/redraw code/handling and what I should look for :/

Here is what I got so far:
* nsCanvasRenderingContext2D.mIsEntireFrameInvalid doesn't seem to get reset to false
* I've forced to ignore `mIsEntireFrameInvalid` and let the call to `nsHTMLCanvasElement::InvalidateCanvasContent` happen. The `!GetPrimaryFrame()` evaluates to true and therefore the function returns early without telling the layer to update

Can someone give me some pointers what to do here?

A way to get around this issue is to draw the nsPageFrame after all the canvas elements inside this page have finished the mozPrintCallback. Rendering the canvas progressive in print preview has the advantage to display other parts of the page (e.g. "normal" DOM content) to the user already and let him look at the drawings, while they build up.

Best,

Julian

Julian Viereck

unread,
May 3, 2012, 3:36:52 PM5/3/12
to Julian Viereck, mozilla.dev...@googlegroups.com, dev-tec...@lists.mozilla.org
Taking discussion from IRC with :roc offline:

> jviereck> roc: hi. I was thinking about adding a `abort()` function to the PrintState that is passed to the mozPrintCallback. Calling this > function will dismiss the printing process. For PDF.JS this is required if rendering one of the pages fails. What do you think about it?
> roc> jviereck: why would you want to abort just because you couldn't render a page?
> roc> why not just render the page as a big red cross or something and carry on printing other pages?

Failing on one page is bad enough. Imagine the user starts the print job, goes to the printer and the printout looks correct at the first spot. But later he notice, something is missing that he depends on it.

Also, printing red crosses might be very expensive for the user and therefore he'd like to know if something went wrong.

As the abortion is programatic, the printing can get aborted, the developer notifies the user that something went wrong during printing and might give him the option to reprint but skip the failing page.

Roc, what's your feeling about the proposed `abort()` now?

Best,

Julian

Robert O'Callahan

unread,
May 3, 2012, 8:44:27 PM5/3/12
to Julian Viereck, mozilla.dev...@googlegroups.com, dev-tec...@lists.mozilla.org
Your arguments are irrefutable.
Reply all
Reply to author
Forward
0 new messages