summaryrefslogtreecommitdiffstats
path: root/source-builder/sb/asciidoc/a2x.py
diff options
context:
space:
mode:
Diffstat (limited to '')
-rwxr-xr-xsource-builder/sb/asciidoc/a2x.py960
1 files changed, 0 insertions, 960 deletions
diff --git a/source-builder/sb/asciidoc/a2x.py b/source-builder/sb/asciidoc/a2x.py
deleted file mode 100755
index 1b192a2..0000000
--- a/source-builder/sb/asciidoc/a2x.py
+++ /dev/null
@@ -1,960 +0,0 @@
-#!/usr/bin/env python
-'''
-a2x - A toolchain manager for AsciiDoc (converts Asciidoc text files to other
- file formats)
-
-Copyright: Stuart Rackham (c) 2009
-License: MIT
-Email: srackham@gmail.com
-
-'''
-
-import os
-import fnmatch
-import HTMLParser
-import re
-import shutil
-import subprocess
-import sys
-import traceback
-import urlparse
-import zipfile
-import xml.dom.minidom
-import mimetypes
-
-PROG = os.path.basename(os.path.splitext(__file__)[0])
-VERSION = '8.6.8'
-
-# AsciiDoc global configuration file directory.
-# NOTE: CONF_DIR is "fixed up" by Makefile -- don't rename or change syntax.
-CONF_DIR = '/etc/asciidoc'
-
-
-######################################################################
-# Default configuration file parameters.
-######################################################################
-
-# Optional environment variable dictionary passed to
-# executing programs. If set to None the existing
-# environment is used.
-ENV = None
-
-# External executables.
-ASCIIDOC = 'asciidoc'
-XSLTPROC = 'xsltproc'
-DBLATEX = 'dblatex' # pdf generation.
-FOP = 'fop' # pdf generation (--fop option).
-W3M = 'w3m' # text generation.
-LYNX = 'lynx' # text generation (if no w3m).
-XMLLINT = 'xmllint' # Set to '' to disable.
-EPUBCHECK = 'epubcheck' # Set to '' to disable.
-# External executable default options.
-ASCIIDOC_OPTS = ''
-DBLATEX_OPTS = ''
-FOP_OPTS = ''
-XSLTPROC_OPTS = ''
-BACKEND_OPTS = ''
-
-######################################################################
-# End of configuration file parameters.
-######################################################################
-
-
-#####################################################################
-# Utility functions
-#####################################################################
-
-OPTIONS = None # These functions read verbose and dry_run command options.
-
-def errmsg(msg):
- sys.stderr.write('%s: %s\n' % (PROG,msg))
-
-def warning(msg):
- errmsg('WARNING: %s' % msg)
-
-def infomsg(msg):
- print '%s: %s' % (PROG,msg)
-
-def die(msg, exit_code=1):
- errmsg('ERROR: %s' % msg)
- sys.exit(exit_code)
-
-def trace():
- """Print traceback to stderr."""
- errmsg('-'*60)
- traceback.print_exc(file=sys.stderr)
- errmsg('-'*60)
-
-def verbose(msg):
- if OPTIONS.verbose or OPTIONS.dry_run:
- infomsg(msg)
-
-class AttrDict(dict):
- """
- Like a dictionary except values can be accessed as attributes i.e. obj.foo
- can be used in addition to obj['foo'].
- If self._default has been set then it will be returned if a non-existant
- attribute is accessed (instead of raising an AttributeError).
- """
- def __getattr__(self, key):
- try:
- return self[key]
- except KeyError, k:
- if self.has_key('_default'):
- return self['_default']
- else:
- raise AttributeError, k
- def __setattr__(self, key, value):
- self[key] = value
- def __delattr__(self, key):
- try: del self[key]
- except KeyError, k: raise AttributeError, k
- def __repr__(self):
- return '<AttrDict ' + dict.__repr__(self) + '>'
- def __getstate__(self):
- return dict(self)
- def __setstate__(self,value):
- for k,v in value.items(): self[k]=v
-
-def isexecutable(file_name):
- return os.path.isfile(file_name) and os.access(file_name, os.X_OK)
-
-def find_executable(file_name):
- '''
- Search for executable file_name in the system PATH.
- Return full path name or None if not found.
- '''
- def _find_executable(file_name):
- if os.path.split(file_name)[0] != '':
- # file_name includes directory so don't search path.
- if not isexecutable(file_name):
- return None
- else:
- return file_name
- for p in os.environ.get('PATH', os.defpath).split(os.pathsep):
- f = os.path.join(p, file_name)
- if isexecutable(f):
- return os.path.realpath(f)
- return None
- if os.name == 'nt' and os.path.splitext(file_name)[1] == '':
- for ext in ('.cmd','.bat','.exe'):
- result = _find_executable(file_name + ext)
- if result: break
- else:
- result = _find_executable(file_name)
- return result
-
-def write_file(filename, data, mode='w'):
- f = open(filename, mode)
- try:
- f.write(data)
- finally:
- f.close()
-
-def read_file(filename, mode='r'):
- f = open(filename, mode)
- try:
- return f.read()
- finally:
- f.close()
-
-def shell_cd(path):
- verbose('chdir %s' % path)
- if not OPTIONS.dry_run:
- os.chdir(path)
-
-def shell_makedirs(path):
- if os.path.isdir(path):
- return
- verbose('creating %s' % path)
- if not OPTIONS.dry_run:
- os.makedirs(path)
-
-def shell_copy(src, dst):
- verbose('copying "%s" to "%s"' % (src,dst))
- if not OPTIONS.dry_run:
- shutil.copy(src, dst)
-
-def shell_rm(path):
- if not os.path.exists(path):
- return
- verbose('deleting %s' % path)
- if not OPTIONS.dry_run:
- os.unlink(path)
-
-def shell_rmtree(path):
- if not os.path.isdir(path):
- return
- verbose('deleting %s' % path)
- if not OPTIONS.dry_run:
- shutil.rmtree(path)
-
-def shell(cmd, raise_error=True):
- '''
- Execute command cmd in shell and return tuple
- (stdoutdata, stderrdata, returncode).
- If raise_error is True then a non-zero return terminates the application.
- '''
- if os.name == 'nt':
- # TODO: this is probably unnecessary, see:
- # http://groups.google.com/group/asciidoc/browse_frm/thread/9442ee0c419f1242
- # Windows doesn't like running scripts directly so explicitly
- # specify python interpreter.
- # Extract first (quoted or unquoted) argument.
- mo = re.match(r'^\s*"\s*(?P<arg0>[^"]+)\s*"', cmd)
- if not mo:
- mo = re.match(r'^\s*(?P<arg0>[^ ]+)', cmd)
- if mo.group('arg0').endswith('.py'):
- cmd = 'python ' + cmd
- # Remove redundant quoting -- this is not just cosmetic,
- # quoting seems to dramatically decrease the allowed command
- # length in Windows XP.
- cmd = re.sub(r'"([^ ]+?)"', r'\1', cmd)
- verbose('executing: %s' % cmd)
- if OPTIONS.dry_run:
- return
- stdout = stderr = subprocess.PIPE
- try:
- popen = subprocess.Popen(cmd, stdout=stdout, stderr=stderr,
- shell=True, env=ENV)
- except OSError, e:
- die('failed: %s: %s' % (cmd, e))
- stdoutdata, stderrdata = popen.communicate()
- if OPTIONS.verbose:
- print stdoutdata
- print stderrdata
- if popen.returncode != 0 and raise_error:
- die('%s returned non-zero exit status %d' % (cmd, popen.returncode))
- return (stdoutdata, stderrdata, popen.returncode)
-
-def find_resources(files, tagname, attrname, filter=None):
- '''
- Search all files and return a list of local URIs from attrname attribute
- values in tagname tags.
- Handles HTML open and XHTML closed tags.
- Non-local URIs are skipped.
- files can be a file name or a list of file names.
- The filter function takes a dictionary of tag attributes and returns True if
- the URI is to be included.
- '''
- class FindResources(HTMLParser.HTMLParser):
- # Nested parser class shares locals with enclosing function.
- def handle_startendtag(self, tag, attrs):
- self.handle_starttag(tag, attrs)
- def handle_starttag(self, tag, attrs):
- attrs = dict(attrs)
- if tag == tagname and (filter is None or filter(attrs)):
- # Accept only local URIs.
- uri = urlparse.urlparse(attrs[attrname])
- if uri[0] in ('','file') and not uri[1] and uri[2]:
- result.append(uri[2])
- if isinstance(files, str):
- files = [files]
- result = []
- for filename in files:
- verbose('finding resources in: %s' % filename)
- if OPTIONS.dry_run:
- continue
- parser = FindResources()
- # HTMLParser has problems with non-ASCII strings.
- # See http://bugs.python.org/issue3932
- contents = read_file(filename)
- mo = re.search(r'\A<\?xml.* encoding="(.*?)"', contents)
- if mo:
- encoding = mo.group(1)
- parser.feed(contents.decode(encoding))
- else:
- parser.feed(contents)
- parser.close()
- result = list(set(result)) # Drop duplicate values.
- result.sort()
- return result
-
-# NOT USED.
-def copy_files(files, src_dir, dst_dir):
- '''
- Copy list of relative file names from src_dir to dst_dir.
- '''
- for filename in files:
- filename = os.path.normpath(filename)
- if os.path.isabs(filename):
- continue
- src = os.path.join(src_dir, filename)
- dst = os.path.join(dst_dir, filename)
- if not os.path.exists(dst):
- if not os.path.isfile(src):
- warning('missing file: %s' % src)
- continue
- dstdir = os.path.dirname(dst)
- shell_makedirs(dstdir)
- shell_copy(src, dst)
-
-def find_files(path, pattern):
- '''
- Return list of file names matching pattern in directory path.
- '''
- result = []
- for (p,dirs,files) in os.walk(path):
- for f in files:
- if fnmatch.fnmatch(f, pattern):
- result.append(os.path.normpath(os.path.join(p,f)))
- return result
-
-def exec_xsltproc(xsl_file, xml_file, dst_dir, opts = ''):
- cwd = os.getcwd()
- shell_cd(dst_dir)
- try:
- shell('"%s" %s "%s" "%s"' % (XSLTPROC, opts, xsl_file, xml_file))
- finally:
- shell_cd(cwd)
-
-def get_source_options(asciidoc_file):
- '''
- Look for a2x command options in AsciiDoc source file.
- Limitation: options cannot contain double-quote characters.
- '''
- def parse_options():
- # Parse options to result sequence.
- inquotes = False
- opt = ''
- for c in options:
- if c == '"':
- if inquotes:
- result.append(opt)
- opt = ''
- inquotes = False
- else:
- inquotes = True
- elif c == ' ':
- if inquotes:
- opt += c
- elif opt:
- result.append(opt)
- opt = ''
- else:
- opt += c
- if opt:
- result.append(opt)
-
- result = []
- if os.path.isfile(asciidoc_file):
- options = ''
- f = open(asciidoc_file)
- try:
- for line in f:
- mo = re.search(r'^//\s*a2x:', line)
- if mo:
- options += ' ' + line[mo.end():].strip()
- finally:
- f.close()
- parse_options()
- return result
-
-
-#####################################################################
-# Application class
-#####################################################################
-
-class A2X(AttrDict):
- '''
- a2x options and conversion functions.
- '''
-
- def execute(self):
- '''
- Process a2x command.
- '''
- self.process_options()
- # Append configuration file options.
- self.asciidoc_opts += ' ' + ASCIIDOC_OPTS
- self.dblatex_opts += ' ' + DBLATEX_OPTS
- self.fop_opts += ' ' + FOP_OPTS
- self.xsltproc_opts += ' ' + XSLTPROC_OPTS
- self.backend_opts += ' ' + BACKEND_OPTS
- # Execute to_* functions.
- if self.backend:
- self.to_backend()
- else:
- self.__getattribute__('to_'+self.format)()
- if not (self.keep_artifacts or self.format == 'docbook' or self.skip_asciidoc):
- shell_rm(self.dst_path('.xml'))
-
- def load_conf(self):
- '''
- Load a2x configuration file from default locations and --conf-file
- option.
- '''
- global ASCIIDOC
- CONF_FILE = 'a2x.conf'
- a2xdir = os.path.dirname(os.path.realpath(__file__))
- conf_files = []
- # From a2x.py directory.
- conf_files.append(os.path.join(a2xdir, CONF_FILE))
- # If the asciidoc executable and conf files are in the a2x directory
- # then use the local copy of asciidoc and skip the global a2x conf.
- asciidoc = os.path.join(a2xdir, 'asciidoc.py')
- asciidoc_conf = os.path.join(a2xdir, 'asciidoc.conf')
- if os.path.isfile(asciidoc) and os.path.isfile(asciidoc_conf):
- self.asciidoc = asciidoc
- else:
- self.asciidoc = None
- # From global conf directory.
- conf_files.append(os.path.join(CONF_DIR, CONF_FILE))
- # From $HOME directory.
- home_dir = os.environ.get('HOME')
- if home_dir is not None:
- conf_files.append(os.path.join(home_dir, '.asciidoc', CONF_FILE))
- # If asciidoc is not local to a2x then search the PATH.
- if not self.asciidoc:
- self.asciidoc = find_executable(ASCIIDOC)
- if not self.asciidoc:
- die('unable to find asciidoc: %s' % ASCIIDOC)
- # From backend plugin directory.
- if self.backend is not None:
- stdout = shell(self.asciidoc + ' --backend list')[0]
- backends = [(i, os.path.split(i)[1]) for i in stdout.splitlines()]
- backend_dir = [i[0] for i in backends if i[1] == self.backend]
- if len(backend_dir) == 0:
- die('missing %s backend' % self.backend)
- if len(backend_dir) > 1:
- die('more than one %s backend' % self.backend)
- verbose('found %s backend directory: %s' %
- (self.backend, backend_dir[0]))
- conf_files.append(os.path.join(backend_dir[0], 'a2x-backend.py'))
- # From --conf-file option.
- if self.conf_file is not None:
- if not os.path.isfile(self.conf_file):
- die('missing configuration file: %s' % self.conf_file)
- conf_files.append(self.conf_file)
- # From --xsl-file option.
- if self.xsl_file is not None:
- if not os.path.isfile(self.xsl_file):
- die('missing XSL file: %s' % self.xsl_file)
- self.xsl_file = os.path.abspath(self.xsl_file)
- # Load ordered files.
- for f in conf_files:
- if os.path.isfile(f):
- verbose('loading configuration file: %s' % f)
- execfile(f, globals())
-
- def process_options(self):
- '''
- Validate and command options and set defaults.
- '''
- if not os.path.isfile(self.asciidoc_file):
- die('missing SOURCE_FILE: %s' % self.asciidoc_file)
- self.asciidoc_file = os.path.abspath(self.asciidoc_file)
- if not self.destination_dir:
- self.destination_dir = os.path.dirname(self.asciidoc_file)
- else:
- if not os.path.isdir(self.destination_dir):
- die('missing --destination-dir: %s' % self.destination_dir)
- self.destination_dir = os.path.abspath(self.destination_dir)
- self.resource_dirs = []
- self.resource_files = []
- if self.resource_manifest:
- if not os.path.isfile(self.resource_manifest):
- die('missing --resource-manifest: %s' % self.resource_manifest)
- f = open(self.resource_manifest)
- try:
- for r in f:
- self.resources.append(r.strip())
- finally:
- f.close()
- for r in self.resources:
- r = os.path.expanduser(r)
- r = os.path.expandvars(r)
- if r.endswith('/') or r.endswith('\\'):
- if os.path.isdir(r):
- self.resource_dirs.append(r)
- else:
- die('missing resource directory: %s' % r)
- elif os.path.isdir(r):
- self.resource_dirs.append(r)
- elif r.startswith('.') and '=' in r:
- ext, mimetype = r.split('=')
- mimetypes.add_type(mimetype, ext)
- else:
- self.resource_files.append(r)
- for p in (os.path.dirname(self.asciidoc), CONF_DIR):
- for d in ('images','stylesheets'):
- d = os.path.join(p,d)
- if os.path.isdir(d):
- self.resource_dirs.append(d)
- verbose('resource files: %s' % self.resource_files)
- verbose('resource directories: %s' % self.resource_dirs)
- if not self.doctype and self.format == 'manpage':
- self.doctype = 'manpage'
- if self.doctype:
- self.asciidoc_opts += ' --doctype %s' % self.doctype
- for attr in self.attributes:
- self.asciidoc_opts += ' --attribute "%s"' % attr
-# self.xsltproc_opts += ' --nonet'
- if self.verbose:
- self.asciidoc_opts += ' --verbose'
- self.dblatex_opts += ' -V'
- if self.icons or self.icons_dir:
- params = [
- 'callout.graphics 1',
- 'navig.graphics 1',
- 'admon.textlabel 0',
- 'admon.graphics 1',
- ]
- if self.icons_dir:
- params += [
- 'admon.graphics.path "%s/"' % self.icons_dir,
- 'callout.graphics.path "%s/callouts/"' % self.icons_dir,
- 'navig.graphics.path "%s/"' % self.icons_dir,
- ]
- else:
- params = [
- 'callout.graphics 0',
- 'navig.graphics 0',
- 'admon.textlabel 1',
- 'admon.graphics 0',
- ]
- if self.stylesheet:
- params += ['html.stylesheet "%s"' % self.stylesheet]
- if self.format == 'htmlhelp':
- params += ['htmlhelp.chm "%s"' % self.basename('.chm'),
- 'htmlhelp.hhp "%s"' % self.basename('.hhp'),
- 'htmlhelp.hhk "%s"' % self.basename('.hhk'),
- 'htmlhelp.hhc "%s"' % self.basename('.hhc')]
- if self.doctype == 'book':
- params += ['toc.section.depth 1']
- # Books are chunked at chapter level.
- params += ['chunk.section.depth 0']
- for o in params:
- if o.split()[0]+' ' not in self.xsltproc_opts:
- self.xsltproc_opts += ' --stringparam ' + o
- if self.fop_opts:
- self.fop = True
- if os.path.splitext(self.asciidoc_file)[1].lower() == '.xml':
- self.skip_asciidoc = True
- else:
- self.skip_asciidoc = False
-
- def dst_path(self, ext):
- '''
- Return name of file or directory in the destination directory with
- the same name as the asciidoc source file but with extension ext.
- '''
- return os.path.join(self.destination_dir, self.basename(ext))
-
- def basename(self, ext):
- '''
- Return the base name of the asciidoc source file but with extension
- ext.
- '''
- return os.path.basename(os.path.splitext(self.asciidoc_file)[0]) + ext
-
- def asciidoc_conf_file(self, path):
- '''
- Return full path name of file in asciidoc configuration files directory.
- Search first the directory containing the asciidoc executable then
- the global configuration file directory.
- '''
- f = os.path.join(os.path.dirname(self.asciidoc), path)
- if not os.path.isfile(f):
- f = os.path.join(CONF_DIR, path)
- if not os.path.isfile(f):
- die('missing configuration file: %s' % f)
- return os.path.normpath(f)
-
- def xsl_stylesheet(self, file_name=None):
- '''
- Return full path name of file in asciidoc docbook-xsl configuration
- directory.
- If an XSL file was specified with the --xsl-file option then it is
- returned.
- '''
- if self.xsl_file is not None:
- return self.xsl_file
- if not file_name:
- file_name = self.format + '.xsl'
- return self.asciidoc_conf_file(os.path.join('docbook-xsl', file_name))
-
- def copy_resources(self, html_files, src_dir, dst_dir, resources=[]):
- '''
- Search html_files for images and CSS resource URIs (html_files can be a
- list of file names or a single file name).
- Copy them from the src_dir to the dst_dir.
- If not found in src_dir then recursively search all specified
- resource directories.
- Optional additional resources files can be passed in the resources list.
- '''
- resources = resources[:]
- resources += find_resources(html_files, 'link', 'href',
- lambda attrs: attrs.get('type') == 'text/css')
- resources += find_resources(html_files, 'img', 'src')
- resources += self.resource_files
- resources = list(set(resources)) # Drop duplicates.
- resources.sort()
- for f in resources:
- if '=' in f:
- src, dst = f.split('=')
- if not dst:
- dst = src
- else:
- src = dst = f
- src = os.path.normpath(src)
- dst = os.path.normpath(dst)
- if os.path.isabs(dst):
- die('absolute resource file name: %s' % dst)
- if dst.startswith(os.pardir):
- die('resource file outside destination directory: %s' % dst)
- src = os.path.join(src_dir, src)
- dst = os.path.join(dst_dir, dst)
- if not os.path.isfile(src):
- for d in self.resource_dirs:
- d = os.path.join(src_dir, d)
- found = find_files(d, os.path.basename(src))
- if found:
- src = found[0]
- break
- else:
- if not os.path.isfile(dst):
- die('missing resource: %s' % src)
- continue
- # Arrive here if resource file has been found.
- if os.path.normpath(src) != os.path.normpath(dst):
- dstdir = os.path.dirname(dst)
- shell_makedirs(dstdir)
- shell_copy(src, dst)
-
- def to_backend(self):
- '''
- Convert AsciiDoc source file to a backend output file using the global
- 'to_<backend name>' function (loaded from backend plugin a2x-backend.py
- file).
- Executes the global function in an A2X class instance context.
- '''
- eval('to_%s(self)' % self.backend)
-
- def to_docbook(self):
- '''
- Use asciidoc to convert asciidoc_file to DocBook.
- args is a string containing additional asciidoc arguments.
- '''
- docbook_file = self.dst_path('.xml')
- if self.skip_asciidoc:
- if not os.path.isfile(docbook_file):
- die('missing docbook file: %s' % docbook_file)
- return
- shell('"%s" --backend docbook -a "a2x-format=%s" %s --out-file "%s" "%s"' %
- (self.asciidoc, self.format, self.asciidoc_opts, docbook_file, self.asciidoc_file))
- if not self.no_xmllint and XMLLINT:
- shell('"%s" --nonet --noout --valid "%s"' % (XMLLINT, docbook_file))
-
- def to_xhtml(self):
- self.to_docbook()
- docbook_file = self.dst_path('.xml')
- xhtml_file = self.dst_path('.html')
- opts = '%s --output "%s"' % (self.xsltproc_opts, xhtml_file)
- exec_xsltproc(self.xsl_stylesheet(), docbook_file, self.destination_dir, opts)
- src_dir = os.path.dirname(self.asciidoc_file)
- self.copy_resources(xhtml_file, src_dir, self.destination_dir)
-
- def to_manpage(self):
- self.to_docbook()
- docbook_file = self.dst_path('.xml')
- opts = self.xsltproc_opts
- exec_xsltproc(self.xsl_stylesheet(), docbook_file, self.destination_dir, opts)
-
- def to_pdf(self):
- if self.fop:
- self.exec_fop()
- else:
- self.exec_dblatex()
-
- def exec_fop(self):
- self.to_docbook()
- docbook_file = self.dst_path('.xml')
- xsl = self.xsl_stylesheet('fo.xsl')
- fo = self.dst_path('.fo')
- pdf = self.dst_path('.pdf')
- opts = '%s --output "%s"' % (self.xsltproc_opts, fo)
- exec_xsltproc(xsl, docbook_file, self.destination_dir, opts)
- shell('"%s" %s -fo "%s" -pdf "%s"' % (FOP, self.fop_opts, fo, pdf))
- if not self.keep_artifacts:
- shell_rm(fo)
-
- def exec_dblatex(self):
- self.to_docbook()
- docbook_file = self.dst_path('.xml')
- xsl = self.asciidoc_conf_file(os.path.join('dblatex','asciidoc-dblatex.xsl'))
- sty = self.asciidoc_conf_file(os.path.join('dblatex','asciidoc-dblatex.sty'))
- shell('"%s" -t %s -p "%s" -s "%s" %s "%s"' %
- (DBLATEX, self.format, xsl, sty, self.dblatex_opts, docbook_file))
-
- def to_dvi(self):
- self.exec_dblatex()
-
- def to_ps(self):
- self.exec_dblatex()
-
- def to_tex(self):
- self.exec_dblatex()
-
- def to_htmlhelp(self):
- self.to_chunked()
-
- def to_chunked(self):
- self.to_docbook()
- docbook_file = self.dst_path('.xml')
- opts = self.xsltproc_opts
- xsl_file = self.xsl_stylesheet()
- if self.format == 'chunked':
- dst_dir = self.dst_path('.chunked')
- elif self.format == 'htmlhelp':
- dst_dir = self.dst_path('.htmlhelp')
- if not 'base.dir ' in opts:
- opts += ' --stringparam base.dir "%s/"' % os.path.basename(dst_dir)
- # Create content.
- shell_rmtree(dst_dir)
- shell_makedirs(dst_dir)
- exec_xsltproc(xsl_file, docbook_file, self.destination_dir, opts)
- html_files = find_files(dst_dir, '*.html')
- src_dir = os.path.dirname(self.asciidoc_file)
- self.copy_resources(html_files, src_dir, dst_dir)
-
- def update_epub_manifest(self, opf_file):
- '''
- Scan the OEBPS directory for any files that have not been registered in
- the OPF manifest then add them to the manifest.
- '''
- opf_dir = os.path.dirname(opf_file)
- resource_files = []
- for (p,dirs,files) in os.walk(os.path.dirname(opf_file)):
- for f in files:
- f = os.path.join(p,f)
- if os.path.isfile(f):
- assert f.startswith(opf_dir)
- f = '.' + f[len(opf_dir):]
- f = os.path.normpath(f)
- if f not in ['content.opf']:
- resource_files.append(f)
- opf = xml.dom.minidom.parseString(read_file(opf_file))
- manifest_files = []
- manifest = opf.getElementsByTagName('manifest')[0]
- for el in manifest.getElementsByTagName('item'):
- f = el.getAttribute('href')
- f = os.path.normpath(f)
- manifest_files.append(f)
- count = 0
- for f in resource_files:
- if f not in manifest_files:
- count += 1
- verbose('adding to manifest: %s' % f)
- item = opf.createElement('item')
- item.setAttribute('href', f.replace(os.path.sep, '/'))
- item.setAttribute('id', 'a2x-%d' % count)
- mimetype = mimetypes.guess_type(f)[0]
- if mimetype is None:
- die('unknown mimetype: %s' % f)
- item.setAttribute('media-type', mimetype)
- manifest.appendChild(item)
- if count > 0:
- write_file(opf_file, opf.toxml())
-
- def to_epub(self):
- self.to_docbook()
- xsl_file = self.xsl_stylesheet()
- docbook_file = self.dst_path('.xml')
- epub_file = self.dst_path('.epub')
- build_dir = epub_file + '.d'
- shell_rmtree(build_dir)
- shell_makedirs(build_dir)
- # Create content.
- exec_xsltproc(xsl_file, docbook_file, build_dir, self.xsltproc_opts)
- # Copy resources referenced in the OPF and resources referenced by the
- # generated HTML (in theory DocBook XSL should ensure they are
- # identical but this is not always the case).
- src_dir = os.path.dirname(self.asciidoc_file)
- dst_dir = os.path.join(build_dir, 'OEBPS')
- opf_file = os.path.join(dst_dir, 'content.opf')
- opf_resources = find_resources(opf_file, 'item', 'href')
- html_files = find_files(dst_dir, '*.html')
- self.copy_resources(html_files, src_dir, dst_dir, opf_resources)
- # Register any unregistered resources.
- self.update_epub_manifest(opf_file)
- # Build epub archive.
- cwd = os.getcwd()
- shell_cd(build_dir)
- try:
- if not self.dry_run:
- zip = zipfile.ZipFile(epub_file, 'w')
- try:
- # Create and add uncompressed mimetype file.
- verbose('archiving: mimetype')
- write_file('mimetype', 'application/epub+zip')
- zip.write('mimetype', compress_type=zipfile.ZIP_STORED)
- # Compress all remaining files.
- for (p,dirs,files) in os.walk('.'):
- for f in files:
- f = os.path.normpath(os.path.join(p,f))
- if f != 'mimetype':
- verbose('archiving: %s' % f)
- zip.write(f, compress_type=zipfile.ZIP_DEFLATED)
- finally:
- zip.close()
- verbose('created archive: %s' % epub_file)
- finally:
- shell_cd(cwd)
- if not self.keep_artifacts:
- shell_rmtree(build_dir)
- if self.epubcheck and EPUBCHECK:
- if not find_executable(EPUBCHECK):
- warning('epubcheck skipped: unable to find executable: %s' % EPUBCHECK)
- else:
- shell('"%s" "%s"' % (EPUBCHECK, epub_file))
-
- def to_text(self):
- text_file = self.dst_path('.text')
- html_file = self.dst_path('.text.html')
- if self.lynx:
- shell('"%s" %s --conf-file "%s" -b html4 -a "a2x-format=%s" -o "%s" "%s"' %
- (self.asciidoc, self.asciidoc_opts, self.asciidoc_conf_file('text.conf'),
- self.format, html_file, self.asciidoc_file))
- shell('"%s" -dump "%s" > "%s"' %
- (LYNX, html_file, text_file))
- else:
- # Use w3m(1).
- self.to_docbook()
- docbook_file = self.dst_path('.xml')
- opts = '%s --output "%s"' % (self.xsltproc_opts, html_file)
- exec_xsltproc(self.xsl_stylesheet(), docbook_file,
- self.destination_dir, opts)
- shell('"%s" -cols 70 -dump -T text/html -no-graph "%s" > "%s"' %
- (W3M, html_file, text_file))
- if not self.keep_artifacts:
- shell_rm(html_file)
-
-
-#####################################################################
-# Script main line.
-#####################################################################
-
-if __name__ == '__main__':
- description = '''A toolchain manager for AsciiDoc (converts Asciidoc text files to other file formats)'''
- from optparse import OptionParser
- parser = OptionParser(usage='usage: %prog [OPTIONS] SOURCE_FILE',
- version='%s %s' % (PROG,VERSION),
- description=description)
- parser.add_option('-a', '--attribute',
- action='append', dest='attributes', default=[], metavar='ATTRIBUTE',
- help='set asciidoc attribute value')
- parser.add_option('--asciidoc-opts',
- action='append', dest='asciidoc_opts', default=[],
- metavar='ASCIIDOC_OPTS', help='asciidoc options')
- #DEPRECATED
- parser.add_option('--copy',
- action='store_true', dest='copy', default=False,
- help='DEPRECATED: does nothing')
- parser.add_option('--conf-file',
- dest='conf_file', default=None, metavar='CONF_FILE',
- help='configuration file')
- parser.add_option('-D', '--destination-dir',
- action='store', dest='destination_dir', default=None, metavar='PATH',
- help='output directory (defaults to SOURCE_FILE directory)')
- parser.add_option('-d','--doctype',
- action='store', dest='doctype', metavar='DOCTYPE',
- choices=('article','manpage','book'),
- help='article, manpage, book')
- parser.add_option('-b','--backend',
- action='store', dest='backend', metavar='BACKEND',
- help='name of backend plugin')
- parser.add_option('--epubcheck',
- action='store_true', dest='epubcheck', default=False,
- help='check EPUB output with epubcheck')
- parser.add_option('-f','--format',
- action='store', dest='format', metavar='FORMAT', default = 'pdf',
- choices=('chunked','epub','htmlhelp','manpage','pdf', 'text',
- 'xhtml','dvi','ps','tex','docbook'),
- help='chunked, epub, htmlhelp, manpage, pdf, text, xhtml, dvi, ps, tex, docbook')
- parser.add_option('--icons',
- action='store_true', dest='icons', default=False,
- help='use admonition, callout and navigation icons')
- parser.add_option('--icons-dir',
- action='store', dest='icons_dir',
- default=None, metavar='PATH',
- help='admonition and navigation icon directory')
- parser.add_option('-k', '--keep-artifacts',
- action='store_true', dest='keep_artifacts', default=False,
- help='do not delete temporary build files')
- parser.add_option('--lynx',
- action='store_true', dest='lynx', default=False,
- help='use lynx to generate text files')
- parser.add_option('-L', '--no-xmllint',
- action='store_true', dest='no_xmllint', default=False,
- help='do not check asciidoc output with xmllint')
- parser.add_option('-n','--dry-run',
- action='store_true', dest='dry_run', default=False,
- help='just print the commands that would have been executed')
- parser.add_option('-r','--resource',
- action='append', dest='resources', default=[],
- metavar='PATH',
- help='resource file or directory containing resource files')
- parser.add_option('-m', '--resource-manifest',
- action='store', dest='resource_manifest', default=None, metavar='FILE',
- help='read resources from FILE')
- #DEPRECATED
- parser.add_option('--resource-dir',
- action='append', dest='resources', default=[],
- metavar='PATH',
- help='DEPRECATED: use --resource')
- #DEPRECATED
- parser.add_option('-s','--skip-asciidoc',
- action='store_true', dest='skip_asciidoc', default=False,
- help='DEPRECATED: redundant')
- parser.add_option('--stylesheet',
- action='store', dest='stylesheet', default=None,
- metavar='STYLESHEET',
- help='HTML CSS stylesheet file name')
- #DEPRECATED
- parser.add_option('--safe',
- action='store_true', dest='safe', default=False,
- help='DEPRECATED: does nothing')
- parser.add_option('--dblatex-opts',
- action='append', dest='dblatex_opts', default=[],
- metavar='DBLATEX_OPTS', help='dblatex options')
- parser.add_option('--backend-opts',
- action='append', dest='backend_opts', default=[],
- metavar='BACKEND_OPTS', help='backend plugin options')
- parser.add_option('--fop',
- action='store_true', dest='fop', default=False,
- help='use FOP to generate PDF files')
- parser.add_option('--fop-opts',
- action='append', dest='fop_opts', default=[],
- metavar='FOP_OPTS', help='options for FOP pdf generation')
- parser.add_option('--xsltproc-opts',
- action='append', dest='xsltproc_opts', default=[],
- metavar='XSLTPROC_OPTS', help='xsltproc options for XSL stylesheets')
- parser.add_option('--xsl-file',
- action='store', dest='xsl_file', metavar='XSL_FILE',
- help='custom XSL stylesheet')
- parser.add_option('-v', '--verbose',
- action='count', dest='verbose', default=0,
- help='increase verbosity')
- if len(sys.argv) == 1:
- parser.parse_args(['--help'])
- source_options = get_source_options(sys.argv[-1])
- argv = source_options + sys.argv[1:]
- opts, args = parser.parse_args(argv)
- if len(args) != 1:
- parser.error('incorrect number of arguments')
- opts.asciidoc_opts = ' '.join(opts.asciidoc_opts)
- opts.dblatex_opts = ' '.join(opts.dblatex_opts)
- opts.fop_opts = ' '.join(opts.fop_opts)
- opts.xsltproc_opts = ' '.join(opts.xsltproc_opts)
- opts.backend_opts = ' '.join(opts.backend_opts)
- opts = eval(str(opts)) # Convert optparse.Values to dict.
- a2x = A2X(opts)
- OPTIONS = a2x # verbose and dry_run used by utility functions.
- verbose('args: %r' % argv)
- a2x.asciidoc_file = args[0]
- try:
- a2x.load_conf()
- a2x.execute()
- except KeyboardInterrupt:
- exit(1)