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

Rowspanned TD element lost on page break #1388

Open
Shinseiki86 opened this issue Feb 18, 2017 · 50 comments
Open

Rowspanned TD element lost on page break #1388

Shinseiki86 opened this issue Feb 18, 2017 · 50 comments

Comments

@Shinseiki86
Copy link

I have a table with merged cells in several rows, but when I use dompdf and performs a page break, the cells are not created correctly.

How can I correct it?
Thanks.

Table: https://jsfiddle.net/shinseiki86/24LL3fd5/

Result:
image

@bsweeney
Copy link
Member

I'm fairly certain there's an existing issue for this ... but I couldn't find it in a quick search.

@bsweeney bsweeney added the bug label Feb 19, 2017
@Shinseiki86
Copy link
Author

Shinseiki86 commented Feb 19, 2017

Right! I have encountered some similar issues, but no solution.

https://sourceforge.net/p/dompdf/bugs/39/
#812
ed6b807

@bsweeney
Copy link
Member

Maybe #232 is most appropriate, which points to #55.

@prtk418
Copy link

prtk418 commented Feb 27, 2019

is this still unsolved? i am facing same issue

@amayer-glei
Copy link

is this still unsolved? i am facing same issue

yes still broken... switched to wkhtlmpdf now.

@simonberger
Copy link
Member

simonberger commented Feb 22, 2020

Minimum example to test the problem (A4, landscape)

<html>

<head>
  <style>
    @page {
      size: a4 landscape;
    }

    table,
    td,
    th {
      border: 1px solid black;
    }
  </style>
</head>

<body>
  <table>
    <tbody>
      <tr>
        <td rowspan="3">1</td>
        <td>2<br>2<br>2<br>2<br>2<br>2<br>2<br>2<br>2<br>2<br>2<br>2<br>2<br>2<br>2<br>2<br>2<br>2<br>2<br>2<br>2<br>2<br>2<br>2<br>2<br>2<br>2<br>2<br>2<br>2<br>2<br>2<br></td>
      </tr>
      <tr>
          <td>3</td>
      </tr>
      <tr>
        <td>4<br>4<br>4<br></td>
      </tr>
    </tbody>
  </table>
</body>

</html>

Any active rowspan is not remembered on page breaks.
I currently debug this behavior but it is really deep in the renderer and/or decorator tasks.

@jonathanpmartins
Copy link

having the same problem here!

page-break-inside: avoid; should work with tbody group tag.

@Jensovic
Copy link

Jensovic commented Mar 3, 2021

Also here. I read that its a known issue that table row has to be on one page, but to not carry a rowspan to the next page is a severe bug.

I did a debug query and it seems that the table structure (tablemap) is intact, its only that the rowspan is not remembered and cells are dropped in the wrong column.

I think it would be possible to remember the rowspan with an temporary array and check this after the pagebreak to "skip" rowspanned cells on the next page.
Another solution would be to check if all rows of a rowspanned cell would fit onto the page, and if not, to pagebreak before that row with >1 rowspanned cell.

Who can help on that?
This bug is a showstopper :/

@Nuranto
Copy link

Nuranto commented Aug 28, 2021

I confirm this issue is still present in last 1.0.2 version

@bsweeney bsweeney modified the milestones: dompdf-next, 1.1.1 Sep 2, 2021
@bsweeney bsweeney changed the title pagebreak with table rowspan not created correctly Rowspanned TD element lost on page break Dec 6, 2021
@kanapes
Copy link

kanapes commented Dec 7, 2021

Thanks Brian - yes this issue looks like my problem #2676.

Have not seen this issue by using the searchengine, sorry for that.
I believe issue #1388 describes my problem very well but if you still need more test cases i can modifie my files from issue #2676.

Just let me know

By the way, the start post of this issue makes me a little bit worried if i have a look on the date.
I do not want to push there is hope for a solution soon?

@bsweeney
Copy link
Member

bsweeney commented Dec 7, 2021

I can't promise a quick solution though I have been thinking about the problem a bit recently. Other more urgent issues have come up recently, however, so it'll have to wait until those are resolved for me to look at the problem.

