Fix clipping, basic text output (no font embedding) and better error handling

This commit is contained in:
Kovid Goyal 2012-12-16 13:27:38 +05:30
parent 2f254bf0e0
commit 5eca5a7b5a
4 changed files with 323 additions and 176 deletions

View File

@ -0,0 +1,152 @@
#!/usr/bin/env python
# vim:fileencoding=UTF-8:ts=4:sw=4:sta:et:sts=4:fdm=marker:ai
from __future__ import (unicode_literals, division, absolute_import,
print_function)
__license__ = 'GPL v3'
__copyright__ = '2012, Kovid Goyal <kovid at kovidgoyal.net>'
__docformat__ = 'restructuredtext en'
import codecs, zlib
from io import BytesIO
EOL = b'\n'
# Sizes {{{
inch = 72.0
cm = inch / 2.54
mm = cm * 0.1
pica = 12.0
_W, _H = (21*cm, 29.7*cm)
A6 = (_W*.5, _H*.5)
A5 = (_H*.5, _W)
A4 = (_W, _H)
A3 = (_H, _W*2)
A2 = (_W*2, _H*2)
A1 = (_H*2, _W*4)
A0 = (_W*4, _H*4)
LETTER = (8.5*inch, 11*inch)
LEGAL = (8.5*inch, 14*inch)
ELEVENSEVENTEEN = (11*inch, 17*inch)
_BW, _BH = (25*cm, 35.3*cm)
B6 = (_BW*.5, _BH*.5)
B5 = (_BH*.5, _BW)
B4 = (_BW, _BH)
B3 = (_BH*2, _BW)
B2 = (_BW*2, _BH*2)
B1 = (_BH*4, _BW*2)
B0 = (_BW*4, _BH*4)
# }}}
# Basic PDF datatypes {{{
def serialize(o, stream):
if hasattr(o, 'pdf_serialize'):
o.pdf_serialize(stream)
elif isinstance(o, bool):
stream.write(b'true' if o else b'false')
elif isinstance(o, (int, float)):
stream.write(type(u'')(o).encode('ascii'))
elif o is None:
stream.write(b'null')
else:
raise ValueError('Unknown object: %r'%o)
class Name(unicode):
def pdf_serialize(self, stream):
raw = self.encode('ascii')
if len(raw) > 126:
raise ValueError('Name too long: %r'%self)
buf = [x if 33 < ord(x) < 126 and x != b'#' else b'#'+hex(ord(x)) for x
in raw]
stream.write(b'/'+b''.join(buf))
class String(unicode):
def pdf_serialize(self, stream):
s = self.replace('\\', '\\\\').replace('(', r'\(').replace(')', r'\)')
try:
raw = s.encode('latin1')
if raw.startswith(codecs.BOM_UTF16_BE):
raise UnicodeEncodeError('')
except UnicodeEncodeError:
raw = codecs.BOM_UTF16_BE + s.encode('utf-16-be')
stream.write(b'('+raw+b')')
class Dictionary(dict):
def pdf_serialize(self, stream):
stream.write(b'<<' + EOL)
for k, v in self.iteritems():
serialize(Name(k), stream)
stream.write(b' ')
serialize(v, stream)
stream.write(EOL)
stream.write(b'>>' + EOL)
class InlineDictionary(Dictionary):
def pdf_serialize(self, stream):
stream.write(b'<< ')
for k, v in self.iteritems():
serialize(Name(k), stream)
stream.write(b' ')
serialize(v, stream)
stream.write(b' ')
stream.write(b'>>')
class Array(list):
def pdf_serialize(self, stream):
stream.write(b'[')
for i, o in enumerate(self):
if i != 0:
stream.write(b' ')
serialize(o, stream)
stream.write(b']')
class Stream(BytesIO):
def __init__(self, compress=False):
BytesIO.__init__(self)
self.compress = compress
def pdf_serialize(self, stream):
raw = self.getvalue()
dl = len(raw)
filters = Array()
if self.compress:
filters.append(Name('FlateDecode'))
raw = zlib.compress(raw)
d = InlineDictionary({'Length':len(raw), 'DL':dl})
if filters:
d['Filter'] = filters
serialize(d, stream)
stream.write(EOL+b'stream'+EOL)
stream.write(raw)
stream.write(EOL+b'endstream'+EOL)
def write_line(self, raw=b''):
self.write(raw if isinstance(raw, bytes) else raw.encode('ascii'))
self.write(EOL)
def write(self, raw):
super(Stream, self).write(raw if isinstance(raw, bytes) else
raw.encode('ascii'))
class Reference(object):
def __init__(self, num, obj):
self.num, self.obj = num, obj
def pdf_serialize(self, stream):
raw = '%d 0 R'%self.num
stream.write(raw.encode('ascii'))
# }}}

View File

@ -11,13 +11,14 @@ import sys, traceback
from math import sqrt from math import sqrt
from collections import namedtuple from collections import namedtuple
from future_builtins import map from future_builtins import map
from functools import wraps
from PyQt4.Qt import (QPaintEngine, QPaintDevice, Qt, QApplication, QPainter, from PyQt4.Qt import (QPaintEngine, QPaintDevice, Qt, QApplication, QPainter,
QTransform, QPainterPath) QTransform, QPainterPath, QFontMetricsF)
from calibre.constants import DEBUG from calibre.constants import DEBUG
from calibre.ebooks.pdf.render.serialize import (Color, inch, A4, PDFStream, from calibre.ebooks.pdf.render.serialize import (Color, PDFStream, Path, Text)
Path) from calibre.ebooks.pdf.render.common import inch, A4
XDPI = 1200 XDPI = 1200
YDPI = 1200 YDPI = 1200
@ -25,6 +26,17 @@ YDPI = 1200
Point = namedtuple('Point', 'x y') Point = namedtuple('Point', 'x y')
ColorState = namedtuple('ColorState', 'color opacity do') ColorState = namedtuple('ColorState', 'color opacity do')
def store_error(func):
@wraps(func)
def errh(self, *args, **kwargs):
try:
func(self, *args, **kwargs)
except:
self.errors.append(traceback.format_exc())
return errh
class GraphicsState(object): # {{{ class GraphicsState(object): # {{{
def __init__(self): def __init__(self):
@ -156,8 +168,9 @@ class GraphicsState(object): # {{{
# Now apply the new operations # Now apply the new operations
for op, val in ops.iteritems(): for op, val in ops.iteritems():
self.apply(op, val, engine, pdf) if op != 'clip':
self.current_state[op] = val self.apply(op, val, engine, pdf)
self.current_state[op] = val
def apply(self, op, val, engine, pdf): def apply(self, op, val, engine, pdf):
getattr(self, 'apply_'+op)(val, engine, pdf) getattr(self, 'apply_'+op)(val, engine, pdf)
@ -219,8 +232,9 @@ class PdfEngine(QPaintEngine):
self.do_stroke = True self.do_stroke = True
self.do_fill = False self.do_fill = False
self.scale = sqrt(sy**2 + sx**2) self.scale = sqrt(sy**2 + sx**2)
self.yscale = sy self.xscale, self.yscale = sx, sy
self.graphics_state = GraphicsState() self.graphics_state = GraphicsState()
self.errors = []
def init_page(self): def init_page(self):
self.pdf.transform(self.pdf_system) self.pdf.transform(self.pdf_system)
@ -246,7 +260,7 @@ class PdfEngine(QPaintEngine):
compress=not DEBUG) compress=not DEBUG)
self.init_page() self.init_page()
except: except:
traceback.print_exc() self.errors.append(traceback.format_exc())
return False return False
return True return True
@ -261,7 +275,7 @@ class PdfEngine(QPaintEngine):
self.end_page(start_new=False) self.end_page(start_new=False)
self.pdf.end() self.pdf.end()
except: except:
traceback.print_exc() self.errors.append(traceback.format_exc())
return False return False
finally: finally:
self.pdf = self.file_object = None self.pdf = self.file_object = None
@ -270,12 +284,15 @@ class PdfEngine(QPaintEngine):
def type(self): def type(self):
return QPaintEngine.Pdf return QPaintEngine.Pdf
@store_error
def drawPixmap(self, rect, pixmap, source_rect): def drawPixmap(self, rect, pixmap, source_rect):
print ('TODO: drawPixmap() currently unimplemented') print ('TODO: drawPixmap() currently unimplemented')
@store_error
def drawImage(self, rect, image, source_rect, flags=Qt.AutoColor): def drawImage(self, rect, image, source_rect, flags=Qt.AutoColor):
print ('TODO: drawImage() currently unimplemented') print ('TODO: drawImage() currently unimplemented')
@store_error
def updateState(self, state): def updateState(self, state):
self.graphics_state.read(state) self.graphics_state.read(state)
self.graphics_state(self) self.graphics_state(self)
@ -299,6 +316,7 @@ class PdfEngine(QPaintEngine):
p.curve_to(*(c1 + c2 + em)) p.curve_to(*(c1 + c2 + em))
return p return p
@store_error
def drawPath(self, path): def drawPath(self, path):
p = self.convert_path(path) p = self.convert_path(path)
fill_rule = {Qt.OddEvenFill:'evenodd', fill_rule = {Qt.OddEvenFill:'evenodd',
@ -312,6 +330,7 @@ class PdfEngine(QPaintEngine):
Qt.WindingFill:'winding'}[path.fillRule()] Qt.WindingFill:'winding'}[path.fillRule()]
self.pdf.add_clip(p, fill_rule=fill_rule) self.pdf.add_clip(p, fill_rule=fill_rule)
@store_error
def drawPoints(self, points): def drawPoints(self, points):
p = Path() p = Path()
for point in points: for point in points:
@ -319,14 +338,16 @@ class PdfEngine(QPaintEngine):
p.line_to(point.x(), point.y() + 0.001) p.line_to(point.x(), point.y() + 0.001)
self.pdf.draw_path(p, stroke=self.do_stroke, fill=False) self.pdf.draw_path(p, stroke=self.do_stroke, fill=False)
@store_error
def drawRects(self, rects): def drawRects(self, rects):
for rect in rects: for rect in rects:
bl = rect.topLeft() bl = rect.topLeft()
self.pdf.draw_rect(bl.x(), bl.y(), rect.width(), rect.height(), self.pdf.draw_rect(bl.x(), bl.y(), rect.width(), rect.height(),
stroke=self.do_stroke, fill=self.do_fill) stroke=self.do_stroke, fill=self.do_fill)
@store_error
def drawTextItem(self, point, text_item): def drawTextItem(self, point, text_item):
# super(PdfEngine, self).drawTextItem(point, text_item) # super(PdfEngine, self).drawTextItem(point+QPoint(0, 300), text_item)
f = text_item.font() f = text_item.font()
px, pt = f.pixelSize(), f.pointSizeF() px, pt = f.pixelSize(), f.pointSizeF()
if px == -1: if px == -1:
@ -343,37 +364,46 @@ class PdfEngine(QPaintEngine):
self.do_fill, self.do_stroke = f, s self.do_fill, self.do_stroke = f, s
return return
to = self.canvas.beginText() to = Text()
# set_transform(QTransform(1, 0, 0, -1, point.x(), point.y()), to.setTextTransform) to.size = sz
fontname = 'Times-Roman' to.set_transform(1, 0, 0, -1, point.x(), point.y())
to.setFont(fontname, sz) # TODO: Embed font
stretch = f.stretch() stretch = f.stretch()
if stretch != 100: if stretch != 100:
to.setHorizontalScale(stretch) to.horizontal_scale = stretch
ws = f.wordSpacing() ws = f.wordSpacing()
if ws != 0: if ws != 0:
to.setWordSpacing(self.map_dx(ws)) to.word_spacing = ws
spacing = f.letterSpacing() spacing = f.letterSpacing()
st = f.letterSpacingType() st = f.letterSpacingType()
if st == f.AbsoluteSpacing and spacing != 0: if st == f.AbsoluteSpacing and spacing != 0:
to.setCharSpace(spacing) to.char_space = spacing/self.scale
# TODO: Handle percentage letter spacing if st == f.PercentageSpacing and spacing not in {100, 0}:
# TODO: Implement this with the TJ operator
avg_char_width = QFontMetricsF(f).averageCharWidth()
to.char_space = (spacing - 100) * avg_char_width / 100
text = type(u'')(text_item.text()) text = type(u'')(text_item.text())
to.textOut(text) to.text = text
# TODO: handle colors with self:
self.canvas.drawText(to) self.graphics_state.apply_fill(self.graphics_state.current_state['stroke'],
self, self.pdf)
self.pdf.draw_text(to)
def draw_line(kind='underline'): def draw_line(kind='underline'):
tw = self.canvas.stringWidth(text, fontname, sz) m = QFontMetricsF(f)
p = self.canvas.beginPath() tw = m.width(text)
p = Path()
if kind == 'underline': if kind == 'underline':
dy = -text_item.descent() dy = m.underlinePos()
elif kind == 'overline': elif kind == 'overline':
dy = text_item.ascent() dy = -m.overlinePos()
elif kind == 'strikeout': elif kind == 'strikeout':
dy = text_item.ascent()/2 dy = -m.strikeOutPos()
p.moveTo(point.x, point.y+dy) p.move_to(point.x(), point.y()+dy)
p.lineTo(point.x+tw, point.y+dy) p.line_to(point.x()+tw, point.y()+dy)
with self:
self.graphics_state.apply_line_width(m.lineWidth(),
self, self.pdf)
self.pdf.draw_path(p, stroke=True, fill=False)
if f.underline(): if f.underline():
draw_line() draw_line()
@ -382,6 +412,7 @@ class PdfEngine(QPaintEngine):
if f.strikeOut(): if f.strikeOut():
draw_line('strikeout') draw_line('strikeout')
@store_error
def drawPolygon(self, points, mode): def drawPolygon(self, points, mode):
if not points: return if not points: return
p = Path() p = Path()
@ -397,8 +428,10 @@ class PdfEngine(QPaintEngine):
def __enter__(self): def __enter__(self):
self.pdf.save_stack() self.pdf.save_stack()
self.saved_ps = (self.do_stroke, self.do_fill)
def __exit__(self, *args): def __exit__(self, *args):
self.do_stroke, self.do_fill = self.saved_ps
self.pdf.restore_stack() self.pdf.restore_stack()
class PdfDevice(QPaintDevice): # {{{ class PdfDevice(QPaintDevice): # {{{
@ -470,13 +503,18 @@ if __name__ == '__main__':
p.drawLine(0, 0, 5000, 0) p.drawLine(0, 0, 5000, 0)
p.restore() p.restore()
# f = p.font() f = p.font()
# f.setPointSize(24) f.setPointSize(24)
# f.setFamily('Times New Roman') f.setUnderline(True)
# p.setFont(f) f.setFamily('Times New Roman')
# # p.scale(2, 2) p.setFont(f)
# p.rotate(45) # p.scale(2, 2)
# p.drawText(QPoint(100, 300), 'Some text') p.rotate(45)
p.setPen(QColor(0, 255, 0))
p.drawText(QPoint(100, 300), 'Some text')
finally: finally:
p.end() p.end()
if dev.engine.errors:
for err in dev.engine.errors: print (err)
raise SystemExit(1)

