Jak uniknąć podziału strony w wierszu tabeli w przypadku wkhtmltopdf


81

Generuję raport pdf ze strony html z jedną tabelą .

W tym celu używam wkhtmltopdf .

kiedy generowany jest plik PDF, zrywa się gdziekolwiek w znaczniku tr .

Chcę tego uniknąć.

Odpowiedzi:


152

Aktualizacja 17.09.2015: Sprawdź używaną wersję: mówi się, że wkhtmltopdf 0.12.2.4 rozwiązuje problem (nie sprawdzałem) .


Jest to znany problem w programie wkhtmltopdf. Algorytm łamania stron używany przez webkit (WK w WKhtmltopdf) nie działa dobrze w przypadku dużych tabel. Proponuję rozbić tabelę na mniejsze fragmenty, które można łatwiej podzielić na strony i często używać CSS:

table, tr, td, th, tbody, thead, tfoot {
    page-break-inside: avoid !important;
}

Przyjrzyj się również następującym zagadnieniom wkhtmltopdf, w których znajdują się interesujące komentarze, które omawiają na przykład problem dzielenia tabel. Istnieje rozwiązanie JS, które programowo dzieli tabele w 168, które mogą ci pomóc (chociaż go nie używam).

Aktualizacja 08.11.2013 Jest wiele dyskusji na ten temat w numerze 168, do którego link znajduje się powyżej. Ktoś zdołał skompilować wersję wkhtmltopdf, która obsługuje lepsze łamanie tabel, ale niestety wygląda na to, że nie została oficjalnie wydana i może zawierać inne błędy. Nie wiem, jak to zdobyć i nie wiem, jak skompilować w systemie Windows, ale każdy zainteresowany może sprawdzić na przykład komentarz tutaj (zobacz nową aktualizację poniżej).

Aktualizacja 24.02.2014 Z przyjemnością usłyszysz, że w wkhtmltopdf 0.12 między innymi ta funkcja została znacznie ulepszona. Jednak poczekaj na wersję 0.12.1 i dokładnie przetestuj, zanim zaczniesz używać jakiejkolwiek nowej wersji, nadal jest trochę niestabilna, chociaż nowi faceci pracujący z antialize wykonują świetną robotę (ashkulz rock)! Bądź na bieżąco na wkhtmltopdf.org i github . Witryna Google Code jest przestarzała i powoli podlega migracji.


1
Dzięki za informację. Wersja 0.12.1 rozwiązuje problem z podziałem strony.
Nidhi Sarvaiya

1
Uwaga, to rozwiązanie działa tylko z najnowszą wersją 0.12.1. Wszystko wcześniej nadal nie działa.
Cerin

4
Walczyłem z tym przez kilka dni. Okazało się, że mój stół był w stylu div o stylu display: inline-block. Zmieniłem to na blocki ze zmianami nad tym wszystkim zaczęło działać!
Hugh,

2
@Nenotlep dzięki za odpowiedź. tak, już opublikowałem nowe pytanie na ten temat: stackoverflow.com/q/36334330/3391783 - to zabawne, jak to wszystko wydawało się działać w wersjach 0.12.1-ish lub 0.12.2-ish i znowu jest zepsute w wersji 0.12. Wersje 3-szowe.
low_rents

2
@DjDacSaunders WKHTMLTOPDF to hack, a nie czyste narzędzie HTML -> PDF. Celem tego jest renderowanie bardzo długiego dokumentu do formatu stronicowanego. Fakt, że mamy nad tym jakąkolwiek kontrolę, jest wspaniały. Jeśli chcesz, aby to usprawniło, absolutnie najlepszym miejscem do kontaktu jest poprzedzający go wkhtml, który jest albo projektem QT, albo być może projektem WebKit. Przewiduję, że ta rzecz nigdy się nie zmieni, ponieważ nie jest to tak naprawdę to, co WebKit miał robić podczas renderowania stron internetowych jako plików PDF: / Aby uzyskać pełną kontrolę, spróbuj PrinceXML. (x) HTML nie jest formatem do druku, a „rozwiązaniem” tego problemu są zawsze hacki.
Joel Peltonen

18

To stary post, ale ponieważ traciłem dużo czasu na szukanie odpowiedniego rozwiązania, wstawię go tutaj, może się komuś przyda.

A więc z tego, co przeczytałem, problem z

page-break-inside: avoid

jest to, że to nie działa. Ale tak naprawdę, jeśli ustawisz to na elemencie, który ma display:blockto działa zgodnie z oczekiwaniami (jak zauważono gdzieś w SO). więc dla prostej struktury tabeli css z

td div, th div{
    page-break-inside: avoid;
}

i strukturę tabeli

<table>
....
<tr>
    <td><div>some text</div></td>
    <td><div>more text</div></td>
</tr>
....
</table>

będzie działać zgodnie z oczekiwaniami.

Miałem trochę bardziej skomplikowany przypadek z rozpiętościami rzędów, więc rozwiązanie z góry rozbijało go do pokoju, co nie było pożądanym efektem. Rozwiązałem to za pomocą elementów div dla każdego zbioru linii rozpiętego na wiersz. Mój jquery js wykonuje całą robotę:

$(window).load(function () {
    var sizes = {};
    $('#the_table tr:first th').each(function (a, td) {         
        var w = $(td).width();
        if (sizes.hasOwnProperty('' + a)) {
            if (sizes['' + a] < w)
                sizes['' + a] = w;
        }
        else {
            sizes['' + a] = w;
        }
    });

    var tableClone = $('#the_table').clone();
    $('#the_table').replaceWith('<div class="container"></div>');

    var curentDivTable;
    var cDiv = $('.container');
    tableClone.find('tr').each(function (i, ln) {
        var line = $(ln);
        if (line.hasClass('main_row')) {
            var div = $('<div class="new-section"><table><tbody>')
            currentDivTable = div.find('tbody');
            cDiv.append(div);               
        }
        currentDivTable.append(line);
    });
    //optional - maybe in % its better than px
    var sum = 0;
    $.each(sizes, function (a, b) {
        sum += b;
    });
    var widths = {};
    $.each(sizes, function (a, b) {
        var p = Math.ceil(b * 100 / sum);
        widths['' + a] = p + '%';
    });
    //setup
    $('.container table').each(function (a, tbl) {
        $(tbl).find('tr:first td, tr:first th').each(function (b, td) {
            $(td).width(widths['' + b]);
        });
        $(tbl).addClass('fixed');
    });
});

css:

div.new-section {
    page-break-inside: avoid;
}
.container, .new-section, .new-section table.fixed{
    width: 100%;
}

.new-section table.fixed{
    table-layout:fixed;
}

Nie wiem, czy wszystko jest potrzebne i nie sądzę, że jest to idealne rozwiązanie, ale spełnia swoje zadanie. Testowany tylko na chromie


16

Od wersji 0.12 ten problem został rozwiązany, ale czasami, gdy tabela jest zbyt długa, aby zmieścić się na stronie, wkhtmltopdf dzieli ją na dwie części i powtarza nagłówki kolumn na nowej stronie, a te nagłówki kolumn są nakładane na pierwszy wiersz.

Znalazłem tymczasowe rozwiązanie tego problemu w sekcji problemów wkhtmltopdf github: https://github.com/wkhtmltopdf/wkhtmltopdf/issues/2531

Po prostu dodaj te linie do swojego widoku css:

tr {
  page-break-inside: avoid; 
}

To faktycznie pomaga. Dziękuję Ci!! Nie wiem, dlaczego nie jest to zachowanie domyślne.
JosephK

6

Zagłębiałem się w ten problem przez wiele dni i wreszcie znalazłem idealne rozwiązanie. Możesz odwołać się do tego projektu phpwkhtmltopdf . Zajrzyj do katalogu, articlea znajdziesz 3 rozwiązania dla 3 problemów. Krótko mówiąc, ostatecznym rozwiązaniem jest dodanie stylu css

thead {
    display: table-row-group;
}
tr {
    page-break-before: always;
    page-break-after: always;
    page-break-inside: avoid;
}
table {
    word-wrap: break-word;
}
table td {
    word-break: break-all;
}

Jeśli jesteś Chińczykiem, możesz śmiało sprawdzić tę stronę关于 wkhtmltopdf , 你 一定 想 知道 这些 Przeczytaj streszczenie , jeśli chcesz skorzystać z wkhtmltopdf


To zadziałało dla mnie. Używam wkhtmltopdf 0.12.4 . Dzięki!
Hugo,


5

W moim przypadku z jakiegoś powodu żadna z poprzednich odpowiedzi nie zadziałała. To, co ostatecznie zadziałało, było w rzeczywistości połączeniem kilku rzeczy.

  1. Zainstalowałem (w Ubuntu 16.04) opakowanie Pythona Wkhtmltopdf o nazwie pdfkit przy użyciu pip3, a następnie zamiast instalować Wkhtmltopdf przez apt-get zainstalowałem statyczny plik binarny (wersja 0.12.3), wykonując poniższy skrypt, pobrany stąd

    #!/bin/sh
    
    sudo apt-get install -y openssl build-essential xorg libssl-dev
    wget http://download.gna.org/wkhtmltopdf/0.12/0.12.3/wkhtmltox-0.12.3_linux-generic-amd64.tar.xz
    tar -xJf wkhtmltox-0.12.3_linux-generic-amd64.tar.xz
    cd wkhtmltox
    sudo chown root:root bin/wkhtmltopdf
    sudo cp -r * /usr/
    
  2. Dodano ten CSS (zgodnie z sugestią w jednej z odpowiedzi tutaj):

    tr, td div, th div{
        page-break-inside: avoid;
    }
    
  3. A następnie dodaj także<thead> i <tbody>tagi, jak sugerowano tutaj (bez tego tabela nadal by się zepsuła):

    <table>
        <thead>
            <tr>
                <th>Column 1</th>
                <th>Column 2</th>
            </tr>
        </thead>
    
        <tbody>
            <tr>
                <td>Value 1</td>
                <td>Value 2</td>
            </tr>
        </tbody>
    </table>
    

Dzięki tym modyfikacjom mogę teraz z powodzeniem używać szablonów Mako do generowania kodu HTML, a następnie przesyłać go do Wkhtmltopdf i uzyskać pięknie podzielony na strony plik PDF.


4

Próbowałem wszelkiego rodzaju manipulacji na moich stołach, ale nic, co próbowałem, nie mogło powstrzymać podziałów strony na środek rzędu. W desperacji wypróbowałem różne wersje i znalazłem następujące:

Wkhtmltopdf 0.12.2.1: Zły

Wkhtmltopdf 0.12.3: Zły

Wkhtmltopdf 0.12.1: Dobrze

Moim rozwiązaniem było przejście na wersję 0.12.1, co rozwiązało moje problemy. To prawda, mogło to być częściowo spowodowane tym, że nie byłem super OCD w moim html, ale ponieważ HTML jest generowany w TinyMCE (przez użytkowników), tak naprawdę nie mam wielkiego wyboru.

Ponadto zagnieżdżone tabele nie działają w żadnej wersji dla mnie.


dla mnie 0.12.1 nie rozwiązuje problemu i zajmuje to daleko
UnixZnowu

2

Jak używać podziałów stron w pliku PDF bez przerywania tr?

Oto rozwiązanie, którego możesz użyć w dowolnym pliku html .....

Po uruchomieniu tr musisz wziąć div wewnątrz tr i przekazać ten css do div:

<tr>
      <div style="page-break-inside:avoid !important; page-break-after:auto !important; overflow: hidden; display:block !important; width:100% ">
     </tr>

2

Miałem do czynienia z tym samym problemem, dodając po wielu próbnych n błędach, ten CSS rozwiązał problem

tr {
    display: inline-table;
}


1

Powyższe odpowiedzi mi nie pomogły. Musiałem specjalnie wyłączyć opcję powiększenia w mojej konfiguracji pdfkit.

PDFKit.configure do |config|

  config.default_options = {
    print_media_type: false,
    page_size: "A4",
    encoding: "UTF-8",
    ## Make sure the zoom option is not enabled!
    ## zoom: '1.3',
    disable_smart_shrinking: false,
    footer_right: "Page [page] of [toPage]"
  }

end

1

Dla każdego, kto nadal ma z tym problemy, jedną rzeczą do zapamiętania jest to, że tabela musi być bezpośrednim dzieckiem ciała , w przeciwnym razie css nie zadziała (przynajmniej tak się stało ze mną).


tak nie było w moim przypadku - mogę potwierdzić, że nawet zagnieżdżone tabele przestrzegały podziałów stron ... problemem dla mnie był bardziej Mac OS vs Ubuntu ...
Petrov

Miałem podobny problem: moja tabela znajdowała się w obrębie elementu div z display: table-cell;zastosowaniem. Stworzenie tych stylów @media only screennaprawiło podziały stron. Jeśli nie możesz uruchomić podziałów stron, spróbuj podzielić i podbić, usuwając połowę CSS etapami i sprawdzając, czy to działa.
Leslie Viljoen

