Skip to content

Commit

Permalink
Merge pull request #891 from Kozea/text-overflow
Browse files Browse the repository at this point in the history
Support text-overflow
  • Loading branch information
liZe authored Jul 8, 2019
2 parents a39951b + a0d912e commit f5d4ee2
Show file tree
Hide file tree
Showing 7 changed files with 145 additions and 13 deletions.
7 changes: 3 additions & 4 deletions docs/features.rst
Original file line number Diff line number Diff line change
Expand Up @@ -500,14 +500,13 @@ The `CSS Basic User Interface Module Level 3`_ also known as CSS3 UI is a
candidate recommendation describing "CSS properties which enable authors to
style user interface related properties and values."

Only one new property defined in this document is implemented in WeasyPrint:
the ``box-sizing`` property.
Two new properties defined in this document are implemented in WeasyPrint:
the ``box-sizing`` and ``text-overflow`` properties.

Some of the properties do not apply for WeasyPrint: ``cursor``, ``resize``,
``caret-color``, ``nav-(up|right|down|left)``.

The other properties are **not** implemented: ``outline-offset`` and
``text-overflow``.
The ``outline-offset`` property is **not** implemented.

.. _CSS Basic User Interface Module Level 3: http://www.w3.org/TR/css-ui-3/

Expand Down
5 changes: 4 additions & 1 deletion weasyprint/css/properties.py
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,6 @@
'margin_left': Dimension(0, 'px'),
'max_height': Dimension(float('inf'), 'px'), # parsed value for 'none'
'max_width': Dimension(float('inf'), 'px'),
'overflow': 'visible',
'padding_top': Dimension(0, 'px'),
'padding_right': Dimension(0, 'px'),
'padding_bottom': Dimension(0, 'px'),
Expand Down Expand Up @@ -196,6 +195,10 @@
'text_decoration_color': 'currentColor',
'text_decoration_style': 'solid',

# Overflow Module 3 (WD): https://www.w3.org/TR/css-overflow-3/
'overflow': 'visible',
'text_overflow': 'clip',

# Proprietary
'anchor': None, # computed value of 'none'
'link': None, # computed value of 'none'
Expand Down
7 changes: 7 additions & 0 deletions weasyprint/css/validation/properties.py
Original file line number Diff line number Diff line change
Expand Up @@ -959,6 +959,13 @@ def overflow(keyword):
return keyword in ('auto', 'visible', 'hidden', 'scroll')


@property()
@single_keyword
def text_overflow(keyword):
"""Validation for the ``text-overflow`` property."""
return keyword in ('clip', 'ellipsis')


@property()
@single_keyword
def position(keyword):
Expand Down
18 changes: 11 additions & 7 deletions weasyprint/draw.py
Original file line number Diff line number Diff line change
Expand Up @@ -996,7 +996,8 @@ def draw_replacedbox(context, box):
context, draw_width, draw_height, box.style['image_rendering'])


def draw_inline_level(context, page, box, enable_hinting, offset_x=0):
def draw_inline_level(context, page, box, enable_hinting, offset_x=0,
text_overflow='clip'):
if isinstance(box, StackingContext):
stacking_context = box
assert isinstance(
Expand All @@ -1006,6 +1007,8 @@ def draw_inline_level(context, page, box, enable_hinting, offset_x=0):
draw_background(context, box.background, enable_hinting)
draw_border(context, box, enable_hinting)
if isinstance(box, (boxes.InlineBox, boxes.LineBox)):
if isinstance(box, boxes.LineBox):
text_overflow = box.text_overflow
for child in box.children:
if isinstance(child, StackingContext):
child_offset_x = offset_x
Expand All @@ -1015,20 +1018,21 @@ def draw_inline_level(context, page, box, enable_hinting, offset_x=0):
if isinstance(child, boxes.TextBox):
draw_text(
context, child, enable_hinting,
offset_x=child_offset_x)
child_offset_x, text_overflow)
else:
draw_inline_level(
context, page, child, enable_hinting,
offset_x=child_offset_x)
context, page, child, enable_hinting, child_offset_x,
text_overflow)
elif isinstance(box, boxes.InlineReplacedBox):
draw_replacedbox(context, box)
else:
assert isinstance(box, boxes.TextBox)
# Should only happen for list markers
draw_text(context, box, enable_hinting, offset_x=offset_x)
draw_text(context, box, enable_hinting, offset_x, text_overflow)


def draw_text(context, textbox, enable_hinting, offset_x=0):
def draw_text(context, textbox, enable_hinting, offset_x=0,
text_overflow='clip'):
"""Draw ``textbox`` to a ``cairo.Context`` from ``PangoCairo.Context``."""
# Pango crashes with font-size: 0
assert textbox.style['font_size']
Expand All @@ -1040,7 +1044,7 @@ def draw_text(context, textbox, enable_hinting, offset_x=0):
context.set_source_rgba(*textbox.style['color'])