Though, of course, anyone is welcome to try to solve the issue so maybe a kind soul will submit a PR.

@kanapes
Copy link

kanapes commented Dec 8, 2021

I just wanted to know which classes would be affected by this or where is the doing for it contained?
Can you narrow down the range a bit.

I also have a lot to do at the moment, but maybe one or the other time will come up.

@kanapes
Copy link

kanapes commented Dec 8, 2021

The evaluation for the rowspan and colspan looks correct.

Apparently there is somewhere the worm in Cellmap.php (line 575 to 589) but I'm not sure yet - guess i'm wrong.

        for ($i = 0; $i < $rowspan; $i++) {
            $row = $this->__row + $i;

            $this->_frames[$key]["rows"][] = $row;

            for ($j = 0; $j < $colspan; $j++) {
                $this->_cells[$row][$this->__col + $j] = $frame;
            }

            if ($collapse) {
                // Resolve vertical borders
                $max_left = max($max_left, $this->_resolve_border($row, $this->__col, "vertical", $bp["left"]));
                $max_right = max($max_right, $this->_resolve_border($row, $this->__col + $colspan, "vertical", $bp["right"]));
            }
        }

@bsweeney
Copy link
Member

bsweeney commented Dec 8, 2021

I believe the issue is that when a row is paged the map of cells associated with the row is reset, but the row-spanned cell is not added back when building the cell map on the following page because the logic only looks at direct children of the elements being rendered.

@kanapes
Copy link

kanapes commented Dec 10, 2021

how do you handle the output during creation. debugging is somewhat difficult that way.

@VMLearningHub
Copy link

VMLearningHub commented May 12, 2022

@foreach ($data['project_feature']['user_roles_data'] as $user_role_key => $user_roles)
                        @php
                            $user_role_value = $user_roles['name'];
                        @endphp
                            @foreach ($user_roles['main_features'] as $main_features_key => $main_features)
                            @php
                                $main_features_value = $main_features['name']
                            @endphp
                                
                                 @foreach ($main_features['sub_features'] as $sub_features_key => $sub_features)
                                 <tr>
                                    @if($user_role_value != "")
                                        <td style="border-top: 1px dotted #000; border-right: 1px dotted #000; padding: 5px; font-weight: 400; color: #333333;vertical-align: baseline;text-align: center;font-size: 14px;">{{$user_role_value}}</td>
                                    @else
                                        <td style="border-right: 1px dotted #000; padding: 5px; font-weight: 400; color: #333333;vertical-align: baseline;text-align: center;font-size: 14px;">{{$user_role_value}}</td>
                                    @endif
                                    @if($main_features_value != "")
                                        <td style="border-top: 1px dotted #000; border-right: 1px dotted #000; padding: 5px; font-weight: 400; color: #333333;vertical-align: baseline;text-align: center;font-size: 14px;">{{$main_features_value}}</td>
                                    @else
                                        <td style="border-right: 1px dotted #000; padding: 5px; font-weight: 400; color: #333333;vertical-align: baseline;text-align: center;font-size: 14px;">{{$main_features_value}}</td>
                                    @endif
                                    <td style='border: 1px dotted rgb(24, 24, 24);padding: 5px;font-size: 14px;color: #333333;'>{{ $sub_features['name'] }}</td>
                                     @php
                                         $user_role_value ="";
                                         $main_features_value="";
                                     @endphp
                                 </tr>
                                 
                                 @endforeach
                            @endforeach
                        @endforeach

image

@233cy
Copy link

233cy commented May 16, 2022

