LaTeX: Define required Unicode characters in the preamble.
[docutils.git] / docutils / test / test_writers / test_latex2e.py
blob6a3ada132934aa39016d445176eadcef326b3f09
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 $titledata
49 %%% Body
50 \begin{document}
51 """)
53 parts = dict(
54 head_prefix = r"""\documentclass[a4paper]{article}
55 """,
56 requirements = r"""\usepackage{ifthen}
57 \usepackage[T1]{fontenc}
58 \usepackage[utf8]{inputenc}
59 """,
60 latex_preamble = r"""% PDF Standard Fonts
61 \usepackage{mathptmx} % Times
62 \usepackage[scaled=.90]{helvet}
63 \usepackage{courier}
64 """,
65 longtable = r"""\usepackage{longtable,ltcaption,array}
66 \setlength{\extrarowheight}{2pt}
67 \newlength{\DUtablewidth} % internal use in tables
68 """,
69 stylesheet = '',
70 fallbacks = '',
71 fallbacks_highlight = r"""% basic code highlight:
72 \providecommand*\DUrolecomment[1]{\textcolor[rgb]{0.40,0.40,0.40}{#1}}
73 \providecommand*\DUroledeleted[1]{\textcolor[rgb]{0.40,0.40,0.40}{#1}}
74 \providecommand*\DUrolekeyword[1]{\textbf{#1}}
75 \providecommand*\DUrolestring[1]{\textit{#1}}
77 % inline markup (custom roles)
78 % \DUrole{#1}{#2} tries \DUrole#1{#2}
79 \providecommand*{\DUrole}[2]{%
80 % backwards compatibility: try \docutilsrole#1{#2}
81 \ifcsname docutilsrole#1\endcsname%
82 \csname docutilsrole#1\endcsname{#2}%
83 \else
84 \csname DUrole#1\endcsname{#2}%
85 \fi%
87 """,
88 pdfsetup = r"""
89 % hyperlinks:
90 \ifthenelse{\isundefined{\hypersetup}}{
91 \usepackage[colorlinks=true,linkcolor=blue,urlcolor=blue]{hyperref}
92 \usepackage{bookmark}
93 \urlstyle{same} % normal text font (alternatives: tt, rm, sf)
94 }{}
95 """,
96 titledata = '')
98 head = head_template.substitute(parts)
100 head_table = head_template.substitute(
101 dict(parts, requirements = parts['requirements'] + parts['longtable']))
103 head_booktabs = head_template.substitute(
104 dict(parts, requirements=parts['requirements']
105 + '\\usepackage{booktabs}\n' + parts['longtable']))
107 head_textcomp = head_template.substitute(
108 dict(parts, requirements = parts['requirements'] +
109 r"""\usepackage{textcomp} % text symbol macros
110 """))
112 head_alltt = head_template.substitute(
113 dict(parts, requirements = parts['requirements'] +
114 r"""\usepackage{alltt}
115 """))
118 totest = {}
119 totest_latex_toc = {}
120 totest_latex_sectnum = {}
121 totest_latex_citations = {}
122 totest_stylesheet = {}
123 totest_stylesheet_embed = {}
124 totest_table_style_auto = {}
125 totest_table_style_booktabs = {}
127 totest['url_chars'] = [
128 ["http://nowhere/url_with%28parens%29",
129 head + r"""
130 \url{http://nowhere/url_with\%28parens\%29}
132 \end{document}
133 """],
136 totest['textcomp'] = [
137 ["2 µm is just 2/1000000 m",
138 head_textcomp + r"""
139 2 µm is just 2/1000000 m
141 \end{document}
142 """],
145 totest['spanish quote'] = [
146 [".. role:: language-es\n\nUnd damit :language-es:`basta`!",
147 head_template.substitute(dict(parts, requirements =
148 r"""\usepackage{ifthen}
149 \usepackage[T1]{fontenc}
150 \usepackage[utf8]{inputenc}
151 \usepackage[spanish,english]{babel}
152 \AtBeginDocument{\shorthandoff{.<>}}
153 """)) + r"""
154 Und damit \foreignlanguage{spanish}{basta}!
156 \end{document}
157 """],
160 totest['code role'] = [
161 [":code:`x=1`",
162 head_template.substitute(dict(parts, requirements = parts['requirements']+
163 r"""\usepackage{color}
164 """, fallbacks = parts['fallbacks_highlight'])) + r"""
165 \texttt{\DUrole{code}{x=1}}
167 \end{document}
168 """],
171 totest['table_of_contents'] = [
172 # input
173 ["""\
174 .. contents:: Table of Contents
176 Title 1
177 =======
178 Paragraph 1.
180 Title 2
181 -------
182 Paragraph 2.
183 """,
184 ## # expected output
185 head_template.substitute(dict(parts,
186 requirements=parts['requirements'] + '\\setcounter{secnumdepth}{0}\n',
187 fallbacks=r"""
188 % title for topics, admonitions, unsupported section levels, and sidebar
189 \providecommand*{\DUtitle}[2][class-arg]{%
190 % call \DUtitle#1{#2} if it exists:
191 \ifcsname DUtitle#1\endcsname%
192 \csname DUtitle#1\endcsname{#2}%
193 \else
194 \smallskip\noindent\textbf{#2}\smallskip%
197 """)) + r"""
198 \phantomsection\label{table-of-contents}
199 \pdfbookmark[1]{Table of Contents}{table-of-contents}
200 \DUtitle[contents]{Table of Contents}
202 \begin{list}{}{}
203 \item \hyperref[title-1]{Title 1}
205 \begin{list}{}{}
206 \item \hyperref[title-2]{Title 2}
207 \end{list}
208 \end{list}
211 \section{Title 1%
212 \label{title-1}%
215 Paragraph 1.
218 \subsection{Title 2%
219 \label{title-2}%
222 Paragraph 2.
224 \end{document}
225 """],
228 totest['footnote text'] = [
229 # input
230 ["""\
231 .. [1] paragraph
233 .. [2]
235 .. [3] 1. enumeration
236 """,
237 ## # expected output
238 head_template.substitute(dict(parts,
239 fallbacks=r"""% numeric or symbol footnotes with hyperlinks
240 \providecommand*{\DUfootnotemark}[3]{%
241 \raisebox{1em}{\hypertarget{#1}{}}%
242 \hyperlink{#2}{\textsuperscript{#3}}%
244 \providecommand{\DUfootnotetext}[4]{%
245 \begingroup%
246 \renewcommand{\thefootnote}{%
247 \protect\raisebox{1em}{\protect\hypertarget{#1}{}}%
248 \protect\hyperlink{#2}{#3}}%
249 \footnotetext{#4}%
250 \endgroup%
252 """)) + r"""%
253 \DUfootnotetext{id1}{id1}{1}{%
254 paragraph
257 \DUfootnotetext{id2}{id2}{2}{}
259 \DUfootnotetext{id3}{id3}{3}{
260 \begin{enumerate}
261 \item enumeration
262 \end{enumerate}
265 \end{document}
266 """],
269 totest_latex_toc['no_sectnum'] = [
270 # input
271 ["""\
272 .. contents::
274 first section
275 -------------
276 """,
277 ## # expected output
278 head_template.substitute(dict(parts,
279 requirements=parts['requirements'] + '\\setcounter{secnumdepth}{0}\n'
280 )) + r"""
281 \phantomsection\label{contents}
282 \pdfbookmark[1]{Contents}{contents}
283 \tableofcontents
286 \section{first section%
287 \label{first-section}%
290 \end{document}
291 """],
294 totest_latex_toc['sectnum'] = [
295 # input
296 ["""\
297 .. contents::
298 .. sectnum::
300 first section
301 -------------
302 """,
303 ## # expected output
304 head_template.substitute(dict(parts,
305 requirements=parts['requirements'] + '\\setcounter{secnumdepth}{0}\n'
306 )) + r"""
307 \phantomsection\label{contents}
308 \pdfbookmark[1]{Contents}{contents}
309 \tableofcontents
312 \section{1   first section%
313 \label{first-section}%
316 \end{document}
317 """],
321 totest_latex_sectnum['no_sectnum'] = [
322 # input
323 ["""\
324 some text
326 first section
327 -------------
328 """,
329 ## # expected output
330 head_template.substitute(dict(parts, requirements = parts['requirements'] +
331 r"""\setcounter{secnumdepth}{0}
332 """)) + r"""
333 some text
336 \section{first section%
337 \label{first-section}%
340 \end{document}
341 """],
344 totest_latex_sectnum['sectnum'] = [
345 # input
346 ["""\
347 .. sectnum::
349 some text
351 first section
352 -------------
353 """,
354 ## # expected output
355 head_template.substitute(dict(parts,
356 requirements=parts['requirements'] + '\\setcounter{secnumdepth}{0}\n'
357 )) + r"""
358 some text
361 \section{first section%
362 \label{first-section}%
365 \end{document}
366 """],
369 totest_latex_citations['citations_with_underscore'] = [
370 # input
371 ["""\
372 Just a test citation [my_cite2006]_.
374 .. [my_cite2006]
375 The underscore is mishandled.
376 """,
377 ## # expected output
378 head + r"""
379 Just a test citation \cite{my_cite2006}.
381 \begin{thebibliography}{my\_cite2006}
382 \bibitem[my\_cite2006]{my_cite2006}{
383 The underscore is mishandled.
385 \end{thebibliography}
387 \end{document}
388 """],
392 totest_latex_citations['adjacent_citations'] = [
393 # input
394 ["""\
395 Two non-citations: [MeYou2007]_[YouMe2007]_.
397 Need to be separated for grouping: [MeYou2007]_ [YouMe2007]_.
399 Two spaces (or anything else) for no grouping: [MeYou2007]_ [YouMe2007]_.
401 But a line break should work: [MeYou2007]_
402 [YouMe2007]_.
404 .. [MeYou2007] not.
405 .. [YouMe2007] important.
406 """,
407 # expected output
408 head + r"""
409 Two non-citations: {[}MeYou2007{]}\_{[}YouMe2007{]}\_.
411 Need to be separated for grouping: \cite{MeYou2007,YouMe2007}.
413 Two spaces (or anything else) for no grouping: \cite{MeYou2007} \cite{YouMe2007}.
415 But a line break should work: \cite{MeYou2007,YouMe2007}.
417 \begin{thebibliography}{MeYou2007}
418 \bibitem[MeYou2007]{MeYou2007}{
419 not.
421 \bibitem[YouMe2007]{YouMe2007}{
422 important.
424 \end{thebibliography}
426 \end{document}
427 """],
431 totest['enumerated_lists'] = [
432 # input
433 ["""\
434 1. Item 1.
435 2. Second to the previous item this one will explain
437 a) nothing.
438 b) or some other.
440 3. Third is
442 (I) having pre and postfixes
443 (II) in roman numerals.
444 """,
445 # expected output
446 head + r"""
447 \begin{enumerate}
448 \item Item 1.
450 \item Second to the previous item this one will explain
451 \end{enumerate}
453 \begin{quote}
454 \begin{enumerate}
455 \renewcommand{\labelenumi}{\alph{enumi})}
456 \item nothing.
458 \item or some other.
459 \end{enumerate}
460 \end{quote}
462 \begin{enumerate}
463 \setcounter{enumi}{2}
464 \item Third is
465 \end{enumerate}
467 \begin{quote}
468 \begin{enumerate}
469 \renewcommand{\labelenumi}{(\Roman{enumi})}
470 \item having pre and postfixes
472 \item in roman numerals.
473 \end{enumerate}
474 \end{quote}
476 \end{document}
477 """],
480 # TODO: need to test for quote replacing if the language uses "ASCII-quotes"
481 # as active character (e.g. de (ngerman)).
484 totest['table_caption'] = [
485 # input
486 ["""\
487 .. table:: Foo
489 +-----+-----+
490 | | |
491 +-----+-----+
492 | | |
493 +-----+-----+
494 """,
495 head_table + r"""
496 \setlength{\DUtablewidth}{\linewidth}
497 \begin{longtable}[c]{|p{0.075\DUtablewidth}|p{0.075\DUtablewidth}|}
498 \caption{Foo}\\
499 \hline
500 & \\
501 \hline
502 & \\
503 \hline
504 \end{longtable}
506 \end{document}
507 """],
510 totest['table_styles'] = [
511 ["""\
512 .. table::
513 :class: borderless
515 +-----+-----+
516 | 1 | 2 |
517 +-----+-----+
518 | 3 | 4 |
519 +-----+-----+
520 """,
521 head_table + r"""
522 \setlength{\DUtablewidth}{\linewidth}
523 \begin{longtable*}[c]{p{0.075\DUtablewidth}p{0.075\DUtablewidth}}
534 \end{longtable*}
536 \end{document}
537 """],
538 ["""\
539 .. table::
540 :class: booktabs
542 +-----+-+
543 | 1 |2|
544 +-----+-+
545 """,
546 head_booktabs + r"""
547 \setlength{\DUtablewidth}{\linewidth}
548 \begin{longtable*}[c]{p{0.075\DUtablewidth}p{0.028\DUtablewidth}}
549 \toprule
555 \bottomrule
556 \end{longtable*}
558 \end{document}
559 """],
560 ["""\
561 .. table::
562 :class: colwidths-auto
564 +-----+-+
565 | 1 |2|
566 +-----+-+
567 """,
568 head_table + r"""
569 \begin{longtable*}[c]{|l|l|}
570 \hline
571 1 & 2 \\
572 \hline
573 \end{longtable*}
575 \end{document}
576 """],
577 ["""\
578 .. table::
579 :widths: auto
581 +-----+-+
582 | 1 |2|
583 +-----+-+
584 """,
585 head_table + r"""
586 \begin{longtable*}[c]{|l|l|}
587 \hline
588 1 & 2 \\
589 \hline
590 \end{longtable*}
592 \end{document}
593 """],
594 ["""\
595 .. table::
596 :widths: 15, 30
598 +-----+-----+
599 | 1 | 2 |
600 +-----+-----+
601 """,
602 head_table + r"""
603 \setlength{\DUtablewidth}{\linewidth}
604 \begin{longtable*}[c]{|p{0.191\DUtablewidth}|p{0.365\DUtablewidth}|}
605 \hline
611 \hline
612 \end{longtable*}
614 \end{document}
615 """],
618 totest_table_style_booktabs['table_styles'] = [
619 # borderless overrides "booktabs" table_style
620 ["""\
621 .. table::
622 :class: borderless
624 +-----+-----+
625 | 1 | 2 |
626 +-----+-----+
627 | 3 | 4 |
628 +-----+-----+
629 """,
630 head_table + r"""
631 \setlength{\DUtablewidth}{\linewidth}
632 \begin{longtable*}[c]{p{0.075\DUtablewidth}p{0.075\DUtablewidth}}
643 \end{longtable*}
645 \end{document}
646 """],
647 ["""\
648 .. table::
649 :widths: auto
651 +-----+-+
652 | 1 |2|
653 +-----+-+
654 """,
655 head_booktabs + r"""
656 \begin{longtable*}[c]{ll}
657 \toprule
658 1 & 2 \\
659 \bottomrule
660 \end{longtable*}
662 \end{document}
663 """],
664 ["""\
665 .. table::
666 :widths: 15, 30
668 +-----+-----+
669 | 1 | 2 |
670 +-----+-----+
671 """,
672 head_booktabs + r"""
673 \setlength{\DUtablewidth}{\linewidth}
674 \begin{longtable*}[c]{p{0.191\DUtablewidth}p{0.365\DUtablewidth}}
675 \toprule
681 \bottomrule
682 \end{longtable*}
684 \end{document}
685 """],
687 totest_table_style_auto['table_styles'] = [
688 ["""\
689 .. table::
690 :class: borderless
692 +-----+-----+
693 | 1 | 2 |
694 +-----+-----+
695 | 3 | 4 |
696 +-----+-----+
697 """,
698 head_table + r"""
699 \begin{longtable*}[c]{ll}
700 1 & 2 \\
701 3 & 4 \\
702 \end{longtable*}
704 \end{document}
705 """],
706 ["""\
707 .. table::
708 :class: booktabs
710 +-----+-+
711 | 1 |2|
712 +-----+-+
713 """,
714 head_booktabs + r"""
715 \begin{longtable*}[c]{ll}
716 \toprule
717 1 & 2 \\
718 \bottomrule
719 \end{longtable*}
721 \end{document}
722 """],
723 # given width overrides "colwidth-auto"
724 ["""\
725 .. table::
726 :widths: 15, 30
728 +-----+-----+
729 | 1 | 2 |
730 +-----+-----+
731 """,
732 head_table + r"""
733 \setlength{\DUtablewidth}{\linewidth}
734 \begin{longtable*}[c]{|p{0.191\DUtablewidth}|p{0.365\DUtablewidth}|}
735 \hline
741 \hline
742 \end{longtable*}
744 \end{document}
745 """],
748 totest['table_align'] = [
749 # input
750 ["""\
751 .. table::
752 :align: right
754 +-----+-----+
755 | 1 | 2 |
756 +-----+-----+
757 """,
758 head_table + r"""
759 \setlength{\DUtablewidth}{\linewidth}
760 \begin{longtable*}[r]{|p{0.075\DUtablewidth}|p{0.075\DUtablewidth}|}
761 \hline
767 \hline
768 \end{longtable*}
770 \end{document}
771 """],
774 totest['table_empty_thead_entry'] = [
775 # input
776 ["""\
777 ===== ======
778 Title
779 ===== ======
780 entry value1
781 ===== ======
782 """,
783 head_table + r"""
784 \setlength{\DUtablewidth}{\linewidth}
785 \begin{longtable*}[c]{|p{0.075\DUtablewidth}|p{0.086\DUtablewidth}|}
786 \hline
787 \textbf{%
788 Title
789 } & \\
790 \hline
791 \endfirsthead
792 \hline
793 \textbf{%
794 Title
795 } & \\
796 \hline
797 \endhead
798 \multicolumn{2}{c}{\hfill ... continued on next page} \\
799 \endfoot
800 \endlastfoot
802 entry
804 value1
806 \hline
807 \end{longtable*}
809 \end{document}
810 """],
813 # The "[" needs to be protected (otherwise it will be seen as an
814 # option to "\\", "\item", etc. ).
816 totest['bracket_protection'] = [
817 # input
818 ["""
819 * [no option] to this item
820 """,
821 head + r"""
822 \begin{itemize}
823 \item {[}no option{]} to this item
824 \end{itemize}
826 \end{document}
827 """],
830 totest['literal_block'] = [
831 # input
832 ["""\
833 Test special characters { [ \\\\ ] } in literal block::
835 { [ ( \macro
837 } ] )
838 """,
839 head_alltt + r"""
840 Test special characters \{ {[} \textbackslash{} {]} \} in literal block:
842 \begin{quote}
843 \begin{alltt}
844 \{ [ ( \textbackslash{}macro
846 \} ] )
847 \end{alltt}
848 \end{quote}
850 \end{document}
851 """],
854 totest['raw'] = [
855 [r""".. raw:: latex
857 $E=mc^2$
859 A paragraph.
861 .. |sub| raw:: latex
863 (some raw text)
865 Foo |sub|
866 same paragraph.
867 """,
868 head + r"""
869 $E=mc^2$
871 A paragraph.
873 Foo (some raw text)
874 same paragraph.
876 \end{document}
877 """],
880 totest['title_with_inline_markup'] = [
881 ["""\
882 This is the *Title*
883 ===================
885 This is the *Subtitle*
886 ----------------------
888 This is a *section title*
889 ~~~~~~~~~~~~~~~~~~~~~~~~~
891 This is the *document*.
892 """,
893 head_template.substitute(dict(parts,
894 requirements=parts['requirements'] + '\\setcounter{secnumdepth}{0}\n',
895 fallbacks=r"""
896 % subtitle (in document title)
897 \providecommand*{\DUdocumentsubtitle}[1]{{\large #1}}
898 """,
899 pdfsetup=parts['pdfsetup'] + r"""\hypersetup{
900 pdftitle={This is the Title},
902 """, titledata=r"""%%% Title Data
903 \title{\phantomsection%
904 This is the \emph{Title}%
905 \label{this-is-the-title}%
906 \\ % subtitle%
907 \DUdocumentsubtitle{This is the \emph{Subtitle}}%
908 \label{this-is-the-subtitle}}
909 \author{}
910 \date{}
911 """)) + r"""\maketitle
914 \section{This is a \emph{section title}%
915 \label{this-is-a-section-title}%
918 This is the \emph{document}.
920 \end{document}
921 """],
924 totest_stylesheet['two-styles'] = [
925 # input
926 ["""two stylesheet links in the header""",
927 head_template.substitute(dict(parts, stylesheet =
928 r"""\usepackage{data/spam}
929 \input{data/ham.tex}
930 """)) + r"""
931 two stylesheet links in the header
933 \end{document}
934 """],
937 totest_stylesheet_embed['two-styles'] = [
938 # input
939 ["""two stylesheets embedded in the header""",
940 head_template.substitute(dict(parts, stylesheet =
941 r"""% Cannot embed stylesheet 'data/spam.sty':
942 % No such file or directory.
943 % embedded stylesheet: data/ham.tex
944 \newcommand{\ham}{wonderful ham}
946 """)) + r"""
947 two stylesheets embedded in the header
949 \end{document}
950 """],
953 if __name__ == '__main__':
954 import unittest
955 unittest.main(defaultTest='suite')