Skip to content

Commit 9472e06

Browse files
thisismillerclaudep
authored andcommitted
Teach svglib how to approximate "ex" and "ch" as em/2.
W3 states: * In the cases where it is impossible or impractical to determine the x-height, a value of 0.5em must be assumed. * In the cases where it is impossible or impractical to determine the measure of the “0” glyph, it must be assumed to be 0.5em wide by 1em tall. See https://www.w3.org/TR/css-values-3/#font-relative-lengths Both ImageMagick and CairoSVG already implement em/2 as the value for ex.
1 parent 6c865f7 commit 9472e06

File tree

4 files changed

+28
-8
lines changed

4 files changed

+28
-8
lines changed

svglib/svglib.py

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -325,10 +325,14 @@ def convertLength(self, svgAttr, em_base=DEFAULT_FONT_SIZE, attr_name=None, defa
325325
return float(text[:-2]) * em_base
326326
elif text.endswith("px"):
327327
return float(text[:-2])
328-
329-
if "ex" in text:
330-
logger.warning("Ignoring unit ex")
331-
text = text.replace("ex", '')
328+
elif text.endswith("ex"):
329+
# The x-height of the text must be assumed to be 0.5em tall when the
330+
# text cannot be measured.
331+
return float(text[:-2]) * em_base / 2
332+
elif text.endswith("ch"):
333+
# The advance measure of the "0" glyph must be assumed to be 0.5em
334+
# wide when the text cannot be measured.
335+
return float(text[:-2]) * em_base / 2
332336

333337
text = text.strip()
334338
length = toLength(text) # this does the default measurements such as mm and cm

tests/samples/others/em_unit.svg

Lines changed: 0 additions & 1 deletion
This file was deleted.

tests/samples/others/units.svg

Lines changed: 10 additions & 0 deletions
Loading

tests/test_samples.py

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -439,10 +439,17 @@ def test_external_svg_in_svg(self):
439439
assert isinstance(img_group.contents[1].contents[0], Rect)
440440
assert img_group.contents[1].transform, (1, 0, 0, 1, 100.0, 200.0)
441441

442-
def test_em_unit_svg(self):
443-
path = join(TEST_ROOT, "samples", "others", "em_unit.svg")
442+
def test_units_svg(self):
443+
path = join(TEST_ROOT, "samples", "others", "units.svg")
444444
drawing = svglib.svg2rlg(path)
445-
assert drawing.contents[0].transform[5] == svglib.DEFAULT_FONT_SIZE
445+
unit_widths = [line.getBounds()[0] for line in drawing.contents[0].contents]
446+
assert unit_widths == sorted(unit_widths)
447+
unit_names = ["px", "pt", "mm", "ex", "ch", "em", "pc", "cm"]
448+
lengths_by_name = dict(zip(unit_names, unit_widths))
449+
assert lengths_by_name["px"] == 1 # 1px == 1px
450+
assert lengths_by_name["em"] == svglib.DEFAULT_FONT_SIZE # 1 em == font size
451+
assert lengths_by_name["ex"] == lengths_by_name["em"] / 2
452+
assert lengths_by_name["ch"] == lengths_by_name["ex"]
446453

447454
def test_empty_style(self):
448455
path = join(TEST_ROOT, "samples", "others", "empty_style.svg")

0 commit comments

Comments
 (0)