Kozea / WeasyPrint

The awesome document factory
https://weasyprint.org
BSD 3-Clause "New" or "Revised" License
7.09k stars 676 forks source link

How to repeat on each page of complex headers (eg, tables)? #92

Closed pikhovkin closed 5 years ago

pikhovkin commented 11 years ago

Hello! Please tell me how to repeat on each page of complex headers (eg, tables) at the top and bottom of the page?

barryvan commented 11 years ago

At this stage, this isn't possible -- see this thread on the mailing list for more details. Basically, to be able to do this, the Generated Content for Paged Media spec would need to be implemented, but it's not, I believe, at a sufficiently-advanced stage for that, as it's only an Editor's Draft at present. :(

In that mailing list thread, I do suggest a (really, truly) horrible hack for getting this to work, involving stacking lots of absolutely-positioned elements on top of each other -- that might work for you, particularly if you're generating the HTML dynamically.

SimonSapin commented 11 years ago

Hi @pikhovkin ,

As you’ve probably seen, what you can put in page-margin headers it pretty limited. You could have any element repeated in every page with position: fixed. (This used to be broken, but was fixed in version 0.19.1.) However a fixedpos element would be the same on every page, you couldn’t have eg. the current page number in there.

The hack @barryvan mentions with fixedpos elements staked on top of each other assumes that such elements are only repeated starting on the page where they are defined, but that is not the case: they are repeated on every page, including pages before.

barryvan commented 11 years ago

Hi @SimonSapin -- just a minor correction. If you use absolutely-positioned, rather than fixed-position, elements, they won't repeat. The only caveat is that you need to include the header content multiple times within the HTML itself to ensure that it's shown on every page.

pikhovkin commented 11 years ago

Thanks.

Solved the problem in two passes. First the document is rendered, and then I'm on every page (except the first) add rendering complex header (top and bottom) with a style {position: fixed}.

SimonSapin commented 11 years ago

I’m interested, how do you do the "except the first page" part? Are the header/footer the same on every other page?

pikhovkin commented 11 years ago

Please see here https://gist.github.com/pikhovkin/5642563

SimonSapin commented 11 years ago

Ok, I see. You can just pass filenames instead of strings: HTML('template.html').

Also, you’re using undocumented APIs. Be aware that there is no promise that your code will still work with the next WeasyPrint version: we reserve the right to completely change undocumented implementation details.

In general this is the moment to ask if the public API should be extended to cover more use cases, but in this case I feel like this really should be a CSS feature. We’re discussing this in the CSS Working Group, there is movement to make CSS Paged Media much more powerful, but it will take a lot of time. If you want to help on that side, send use cases to www-style: http://lists.w3.org/Archives/Public/www-style/

pikhovkin commented 11 years ago

Thanks for comments.

To solve the problem of complex headers and footers can be like in mpdf http://www.mpdf1.com/mpdf/index.php

miracle2k commented 9 years ago

Prince has static() and flow(), but from what I can tell, this is totally non standard, right? I'm possibly interested in implementing something along those lines - or looking into it, anyway. I don't know much about the state of paged media in CSS. The css-gcpm draft has recently been updated. Does it make sense to follow it by now?

SimonSapin commented 9 years ago

The status of GCPM is, well, a long story. And a big mess. The original editor of GCPM left W3C’s CSS working group and republished his stuff as https://books.spec.whatwg.org/ and https://figures.spec.whatwg.org/ . Meanwhile, http://dev.w3.org/csswg/css-gcpm/ has a new editor at W3C, and the two have started diverging. There doesn’t seem to be much interest from browser vendors in either them, so it’s mostly about documenting what Prince XML and Antenna House are doing. Except there doesn’t seem to be much interest from YesLogic (Prince XML) or Antenna House in standardizing what they’re doing or getting interoperability, either.

There’s also CSS Regions http://dev.w3.org/csswg/css-regions/ , a proposal from Adobe that’s getting into IE and WebKit, which might be relevant here.

jsenecal commented 8 years ago

FWIW, I'm using what @pikhovkin shared and it does wonders. Like @SimonSapin mentionned - it could be nice to document those APIs.

doekman commented 7 years ago

Hi, based on the gist of @pikhovkin, I created an extension to Flask-WeasyPrint in this gist. It's still a bit rough, but I thought to share it, report my findings and possibly get feedback.

I understand it uses undocumented API's. I take my chances, because I don't know an other way to create the PDF's I need with WeasyPrint. But documenting them would be appreciated a lot!

It uses Jinja2 to render the Overlay HTML (header/footer-HTML), so page number and number of pages can be used in HTML. Custom positioning of content can be done by supplying positions through the context which will be rendered by Jinja2.

The render method shares the stylesheets with the Overlay pages to prevent errors like "CAIRO out of memory" (I probably got this error because I used @font-face definitions, and this way, the FontConfiguration is shared).

whitelynx commented 6 years ago

Has there been any progress on GCPM in the last 3 years? Is it getting to a stage where we could start implementing something based on it?

liZe commented 6 years ago

Has there been any progress on GCPM in the last 3 years?

Yes, you can check what the CSSWG did.

What we want is running elements. With named pages (added in #495), we should be able to repeat complex headers in page margins.

Is it getting to a stage where we could start implementing something based on it?

Of course. Even if GCPM is "a long story" and "a big mess", running elements can IMHO be added safely to WeasyPrint. The spec didn't really change during the last years, the only reported issue even includes an interesting alternative syntax to fix it.

Implementing both syntaxes looks like a good idea for me. Even if it's a draft, the feature is useful and having it implemented may even help the CSSWG.

Anyone interested? I'll be happy to help!

liZe commented 6 years ago

@doekman

The render method shares the stylesheets with the Overlay pages to prevent errors like "CAIRO out of memory" (I probably got this error because I used @font-face definitions, and this way, the FontConfiguration is shared).

That's probably fixed now that #441 is fixed.

biggosh commented 6 years ago

Hi @liZe.

Is there any planned date for running and element support? I've seen the code and I've noticed there is many parts to touch. If you can give me some suggestions (what to touch and where to insert my code) I can try to start with a simple implementation.

liZe commented 6 years ago

Hi @liZe.

Oh, I'm late…

Is there any planned date for running and element support?

Unfortunately, no.

I've seen the code and I've noticed there is many parts to touch. If you can give me some suggestions (what to touch and where to insert my code) I can try to start with a simple implementation.

Currently, I have no idea about how to implement that, but I can help if you're still interested (and I won't wait half a year this time).

zilles commented 5 years ago

Just something for the next person to find this thread: The "content" property you use in the margin boxes (@top-center) can point to an svg file with arbitrarily complex layout. I used this to create a complex header that would not have been possible with text content.

@top-center {
    content: url(header.svg);
    margin-top: auto;
    margin-bottom: 0;
    height: 1in;
}

header.svg:

<svg height="96" width="684" xmlns="http://www.w3.org/2000/svg">
    <style>
    text { fill: black; }
    .big { font: bold 16pt helvetica; }
    .small { font: 11pt helvetica; }
    </style>
    <text x="0" y="62" class="big">Big Text</text>
    <text x="0" y="83" class="small">Smaller Text</text>
</svg> 
nathandem commented 5 years ago

Hi there,

I recently encountered the same header/footer problem mentioned above by several in this github issue (and elsewhere). Inspired by the examples above, I wrote a solution which is, I think, a little bit more polished and that could/should be integrated in the lib.

Here's the code: https://gist.github.com/nathandem/63f410d501d200cfa5613b38aaf74c44

Why including this patch in the core? Disclaimer: it doesn't try to takes into account web standards, but it will solve a major pain point for the lib users who want to create professional looking PDFs. I had to spend a lot of time searching online plus digging on the internal API to come up with a solution, I'd like to spear this effort/time to the next users :) This is the reason why I think that the feature should be integrated in the core (and visible right away in the doc).