@foreach ($data['project_feature']['user_roles_data'] as $user_role_key => $user_roles)
                        @php
                            $user_role_value = $user_roles['name'];
                        @endphp
                            @foreach ($user_roles['main_features'] as $main_features_key => $main_features)
                            @php
                                $main_features_value = $main_features['name']
                            @endphp
                                
                                 @foreach ($main_features['sub_features'] as $sub_features_key => $sub_features)
                                 <tr>
                                    @if($user_role_value != "")
                                        <td style="border-top: 1px dotted #000; border-right: 1px dotted #000; padding: 5px; font-weight: 400; color: #333333;vertical-align: baseline;text-align: center;font-size: 14px;">{{$user_role_value}}</td>
                                    @else
                                        <td style="border-right: 1px dotted #000; padding: 5px; font-weight: 400; color: #333333;vertical-align: baseline;text-align: center;font-size: 14px;">{{$user_role_value}}</td>
                                    @endif
                                    @if($main_features_value != "")
                                        <td style="border-top: 1px dotted #000; border-right: 1px dotted #000; padding: 5px; font-weight: 400; color: #333333;vertical-align: baseline;text-align: center;font-size: 14px;">{{$main_features_value}}</td>
                                    @else
                                        <td style="border-right: 1px dotted #000; padding: 5px; font-weight: 400; color: #333333;vertical-align: baseline;text-align: center;font-size: 14px;">{{$main_features_value}}</td>
                                    @endif
                                    <td style='border: 1px dotted rgb(24, 24, 24);padding: 5px;font-size: 14px;color: #333333;'>{{ $sub_features['name'] }}</td>
                                     @php
                                         $user_role_value ="";
                                         $main_features_value="";
                                     @endphp
                                 </tr>
                                 
                                 @endforeach
                            @endforeach
                        @endforeach

图像

How did you solve it

@bsweeney bsweeney added this to the 2.0.3 milestone Dec 29, 2022
@catalinmv
Copy link

catalinmv commented Feb 8, 2023

I suppose this doesn't have any chance of getting fixed any time soon, right?
Will finish my current project soon, an important part of it are the generated tables, some of which have cells that span multiple rows. If this won't be fixed i will be forced to change to tcpdf, which is quite a bit of work from what i've seen in some initial tests. Any way that we can speed up the fixing of this issue?

@bsweeney bsweeney modified the milestones: 2.0.5, 2.0.4 Feb 8, 2023
@bsweeney
Copy link
Member

bsweeney commented Feb 8, 2023

I don't really have a sense of what this will take to fix but I'll take a look.

Also, what is your definition of "soon" because releases are sparse.

@Jensovic
Copy link

Jensovic commented Feb 8, 2023 via email

@bsweeney
Copy link
Member

bsweeney commented Feb 9, 2023

Looks like you're referring to #2090? While that does improve the rendering I don't know that it's a good enough solution to the problem. Though it has been a while since I've reviewed the issue so it's possible that might be a good basis for a solution.

@catalinmv
Copy link

catalinmv commented Feb 9, 2023

Sorry about the "soon", in my case it's the end of this month. Sorry for insisting with this, everything is perfect for our project with the library, except this bug. Sadly, there are tables generated that contain cells that span multiple rows (sometimes a cell could even go on 3 pages, starts on page 1, occupies page 2 fully and ends on page 3). So the solution to not allow page-break is not good for us.
Thank you for your work on this project!

@Jensovic
Copy link

Jensovic commented Feb 9, 2023 via email

@bsweeney
Copy link
Member

bsweeney commented Feb 9, 2023

Sadly, there are tables generated that contain cells that span multiple rows (sometimes a cell could even go on 3 pages, starts on page 1, occupies page 2 fully and ends on page 3)

Would this cell have content as well? The case cited by the OP here is that a rowspanned cell that fully renders on the first page does not render at all on the next. In this scenario, the cell does not flow to the next page because it has been fully rendered. This issue is still challenging but the solution is more straightforward, and that's to ensure the cell flows alongside the rows it spans.

If the rowspanned cell has content that has to span multiple pages, that's a bit of a different issue. That's a more fundamental problem with Dompdf because when an element has to be split for rendering to a new page Dompdf does not return to the previous page to pick up where it left off. Though I have been pondering this issue, it's a more challenging problem to solve.

@catalinmv
Copy link

catalinmv commented Feb 9, 2023

Yes, it has content, but most of the time it's not that much. But yes, there are cases where the content will have to be drawn on both pages, some of it on one page and the rest on the other. I would say that 95% of the situations there is little content, that can be drawn on either page. Another issue could be vertical centering in this case and i have no idea how it would work.

