Source code for bkgen.idml


import logging
log = logging.getLogger(__name__)

import os
from lxml import etree
from bl.dict import Dict
from bl.id import random_id
from bl.zip import ZIP
from bl.string import String
from bxml import XML
from .icml import ICML
from bkgen import NS
from bkgen.source import Source

[docs]class IDML(ZIP, Source): POINTS_PER_EM = ICML.POINTS_PER_EM NS = ICML.NS @property def basename(self): return os.path.splitext(os.path.basename(self.fn))[0] @property def output_path(self): # a directory next to the .idml file with the same basename return os.path.splitext(self.fn)[0] @property def design_map(self, path=None): path = path or self.output_path return ICML(root=self.read('designmap.xml'))
[docs] def items_dict(self): """returns a dict of items (anything with a Self) in the IDML file. Needed to resolve Article components.""" d = Dict() for rp in [rp for rp in self.zipfile.namelist() if os.path.splitext(rp)[-1].lower()=='.xml']: x = XML(root=self.read(rp)) for item in x.root.xpath("//*[@Self] | //idPkg:*[@Self]", namespaces=self.NS): assert item.get('Self') not in d d[item.get('Self')] = item return d
[docs] def documents(self, path=None, articles=True, **params): """return a collection of pub:documents from the stories in the .idml file. If the .idml file has Articles, use those as guidance; otherwise, use the stories directly. """ path = path if path is not None else self.output_path designmap = self.design_map(path=path) if articles==True and len(designmap.root.xpath("//Article")) > 0: return self.articles_documents(path=path, designmap=designmap) else: documents = [] for story in designmap.root.xpath("idPkg:Story", namespaces=self.NS): # use a temporary file for the story source, just in case it's huge tfn = os.path.join(os.path.dirname(self.fn), random_id()) with open(tfn, 'wb') as tf: tf.write(self.read(story.get('src'))) root = etree.parse(tfn).getroot() os.remove(tfn) icml = ICML(root=root) document_fn = os.path.join(path, story.get('src')) log.debug(document_fn) document = icml.document(fn=document_fn) documents.append(document) # fix links between documents ids = {} for d in documents: for e in d.root.xpath("//*[@id]"): ids[e.get('id')] = d.fn for doc in documents: for e in doc.root.xpath("//pub:include[@id]", namespaces=NS): if e.get('id') in ids: id = e.attrib.pop('id') targetfn = ids[id] e.set('src', os.path.relpath(targetfn, os.path.dirname(doc.fn)) + '#' + id) return documents
[docs] def articles_documents(self, path=None, designmap=None): """return a collection of pub:documents built from the InDesign Articles in the .idml file.""" path = path if path is not None else self.output_path designmap = designmap or self.design_map() itemsdict = self.items_dict() documents = [] for article in designmap.root.xpath("//Article"): icml = ICML(root=etree.Element('Document')) story_ids = [] for member in article.xpath("ArticleMember"): item = itemsdict[member.get('ItemRef')] log.info("ItemRef=%r => %r ParentStory=%r" % (member.get('ItemRef'), item.tag, item.get('ParentStory'))) if item.get('ParentStory') is not None: story_id = item.get('ParentStory') if story_id not in story_ids: story = itemsdict[story_id] log.info(" %r Self=%r" % (story.tag, story.get('Self'))) icml.root.append(story) story_ids.append(story_id) else: for elem in item.xpath(".//*[@ParentStory]", namespaces=self.NS): story_id = elem.get('ParentStory') log.info(" %r ParentStory=%r" % (elem, story_id)) if story_id not in story_ids: story = itemsdict[story_id] log.info(" %r Self=%r" % (story.tag, story.get('Self'))) icml.root.append(story) story_ids.append(story_id) fb = "%s_%s.xml" % (self.basename, String(article.get('Name')).hyphenify()) document_fn = os.path.join(path, fb) log.debug(document_fn) document = icml.document(fn=document_fn) documents.append(document) log.debug(document.fn) return documents
[docs] def stylesheet(self, fn=None, points_per_em=POINTS_PER_EM): """return a stylesheet from the .idml file's style definitions""" if fn is None: fn = os.path.join(self.output_path, os.path.basename(self.output_path)+'.css') return ICML(root=self.read('Resources/Styles.xml')).stylesheet( fn=fn, points_per_em=points_per_em)