Next steps If the idea of this patch gets accepted, I'd to receive some guidance from the maintainer to know 1) if there're some elements I could improve to reach the code standard of the lib, 2) where I should put the code. I guess some of it should be part of the public API (the main weasyprint/__init__.py), some may be located elsewhere (maybe weasyprint/layout/pages.py?). I will then write a PR with tests and documentation, so that everyone can solve this problem quickly from now on!

So, are you interested in me adding support for this feature in Weasyprint? :)

PS : If someone launches himself/herself in a major rewrite to support the newer standards someday (proper support of flexboxes and co), this section of the code (along others) will probably be rewritten. Yet in the meantime (it's been already 6 years!), this will help many!

Cheers!

liZe commented 5 years ago

Here's the code: https://gist.github.com/nathandem/63f410d501d200cfa5613b38aaf74c44

Hey, that's really useful :heart:, thanks a lot for sharing this!

Why including this patch in the core? Disclaimer: it doesn't try to takes into account web standards, but it will solve a major pain point for the lib users who want to create professional looking PDFs.

Integrating this in the core may look like a good solution, but I think it's not. WeasyPrint has a long history with web standards, and the golden rule I now know from this history is that we really often have to blindly follow specifications. There's an endless work waiting for us to implement the specs, and each time we've tried to create our own specs it ended in a bad way. Moreover, maintaining this code (ie. fixing bugs, but also adding new features, and keeping it almost forever as it would be in public API, even if it relies on WeasyPrint's internals) is not a work I want to do.