View File

@ -0,0 +1,35 @@
#!/usr/bin/env python
# vim:fileencoding=UTF-8:ts=4:sw=4:sta:et:sts=4:fdm=marker:ai
from __future__ import (unicode_literals, division, absolute_import,
print_function)
__license__ = 'GPL v3'
__copyright__ = '2012, Kovid Goyal <kovid at kovidgoyal.net>'
__docformat__ = 'restructuredtext en'
from calibre.ebooks.pdf.render.common import (
Dictionary, Name)
STANDARD_FONTS = {
'Times-Roman', 'Helvetica', 'Courier', 'Symbol', 'Times-Bold',
'Helvetica-Bold', 'Courier-Bold', 'ZapfDingbats', 'Times-Italic',
'Helvetica-Oblique', 'Courier-Oblique', 'Times-BoldItalic',
'Helvetica-BoldOblique', 'Courier-BoldOblique', }
class FontManager(object):
def __init__(self, objects):
self.objects = objects
self.std_map = {}
def add_standard_font(self, name):
if name not in STANDARD_FONTS:
raise ValueError('%s is not a standard font'%name)
if name not in self.std_map:
self.std_map[name] = self.objects.add(Dictionary({
'Type':Name('Font'),
'Subtype':Name('Type1'),
'BaseFont':Name(name)
}))
return self.std_map[name]

