diff --git a/docs/guides/feature-flags.rst b/docs/guides/feature-flags.rst index 91ba814356b..7435e0ed806 100644 --- a/docs/guides/feature-flags.rst +++ b/docs/guides/feature-flags.rst @@ -12,6 +12,8 @@ or disable one or more of these featured flags for a particular project. Available Flags --------------- +``USE_PDF_LATEXMK``: :featureflags:`USE_PDF_LATEXMK` + ``USE_SPHINX_LATEST``: :featureflags:`USE_SPHINX_LATEST` ``USE_SETUPTOOLS_LATEST``: :featureflags:`USE_SETUPTOOLS_LATEST` diff --git a/readthedocs/doc_builder/backends/sphinx.py b/readthedocs/doc_builder/backends/sphinx.py index 99592139433..2938ce8d325 100644 --- a/readthedocs/doc_builder/backends/sphinx.py +++ b/readthedocs/doc_builder/backends/sphinx.py @@ -6,12 +6,14 @@ .. _Sphinx: http://www.sphinx-doc.org/ """ import codecs +import itertools import logging import os import shutil import sys import zipfile from glob import glob +from pathlib import Path from django.conf import settings from django.template import loader as template_loader @@ -151,6 +153,9 @@ def get_config_params(self): 'dont_overwrite_sphinx_context': self.project.has_feature( Feature.DONT_OVERWRITE_SPHINX_CONTEXT, ), + 'use_pdf_latexmk': self.project.has_feature( + Feature.USE_PDF_LATEXMK, + ), } finalize_sphinx_context_data.send( @@ -395,6 +400,67 @@ def build(self): raise BuildEnvironmentError('No TeX files were found') # Run LaTeX -> PDF conversions + if self.project.has_feature(Feature.USE_PDF_LATEXMK): + return self._build_latexmk(cwd, latex_cwd) + + return self._build_pdflatex(tex_files, latex_cwd) + + def _build_latexmk(self, cwd, latex_cwd): + # These steps are copied from the Makefile generated by Sphinx >= 1.6 + # https://github.com/sphinx-doc/sphinx/blob/master/sphinx/texinputs/Makefile_t + latex_path = Path(latex_cwd) + images = [] + for extension in ('png', 'gif', 'jpg', 'jpeg'): + images.extend(latex_path.glob(f'*.{extension}')) + + # FIXME: instead of checking by language here, what we want to check if + # ``latex_engine`` is ``platex`` + pdfs = [] + if self.project.language == 'ja': + # Japanese language is the only one that requires this extra + # step. I don't know exactly why but most of the documentation that + # I read differentiate this language from the others. I suppose + # it's because it mix kanji (Chinese) with its own symbols. + pdfs = latex_path.glob('*.pdf') + + for image in itertools.chain(images, pdfs): + self.run( + 'extractbb', + image.name, + cwd=latex_cwd, + record=False, + ) + + rcfile = 'latexmkrc' + if self.project.language == 'ja': + rcfile = 'latexmkjarc' + + self.run( + 'cat', + rcfile, + cwd=latex_cwd, + ) + + cmd = self.run( + 'latexmk', + '-r', + rcfile, + + # FIXME: check for platex here as well + '-pdfdvi' if self.project.language == 'ja' else '-pdf', + + '-dvi-', + '-ps-', + f'-jobname={self.project.slug}', + warn_only=True, + cwd=latex_cwd, + ) + + self.pdf_file_name = f'{self.project.slug}.pdf' + + return cmd.successful + + def _build_pdflatex(self, tex_files, latex_cwd): pdflatex_cmds = [ ['pdflatex', '-interaction=nonstopmode', tex_file] for tex_file in tex_files diff --git a/readthedocs/doc_builder/templates/doc_builder/conf.py.tmpl b/readthedocs/doc_builder/templates/doc_builder/conf.py.tmpl index 9a842eac0d6..c1e8b12f5db 100644 --- a/readthedocs/doc_builder/templates/doc_builder/conf.py.tmpl +++ b/readthedocs/doc_builder/templates/doc_builder/conf.py.tmpl @@ -140,3 +140,38 @@ if 'extensions' in globals(): extensions.insert(0, "readthedocs_ext.readthedocs") else: extensions = ["readthedocs_ext.readthedocs"] + +{% if use_pdf_latexmk %} +project_language = '{{ project.language }}' + +# User's Sphinx configurations +language_user = globals().get('language', None) +latex_engine_user = globals().get('latex_engine', None) +latex_elements_user = globals().get('latex_elements', None) + +chinese = any([ + language_user in ('zh_CN', 'zh_TW'), + project_language in ('zh_CN', 'zh_TW'), +]) + +japanase = any([ + language_user == 'ja', + project_language == 'ja', +]) + +if chinese: + latex_engine = latex_engine_user or 'xelatex' + + # Remove this once xindy gets installed in Docker image and XINDYOPS + # env variable is supported + # https://github.com/rtfd/readthedocs-docker-images/pull/98 + latex_use_xindy = False + + latex_elements_rtd = { + 'preamble': '\\usepackage[UTF8]{ctex}\n', + } + latex_elements = latex_elements_user or latex_elements_rtd +elif japanase: + latex_engine = latex_engine_user or 'platex' + latex_use_xindy = False +{% endif %} diff --git a/readthedocs/projects/models.py b/readthedocs/projects/models.py index 94f154861e1..0a23a333bb6 100644 --- a/readthedocs/projects/models.py +++ b/readthedocs/projects/models.py @@ -1314,10 +1314,12 @@ def add_features(sender, **kwargs): DONT_SHALLOW_CLONE = 'dont_shallow_clone' USE_TESTING_BUILD_IMAGE = 'use_testing_build_image' SHARE_SPHINX_DOCTREE = 'share_sphinx_doctree' + USE_PDF_LATEXMK = 'use_pdf_latexmk' FEATURES = ( (USE_SPHINX_LATEST, _('Use latest version of Sphinx')), (USE_SETUPTOOLS_LATEST, _('Use latest version of setuptools')), + (USE_PDF_LATEXMK, _('Use latexmk to build the PDF')), (ALLOW_DEPRECATED_WEBHOOKS, _('Allow deprecated webhook views')), (PIP_ALWAYS_UPGRADE, _('Always run pip install --upgrade')), (SKIP_SUBMODULES, _('Skip git submodule checkout')),