Next steps If the idea of this patch gets accepted, I'd to receive some guidance from the maintainer to know 1) if there're some elements I could improve to reach the code standard of the lib, 2) where I should put the code. I guess some of it should be part of the public API (the main weasyprint/__init__.py), some may be located elsewhere (maybe weasyprint/layout/pages.py?). I will then write a PR with tests and documentation, so that everyone can solve this problem quickly from now on!

I really appreciate your work, the code is clean and could be really useful to a lot of people. Maybe adding it in the documentation (and probably the whole issue) is the solution for now. It also would be useful to have a place where code snippets (both CSS and Python) could be shared, but I don't know how/where to do this.

So, are you interested in me adding support for this feature in Weasyprint? :)

PS : If someone launches himself/herself in a major rewrite to support the newer standards someday (proper support of flexboxes and co), this section of the code (along others) will probably be rewritten. Yet in the meantime (it's been already 6 years!), this will help many!

Actually, supporting running elements doesn't need a major rewrite, and I think that it would be a great way to support this feature in WeasyPrint :smile:. I can help, of course (but it looks like you already know WeasyPrint internals quite well!)

About a proper support of flexbox, the work mainly has to be done in flex.py and it wouldn't take a lot of work to improve the current situation (which is not that bad, is it?)

nathandem commented 5 years ago

Thanks for your feedback @liZe :)

I understand your point of not wanting to include this hack in the codebase. It'd break the project rules and would have to be maintained.

I didn't actually thought about it first, but including a reference to this snippet in the documentation would in practice (almost) achieve the same result! I say almost because it wouldn't be as straightforward integrate for beginners as importing the lib and calling a method directly. But say 80% of the users should be able to go along without having to digging outside the doc, so that'd already a big improvement!

I'd suggest to add a new section in the documentation entitled "Tips & tricks" (or something along this line). This code would be the first snippet in this new section. I'd also complement it with a little explanation, an example or two and add a short reference from the tutorial part to it. The other snippets you mentioned would then go in the new section too.

Concerning the running elements, I don't want to advance myself on this - because of time, competence and motivation. But if this proposal of this new section of the doc and this code + explanation is deemed interesting, I can do that for this weekend ;)

tookas92 commented 5 years ago

Here's the code: https://gist.github.com/nathandem/63f410d501d200cfa5613b38aaf74c44

Hey I have problem with this solution. In my header I want to add image and everytime I render pdf instead of header image I have only alt attribute text. Why it might happen?

nathandem commented 5 years ago

@tookas92 The problem may come from the fact that you use relative urls in your header and you forgot to provide a base_url. (Note: in case you are using Django, you can get the base url easily from your views with request.build_absolute_uri().)

If it doesn't help, please create gists with a copy of the relevant files + your settup and share the links here so that I can have a look ;)

liZe commented 5 years ago

Now that the documentation has been updated (thanks @nathandem!), I think that we can close this issue. Don't hesitate to add comments if you find new tricks.

mchccc commented 1 year ago

Just something for the next person to find this thread: The "content" property you use in the margin boxes (@top-center) can point to an svg file with arbitrarily complex layout.

@zilles those tip and snippet seem very interesting, but I wasn't able to get them to work.. do you think they're still valid after these years?

Ravin001 commented 1 year ago

I was able to repeat the header and footer by using suggestions here, which I found is less complex for my case- https://github.com/Kozea/WeasyPrint/issues/693 e.g. for the header:

@page {
    size: letter;
    margin: 4cm 8mm 2cm;
}

.pdf-header {
    /* position fixed to repeat the header on all pages */
    position: fixed;
    top: -3.7cm;
    width: 100%;
}