-
Notifications
You must be signed in to change notification settings - Fork 227
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
Indent HTML lists correctly (Issue 1073) #1170
base: master
Are you sure you want to change the base?
Conversation
Some debugging and polish needed.
Some variables need tweaking. Needs testing. Code reuse unsatisfactory.
Some variables need tweaking. Needs testing. Code reuse unsatisfactory.
Potentially significant issues with tests: 1. test_html_ln_outside_p - IndexError: list index out of range. 2. test_html_ol_ul_line_height - actual distance between lines differs slightly from expected. Code reuse unsatisfactory.
Need feedback for handling <dd> and <blockquote>. Potentially significant issues with tests: 1. test_html_ol_ul_line_height - actual distance between lines differs slightly from expected. Need feedback for whether or not the new indentation that contradicts old tests is satisfactory. Code reuse unsatisfactory. Need feedback.
Bug present: bullets are made one per line instead of one per paragraph. Saving progress before introducing a `Bullet` class.
Feature implemented. Testing and adjustments of tests needed.
Prevented from `Paragraph.top_margin` being added to `pdf.y` of first lines of paragraphs with bullets.
Prevented from `Paragraph.top_margin` being added to `pdf.y` of first lines of paragraphs with bullets. `<ul>` and `<ol>` tags now cause a creation of a paragraph with the string `\n` being used to generate a fragment of the height `list_pseudo_margin`. Adjusted defaults for `li_tag_indent`.
Changed `Paragraph.generate_bullet_frag()` into `generate_bullet_frag_and_tl`, and made it also generate the bullet text line. Dealing with the issue of inappropriately large distance between `<dt>` and their child `<dd>` elements when `Paragraph.top_margin` is 0.
Changed `Paragraph.generate_bullet_frag()` into `generate_bullet_frag_and_tl`, and made it also generate the bullet text line.
# Conflicts: # fpdf/html.py
Adjusted old tests.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Nice work so far, but the devil is in the detail...
As I'm sure you've noticed, the interplay between the HTML parser, text regions, line wrapping, and rendering is non-trivial. I've added some pointers of how to fix the parts that don't quite add up yet.
fpdf/html.py
Outdated
else: | ||
self.line_height_stack.append(None) | ||
if self.indent == 1: | ||
self._new_paragraph(top_margin=self.list_top_margin, line_height=0) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Applying the margins with the </?[uo]l>
instead of the <li>
is the correct and clean design. 👍
But I'm not sure if checking for self.indent == 1:
is the right criterion for the top margin.
What is the logic (or HTML spec) behind that?
Btw:
You're using the NBSP to get the actual margin, with list_top_margin
only adding a very small and probably unnecessary amount to it. If you do that, you could probably just leave list_top_margin
away completely.
But then, actually using the Paragraph()
top and bottom margin functionality has additional benefits. For example, the paragraph will not apply them at the top or bottom of a page. Your current solution will create an unnecessary empty space there, possibly resulting in unnecessary page breaks.
The better solution might be to just add a top/bottom margin of the current text size, and modify Paragraph()
so it applies its margins even if it contains no text.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
But I'm not sure if checking for self.indent == 1: is the right criterion for the top margin.
What is the logic (or HTML spec) behind that?
That is done to avoid applying margins in the case of nested lists, i.e. the margins are only applied if we aren't already handling an HTML element that increases HTML2PDF.indent
.
The better solution might be to just add a top/bottom margin of the current text size, and modify Paragraph() so it applies its margins even if it contains no text.
Will do.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
That is done to avoid applying margins in the case of nested lists,
The specs suggest the following defaults:
dir, dl, menu, ol, ul { margin-block: 1em; }
which implies that nested lists also should have vertical margins (as there is no exception defined for them).
Browser makers seem to interpret that rather liberally, with both Firefox and Edge (hence Chromium) applying a margin above, but not below. They differ in that Firefox keeps the two nested bullets on the same line, while Chromium jumps one line:
I guess only using vertical margins for the top level list is just as compliant as those two...
@@ -699,3 +699,11 @@ def test_html_ol_ul_line_height(tmp_path): | |||
</ul>""" | |||
) | |||
assert_pdf_equal(pdf, HERE / "html_ol_ul_line_height.pdf", tmp_path) | |||
|
|||
|
|||
def test_html_long_list_entries(tmp_path): |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You have added functionality to text_region.py, which is very useful even when not parsing HTML. That means we need tests to verify that the new arguments for Paragraph()
work correctly when used directly as well. This is necessary to avoid regressions with any future changes.
It is also a good reason to avoid changing line_break.py, because otherwise you'd have to add tests to verify your changes there as well... 😉
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Will do.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
On second thought, I would like to also ask what tests I should make for that.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Those tests should allow to verify that Paragraph
s are rendered correctly with any combination of indent and bullet string either present or not, both with bullet strings that are longer or shorter than the text indent is wide.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I have added a test for long <ol>
bullets. The other tests that are already present seem to already fulfil the need for testing whether or not a Paragraph
is rendered correctly with different bullet
s and tag_indents
. Am I missing something?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I currently don't see Paragraph being used outside of handling HTML, hence why the assumption.
Explained above.
There don't seem to be docstrings for ParagraphCollectorMixin.paragraph() and Paragraph. Should I create one?
That would be very helpful! It looks like I have been skimping a bit on docstrings in that module.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Added the docstring to the Paragraph
class. TextRegion.md
has been edited. Test for generation of Paragraph
objects has been added, albeit it does not test the handling of bullet_rel_x_displacement
and bullet_rel_y_displacement
, as relevant Paragraph
-instantiating methods do not have the relevant functionality yet.
I am considering removing bullet_rel_y_displacement
from Paragraph
altogether in favour of vertical alignment, but the introduction of alignment will require some additional time.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'd recommend to leave vertical bullet alignment as a future enhancement. Let's get this stable and robust first, and then think about any more bells and whistles.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Bumping.
A commit is ready for examination.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Apologies for bumping again.
Is there anything else required to do regarding this?
…`list_vertical_margin`. Removed the `MultiLineBreak.indent` attribute. Added a test for long `<ol>` bullets.
# Conflicts: # CHANGELOG.md
…_bullet_frag_and_tl`.
…_bullet_frag_and_tl`.
# Conflicts: # fpdf/text_region.py
…ags_and_tl` method and in the `Bullet` class.
Edited `TextRegion.md` to reflect the introduced changes for `Paragraph`s. Added tests for `Paragraph` generation in `test_html.py`
Reformatted |
# Conflicts: # CHANGELOG.md # test/html/html_features.pdf
Now that the code has largely settled, let's look at the results.
The indents for lists are now much smaller than before. The previous default indent depended on the font size (5 x width of NBSP). This now seems to have changed to 5 "document units". With the default of mm this is too small. With the document units set to eg. inches, it will be way too large. You will have to pick a reasonable size in mm (eg. 8 or 10), and then make sure that if the document units aren't mm, this value gets converted appropriately before being used. The documentation also must clearly state that Note that top and bottom margins of The docstring for |
Regarding tests with small indents, do you want me to pass Regarding the new margins between elements in I might not have time to confidently deal with the 'magic numbers' tonight, so I will likely be pushing the changes tomorrow, and not today. Should I just do the conversion into appropriate units with them? If so, would you prefer for me to intentionally change them a little in order for them to look nicer, or would you prefer a more exact conversion? EDIT: Going to note that, currently, due to the |
…margin values in `html.py` to the chosen document unit of measurement. Adjusted default tag indent values. Moved the `Paragraph` docstring to the `ParagraphCollectorMixin.paragraph()` method. Changed the `CustomPDF` class in `test_html_customize_ul` to have non-static attributes `li_tag_indent` and `ul_bullet_char`. Adjusted tests.
Fixes #1073
Implements paragraph indentation via adjustments of
pdf.x
instead of using a natural number of whitespaces.Breaks up list items into individual paragraphs.
Checklist:
The GitHub pipeline is OK (green),
meaning that both
pylint
(static code analyzer) andblack
(code formatter) are happy with the changes of this PR.A unit test is covering the code added / modified by this PR
This PR is ready to be merged
In case of a new feature, docstrings have been added, with also some documentation in the
docs/
folderA mention of the change is present in
CHANGELOG.md
Not sure how to actually name the
HTML2FPDF.list_pseudo_margin
attribute. It is used for determining the height of the\n
line created when a<ul>
or<ol>
starting tag is handled.Should the re-implementation of paragraph indentation be reflected in a doctstring, even though the
tag_indents
parameter ofwrite_html()
has not been touched? If so, where should it be placed?By submitting this pull request, I confirm that my contribution is made under the terms of the GNU LGPL 3.0 license.