Closed Bug 1772225 Opened 2 years ago Closed 2 years ago

PDF from Google Docs prints out with little artifacts (rasterized text?)

Categories

(Core :: Printing: Output, defect, P2)

defect

Tracking

()

VERIFIED FIXED
104 Branch
Tracking Status
firefox-esr91 --- wontfix
firefox-esr102 --- wontfix
firefox101 --- wontfix
firefox102 --- wontfix
firefox103 + verified
firefox104 --- verified

People

(Reporter: overholt, Assigned: jfkthame)

References

(Regression)

Details

(Keywords: regression)

Attachments

(5 files)

STR

  1. on https://docs.google.com/document/d/1VcZfztKj4Fc3BsI36NtAU1oxrNFDQUZDO4B7BV46rwQ/edit, press the print button (or via the menu)
  2. the resulting pdf opens in firefox
  3. print it

Expected

  • crisp text in my printout

Actual

  • see attached photo of the resulting page (contrasted with me printing the first page of a bug page itself)

I just printed the page myself and not able to reproduce.
I'm on Beta/102.b2, Win10, printing to HP Officejet Pro 8615

Andrew, can you provide your profile details and I can see if jwatt can poke at this? Thanks.

Flags: needinfo?(overholt)

I can also repro - Andrew and I are both on MacOS. I'm using version 11.4 (Big Sur) using Nightly 103.0a1 (2022-06-01) (64-bit).

FYI: I don't see this bug with other google docs; they are crisp when printed. Andrew has an interesting theory -- Is it possible that offscreen canvas is the cause of the artifacts/dithering? (i.e. Is Andrew's test doc using offscreen canvas? )

Flags: needinfo?(overholt)

pdf.js uses OffscreenCanvas since bug 1763330, so it might be related (though I doubt it)... In any case it should be easy to discard, does this reproduce with gfx.offscreencanvas.domain-enabled=false?

For printing, pdf.js uses mozPrintCallback, a privileged API. Off-hand, this should go through nsDisplayCanvas::Paint with a DrawTargetRecording, which is fine but at a glance we seem to rasterize the text using FillRect, rather than potentially send the text recording.

Andrew / Lee, do you happen to know how easy would it be to add the canvas as a subrecording in this case, like we do for iframes etc, rather than calling FillRect which (presumably, might be wrong) rasterizes?

That should explain the print quality difference.

Flags: needinfo?(lsalzman)
Flags: needinfo?(aosmond)

Andrew, can you attach to the bug the PDF that fails to print for you? Just so we don't lose it.

Flags: needinfo?(overholt)

(In reply to Maire Reavy [:mreavy] from comment #2)

FYI: I don't see this bug with other google docs; they are crisp when printed. Andrew has an interesting theory -- Is it possible that offscreen canvas is the cause of the artifacts/dithering? (i.e. Is Andrew's test doc using offscreen canvas? )

Oh I hadn't considered that. I was thinking of https://workspaceupdates.googleblog.com/2021/05/Google-Docs-Canvas-Based-Rendering-Update.html, actually, but it seems Frank tried with the doc I created and not another one so that isn't the case.

Attached file Test text.pdf (deleted) —

