latex writer: fix fallback definition of \DUroles macro.
[docutils.git] / docutils / test / test_writers / test_latex2e.py
blob88917e835f25839c193c2ddd3601ebd54bc74e76
1 # -*- coding: utf-8 -*-
2 #! /usr/bin/env python
4 # $Id$
5 # Author: engelbert gruber <grubert@users.sourceforge.net>
6 # Copyright: This module has been placed in the public domain.
8 """
9 Tests for latex2e writer.
10 """
12 import string
13 from __init__ import DocutilsTestSupport
15 def suite():
16 settings = {'use_latex_toc': False}
17 s = DocutilsTestSupport.PublishTestSuite('latex', suite_settings=settings)
18 s.generateTests(totest)
19 settings['use_latex_toc'] = True
20 s.generateTests(totest_latex_toc)
21 settings['use_latex_toc'] = False
22 settings['sectnum_xform'] = False
23 s.generateTests(totest_latex_sectnum)
24 settings['sectnum_xform'] = True
25 settings['use_latex_citations'] = True
26 s.generateTests(totest_latex_citations)
27 settings['table_style'] = ['colwidths-auto']
28 s.generateTests(totest_table_style_auto)
29 settings['table_style'] = ['booktabs']
30 s.generateTests(totest_table_style_booktabs)
31 settings['stylesheet_path'] = 'data/spam,data/ham.tex'
32 s.generateTests(totest_stylesheet)
33 settings['embed_stylesheet'] = True
34 settings['warning_stream'] = ''
35 s.generateTests(totest_stylesheet_embed)
36 return s
38 head_template = string.Template(
39 r"""$head_prefix% generated by Docutils <http://docutils.sourceforge.net/>
40 \usepackage{cmap} % fix search and cut-and-paste in Acrobat
41 $requirements
42 %%% Custom LaTeX preamble
43 $latex_preamble
44 %%% User specified packages and stylesheets
45 $stylesheet
46 %%% Fallback definitions for Docutils-specific commands
47 $fallbacks$pdfsetup
48 %%% Body
49 \begin{document}
50 $titledata""")
52 parts = dict(
53 head_prefix = r"""\documentclass[a4paper]{article}
54 """,
55 requirements = r"""\usepackage{ifthen}
56 \usepackage[T1]{fontenc}
57 \usepackage[utf8]{inputenc}
58 """,
59 latex_preamble = r"""% PDF Standard Fonts
60 \usepackage{mathptmx} % Times
61 \usepackage[scaled=.90]{helvet}
62 \usepackage{courier}
63 """,
64 longtable = r"""\usepackage{longtable,ltcaption,array}
65 \setlength{\extrarowheight}{2pt}
66 \newlength{\DUtablewidth} % internal use in tables
67 """,
68 stylesheet = '',
69 fallbacks = '',
70 fallbacks_highlight = r"""% basic code highlight:
71 \providecommand*\DUrolecomment[1]{\textcolor[rgb]{0.40,0.40,0.40}{#1}}
72 \providecommand*\DUroledeleted[1]{\textcolor[rgb]{0.40,0.40,0.40}{#1}}
73 \providecommand*\DUrolekeyword[1]{\textbf{#1}}
74 \providecommand*\DUrolestring[1]{\textit{#1}}
76 % inline markup (custom roles)
77 % \DUrole{#1}{#2} tries \DUrole#1{#2}
78 \providecommand*{\DUrole}[2]{%
79 \ifcsname DUrole#1\endcsname%
80 \csname DUrole#1\endcsname{#2}%
81 \else
82 % backwards compatibility: try \docutilsrole#1{#2}
83 \ifcsname docutilsrole#1\endcsname%
84 \csname docutilsrole#1\endcsname{#2}%
85 \else%
86 #2%
87 \fi%
88 \fi%
90 """,
91 pdfsetup = r"""
92 % hyperlinks:
93 \ifthenelse{\isundefined{\hypersetup}}{
94 \usepackage[colorlinks=true,linkcolor=blue,urlcolor=blue]{hyperref}
95 \usepackage{bookmark}
96 \urlstyle{same} % normal text font (alternatives: tt, rm, sf)
97 }{}
98 """,
99 titledata = '')
101 head = head_template.substitute(parts)
103 head_table = head_template.substitute(
104 dict(parts, requirements = parts['requirements'] + parts['longtable']))
106 head_booktabs = head_template.substitute(
107 dict(parts, requirements=parts['requirements']
108 + '\\usepackage{booktabs}\n' + parts['longtable']))
110 head_textcomp = head_template.substitute(
111 dict(parts, requirements = parts['requirements'] +
112 r"""\usepackage{textcomp} % text symbol macros
113 """))
115 head_alltt = head_template.substitute(
116 dict(parts, requirements = parts['requirements'] +
117 r"""\usepackage{alltt}
118 """))
121 totest = {}
122 totest_latex_toc = {}
123 totest_latex_sectnum = {}
124 totest_latex_citations = {}
125 totest_stylesheet = {}
126 totest_stylesheet_embed = {}
127 totest_table_style_auto = {}
128 totest_table_style_booktabs = {}
130 totest['url_chars'] = [
131 ["http://nowhere/url_with%28parens%29",
132 head + r"""
133 \url{http://nowhere/url_with\%28parens\%29}
135 \end{document}
136 """],
139 totest['textcomp'] = [
140 ["2 µm is just 2/1000000 m",
141 head_textcomp + r"""
142 2 µm is just 2/1000000 m
144 \end{document}
145 """],
148 totest['spanish quote'] = [
149 [".. role:: language-es\n\nUnd damit :language-es:`basta`!",
150 head_template.substitute(dict(parts, requirements =
151 r"""\usepackage{ifthen}
152 \usepackage[T1]{fontenc}
153 \usepackage[utf8]{inputenc}
154 \usepackage[spanish,english]{babel}
155 \AtBeginDocument{\shorthandoff{.<>}}
156 """)) + r"""
157 Und damit \foreignlanguage{spanish}{basta}!
159 \end{document}
160 """],
163 totest['code role'] = [
164 [":code:`x=1`",
165 head_template.substitute(dict(parts, requirements = parts['requirements']+
166 r"""\usepackage{color}
167 """, fallbacks = parts['fallbacks_highlight'])) + r"""
168 \texttt{\DUrole{code}{x=1}}
170 \end{document}
171 """],
174 totest['table_of_contents'] = [
175 # input
176 ["""\
177 .. contents:: Table of Contents
179 Title 1
180 =======
181 Paragraph 1.
183 Title 2
184 -------
185 Paragraph 2.
186 """,
187 ## # expected output
188 head_template.substitute(dict(parts,
189 requirements=parts['requirements'] + '\\setcounter{secnumdepth}{0}\n',
190 fallbacks=r"""
191 % title for topics, admonitions, unsupported section levels, and sidebar
192 \providecommand*{\DUtitle}[2][class-arg]{%
193 % call \DUtitle#1{#2} if it exists:
194 \ifcsname DUtitle#1\endcsname%
195 \csname DUtitle#1\endcsname{#2}%
196 \else
197 \smallskip\noindent\textbf{#2}\smallskip%
200 """)) + r"""
201 \phantomsection\label{table-of-contents}
202 \pdfbookmark[1]{Table of Contents}{table-of-contents}
203 \DUtitle[contents]{Table of Contents}
205 \begin{list}{}{}
206 \item \hyperref[title-1]{Title 1}
208 \begin{list}{}{}
209 \item \hyperref[title-2]{Title 2}
210 \end{list}
211 \end{list}
214 \section{Title 1%
215 \label{title-1}%
218 Paragraph 1.
221 \subsection{Title 2%
222 \label{title-2}%
225 Paragraph 2.
227 \end{document}
228 """],
231 totest['footnote text'] = [
232 # input
233 ["""\
234 .. [1] paragraph
236 .. [2]
238 .. [3] 1. enumeration
239 """,
240 ## # expected output
241 head_template.substitute(dict(parts,
242 fallbacks=r"""% numeric or symbol footnotes with hyperlinks
243 \providecommand*{\DUfootnotemark}[3]{%
244 \raisebox{1em}{\hypertarget{#1}{}}%
245 \hyperlink{#2}{\textsuperscript{#3}}%
247 \providecommand{\DUfootnotetext}[4]{%
248 \begingroup%
249 \renewcommand{\thefootnote}{%
250 \protect\raisebox{1em}{\protect\hypertarget{#1}{}}%
251 \protect\hyperlink{#2}{#3}}%
252 \footnotetext{#4}%
253 \endgroup%
255 """)) + r"""%
256 \DUfootnotetext{id1}{id1}{1}{%
257 paragraph
260 \DUfootnotetext{id2}{id2}{2}{}
262 \DUfootnotetext{id3}{id3}{3}{
263 \begin{enumerate}
264 \item enumeration
265 \end{enumerate}
268 \end{document}
269 """],
272 totest_latex_toc['no_sectnum'] = [
273 # input
274 ["""\
275 .. contents::
277 first section
278 -------------
279 """,
280 ## # expected output
281 head_template.substitute(dict(parts,
282 requirements=parts['requirements'] + '\\setcounter{secnumdepth}{0}\n'
283 )) + r"""
284 \phantomsection\label{contents}
285 \pdfbookmark[1]{Contents}{contents}
286 \tableofcontents
289 \section{first section%
290 \label{first-section}%
293 \end{document}
294 """],
297 totest_latex_toc['sectnum'] = [
298 # input
299 ["""\
300 .. contents::
301 .. sectnum::
303 first section
304 -------------
305 """,
306 ## # expected output
307 head_template.substitute(dict(parts,
308 requirements=parts['requirements'] + '\\setcounter{secnumdepth}{0}\n'
309 )) + r"""
310 \phantomsection\label{contents}
311 \pdfbookmark[1]{Contents}{contents}
312 \tableofcontents
315 \section{1   first section%
316 \label{first-section}%
319 \end{document}
320 """],
324 totest_latex_sectnum['no_sectnum'] = [
325 # input
326 ["""\
327 some text
329 first section
330 -------------
331 """,
332 ## # expected output
333 head_template.substitute(dict(parts, requirements = parts['requirements'] +
334 r"""\setcounter{secnumdepth}{0}
335 """)) + r"""
336 some text
339 \section{first section%
340 \label{first-section}%
343 \end{document}
344 """],
347 totest_latex_sectnum['sectnum'] = [
348 # input
349 ["""\
350 .. sectnum::
352 some text
354 first section
355 -------------
356 """,
357 ## # expected output
358 head_template.substitute(dict(parts,
359 requirements=parts['requirements'] + '\\setcounter{secnumdepth}{0}\n'
360 )) + r"""
361 some text
364 \section{first section%
365 \label{first-section}%
368 \end{document}
369 """],
372 totest_latex_citations['citations_with_underscore'] = [
373 # input
374 ["""\
375 Just a test citation [my_cite2006]_.
377 .. [my_cite2006]
378 The underscore is mishandled.
379 """,
380 ## # expected output
381 head + r"""
382 Just a test citation \cite{my_cite2006}.
384 \begin{thebibliography}{my\_cite2006}
385 \bibitem[my\_cite2006]{my_cite2006}{
386 The underscore is mishandled.
388 \end{thebibliography}
390 \end{document}
391 """],
395 totest_latex_citations['adjacent_citations'] = [
396 # input
397 ["""\
398 Two non-citations: [MeYou2007]_[YouMe2007]_.
400 Need to be separated for grouping: [MeYou2007]_ [YouMe2007]_.
402 Two spaces (or anything else) for no grouping: [MeYou2007]_ [YouMe2007]_.
404 But a line break should work: [MeYou2007]_
405 [YouMe2007]_.
407 .. [MeYou2007] not.
408 .. [YouMe2007] important.
409 """,
410 # expected output
411 head + r"""
412 Two non-citations: {[}MeYou2007{]}\_{[}YouMe2007{]}\_.
414 Need to be separated for grouping: \cite{MeYou2007,YouMe2007}.
416 Two spaces (or anything else) for no grouping: \cite{MeYou2007} \cite{YouMe2007}.
418 But a line break should work: \cite{MeYou2007,YouMe2007}.
420 \begin{thebibliography}{MeYou2007}
421 \bibitem[MeYou2007]{MeYou2007}{
422 not.
424 \bibitem[YouMe2007]{YouMe2007}{
425 important.
427 \end{thebibliography}
429 \end{document}
430 """],
434 totest['enumerated_lists'] = [
435 # input
436 ["""\
437 1. Item 1.
438 2. Second to the previous item this one will explain
440 a) nothing.
441 b) or some other.
443 3. Third is
445 (I) having pre and postfixes
446 (II) in roman numerals.
447 """,
448 # expected output
449 head + r"""
450 \begin{enumerate}
451 \item Item 1.
453 \item Second to the previous item this one will explain
454 \end{enumerate}
456 \begin{quote}
457 \begin{enumerate}
458 \renewcommand{\labelenumi}{\alph{enumi})}
459 \item nothing.
461 \item or some other.
462 \end{enumerate}
463 \end{quote}
465 \begin{enumerate}
466 \setcounter{enumi}{2}
467 \item Third is
468 \end{enumerate}
470 \begin{quote}
471 \begin{enumerate}
472 \renewcommand{\labelenumi}{(\Roman{enumi})}
473 \item having pre and postfixes
475 \item in roman numerals.
476 \end{enumerate}
477 \end{quote}
479 \end{document}
480 """],
483 # TODO: need to test for quote replacing if the language uses "ASCII-quotes"
484 # as active character (e.g. de (ngerman)).
487 totest['table_caption'] = [
488 # input
489 ["""\
490 .. table:: Foo
492 +-----+-----+
493 | | |
494 +-----+-----+
495 | | |
496 +-----+-----+
497 """,
498 head_table + r"""
499 \setlength{\DUtablewidth}{\linewidth}
500 \begin{longtable}[c]{|p{0.075\DUtablewidth}|p{0.075\DUtablewidth}|}
501 \caption{Foo}\\
502 \hline
503 & \\
504 \hline
505 & \\
506 \hline
507 \end{longtable}
509 \end{document}
510 """],
513 totest['table_styles'] = [
514 ["""\
515 .. table::
516 :class: borderless
518 +-----+-----+
519 | 1 | 2 |
520 +-----+-----+
521 | 3 | 4 |
522 +-----+-----+
523 """,
524 head_table + r"""
525 \setlength{\DUtablewidth}{\linewidth}
526 \begin{longtable*}[c]{p{0.075\DUtablewidth}p{0.075\DUtablewidth}}
537 \end{longtable*}
539 \end{document}
540 """],
541 ["""\
542 .. table::
543 :class: booktabs
545 +-----+-+
546 | 1 |2|
547 +-----+-+
548 """,
549 head_booktabs + r"""
550 \setlength{\DUtablewidth}{\linewidth}
551 \begin{longtable*}[c]{p{0.075\DUtablewidth}p{0.028\DUtablewidth}}
552 \toprule
558 \bottomrule
559 \end{longtable*}
561 \end{document}
562 """],
563 ["""\
564 .. table::
565 :class: colwidths-auto
567 +-----+-+
568 | 1 |2|
569 +-----+-+
570 """,
571 head_table + r"""
572 \begin{longtable*}[c]{|l|l|}
573 \hline
574 1 & 2 \\
575 \hline
576 \end{longtable*}
578 \end{document}
579 """],
580 ["""\
581 .. table::
582 :widths: auto
584 +-----+-+
585 | 1 |2|
586 +-----+-+
587 """,
588 head_table + r"""
589 \begin{longtable*}[c]{|l|l|}
590 \hline
591 1 & 2 \\
592 \hline
593 \end{longtable*}
595 \end{document}
596 """],
597 ["""\
598 .. table::
599 :widths: 15, 30
601 +-----+-----+
602 | 1 | 2 |
603 +-----+-----+
604 """,
605 head_table + r"""
606 \setlength{\DUtablewidth}{\linewidth}
607 \begin{longtable*}[c]{|p{0.191\DUtablewidth}|p{0.365\DUtablewidth}|}
608 \hline
614 \hline
615 \end{longtable*}
617 \end{document}
618 """],
621 totest_table_style_booktabs['table_styles'] = [
622 # borderless overrides "booktabs" table_style
623 ["""\
624 .. table::
625 :class: borderless
627 +-----+-----+
628 | 1 | 2 |
629 +-----+-----+
630 | 3 | 4 |
631 +-----+-----+
632 """,
633 head_table + r"""
634 \setlength{\DUtablewidth}{\linewidth}
635 \begin{longtable*}[c]{p{0.075\DUtablewidth}p{0.075\DUtablewidth}}
646 \end{longtable*}
648 \end{document}
649 """],
650 ["""\
651 .. table::
652 :widths: auto
654 +-----+-+
655 | 1 |2|
656 +-----+-+
657 """,
658 head_booktabs + r"""
659 \begin{longtable*}[c]{ll}
660 \toprule
661 1 & 2 \\
662 \bottomrule
663 \end{longtable*}
665 \end{document}
666 """],
667 ["""\
668 .. table::
669 :widths: 15, 30
671 +-----+-----+
672 | 1 | 2 |
673 +-----+-----+
674 """,
675 head_booktabs + r"""
676 \setlength{\DUtablewidth}{\linewidth}
677 \begin{longtable*}[c]{p{0.191\DUtablewidth}p{0.365\DUtablewidth}}
678 \toprule
684 \bottomrule
685 \end{longtable*}
687 \end{document}
688 """],
690 totest_table_style_auto['table_styles'] = [
691 ["""\
692 .. table::
693 :class: borderless
695 +-----+-----+
696 | 1 | 2 |
697 +-----+-----+
698 | 3 | 4 |
699 +-----+-----+
700 """,
701 head_table + r"""
702 \begin{longtable*}[c]{ll}
703 1 & 2 \\
704 3 & 4 \\
705 \end{longtable*}
707 \end{document}
708 """],
709 ["""\
710 .. table::
711 :class: booktabs
713 +-----+-+
714 | 1 |2|
715 +-----+-+
716 """,
717 head_booktabs + r"""
718 \begin{longtable*}[c]{ll}
719 \toprule
720 1 & 2 \\
721 \bottomrule
722 \end{longtable*}
724 \end{document}
725 """],
726 # given width overrides "colwidth-auto"
727 ["""\
728 .. table::
729 :widths: 15, 30
731 +-----+-----+
732 | 1 | 2 |
733 +-----+-----+
734 """,
735 head_table + r"""
736 \setlength{\DUtablewidth}{\linewidth}
737 \begin{longtable*}[c]{|p{0.191\DUtablewidth}|p{0.365\DUtablewidth}|}
738 \hline
744 \hline
745 \end{longtable*}
747 \end{document}
748 """],
751 totest['table_align'] = [
752 # input
753 ["""\
754 .. table::
755 :align: right
757 +-----+-----+
758 | 1 | 2 |
759 +-----+-----+
760 """,
761 head_table + r"""
762 \setlength{\DUtablewidth}{\linewidth}
763 \begin{longtable*}[r]{|p{0.075\DUtablewidth}|p{0.075\DUtablewidth}|}
764 \hline
770 \hline
771 \end{longtable*}
773 \end{document}
774 """],
777 totest['table_empty_cells'] = [
778 ["""\
779 ===== ======
780 Title
781 ===== ======
782 entry value1
783 ===== ======
784 """,
785 head_table + r"""
786 \setlength{\DUtablewidth}{\linewidth}
787 \begin{longtable*}[c]{|p{0.075\DUtablewidth}|p{0.086\DUtablewidth}|}
788 \hline
789 \textbf{%
790 Title
791 } & \\
792 \hline
793 \endfirsthead
794 \hline
795 \textbf{%
796 Title
797 } & \\
798 \hline
799 \endhead
800 \multicolumn{2}{c}{\hfill ... continued on next page} \\
801 \endfoot
802 \endlastfoot
804 entry
806 value1
808 \hline
809 \end{longtable*}
811 \end{document}
812 """],
813 ["""\
814 +----+----+
815 | c3 | c4 |
816 +----+----+
818 +---------+
819 """,
820 head_table + r"""
821 \setlength{\DUtablewidth}{\linewidth}
822 \begin{longtable*}[c]{|p{0.063\DUtablewidth}|p{0.063\DUtablewidth}|}
823 \hline
829 \hline
830 \multicolumn{2}{|p{0.13\DUtablewidth}|}{} \\
831 \hline
832 \end{longtable*}
834 \end{document}
835 """],
838 # The "[" needs to be protected (otherwise it will be seen as an
839 # option to "\\", "\item", etc. ).
841 totest['bracket_protection'] = [
842 # input
843 ["""
844 * [no option] to this item
845 """,
846 head + r"""
847 \begin{itemize}
848 \item {[}no option{]} to this item
849 \end{itemize}
851 \end{document}
852 """],
855 totest['literal_block'] = [
856 # input
857 ["""\
858 Test special characters { [ \\\\ ] } in literal block::
860 { [ ( \macro
862 } ] )
863 """,
864 head_alltt + r"""
865 Test special characters \{ {[} \textbackslash{} {]} \} in literal block:
867 \begin{quote}
868 \begin{alltt}
869 \{ [ ( \textbackslash{}macro
871 \} ] )
872 \end{alltt}
873 \end{quote}
875 \end{document}
876 """],
879 totest['raw'] = [
880 [r""".. raw:: latex
882 $E=mc^2$
884 A paragraph.
886 .. |sub| raw:: latex
888 (some raw text)
890 Foo |sub|
891 same paragraph.
892 """,
893 head + r"""
894 $E=mc^2$
896 A paragraph.
898 Foo (some raw text)
899 same paragraph.
901 \end{document}
902 """],
905 totest['title_with_inline_markup'] = [
906 ["""\
907 This is the *Title*
908 ===================
910 This is the *Subtitle*
911 ----------------------
913 This is a *section title*
914 ~~~~~~~~~~~~~~~~~~~~~~~~~
916 This is the *document*.
917 """,
918 head_template.substitute(dict(parts,
919 requirements=parts['requirements'] + '\\setcounter{secnumdepth}{0}\n',
920 fallbacks=r"""
921 % subtitle (in document title)
922 \providecommand*{\DUdocumentsubtitle}[1]{{\large #1}}
923 """,
924 pdfsetup=parts['pdfsetup'] + r"""\hypersetup{
925 pdftitle={This is the Title},
927 """, titledata=r"""\title{This is the \emph{Title}%
928 \label{this-is-the-title}%
929 \\ % subtitle%
930 \DUdocumentsubtitle{This is the \emph{Subtitle}}%
931 \label{this-is-the-subtitle}}
932 \author{}
933 \date{}
934 """)) + r"""\maketitle
937 \section{This is a \emph{section title}%
938 \label{this-is-a-section-title}%
941 This is the \emph{document}.
943 \end{document}
944 """],
947 totest_stylesheet['two-styles'] = [
948 # input
949 ["""two stylesheet links in the header""",
950 head_template.substitute(dict(parts, stylesheet =
951 r"""\usepackage{data/spam}
952 \input{data/ham.tex}
953 """)) + r"""
954 two stylesheet links in the header
956 \end{document}
957 """],
960 totest_stylesheet_embed['two-styles'] = [
961 # input
962 ["""two stylesheets embedded in the header""",
963 head_template.substitute(dict(parts, stylesheet =
964 r"""% Cannot embed stylesheet 'data/spam.sty':
965 % No such file or directory.
966 % embedded stylesheet: data/ham.tex
967 \newcommand{\ham}{wonderful ham}
969 """)) + r"""
970 two stylesheets embedded in the header
972 \end{document}
973 """],
976 if __name__ == '__main__':
977 import unittest
978 unittest.main(defaultTest='suite')