diff --git a/.pydevproject b/.pydevproject index ce1227533e..a8ae13b3c5 100644 --- a/.pydevproject +++ b/.pydevproject @@ -5,5 +5,6 @@ python 2.5 /libprs500/src +/libprs500/libprs500.lrf.txt diff --git a/setup.py b/setup.py index 38be1c81f6..bb15d6ad30 100644 --- a/setup.py +++ b/setup.py @@ -33,7 +33,10 @@ if sys.argv[1] == 'py2exe': f.close() try: import py2exe - console = [{'script' : 'src/libprs500/cli/main.py', 'dest_base':'prs500'}] + console = [{ + 'script' : 'src/libprs500/cli/main.py', 'dest_base':'prs500', + 'script' : 'src/libprs500/lrf/html/convert_from.py', 'dest_base':'html2lrf' + }] windows = [{'script' : 'src/libprs500/gui/main.py', 'dest_base':'prs500-gui', 'icon_resources':[(1,'icons/library.ico')]}] excludes = ["Tkconstants", "Tkinter", "tcl", "_imagingtk", @@ -94,9 +97,8 @@ setup( 'prs500 = libprs500.cli.main:main', \ 'lrf-meta = libprs500.lrf.meta:main', \ 'rtf-meta = libprs500.metadata.rtf:main', \ - 'makelrf = libprs500.lrf.makelrf:main', \ 'txt2lrf = libprs500.lrf.makelrf:txt', \ - 'html2lrf = libprs500.lrf.makelrf:html',\ + 'html2lrf = libprs500.lrf.html.convert_from:main',\ ], 'gui_scripts' : [ 'prs500-gui = libprs500.gui.main:main'] }, diff --git a/src/libprs500/lrf/BBeBook-0.2.jar b/src/libprs500/lrf/BBeBook-0.2.jar deleted file mode 100644 index 1363a8bc1e..0000000000 Binary files a/src/libprs500/lrf/BBeBook-0.2.jar and /dev/null differ diff --git a/src/libprs500/lrf/cover.jpg b/src/libprs500/lrf/cover.jpg deleted file mode 100644 index 34f9a351d6..0000000000 Binary files a/src/libprs500/lrf/cover.jpg and /dev/null differ diff --git a/src/libprs500/lrf/html/convert.py b/src/libprs500/lrf/html/convert_from.py similarity index 80% rename from src/libprs500/lrf/html/convert.py rename to src/libprs500/lrf/html/convert_from.py index 1b527a4877..5aacd49820 100644 --- a/src/libprs500/lrf/html/convert.py +++ b/src/libprs500/lrf/html/convert_from.py @@ -19,10 +19,10 @@ Code to convert HTML ebooks into LRF ebooks. """ import os, re, sys from htmlentitydefs import name2codepoint - +from optparse import OptionParser from libprs500.lrf.html.BeautifulSoup import BeautifulSoup, Comment, Tag, NavigableString -from libprs500.lrf.pylrs.pylrs import Book, Page, Paragraph, TextBlock, CR +from libprs500.lrf.pylrs.pylrs import Book, Page, Paragraph, TextBlock, CR, Italic from libprs500.lrf.pylrs.pylrs import Span as _Span from libprs500.lrf import ConversionError @@ -40,7 +40,7 @@ class Span(_Span): (an int) if successful. Otherwise, returns None. Assumes: 1 pixel is 1/4 mm. One em is 10pts """ - m = re.match("\s*([0-9]*\.?[0-9]*)\s*(%|em|px|mm|cm|in|pt|pc)", val) + m = re.match("\s*(-*[0-9]*\.?[0-9]*)\s*(%|em|px|mm|cm|in|pt|pc)", val) if m is not None: unit = float(m.group(1)) if m.group(2) == '%': @@ -160,6 +160,10 @@ class Span(_Span): src = pat.sub(repl, src) if not src: raise ConversionError('No point in adding an empty string') + if 'font-style' in css.keys(): + fs = css.pop('font-style') + if fs.lower() == 'italic': + src = Italic(src) attrs = Span.translate_attrs(css) _Span.__init__(self, text=src, **attrs) @@ -227,6 +231,13 @@ class HTMLConvertor(object): """ Return a dictionary of style properties applicable to Tag tag. """ + def merge_parent_css(prop, pcss): + temp = {} + for key in pcss.keys(): + if key.lower().startswith('font'): + temp[key] = pcss[key] + prop.update(temp) + prop = dict() if tag.has_key("align"): prop["text-align"] = tag["align"] @@ -238,7 +249,7 @@ class HTMLConvertor(object): if self.css.has_key(classname): prop.update(self.css[classname]) if parent_css: - prop.update(parent_css) + merge_parent_css(prop, parent_css) if tag.has_key("style"): prop.update(self.parse_style_properties(tag["style"])) return prop @@ -256,22 +267,52 @@ class HTMLConvertor(object): self.current_page.append(self.current_block) if self.current_page: self.book.append(self.current_page) + + def end_page(self): + self.current_block.append(self.current_para) + self.current_para = Paragraph() + self.current_page.append(self.current_block) + self.current_block = TextBlock() + self.book.append(self.current_page) + self.current_page = Page() def parse_tag(self, tag, parent_css): + def sanctify_css(css): + """ Make css safe for use in a SPAM Xylog tag """ + for key in css.keys(): + test = key.lower() + if test.startswith('margin') or 'indent' in test or \ + 'padding' in test or 'border' in test or test in \ + ['color', 'display', 'text-decoration', 'letter-spacing']: + css.pop(key) + return css + def add_text(tag, css): try: - self.current_para.append(Span(tag, css)) + self.current_para.append(Span(tag, sanctify_css(css))) except ConversionError, err: if self.verbose: print >>sys.stderr, err + + def process_text_tag(tag, pcss): + if 'page-break-before' in pcss.keys(): + if pcss['page-break-before'].lower() != 'avoid': + self.end_page() + pcss.pop('page-break-before') + end_page = False + if 'page-break-after' in pcss.keys(): + end_page = True + pcss.pop('page-break-after') for c in tag.contents: if isinstance(tag, NavigableString): add_text(tag, pcss) else: self.parse_tag(c, pcss) + if end_page: + self.end_page() try: tagname = tag.name.lower() @@ -280,8 +321,17 @@ class HTMLConvertor(object): return if tagname in ["title", "script", "meta"]: pass + elif tagname in ['style', 'link']: + # TODO: Append CSS to self.css + pass elif tagname == 'p': css = self.tag_css(tag, parent_css=parent_css) + indent = css.pop('text-indent', '') + if indent: + # TODO: If indent is different from current textblock's parindent + # start a new TextBlock + pass + self.current_para.CR() # Put a paragraph end self.current_block.append(self.current_para) self.current_para = Paragraph() process_text_tag(tag, css) @@ -302,13 +352,14 @@ class HTMLConvertor(object): self.current_para = Paragraph() self.current_page = Page() else: + css = self.tag_css(tag, parent_css=parent_css) for c in tag.contents: if isinstance(c, Comment): continue elif isinstance(c, Tag): - self.parse_tag(c) + self.parse_tag(c, css) elif isinstance(c, NavigableString): - add_text(c, parent_css) + add_text(c, css) def writeto(self, path): if path.lower().endswith('lrs'): @@ -327,8 +378,33 @@ def process_file(path, options): book = Book(title=options.title, author=options.author, \ sourceencoding='utf8') conv = HTMLConvertor(book, soup) - name = os.path.splitext(os.path.basename(path))[0]+'.lrs' + name = os.path.splitext(os.path.basename(path))[0]+'.lrf' os.chdir(cwd) conv.writeto(name) finally: - os.chdir(cwd) \ No newline at end of file + os.chdir(cwd) + +def main(): + """ CLI for html -> lrf conversions """ + parser = OptionParser(usage=\ + """usage: %prog [options] mybook.txt + + %prog converts mybook.txt to mybook.lrf + """\ + ) + parser.add_option("-t", "--title", action="store", type="string", \ + dest="title", help="Set the title") + parser.add_option("-a", "--author", action="store", type="string", \ + dest="author", help="Set the author", default='Unknown') + options, args = parser.parse_args() + if len(args) != 1: + parser.print_help() + sys.exit(1) + src = args[0] + if options.title == None: + options.title = os.path.splitext(os.path.basename(src))[0] + process_file(src, options) + + +if __name__ == '__main__': + main() \ No newline at end of file diff --git a/src/libprs500/lrf/libtidy.py b/src/libprs500/lrf/libtidy.py deleted file mode 100644 index e41c80bebc..0000000000 --- a/src/libprs500/lrf/libtidy.py +++ /dev/null @@ -1,266 +0,0 @@ -## Copyright (C) 2006 Kovid Goyal kovid@kovidgoyal.net -## This program is free software; you can redistribute it and/or modify -## it under the terms of the GNU General Public License as published by -## the Free Software Foundation; either version 2 of the License, or -## (at your option) any later version. -## -## This program is distributed in the hope that it will be useful, -## but WITHOUT ANY WARRANTY; without even the implied warranty of -## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -## GNU General Public License for more details. -## -## You should have received a copy of the GNU General Public License along -## with this program; if not, write to the Free Software Foundation, Inc., -## 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. - -""" -Thin ctypes based wrapper around libtidy. Example usage: ->>> from libtidy import parseString ->>> print parseString('