(In reply to Emilio Cobos Álvarez (:emilio) from comment #4)

Andrew, can you attach to the bug the PDF that fails to print for you? Just so we don't lose it.

Here's what got generated just now and I assume it's either the same or quite similar to what was generated last night when I tried (it's the same doc and the same Nightly so hopefully nothing changed on the gdocs side).

Flags: needinfo?(overholt)

Just an fyi...Jwatt (printing) and Jkew (fonts) will be on holiday in UK through the weekend. Asked Emilio to convene with them 6/6 on return to get their feedback on this.

Can someone who can reproduce work up a regression range? sounds like a recent change introduced this.

It regressed some time between 2021-04-28 and 2021-04-29 (trying to narrow that down more).

Attached image screenshot text rendering comparison (deleted) —

Likely from the cairo library update in bug 739096. The output of 'Save to PDF' improved but is still noticeable rasterized. Rendering of PDF provided by GDocs at the bottom for comparison.

Maybe Jonathan has thoughts given comment 10.

Flags: needinfo?(jfkthame)

Hello,

I have managed to reproduce this issue on Windows 11, macOS 11.6.6 and Ubuntu 20 with the latest Nightly 03.0a1 (2022-06-02) and a Canon TS3400 series.

Upon attempting to pull a narrower regression range than the already provided one from comment 11, the bisection tool returns the "no more data to bisect" when setting the 2021-04-27(good) - 2021-04-30(bad). This is the regression range I was able to pull keeping this in mind.

From the above mentioned pushlog, was wondering if 1675965 and/or 1707977 could be the culprits for this situation.

It is worth mentioning that I have managed to reproduce this behavior with other documents as well , other then the test file provided.
Here is an image portraying how the print quality has changed from 2021-04-28(good) to 2021-04-29(bad) to latest Nightly .

Hope this helps.

I didn't manage to reproduce with my personal printer (Brother) on windows 11 but I printed it in using Save to PDF, Microsoft Print to PDF and Adobe print.
Both Microsoft and Adobe are generating some paths (set of Bezier curves for each glyph) and Save is rasterizing the content and then put the image into a pdf.
So I guess that a possible way to fix this issue (and likely few others we've) would be generate some paths instead of rasterizing.

The top of the o or a are a bit flat and there are a lot of aa artifacts around some letters.
The spikes around the letters are likely because of the algorithm used to scale up the image when using Canvas::drawImage.

It looks to me like Emilio's comment 3 is key here. When the user asks GDocs to print, it appears to first generate a PDF version of the document (server-side), which Firefox displays using pdf.js, which renders it to a <canvas>. When we then print that document, the entire pdf.js canvas is printed as a raster image; hence the poor rendering.

So we need to figure out why the canvas is being rasterized and printed (or saved to PDF) as an image. It sounds like the cairo update in bug 739096 may be responsible for this, but offhand I don't know why. My current guess is that somewhere inside cairo, we're encountering an error or unsupported case that causes the library to fall back to simple rasterization instead of generating vector output.

(In reply to Calixte Denizet (:calixte) from comment #14)

I didn't manage to reproduce with my personal printer (Brother) on windows 11 but I printed it in using Save to PDF, Microsoft Print to PDF and Adobe print.
Both Microsoft and Adobe are generating some paths (set of Bezier curves for each glyph) and Save is rasterizing the content and then put the image into a pdf.

That's interesting.... the original PDF generated by GDocs contains the text as text objects, with an embedded (subset) Arial font to render them. So I would expect a "good" save-to-pdf path to preserve this, not to convert the glyphs to paths (which will most likely lose lots of accessibility etc compared to having actual text in the document).

Flags: needinfo?(jfkthame)

I created a pdf with a black rectangle in using Inkscape, I opened it in Firefox, print it in using Save in PDF.
The initial pdf contains few commands to set the color, draw and fill the rectangle but the "saved" pdf contains an image of the rectangle (reproducible on Windows 11 and Mac 12.3.1).
So my feeling is that something is configured somewhere to always rasterize the canvas.

That's interesting.... the original PDF generated by GDocs contains the text as text objects, with an embedded (subset) Arial font to render them. So I would expect a "good" save-to-pdf path to preserve this, not to convert the glyphs to paths (which will most likely lose lots of accessibility etc compared to having actual text in the document).

I agree with you, I just wonder why Adobe and Microsoft aren't doing that.

Well, that's the issue I'm describing in comment 3, right? I don't see anything that would use the DrawTargetRecording path for canvas.

Right - as currently implemented, there is no other way to print it because whatever is rendered to a <canvas> element exists only as a bitmap image; AFAIK we don't record the drawing operations that go to make up a canvas image, they're just executed immediately.

So pdf.js renders the original (structured, vector-based) PDF to a canvas rendering context, generating a full-page rasterized image, which is what we see on the screen. When we then ask Firefox to print, all it has to work with is that rasterized (screen-resolution) bitmap; we don't have a mechanism to go back to the original drawing operations (derived from the original PDF) and re-execute those on a different (higher-resolution or vector-based) target.

Maybe we could create some kind of alternative path (maybe a new internal printing API) that pdf.js can use for printing, which causes it to re-render the PDF to a printing context instead of the canvas bitmap context. So when we print a pdf.js view, it wouldn't actually be printing the <canvas> element as displayed in the browser but re-drawing the original PDF elements to a printing DrawTarget.

Alternatively, we could try to solve this more generally for all <canvas> elements by having them record the drawing operations performed, and then replaying those operations when asked to print (instead of the only "recording" being the bitmap surface).

(Note that this is a general problem that affects any <canvas> element when printed: they'll tend to look a bit coarse or blurry because we're printing a bitmap image that was intended for screen display. But it's most obvious with pdf.js pages, where there's an expectation that PDFs should display or print clearly at any scale, but actually the whole page is being rendered as a screen-resolution bitmap image.)

We have a mozPrintCallback already which we use for PDF.js, presumably we could use the DrawTargetRecording when executing it...

:jfkthame, at least in the pdf.js context, I guess that always recording could lead to decrease the overall performances.

As mentioned by Emilio, thanks to mozPrintCallback we always redraw the canvas when printing:
https://github.com/mozilla/pdf.js/blob/135b9fbcfb2e2554754082d482341b4cbe9e1b43/web/firefox_print_service.js#L54

Printing blurrines is one of the most common complaints about PDF.js (see the bugs I just added to "See Also"). We should really try not to regress it further.

(In reply to Calixte Denizet (:calixte) from comment #21)

:jfkthame, at least in the pdf.js context, I guess that always recording could lead to decrease the overall performances.

As mentioned by Emilio, thanks to mozPrintCallback we always redraw the canvas when printing:
https://github.com/mozilla/pdf.js/blob/135b9fbcfb2e2554754082d482341b4cbe9e1b43/web/firefox_print_service.js#L54

We could measure the performance impact of always recording, and if it isn't feasible we could enable it exclusively in the mozPrintCallback case.

The most immediate thing we could perhaps do to mitigate the blurriness would be to rasterize at higher resolution.

It seems that the FirefoxPrintService code is currently using a default of 150dpi, which is pretty low for printed output. If I hard-code this._printResolution there to 300 instead, Save to PDF gives me a less-blurry result, and presumably physical output would also be improved.

The downside, of course, is that this generates substantially larger PDF output files / print spool files, and is likely significantly slower. So it's a trade-off, and far inferior to a solution that actually records the canvas drawing operations and replays them to the print target.

A scale factor of 2 (almost 150 / 72) has been added in the past for a similar issue:
https://bugzilla.mozilla.org/show_bug.cgi?id=811002#c1 (pretty interesting bug).

I confirmed the regression window locally. Prior to the regression we properly emit glyphs when printing. After the regression it becomes a single rasterized bitmap.

Increasing the resolution could work in some cases, but the problem will likely reappear at some point...
It works correctly with MS Print to pdf, so my feeling is that we should have something at least equivalent and we've likely the opportunity to do something better (real fonts instead of paths).

(In reply to Jeff Muizelaar [:jrmuizel] from comment #25)

I confirmed the regression window locally. Prior to the regression we properly emit glyphs when printing. After the regression it becomes a single rasterized bitmap.

Yeah -- somehow I must have messed up when testing, as I initially thought we rasterized even before the cairo update, but you're right, we did generate actual glyphs.

Interestingly, however, on both Windows and Linux, I get rasterized output from Save to PDF with builds both before and after the cairo update landed, although the rasterization does change somewhat.

So it kind of looks like this may have only ever worked "well" on macOS (before bug 739096), and the other platforms have always had poor output.

This was almost certainly caused by https://hg.mozilla.org/mozilla-central/rev/698ee02c64164310cc03aa69d259960b12cf751f. The recording has a CreateSimilarSurface that the majority of the content is drawn to. Prior to 698ee02c64164310cc03aa69d259960b12cf751f the CreateSimilarSurface used a CGLayer which avoids rasterization with a PDF CG context. Without CGLayer support, similar surfaces just use a regular CGBitmapContext which causes this bug.

Makes sense. I was thinking that looked like the most probable cause. So we could try to resurrect and update that code, I guess.... or, given that the other platforms also end up with rasterized PDF output, would there be some more general way we can address this?

Regressed by: 739096

The CreateSimilarDrawTarget comes from https://searchfox.org/mozilla-central/rev/97c13320e56884daf14016048e9d2182c880f8a9/layout/generic/nsPageSequenceFrame.cpp#605.

I don't know what the cause of the other platforms getting rasterized output is. I would expect things should be able to work properly there to.

The win32-printing surface looks like it should handle create_similiar_surface correctly.
https://searchfox.org/mozilla-central/rev/97c13320e56884daf14016048e9d2182c880f8a9/gfx/cairo/cairo/src/win32/cairo-win32-printing-surface.c#2012

I'm not as sure about the pdf surface.

As to fixing this, I'm not sure the best approach yet. I'll take a closer look.

Has Regression Range: --- → yes

The best solution I've come up with so far is resurrecting the code. The way the painting happens with mozPrintCallback makes it hard not to use create_similar_surface and it doesn't look there is an easy way to paint one CGPDFContext to another.

Flags: needinfo?(aosmond)

Another option: Use a DrawTargetRecording instead of a SimilarDrawTarget for the canvases and then add support for replaying a SourceSurfaceRecording in DrawTargetCairo::DrawSurface/Fill

Jeff, what do you think about resurrecting the code for 102 and 103 so we don't ship with this regression? Then it would be nice to find a solution for all platforms given the amount of duplicates we have for the PDF print blurriness bugs.

[Tracking Requested - why for this release]: Severe PDF and Google Docs printing regression.

Flags: needinfo?(jmuizelaar)

Jonathan, I haven't looked at how hard it would be to restore the CGLayer functionality. Do you have a better sense of this?

Flags: needinfo?(jmuizelaar) → needinfo?(jfkthame)

My recollection is that at the time of the cairo update, it looked like migrating it was going to be a fair bit of work, because so much of cairo's internal plumbing around surfaces/lifetimes/ownership had undergone substantial changes. So....feels like it could be a bit tricky/risky.

How much work would comment 32 involve, do you think? (Seems like creating a recording there should be pretty simple, but I don't have a good handle on what it would take to hook up the replay side of it.)

Flags: needinfo?(jfkthame)
Severity: -- → S2
Priority: -- → P2

The approach from comment 32 is pretty simple in theory but there could be unforeseen issues. We already use recordings for printing so that's a good sign.

In DrawSurface/Fill you should be able to check the type of SourceSurface. If it's a RECORDING than you should be able to use InlineTranslator or some adaption of it (see PrintTranslator) to replay it on the destination surface.

Depends on: 1774631
Flags: needinfo?(lsalzman)

[Tracking Requested - why for this release]: Same reason as comment 33, severe PDF and Google Docs printing regression.

I'm currently working on a patch that should fix this for macOS by reintroducing a minimal amount of CGLayer support into cairo_quartz_surface. Will post for review after a bit more cleanup/testing.

Assignee: nobody → jfkthame
Status: NEW → ASSIGNED

In my local testing, this seems to fix the quality of print/pdf output.
Try build in progress: https://treeherder.mozilla.org/jobs?repo=try&revision=a349f0d00e489a93ee070f4e53194853460375fd.

Hmm, looks like printing reftests are failing, will investigate...

Pushed by jkew@mozilla.com: https://hg.mozilla.org/integration/autoland/rev/77806b45f935 Implement a CGLayer-backed version of cairo_quartz_surface, to improve print/PDF output quality for pdf.js documents. r=jrmuizel
Status: ASSIGNED → RESOLVED
Closed: 2 years ago
Resolution: --- → FIXED
Target Milestone: --- → 104 Branch

The patch landed in nightly and beta is affected.
:jfkthame, is this bug important enough to require an uplift?

  • If yes, please nominate the patch for beta approval.
  • If no, please set status-firefox103 to wontfix.

For more information, please visit auto_nag documentation.

Flags: needinfo?(jfkthame)

Comment on attachment 9282585 [details]
Bug 1772225 - Implement a CGLayer-backed version of cairo_quartz_surface, to improve print/PDF output quality for pdf.js documents. r=jrmuizel

Beta/Release Uplift Approval Request

  • User impact if declined: Poor-quality rasterized print/pdf output from pdf.js documents
  • Is this code covered by automated tests?: No
  • Has the fix been verified in Nightly?: Yes
  • Needs manual test from QE?: Yes
  • If yes, steps to reproduce: 1. On macOS, open the attached testcase (https://bugzilla.mozilla.org/attachment.cgi?id=9279413) in the Firefox pdf.js viewer
  1. Print to various printers and check that output is clear (not low-res rasterized)
  2. Print to the Save-to-PDF destination and check that the resulting PDF has clear (not blurry) text, and text can be selected/searched in a PDF viewer.
  • List of other uplifts needed: None
  • Risk to taking this patch: Low
  • Why is the change risky/not risky? (and alternatives if risky): This patch basically resurrects some code that we used to have prior to the big cairo update, so the "new" code here is not entirely new/untested, it restores an old codepath that we used to use routinely.
  • String changes made/needed:
  • Is Android affected?: Yes
Flags: needinfo?(jfkthame)
Attachment #9282585 - Flags: approval-mozilla-beta?
Flags: qe-verify+

(Once this is safely landed/tested on beta, I think we should also consider uplift to esr102.)

Comment on attachment 9282585 [details]
Bug 1772225 - Implement a CGLayer-backed version of cairo_quartz_surface, to improve print/PDF output quality for pdf.js documents. r=jrmuizel

Approved for 103.0b3, thanks.

Attachment #9282585 - Flags: approval-mozilla-beta? → approval-mozilla-beta+
QA Whiteboard: [qa-triaged]

Hello,

Confirming this issue as verified fixed on Windows 10x64 , macOS 11.6.7 and Ubuntu 22 with HP DeskJet 3720, Phaser 3020, HP OfficeJet 6950 and Canon TS3400 using 104.0a1 (2022-06-30) and 103.0b3.

Status: RESOLVED → VERIFIED
Flags: qe-verify+
Regressions: 1786130
No longer regressions: 1786130

Note, this bug caused a pretty painful regression -- bug 1779202 -- which is proving a bit difficult to understand/address (see bug 1779202 comment 20 and others; it seems to be an Apple CoreGraphics bug with rendering certain PDFs, and we need to better-understand what's going wrong so we can report the underlying bug upstream, & also work around in the meantime).

To mitigate that bug in the short term, jfkthame has just landed a patch which effectively undoes the behavior-change from this bug here, and instead puts this bug's behavior behind a pref, gfx.cairo_quartz_cg_layer.enabled.

So: as of tomorrow's Nightly (probably), this bug here will probably be reproducible again (i.e. the behavior described in comment 0 here will again be the default). Folks can fix it locally by setting gfx.cairo_quartz_cg_layer.enabled to true (with the downside that then you'll also be able to reproduce bug 1779202 when printing certain PDFs).

I've filed bug 1789482 for the followup work here (identifying & avoiding the scaling bug so we can flip gfx.cairo_quartz_cg_layer.enabled to true and effectively "re-fix" this bug without bringing back bug 1779202).

In the meantime, we'll effectively behave as if this bug were reintroduced; but I'm tracking that in this new bug, bug 1789482, rather than reopening this bug (given that patches have landed here and months have gone by).

You need to log in before you can comment on or make changes to this bug.

Attachment

General

Created:
Updated:
Size: