mirror of
https://github.com/gryf/ebook-converter.git
synced 2025-12-28 12:12:26 +01:00
206 lines
6.8 KiB
Python
206 lines
6.8 KiB
Python
#!/usr/bin/env python2
|
|
# vim:fileencoding=utf-8
|
|
# License: GPLv3 Copyright: 2016, Kovid Goyal <kovid at kovidgoyal.net>
|
|
|
|
from __future__ import absolute_import, division, print_function, unicode_literals
|
|
|
|
import os
|
|
from polyglot.builtins import range, unicode_type
|
|
|
|
|
|
class ReadOnlyFileBuffer(object):
|
|
|
|
''' A zero copy implementation of a file like object. Uses memoryviews for efficiency. '''
|
|
|
|
def __init__(self, raw):
|
|
self.sz, self.mv = len(raw), (raw if isinstance(raw, memoryview) else memoryview(raw))
|
|
self.pos = 0
|
|
|
|
def tell(self):
|
|
return self.pos
|
|
|
|
def read(self, n=None):
|
|
if n is None:
|
|
ans = self.mv[self.pos:]
|
|
self.pos = self.sz
|
|
return ans
|
|
ans = self.mv[self.pos:self.pos+n]
|
|
self.pos = min(self.pos + n, self.sz)
|
|
return ans
|
|
|
|
def seek(self, pos, whence=os.SEEK_SET):
|
|
if whence == os.SEEK_SET:
|
|
self.pos = pos
|
|
elif whence == os.SEEK_END:
|
|
self.pos = self.sz + pos
|
|
else:
|
|
self.pos += pos
|
|
self.pos = max(0, min(self.pos, self.sz))
|
|
return self.pos
|
|
|
|
def getvalue(self):
|
|
return self.mv
|
|
|
|
def close(self):
|
|
pass
|
|
|
|
|
|
def svg_path_to_painter_path(d):
|
|
'''
|
|
Convert a tiny SVG 1.2 path into a QPainterPath.
|
|
|
|
:param d: The value of the d attribute of an SVG <path> tag
|
|
'''
|
|
from PyQt5.Qt import QPainterPath
|
|
cmd = last_cmd = b''
|
|
path = QPainterPath()
|
|
moveto_abs, moveto_rel = b'M', b'm'
|
|
closepath1, closepath2 = b'Z', b'z'
|
|
lineto_abs, lineto_rel = b'L', b'l'
|
|
hline_abs, hline_rel = b'H', b'h'
|
|
vline_abs, vline_rel = b'V', b'v'
|
|
curveto_abs, curveto_rel = b'C', b'c'
|
|
smoothcurveto_abs, smoothcurveto_rel = b'S', b's'
|
|
quadcurveto_abs, quadcurveto_rel = b'Q', b'q'
|
|
smoothquadcurveto_abs, smoothquadcurveto_rel = b'T', b't'
|
|
|
|
# Store the last parsed values
|
|
# x/y = end position
|
|
# x1/y1 and x2/y2 = bezier control points
|
|
x = y = x1 = y1 = x2 = y2 = 0
|
|
|
|
if isinstance(d, unicode_type):
|
|
d = d.encode('ascii')
|
|
d = d.replace(b',', b' ').replace(b'\n', b' ')
|
|
end = len(d)
|
|
pos = [0]
|
|
|
|
def read_byte():
|
|
p = pos[0]
|
|
pos[0] += 1
|
|
return d[p:p+1]
|
|
|
|
def parse_float():
|
|
chars = []
|
|
while pos[0] < end:
|
|
c = read_byte()
|
|
if c == b' ' and not chars:
|
|
continue
|
|
if c in b'-.0123456789':
|
|
chars.append(c)
|
|
else:
|
|
break
|
|
if not chars:
|
|
raise ValueError('Premature end of input while expecting a number')
|
|
return float(b''.join(chars))
|
|
|
|
def parse_floats(num, x_offset=0, y_offset=0):
|
|
for i in range(num):
|
|
val = parse_float()
|
|
yield val + (x_offset if i % 2 == 0 else y_offset)
|
|
|
|
repeated_command = None
|
|
|
|
while pos[0] < end:
|
|
last_cmd = cmd
|
|
cmd = read_byte() if repeated_command is None else repeated_command
|
|
repeated_command = None
|
|
|
|
if cmd == b' ':
|
|
continue
|
|
if cmd == moveto_abs:
|
|
x, y = parse_float(), parse_float()
|
|
path.moveTo(x, y)
|
|
elif cmd == moveto_rel:
|
|
x += parse_float()
|
|
y += parse_float()
|
|
path.moveTo(x, y)
|
|
elif cmd == closepath1 or cmd == closepath2:
|
|
path.closeSubpath()
|
|
elif cmd == lineto_abs:
|
|
x, y = parse_floats(2)
|
|
path.lineTo(x, y)
|
|
elif cmd == lineto_rel:
|
|
x += parse_float()
|
|
y += parse_float()
|
|
path.lineTo(x, y)
|
|
elif cmd == hline_abs:
|
|
x = parse_float()
|
|
path.lineTo(x, y)
|
|
elif cmd == hline_rel:
|
|
x += parse_float()
|
|
path.lineTo(x, y)
|
|
elif cmd == vline_abs:
|
|
y = parse_float()
|
|
path.lineTo(x, y)
|
|
elif cmd == vline_rel:
|
|
y += parse_float()
|
|
path.lineTo(x, y)
|
|
elif cmd == curveto_abs:
|
|
x1, y1, x2, y2, x, y = parse_floats(6)
|
|
path.cubicTo(x1, y1, x2, y2, x, y)
|
|
elif cmd == curveto_rel:
|
|
x1, y1, x2, y2, x, y = parse_floats(6, x, y)
|
|
path.cubicTo(x1, y1, x2, y2, x, y)
|
|
elif cmd == smoothcurveto_abs:
|
|
if last_cmd == curveto_abs or last_cmd == curveto_rel or last_cmd == smoothcurveto_abs or last_cmd == smoothcurveto_rel:
|
|
x1 = 2 * x - x2
|
|
y1 = 2 * y - y2
|
|
else:
|
|
x1, y1 = x, y
|
|
x2, y2, x, y = parse_floats(4)
|
|
path.cubicTo(x1, y1, x2, y2, x, y)
|
|
elif cmd == smoothcurveto_rel:
|
|
if last_cmd == curveto_abs or last_cmd == curveto_rel or last_cmd == smoothcurveto_abs or last_cmd == smoothcurveto_rel:
|
|
x1 = 2 * x - x2
|
|
y1 = 2 * y - y2
|
|
else:
|
|
x1, y1 = x, y
|
|
x2, y2, x, y = parse_floats(4, x, y)
|
|
path.cubicTo(x1, y1, x2, y2, x, y)
|
|
elif cmd == quadcurveto_abs:
|
|
x1, y1, x, y = parse_floats(4)
|
|
path.quadTo(x1, y1, x, y)
|
|
elif cmd == quadcurveto_rel:
|
|
x1, y1, x, y = parse_floats(4, x, y)
|
|
path.quadTo(x1, y1, x, y)
|
|
elif cmd == smoothquadcurveto_abs:
|
|
if last_cmd in (quadcurveto_abs, quadcurveto_rel, smoothquadcurveto_abs, smoothquadcurveto_rel):
|
|
x1 = 2 * x - x1
|
|
y1 = 2 * y - y1
|
|
else:
|
|
x1, y1 = x, y
|
|
x, y = parse_floats(2)
|
|
path.quadTo(x1, y1, x, y)
|
|
elif cmd == smoothquadcurveto_rel:
|
|
if last_cmd in (quadcurveto_abs, quadcurveto_rel, smoothquadcurveto_abs, smoothquadcurveto_rel):
|
|
x1 = 2 * x - x1
|
|
y1 = 2 * y - y1
|
|
else:
|
|
x1, y1 = x, y
|
|
x, y = parse_floats(2, x, y)
|
|
path.quadTo(x1, y1, x, y)
|
|
elif cmd in b'-.0123456789':
|
|
# A new number begins
|
|
# In this case, multiple parameters tuples are specified for the last command
|
|
# We rewind to reparse data correctly
|
|
pos[0] -= 1
|
|
|
|
# Handle extra parameters
|
|
if last_cmd == moveto_abs:
|
|
repeated_command = cmd = lineto_abs
|
|
elif last_cmd == moveto_rel:
|
|
repeated_command = cmd = lineto_rel
|
|
elif last_cmd in (closepath1, closepath2):
|
|
raise ValueError('Extra parameters after close path command')
|
|
elif last_cmd in (
|
|
lineto_abs, lineto_rel, hline_abs, hline_rel, vline_abs,
|
|
vline_rel, curveto_abs, curveto_rel,smoothcurveto_abs,
|
|
smoothcurveto_rel, quadcurveto_abs, quadcurveto_rel,
|
|
smoothquadcurveto_abs, smoothquadcurveto_rel
|
|
):
|
|
repeated_command = cmd = last_cmd
|
|
else:
|
|
raise ValueError('Unknown path command: %s' % cmd)
|
|
return path
|