View File

@ -7,156 +7,19 @@ __license__ = 'GPL v3'
__copyright__ = '2012, Kovid Goyal <kovid at kovidgoyal.net>' __copyright__ = '2012, Kovid Goyal <kovid at kovidgoyal.net>'
__docformat__ = 'restructuredtext en' __docformat__ = 'restructuredtext en'
import codecs, zlib, hashlib import hashlib
from io import BytesIO
from future_builtins import map from future_builtins import map
from collections import namedtuple from collections import namedtuple
from calibre.constants import (__appname__, __version__) from calibre.constants import (__appname__, __version__)
from calibre.ebooks.pdf.render.common import (
Reference, EOL, serialize, Stream, Dictionary, String, Name, Array)
from calibre.ebooks.pdf.render.fonts import FontManager
PDFVER = b'%PDF-1.6' PDFVER = b'%PDF-1.6'
EOL = b'\n'
Color = namedtuple('Color', 'red green blue opacity') Color = namedtuple('Color', 'red green blue opacity')
# Sizes {{{
inch = 72.0
cm = inch / 2.54
mm = cm * 0.1
pica = 12.0
_W, _H = (21*cm, 29.7*cm)
A6 = (_W*.5, _H*.5)
A5 = (_H*.5, _W)
A4 = (_W, _H)
A3 = (_H, _W*2)
A2 = (_W*2, _H*2)
A1 = (_H*2, _W*4)
A0 = (_W*4, _H*4)
LETTER = (8.5*inch, 11*inch)
LEGAL = (8.5*inch, 14*inch)
ELEVENSEVENTEEN = (11*inch, 17*inch)
_BW, _BH = (25*cm, 35.3*cm)
B6 = (_BW*.5, _BH*.5)
B5 = (_BH*.5, _BW)
B4 = (_BW, _BH)
B3 = (_BH*2, _BW)
B2 = (_BW*2, _BH*2)
B1 = (_BH*4, _BW*2)
B0 = (_BW*4, _BH*4)
# }}}
# Basic PDF datatypes {{{
def serialize(o, stream):
if hasattr(o, 'pdf_serialize'):
o.pdf_serialize(stream)
elif isinstance(o, bool):
stream.write(b'true' if o else b'false')
elif isinstance(o, (int, float)):
stream.write(type(u'')(o).encode('ascii'))
elif o is None:
stream.write(b'null')
else:
raise ValueError('Unknown object: %r'%o)
class Name(unicode):
def pdf_serialize(self, stream):
raw = self.encode('ascii')
if len(raw) > 126:
raise ValueError('Name too long: %r'%self)
buf = [x if 33 < ord(x) < 126 and x != b'#' else b'#'+hex(ord(x)) for x
in raw]
stream.write(b'/'+b''.join(buf))
class String(unicode):
def pdf_serialize(self, stream):
s = self.replace('\\', '\\\\').replace('(', r'\(').replace(')', r'\)')
try:
raw = s.encode('latin1')
if raw.startswith(codecs.BOM_UTF16_BE):
raise UnicodeEncodeError('')
except UnicodeEncodeError:
raw = codecs.BOM_UTF16_BE + s.encode('utf-16-be')
stream.write(b'('+raw+b')')
class Dictionary(dict):
def pdf_serialize(self, stream):
stream.write(b'<<' + EOL)
for k, v in self.iteritems():
serialize(Name(k), stream)
stream.write(b' ')
serialize(v, stream)
stream.write(EOL)
stream.write(b'>>' + EOL)
class InlineDictionary(Dictionary):
def pdf_serialize(self, stream):
stream.write(b'<< ')
for k, v in self.iteritems():
serialize(Name(k), stream)
stream.write(b' ')
serialize(v, stream)
stream.write(b' ')
stream.write(b'>>')
class Array(list):
def pdf_serialize(self, stream):
stream.write(b'[')
for i, o in enumerate(self):
if i != 0:
stream.write(b' ')
serialize(o, stream)
stream.write(b']')
class Stream(BytesIO):
def __init__(self, compress=False):
BytesIO.__init__(self)
self.compress = compress
def pdf_serialize(self, stream):
raw = self.getvalue()
dl = len(raw)
filters = Array()
if self.compress:
filters.append(Name('FlateDecode'))
raw = zlib.compress(raw)
d = InlineDictionary({'Length':len(raw), 'DL':dl})
if filters:
d['Filter'] = filters
serialize(d, stream)
stream.write(EOL+b'stream'+EOL)
stream.write(raw)
stream.write(EOL+b'endstream'+EOL)
def write_line(self, raw=b''):
self.write(raw if isinstance(raw, bytes) else raw.encode('ascii'))
self.write(EOL)
def write(self, raw):
super(Stream, self).write(raw if isinstance(raw, bytes) else
raw.encode('ascii'))
class Reference(object):
def __init__(self, num, obj):
self.num, self.obj = num, obj
def pdf_serialize(self, stream):
raw = '%d 0 R'%self.num
stream.write(raw.encode('ascii'))
# }}}
class IndirectObjects(object): class IndirectObjects(object):
def __init__(self): def __init__(self):
@ -222,6 +85,7 @@ class Page(Stream):
'Parent': parentref, 'Parent': parentref,
}) })
self.opacities = {} self.opacities = {}
self.fonts = {}
def set_opacity(self, opref): def set_opacity(self, opref):
if opref not in self.opacities: if opref not in self.opacities:
@ -230,6 +94,11 @@ class Page(Stream):
serialize(Name(name), self) serialize(Name(name), self)
self.write(b' gs ') self.write(b' gs ')
def add_font(self, fontref):
if fontref not in self.fonts:
self.fonts[fontref] = 'F%d'%len(self.fonts)
return self.fonts[fontref]
def add_resources(self): def add_resources(self):
r = Dictionary() r = Dictionary()
if self.opacities: if self.opacities:
@ -237,6 +106,11 @@ class Page(Stream):
for opref, name in self.opacities.iteritems(): for opref, name in self.opacities.iteritems():
extgs[name] = opref extgs[name] = opref
r['ExtGState'] = extgs r['ExtGState'] = extgs
if self.fonts:
fonts = Dictionary()
for ref, name in self.fonts.iteritems():
fonts[name] = ref
r['Font'] = fonts
if r: if r:
self.page_dict['Resources'] = r self.page_dict['Resources'] = r
@ -263,6 +137,44 @@ class Path(object):
def curve_to(self, x1, y1, x2, y2, x, y): def curve_to(self, x1, y1, x2, y2, x, y):
self.ops.append((x1, y1, x2, y2, x, y, 'c')) self.ops.append((x1, y1, x2, y2, x, y, 'c'))
class Text(object):
def __init__(self):
self.transform = self.default_transform = [1, 0, 0, 1, 0, 0]
self.font_name = 'Times-Roman'
self.font_path = None
self.horizontal_scale = self.default_horizontal_scale = 100
self.word_spacing = self.default_word_spacing = 0
self.char_space = self.default_char_space = 0
self.size = 12
self.text = ''
def set_transform(self, *args):
if len(args) == 1:
m = args[0]
vals = [m.m11(), m.m12(), m.m21(), m.m22(), m.dx(), m.dy()]
else:
vals = args
self.transform = vals
def pdf_serialize(self, stream, font_name):
if not self.text: return
stream.write_line('BT ')
serialize(Name(font_name), stream)
stream.write(' %g Tf '%self.size)
stream.write(' '.join(map(type(u''), self.transform)) + ' Tm ')
if self.horizontal_scale != self.default_horizontal_scale:
stream.write('%g Tz '%self.horizontal_scale)
if self.word_spacing != self.default_word_spacing:
stream.write('%g Tw '%self.word_spacing)
if self.char_space != self.default_char_space:
stream.write('%g Tc '%self.char_space)
stream.write_line()
serialize(String(self.text), stream)
stream.write(' Tj ')
stream.write_line('ET')
class Catalog(Dictionary): class Catalog(Dictionary):
def __init__(self, pagetree): def __init__(self, pagetree):
@ -325,6 +237,7 @@ class PDFStream(object):
self.info = Dictionary({'Creator':String(creator), self.info = Dictionary({'Creator':String(creator),
'Producer':String(creator)}) 'Producer':String(creator)})
self.stroke_opacities, self.fill_opacities = {}, {} self.stroke_opacities, self.fill_opacities = {}, {}
self.font_manager = FontManager(self.objects)
@property @property
def page_tree(self): def page_tree(self):
@ -377,8 +290,9 @@ class PDFStream(object):
def add_clip(self, path, fill_rule='winding'): def add_clip(self, path, fill_rule='winding'):
if not path.ops: return if not path.ops: return
self.write_path(path)
op = 'W' if fill_rule == 'winding' else 'W*' op = 'W' if fill_rule == 'winding' else 'W*'
self.current_page.write(op + ' ' + 'n') self.current_page.write_line(op + ' ' + 'n')
def set_dash(self, array, phase=0): def set_dash(self, array, phase=0):
array = Array(array) array = Array(array)
@ -421,6 +335,14 @@ class PDFStream(object):
self.page_tree.obj.add_page(pageref) self.page_tree.obj.add_page(pageref)
self.current_page = Page(self.page_tree, compress=self.compress) self.current_page = Page(self.page_tree, compress=self.compress)
def draw_text(self, text_object):
if text_object.font_path is None:
fontref = self.font_manager.add_standard_font(text_object.font_name)
else:
raise NotImplementedError()
name = self.current_page.add_font(fontref)
text_object.pdf_serialize(self.current_page, name)
def end(self): def end(self):
if self.current_page.getvalue(): if self.current_page.getvalue():
self.end_page() self.end_page()