Source code for quill.importer.quill_importer

"""
Importer for Quill Archives
"""

import tarfile
import struct
import os


from base import ImporterBase, QuillImporterError


[docs]class QuillPage(object): # Purely arbitrary # A line-width of 0.003 gives a good visual approximation to Quill's pen thickness of 5 pen_scale_factor = float(5.0/0.003) def __init__(self, page_file=None, page_number=1): self.page_number = page_number fp = self.fp = page_file self.version = struct.unpack(">i", fp.read(4)) if self.version != (6,): raise QuillImporterError('wrong page version') nbytes = struct.unpack(">h", fp.read(2)) self.uuid = fp.read(36) self.tsversion = struct.unpack(">i", fp.read(4)) if self.tsversion != (1,): raise QuillImporterError('wrong tag set version') self.ntags = struct.unpack(">i", fp.read(4)) # If we have tags self.tags = [] for x in xrange(self.ntags[0]): self.tversion = struct.unpack(">i", fp.read(4)) if self.tversion != (1,): raise QuillImporterError('wrong tag version') nbytes = struct.unpack(">h", fp.read(2)) tag = {} tag["tag"] = fp.read(nbytes[0]).decode("utf-8") tag["autogenerated"] = struct.unpack(">?", fp.read(1))[0] tag["ctime"] = struct.unpack(">q", fp.read(8))[0] dummy = struct.unpack(">q", fp.read(8)) self.tags.append(tag) foo = struct.unpack(">i", fp.read(4)) foo = struct.unpack(">i", fp.read(4)) self.paper_type = struct.unpack(">i", fp.read(4)) self.nimages = struct.unpack(">i", fp.read(4)) self.images = [ self.read_image() for i in xrange(self.nimages[0]) ] dummy = struct.unpack(">i", fp.read(4)) if dummy != (0,): raise QuillImporterError('out of sync') self.read_only = struct.unpack(">?", fp.read(1)) self.aspect_ratio = struct.unpack(">f", fp.read(4))[0] self.nstrokes = struct.unpack(">i", fp.read(4)) self.strokes = [ self.read_stroke() for i in xrange(self.nstrokes[0]) ] self.nlines = struct.unpack(">i", fp.read(4)) # dummy = struct.unpack(">i", fp.read(4)) # self.ntext = struct.unpack(">i", fp.read(4))
[docs] def read_image(self): fp = self.fp version = struct.unpack(">i", fp.read(4)) if version != (1,): raise QuillImporterError('wrong image version') uuid_nbytes = struct.unpack(">h", fp.read(2)) uuid = fp.read(36) top_left = struct.unpack(">f", fp.read(4)) top_right = struct.unpack(">f", fp.read(4)) bottom_left = struct.unpack(">f", fp.read(4)) bottom_right = struct.unpack(">f", fp.read(4)) constrain_aspect = struct.unpack(">?", fp.read(1)) from quill.image import Image return Image(uuid, top_left[0], top_right[0], bottom_left[0], bottom_right[0], constrain_aspect[0])
[docs] def read_stroke(self): fp = self.fp version = struct.unpack(">i", fp.read(4)) if version != (2,): raise QuillImporterError('wrong stroke version') pen_color = struct.unpack(">i", fp.read(4)) red = (pen_color[0] >> 16) & 0xFF green = (pen_color[0] >> 8) & 0xFF blue = pen_color[0] & 0xFF thickness = struct.unpack(">i", fp.read(4)) toolint = struct.unpack(">i", fp.read(4)) fountain_pen = (toolint == (0,)) N = struct.unpack(">i", fp.read(4)) points = [] for i in xrange(N[0]): x = struct.unpack(">f", fp.read(4)) y = struct.unpack(">f", fp.read(4)) p = struct.unpack(">f", fp.read(4)) points.append((x[0], y[0], p[0])) from quill.stroke import Stroke return Stroke(fountain_pen, red, green, blue, points)
def _draw(self, cairo_context): cr = cairo_context cr.set_source_rgb(0, 0, 0) cr.set_line_cap(cairo.LINE_CAP_ROUND) cr.set_line_join(cairo.LINE_JOIN_ROUND) # cr.translate(.010, .010) cr.set_line_width(0.003) cr.scale(self.height, self.height) fp = self.fp for stroke in xrange(self.nstrokes[0]): sversion = struct.unpack(">i", fp.read(4)) pen_color = struct.unpack(">i", fp.read(4)) red = (pen_color[0] >> 16) & 0xFF green = (pen_color[0] >> 8) & 0xFF blue = pen_color[0] & 0xFF cr.set_source_rgb(float(red/255.0), float(green/255.0), float(blue/255.0)) pen_thickness = struct.unpack(">i", fp.read(4)) pen_thickness_factor = float(pen_thickness[0])/self.pen_scale_factor toolint = struct.unpack(">i", fp.read(4)) fountain_pen = (toolint == (0,)) N = struct.unpack(">i", fp.read(4)) points = [] for instance in xrange(N[0]): x = struct.unpack(">f", fp.read(4)) y = struct.unpack(">f", fp.read(4)) p = struct.unpack(">f", fp.read(4)) points.append((x[0], y[0], p[0])) if fountain_pen: # width changes, each segment is its own stroke for point in xrange(len(points) - 1): cr.set_line_width(pen_thickness_factor * ((points[point][2] + points[point+1][2])/2)) cr.move_to(points[point][0], points[point][1]) cr.line_to(points[point + 1][0], points[point + 1][1]) cr.stroke() else: # constant width, join all segment into one stroke cr.set_line_width(pen_thickness_factor) for point in xrange(len(points) - 1): cr.move_to(points[point][0], points[point][1]) cr.line_to(points[point + 1][0], points[point + 1][1]) cr.stroke() self.nlines = struct.unpack(">i", fp.read(4)) dummy = struct.unpack(">i", fp.read(4)) self.ntext = struct.unpack(">i", fp.read(4))
[docs]class QuillIndex(object): def __init__(self, index_file): fp = index_file self.version = struct.unpack(">i", fp.read(4)) if self.version != (4,): raise QuillImporterError('wrong page version') self.npages = struct.unpack(">i", fp.read(4)) self.page_uuids = [] for x in xrange(self.npages[0]): nbytes = struct.unpack(">h", fp.read(2)) u = fp.read(36) self.page_uuids.append(u) self.currentPage = struct.unpack(">i", fp.read(4)) nbytes = struct.unpack(">h", fp.read(2)) self.title = fp.read(nbytes[0]) self.ctime = struct.unpack(">q", fp.read(8)) self.mtime = struct.unpack(">q", fp.read(8)) nbytes = struct.unpack(">h", fp.read(2)) self.uuid = fp.read(36) def __repr__(self): s = 'File version: '+str(self.version)+'\n' s += 'Title: ' + self.title + '\n' s += 'ctime: ' + str(self.ctime) + '\n' s += 'mtime: ' + str(self.mtime) + '\n' for i,p in enumerate(self.page_uuids): s += 'page ' + str(i) + ': ' + p + '\n' return s
[docs]class QuillImporter(ImporterBase): def __init__(self, quill_filename): self._filename = quill_filename with tarfile.open(self._filename, "r") as t: self._open_quill_archive(t) def _open_quill_archive(self, t): index_files = [ t.extractfile(f) for f in t.getmembers() if f.name.endswith('index.quill_data') ] if len(index_files) != 1: raise QuillImporterError('Not a Quill file') self._index = q = QuillIndex(index_files[0]) notebook_dir = os.path.split(index_files[0].name)[0] self._page_filenames = [notebook_dir+'/page_'+page_uuid+'.quill_data' for page_uuid in q.page_uuids ]
[docs] def uuid(self): return self._index.uuid
[docs] def title(self): return self._index.title
[docs] def mtime_millis(self): return self._index.mtime[0]
[docs] def ctime_millis(self): return self._index.ctime[0]
[docs] def n_pages(self): return self._index.npages[0]
[docs] def get_page(self, n): """ Return the n-th page. """ page_filename = self._page_filenames[n] with tarfile.open(self._filename, "r") as t: page_file = t.extractfile(page_filename) qp = QuillPage(page_file, n) from quill.page import Page return Page(n, qp.uuid, qp.aspect_ratio, qp.strokes, qp.images)