Kozea / WeasyPrint

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

list-style-position: outside yields incorrect result #1557

Open lmvlmv opened 2 years ago

lmvlmv commented 2 years ago

I'm tyring to render ordered and unordered lists according to an existing style. This involes a lot of padding between the list element marker and the list element text.

The following demonstrates what I'm seeing:

import weasyprint
css='''
ul {
    border: red;
    border-style: solid;
    margin-left: 0;
    padding-left: 0;
}

ul.outside {
    list-style-position: outside;
}

ul.inside {
    list-style-position: inside;
}

li {
    border: green;
    border-style: solid;
    margin-left: 1em;
    padding-left: 5em;
}

'''

html='''
<HTML>
<HEAD>
<TITLE>List style test</TITLE>
<link rel="stylesheet" href="test.css">
</HEAD>
<BODY>
<UL class="outside">
<LI> list-style-position: outside </LI>
<LI> list-style-position: outside </LI>
<LI> list-style-position: outside </LI>
<LI> list-style-position: outside </LI>
</UL>
<UL class="inside">
<LI> list-style-position: inside </LI>
<LI> list-style-position: inside </LI>
<LI> list-style-position: inside </LI>
<LI> list-style-position: inside </LI>
</UL>
</BODY>
</HTML>
'''

with open('test.css', 'w') as cssfile:
    cssfile.write(css)

with open('test.html', 'w') as htmlfile:
    htmlfile.write(html)

weasyprint.HTML(string=html).write_pdf('test.pdf', stylesheets=[weasyprint.CSS(string=css)])

Compare test.html with test.pdf

The UL has a green border and LI red for indication of the box locations only. The list element padding is large to show the problem clearly.

In HTML this is rendered as:

image

In the PDF:

image

I'm no web designer/PDF typsetter by trade but it seems like the bullet it's placed outside the contining box for the LI in WeasyPrint but just offset by some fixed amount. Or am I completely wrong?

lmvlmv commented 2 years ago

Seems like in weasyprint\formatting_structure\build.py

    if parent_style['list_style_position'] == 'outside':
        marker_box = boxes.BlockBox.anonymous_from(box, children)
        # We can safely edit everything that can't be changed by user style
        # See https://drafts.csswg.org/css-pseudo-4/#marker-pseudo
        marker_box.style['position'] = 'absolute'
        if parent_style['direction'] == 'ltr':
            translate_x = properties.Dimension(-100, '%')
        else:
            translate_x = properties.Dimension(100, '%')
        translate_y = computed_values.ZERO_PIXELS
        marker_box.style['transform'] = (
            ('translate', (translate_x, translate_y)),)

The li::marker box is shifted 100% of it's own width but doesn't take into account the position of the li parent.

dmint789 commented 1 month ago

Hi. I was wondering if this bug is being worked on, as it affects the documents in my repo. I would like to use this property to have even indentation on every line of a long list item.

liZe commented 1 month ago

I was wondering if this bug is being worked on

I don’t think so. Interested in solving this issue?

dmint789 commented 1 month ago

I would be, but I don't know where to start, I've never contributed to this project before. Is there any chance you could fix this, or are you busy with other issues?

liZe commented 1 month ago

I would be, but I don't know where to start, I've never contributed to this project before.

That’s actually a good first issue to dive into WeasyPrint’s code!

In this kind of issues, the first step is often to read the specification to understand what we have to do. Maybe there’s a detailed way to display bullets (but I’m afraid that’s a rare case where the specification doesn’t give a real algorithm).

Then, you can check what we do. The comment above gives you a good hint: the related code is in build.py:

https://github.com/Kozea/WeasyPrint/blob/83641f3fa647458130a2130dd42cba438a3dd603/weasyprint/formatting_structure/build.py#L357-L368

The current code applies something like transform: translate(-100%) to the element. It works well, except when there’s a padding or a border on the parent.

If there’s an algorithm in the specification, we have to implement it instead of our translation. If there’s no specification, we can do what we want (ie. there’s no bug in WeasyPrint, even if the rendering is different that what browsers do). But… that would be better to follow what other browsers do, at least if they all do the same. You can test different browsers in different cases to be sure that they all give the same result.

Then, you can then think of a solution that would give the same result as browsers, propose it so that I can validate it (if you’re not sure), and finally you can open a PR with this new algorithm!

dmint789 commented 1 month ago

@liZe thank you for the detailed response, I will look into it.

dmint789 commented 1 month ago

Oh gosh, I just realized my browser produces the same result and doesn't indent wrapped lines of the list items with list-style-position: outside. I'll have to investigate further.

dmint789 commented 1 month ago

I realized what the problem was and it had nothing to do with this bug, my bad