From b90f319e371744fb017b5ce3c9de1c2d58816aa3 Mon Sep 17 00:00:00 2001 From: Alastair Houghton Date: Tue, 13 Jan 2015 13:25:28 +0000 Subject: [PATCH] Added initial Apple help support. --- sphinx/builders/__init__.py | 1 + sphinx/builders/applehelp.py | 158 +++++++++++++++++++++++++++++++++++ sphinx/config.py | 18 ++++ sphinx/quickstart.py | 45 ++++++++++ 4 files changed, 222 insertions(+) create mode 100644 sphinx/builders/applehelp.py diff --git a/sphinx/builders/__init__.py b/sphinx/builders/__init__.py index abc8fc74c1..82bd7ec0b8 100644 --- a/sphinx/builders/__init__.py +++ b/sphinx/builders/__init__.py @@ -442,6 +442,7 @@ BUILTIN_BUILDERS = { 'htmlhelp': ('htmlhelp', 'HTMLHelpBuilder'), 'devhelp': ('devhelp', 'DevhelpBuilder'), 'qthelp': ('qthelp', 'QtHelpBuilder'), + 'applehelp': ('applehelp', 'AppleHelpBuilder'), 'epub': ('epub', 'EpubBuilder'), 'latex': ('latex', 'LaTeXBuilder'), 'text': ('text', 'TextBuilder'), diff --git a/sphinx/builders/applehelp.py b/sphinx/builders/applehelp.py new file mode 100644 index 0000000000..90f1f35b82 --- /dev/null +++ b/sphinx/builders/applehelp.py @@ -0,0 +1,158 @@ +# -*- coding: utf-8 -*- +""" + sphinx.builders.applehelp + ~~~~~~~~~~~~~~~~~~~~~~~~~ + + Build Apple help books. + + :copyright: Copyright 2007-2015 by the Sphinx team, see AUTHORS. + :license: BSD, see LICENSE for details. +""" +from __future__ import print_function + +import os +import codecs +import errno + +from os import path + +from sphinx.builders.html import StandaloneHTMLBuilder +from sphinx.util.osutil import copyfile, ensuredir, os_path +from sphinx.util.console import bold +from sphinx.errors import SphinxError + +import plistlib +import subprocess + + +# Use plistlib.dump in 3.4 and above +try: + write_plist = plistlib.dump +except AttributeError: + write_plist = plistlib.writePlist + + +class AppleHelpIndexerFailed(SphinxError): + def __str__(self): + return 'Help indexer failed' + + +class AppleHelpBuilder(StandaloneHTMLBuilder): + """ + Builder that outputs an Apple help book. Requires Mac OS X as it relies + on the ``hiutil`` command line tool. + """ + name = 'applehelp' + + # don't copy the reST source + copysource = False + supported_image_types = ['image/png', 'image/gif', 'image/jpeg', + 'image/tiff', 'image/jp2', 'image/svg+xml'] + + # don't add links + add_permalinks = False + # *do* add the sidebar (Apple Help doesn't have its own) + embedded = False + + def init(self): + super(AppleHelpBuilder, self).init() + # the output files for HTML help must be .html only + self.out_suffix = '.html' + + self.bundle_path = path.join(self.outdir, + self.config.applehelp_bundle_name \ + + '.help') + self.outdir = path.join(self.bundle_path, + 'Contents', + 'Resources', + (self.config.language or 'en') + '.lproj') + + def handle_finish(self): + contents_dir = path.join(self.bundle_path, 'Contents') + resources_dir = path.join(contents_dir, 'Resources') + language_dir = path.join(resources_dir, + (self.config.language or 'en') + '.lproj') + + for d in [contents_dir, resources_dir, language_dir]: + ensuredir(d) + + # Construct the Info.plist file + info_plist = { + 'CFBundleDevelopmentRegion': self.config.applehelp_dev_region, + 'CFBundleIdentifier': self.config.applehelp_bundle_id, + 'CFBundleInfoDictionaryVersion': 6.0, + 'CFBundleName': self.config.applehelp_bundle_name, + 'CFBundlePackageType': 'BNDL', + 'CFBundleShortVersionString': self.config.release, + 'CFBundleSignature': 'hbwr', + 'CFBundleVersion': self.config.applehelp_bundle_version, + 'CFBundleHelpTOCFile': 'index.html', + 'HPDBookAccessPath': 'index.html', + 'HPDBookIndexPath': 'index.helpindex', + 'HPDBookTitle': self.config.html_title, + 'HPDBookType': 3, + } + + if self.config.applehelp_icon is not None: + info_plist['HPDBookIconPath'] \ + = path.basename(self.config.applehelp_icon) + + if self.config.applehelp_kb_url is not None: + info_plist['HPDBookKBProduct'] = self.config.applehelp_kb_product + info_plist['HPDBookKBURL'] = self.config.applehelp_kb_url + + if self.config.applehelp_remote_url is not None: + info_plist['HPDBookRemoteURL'] = self.config.applehelp_remote_url + + self.info(bold('writing Info.plist... '), nonl=True) + f = codecs.open(path.join(contents_dir, 'Info.plist'), 'w') + try: + write_plist(info_plist, f) + finally: + f.close() + self.info('done') + + # Copy the icon, if one is supplied + if self.config.applehelp_icon: + self.info(bold('copying icon... '), nonl=True) + + try: + copyfile(path.join(self.srcdir, self.config.applehelp_icon), + path.join(resources_dir, info_plist['HPDBookIconPath'])) + + self.info('done') + except Exception as err: + self.warn('cannot copy icon file %r: %s' % + (path.join(self.srcdir, self.config.applehelp_icon), + err)) + del info_plist['HPDBookIconPath'] + + # Generate the help index + self.info(bold('generating help index... '), nonl=True) + + args = [ + '/usr/bin/hiutil', + '-Cf', + path.join(language_dir, 'index.helpindex'), + language_dir + ] + + if self.config.applehelp_index_anchors is not None: + args.append('-a') + + if self.config.applehelp_min_term_length is not None: + args += ['-m', '%s' % self.config.applehelp_min_term_length] + + if self.config.applehelp_stopwords is not None: + args += ['-s', self.config.applehelp_stopwords] + + if self.config.applehelp_locale is not None: + args += ['-l', self.config.applehelp_locale] + + result = subprocess.call(args) + + if result != 0: + raise AppleHelpIndexerFailed + else: + self.info('done') + diff --git a/sphinx/config.py b/sphinx/config.py index 145e60675e..0336b60cb7 100644 --- a/sphinx/config.py +++ b/sphinx/config.py @@ -129,6 +129,24 @@ class Config(object): # Devhelp only options devhelp_basename = (lambda self: make_filename(self.project), None), + # Apple help only options + applehelp_bundle_name = (lambda self: make_filename(self.project), + 'applehelp'), + applehelp_bundle_id = (lambda self: 'com.mycompany.%s.help' \ + % make_filename(self.project), 'applehelp'), + applehelp_dev_region = ('en_us', 'applehelp'), + applehelp_bundle_version = (1, 'applehelp'), + applehelp_icon = (None, 'applehelp'), + applehelp_kb_product = (lambda self: '%s-%s' \ + % (make_filename(self.project), self.release), + 'applehelp'), + applehelp_kb_url = (None, 'applehelp'), + applehelp_remote_url = (None, 'applehelp'), + applehelp_index_anchors = (False, 'applehelp'), + applehelp_min_term_length = (None, 'applehelp'), + applehelp_stopwords = (lambda self: self.language or 'en', 'applehelp'), + applehelp_locale = (lambda self: self.language or 'en_us', 'applehelp'), + # Epub options epub_basename = (lambda self: make_filename(self.project), None), epub_theme = ('epub', 'html'), diff --git a/sphinx/quickstart.py b/sphinx/quickstart.py index 3d3d3eb9fd..2d2e33405f 100644 --- a/sphinx/quickstart.py +++ b/sphinx/quickstart.py @@ -32,6 +32,7 @@ except ImportError: from six import PY2, PY3, text_type from six.moves import input +from six.moves.urllib.parse import quote as urlquote from docutils.utils import column_width from sphinx import __version__ @@ -266,6 +267,43 @@ html_static_path = ['%(dot)sstatic'] # Output file base name for HTML help builder. htmlhelp_basename = '%(project_fn)sdoc' +# -- Options for Apple Help output ---------------------------------------- + +# The bundle name. +#applehelp_bundle_name = u'%(project_fn)s' + +# The bundle id. +#applehelp_bundle_id = 'com.mycompany.%(project_url)s.help' + +# The development region. Should be 'en_us' in most cases. +#applehelp_dev_region = 'en_us' + +# The bundle version. +#applehelp_bundle_version = 1 + +# The icon file +#applehelp_icon = '%(project_fn)s.icns' + +# These allow remote searching of a knowledge base on your server +#applehelp_kb_url = "https://kb.example.com/search?p='product'&q='query'&l='lang'" +#applehelp_kb_product = '%(project_fn)s-%(release)s' + +# This lets you host a remote copy of the documentation that you can update +# without having to ship new versions of your application +#applehelp_remote_url = 'https://help.example.com/%(project_fn)s/%(version)s/' + +# Whether to index anchors +#applehelp_index_anchors = False + +# Minimum term length for indexing +#applehelp_min_term_length = None + +# Stop words (either a language identifier, for built-in stop words, or a plist) +#applehelp_stopwords = %(language)r + +# Locale for indexing +#applehelp_locale = %(language)r + # -- Options for LaTeX output --------------------------------------------- latex_elements = { @@ -491,6 +529,7 @@ help: \t@echo " json to make JSON files" \t@echo " htmlhelp to make HTML files and a HTML help project" \t@echo " qthelp to make HTML files and a qthelp project" +\t@echo " applehelp to make an Apple Help Book" \t@echo " devhelp to make HTML files and a Devhelp project" \t@echo " epub to make an epub" \t@echo " latex to make LaTeX files, you can set PAPER=a4 or PAPER=letter" @@ -552,6 +591,11 @@ qthelp: \t@echo "To view the help file:" \t@echo "# assistant -collectionFile $(BUILDDIR)/qthelp/%(project_fn)s.qhc" +applehelp: +\t$(SPHINXBUILD) -b applehelp $(ALLSPHINXOPTS) $(BUILDDIR)/applehelp +\t@echo +\t@echo "Build finished. The help book is in $(BUILDDIR)/applehelp." + devhelp: \t$(SPHINXBUILD) -b devhelp $(ALLSPHINXOPTS) $(BUILDDIR)/devhelp \t@echo @@ -1273,6 +1317,7 @@ def generate(d, overwrite=True, silent=False): d['mastertocmaxdepth'] = 2 d['project_fn'] = make_filename(d['project']) + d['project_url'] = urlquote(d['project']) d['project_manpage'] = d['project_fn'].lower() d['now'] = time.asctime() d['project_underline'] = column_width(d['project']) * '='