textbox.pango_layout.reactivate(textbox.style)
show_first_line(context, textbox)
show_first_line(context, textbox, text_overflow)

values = textbox.style['text_decoration_line']

Expand Down
8 changes: 8 additions & 0 deletions weasyprint/formatting_structure/boxes.py
Original file line number Diff line number Diff line change
Expand Up @@ -416,6 +416,14 @@ class LineBox(ParentBox):
be split into multiple line boxes, one for each actual line.
"""
text_overflow = 'clip'

@classmethod
def anonymous_from(cls, parent, *args, **kwargs):
box = super().anonymous_from(parent, *args, **kwargs)
if parent.style['overflow'] != 'visible':
box.text_overflow = parent.style['text_overflow']
return box


class InlineLevelBox(Box):
Expand Down
93 changes: 93 additions & 0 deletions weasyprint/tests/test_draw/test_text.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@
"""
weasyprint.tests.test_draw.test_text
------------------------------------
Test how text is drawn.
:copyright: Copyright 2011-2019 Simon Sapin and contributors, see AUTHORS.
:license: BSD, see LICENSE for details.
"""

from . import assert_pixels


def test_text_overflow_clip():
assert_pixels('text_overflow', 9, 7, '''
_________
_RRRRRRR_
_RRRRRRR_
_________
_RR__RRR_
_RR__RRR_
_________
''', '''
<style>
@font-face {src: url(AHEM____.TTF); font-family: ahem}
@page {
size: 9px 7px;
background: white;
}
body {
color: red;
font-family: ahem;
font-size: 2px;
}
div {
line-height: 1;
margin: 1px;
overflow: hidden;
width: 3.5em;
}
</style>
<div>abcde</div>
<div style="white-space: nowrap">a bcde</div>''')


def test_text_overflow_ellipsis():
assert_pixels('text_overflow', 9, 16, '''
_________
_RRRRRR__
_RRRRRR__
_________
_RR__RR__
_RR__RR__
_________
_RRRRRR__
_RRRRRR__
_________
_RRRRRRR_
_RRRRRRR_
_________
_RRRRRRR_
_RRRRRRR_
_________
''', '''
<style>
@font-face {src: url(AHEM____.TTF); font-family: ahem}
@page {
background: white;
size: 9px 16px;
}
body {
color: red;
font-family: ahem;
font-size: 2px;
}
div {
line-height: 1;
margin: 1px;
overflow: hidden;
text-overflow: ellipsis;
width: 3.5em;
}
div div {
margin: 0;
}
</style>
<div>abcde</div>
<div style="white-space: nowrap">a bcde</div>
<div><span>a<span>b</span>cd</span>e</div>
<div><div style="text-overflow: clip">abcde</div></div>
<div><div style="overflow: visible">abcde</div></div>
''')
20 changes: 19 additions & 1 deletion weasyprint/text.py
Original file line number Diff line number Diff line change
Expand Up @@ -96,6 +96,13 @@
PANGO_TAB_LEFT
} PangoTabAlign;
typedef enum {
PANGO_ELLIPSIZE_NONE,
PANGO_ELLIPSIZE_START,
PANGO_ELLIPSIZE_MIDDLE,
PANGO_ELLIPSIZE_END
} PangoEllipsizeMode;
typedef struct {
const PangoAttrClass *klass;
guint start_index;
Expand Down Expand Up @@ -218,6 +225,9 @@
PangoRectangle *ink_rect, PangoRectangle *logical_rect);
PangoContext * pango_layout_get_context (PangoLayout *layout);
void pango_layout_set_ellipsize (
PangoLayout *layout,
PangoEllipsizeMode ellipsize);
void pango_get_log_attrs (
const char *text, int length, int level, PangoLanguage *language,
Expand Down Expand Up @@ -1217,10 +1227,18 @@ def split_first_line(text, style, context, max_width, justification_spacing,
style['hyphenate_character'])


def show_first_line(context, textbox):
def show_first_line(context, textbox, text_overflow):
"""Draw the given ``textbox`` line to the cairo ``context``."""
pango.pango_layout_set_single_paragraph_mode(
textbox.pango_layout.layout, True)

if text_overflow == 'ellipsis':
max_width = context.clip_extents()[2] - textbox.position_x
pango.pango_layout_set_width(
textbox.pango_layout.layout, units_from_double(max_width))
pango.pango_layout_set_ellipsize(
textbox.pango_layout.layout, pango.PANGO_ELLIPSIZE_END)

first_line, _ = textbox.pango_layout.get_first_line()
context = ffi.cast('cairo_t *', context._pointer)
pangocairo.pango_cairo_show_layout_line(context, first_line)
Expand Down

0 comments on commit f5d4ee2

Please sign in to comment.