Skip to content
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

Backport 'Fix for PDF texts not being selectable' #240

Open
wants to merge 2 commits into
base: 1.0.x
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 3 additions & 2 deletions NEWS.rst
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,10 @@
======


Version 2.0, not released yet
=============================
Version 1.0.23, not released yet
================================

* Make text selectable on generated PDF files
* Fix markers
* Fix URL/id handling
* Test CairoSVG with Travis
Expand Down
3 changes: 3 additions & 0 deletions cairosvg/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,9 @@ def main():
option_parser.add_option(
"-o", "--output",
default="", help="output filename")
option_parser.add_option(
"--text-as-text", action="store_true",
help="saves text in PDF as text instead of paths")
options, args = option_parser.parse_args()

# Print help if no argument is given
Expand Down
24 changes: 20 additions & 4 deletions cairosvg/surface/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -75,9 +75,10 @@ class Surface(object):

# Subclasses must either define this or override _create_surface()
surface_class = None
draw_text_as_text = False

@classmethod
def convert(cls, bytestring=None, **kwargs):
def convert(cls, bytestring=None, draw_text_as_text=None, **kwargs):
"""Convert a SVG document to the format for this class.

Specify the input by passing one of these:
Expand All @@ -90,6 +91,9 @@ def convert(cls, bytestring=None, **kwargs):

:param write_to: The filename of file-like object where to write the
output. If None or not provided, return a byte string.
:param draw_text_as_text: Draw text as text, instead of paths, reducing
the file size in PDFs and allowing text selection. May
not support some path clipping operations.

Only ``source`` can be passed as a positional argument, other
parameters are keyword-only.
Expand All @@ -105,12 +109,14 @@ def convert(cls, bytestring=None, **kwargs):
output = io.BytesIO()
else:
output = write_to
cls(tree, output, dpi, None, parent_width, parent_height).finish()
cls(tree, output, dpi, None, parent_width, parent_height,
draw_text_as_text).finish()
if write_to is None:
return output.getvalue()

def __init__(self, tree, output, dpi, parent_surface=None,
parent_width=None, parent_height=None):
parent_width=None, parent_height=None,
draw_text_as_text=None):
"""Create the surface from a filename or a file-like object.

The rendered content is written to ``output`` which can be a filename,
Expand Down Expand Up @@ -144,6 +150,8 @@ def __init__(self, tree, output, dpi, parent_surface=None,
self._old_parent_node = self.parent_node = None
self.output = output
self.dpi = dpi
if draw_text_as_text is not None:
self.draw_text_as_text = draw_text_as_text
self.font_size = size(self, "12pt")
self.stroke_and_fill = True
width, height, viewbox = node_format(self, tree)
Expand Down Expand Up @@ -323,6 +331,9 @@ def draw(self, node):
self.context.clip()
self.context.set_fill_rule(cairo.FILL_RULE_WINDING)

save_cursor = (self.cursor_position, self.cursor_d_position,
malthe marked this conversation as resolved.
Show resolved Hide resolved
self.text_path_width)

if node.tag in TAGS:
try:
TAGS[node.tag](self, node)
Expand Down Expand Up @@ -362,7 +373,12 @@ def draw(self, node):
if node.get("fill-rule") == "evenodd":
self.context.set_fill_rule(cairo.FILL_RULE_EVEN_ODD)
self.context.set_source_rgba(*color(paint_color, fill_opacity))
self.context.fill_preserve()
if self.draw_text_as_text and TAGS[node.tag] == text:
self.cursor_position, self.cursor_d_position, \
self.text_path_width = save_cursor
text(self, node)
else:
self.context.fill_preserve()
self.context.restore()

# Stroke
Expand Down
7 changes: 6 additions & 1 deletion cairosvg/surface/text.py
Original file line number Diff line number Diff line change
Expand Up @@ -205,7 +205,12 @@ def text(surface, node):
surface.context.rel_move_to(-x_align, y_align)
surface.context.rotate(last_r if r is None else r)

surface.context.text_path(letter)
# Only draw characters with 'content' (workaround for bug in cairo)
if not letter.isspace():
if surface.draw_text_as_text:
surface.context.show_text(letter)
else:
surface.context.text_path(letter)
surface.context.restore()
if not text_path:
surface.cursor_position = cursor_position
Expand Down