@zainalawaludin
Copy link

has this problem been solved? i have the same problem

@233cy
Copy link

233cy commented Mar 15, 2023

建议直接使用lampnick/doctron(docker服务 页面转图片或者pdf)搭建服务,然后在服务端通过mvc渲染出一个pdf,通过docker服务来生成图片pdf完成度可以达到98%。不过具体看每个人的具体需求

@zainalawaludin
Copy link

建议直接使用lampnick/doctron(docker服务 页面转图片或者pdf)搭建服务,然后在服务端通过mvc渲染出一个pdf,通过docker服务来生成图片pdf完成度可以达到98%。不过具体看每个人的具体需求

what does this mean ?

@233cy
Copy link

233cy commented Mar 15, 2023

建议直接使用lampnick/doctron(docker服务 页面转图片或者pdf)搭建服务,然后在服务端通过mvc渲染出一个pdf,通过docker服务来生成图片pdf完成度可以达到98%。不过具体看每个人的具体需求

what does this mean ?

hub.docker.com Learn about the container lampnick/doctron

@bsweeney
Copy link
Member

I started looking at this issue though I don't have a timeline for resolution yet.

@jonathanpmartins
Copy link

This is a 5 year problem. Would be very nice if it works like it logically supposed to. I'm using it since 2018 and it's very annoying to explain to my clients that this is a dependency problem, cannot do anything without more "time/money" input. So, it is, what it is... We are open source and the licence don't have any guarantees. If nobody is wanting to solve the problem, not even the clients when I talk about "money/time" input, I can just think that this is good enough as it is...
If you need help because time is short don't hesitate to call for it, I'm happy to help, so I think are others.
Every improvement will be so much appreciated!

@bsweeney
Copy link
Member

As I said I'm working on this issue now, so I wouldn't recommend anybody put any time into it just yet. I have a work-in-progress branch but right now it only addresses paging of tables and floats, not the rowspan issue. After reviewing current functionality I need to do a bit of refactoring. Once that's done I think I'll be able to address paging rowspans. At that point I'll open a PR and anyone who wants to help by testing the update and submitting feedback is more than welcome.

@jonathanpmartins
Copy link

I very much appreciate your work and I think other too. Thank you for your time spend in this project!
I would be happy to help testing the PR and give some feedback. This is great!

@233cy
Copy link

233cy commented Mar 23, 2023

Although this is not a good idea, I have found a relatively suitable method to generate pdf. 1. Use knplabs/knp-snap to generate pdf. 2. Use mikehaertl/php-pdftk to manage permissions on pdf. This will perfectly solve htmltopdf

But here I also hope that TCpdf can do better.

@iceaceice
Copy link

iceaceice commented May 26, 2023

I have a temporary solution for this but I didn't use the row span instead I counted the row that my page will print here's my solution:

    <tbody>
        @if (!empty($problemList))
            @php
                // Max row to print in next page of PDF
                $next_page_max_row = 48;

                // Default max row
                $max_row = 34;

                $row_counter = 0;

                // Store the previous loop count
                $prev_loop_count = 0;

                // check if the page break;
                $next_page = false;
            @endphp
            @foreach ($problemList as $list)

                @php
                    // Convert to array
                    $problem = str_split($list->problem, 19);
                    $management = str_split($list->management, 20);

                    // Nurse in charge by column length counter
                    $prefix_len = (!empty($list->physician->consultant->gender) ? strlen($list->physician->consultant->gender == 'Male' ? 'Dr. ' : 'Dra. ') : 0);
                    $lname_len = (!empty($list->physician->consultant->last_name) ? strlen($list->physician->consultant->last_name) : 0);
                    $fname_len = (!empty($list->physician->consultant->first_name) ? strlen($list->physician->consultant->first_name): 0);
                    $sname_len = (!empty($list->physician->consultant->suffix_name) ? strlen($list->physician->consultant->suffix_name) : 0);
                    $mname_len = (!empty($list->physician->consultant->middle_name) ? strlen($list->physician->consultant->middle_name ): 0);

                    // Concating the the full name
                    $nurse_name_length = (!empty($list->physician->consultant) ? ($list->physician->consultant->gender == 'Male' ? 'Dr. ' : 'Dra. ') : "") .
                                         (!empty($list->physician->consultant->last_name) ? ($list->physician->consultant->last_name . (($lname_len + $fname_len + $prefix_len + 2) <= 31 ? ", " : ", \n")) : "") .
                                         (!empty($list->physician->consultant->first_name) ? ($list->physician->consultant->first_name . (!empty($list->physician->consultant->suffix_name) ? (" " . $list->physician->consultant->suffix_name . ($lname_len + $fname_len + $sname_len + $prefix_len + 4 <= 31 ? " " : "\n")) : ($lname_len + $fname_len + $prefix_len + 4 <= 31 ? " " : "\n"))) : " ") .
                                         (!empty($list->physician->consultant->middle_name) ? $list->physician->consultant->middle_name : "");

                    $nurse_full_name = explode("\n", $nurse_name_length);

                    $nurse_count = count($nurse_full_name);
                    $management_count = count($management);
                    $problem_count = count($problem);

                    $countArray = array(
                        "problem_name" => $problem_count,
                        "management_name" => $management_count,
                    );

                    // Determine the higher array count
                    $maxCount = max($countArray);
                    $temp_array_count = $maxCount;
                    $count_checker = $row_counter + $maxCount;

                @endphp
                @if ($count_checker <= $max_row)
                    <?php $row_counter += $temp_array_count ?>
                    @for ($i = 0; $i < $temp_array_count; $i++)
                        <tr>
                            <td class="align-center border-left {{ $i == 0 ? 'border-top' : '' }}" style="vertical-align: top; padding-left: 5px; white-space: nowrap;">{{ $i == 0 ? !empty($list->date_time) ? date('Y-m-d @ H:i', strtotime($list->date_time)) : "" : "" }}</td>
                            <td class="align-center border-left {{ $i == 0 ? 'border-top' : '' }}" style="vertical-align: top; padding-left: 5px; white-space: nowrap;">{{ $i == 0 ? !empty($list->treatment->name) ? $list->treatment->name : "" : "" }}</td>
                            <td class="align-top-left border-left {{ $i == 0 ? 'border-top' : '' }}" style="padding-left: 5px;">{{ isset($problem[$i]) ? $problem[$i] : "" }}</td>
                            <td class="align-top-left border-left {{ $i == 0 ? 'border-top' : '' }}" style="padding-left: 5px;">{{ isset($management[$i]) ? $management[$i] : "" }}</td>
                            <td class="align-center border-left {{ $i == 0 ? 'border-top' : '' }}" style="vertical-align: top; padding-left: 5px;">{{ isset($nurse_full_name[$i]) ? $nurse_full_name[$i] : "" }}</td>
                        </tr>
                    @endfor

                    {{-- Reset the row counter then add the next default max row  --}}
                    @if ($count_checker == $max_row)
                        <?php
                            $max_row = $next_page_max_row;
                            $row_counter = 0;
                        ?>
                    @endif
                @else
                    @php
                        $temp_counter = 0;
                        $max_loop = ($max_row - $row_counter);
                        $next_page = true;
                        $max_row = $next_page_max_row;
                    @endphp

                    @for ($i = 0; $i < $temp_array_count; $i++)
                        <tr>
                            <td class="align-center border-left {{ $i == 0 ? 'border-top' : '' }}" style="vertical-align: top; padding-left: 5px; white-space: nowrap;">{{ $i == 0 ? !empty($list->date_time) ? date('Y-m-d @ H:i', strtotime($list->date_time)) : "" : "" }}</td>
                            <td class="align-center border-left {{ $i == 0 ? 'border-top' : '' }}" style="vertical-align: top; padding-left: 5px; white-space: nowrap;">{{ $i == 0 ? !empty($list->treatment->name) ? $list->treatment->name : "" : "" }}</td>
                            <td class="align-top-left border-left {{ $i == 0 ? 'border-top' : '' }}" style="padding-left: 5px;">{{ isset($problem[$i]) ? $problem[$i] : "" }}</td>
                            <td class="align-top-left border-left {{ $i == 0 ? 'border-top' : '' }}" style="padding-left: 5px;">{{ isset($management[$i]) ? $management[$i] : "" }}</td>
                            <td class="align-center border-left {{ $i == 0 ? 'border-top' : '' }}" style="padding-left: 5px;">{{ isset($nurse_full_name[$i]) ? $nurse_full_name[$i] : "" }}</td>
                        </tr>
                            
                        @if ($temp_counter == $max_loop)
                            @if ($next_page)
                                <?php $prev_loop_count = $temp_array_count - $max_loop ?>
                            @endif
                        @endif

                        <?php $temp_counter++ ?>
                    @endfor

                    @if ($prev_loop_count && $next_page)
                        <?php
                            if ($prev_loop_count > $max_row) {
                                $row_counter = ($prev_loop_count - $max_row);
                                $prev_loop_count = 0;
                            }
                            elseif ($prev_loop_count == $max_row) {
                                $row_counter = 0;
                                $prev_loop_count = 0;
                            }
                            else {
                                $row_counter = $prev_loop_count;
                                $prev_loop_count = 0;
                            }
                        ?>
                    @endif
                @endif
            @endforeach
        @endif
    </tbody>
