def transform_chunk(chunk_xml, chunk_no, annotations, empty=False, _empty_html_static=[]): """ transforms one chunk, returns a HTML string, a TOC object and a set of used characters """ toc = TOC() for element in chunk_xml[0]: if element.tag == "naglowek_czesc": toc.add(node_name(element), "part%d.html#book-text" % chunk_no) elif element.tag in ("naglowek_rozdzial", "naglowek_akt", "srodtytul"): toc.add(node_name(element), "part%d.html" % chunk_no) elif element.tag in ('naglowek_podrozdzial', 'naglowek_scena'): subnumber = toc.add(node_name(element), "part%d.html" % chunk_no, level=1, is_part=False) element.set('sub', str(subnumber)) if empty: if not _empty_html_static: _empty_html_static.append(open(get_resource('epub/emptyChunk.html')).read()) chars = set() output_html = _empty_html_static[0] else: find_annotations(annotations, chunk_xml, chunk_no) replace_by_verse(chunk_xml) html_tree = xslt(chunk_xml, get_resource('epub/xsltScheme.xsl')) chars = used_chars(html_tree.getroot()) output_html = etree.tostring( html_tree, pretty_print=True, xml_declaration=True, encoding="utf-8", doctype='<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN" ' + '"http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">' ) return output_html, toc, chars
class PrestigioCover(Cover): width = 580 height = 783 background_img = get_resource('res/cover-prestigio.png') author_top = 446 author_margin_left = 118 author_margin_right = 62 author_lineskip = 60 author_color = '#fff' author_shadow = '#000' author_font_ttf = get_resource('fonts/JunicodeWL-Italic.ttf') author_font_size = 50 title_top = 0 title_margin_left = 118 title_margin_right = 62 title_lineskip = 60 title_color = '#fff' title_shadow = '#000' title_font_ttf = get_resource('fonts/JunicodeWL-Italic.ttf') title_font_size = 50 def pretty_title(self): return u"„%s”" % self.title
class GandalfCover(Cover): width = 600 height = 730 background_img = get_resource('res/cover-gandalf.png') author_font_ttf = get_resource('fonts/JunicodeWL-Regular.ttf') author_font_size = 30 title_font_ttf = get_resource('fonts/JunicodeWL-Regular.ttf') title_font_size = 40 logo_bottom = 25 logo_width = 250 format = 'PNG'
def image(self): img = super(LogoWLCover, self).image() metr = Metric(self, self.scale) gradient = Image.new("RGBA", (metr.width - metr.bar_width, metr.gradient_height), self.gradient_color) gradient_mask = Image.new("L", (metr.width - metr.bar_width, metr.gradient_height)) draw = ImageDraw.Draw(gradient_mask) for line in range(0, metr.gradient_height): draw.line( (0, line, metr.width - metr.bar_width, line), fill=int(255 * self.gradient_opacity * line / metr.gradient_height), ) img.paste(gradient, (metr.bar_width, metr.height - metr.gradient_height), mask=gradient_mask) cursor = metr.width - metr.gradient_logo_margin_right logo_top = metr.height - metr.gradient_height / 2 - metr.gradient_logo_height / 2 for logo_path in self.gradient_logos[::-1]: logo = Image.open(get_resource(logo_path)) logo = logo.resize( (logo.size[0] * metr.gradient_logo_height / logo.size[1], metr.gradient_logo_height), Image.ANTIALIAS ) cursor -= logo.size[0] img.paste(logo, (cursor, logo_top), mask=logo) cursor -= metr.gradient_logo_spacing return img
def image(self): img = super(LogoWLCover, self).image() metr = Metric(self, self.scale) gradient = Image.new( 'RGBA', (metr.width - metr.bar_width, metr.gradient_height), self.gradient_color) gradient_mask = Image.new( 'L', (metr.width - metr.bar_width, metr.gradient_height)) draw = ImageDraw.Draw(gradient_mask) for line in range(0, metr.gradient_height): draw.line((0, line, metr.width - metr.bar_width, line), fill=int(255 * self.gradient_opacity * line / metr.gradient_height)) img.paste(gradient, (metr.bar_width, metr.height - metr.gradient_height), mask=gradient_mask) cursor = metr.width - metr.gradient_logo_margin_right logo_top = metr.height - metr.gradient_height / 2 - metr.gradient_logo_height / 2 - metr.bleed / 2 for logo_path in self.gradient_logos[::-1]: logo = Image.open(get_resource(logo_path)) logo = logo.resize( (logo.size[0] * metr.gradient_logo_height / logo.size[1], metr.gradient_logo_height), Image.ANTIALIAS) cursor -= logo.size[0] img.paste(logo, (cursor, logo_top), mask=logo) cursor -= metr.gradient_logo_spacing return img
def image(self): metr = Metric(self, self.scale) img = Image.new("RGB", (metr.width, metr.height), self.background_color) if self.background_img: background = Image.open(self.background_img) img.paste(background, None, background) del background # WL logo if metr.logo_width: logo = Image.open(get_resource("res/wl-logo.png")) logo = logo.resize((metr.logo_width, logo.size[1] * metr.logo_width / logo.size[0])) img.paste(logo, ((metr.width - metr.logo_width) / 2, img.size[1] - logo.size[1] - metr.logo_bottom)) top = metr.author_top tbox = TextBox(metr.width - metr.author_margin_left - metr.author_margin_right, metr.height - top) author_font = ImageFont.truetype(self.author_font_ttf, metr.author_font_size) tbox.text(self.pretty_author(), self.author_color, author_font, metr.author_lineskip, self.author_shadow) text_img = tbox.image() img.paste(text_img, (metr.author_margin_left, top), text_img) top += text_img.size[1] + metr.title_top tbox = TextBox(metr.width - metr.title_margin_left - metr.title_margin_right, metr.height - top) title_font = ImageFont.truetype(self.title_font_ttf, metr.title_font_size) tbox.text(self.pretty_title(), self.title_color, title_font, metr.title_lineskip, self.title_shadow) text_img = tbox.image() img.paste(text_img, (metr.title_margin_left, top), text_img) return img
def to_png(self): tmpl = open(get_resource('res/embeds/latex/template.tex'), 'rb').read().decode('utf-8') tempdir = mkdtemp('-librarian-embed-latex') fpath = os.path.join(tempdir, 'doc.tex') with open(fpath, 'wb') as f: f.write((tmpl % {'code': self.data}).encode('utf-8')) call(['xelatex', '-interaction=batchmode', '-output-directory', tempdir, fpath], stdout=PIPE, stderr=PIPE) call(['convert', '-density', '150', os.path.join(tempdir, 'doc.pdf'), '-trim', os.path.join(tempdir, 'doc.png')]) pngdata = open(os.path.join(tempdir, 'doc.png'), 'rb').read() shutil.rmtree(tempdir) return create_embed('image/png', data=pngdata)
def to_png(self): tmpl = open(get_resource('res/embeds/latex/template.tex')).read().decode('utf-8') tempdir = mkdtemp('-librarian-embed-latex') fpath = os.path.join(tempdir, 'doc.tex') with open(fpath, 'w') as f: f.write((tmpl % {'code': self.data}).encode('utf-8')) call(['xelatex', '-interaction=batchmode', '-output-directory', tempdir, fpath], stdout=PIPE, stderr=PIPE) call(['convert', '-density', '150', os.path.join(tempdir, 'doc.pdf'), '-trim', os.path.join(tempdir, 'doc.png')]) pngdata = open(os.path.join(tempdir, 'doc.png')).read() shutil.rmtree(tempdir) return create_embed('image/png', data=pngdata)
def get_short_lng_code(text): result = '' text = ''.join(text) with open(get_resource('res/ISO-639-2_8859-1.txt'), 'rb') as f: for line in f: list = line.strip().split('|') if list[0] == text: result = list[2] if result == '': return text else: return result
def lang_code_3to2(context, text): """Convert 3-letter language code to 2-letter code""" result = '' text = ''.join(text) with open(get_resource('res/ISO-639-2_8859-1.txt'), 'rb') as f: for line in f.read().decode('latin1').split('\n'): list = line.strip().split('|') if list[0] == text: result = list[2] if result == '': return text else: return result
def transform_chunk(chunk_xml, chunk_no, annotations, empty=False, _empty_html_static=[]): """ transforms one chunk, returns a HTML string, a TOC object and a set of used characters """ toc = TOC() for element in chunk_xml[0]: if element.tag in ("naglowek_czesc", "naglowek_rozdzial", "naglowek_akt", "srodtytul"): toc.add(node_name(element), "part%d.html" % chunk_no) elif element.tag in ('naglowek_podrozdzial', 'naglowek_scena'): subnumber = toc.add(node_name(element), "part%d.html" % chunk_no, level=1, is_part=False) element.set('sub', str(subnumber)) if empty: if not _empty_html_static: _empty_html_static.append(open(get_resource('epub/emptyChunk.html')).read()) chars = set() output_html = _empty_html_static[0] else: find_annotations(annotations, chunk_xml, chunk_no) replace_by_verse(chunk_xml) html_tree = xslt(chunk_xml, get_resource('epub/xsltScheme.xsl')) chars = used_chars(html_tree.getroot()) output_html = etree.tostring(html_tree, method="html", pretty_print=True) return output_html, toc, chars
def lang_code_3to2(context, text): """Convert 3-letter language code to 2-letter code""" result = '' text = ''.join(text) with open(get_resource('res/ISO-639-2_8859-1.txt'), 'rb') as f: for line in f: list = line.strip().split('|') if list[0] == text: result=list[2] if result == '': return text else: return result
class BookotekaCover(Cover): width = 2140 height = 2733 background_img = get_resource('res/cover-bookoteka.png') author_top = 480 author_margin_left = 307 author_margin_right = 233 author_lineskip = 156 author_color = '#d9d919' author_font_ttf = get_resource('fonts/JunicodeWL-Regular.ttf') author_font_size = 130 title_top = 400 title_margin_left = 307 title_margin_right = 233 title_lineskip = 168 title_color = '#d9d919' title_font_ttf = get_resource('fonts/JunicodeWL-Regular.ttf') title_font_size = 140 format = 'PNG'
def image(self): img = Image.new('RGB', (self.width, self.height), self.background_color) if self.background_img: background = Image.open(self.background_img) img.paste(background, None, background) del background # WL logo if self.logo_width: logo = Image.open(get_resource('res/wl-logo.png')) logo = logo.resize((self.logo_width, logo.size[1] * self.logo_width / logo.size[0])) img.paste(logo, ((self.width - self.logo_width) / 2, img.size[1] - logo.size[1] - self.logo_bottom)) top = self.author_top tbox = TextBox( self.width - self.author_margin_left - self.author_margin_right, self.height - top, ) author_font = self.author_font or ImageFont.truetype( get_resource('fonts/DejaVuSerif.ttf'), 30) tbox.text(self.pretty_author(), self.author_color, author_font, self.author_lineskip, self.author_shadow) text_img = tbox.image() img.paste(text_img, (self.author_margin_left, top), text_img) top += text_img.size[1] + self.title_top tbox = TextBox( self.width - self.title_margin_left - self.title_margin_right, self.height - top, ) title_font = self.author_font or ImageFont.truetype( get_resource('fonts/DejaVuSerif.ttf'), 40) tbox.text(self.pretty_title(), self.title_color, title_font, self.title_lineskip, self.title_shadow) text_img = tbox.image() img.paste(text_img, (self.title_margin_left, top), text_img) return img
def add_source_note(wlpic, img): from PIL import ImageDraw, ImageFont from librarian import get_resource annotated = Image.new(img.mode, (img.size[0], img.size[1] + 40), (255, 255, 255)) annotated.paste(img, (0, 0)) annotation = Image.new('RGB', (img.size[0] * 3, 120), (255, 255, 255)) ImageDraw.Draw(annotation).text( (30, 15), wlpic.picture_info.source_name, (0, 0, 0), font=ImageFont.truetype(get_resource("fonts/DejaVuSerif.ttf"), 75) ) annotated.paste(annotation.resize((img.size[0], 40), Image.ANTIALIAS), (0, img.size[1])) return annotated
def transform(wldoc, verbose=False, sample=None, cover=None, flags=None): """ produces a MOBI file wldoc: a WLDocument sample=n: generate sample e-book (with at least n paragraphs) cover: a cover.Cover factory overriding default flags: less-advertising, """ document = deepcopy(wldoc) del wldoc book_info = document.book_info # provide a cover by default if not cover: cover = DefaultEbookCover cover_file = NamedTemporaryFile(suffix='.png', delete=False) bound_cover = cover(book_info) bound_cover.save(cover_file) if bound_cover.uses_dc_cover: if document.book_info.cover_by: document.edoc.getroot().set('data-cover-by', document.book_info.cover_by) if document.book_info.cover_source: document.edoc.getroot().set('data-cover-source', document.book_info.cover_source) if not flags: flags = [] flags = list(flags) + ['with-full-fonts'] epub = document.as_epub(verbose=verbose, sample=sample, html_toc=True, flags=flags, style=get_resource('epub/style.css')) if verbose: kwargs = {} else: devnull = open("/dev/null", 'w') kwargs = {"stdout": devnull, "stderr": devnull} output_file = NamedTemporaryFile(prefix='librarian', suffix='.mobi', delete=False) output_file.close() subprocess.check_call(['ebook-convert', epub.get_filename(), output_file.name, '--no-inline-toc', '--mobi-file-type=both', '--subset-embedded-fonts', '--mobi-ignore-margins', '--cover=%s' % cover_file.name], **kwargs) os.unlink(cover_file.name) return OutputFile.from_filename(output_file.name)
def image(self): metr = Metric(self, self.scale) img = Image.new('RGB', (metr.width, metr.height), self.background_color) if self.background_img: background = Image.open(self.background_img) img.paste(background, None, background) del background # WL logo if metr.logo_width: logo = Image.open(get_resource('res/wl-logo.png')) logo = logo.resize( (metr.logo_width, int(round(logo.size[1] * metr.logo_width / logo.size[0])))) img.paste(logo, ((metr.width - metr.logo_width) // 2, img.size[1] - logo.size[1] - metr.logo_bottom)) top = metr.author_top tbox = TextBox( metr.width - metr.author_margin_left - metr.author_margin_right, metr.height - top, ) author_font = ImageFont.truetype(self.author_font_ttf, metr.author_font_size) tbox.text(self.pretty_author(), self.author_color, author_font, metr.author_lineskip, self.author_shadow) text_img = tbox.image() img.paste(text_img, (metr.author_margin_left, top), text_img) top += text_img.size[1] + metr.title_top tbox = TextBox( metr.width - metr.title_margin_left - metr.title_margin_right, metr.height - top, ) title_font = ImageFont.truetype(self.title_font_ttf, metr.title_font_size) tbox.text(self.pretty_title(), self.title_color, title_font, metr.title_lineskip, self.title_shadow) text_img = tbox.image() img.paste(text_img, (metr.title_margin_left, top), text_img) return img
def set_hyph_language(source_tree): def get_short_lng_code(text): result = '' text = ''.join(text) with open(get_resource('res/ISO-639-2_8859-1.txt'), 'rb') as f: for line in f: list = line.strip().split('|') if list[0] == text: result = list[2] if result == '': return text else: return result bibl_lng = etree.XPath('//dc:language//text()', namespaces={'dc': str(DCNS)})(source_tree) short_lng = get_short_lng_code(bibl_lng[0]) try: return Hyphenator(get_resource('res/hyph-dictionaries/hyph_' + short_lng + '.dic')) except: pass
def transform(wldoc, verbose=False, style=None, html_toc=False, sample=None, cover=None, flags=None): """ produces a EPUB file sample=n: generate sample e-book (with at least n paragraphs) cover: a cover.Cover factory or True for default flags: less-advertising, without-fonts, working-copy """ def transform_file(wldoc, chunk_counter=1, first=True, sample=None): """ processes one input file and proceeds to its children """ replace_characters(wldoc.edoc.getroot()) hyphenator = set_hyph_language(wldoc.edoc.getroot()) hyphenate_and_fix_conjunctions(wldoc.edoc.getroot(), hyphenator) # every input file will have a TOC entry, # pointing to starting chunk toc = TOC(wldoc.book_info.title, "part%d.html" % chunk_counter) chars = set() if first: # write book title page html_tree = xslt(wldoc.edoc, get_resource('epub/xsltTitle.xsl')) chars = used_chars(html_tree.getroot()) zip.writestr( 'OPS/title.html', etree.tostring( html_tree, pretty_print=True, xml_declaration=True, encoding="utf-8", doctype='<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN"' + ' "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">' ) ) # add a title page TOC entry toc.add(u"Strona tytułowa", "title.html") elif wldoc.book_info.parts: # write title page for every parent if sample is not None and sample <= 0: chars = set() html_string = open(get_resource('epub/emptyChunk.html')).read() else: html_tree = xslt(wldoc.edoc, get_resource('epub/xsltChunkTitle.xsl')) chars = used_chars(html_tree.getroot()) html_string = etree.tostring( html_tree, pretty_print=True, xml_declaration=True, encoding="utf-8", doctype='<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN"' + ' "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">' ) zip.writestr('OPS/part%d.html' % chunk_counter, html_string) add_to_manifest(manifest, chunk_counter) add_to_spine(spine, chunk_counter) chunk_counter += 1 if len(wldoc.edoc.getroot()) > 1: # rdf before style master main_text = wldoc.edoc.getroot()[1] else: # rdf in style master main_text = wldoc.edoc.getroot()[0] if main_text.tag == RDFNS('RDF'): main_text = None if main_text is not None: for chunk_xml in chop(main_text): empty = False if sample is not None: if sample <= 0: empty = True else: sample -= len(chunk_xml.xpath('//strofa|//akap|//akap_cd|//akap_dialog')) chunk_html, chunk_toc, chunk_chars = transform_chunk(chunk_xml, chunk_counter, annotations, empty) toc.extend(chunk_toc) chars = chars.union(chunk_chars) zip.writestr('OPS/part%d.html' % chunk_counter, chunk_html) add_to_manifest(manifest, chunk_counter) add_to_spine(spine, chunk_counter) chunk_counter += 1 for child in wldoc.parts(): child_toc, chunk_counter, chunk_chars, sample = transform_file( child, chunk_counter, first=False, sample=sample) toc.append(child_toc) chars = chars.union(chunk_chars) return toc, chunk_counter, chars, sample document = deepcopy(wldoc) del wldoc if flags: for flag in flags: document.edoc.getroot().set(flag, 'yes') # add editors info editors = document.editors() if editors: document.edoc.getroot().set('editors', u', '.join(sorted( editor.readable() for editor in editors))) if document.book_info.funders: document.edoc.getroot().set('funders', u', '.join( document.book_info.funders)) if document.book_info.thanks: document.edoc.getroot().set('thanks', document.book_info.thanks) opf = xslt(document.book_info.to_etree(), get_resource('epub/xsltContent.xsl')) manifest = opf.find('.//' + OPFNS('manifest')) guide = opf.find('.//' + OPFNS('guide')) spine = opf.find('.//' + OPFNS('spine')) output_file = NamedTemporaryFile(prefix='librarian', suffix='.epub', delete=False) zip = zipfile.ZipFile(output_file, 'w', zipfile.ZIP_DEFLATED) # write static elements mime = zipfile.ZipInfo() mime.filename = 'mimetype' mime.compress_type = zipfile.ZIP_STORED mime.extra = '' zip.writestr(mime, 'application/epub+zip') zip.writestr( 'META-INF/container.xml', '<?xml version="1.0" ?>' '<container version="1.0" ' 'xmlns="urn:oasis:names:tc:opendocument:xmlns:container">' '<rootfiles><rootfile full-path="OPS/content.opf" ' 'media-type="application/oebps-package+xml" />' '</rootfiles></container>' ) zip.write(get_resource('res/wl-logo-small.png'), os.path.join('OPS', 'logo_wolnelektury.png')) zip.write(get_resource('res/jedenprocent.png'), os.path.join('OPS', 'jedenprocent.png')) if not style: style = get_resource('epub/style.css') zip.write(style, os.path.join('OPS', 'style.css')) if cover: if cover is True: cover = DefaultEbookCover cover_file = StringIO() bound_cover = cover(document.book_info) bound_cover.save(cover_file) cover_name = 'cover.%s' % bound_cover.ext() zip.writestr(os.path.join('OPS', cover_name), cover_file.getvalue()) del cover_file cover_tree = etree.parse(get_resource('epub/cover.html')) cover_tree.find('//' + XHTMLNS('img')).set('src', cover_name) zip.writestr('OPS/cover.html', etree.tostring( cover_tree, pretty_print=True, xml_declaration=True, encoding="utf-8", doctype='<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN" ' + '"http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">' )) if bound_cover.uses_dc_cover: if document.book_info.cover_by: document.edoc.getroot().set('data-cover-by', document.book_info.cover_by) if document.book_info.cover_source: document.edoc.getroot().set('data-cover-source', document.book_info.cover_source) manifest.append(etree.fromstring( '<item id="cover" href="cover.html" media-type="application/xhtml+xml" />')) manifest.append(etree.fromstring( '<item id="cover-image" href="%s" media-type="%s" />' % (cover_name, bound_cover.mime_type()))) spine.insert(0, etree.fromstring('<itemref idref="cover"/>')) opf.getroot()[0].append(etree.fromstring('<meta name="cover" content="cover-image"/>')) guide.append(etree.fromstring('<reference href="cover.html" type="cover" title="Okładka"/>')) annotations = etree.Element('annotations') toc_file = etree.fromstring( '<?xml version="1.0" encoding="utf-8"?><!DOCTYPE ncx PUBLIC ' '"-//NISO//DTD ncx 2005-1//EN" ' '"http://www.daisy.org/z3986/2005/ncx-2005-1.dtd">' '<ncx xmlns="http://www.daisy.org/z3986/2005/ncx/" xml:lang="pl" ' 'version="2005-1"><head></head><docTitle></docTitle><navMap>' '</navMap></ncx>' ) nav_map = toc_file[-1] if html_toc: manifest.append(etree.fromstring( '<item id="html_toc" href="toc.html" media-type="application/xhtml+xml" />')) spine.append(etree.fromstring( '<itemref idref="html_toc" />')) guide.append(etree.fromstring('<reference href="toc.html" type="toc" title="Spis treści"/>')) toc, chunk_counter, chars, sample = transform_file(document, sample=sample) if len(toc.children) < 2: toc.add(u"Początek utworu", "part1.html") # Last modifications in container files and EPUB creation if len(annotations) > 0: toc.add("Przypisy", "annotations.html") manifest.append(etree.fromstring( '<item id="annotations" href="annotations.html" media-type="application/xhtml+xml" />')) spine.append(etree.fromstring( '<itemref idref="annotations" />')) replace_by_verse(annotations) html_tree = xslt(annotations, get_resource('epub/xsltAnnotations.xsl')) chars = chars.union(used_chars(html_tree.getroot())) zip.writestr('OPS/annotations.html', etree.tostring( html_tree, pretty_print=True, xml_declaration=True, encoding="utf-8", doctype='<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN" ' + '"http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">' )) toc.add("Wesprzyj Wolne Lektury", "support.html") manifest.append(etree.fromstring( '<item id="support" href="support.html" media-type="application/xhtml+xml" />')) spine.append(etree.fromstring( '<itemref idref="support" />')) html_string = open(get_resource('epub/support.html')).read() chars.update(used_chars(etree.fromstring(html_string))) zip.writestr('OPS/support.html', html_string) toc.add("Strona redakcyjna", "last.html") manifest.append(etree.fromstring( '<item id="last" href="last.html" media-type="application/xhtml+xml" />')) spine.append(etree.fromstring( '<itemref idref="last" />')) html_tree = xslt(document.edoc, get_resource('epub/xsltLast.xsl')) chars.update(used_chars(html_tree.getroot())) zip.writestr('OPS/last.html', etree.tostring( html_tree, pretty_print=True, xml_declaration=True, encoding="utf-8", doctype='<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN" ' + '"http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">' )) if not flags or not 'without-fonts' in flags: # strip fonts tmpdir = mkdtemp('-librarian-epub') try: cwd = os.getcwd() except OSError: cwd = None os.chdir(os.path.join(os.path.dirname(os.path.realpath(__file__)), 'font-optimizer')) for fname in 'DejaVuSerif.ttf', 'DejaVuSerif-Bold.ttf', 'DejaVuSerif-Italic.ttf', 'DejaVuSerif-BoldItalic.ttf': optimizer_call = ['perl', 'subset.pl', '--chars', ''.join(chars).encode('utf-8'), get_resource('fonts/' + fname), os.path.join(tmpdir, fname)] if verbose: print "Running font-optimizer" subprocess.check_call(optimizer_call) else: subprocess.check_call(optimizer_call, stdout=subprocess.PIPE, stderr=subprocess.PIPE) zip.write(os.path.join(tmpdir, fname), os.path.join('OPS', fname)) manifest.append(etree.fromstring( '<item id="%s" href="%s" media-type="application/x-font-truetype" />' % (fname, fname))) rmtree(tmpdir) if cwd is not None: os.chdir(cwd) zip.writestr('OPS/content.opf', etree.tostring(opf, pretty_print=True, xml_declaration=True, encoding="utf-8")) title = document.book_info.title attributes = "dtb:uid", "dtb:depth", "dtb:totalPageCount", "dtb:maxPageNumber" for st in attributes: meta = toc_file.makeelement(NCXNS('meta')) meta.set('name', st) meta.set('content', '0') toc_file[0].append(meta) toc_file[0][0].set('content', str(document.book_info.url)) toc_file[0][1].set('content', str(toc.depth())) set_inner_xml(toc_file[1], ''.join(('<text>', title, '</text>'))) # write TOC if html_toc: toc.add(u"Spis treści", "toc.html", index=1) zip.writestr('OPS/toc.html', toc.html().encode('utf-8')) toc.write_to_xml(nav_map) zip.writestr('OPS/toc.ncx', etree.tostring(toc_file, pretty_print=True, xml_declaration=True, encoding="utf-8")) zip.close() return OutputFile.from_filename(output_file.name)
def transform(wldoc, verbose=False, save_tex=None, morefloats=None, cover=None, flags=None, customizations=None, ilustr_path='', latex_dir=False): """ produces a PDF file with XeLaTeX wldoc: a WLDocument verbose: prints all output from LaTeX save_tex: path to save the intermediary LaTeX file to morefloats (old/new/none): force specific morefloats cover: a cover.Cover factory or True for default flags: less-advertising, customizations: user requested customizations regarding various formatting parameters (passed to wl LaTeX class) """ # Parse XSLT try: book_info = wldoc.book_info document = load_including_children(wldoc) root = document.edoc.getroot() if cover: if cover is True: cover = make_cover bound_cover = cover(book_info, width=1200) root.set('data-cover-width', str(bound_cover.width)) root.set('data-cover-height', str(bound_cover.height)) if bound_cover.uses_dc_cover: if book_info.cover_by: root.set('data-cover-by', book_info.cover_by) if book_info.cover_source: root.set('data-cover-source', book_info.cover_source) if flags: for flag in flags: root.set('flag-' + flag, 'yes') # check for LaTeX packages if morefloats: root.set('morefloats', morefloats.lower()) elif package_available('morefloats', 'maxfloats=19'): root.set('morefloats', 'new') # add customizations if customizations is not None: root.set('customizations', u','.join(customizations)) # add editors info editors = document.editors() if editors: root.set( 'editors', u', '.join(sorted(editor.readable() for editor in editors))) if document.book_info.funders: root.set('funders', u', '.join(document.book_info.funders)) if document.book_info.thanks: root.set('thanks', document.book_info.thanks) # hack the tree move_motifs_inside(document.edoc) hack_motifs(document.edoc) parse_creator(document.edoc) substitute_hyphens(document.edoc) fix_hanging(document.edoc) fix_tables(document.edoc) mark_subauthors(document.edoc) # wl -> TeXML style_filename = get_stylesheet("wl2tex") style = etree.parse(style_filename) functions.reg_mathml_latex() # TeXML -> LaTeX temp = mkdtemp('-wl2pdf') for ilustr in document.edoc.findall("//ilustr"): shutil.copy(os.path.join(ilustr_path, ilustr.get("src")), temp) for sponsor in book_info.sponsors: ins = etree.Element("data-sponsor", name=sponsor) logo = sponsor_logo(sponsor) if logo: fname = 'sponsor-%s' % os.path.basename(logo) shutil.copy(logo, os.path.join(temp, fname)) ins.set('src', fname) root.insert(0, ins) if book_info.sponsor_note: root.set("sponsor-note", book_info.sponsor_note) texml = document.transform(style) if cover: with open(os.path.join(temp, 'cover.png'), 'w') as f: bound_cover.save(f, quality=80) del document # no longer needed large object :) tex_path = os.path.join(temp, 'doc.tex') fout = open(tex_path, 'wb') process(six.BytesIO(texml), fout, 'utf-8') fout.close() del texml if save_tex: shutil.copy(tex_path, save_tex) # LaTeX -> PDF shutil.copy(get_resource('pdf/wl.cls'), temp) shutil.copy(get_resource('res/wl-logo.png'), temp) if latex_dir: return temp try: cwd = os.getcwd() except OSError: cwd = None os.chdir(temp) # some things work better when compiled twice # (table of contents, [line numbers - disabled]) for run in range(2): if verbose: p = call(['xelatex', tex_path]) else: p = call(['xelatex', '-interaction=batchmode', tex_path], stdout=PIPE, stderr=PIPE) if p: raise ParseError("Error parsing .tex file") if cwd is not None: os.chdir(cwd) output_file = NamedTemporaryFile(prefix='librarian', suffix='.pdf', delete=False) pdf_path = os.path.join(temp, 'doc.pdf') shutil.move(pdf_path, output_file.name) shutil.rmtree(temp) return OutputFile.from_filename(output_file.name) except (XMLSyntaxError, XSLTApplyError) as e: raise ParseError(e)
def html(self): with open(get_resource('epub/toc.html')) as f: t = unicode(f.read(), 'utf-8') return t % self.html_part()
def html(self): with open(get_resource('epub/toc.html'), 'rb') as f: t = f.read().decode('utf-8') return t % self.html_part()
def get_stylesheet(name): return get_resource(STYLESHEETS[name])
def transform(wldoc, verbose=False, save_tex=None, morefloats=None, cover=None, flags=None, customizations=None): """ produces a PDF file with XeLaTeX wldoc: a WLDocument verbose: prints all output from LaTeX save_tex: path to save the intermediary LaTeX file to morefloats (old/new/none): force specific morefloats cover: a cover.Cover factory or True for default flags: less-advertising, customizations: user requested customizations regarding various formatting parameters (passed to wl LaTeX class) """ # Parse XSLT try: book_info = wldoc.book_info document = load_including_children(wldoc) root = document.edoc.getroot() if cover: if cover is True: cover = DefaultEbookCover bound_cover = cover(book_info, width=1200) root.set('data-cover-width', str(bound_cover.width)) root.set('data-cover-height', str(bound_cover.height)) if bound_cover.uses_dc_cover: if book_info.cover_by: root.set('data-cover-by', book_info.cover_by) if book_info.cover_source: root.set('data-cover-source', book_info.cover_source) if flags: for flag in flags: root.set('flag-' + flag, 'yes') # check for LaTeX packages if morefloats: root.set('morefloats', morefloats.lower()) elif package_available('morefloats', 'maxfloats=19'): root.set('morefloats', 'new') # add customizations if customizations is not None: root.set('customizations', u','.join(customizations)) # add editors info editors = document.editors() if editors: root.set('editors', u', '.join(sorted( editor.readable() for editor in editors))) if document.book_info.funders: root.set('funders', u', '.join(document.book_info.funders)) if document.book_info.thanks: root.set('thanks', document.book_info.thanks) # hack the tree move_motifs_inside(document.edoc) hack_motifs(document.edoc) parse_creator(document.edoc) substitute_hyphens(document.edoc) fix_hanging(document.edoc) fix_tables(document.edoc) # wl -> TeXML style_filename = get_stylesheet("wl2tex") style = etree.parse(style_filename) functions.reg_mathml_latex() # TeXML -> LaTeX temp = mkdtemp('-wl2pdf') for sponsor in book_info.sponsors: ins = etree.Element("data-sponsor", name=sponsor) logo = sponsor_logo(sponsor) if logo: fname = 'sponsor-%s' % os.path.basename(logo) shutil.copy(logo, os.path.join(temp, fname)) ins.set('src', fname) root.insert(0, ins) if book_info.sponsor_note: root.set("sponsor-note", book_info.sponsor_note) texml = document.transform(style) if cover: with open(os.path.join(temp, 'cover.png'), 'w') as f: bound_cover.save(f, quality=80) del document # no longer needed large object :) tex_path = os.path.join(temp, 'doc.tex') fout = open(tex_path, 'w') process(StringIO(texml), fout, 'utf-8') fout.close() del texml if save_tex: shutil.copy(tex_path, save_tex) # LaTeX -> PDF shutil.copy(get_resource('pdf/wl.cls'), temp) shutil.copy(get_resource('res/wl-logo.png'), temp) try: cwd = os.getcwd() except OSError: cwd = None os.chdir(temp) if verbose: p = call(['xelatex', tex_path]) else: p = call(['xelatex', '-interaction=batchmode', tex_path], stdout=PIPE, stderr=PIPE) if p: raise ParseError("Error parsing .tex file") if cwd is not None: os.chdir(cwd) output_file = NamedTemporaryFile(prefix='librarian', suffix='.pdf', delete=False) pdf_path = os.path.join(temp, 'doc.pdf') shutil.move(pdf_path, output_file.name) shutil.rmtree(temp) return OutputFile.from_filename(output_file.name) except (XMLSyntaxError, XSLTApplyError), e: raise ParseError(e)
def to_latex(self): xslt = etree.parse(get_resource('res/embeds/mathml/mathml2latex.xslt')) output = self.tree.xslt(xslt) return create_embed('application/x-latex', data=unicode(output))
def transform(wldoc, verbose=False, style=None, html_toc=False, sample=None, cover=None, flags=None, hyphenate=False, ilustr_path='', output_type='epub'): """ produces a EPUB file sample=n: generate sample e-book (with at least n paragraphs) cover: a cover.Cover factory or True for default flags: less-advertising, without-fonts, working-copy """ def transform_file(wldoc, chunk_counter=1, first=True, sample=None): """ processes one input file and proceeds to its children """ replace_characters(wldoc.edoc.getroot()) hyphenator = set_hyph_language(wldoc.edoc.getroot()) if hyphenate else None hyphenate_and_fix_conjunctions(wldoc.edoc.getroot(), hyphenator) # every input file will have a TOC entry, # pointing to starting chunk toc = TOC(wldoc.book_info.title, "part%d.html" % chunk_counter) chars = set() if first: # write book title page html_tree = xslt(wldoc.edoc, get_resource('epub/xsltTitle.xsl'), outputtype=output_type) chars = used_chars(html_tree.getroot()) zip.writestr( 'OPS/title.html', etree.tostring( html_tree, pretty_print=True, xml_declaration=True, encoding="utf-8", doctype='<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN"' + ' "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">' ) ) # add a title page TOC entry toc.add(u"Strona tytułowa", "title.html") elif wldoc.book_info.parts: # write title page for every parent if sample is not None and sample <= 0: chars = set() html_string = open(get_resource('epub/emptyChunk.html')).read() else: html_tree = xslt(wldoc.edoc, get_resource('epub/xsltChunkTitle.xsl')) chars = used_chars(html_tree.getroot()) html_string = etree.tostring( html_tree, pretty_print=True, xml_declaration=True, encoding="utf-8", doctype='<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN"' + ' "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">' ) zip.writestr('OPS/part%d.html' % chunk_counter, html_string) add_to_manifest(manifest, chunk_counter) add_to_spine(spine, chunk_counter) chunk_counter += 1 if len(wldoc.edoc.getroot()) > 1: # rdf before style master main_text = wldoc.edoc.getroot()[1] else: # rdf in style master main_text = wldoc.edoc.getroot()[0] if main_text.tag == RDFNS('RDF'): main_text = None if main_text is not None: for chunk_xml in chop(main_text): empty = False if sample is not None: if sample <= 0: empty = True else: sample -= len(chunk_xml.xpath('//strofa|//akap|//akap_cd|//akap_dialog')) chunk_html, chunk_toc, chunk_chars = transform_chunk(chunk_xml, chunk_counter, annotations, empty) toc.extend(chunk_toc) chars = chars.union(chunk_chars) zip.writestr('OPS/part%d.html' % chunk_counter, chunk_html) add_to_manifest(manifest, chunk_counter) add_to_spine(spine, chunk_counter) chunk_counter += 1 for child in wldoc.parts(): child_toc, chunk_counter, chunk_chars, sample = transform_file( child, chunk_counter, first=False, sample=sample) toc.append(child_toc) chars = chars.union(chunk_chars) return toc, chunk_counter, chars, sample document = deepcopy(wldoc) del wldoc if flags: for flag in flags: document.edoc.getroot().set(flag, 'yes') document.clean_ed_note() document.clean_ed_note('abstrakt') # add editors info editors = document.editors() if editors: document.edoc.getroot().set('editors', u', '.join(sorted( editor.readable() for editor in editors))) if document.book_info.funders: document.edoc.getroot().set('funders', u', '.join( document.book_info.funders)) if document.book_info.thanks: document.edoc.getroot().set('thanks', document.book_info.thanks) opf = xslt(document.book_info.to_etree(), get_resource('epub/xsltContent.xsl')) manifest = opf.find('.//' + OPFNS('manifest')) guide = opf.find('.//' + OPFNS('guide')) spine = opf.find('.//' + OPFNS('spine')) output_file = NamedTemporaryFile(prefix='librarian', suffix='.epub', delete=False) zip = zipfile.ZipFile(output_file, 'w', zipfile.ZIP_DEFLATED) functions.reg_mathml_epub(zip) if os.path.isdir(ilustr_path): for i, filename in enumerate(os.listdir(ilustr_path)): file_path = os.path.join(ilustr_path, filename) zip.write(file_path, os.path.join('OPS', filename)) image_id = 'image%s' % i manifest.append(etree.fromstring( '<item id="%s" href="%s" media-type="%s" />' % (image_id, filename, guess_type(file_path)[0]))) # write static elements mime = zipfile.ZipInfo() mime.filename = 'mimetype' mime.compress_type = zipfile.ZIP_STORED mime.extra = '' zip.writestr(mime, 'application/epub+zip') zip.writestr( 'META-INF/container.xml', '<?xml version="1.0" ?>' '<container version="1.0" ' 'xmlns="urn:oasis:names:tc:opendocument:xmlns:container">' '<rootfiles><rootfile full-path="OPS/content.opf" ' 'media-type="application/oebps-package+xml" />' '</rootfiles></container>' ) zip.write(get_resource('res/wl-logo-small.png'), os.path.join('OPS', 'logo_wolnelektury.png')) zip.write(get_resource('res/jedenprocent.png'), os.path.join('OPS', 'jedenprocent.png')) if not style: style = get_resource('epub/style.css') zip.write(style, os.path.join('OPS', 'style.css')) if cover: if cover is True: cover = DefaultEbookCover cover_file = StringIO() bound_cover = cover(document.book_info) bound_cover.save(cover_file) cover_name = 'cover.%s' % bound_cover.ext() zip.writestr(os.path.join('OPS', cover_name), cover_file.getvalue()) del cover_file cover_tree = etree.parse(get_resource('epub/cover.html')) cover_tree.find('//' + XHTMLNS('img')).set('src', cover_name) zip.writestr('OPS/cover.html', etree.tostring( cover_tree, pretty_print=True, xml_declaration=True, encoding="utf-8", doctype='<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN" ' + '"http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">' )) if bound_cover.uses_dc_cover: if document.book_info.cover_by: document.edoc.getroot().set('data-cover-by', document.book_info.cover_by) if document.book_info.cover_source: document.edoc.getroot().set('data-cover-source', document.book_info.cover_source) manifest.append(etree.fromstring( '<item id="cover" href="cover.html" media-type="application/xhtml+xml" />')) manifest.append(etree.fromstring( '<item id="cover-image" href="%s" media-type="%s" />' % (cover_name, bound_cover.mime_type()))) spine.insert(0, etree.fromstring('<itemref idref="cover"/>')) opf.getroot()[0].append(etree.fromstring('<meta name="cover" content="cover-image"/>')) guide.append(etree.fromstring('<reference href="cover.html" type="cover" title="Okładka"/>')) annotations = etree.Element('annotations') toc_file = etree.fromstring( '<?xml version="1.0" encoding="utf-8"?><!DOCTYPE ncx PUBLIC ' '"-//NISO//DTD ncx 2005-1//EN" ' '"http://www.daisy.org/z3986/2005/ncx-2005-1.dtd">' '<ncx xmlns="http://www.daisy.org/z3986/2005/ncx/" xml:lang="pl" ' 'version="2005-1"><head></head><docTitle></docTitle><navMap>' '</navMap></ncx>' ) nav_map = toc_file[-1] if html_toc: manifest.append(etree.fromstring( '<item id="html_toc" href="toc.html" media-type="application/xhtml+xml" />')) spine.append(etree.fromstring( '<itemref idref="html_toc" />')) guide.append(etree.fromstring('<reference href="toc.html" type="toc" title="Spis treści"/>')) toc, chunk_counter, chars, sample = transform_file(document, sample=sample) if len(toc.children) < 2: toc.add(u"Początek utworu", "part1.html") # Last modifications in container files and EPUB creation if len(annotations) > 0: toc.add("Przypisy", "annotations.html") manifest.append(etree.fromstring( '<item id="annotations" href="annotations.html" media-type="application/xhtml+xml" />')) spine.append(etree.fromstring( '<itemref idref="annotations" />')) replace_by_verse(annotations) html_tree = xslt(annotations, get_resource('epub/xsltAnnotations.xsl')) chars = chars.union(used_chars(html_tree.getroot())) zip.writestr('OPS/annotations.html', etree.tostring( html_tree, pretty_print=True, xml_declaration=True, encoding="utf-8", doctype='<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN" ' + '"http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">' )) toc.add("Wesprzyj Wolne Lektury", "support.html") manifest.append(etree.fromstring( '<item id="support" href="support.html" media-type="application/xhtml+xml" />')) spine.append(etree.fromstring( '<itemref idref="support" />')) html_string = open(get_resource('epub/support.html')).read() chars.update(used_chars(etree.fromstring(html_string))) zip.writestr('OPS/support.html', html_string) toc.add("Strona redakcyjna", "last.html") manifest.append(etree.fromstring( '<item id="last" href="last.html" media-type="application/xhtml+xml" />')) spine.append(etree.fromstring( '<itemref idref="last" />')) html_tree = xslt(document.edoc, get_resource('epub/xsltLast.xsl'), outputtype=output_type) chars.update(used_chars(html_tree.getroot())) zip.writestr('OPS/last.html', etree.tostring( html_tree, pretty_print=True, xml_declaration=True, encoding="utf-8", doctype='<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN" ' + '"http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">' )) if not flags or 'without-fonts' not in flags: # strip fonts tmpdir = mkdtemp('-librarian-epub') try: cwd = os.getcwd() except OSError: cwd = None os.chdir(os.path.join(os.path.dirname(os.path.realpath(__file__)), 'font-optimizer')) for fname in 'DejaVuSerif.ttf', 'DejaVuSerif-Bold.ttf', 'DejaVuSerif-Italic.ttf', 'DejaVuSerif-BoldItalic.ttf': optimizer_call = ['perl', 'subset.pl', '--chars', ''.join(chars).encode('utf-8'), get_resource('fonts/' + fname), os.path.join(tmpdir, fname)] if verbose: print "Running font-optimizer" subprocess.check_call(optimizer_call) else: dev_null = open(os.devnull, 'w') subprocess.check_call(optimizer_call, stdout=dev_null, stderr=dev_null) zip.write(os.path.join(tmpdir, fname), os.path.join('OPS', fname)) manifest.append(etree.fromstring( '<item id="%s" href="%s" media-type="application/x-font-truetype" />' % (fname, fname))) rmtree(tmpdir) if cwd is not None: os.chdir(cwd) zip.writestr('OPS/content.opf', etree.tostring(opf, pretty_print=True, xml_declaration=True, encoding="utf-8")) title = document.book_info.title attributes = "dtb:uid", "dtb:depth", "dtb:totalPageCount", "dtb:maxPageNumber" for st in attributes: meta = toc_file.makeelement(NCXNS('meta')) meta.set('name', st) meta.set('content', '0') toc_file[0].append(meta) toc_file[0][0].set('content', str(document.book_info.url)) toc_file[0][1].set('content', str(toc.depth())) set_inner_xml(toc_file[1], ''.join(('<text>', title, '</text>'))) # write TOC if html_toc: toc.add(u"Spis treści", "toc.html", index=1) zip.writestr('OPS/toc.html', toc.html().encode('utf-8')) toc.write_to_xml(nav_map) zip.writestr('OPS/toc.ncx', etree.tostring(toc_file, pretty_print=True, xml_declaration=True, encoding="utf-8")) zip.close() return OutputFile.from_filename(output_file.name)
def transform_file(wldoc, chunk_counter=1, first=True, sample=None): """ processes one input file and proceeds to its children """ replace_characters(wldoc.edoc.getroot()) hyphenator = set_hyph_language(wldoc.edoc.getroot()) if hyphenate else None hyphenate_and_fix_conjunctions(wldoc.edoc.getroot(), hyphenator) # every input file will have a TOC entry, # pointing to starting chunk toc = TOC(wldoc.book_info.title, "part%d.html" % chunk_counter) chars = set() if first: # write book title page html_tree = xslt(wldoc.edoc, get_resource('epub/xsltTitle.xsl'), outputtype=output_type) chars = used_chars(html_tree.getroot()) zip.writestr( 'OPS/title.html', etree.tostring( html_tree, pretty_print=True, xml_declaration=True, encoding="utf-8", doctype='<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN"' + ' "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">' ) ) # add a title page TOC entry toc.add(u"Strona tytułowa", "title.html") elif wldoc.book_info.parts: # write title page for every parent if sample is not None and sample <= 0: chars = set() html_string = open(get_resource('epub/emptyChunk.html')).read() else: html_tree = xslt(wldoc.edoc, get_resource('epub/xsltChunkTitle.xsl')) chars = used_chars(html_tree.getroot()) html_string = etree.tostring( html_tree, pretty_print=True, xml_declaration=True, encoding="utf-8", doctype='<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN"' + ' "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">' ) zip.writestr('OPS/part%d.html' % chunk_counter, html_string) add_to_manifest(manifest, chunk_counter) add_to_spine(spine, chunk_counter) chunk_counter += 1 if len(wldoc.edoc.getroot()) > 1: # rdf before style master main_text = wldoc.edoc.getroot()[1] else: # rdf in style master main_text = wldoc.edoc.getroot()[0] if main_text.tag == RDFNS('RDF'): main_text = None if main_text is not None: for chunk_xml in chop(main_text): empty = False if sample is not None: if sample <= 0: empty = True else: sample -= len(chunk_xml.xpath('//strofa|//akap|//akap_cd|//akap_dialog')) chunk_html, chunk_toc, chunk_chars = transform_chunk(chunk_xml, chunk_counter, annotations, empty) toc.extend(chunk_toc) chars = chars.union(chunk_chars) zip.writestr('OPS/part%d.html' % chunk_counter, chunk_html) add_to_manifest(manifest, chunk_counter) add_to_spine(spine, chunk_counter) chunk_counter += 1 for child in wldoc.parts(): child_toc, chunk_counter, chunk_chars, sample = transform_file( child, chunk_counter, first=False, sample=sample) toc.append(child_toc) chars = chars.union(chunk_chars) return toc, chunk_counter, chars, sample
def image(self): metr = Metric(self, self.scale) img = Image.new('RGB', (metr.width, metr.height), self.background_color) draw = ImageDraw.Draw(img) if self.epoch in self.epoch_colors: epoch_color = self.epoch_colors[self.epoch] else: epoch_color = '#000' draw.rectangle((0, 0, metr.bar_width, metr.height), fill=epoch_color) if self.background_img: src = Image.open(self.background_img) trg_size = (metr.width - metr.bar_width, metr.height) if src.size[0] * trg_size[1] < src.size[1] * trg_size[0]: resized = ( trg_size[0], src.size[1] * trg_size[0] / src.size[0] ) cut = (resized[1] - trg_size[1]) / 2 src = src.resize(resized, Image.ANTIALIAS) src = src.crop((0, cut, src.size[0], src.size[1] - cut)) else: resized = ( src.size[0] * trg_size[1] / src.size[1], trg_size[1], ) cut = (resized[0] - trg_size[0]) / 2 src = src.resize(resized, Image.ANTIALIAS) src = src.crop((cut, 0, src.size[0] - cut, src.size[1])) img.paste(src, (metr.bar_width, 0)) del src box = TextBox(metr.title_box_width, metr.height, padding_y=metr.box_padding_y) author_font = ImageFont.truetype( self.author_font_ttf, metr.author_font_size) box.text(self.pretty_author(), font=author_font, line_height=metr.author_lineskip, color=self.author_color, shadow_color=self.author_shadow, ) box.skip(metr.box_above_line) box.draw.line((metr.box_line_left, box.height, metr.box_line_right, box.height), fill=self.author_color, width=metr.box_line_width) box.skip(metr.box_below_line) title_font = ImageFont.truetype( self.title_font_ttf, metr.title_font_size) box.text(self.pretty_title(), line_height=metr.title_lineskip, font=title_font, color=epoch_color, shadow_color=self.title_shadow, ) if self.with_logo: logo = Image.open(get_resource('res/wl-logo-mono.png')) logo = logo.resize((metr.logo_width, logo.size[1] * metr.logo_width / logo.size[0]), Image.ANTIALIAS) alpha = logo.split()[3] alpha = ImageEnhance.Brightness(alpha).enhance(.75) logo.putalpha(alpha) box.skip(metr.logo_top + logo.size[1]) box_img = box.image() if self.kind == 'Liryka': # top box_top = metr.box_top_margin elif self.kind == 'Epika': # bottom box_top = metr.height - metr.box_bottom_margin - box_img.size[1] else: # center box_top = (metr.height - box_img.size[1]) / 2 box_left = metr.bar_width + (metr.width - metr.bar_width - box_img.size[0]) / 2 draw.rectangle((box_left, box_top, box_left + box_img.size[0], box_top + box_img.size[1]), fill='#fff') img.paste(box_img, (box_left, box_top), box_img) if self.with_logo: img.paste(logo, (box_left + (box_img.size[0] - logo.size[0]) / 2, box_top + box_img.size[1] - metr.box_padding_y - logo.size[1]), mask=logo) return img
class Cover(object): """Abstract base class for cover images generator.""" width = 600 height = 800 background_color = '#fff' background_img = None author_top = 100 author_margin_left = 20 author_margin_right = 20 author_lineskip = 40 author_color = '#000' author_shadow = None author_font_ttf = get_resource('fonts/DejaVuSerif.ttf') author_font_size = 30 title_top = 100 title_margin_left = 20 title_margin_right = 20 title_lineskip = 54 title_color = '#000' title_shadow = None title_font_ttf = get_resource('fonts/DejaVuSerif.ttf') title_font_size = 40 logo_bottom = None logo_width = None uses_dc_cover = False format = 'JPEG' scale = 1 scale_after = 1 exts = { 'JPEG': 'jpg', 'PNG': 'png', } mime_types = { 'JPEG': 'image/jpeg', 'PNG': 'image/png', } def __init__(self, book_info, format=None, width=None, height=None): self.author = ", ".join(auth.readable() for auth in book_info.authors) self.title = book_info.title if format is not None: self.format = format if width and height: self.height = height * self.width / width scale = max( float(width or 0) / self.width, float(height or 0) / self.height) if scale >= 1: self.scale = scale elif scale: self.scale_after = scale def pretty_author(self): """Allows for decorating author's name.""" return self.author def pretty_title(self): """Allows for decorating title.""" return self.title def image(self): metr = Metric(self, self.scale) img = Image.new('RGB', (metr.width, metr.height), self.background_color) if self.background_img: background = Image.open(self.background_img) img.paste(background, None, background) del background # WL logo if metr.logo_width: logo = Image.open(get_resource('res/wl-logo.png')) logo = logo.resize((metr.logo_width, logo.size[1] * metr.logo_width / logo.size[0])) img.paste(logo, ((metr.width - metr.logo_width) / 2, img.size[1] - logo.size[1] - metr.logo_bottom)) top = metr.author_top tbox = TextBox( metr.width - metr.author_margin_left - metr.author_margin_right, metr.height - top, ) author_font = ImageFont.truetype(self.author_font_ttf, metr.author_font_size) tbox.text(self.pretty_author(), self.author_color, author_font, metr.author_lineskip, self.author_shadow) text_img = tbox.image() img.paste(text_img, (metr.author_margin_left, top), text_img) top += text_img.size[1] + metr.title_top tbox = TextBox( metr.width - metr.title_margin_left - metr.title_margin_right, metr.height - top, ) title_font = ImageFont.truetype(self.title_font_ttf, metr.title_font_size) tbox.text(self.pretty_title(), self.title_color, title_font, metr.title_lineskip, self.title_shadow) text_img = tbox.image() img.paste(text_img, (metr.title_margin_left, top), text_img) return img def final_image(self): img = self.image() if self.scale_after != 1: img = img.resize((int(round(img.size[0] * self.scale_after)), int(round(img.size[1] * self.scale_after))), Image.ANTIALIAS) return img def mime_type(self): return self.mime_types[self.format] def ext(self): return self.exts[self.format] def save(self, *args, **kwargs): default_kwargs = { 'format': self.format, 'quality': 95, } default_kwargs.update(kwargs) return self.final_image().save(*args, **default_kwargs) def output_file(self, *args, **kwargs): imgstr = StringIO() self.save(imgstr, *args, **kwargs) return OutputFile.from_string(imgstr.getvalue())
def transform_file(wldoc, chunk_counter=1, first=True, sample=None): """ processes one input file and proceeds to its children """ replace_characters(wldoc.edoc.getroot()) hyphenator = set_hyph_language(wldoc.edoc.getroot()) hyphenate_and_fix_conjunctions(wldoc.edoc.getroot(), hyphenator) # every input file will have a TOC entry, # pointing to starting chunk toc = TOC(wldoc.book_info.title, "part%d.html" % chunk_counter) chars = set() if first: # write book title page html_tree = xslt(wldoc.edoc, get_resource('epub/xsltTitle.xsl')) chars = used_chars(html_tree.getroot()) zip.writestr( 'OPS/title.html', etree.tostring( html_tree, pretty_print=True, xml_declaration=True, encoding="utf-8", doctype='<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN"' + ' "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">' ) ) # add a title page TOC entry toc.add(u"Strona tytułowa", "title.html") elif wldoc.book_info.parts: # write title page for every parent if sample is not None and sample <= 0: chars = set() html_string = open(get_resource('epub/emptyChunk.html')).read() else: html_tree = xslt(wldoc.edoc, get_resource('epub/xsltChunkTitle.xsl')) chars = used_chars(html_tree.getroot()) html_string = etree.tostring( html_tree, pretty_print=True, xml_declaration=True, encoding="utf-8", doctype='<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN"' + ' "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">' ) zip.writestr('OPS/part%d.html' % chunk_counter, html_string) add_to_manifest(manifest, chunk_counter) add_to_spine(spine, chunk_counter) chunk_counter += 1 if len(wldoc.edoc.getroot()) > 1: # rdf before style master main_text = wldoc.edoc.getroot()[1] else: # rdf in style master main_text = wldoc.edoc.getroot()[0] if main_text.tag == RDFNS('RDF'): main_text = None if main_text is not None: for chunk_xml in chop(main_text): empty = False if sample is not None: if sample <= 0: empty = True else: sample -= len(chunk_xml.xpath('//strofa|//akap|//akap_cd|//akap_dialog')) chunk_html, chunk_toc, chunk_chars = transform_chunk(chunk_xml, chunk_counter, annotations, empty) toc.extend(chunk_toc) chars = chars.union(chunk_chars) zip.writestr('OPS/part%d.html' % chunk_counter, chunk_html) add_to_manifest(manifest, chunk_counter) add_to_spine(spine, chunk_counter) chunk_counter += 1 for child in wldoc.parts(): child_toc, chunk_counter, chunk_chars, sample = transform_file( child, chunk_counter, first=False, sample=sample) toc.append(child_toc) chars = chars.union(chunk_chars) return toc, chunk_counter, chars, sample
def sponsor_logo(name): return { 'Narodowe Centrum Kultury': get_resource('res/sponsors/nck.png') }.get(name.strip())
class WLCover(Cover): """Wolne Lektury cover without logos.""" width = 600 height = 833 uses_dc_cover = True author_font_ttf = get_resource('fonts/JunicodeWL-Regular.ttf') author_font_size = 20 author_lineskip = 30 title_font_ttf = get_resource('fonts/DejaVuSerif-Bold.ttf') title_font_size = 30 title_lineskip = 40 title_box_width = 350 box_top_margin = 100 box_bottom_margin = 100 box_padding_y = 20 box_above_line = 10 box_below_line = 15 box_line_left = 75 box_line_right = 275 box_line_width = 2 logo_top = 15 logo_width = 140 bar_width = 35 bar_color = '#000' box_position = 'middle' background_color = '#444' author_color = '#444' background_img = get_resource('res/cover.png') format = 'JPEG' epoch_colors = { u'Starożytność': '#9e3610', u'Średniowiecze': '#564c09', u'Renesans': '#8ca629', u'Barok': '#a6820a', u'Oświecenie': '#f2802e', u'Romantyzm': '#db4b16', u'Pozytywizm': '#961060', u'Modernizm': '#7784e0', u'Dwudziestolecie międzywojenne': '#3044cf', u'Współczesność': '#06393d', } kind_box_position = { u'Liryka': 'top', u'Epika': 'bottom', } def __init__(self, book_info, format=None, width=None, height=None, bleed=0): super(WLCover, self).__init__(book_info, format=format, width=width, height=height) # Set box position. self.box_position = book_info.cover_box_position or \ self.kind_box_position.get(book_info.kind, self.box_position) # Set bar color. if book_info.cover_bar_color == 'none': self.bar_width = 0 else: self.bar_color = book_info.cover_bar_color or \ self.epoch_colors.get(book_info.epoch, self.bar_color) # Set title color. self.title_color = self.epoch_colors.get(book_info.epoch, self.title_color) self.bleed = bleed self.box_top_margin += bleed self.box_bottom_margin += bleed self.bar_width += bleed if book_info.cover_url: url = book_info.cover_url bg_src = None if bg_src is None: bg_src = URLOpener().open(url) self.background_img = StringIO(bg_src.read()) bg_src.close() def pretty_author(self): return self.author.upper() def add_box(self, img): if self.box_position == 'none': return img metr = Metric(self, self.scale) # Write author name. box = TextBox(metr.title_box_width, metr.height, padding_y=metr.box_padding_y) author_font = ImageFont.truetype(self.author_font_ttf, metr.author_font_size) box.text(self.pretty_author(), font=author_font, line_height=metr.author_lineskip, color=self.author_color, shadow_color=self.author_shadow) box.skip(metr.box_above_line) box.draw.line( (metr.box_line_left, box.height, metr.box_line_right, box.height), fill=self.author_color, width=metr.box_line_width) box.skip(metr.box_below_line) # Write title. title_font = ImageFont.truetype(self.title_font_ttf, metr.title_font_size) box.text(self.pretty_title(), line_height=metr.title_lineskip, font=title_font, color=self.title_color, shadow_color=self.title_shadow) box_img = box.image() # Find box position. if self.box_position == 'top': box_top = metr.box_top_margin elif self.box_position == 'bottom': box_top = metr.height - metr.box_bottom_margin - box_img.size[1] else: # Middle. box_top = (metr.height - box_img.size[1]) / 2 box_left = metr.bar_width + (metr.width - metr.bar_width - box_img.size[0]) / 2 # Draw the white box. ImageDraw.Draw(img).rectangle( (box_left, box_top, box_left + box_img.size[0], box_top + box_img.size[1]), fill='#fff') # Paste the contents into the white box. img.paste(box_img, (box_left, box_top), box_img) return img def add_cut_lines(self, img): line_ratio = 0.5 if self.bleed == 0: return img metr = Metric(self, self.scale) draw = ImageDraw.Draw(img) for corner_x, corner_y in ((0, 0), (metr.width, 0), (0, metr.height), (metr.width, metr.height)): dir_x = 1 if corner_x == 0 else -1 dir_y = 1 if corner_y == 0 else -1 for offset in (-1, 0, 1): draw.line((corner_x, corner_y + dir_y * metr.bleed + offset, corner_x + dir_x * metr.bleed * line_ratio, corner_y + dir_y * metr.bleed + offset), fill='black' if offset == 0 else 'white', width=1) draw.line((corner_x + dir_x * metr.bleed + offset, corner_y, corner_x + dir_x * metr.bleed + offset, corner_y + dir_y * metr.bleed * line_ratio), fill='black' if offset == 0 else 'white', width=1) return img def image(self): metr = Metric(self, self.scale) img = Image.new('RGB', (metr.width, metr.height), self.background_color) draw = ImageDraw.Draw(img) draw.rectangle((0, 0, metr.bar_width, metr.height), fill=self.bar_color) if self.background_img: src = Image.open(self.background_img) trg_size = (metr.width - metr.bar_width, metr.height) if src.size[0] * trg_size[1] < src.size[1] * trg_size[0]: resized = (trg_size[0], src.size[1] * trg_size[0] / src.size[0]) cut = (resized[1] - trg_size[1]) / 2 src = src.resize(resized, Image.ANTIALIAS) src = src.crop((0, cut, src.size[0], src.size[1] - cut)) else: resized = ( src.size[0] * trg_size[1] / src.size[1], trg_size[1], ) cut = (resized[0] - trg_size[0]) / 2 src = src.resize(resized, Image.ANTIALIAS) src = src.crop((cut, 0, src.size[0] - cut, src.size[1])) img.paste(src, (metr.bar_width, 0)) del src img = self.add_box(img) img = self.add_cut_lines(img) return img