Skip to content

Commit

Permalink
Avoid invalid PDF operators when drawing SVG text
Browse files Browse the repository at this point in the history
According to the PDF specification, "special graphics state" operators (q, Q, cm) aren't permitted while a text object is being drawn.
  • Loading branch information
rianmcguire committed Jan 27, 2022
1 parent 15f5b3c commit a5d6261
Show file tree
Hide file tree
Showing 2 changed files with 12 additions and 13 deletions.
11 changes: 8 additions & 3 deletions weasyprint/draw.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@
import operator
from colorsys import hsv_to_rgb, rgb_to_hsv
from io import BytesIO
from math import ceil, floor, pi, sqrt, tan
from math import ceil, cos, floor, pi, sin, sqrt, tan
from xml.etree import ElementTree

from PIL import Image
Expand Down Expand Up @@ -1031,8 +1031,10 @@ def draw_emojis(stream, font_size, x, y, emojis):
stream.pop_state()


def draw_first_line(stream, textbox, text_overflow, block_ellipsis, x, y):
def draw_first_line(stream, textbox, text_overflow, block_ellipsis, x, y, angle=0):
"""Draw the given ``textbox`` line to the document ``stream``."""
from .document import Matrix

pango.pango_layout_set_single_paragraph_mode(
textbox.pango_layout.layout, True)

Expand Down Expand Up @@ -1083,7 +1085,10 @@ def draw_first_line(stream, textbox, text_overflow, block_ellipsis, x, y):
while runs[-1].next != ffi.NULL:
runs.append(runs[-1].next)

stream.text_matrix(font_size, 0, 0, -font_size, x, y)
matrix = Matrix(font_size, 0, 0, -font_size, x, y)
if angle:
matrix = Matrix(a=cos(angle), b=-sin(angle), c=sin(angle), d=cos(angle)) @ matrix
stream.text_matrix(*matrix.values)
last_font = None
string = ''
fonts = stream.get_fonts()
Expand Down
14 changes: 4 additions & 10 deletions weasyprint/svg/text.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
"""

from math import cos, inf, radians, sin
from math import inf, radians

from .bounding_box import EMPTY_BOUNDING_BOX, extend_bounding_box
from .utils import normalize, size
Expand Down Expand Up @@ -120,6 +120,7 @@ def text(svg, node, font_size):
svg.cursor_position = (x + dx, y + dy)
return

svg.stream.push_state()
svg.stream.begin_text()
emoji_lines = []

Expand All @@ -133,21 +134,14 @@ def text(svg, node, font_size):
svg.cursor_d_position[1] += dy or 0
layout, _, _, width, height, _ = split_first_line(
letter, style, svg.context, inf, 0)
svg.stream.push_state()
x = svg.cursor_position[0] if x is None else x
y = svg.cursor_position[1] if y is None else y
if i:
x += letter_spacing
x_position = x + svg.cursor_d_position[0] + x_align
y_position = y + svg.cursor_d_position[1] + y_align
svg.stream.move_to(x_position, y_position)
cursor_position = x + width, y
angle = last_r if r is None else r
if angle:
svg.stream.transform(e=x_position, f=y_position)
svg.stream.transform(
a=cos(angle), b=sin(angle), c=-sin(angle), d=cos(angle))
svg.stream.transform(e=-x_position, f=-y_position)
points = (
(cursor_position[0] + x_align +
svg.cursor_d_position[0],
Expand All @@ -163,12 +157,12 @@ def text(svg, node, font_size):
svg.fill_stroke(node, font_size, text=True)
emojis = draw_first_line(
svg.stream, TextBox(layout, style), 'none', 'none',
x_position, y_position)
x_position, y_position, angle)
emoji_lines.append((font_size, x, y, emojis))
svg.stream.pop_state()
svg.cursor_position = cursor_position

svg.stream.end_text()
svg.stream.pop_state()

for font_size, x, y, emojis in emoji_lines:
draw_emojis(svg.stream, font_size, x, y, emojis)

0 comments on commit a5d6261

Please sign in to comment.