</table>
Date & Time Treatment Problem Management Nurse-in-Charge

@bsweeney bsweeney modified the milestones: 2.0.4, 2.0.5 Jun 11, 2023
@catalinmv
Copy link

Hey, sorry to ask about this again, are there any updates on this issue? Any chances for this to be fixed in the next update?

@bsweeney
Copy link
Member

Not the next update but there is work-in-progress to address the issue in the paging-updates branch.

@tangchoozin
Copy link

Since it's still WIP, I will share how I achieve what I need. Not sure it will be helpful to other people but it's quite enough for me

Screenshot 2023-12-16 at 10 20 12 AM

To achieve this, I need to remove all the rowspan in my <td>
Then at certain <td>, i have to use border-bottom-color: transparent !important; or border-top-color: transparent !important; or both

So i defined a class

.no-top-bottom {
	border-bottom-color: transparent !important;
	border-top-color: transparent !important;
}

.no-bottom {
	border-bottom-color: transparent !important;
}

.no-top {
	border-top-color: transparent !important;
}

Then the table content will be something like this. Can just loop it 10 or 20 times and play around with it

<tr>
	<td class="no-top-bottom"></td>
	<td class="no-top-bottom"></td>
	<td></td>
	<td></td>
	<td></td>
	<td></td>
	<td></td>
	<td><br></td>
</tr>
<tr>
	<td class="no-top-bottom"></td>
	<td class="no-top-bottom"></td>
	<td></td>
	<td></td>
	<td></td>
	<td></td>
	<td></td>
	<td><br></td>
</tr>
<tr>
	<td class="no-top-bottom"></td>
	<td class="no-bottom"></td>
	<td></td>
	<td></td>
	<td></td>
	<td></td>
	<td></td>
	<td><br></td>
</tr>
<tr>
	<td class="no-top"></td>
	<td class="no-top"></td>
	<td></td>
	<td></td>
	<td></td>
	<td></td>
	<td></td>
	<td><br></td>
</tr>

@aminankit31
Copy link

if we are using a static table then we can add a class to that particular row where the table breaks and we can apply CSS like
tr.row-break {
page-break-before: always;
}

@Doclassif
Copy link

This issue is still present. 2.0.1 version.

@Yahasana
Copy link

Yahasana commented Jan 29, 2024

I must say that dompdf has so many bugs when render page break and very hard to fix.
image

solution to fix new page style or content broken.

  1. set lines number for each page, render the page content line by line.
  2. if the lines number reach to break to new page.
    echo '<tr><td><div style="position:absolute;left:0; page-break-before: always"></div></td></tr>'
    page-break-before: always create new page and position:absolute clear unexpected text-indent/margin/padding style.
    don't ask why it works. it's 💯 magic
  3. continue step 1)
    image

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

No branches or pull requests