Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

plotly-graph PDF with electron's printToPDF #6

Merged
merged 19 commits into from
Sep 1, 2017
Merged

Conversation

etpinard
Copy link
Contributor

See commit 54b4173 for the details and see comparison with the batik outputs here.

Note that, electron's printToPDF is way faster too. Generating all 411 mocks took about 8 minutes using batik and just under 3 minutes using printToPDF.

- adding util/remote makes the renderer's remote module easily
  mockable using sinon.
- add stubProp common test util, to e.g. reduce iframeLoadDelay
  in tests
- when *batik* option isn't defined, use win.webContents.printToPDF
- step-by-step:
  + use svg image string to render image in DOM
  + call window.getSelection.selectAllChidren to grab only img div
  + call printToPDF on img div
@etpinard
Copy link
Contributor Author

etpinard commented Aug 22, 2017

To me 👀 , printToPDF and batik output very similar PDFs, but comparing PDFs side-by-side isn't the most precise.


For all images below: left: batik / right: printToPDF

printToPDF seems to give better results for graphs with outlined legends:

image

bounding boxed in general look a lot better in the printToPDF outputs:

image

image

Both outputs are wrong (they both show little gap(s)) for bar_bargap0:

image

batik is bad at doing carpets:

image

batik doesn't like parcoords tick fonts:

image

printToPDF has issues with some gl3d grid lines:

image

printToPDF doesn't seem to like polar text bg:

image