1

Znalazłem to absurdalne rozwiązanie, ale dla mnie zadziałało bardzo dobrze :)

Po prostu wstawiłem bardzo długą kolumnę rozpiętości wierszy, taką jak ta

<td rowspan="XXX TOTAL ROWS" style="width:0px"></td>

a wtedy stół nie pękł.


1

Inna opcja: umieść każdy trosobno, tbodya następnie zastosuj reguły css peage break do pliku tbody. Tabele obsługują wiele plikówtbody s.

Trochę dodatkowych znaczników, ale dla mnie działa przyzwoicie.


Wypróbowałem to na grupie elementów tr - owijając je w oddzielne elementy tbody - aby spróbować zachować razem pewne grupy wierszy. Nie przyniosło to żadnego skutku. Wykonanie tej metody bez „page-break-inside: unikaj”; na elemencie "tr" ponownie spowodowało powrót do drukowania danych na nagłówkach stron (zachowanie "domyślne").
JosephK

Tak, teraz stosuję tę samą regułę „page-break-inside: unikaj” zarówno dla tbody, jak i tr i td: „tbody, tbody> tr, tbody> tr> td, tbody> tr> th {page-break-inside: unikaj;} ”, co wydaje się działać w większości sytuacji.
Troy Morehouse

Dzięki, ale właśnie spróbowałem. Wciąż dzieli strony w środku moich grup wierszy tabeli. Próbowałem też dodać klasę do ciała i css na klasie z opcją „unikaj” - bez efektu. Chciałbym wiedzieć, co to właściwie „robi” z regułą css - może jakiś sposób, aby sprawić, że grupa trs to naprawdę „jeden wiersz” - ale skoro utworzenie tr o wysokości 2x + również ją łamie, zgaduję, że nie. Może ktoś stworzy użyteczne rozwiązanie HTML do PDF za kolejne 10 lat, ale myślę, że zamiast tego czeka na bezpośredni neuronowy transfer danych.
JosephK,

1

Rozwiązałem problem, stosując kombinację kilku sugerowanych rozwiązań.

Owinąłem tabelę w div i zdefiniowałem następujący CSS.

.wrapping-div {
        display: block;
        page-break-inside: avoid !important;
    }

.wrapping-div table, .wrapping-div tbody, .wrapping-div tr, .wrapping-div td, .wrapping-div th {
        page-break-inside: avoid !important;
    }

Struktura tabeli po zakończeniu została zdefiniowana jako następujący przykład:

<div class="wrapping-div">
 <table>
  <tbody>
   <tr>
    <th>
      header
    </th>
    <td>
      content
    </td>
   </tr>
  </tbody>
 </table>
</div>

Nie musiałem tworzyć żadnego elementu div wewnątrz znaczników td lub th.

Ważne rzeczy, które zauważyłem podczas próby rozwiązania problemu:

  • Ciało musi być zawarte w tabeli
  • Element div musi mieć display: block
  • Gdy tabela nie mieści się na stronie, automatycznie przeniesie całą tabelę na następną stronę (nie próbowałem tego z dużymi stołami)
  • Jeśli usuniesz tylko selektor „.wrapping-div table” z CSS, pozwoli to na podzielenie tabeli na dwie strony, ale wyrenderuje ją poprawnie, nie dzieląc jednej komórki na dwie strony (jest to domyślne zachowanie w programie Word )

Mam nadzieję, że to pomoże.



1

Aby uniknąć podziału strony, możemy użyć opcji css unikania podziału strony.

tr { page-break-inside: avoid; }

Przerwij dowolną treść (obraz / tekst) i umieść ją na następnej stronie

.sample-image { page-break-before: always; }

0

Czy masz głowę stołu? i korpus stołu?

<table>
<tbody>
<tr><th>Name</th><th>Value</th></tr>
<tr><td>url</td><td>stackoverflow.com</td></tr>
<tr><td>ip</td><td>123.123.123.123</td></tr>
</tbody>
</table>

To jest właściwe formatowanie tabeli, podczas gdy większości przeglądarek nie obchodziło to mniej, konwertery takie jak ten, o którym wspomniałeś, mogą, jeśli brakujące <tbody>lub <th>tagi sugeruję, abyś spróbował najpierw je dodać.

Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.