fowehfow

', \ - output_xhtml=1, add_xml_decl=1, indent=1, tidy_mark=0) - - - - - - - -

- fowehfow -

- - -""" - -import ctypes -from cStringIO import StringIO -import weakref - -class TidyLibError(Exception): - def __init__(self, arg): - self.arg=arg - -class InvalidOptionError(TidyLibError): - def __str__(self): - return "%s was not a valid Tidy option." % (self.arg) - __repr__=__str__ - -class OptionArgError(TidyLibError): - def __init__(self, arg): - self.arg=arg - def __str__(self): - return self.arg - -# search the path for libtidy using the known names; -thelib=None -for libname in ('cygtidy-0-99-0', 'libtidy', 'libtidy.so', 'tidylib'): - try: - thelib = getattr(ctypes.cdll, libname) - break - except OSError: - pass -if not thelib: - raise OSError("Couldn't find libtidy, please make sure it is installed.") - -class Loader: - """ - I am a trivial wrapper that eliminates the need for tidy.tidyFoo, - so you can just access tidy.Foo - """ - def __init__(self): - self.lib = thelib - def __getattr__(self, name): - try: - return getattr(self.lib, "tidy%s" % name) - # current ctypes uses ValueError, future will use AttributeError - except (ValueError, AttributeError): - return getattr(self.lib, name) - -_tidy=Loader() - -# define a callback to pass to Tidylib -def _putByte(handle, c): - """Lookup sink by handle and call its putByte method""" - sinkfactory[handle].putByte(c) - return 0 - -PUTBYTEFUNC = ctypes.CFUNCTYPE(ctypes.c_int, ctypes.c_int, ctypes.c_char) -putByte = PUTBYTEFUNC(_putByte) - -class _OutputSink(ctypes.Structure): - _fields_ = [("sinkData", ctypes.c_int), - ("putByte", PUTBYTEFUNC), - ] - -class _Sink: - def __init__(self): - self._data = StringIO() - self.struct = _OutputSink() - self.struct.putByte = putByte - - def putByte(self, c): - self._data.write(c) - - def __str__(self): - return self._data.getvalue() - -class ReportItem: - def __init__(self, err): - self.err = err - if err.startswith('line'): - tokens = err.split(' ',6) - self.severity = tokens[5][0] # W or E - self.line = int(tokens[1]) - self.col = int(tokens[3]) - self.message = tokens[6] - else: - tokens = err.split(' ',1) - self.severity = tokens[0][0] - self.message = tokens[1] - self.line = None - self.col = None - # TODO - parse emacs mode - - def __str__(self): - severities = dict(W='Warning', E='Error', C='Config') - try: - if self.line: - return "line %d col %d - %s: %s" % (self.line, self.col, - severities[self.severity], - self.message) - - else: - return "%s: %s" % (severities[self.severity], self.message) - except KeyError: - return self.err - - def __repr__(self): - return "%s('%s')" % (self.__class__.__name__, - str(self).replace("'", "\\'")) - -class FactoryDict(dict): - """I am a dict with a create method and no __setitem__. This allows - me to control my own keys. - """ - def create(self): - """Subclasses should implement me to generate a new item""" - - def _setitem(self, name, value): - dict.__setitem__(self, name, value) - - def __setitem__(self, name, value): - raise TypeError, "Use create() to get a new object" - - -class SinkFactory(FactoryDict): - """Mapping for lookup of sinks by handle""" - def __init__(self): - FactoryDict.__init__(self) - self.lastsink = 0 - - def create(self): - sink = _Sink() - sink.struct.sinkData = self.lastsink - FactoryDict._setitem(self, self.lastsink, sink) - self.lastsink = self.lastsink+1 - return sink - -sinkfactory = SinkFactory() - -class _Document(object): - def __init__(self): - self.cdoc = _tidy.Create() - self.errsink = sinkfactory.create() - _tidy.SetErrorSink(self.cdoc, ctypes.byref(self.errsink.struct)) - - def write(self, stream): - stream.write(str(self)) - - def get_errors(self): - ret = [] - for line in str(self.errsink).split('\n'): - line = line.strip(' \n\r') - if line: ret.append(ReportItem(line)) - return ret - - errors=property(get_errors) - - def __str__(self): - stlen = ctypes.c_int(8192) - st = ctypes.c_buffer(stlen.value) - rc = _tidy.SaveString(self.cdoc, st, ctypes.byref(stlen)) - if rc==-12: # buffer too small - st = ctypes.c_buffer(stlen.value) - _tidy.SaveString(self.cdoc, st, ctypes.byref(stlen)) - return st.value - -errors = {'missing or malformed argument for option: ': OptionArgError, - 'unknown option: ': InvalidOptionError, - } - - -class DocumentFactory(FactoryDict): - def _setOptions(self, doc, **options): - for k in options.keys(): - - # this will flush out most argument type errors... - if options[k] is None: options[k] = '' - - _tidy.OptParseValue(doc.cdoc, - k.replace('_', '-'), - str(options[k])) - if doc.errors: - match=filter(doc.errors[-1].message.startswith, errors.keys()) - if match: - raise errors[match[0]](doc.errors[-1].message) - - def load(self, doc, arg, loader): - loader(doc.cdoc, arg) - _tidy.CleanAndRepair(doc.cdoc) - - def loadFile(self, doc, filename): - self.load(doc, filename, _tidy.ParseFile) - - def loadString(self, doc, st): - self.load(doc, st, _tidy.ParseString) - - def _create(self, *args, **kwargs): - doc = _Document() - self._setOptions(doc, **kwargs) - ref = weakref.ref(doc, self.releaseDoc) - FactoryDict._setitem(self, ref, doc.cdoc) - return doc - - def parse(self, filename, *args, **kwargs): - """ - Open and process filename as an HTML file, returning a - processed document object. - @param kwargs: named options to pass to TidyLib for processing - the input file. - @param filename: the name of a file to process - @return: a document object - """ - doc = self._create(**kwargs) - self.loadFile(doc, filename) - return doc - - def parseString(self, st, *args, **kwargs): - """ - Use st as an HTML file, and process it, returning a - document object. - @param kwargs: named options to pass to TidyLib for processing - the input file. - @param st: the string to parse - @return: a document object - """ - doc = self._create(**kwargs) - self.loadString(doc, st) - return doc - - def releaseDoc(self, ref): - _tidy.Release(self[ref]) - -docfactory = DocumentFactory() -parse = docfactory.parse -parseString = docfactory.parseString \ No newline at end of file diff --git a/src/libprs500/lrf/makelrf.py b/src/libprs500/lrf/makelrf.py index 3f14532837..0b9db5544b 100755 --- a/src/libprs500/lrf/makelrf.py +++ b/src/libprs500/lrf/makelrf.py @@ -17,19 +17,14 @@ import shutil import sys import hashlib import re -import time import pkg_resources import subprocess from tempfile import mkdtemp from optparse import OptionParser -import xml.dom.minidom as dom - from libprs500.lrf import ConversionError from libprs500.lrf.meta import LRFException, LRFMetaFile from libprs500.ptempfile import PersistentTemporaryFile -_bbebook = 'BBeBook-0.2.jar' - def generate_thumbnail(path): """ Generate a JPEG thumbnail of size ~ 128x128 (aspect ratio preserved)""" try: @@ -45,30 +40,6 @@ def generate_thumbnail(path): im.save(thumb.name) return thumb -def create_xml(cfg): - doc = dom.getDOMImplementation().createDocument(None, None, None) - def add_field(parent, tag, value): - elem = doc.createElement(tag) - elem.appendChild(doc.createTextNode(value)) - parent.appendChild(elem) - - info = doc.createElement('Info') - info.setAttribute('version', '1.0') - book_info = doc.createElement('BookInfo') - doc_info = doc.createElement('DocInfo') - info.appendChild(book_info) - info.appendChild(doc_info) - add_field(book_info, 'File', cfg['File']) - add_field(doc_info, 'Output', cfg['Output']) - for field in ['Title', 'Author', 'BookID', 'Publisher', 'Label', \ - 'Category', 'Classification', 'Icon', 'Cover', 'FreeText']: - if cfg.has_key(field): - add_field(book_info, field, cfg[field]) - add_field(doc_info, 'Language', 'en') - add_field(doc_info, 'Creator', _bbebook) - add_field(doc_info, 'CreationDate', time.strftime('%Y-%m-%d', time.gmtime())) - doc.appendChild(info) - return doc.toxml() def makelrf(author=None, title=None, \ thumbnail=None, src=None, odir=".",\ @@ -150,127 +121,3 @@ def makelrf(author=None, title=None, \ if dirpath: shutil.rmtree(dirpath, True) -def txt(): - """ CLI for txt -> lrf conversions """ - parser = OptionParser(usage=\ - """usage: %prog [options] mybook.txt - - %prog converts mybook.txt to mybook.lrf - """\ - ) - parser.add_option("-t", "--title", action="store", type="string", \ - dest="title", help="Set the title") - parser.add_option("-a", "--author", action="store", type="string", \ - dest="author", help="Set the author", default='Unknown') - defenc = 'cp1252' - enchelp = 'Set the encoding used to decode ' + \ - 'the text in mybook.txt. Default encoding is ' + defenc - parser.add_option('-e', '--encoding', action='store', type='string', \ - dest='encoding', help=enchelp, default=defenc) - options, args = parser.parse_args() - if len(args) != 1: - parser.print_help() - sys.exit(1) - src = args[0] - if options.title == None: - options.title = os.path.splitext(os.path.basename(src))[0] - try: - convert_txt(src, options) - except ConversionError, err: - print >>sys.stderr, err - sys.exit(1) - - -def convert_txt(path, options): - """ - Convert the text file at C{path} into an lrf file. - @param options: Object with the following attributes: - C{author}, C{title}, C{encoding} (the assumed encoding of - the text in C{path}.) - """ - import fileinput - from libprs500.lrf.pylrs.pylrs import Book - book = Book(title=options.title, author=options.author, \ - sourceencoding=options.encoding) - buffer = '' - block = book.Page().TextBlock() - for line in fileinput.input(path): - line = line.strip() - if line: - buffer += line - else: - block.Paragraph(buffer) - buffer = '' - basename = os.path.basename(path) - name = os.path.splitext(basename)[0]+'.lrf' - try: - book.renderLrf(name) - except UnicodeDecodeError: - raise ConversionError(path + ' is not encoded in ' + \ - options.encoding +'. Specify the '+ \ - 'correct encoding with the -e option.') - return os.path.abspath(name) - - -def html(): - """ CLI for html -> lrf conversions """ - parser = OptionParser(usage=\ - """usage: %prog [options] mybook.txt - - %prog converts mybook.txt to mybook.lrf - """\ - ) - parser.add_option("-t", "--title", action="store", type="string", \ - dest="title", help="Set the title") - parser.add_option("-a", "--author", action="store", type="string", \ - dest="author", help="Set the author", default='Unknown') - options, args = parser.parse_args() - if len(args) != 1: - parser.print_help() - sys.exit(1) - src = args[0] - if options.title == None: - options.title = os.path.splitext(os.path.basename(src))[0] - from libprs500.lrf.html.convert import process_file - process_file(src, options) - -def main(cargs=None): - parser = OptionParser(usage=\ - """usage: %prog [options] mybook.[html|pdf|rar] - - %prog converts mybook to mybook.lrf - If you specify a rar file you must have the unrar command line client - installed. makelrf assumes the rar file is an archive containing the - html file you want converted."""\ - ) - - parser.add_option("-t", "--title", action="store", type="string", \ - dest="title", help="Set the book title") - parser.add_option("-a", "--author", action="store", type="string", \ - dest="author", help="Set the author") - parser.add_option('-r', '--rasterize', action='store_false', \ - dest="rasterize", - help="Convert pdfs into image files.") - parser.add_option('-c', '--cover', action='store', dest='cover',\ - help="Path to a graphic that will be set as the cover. "\ - "If it is specified the thumbnail is automatically "\ - "generated from it") - parser.add_option("--thumbnail", action="store", type="string", \ - dest="thumbnail", \ - help="Path to a graphic that will be set as the thumbnail") - if not cargs: - cargs = sys.argv - options, args = parser.parse_args() - if len(args) != 1: - parser.print_help() - sys.exit(1) - src = args[0] - root, ext = os.path.splitext(src) - if ext not in ['.html', '.pdf', '.rar']: - print >> sys.stderr, "Can only convert files ending in .html|.pdf|.rar" - parser.print_help() - sys.exit(1) - name = makelrf(author=options.author, title=options.title, \ - thumbnail=options.thumbnail, src=src, cover=options.cover, \ - rasterize=options.rasterize) - print "LRF generated:", name diff --git a/src/libprs500/lrf/txt/__init__.py b/src/libprs500/lrf/txt/__init__.py new file mode 100644 index 0000000000..5586282c18 --- /dev/null +++ b/src/libprs500/lrf/txt/__init__.py @@ -0,0 +1,14 @@ +## Copyright (C) 2006 Kovid Goyal kovid@kovidgoyal.net +## This program is free software; you can redistribute it and/or modify +## it under the terms of the GNU General Public License as published by +## the Free Software Foundation; either version 2 of the License, or +## (at your option) any later version. +## +## This program is distributed in the hope that it will be useful, +## but WITHOUT ANY WARRANTY; without even the implied warranty of +## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +## GNU General Public License for more details. +## +## You should have received a copy of the GNU General Public License along +## with this program; if not, write to the Free Software Foundation, Inc., +## 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. \ No newline at end of file diff --git a/src/libprs500/lrf/txt/convert_from.py b/src/libprs500/lrf/txt/convert_from.py new file mode 100644 index 0000000000..19a6f8598f --- /dev/null +++ b/src/libprs500/lrf/txt/convert_from.py @@ -0,0 +1,86 @@ +## Copyright (C) 2006 Kovid Goyal kovid@kovidgoyal.net +## This program is free software; you can redistribute it and/or modify +## it under the terms of the GNU General Public License as published by +## the Free Software Foundation; either version 2 of the License, or +## (at your option) any later version. +## +## This program is distributed in the hope that it will be useful, +## but WITHOUT ANY WARRANTY; without even the implied warranty of +## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +## GNU General Public License for more details. +## +## You should have received a copy of the GNU General Public License along +## with this program; if not, write to the Free Software Foundation, Inc., +## 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +""" +Convert .txt files to .lrf +""" +import os, sys +from optparse import OptionParser + +from libprs500.lrf import ConversionError + +def main(): + """ CLI for txt -> lrf conversions """ + parser = OptionParser(usage=\ + """usage: %prog [options] mybook.txt + + %prog converts mybook.txt to mybook.lrf + """\ + ) + parser.add_option("-t", "--title", action="store", type="string", \ + dest="title", help="Set the title") + parser.add_option("-a", "--author", action="store", type="string", \ + dest="author", help="Set the author", default='Unknown') + defenc = 'cp1252' + enchelp = 'Set the encoding used to decode ' + \ + 'the text in mybook.txt. Default encoding is ' + defenc + parser.add_option('-e', '--encoding', action='store', type='string', \ + dest='encoding', help=enchelp, default=defenc) + options, args = parser.parse_args() + if len(args) != 1: + parser.print_help() + sys.exit(1) + src = args[0] + if options.title == None: + options.title = os.path.splitext(os.path.basename(src))[0] + try: + convert_txt(src, options) + except ConversionError, err: + print >>sys.stderr, err + sys.exit(1) + + +def convert_txt(path, options): + """ + Convert the text file at C{path} into an lrf file. + @param options: Object with the following attributes: + C{author}, C{title}, C{encoding} (the assumed encoding of + the text in C{path}.) + """ + import fileinput + from libprs500.lrf.pylrs.pylrs import Book + book = Book(title=options.title, author=options.author, \ + sourceencoding=options.encoding) + buffer = '' + block = book.Page().TextBlock() + for line in fileinput.input(path): + line = line.strip() + if line: + buffer += line + else: + block.Paragraph(buffer) + buffer = '' + basename = os.path.basename(path) + name = os.path.splitext(basename)[0]+'.lrf' + try: + book.renderLrf(name) + except UnicodeDecodeError: + raise ConversionError(path + ' is not encoded in ' + \ + options.encoding +'. Specify the '+ \ + 'correct encoding with the -e option.') + return os.path.abspath(name) + + +if __name__ == '__main__': + main() \ No newline at end of file