printSelectionOnly: true,
pageSize: {
width: (imgOpts.width) / 0.0035,
height: (imgOpts.height) / 0.0035
Copy link
Contributor Author

@etpinard etpinard Aug 22, 2017

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

pageSize here expects numbers in micrometer, plotly.js set width and height in pixels. I took this number from this source. I gives ok results, but not perfect.

Comparing the batik and electron outputs of a graph with non-white bg illustrates the issue:

image

batik on the left / printToPDF on the right.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Commit 5992a8b improves this.

But printToPDF still output a (small white) margin:

image

Would this be a deal-breaker for printToPDF?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

with 135cb1b

image

those white margins are gone (actually, they're still there, but now they appear with background-color: fullLayout.paper_bgcolor)

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for the deep dive into this one @etpinard - given all the other improvements we get with printToPDF I think this extra bit of margin, now that it keeps the right color, is acceptable.

Might be worthwhile at some point taking another look at this but for the first release this is great.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Might be worthwhile at some point taking another look at this

For sure. I'll open an issue as soon as this PR is merged.

@bpostlethwaite
Copy link
Member

bpostlethwaite commented Aug 23, 2017

Thanks for the gist https://gist.github.com/etpinard/1ce4f6e4c089d5bfc426485638d0abda - nice work on the comparison.

I am impressed how well the Electron printToPDF did without much tweaking. My gut says that it is close enough "out of the box" to invest the time making it even better.

I'll let @alexcjohnson's discerning 👀 scrutinize those pdfs and for the final judgement on that one :)

@etpinard
Copy link
Contributor Author

My gut says that it is close enough "out of the box" to invest the time making it even better.

I think so too, especially that the out-of-the-box is much faster 🐎

- only expose svg2pdf routine!
- i.e. 🔪 eps logic (will be in own pdftops util)
  and scale logic (to be done in renderer hopefully)
@etpinard
Copy link
Contributor Author

etpinard commented Aug 28, 2017

by the way, gistclone is pretty awesome for cloning gists w/o having to go to the gist page (which might take a long time to load).

- use correct px-by-micron logic
- add margin offset (that appears to work ok)
@etpinard
Copy link
Contributor Author

Fonts (after installing them locally - ref #11) are looking sharp:

image

image

@etpinard
Copy link
Contributor Author

Oh and I should add, switching to electron's printToPDF will ✅ plotly.js' oldest bug plotly/plotly.js#109 🎉 ⏲️ 🎉

Screenshot of pdf output:

image

@alexcjohnson
Copy link
Collaborator

Some vacation-delayed comments on the images above:

printToPDF seems to give better results for graphs with outlined legends:
bounding boxed in general look a lot better in the printToPDF outputs:

So much better to be generating PDFs in the same environment the fonts were originally rendered in - major win!

Both outputs are wrong (they both show little gap(s)) for bar_bargap0:

We can ignore this for now as it's not new, but would be worth investigating later. This is happening because we apply shape-rendering: crispEdges via CSS (classed('crisp', true)) rather than inline styling. Though I notice that's not applied consistently anymore, I see a lot of places we've used inline styling. Anyway the idea, which may need refining particularly in light of electron, was to explicitly drop it from vector output so we get higher precision when the user can do stuff like put the vector image into another container and zoom or rotate it. Unfortunately that means these subpixel gaps between bars return, as it was only crispEdges that hid them. Not sure that this even has a solution in theory, when you consider the possibility of rotating the vector image on screen.

FYI Drawing.crispRound is also explicitly short-circuited in static plots - this is even more important in fact, so that vector output has items positioned & sized correctly to sub-pixel precision.

batik is bad at doing carpets:
batik doesn't like parcoords tick fonts:

How did we miss these for so long? Nice wins for electron anyway... but I worry that svg exports brought into another environment (illustrator and its ilk?) might cause this to surface again - perhaps we could figure out what carpet & parcoords do differently here and make them match everything else?

printToPDF has issues with some gl3d grid lines:

Is it not getting the same plotGlPixelRatio as we use normally?

printToPDF doesn't seem to like polar text bg:

polar text gets a white shadow behind it, which seems to get corrupted by printToPDF... but it looks like Batik is dropping the shadow entirely, which is arguably less bad but still not correct. Parcoords is supposed to have a similar shadow on tick labels, which it appears both printToPDF and Batik miss. Would be worth spending some time figuring out if there's a more robust way to apply this shadowing.

- open new window for each pdf export which:
  + 🔪 the extraneous margin-left in printSelectionOnly exports
  + no longer freezes up when parallelLimit > 1
- we still need to add a few pixel to the export pageSize
  + but now hide those artificial margin by setting and exporting
    the graphs' bgColor.
const pxByMicrometer = 0.00377957517575025
const inducedMarginOffset = 18
const offset = 6
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We still need this unfortunately to make sure the exported pdf always fits on one page.

Like I said to @alexcjohnson in a private convo, I believe electron (or chromium?) is messing up their pixel to micron computation somewhere. I should try to write up a bug report for them. Oh well, in the meantime hacks appears to work.

To see it the results:

npm i -g gistclone
gistclone https://gist.github.com/etpinard/1ce4f6e4c089d5bfc426485638d0abda
cd "plotly-graph PDF comparison"
<pdf-viewer-of-your-choice> all-electron.pdf

// we can't set image src into html as chromium has a 2MB URL limit
// https://craignicol.wordpress.com/2016/07/19/excellent-export-and-the-chrome-url-limit/

win.loadURL(`data:text/html,${html}`)
Copy link
Contributor Author

@etpinard etpinard Aug 31, 2017

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Setting this to data:image/svg+xml,${imgData} wasn't an option unfortunately as Chromium sets a limit of 2MB for URLs, and yes, SVGs of size > 2MB do happen.

@etpinard
Copy link
Contributor Author

How did we miss these for so long?

We don't test PDF generation for all our mocks (just these mocks to be precise) and even then we only check if the export file size is bigger than some number.

It might worth spending some time adding pdf diff test to our suite (e.g. using diffpdf). I suspect once plotly-image-exporter is out, pdf exports will be pretty popular.

but I worry that svg exports brought into another environment (illustrator and its ilk?)

Good point about Illustrator, we should check 🔬

printToPDF has issues with some gl3d grid lines:
Is it not getting the same plotGlPixelRatio as we use normally?

This is probably the last blocking issue remaining (well this and the polar font thing, but it's polar so 😏 ). To make matters worse, those grid lines look different when generated with xvfb and regular headfull electron. Left w/o xfvb / right w/ xfvb:

image

Hopefully tweaking plotGlPixelRatio will do the trick 🙂

@@ -30,6 +30,9 @@ module.exports = {

mathJaxConfigQuery: '?config=TeX-AMS-MML_SVG',

// config option passed in render step
plotGlPixelRatio: 3,
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hopefully tweaking plotGlPixelRatio will do the trick 🙂

Yep, it did:

image

* - https://xmlgraphics.apache.org/batik/tools/rasterizer.html
* - http://archive.apache.org/dist/xmlgraphics/batik/
*/
class Batik {
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

One for the ages.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🌅 It's a brand new day 💯

- 🔪 plotly-graph-exporter stdout msg test
  (for now, this fails on CI)
@@ -0,0 +1,33 @@
version: 2
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Circle 2.0 is blazing fast 🐎 - see https://circleci.com/docs/2.0/

@etpinard
Copy link
Contributor Author

etpinard commented Sep 1, 2017

Ok. I gonna go ahead and merge this thing to keep the wheels spinning.

Feel free to open an issue if you want to discuss anything printToPDF related.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants