diff --git a/CHANGES b/CHANGES index 82f2171d0..ad43fbf7a 100644 --- a/CHANGES +++ b/CHANGES @@ -11,6 +11,7 @@ Features added -------------- * #3470: Make genindex support all kinds of letters, not only Latin ones +* latex macros for directional double quotes (refs #3527) Bugs fixed ---------- @@ -25,6 +26,9 @@ Bugs fixed are same without creation date. Thanks to Yoshiki Shibukawa. * #3487: intersphinx: failed to refer options * #3496: latex longtable's last column may be much wider than its contents +* #3507: wrong quotes in latex output for productionlist directive +* #3533: Moving from Sphinx 1.3.1 to 1.5.3 breaks LaTeX compilation of links + rendered as code * #2665, #2607: Link names in C++ docfields, and make it possible for other domains. Testing diff --git a/doc/latex.rst b/doc/latex.rst index ebc14765e..d42878e85 100644 --- a/doc/latex.rst +++ b/doc/latex.rst @@ -438,6 +438,14 @@ Let us now list some macros from the package file the new macros are wrappers of the formerly hard-coded ``\texttt``, ``\emph``, ... The default definitions can be found in :file:`sphinx.sty`. +- macros for directional double quotes: pairs of straight double quote ``"`` + in reST source are converted into LaTeX mark-up + ``\sphinxquotedblleft{}`` and ``\sphinxquotedblright{}`` which default to + `````\ ````` and ``''`` (i.e. the TeX mark-up for directional double + quotes via font ligaturing mechanism.) + + .. versionadded:: 1.5.4 + Formerly, produced TeX was directly with `````\ ````` and ``''``. - paragraph level environments: for each admonition type ````, the used environment is named ``sphinx``. They may be ``\renewenvironment`` 'd individually, and must then be defined with one argument (it is the heading diff --git a/sphinx/texinputs/sphinx.sty b/sphinx/texinputs/sphinx.sty index 1a006cb40..475769028 100644 --- a/sphinx/texinputs/sphinx.sty +++ b/sphinx/texinputs/sphinx.sty @@ -287,7 +287,7 @@ \fi % fix a space-gobbling issue due to LaTeX's original \do@noligs \let\do@noligs\sphinx@do@noligs - \@noligs\endlinechar\m@ne\everyeof{\noexpand}% + \@noligs\endlinechar\m@ne \expandafter\scantokens \fi {\texttt{#1}}}} \def\sphinx@do@noligs #1{\catcode`#1\active\begingroup\lccode`\~`#1\relax @@ -592,22 +592,6 @@ \newcommand*\sphinxbreaksatspaceinparsedliteral{% \lccode`~32 \lowercase{\let~}\spx@verbatim@space\lccode`\~`\~ } -% now the hack for \url to work (hyperref is assumed in use): -% the aim it to deactivate - . , ; ? ! / in \url's argument. -% also the space token, but not end of lines which we assume don't arise there. -\def\spx@hack@hyper@normalise {% - \expandafter\spx@hack@hyper@normalise@aux\hyper@normalise - \spx@hack@hyper@normalise@aux\hyper@n@rmalise\relax\spx@undefined -}% -\long\def\spx@hack@hyper@normalise@aux#1\hyper@n@rmalise#2#3\spx@undefined{% - \ifx\spx@hack@hyper@normalise@aux#2% - \def\hyper@normalise{#1\sphinxunactivateextrasandspace\hyper@n@rmalise}% - \else - \PackageWarning{sphinx}{Could not patch \string\url\space command.% - ^^J Not using extra active characters in alltt environment}% - \sphinxunactivateextras - \fi -}% \newcommand*{\sphinxunactivateextras}{\let\do\@makeother \sphinxbreaksbeforeactivelist\sphinxbreaksafteractivelist\do\-}% % the \catcode13=5\relax (deactivate end of input lines) is left to callers @@ -623,17 +607,33 @@ \sphinxbreaksattexescapedchars \sphinxbreaksviaactiveinparsedliteral \sphinxbreaksatspaceinparsedliteral - \spx@hack@hyper@normalise % alltt takes care of the ' as derivative ("prime") in math mode \everymath\expandafter{\the\everymath\sphinxunactivateextrasandspace \catcode`\<=12\catcode`\>=12\catcode`\^=7\catcode`\_=8 }% % not sure if displayed math (align,...) can end up in parsed-literal, anyway \everydisplay\expandafter{\the\everydisplay - \catcode13=5\sphinxunactivateextrasandspace + \catcode13=5 \sphinxunactivateextrasandspace \catcode`\<=12\catcode`\>=12\catcode`\^=7\catcode`\_=8 }% \fi } {\end{alltt}} +% Protect \href's first argument in contexts such as sphinxalltt (or +% \sphinxcode). Sphinx uses \#, \%, \& ... always inside \sphinxhref. +\protected\def\sphinxhref#1#2{{% + \sphinxunactivateextrasandspace % never do \scantokens with active space! + \endlinechar\m@ne\everyeof{{#2}}% keep catcode regime for #2 + \scantokens{\href{#1}}% normalise it for #1 during \href expansion +}} +% Same for \url. And also \nolinkurl for coherence. +\protected\def\sphinxurl#1{{% + \sphinxunactivateextrasandspace + \endlinechar\m@ne\scantokens{\url{#1}}% +}} +\protected\def\sphinxnolinkurl#1{{% + \sphinxunactivateextrasandspace + \endlinechar\m@ne\scantokens{\nolinkurl{#1}}% +}} + % Topic boxes % Again based on use of "framed.sty", this allows breakable framed boxes. @@ -1243,6 +1243,10 @@ \protected\def\sphinxstyleabbreviation {\textsc} \protected\def\sphinxstyleliteralintitle {\sphinxcode} +% LaTeX writer uses macros to hide double quotes from \sphinxcode's \@noligs +\protected\def\sphinxquotedblleft{``} +\protected\def\sphinxquotedblright{''} + % stylesheet for highlighting with pygments \RequirePackage{sphinxhighlight} diff --git a/sphinx/writers/latex.py b/sphinx/writers/latex.py index 6e408448d..f7f39eeaf 100644 --- a/sphinx/writers/latex.py +++ b/sphinx/writers/latex.py @@ -1755,12 +1755,12 @@ class LaTeXTranslator(nodes.NodeVisitor): elif uri.startswith(URI_SCHEMES): if len(node) == 1 and uri == node[0]: if node.get('nolinkurl'): - self.body.append('\\nolinkurl{%s}' % self.encode_uri(uri)) + self.body.append('\\sphinxnolinkurl{%s}' % self.encode_uri(uri)) else: - self.body.append('\\url{%s}' % self.encode_uri(uri)) + self.body.append('\\sphinxurl{%s}' % self.encode_uri(uri)) raise nodes.SkipNode else: - self.body.append('\\href{%s}{' % self.encode_uri(uri)) + self.body.append('\\sphinxhref{%s}{' % self.encode_uri(uri)) self.context.append('}') elif uri.startswith('#'): # references to labels in the same document @@ -2209,7 +2209,9 @@ class LaTeXTranslator(nodes.NodeVisitor): def visit_Text(self, node): text = self.encode(node.astext()) if not self.no_contractions and not self.in_parsed_literal: - text = educate_quotes_latex(text) + text = educate_quotes_latex(text, + dquotes=("\\sphinxquotedblleft{}", + "\\sphinxquotedblright{}")) self.body.append(text) def depart_Text(self, node): diff --git a/tests/test_build_latex.py b/tests/test_build_latex.py index 6c8861f1b..73a154fb4 100644 --- a/tests/test_build_latex.py +++ b/tests/test_build_latex.py @@ -551,20 +551,20 @@ def test_latex_show_urls_is_inline(app, status, warning): 'First\n%\n\\end{footnote}') in result assert ('Second footnote: %\n\\begin{footnote}[1]\\sphinxAtStartFootnote\n' 'Second\n%\n\\end{footnote}') in result - assert '\\href{http://sphinx-doc.org/}{Sphinx} (http://sphinx-doc.org/)' in result + assert '\\sphinxhref{http://sphinx-doc.org/}{Sphinx} (http://sphinx-doc.org/)' in result assert ('Third footnote: %\n\\begin{footnote}[3]\\sphinxAtStartFootnote\n' 'Third\n%\n\\end{footnote}') in result - assert ('\\href{http://sphinx-doc.org/~test/}{URL including tilde} ' + assert ('\\sphinxhref{http://sphinx-doc.org/~test/}{URL including tilde} ' '(http://sphinx-doc.org/\\textasciitilde{}test/)') in result - assert ('\\item[{\\href{http://sphinx-doc.org/}{URL in term} ' + assert ('\\item[{\\sphinxhref{http://sphinx-doc.org/}{URL in term} ' '(http://sphinx-doc.org/)}] \\leavevmode\nDescription' in result) assert ('\\item[{Footnote in term \\sphinxfootnotemark[5]}] ' '\\leavevmode%\n\\begin{footnotetext}[5]\\sphinxAtStartFootnote\n' 'Footnote in term\n%\n\\end{footnotetext}\nDescription') in result - assert ('\\item[{\\href{http://sphinx-doc.org/}{Term in deflist} ' + assert ('\\item[{\\sphinxhref{http://sphinx-doc.org/}{Term in deflist} ' '(http://sphinx-doc.org/)}] \\leavevmode\nDescription') in result - assert '\\url{https://github.com/sphinx-doc/sphinx}\n' in result - assert ('\\href{mailto:sphinx-dev@googlegroups.com}' + assert '\\sphinxurl{https://github.com/sphinx-doc/sphinx}\n' in result + assert ('\\sphinxhref{mailto:sphinx-dev@googlegroups.com}' '{sphinx-dev@googlegroups.com}') in result @@ -594,29 +594,29 @@ def test_latex_show_urls_is_footnote(app, status, warning): 'First\n%\n\\end{footnote}') in result assert ('Second footnote: %\n\\begin{footnote}[1]\\sphinxAtStartFootnote\n' 'Second\n%\n\\end{footnote}') in result - assert ('\\href{http://sphinx-doc.org/}{Sphinx}' + assert ('\\sphinxhref{http://sphinx-doc.org/}{Sphinx}' '%\n\\begin{footnote}[4]\\sphinxAtStartFootnote\n' - '\\nolinkurl{http://sphinx-doc.org/}\n%\n\\end{footnote}') in result + '\\sphinxnolinkurl{http://sphinx-doc.org/}\n%\n\\end{footnote}') in result assert ('Third footnote: %\n\\begin{footnote}[6]\\sphinxAtStartFootnote\n' 'Third\n%\n\\end{footnote}') in result - assert ('\\href{http://sphinx-doc.org/~test/}{URL including tilde}' + assert ('\\sphinxhref{http://sphinx-doc.org/~test/}{URL including tilde}' '%\n\\begin{footnote}[5]\\sphinxAtStartFootnote\n' - '\\nolinkurl{http://sphinx-doc.org/~test/}\n%\n\\end{footnote}') in result - assert ('\\item[{\\href{http://sphinx-doc.org/}' + '\\sphinxnolinkurl{http://sphinx-doc.org/~test/}\n%\n\\end{footnote}') in result + assert ('\\item[{\\sphinxhref{http://sphinx-doc.org/}' '{URL in term}\\sphinxfootnotemark[8]}] ' '\\leavevmode%\n\\begin{footnotetext}[8]\\sphinxAtStartFootnote\n' - '\\nolinkurl{http://sphinx-doc.org/}\n%\n' + '\\sphinxnolinkurl{http://sphinx-doc.org/}\n%\n' '\\end{footnotetext}\nDescription') in result assert ('\\item[{Footnote in term \\sphinxfootnotemark[10]}] ' '\\leavevmode%\n\\begin{footnotetext}[10]\\sphinxAtStartFootnote\n' 'Footnote in term\n%\n\\end{footnotetext}\nDescription') in result - assert ('\\item[{\\href{http://sphinx-doc.org/}{Term in deflist}' + assert ('\\item[{\\sphinxhref{http://sphinx-doc.org/}{Term in deflist}' '\\sphinxfootnotemark[9]}] ' '\\leavevmode%\n\\begin{footnotetext}[9]\\sphinxAtStartFootnote\n' - '\\nolinkurl{http://sphinx-doc.org/}\n%\n' + '\\sphinxnolinkurl{http://sphinx-doc.org/}\n%\n' '\\end{footnotetext}\nDescription') in result - assert ('\\url{https://github.com/sphinx-doc/sphinx}\n' in result) - assert ('\\href{mailto:sphinx-dev@googlegroups.com}' + assert ('\\sphinxurl{https://github.com/sphinx-doc/sphinx}\n' in result) + assert ('\\sphinxhref{mailto:sphinx-dev@googlegroups.com}' '{sphinx-dev@googlegroups.com}\n') in result @@ -646,19 +646,19 @@ def test_latex_show_urls_is_no(app, status, warning): 'First\n%\n\\end{footnote}') in result assert ('Second footnote: %\n\\begin{footnote}[1]\\sphinxAtStartFootnote\n' 'Second\n%\n\\end{footnote}') in result - assert '\\href{http://sphinx-doc.org/}{Sphinx}' in result + assert '\\sphinxhref{http://sphinx-doc.org/}{Sphinx}' in result assert ('Third footnote: %\n\\begin{footnote}[3]\\sphinxAtStartFootnote\n' 'Third\n%\n\\end{footnote}') in result - assert '\\href{http://sphinx-doc.org/~test/}{URL including tilde}' in result - assert ('\\item[{\\href{http://sphinx-doc.org/}{URL in term}}] ' + assert '\\sphinxhref{http://sphinx-doc.org/~test/}{URL including tilde}' in result + assert ('\\item[{\\sphinxhref{http://sphinx-doc.org/}{URL in term}}] ' '\\leavevmode\nDescription') in result assert ('\\item[{Footnote in term \\sphinxfootnotemark[5]}] ' '\\leavevmode%\n\\begin{footnotetext}[5]\\sphinxAtStartFootnote\n' 'Footnote in term\n%\n\\end{footnotetext}\nDescription') in result - assert ('\\item[{\\href{http://sphinx-doc.org/}{Term in deflist}}] ' + assert ('\\item[{\\sphinxhref{http://sphinx-doc.org/}{Term in deflist}}] ' '\\leavevmode\nDescription') in result - assert ('\\url{https://github.com/sphinx-doc/sphinx}\n' in result) - assert ('\\href{mailto:sphinx-dev@googlegroups.com}' + assert ('\\sphinxurl{https://github.com/sphinx-doc/sphinx}\n' in result) + assert ('\\sphinxhref{mailto:sphinx-dev@googlegroups.com}' '{sphinx-dev@googlegroups.com}\n') in result diff --git a/tests/test_markup.py b/tests/test_markup.py index 23eb4f80c..1d5407a27 100644 --- a/tests/test_markup.py +++ b/tests/test_markup.py @@ -217,7 +217,7 @@ def get_verifier(verify, verify_re): 'verify_re', u'`test `_', None, - r'\\href{http://example.com/~me/}{test}.*', + r'\\sphinxhref{http://example.com/~me/}{test}.*', ), ]) def test_inline(get_verifier, type, rst, html_expected, latex_expected):