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

webContents.printToPDF regression in v5 #18093

Closed
3 tasks done
fabiospampinato opened this issue May 1, 2019 · 10 comments
Closed
3 tasks done

webContents.printToPDF regression in v5 #18093

fabiospampinato opened this issue May 1, 2019 · 10 comments

Comments

@fabiospampinato
Copy link
Contributor

Preflight Checklist

  • I have read the Contributing Guidelines for this project.
  • I agree to follow the Code of Conduct that this project adheres to.
  • I have searched the issue tracker for an issue that matches the one I want to file, without success.

Issue Details

  • Electron Version:
    • 5.0.0
  • Operating System:
    • macOS 10.14
  • Last Known Working Electron version::
    • 4.0.0

Expected Behavior

The html should get printed to PDF.

Actual Behavior

An empty PDF is generated.

To Reproduce

I'm using this function to print arbitrary HTML to PDF. For the sake of debugging this I've added the following here:

options.html = fs.readFileSync ( '/Users/fabio/Desktop/foo.html', { encoding: 'utf8' } );

Now, changing the content of the foo.html file I get the following results, these HTMLs strings get printed to an empty PDF:

<html>
  <head>
    <meta charset="utf-8">
    <style>
      @keyframes wtf {
        50% {
          background-color: #fde9bc;
        }
      }
    </style>
  </head>
  <body>Printed!</body>
</html>
<html>
  <head>
    <meta charset="utf-8">
    <style>
      @keyframes wtf {
        50% {
          /* background-color: #fde9bc; */
        }
      }
    </style>
  </head>
  <body>Printed!</body>
</html>

While the following get properly printed to PDF:

<html>
  <head>
    <meta charset="utf-8">
    <style>
      @keyframes wtf {
        50% {
          background-color: red;
        }
      }
    </style>
  </head>
  <body>Printed!</body>
</html>
<html>
  <head>
    <meta charset="utf-8">
    <style>
      @keyframes wtf {
        50% {
        }
      }
    </style>
  </head>
  <body>Printed!</body>
</html>

It looks like a pretty specific edge case, unfortunately I guess I would have to fallback to Electron v4, which didn't have this issue.

@fabiospampinato
Copy link
Contributor Author

It looks like that hex colors make the whole thing fail, when converting them to rgb it works.

@fabiospampinato
Copy link
Contributor Author

fabiospampinato commented May 5, 2019

It looks like that's actually the # character that's causing the issue, as also things like:

clip-path: url(foo#clip-bottom);

cause the thing to fail.

@fabiospampinato
Copy link
Contributor Author

This is really not a solution at all, as it's broken in many fundamental ways, but I've written this function for transforming an HTML string into something Electron v5 can print, perhaps something similar could work for you too, fellow reader:

function ElectronNumSignPatch ( html ) {
  const hexRe = /#([0-9a-f]{3}|[0-9a-f]{6})/gi;
  const styleRe = /<style>([^]*?)<\/style>/gi;
  const headRe = /<\/head>/i;
  function hex2rgb ( hex ) {
    if ( hex.length === 3 ) return hex2rgb ( `${hex[0]}${hex[0]}${hex[1]}${hex[1]}${hex[2]}${hex[2]}` );
    return `rgb(${parseInt ( hex.slice ( 0, 2 ), 16 )},${parseInt ( hex.slice ( 2, 4 ), 16 )},${parseInt ( hex.slice ( 4, 6 ), 16 )})`;
  }
  const links = [];
  return html.replace ( styleRe, ( match, css ) => links.push ( `<link rel="stylesheet" type="text/css" href="data:text/css;base64,${btoa ( css )}">` ) || '' )
             .replace ( headRe, ( match ) => `${links.join ( '' )}${match}` )
             .replace ( hexRe, ( match, hex ) => hex2rgb ( hex ) )
             .replace ( /#/g, '&num;' );
}
  • It converts hex colors to rgb (4/8-chars hex colors aren't supported)
  • It converts inline styles into base64-encoded strings (as they may contain id selectors)
  • It converts the remaining # signs into the respective HTML entity

Obviously this has so many flaws, but it works for my use case for now.

@fabiospampinato
Copy link
Contributor Author

fabiospampinato commented May 7, 2019

Actually there's a much better solution, I can't believe I didn't think about this 2 hours ago, just base64 encode the whole thing:

win.loadURL ( `data:text/html;base64,${Buffer.from ( html ).toString ( 'base64' )}` );

@fabiospampinato
Copy link
Contributor Author

Actually there's a much better solution, I can't believe I didn't think about this 2 hours ago, just base64 encode the whole thing:

This workaround has a pretty big caveat though: data urls larger than 2MB aren't allowed in Chromium, so this just fails (did-finish-load is never called) when attempting to printing files whose HTML is larger than 2MB.

I'm not sure what the workaround to that would be, it seems that printing is just broken under Electron (#17523).

@electron-triage
Copy link

Thank you for taking the time to report this issue and helping to make Electron better.

The version of Electron you reported this on has been superseded by newer releases.

If you're still experiencing this issue in Electron 6.x.y or later, please add a comment specifying the version you're testing with and any other new information that a maintainer trying to reproduce the issue should know.

I'm setting the blocked/need-info label for the above reasons. This issue will be closed 7 days from now if there is no response.

Thanks in advance! Your help is appreciated.

@electron-triage electron-triage added the blocked/need-info ❌ Cannot proceed without more information label Feb 19, 2020
@fabiospampinato
Copy link
Contributor Author

fabiospampinato commented Feb 19, 2020

I can still reproduce in v7.10.

And using data-urls is too brittle as they are length limited, if anybody knows a more reliable workaround for this bug I'd be interested in hearing it.

@fabiospampinato
Copy link
Contributor Author

fabiospampinato commented Feb 19, 2020

And using data-urls is too brittle as they are length limited, if anybody knows a more reliable workaround for this bug I'd be interested in hearing it.

Here it is: if html.length <= 1550000 then use a base64-encoded url, this makes sure the reported bug won't be encountered and the ~2MB limit of data-urls won't be hit. If the length of the string is larger than that then write a temporary file and load it via BrowserWindow#loadFile.

This should work in all cases I think, unless the file to write is in the gigabytes or something, however writing a file to disk in order for it to be read again feels wrong when I already have the content of that file in RAM.

Perhaps the length limit of data-urls could be made customizable in Electron?


If node added support for Blob urls this could be solved better, potentially allowing buffers to be loaded directly without converting them to strings first: nodejs/node#16167

@electron-triage electron-triage added 7-1-x and removed blocked/need-info ❌ Cannot proceed without more information labels Feb 26, 2020
@fabiospampinato
Copy link
Contributor Author

fabiospampinato commented Jun 30, 2020

In hindsight I'm not sure this is actually an issue because I guess the hash character makes it so that the rest of the string is treated as a url fragment. I'm not sure whether that's actually useful for anything though.

Anyway there should just be an API for just loading a arbitrarily long buffer/html into the window rather than this dataurls and temporary html files mess.

@codebytere
Copy link
Member

Closing as v5 is no longer supported.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
No open projects
5.0.x
Unsorted Issues
Development

No branches or pull requests

4 participants