diff --git a/setup.py b/setup.py index 5302fa9bc3..de0f292d8d 100644 --- a/setup.py +++ b/setup.py @@ -20,23 +20,23 @@ from libprs500 import __appname__ as APPNAME entry_points = { 'console_scripts': [ \ - 'prs500 = libprs500.devices.prs500.cli.main:main', \ - 'lrf-meta = libprs500.ebooks.lrf.meta:main', \ - 'rtf-meta = libprs500.ebooks.metadata.rtf:main', \ - 'pdf-meta = libprs500.ebooks.metadata.pdf:main', \ - 'txt2lrf = libprs500.ebooks.lrf.txt.convert_from:main', \ - 'html2lrf = libprs500.ebooks.lrf.html.convert_from:main',\ - 'markdown = libprs500.ebooks.markdown.markdown:main',\ - 'lit2lrf = libprs500.ebooks.lrf.lit.convert_from:main',\ - 'rtf2lrf = libprs500.ebooks.lrf.rtf.convert_from:main',\ - 'web2disk = libprs500.web.fetch.simple:main',\ - 'web2lrf = libprs500.ebooks.lrf.web.convert_from:main',\ - 'pdf2lrf = libprs500.ebooks.lrf.pdf.convert_from:main',\ - 'any2lrf = libprs500.ebooks.lrf.any.convert_from:main',\ - 'lrf2lrs = libprs500.ebooks.lrf.parser:main',\ - 'libprs500-beta = libprs500.gui2.main:main',\ + 'prs500 = libprs500.devices.prs500.cli.main:main', + 'lrf-meta = libprs500.ebooks.lrf.meta:main', + 'rtf-meta = libprs500.ebooks.metadata.rtf:main', + 'pdf-meta = libprs500.ebooks.metadata.pdf:main', + 'txt2lrf = libprs500.ebooks.lrf.txt.convert_from:main', + 'html2lrf = libprs500.ebooks.lrf.html.convert_from:main', + 'markdown = libprs500.ebooks.markdown.markdown:main', + 'lit2lrf = libprs500.ebooks.lrf.lit.convert_from:main', + 'rtf2lrf = libprs500.ebooks.lrf.rtf.convert_from:main', + 'web2disk = libprs500.web.fetch.simple:main', + 'web2lrf = libprs500.ebooks.lrf.web.convert_from:main', + 'pdf2lrf = libprs500.ebooks.lrf.pdf.convert_from:main', + 'any2lrf = libprs500.ebooks.lrf.any.convert_from:main', + 'lrf2lrs = libprs500.ebooks.lrf.parser:main', + 'lrfviewer = libprs500.gui2.lrf_renderer.main:main', ], - 'gui_scripts' : [ APPNAME+' = libprs500.gui.main:main'] + 'gui_scripts' : [ APPNAME+' = libprs500.gui2.main:main'] } def _ep_to_script(ep, base='src'): diff --git a/src/libprs500/ebooks/lrf/objects.py b/src/libprs500/ebooks/lrf/objects.py index daf1a7244a..9c931fef59 100644 --- a/src/libprs500/ebooks/lrf/objects.py +++ b/src/libprs500/ebooks/lrf/objects.py @@ -17,21 +17,21 @@ import struct, array, zlib, cStringIO from libprs500.ebooks.lrf import LRFParseError from libprs500.ebooks.lrf.tags import Tag -class LRFObject(object): - - tag_map = { - 0xF500: ['', ''], - 0xF502: ['infoLink', 'D'], - 0xF501: ['', ''], - - # Ruby tags +ruby_tags = { 0xF575: ['rubyAlignAndAdjust', 'W'], 0xF576: ['rubyoverhang', 'W', {0: 'none', 1:'auto'}], 0xF577: ['empdotsposition', 'W', {1: 'before', 2:'after'}], 0xF578: ['','parse_empdots'], 0xF579: ['emplineposition', 'W', {1: 'before', 2:'after'}], 0xF57A: ['emplinetype', 'W', {0: 'none', 0x10: 'solid', 0x20: 'dashed', 0x30: 'double', 0x40: 'dotted'}] +} +class LRFObject(object): + + tag_map = { + 0xF500: ['', ''], + 0xF502: ['infoLink', 'D'], + 0xF501: ['', ''], } @classmethod @@ -99,7 +99,7 @@ class LRFObject(object): return unicode(self.__class__.__name__) def __str__(self): - return unicode(self) + return unicode(self).encode('utf-8') class LRFContentObject(LRFObject): @@ -199,14 +199,27 @@ class PageTree(LRFObject): class StyleObject(object): - def __unicode__(self): - s = '<%s objid="%s" stylelabel="%s" '%(self.__class__.__name__.replace('Attr', 'Style'), self.id, self.id) + def _tags_to_xml(self): + s = u'' for h in self.tag_map.values(): attr = h[0] if hasattr(self, attr): - s += '%s="%s" '%(attr, getattr(self, attr)) - s += '/>\n' + s += u'%s="%s" '%(attr, getattr(self, attr)) return s + + def __unicode__(self): + s = u'<%s objid="%s" stylelabel="%s" '%(self.__class__.__name__.replace('Attr', 'Style'), self.id, self.id) + s += self._tags_to_xml() + s += u'/>\n' + return s + + def as_dict(self): + d = {} + for h in self.tag_map.values(): + attr = h[0] + if hasattr(self, attr): + d[attr] = getattr(self, attr) + return d class PageAttr(StyleObject, LRFObject): tag_map = { @@ -234,13 +247,20 @@ class PageAttr(StyleObject, LRFObject): class Color(object): def __init__(self, val): - self.b, self.g, self.r, self.a = val & 0xFF, (val>>8)&0xFF, (val>>16)&0xFF, (val>>24)&0xFF + self.a, self.r, self.g, self.b = val & 0xFF, (val>>8)&0xFF, (val>>16)&0xFF, (val>>24)&0xFF def __unicode__(self): return u'0x%02x%02x%02x%02x'%(self.a, self.r, self.g, self.b) def __str__(self): return unicode(self) + + def __len__(self): + return 4 + + def __getitem__(self, i): # Qt compatible ordering and values + return (self.r, self.g, self.b, 0xff-self.a)[i] # In Qt 0xff is opaque while in LRS 0x00 is opaque + class EmptyPageElement(object): def __iter__(self): @@ -310,6 +330,11 @@ class Page(LRFStream): } tag_map.update(PageAttr.tag_map) tag_map.update(LRFStream.tag_map) + style = property(fget=lambda self : self._document.objects[self.style_id]) + evenheader = property(fget=lambda self : self._document.objects[self.style.evenheaderid]) + evenfooter = property(fget=lambda self : self._document.objects[self.style.evenfooterid]) + oddheader = property(fget=lambda self : self._document.objects[self.style.oddheaderid]) + oddfooter = property(fget=lambda self : self._document.objects[self.style.oddfooterid]) class Content(LRFContentObject): tag_map = { @@ -372,11 +397,13 @@ class Page(LRFStream): if hasattr(self, 'xspace'): delattr(self, 'xspace') if hasattr(self, 'yspace'): delattr(self, 'yspace') - @apply - def style(): - def fget(self): - return self._document.objects[self.style_id] - return property(fget=fget) + def header(self, odd): + id = self._document.objects[self.style_id].oddheaderid if odd else self._document.objects[self.style_id].evenheaderid + return self._document.objects[id] + + def footer(self, odd): + id = self._document.objects[self.style_id].oddfooterid if odd else self._document.objects[self.style_id].evenfooterid + return self._document.objects[id] def initialize(self): self.content = Page.Content(self.stream, self._document.objects) @@ -437,6 +464,7 @@ class TextAttr(StyleObject, LRFObject): 0xF5F1: ['textlinewidth', 'W'], 0xF5F2: ['linecolor', 'D', Color], } + tag_map.update(ruby_tags) tag_map.update(LRFObject.tag_map) @@ -450,17 +478,8 @@ class Block(LRFStream): extra_attrs = [i[0] for i in BlockAttr.tag_map.values()] extra_attrs.extend([i[0] for i in TextAttr.tag_map.values()]) - @apply - def style(): - def fget(self): - return self._document.objects[self.style_id] - return property(fget=fget) - - @apply - def textstyle(): - def fget(self): - return self._document.objects[self.textstyle_id] - return property(fget=fget) + style = property(fget=lambda self : self._document.objects[self.style_id]) + textstyle = property(fget=lambda self : self._document.objects[self.textstyle_id]) def initialize(self): self.attrs = {} @@ -527,11 +546,7 @@ class Text(LRFStream): tag_map.update(TextAttr.tag_map) tag_map.update(LRFStream.tag_map) - @apply - def style(): - def fget(self): - return self._document.objects[self.style_id] - return property(fget=fget) + style = property(fget=lambda self : self._document.objects[self.style_id]) class Content(LRFContentObject): tag_map = { @@ -561,7 +576,7 @@ class Text(LRFStream): 0xF5BC: 'end_container', 0xF5BD: ['simple_container', 'EmpDots'], 0xF5BE: 'end_container', - 0xF5C1: ['simple_container', 'EmpLine'], + 0xF5C1: 'empline', 0xF5C2: 'end_container', 0xF5C3: 'draw_char', 0xF5C4: 'end_container', @@ -576,6 +591,7 @@ class Text(LRFStream): text_map = { 0x22: u'"', 0x26: u'&', 0x27: u''', 0x3c: u'<', 0x3e: u'>' } linetype_map = {0: 'none', 0x10: 'solid', 0x20: 'dashed', 0x30: 'double', 0x40: 'dotted'} adjustment_map = {1: 'top', 2: 'center', 3: 'baseline', 4: 'bottom'} + lineposition_map = {1:'before', 2:'after'} def __init__(self, bytes, objects, parent=None, name=None, attrs={}): self.parent = parent @@ -663,6 +679,37 @@ class Text(LRFStream): self._contents.append(Text.Content(self.stream, self.objects, parent=self, name='CharButton', attrs={'refobj':tag.dword})) + def empline(self, tag): + + def invalid(op): + self.stream.seek(op) + self.simple_container('EmpLine') + + oldpos = self.stream.tell() + try: + t = Tag(self.stream) + if t.id not in [0xF579, 0xF57A]: + raise LRFParseError + except LRFParseError: + invalid(oldpos) + return + h = TextAttr.tag_map[t.id] + attrs = {} + attrs[h[0]] = TextAttr.tag_to_val(h, None, t, None) + oldpos = self.stream.tell() + try: + t = Tag(self.stream) + if t.id not in [0xF579, 0xF57A]: + raise LRFParseError + h = TextAttr.tag_map[t.id] + attrs[h[0]] = TextAttr.tag_to_val(h, None, t, None) + except LRFParseError: + self.stream.seek(oldpos) + + cont = Text.Content(self.stream, self.objects, parent=self, + name='EmpLine', attrs=attrs) + self._contents.append(cont) + def space(self, tag): self._contents.append(Text.Content('', self.objects, parent=self, name='Space', attrs={'xsize':tag.sword})) @@ -737,17 +784,9 @@ class Image(LRFObject): def parse_image_size(self, tag, f): self.xsize, self.ysize = struct.unpack("\n'%\ @@ -755,8 +794,9 @@ class Image(LRFObject): class PutObj(EmptyPageElement): - def __init__(self, x, y, refobj): + def __init__(self, objects, x, y, refobj): self.x, self.y, self.refobj = x, y, refobj + self.object = objects[refobj] def __unicode__(self): return u''%(self.x, self.y, self.refobj) @@ -791,7 +831,7 @@ class Canvas(LRFStream): stream = cStringIO.StringIO(self.stream) while stream.tell() < len(self.stream): tag = Tag(stream) - self._contents.append(PutObj(*struct.unpack("\n'%(self.id, self.id) + s += u'\n'%(self._tags_to_xml(),) doc = self._document s += u'\n'%\ (self.binding_map[doc.binding], doc.dpi, doc.width, doc.height, doc.color_depth) diff --git a/src/libprs500/ebooks/lrf/parser.py b/src/libprs500/ebooks/lrf/parser.py index 752bdd47d0..0e2274939c 100644 --- a/src/libprs500/ebooks/lrf/parser.py +++ b/src/libprs500/ebooks/lrf/parser.py @@ -19,11 +19,13 @@ import sys, array, os, re, codecs, logging from libprs500 import __author__, __appname__, __version__, setup_cli_handlers from libprs500.ebooks.lrf.meta import LRFMetaFile from libprs500.ebooks.lrf.objects import get_object, PageTree, StyleObject, \ - Font, Text, TOCObject + Font, Text, TOCObject, BookAttr, ruby_tags class LRFDocument(LRFMetaFile): + class temp(object): pass + def __init__(self, stream): LRFMetaFile.__init__(self, stream) self.scramble_key = self.xor_key @@ -32,6 +34,17 @@ class LRFDocument(LRFMetaFile): self.image_map = {} self._parse_objects() self.toc = None + self.metadata = LRFDocument.temp() + for a in ('title', 'title_reading', 'author', 'author_reading', 'book_id', + 'classification', 'free_text', 'publisher', 'label', 'category'): + setattr(self.metadata, a, getattr(self, a)) + self.doc_info = LRFDocument.temp() + for a in ('thumbnail', 'language', 'creator', 'producer', 'page'): + setattr(self.doc_info, a, getattr(self, a)) + self.doc_info.thumbnail_extension = self.thumbail_extension() + self.device_info = LRFDocument.temp() + for a in ('dpi', 'width', 'height'): + setattr(self.device_info, a, getattr(self, a)) def _parse_objects(self): self.objects = {} @@ -53,6 +66,12 @@ class LRFDocument(LRFMetaFile): self.page_trees.append(obj) elif isinstance(obj, TOCObject): self.toc = obj + elif isinstance(obj, BookAttr): + self.ruby_tags = {} + for h in ruby_tags.values(): + attr = h[0] + if hasattr(obj, attr): + self.ruby_tags[attr] = getattr(obj, attr) def __iter__(self): for pt in self.page_trees: @@ -64,22 +83,22 @@ class LRFDocument(LRFMetaFile): def to_xml(self): bookinfo = u'\n\n\n' - bookinfo += u'%s\n'%(self.title_reading, self.title) - bookinfo += u'%s\n'%(self.author_reading, self.author) - bookinfo += u'%s\n'%(self.book_id,) - bookinfo += u'%s\n'%(self.publisher,) - bookinfo += u'\n'%(self.label,) - bookinfo += u'%s\n'%(self.category,) - bookinfo += u'%s\n'%(self.classification,) - bookinfo += u'%s\n\n\n'%(self.free_text,) - th = self.thumbnail + bookinfo += u'%s\n'%(self.metadata.title_reading, self.metadata.title) + bookinfo += u'%s\n'%(self.metadata.author_reading, self.metadata.author) + bookinfo += u'%s\n'%(self.metadata.book_id,) + bookinfo += u'%s\n'%(self.metadata.publisher,) + bookinfo += u'\n'%(self.metadata.label,) + bookinfo += u'%s\n'%(self.metadata.category,) + bookinfo += u'%s\n'%(self.metadata.classification,) + bookinfo += u'%s\n\n\n'%(self.metadata.free_text,) + th = self.doc_info.thumbnail if th: - bookinfo += u'\n'%(self.title+'_thumbnail.'+self.thumbail_extension(),) - open(self.title+'_thumbnail.'+self.thumbail_extension(), 'wb').write(th) - bookinfo += u'%s\n'%(self.language,) - bookinfo += u'%s\n'%(self.creator,) - bookinfo += u'%s\n'%(self.producer,) - bookinfo += u'%s\n\n\n\n'%(self.page,) + bookinfo += u'\n'%(self.metadata.title+'_thumbnail.'+self.doc_info.thumbail_extension(),) + open(self.metadata.title+'_thumbnail.'+self.doc_info.thumbail_extension(), 'wb').write(th) + bookinfo += u'%s\n'%(self.doc_info.language,) + bookinfo += u'%s\n'%(self.doc_info.creator,) + bookinfo += u'%s\n'%(self.doc_info.producer,) + bookinfo += u'%s\n\n\n\n'%(self.doc_info.page,) pages = u'' done_main = False pt_id = -1 @@ -112,13 +131,16 @@ class LRFDocument(LRFMetaFile): self.write_files() return '\n' + bookinfo + pages + styles + objects + '' - -def main(args=sys.argv, logger=None): +def option_parser(): from optparse import OptionParser parser = OptionParser(usage='%prog book.lrf', epilog='Created by '+__author__, version=__appname__ + ' ' + __version__) parser.add_option('--output', '-o', default=None, help='Output LRS file', dest='out') parser.add_option('--verbose', default=False, action='store_true', dest='verbose') + return parser + +def main(args=sys.argv, logger=None): + parser = option_parser() opts, args = parser.parse_args(args) if logger is None: level = logging.DEBUG if opts.verbose else logging.INFO