danfickle / openhtmltopdf

An HTML to PDF library for the JVM. Based on Flying Saucer and Apache PDF-BOX 2. With SVG image support. Now also with accessible PDF support (WCAG, Section 508, PDF/UA)!
https://danfickle.github.io/pdf-templates/index.html
Other
1.89k stars 355 forks source link

Orphan html elements #481

Open keycad opened 4 years ago

keycad commented 4 years ago

Hi, I have a dynamic document which has some subheadings and variable length content below. In some cases, I get a heading placed at the end of one page, while the content starts in the next one. I tried to use orphans property, but during my tests I found it only works within the same tag.

So, how can I achieve this?

This is a small example to reproduce it, based on your annotated example. The desired effect would move "Title" to a new page, if there's no room for "Line 1" as well in the same page.

    .no-orphans {
      widows: 0;
      orphans: 2;
    }

  <div class="no-orphans">
    <h2>Title</h2>
    <p>Line 1</p>
    <p>Line 2</p>
    <p>Line 3</p>
    <p>Line 4</p>
    Line 5<br/>
    Line 6<br/>
    Line 7<br/>
    Line 8<br/>
    Line 9<br/>
    Line 10<br/>
    Line 11<br/>
  </div>

NOTE: This is a small repro based on the example, but the real scenario replaces all the <p>'s content with <div>'s that contain images, text... And the amount of these is dynamically generated, so it's not easy to enclose the first one with the title in the same container to include a no-break-inside property, without including some non-desired and custom logic in the template.

rototor commented 4 years ago

The sad reality is that often the orphans property does not work. Especially if your HTML has some complex structure. Doing this right in all cases is hard.

For that reason I built the -fs-page-break-min-height special css property as workaround. See also #31

In your case you would put something like "-fs-page-break-min-height: 2cm" on outer most container of the title element. It may also work if you put it directly on the title, but depending on the structure of your HTML some parts of the outer container may then be painted on the previous page.

keycad commented 4 years ago

Thanks for the quick response! Adding 2.2cm solves it, but also breaks other div's that could fit (from different pages but same outer container) into the next page, leaving too much whitespace at the end. Is there any other option?

rototor commented 4 years ago

Not that I know of. As I generate the HTML for the reports using FreeMarker I calculated the needed space dynamically on the expected content and placed the -fs-page-break-min-height in an inline style. FreeMarker is more and more becoming a full blown programming language, it nowadays even has lambdas.

If you know what content type gets into a container you could also add a css class for each type on the container div and define in your style sheet the minimum needed height per type.

keycad commented 4 years ago

I see. But in that case, there's no much difference with doing calculations to know if I need to manually include a page break before the orphan title element. I'll leave the issue open if that's ok, in case it can be eventually fixed. Thanks for the help anyway @rototor

rototor commented 4 years ago

@keycad The difference is that you only need to know how much space the element needs. You don't need to track/calculate how much space was already used on the page. When you manually insert a page break you must calculate that for yourself, and as soon as some CSS styles are changed your manual calculation will be wrong.