SF.net SVN: docutils:[10003 ] trunk/docutils/test/test _writers

milde--- via Docutils-checkins <[email protected]>
Newsgroups gmane.text.docutils.cvs
Message-ID <[email protected]>
Revision: 10003
          http://sourceforge.net/p/docutils/code/10003
Author:   milde
Date:     2024-12-18 22:03:08 +0000 (Wed, 18 Dec 2024)
Log Message:
-----------
Simplify latex2e writer test modules.

Remove basic publish_parts()-test in "test_latex2e.py".
Parts are now tested in a dedicated test module.

"test_latex2e.py" now only checks the "body" part of the output.
Test of syntax constructs that change the document premable (head), are
moved to "test_latex2e_parts.py".

Modified Paths:
--------------
    trunk/docutils/test/test_writers/test_latex2e.py
    trunk/docutils/test/test_writers/test_latex2e_misc.py
    trunk/docutils/test/test_writers/test_latex2e_parts.py

Modified: trunk/docutils/test/test_writers/test_latex2e.py
===================================================================
--- trunk/docutils/test/test_writers/test_latex2e.py	2024-12-16 22:33:32 UTC (rev 10002)
+++ trunk/docutils/test/test_writers/test_latex2e.py	2024-12-18 22:03:08 UTC (rev 10003)
@@ -1,15 +1,17 @@
 #! /usr/bin/env python3
 # $Id$
 # Author: engelbert gruber <[email protected]>
+# Maintainer: [email protected]
 # Copyright: This module has been placed in the public domain.
 
 """
 Tests for latex2e writer.
+
+This module test only the "body" part of the output.
+For tests of constructs that change the "head", see test-latex2e_parts.py.
 """
 
-import os
 from pathlib import Path
-import string
 import sys
 import unittest
 
@@ -18,21 +20,10 @@
     # so we import the local `docutils` package.
     sys.path.insert(0, str(Path(__file__).resolve().parents[2]))
 
-from docutils.core import publish_string
+from docutils.core import publish_parts
 from docutils.writers import latex2e
 
-# DATA_ROOT is ./test/data from the docutils root
-DATA_ROOT = Path(__file__).resolve().parents[1] / 'data'
 
-ham = os.path.relpath(DATA_ROOT/'ham.tex').replace('\\', '/')
-spam = os.path.relpath(DATA_ROOT/'spam').replace('\\', '/')
-# workaround for PyPy (cf. https://sourceforge.net/p/docutils/bugs/471/)
-if sys.implementation.name == "pypy" and sys.version_info < (3, 10):
-    spampath = f"PosixPath('{spam}.sty')"
-else:
-    spampath = f"'{spam}.sty'"
-
-
 class WriterPublishTestCase(unittest.TestCase):
 
     maxDiff = None
@@ -39,460 +30,108 @@
     settings = {
         '_disable_config': True,
         'strict_visitor': True,
-        'output_encoding': 'unicode',
         # avoid latex writer future warnings:
         'use_latex_citations': False,
         'legacy_column_widths': True,
         }
 
-    def test_publish(self):
+    def test_body(self):
         for name, (settings_overrides, cases) in samples.items():
             for casenum, (rst_input, expected) in enumerate(cases):
                 with self.subTest(id=f'samples[{name!r}][{casenum}]'):
-                    output = publish_string(
+                    output = publish_parts(
                         source=rst_input,
                         writer=latex2e.Writer(),
-                        settings_overrides=self.settings|settings_overrides)
+                        settings_overrides=self.settings|settings_overrides
+                        )['body']
                     self.assertEqual(expected, output)
 
 
-head_template = string.Template(
-r"""$head_prefix% generated by Docutils <https://docutils.sourceforge.io/>
-\usepackage{cmap} % fix search and cut-and-paste in Acrobat
-$requirements
-%%% Custom LaTeX preamble
-$latex_preamble
-%%% User specified packages and stylesheets
-$stylesheet
-%%% Fallback definitions for Docutils-specific commands
-$fallbacks$pdfsetup
-%%% Body
-\begin{document}
-$titledata""")
-
-parts = {
-'head_prefix': r"""\documentclass[a4paper]{article}
-""",
-'requirements': r"""\usepackage[T1]{fontenc}
-""",
-'latex_preamble': r"""% PDF Standard Fonts
-\usepackage{mathptmx} % Times
-\usepackage[scaled=.90]{helvet}
-\usepackage{courier}
-""",
-'longtable': r"""\usepackage{longtable,ltcaption,array}
-\setlength{\extrarowheight}{2pt}
-\newlength{\DUtablewidth} % internal use in tables
-""",
-'stylesheet': '',
-'fallbacks': '',
-'fallbacks_highlight': r"""
-% basic code highlight:
-\providecommand*\DUrolecomment[1]{\textcolor[rgb]{0.40,0.40,0.40}{#1}}
-\providecommand*\DUroledeleted[1]{\textcolor[rgb]{0.40,0.40,0.40}{#1}}
-\providecommand*\DUrolekeyword[1]{\textbf{#1}}
-\providecommand*\DUrolestring[1]{\textit{#1}}
-
-% custom inline roles: \DUrole{#1}{#2} tries \DUrole#1{#2}
-\providecommand*{\DUrole}[2]{%
-  \ifcsname DUrole#1\endcsname%
-    \csname DUrole#1\endcsname{#2}%
-  \else%
-    #2%
-  \fi%
-}
-""",
-'pdfsetup': r"""
-% hyperlinks:
-\ifdefined\hypersetup
-\else
-  \usepackage[colorlinks=true,linkcolor=blue,urlcolor=blue]{hyperref}
-  \usepackage{bookmark}
-  \urlstyle{same} % normal text font (alternatives: tt, rm, sf)
-\fi
-""",
-'titledata': ''}
-
-head = head_template.substitute(parts)
-
-head_table = head_template.substitute(
-    dict(parts, requirements=parts['requirements'] + parts['longtable']))
-
-head_booktabs = head_template.substitute(
-    dict(parts, requirements=parts['requirements']
-         + '\\usepackage{booktabs}\n' + parts['longtable']))
-
-head_image = head_template.substitute(
-    dict(parts, requirements=parts['requirements']
-         + '\\usepackage{graphicx}\n'))
-
-head_minitoc = head_template.substitute(
-    dict(parts, requirements=parts['requirements']
-         + '%% local table of contents\n'
-           '\\usepackage{minitoc}\n'
-           '\\dosecttoc\n'
-           '\\mtcsetdepth{secttoc}{5}\n'
-           '\\setcounter{secnumdepth}{0}\n'))
-
-head_textcomp = head_template.substitute(
-    dict(parts, requirements=parts['requirements']
-         + '\\usepackage{textcomp} % text symbol macros\n'))
-
-head_alltt = head_template.substitute(
-    dict(parts, requirements=parts['requirements']
-         + '\\usepackage{alltt}\n'))
-
-
 samples = {}
 
-samples['url_chars'] = ({}, [
-["http://nowhere/url_with%28parens%29",
-head + r"""
-\url{http://nowhere/url_with\%28parens\%29}
 
-\end{document}
-"""],
-])
-
-samples['textcomp'] = ({}, [
-["2 µm is just 2/1000000 m",
-head_textcomp + r"""
-2 \textmu{}m is just 2/1000000 m
-
-\end{document}
-"""],
-])
-
-samples['images'] = ({}, [
-["""
-.. image:: blue%20square.png
-.. image:: vectors.svg
-""",
-head_image + r"""
-\includegraphics{blue square.png}
-
-\includegraphics{vectors.svg}
-
-\end{document}
-"""],
-])
-
-samples['svg-image'] = ({'stylesheet': 'svg'}, [
-["""
-.. image:: pixels.png
-.. image:: vectors.svg
-""",
-head_template.substitute(dict(parts,
-requirements=r"""\usepackage[T1]{fontenc}
-\usepackage{graphicx}
-""",
-stylesheet=r"""\usepackage{svg}
-""")) + r"""
-\includegraphics{pixels.png}
-
-\includesvg{vectors.svg}
-
-\end{document}
-"""],
-])
-
-samples['spanish_quote'] = ({}, [
-[".. role:: language-es\n\nUnd damit :language-es:`basta`!",
-head_template.substitute(dict(parts,
-requirements=r"""\usepackage[T1]{fontenc}
-\usepackage[spanish,main=english]{babel}
-\AtBeginDocument{\shorthandoff{.<>}}
-""")) + r"""
-Und damit \foreignlanguage{spanish}{basta}!
-
-\end{document}
-"""],
-])
-
-samples['code_role'] = ({}, [
-[':code:`x=1`',
-head_template.substitute(dict(parts, requirements=parts['requirements']
-                              + '\\usepackage{color}\n',
-                              fallbacks=parts['fallbacks_highlight']))
-+ r"""
-\texttt{\DUrole{code}{x=1}}
-
-\end{document}
-"""],
-])
-
-samples['minitoc'] = ({}, [
-# local toc reqires the minitoc package
-# and either \tableofcontents or \faketableofcontents
+samples['default'] = ({}, [
+# url chars
+['http://nowhere/url_with%28parens%29',
+ '\n\\url{http://nowhere/url_with\\%28parens\\%29}\n'
+],
+# enumerated lists
 ["""\
-section with local ToC
-======================
+1. Item 1.
+2. Second to the previous item this one will explain
 
-.. contents::
-   :local:
+  a) nothing.
+  b) or some other.
 
-section not in local toc
-========================
-""",
-head_minitoc + r"""
+3. Third is
 
-\section{section with local ToC%
-  \label{section-with-local-toc}%
-}
-
-\phantomsection\label{contents}
-\mtcsettitle{secttoc}{}
-\secttoc
-
-
-\section{section not in local toc%
-  \label{section-not-in-local-toc}%
-}
-
-\faketableofcontents % for local ToCs
-
-\end{document}
-"""],
-])
-
-samples['table_of_contents'] = ({'use_latex_toc': False}, [
-["""\
-.. contents:: Table of Contents
-
-Title 1
-=======
-Paragraph 1.
-
-Title 2
--------
-Paragraph 2.
+  (I) having pre and postfixes
+  (II) in roman numerals.
 """,
-# expected output
-head_template.substitute(dict(parts,
-    requirements=parts['requirements'] + '\\setcounter{secnumdepth}{0}\n',
-    fallbacks=r"""
-% class handling for environments (block-level elements)
-% \begin{DUclass}{spam} tries \DUCLASSspam and
-% \end{DUclass}{spam} tries \endDUCLASSspam
-\ifdefined\DUclass
-\else % poor man's "provideenvironment"
-  \newenvironment{DUclass}[1]%
-    {% "#1" does not work in end-part of environment.
-     \def\DocutilsClassFunctionName{DUCLASS#1}
-     \csname \DocutilsClassFunctionName \endcsname}%
-    {\csname end\DocutilsClassFunctionName \endcsname}%
-\fi
+r"""
+\begin{enumerate}
+\item Item 1.
 
-% title for topics, admonitions, unsupported section levels, and sidebar
-\providecommand*{\DUtitle}[1]{%
-  \smallskip\noindent\textbf{#1}\smallskip}
+\item Second to the previous item this one will explain
+\end{enumerate}
 
-\providecommand*{\DUCLASScontents}{%
-  \renewenvironment{itemize}%
-    {\begin{list}{}{\setlength{\partopsep}{0pt}
-                    \setlength{\parsep}{0pt}}
-                   }%
-    {\end{list}}%
-}
-""")) + r"""
-\phantomsection\label{table-of-contents}
-\pdfbookmark[1]{Table of Contents}{table-of-contents}
+\begin{quote}
+\begin{enumerate}
+\renewcommand{\labelenumi}{\alph{enumi})}
+\item nothing.
 
-\begin{DUclass}{contents}
+\item or some other.
+\end{enumerate}
+\end{quote}
 
-\DUtitle{Table of Contents}
-
-\begin{itemize}
-\item \hyperref[title-1]{Title 1}
-
-\begin{itemize}
-\item \hyperref[title-2]{Title 2}
-\end{itemize}
-\end{itemize}
-\end{DUclass}
-
-
-\section{Title 1%
-  \label{title-1}%
-}
-
-Paragraph 1.
-
-
-\subsection{Title 2%
-  \label{title-2}%
-}
-
-Paragraph 2.
-
-\end{document}
-"""],
-])
-
-
-samples['footnote_text'] = ({}, [
-["""\
-.. [1] paragraph
-
-.. [2] 1. enumeration
-""",
-# expected output
-head_template.substitute(dict(parts,
-    fallbacks=r"""
-% numerical or symbol footnotes with hyperlinks and backlinks
-\providecommand*{\DUfootnotemark}[3]{%
-  \raisebox{1em}{\hypertarget{#1}{}}%
-  \hyperlink{#2}{\textsuperscript{#3}}%
-}
-\providecommand{\DUfootnotetext}[4]{%
-  \begingroup%
-  \renewcommand{\thefootnote}{%
-    \protect\raisebox{1em}{\protect\hypertarget{#1}{}}%
-    \protect\hyperlink{#2}{#3}}%
-  \footnotetext{#4}%
-  \endgroup%
-}
-""")) + r"""%
-\DUfootnotetext{footnote-1}{footnote-1}{1}{%
-paragraph
-}
-%
-\DUfootnotetext{footnote-2}{footnote-2}{2}{
 \begin{enumerate}
-\item enumeration
+\setcounter{enumi}{2}
+\item Third is
 \end{enumerate}
-}
 
-\end{document}
-"""],
-])
+\begin{quote}
+\begin{enumerate}
+\renewcommand{\labelenumi}{(\Roman{enumi})}
+\item having pre and postfixes
 
-samples['no_sectnum'] = ({}, [
-["""\
-.. contents::
-
-unnumbered section
-------------------
-""",
-# expected output
-head_template.substitute(dict(parts,
-    requirements=parts['requirements'] + '\\setcounter{secnumdepth}{0}\n'
-)) + r"""
-\phantomsection\label{contents}
-\pdfbookmark[1]{Contents}{contents}
-\tableofcontents
-
-
-\section{unnumbered section%
-  \label{unnumbered-section}%
-}
-
-\end{document}
+\item in roman numerals.
+\end{enumerate}
+\end{quote}
 """],
-])
-
-samples['sectnum'] = ({}, [
-["""\
-.. contents::
-.. sectnum::
-
-first section
--------------
+# bracket protection
+["""
+* [no option] to this item
 """,
-# expected output
-head_template.substitute(dict(parts,
-    requirements=parts['requirements'] + '\\setcounter{secnumdepth}{0}\n'
-)) + r"""
-\phantomsection\label{contents}
-\pdfbookmark[1]{Contents}{contents}
-\tableofcontents
-
-
-\section{1~~~first section%
-  \label{first-section}%
-}
-
-\end{document}
+r"""
+\begin{itemize}
+\item {[}no option{]} to this item
+\end{itemize}
 """],
-])
+# raw directive and role
+[r""".. raw:: latex
 
-samples['depth'] = ({}, [
-["""\
-.. contents::
-    :depth: 1
+   $E=mc^2$
 
-first section
--------------
-""",
-# expected output
-head_template.substitute(dict(parts,
-    requirements=parts['requirements'] + '\\setcounter{secnumdepth}{0}\n'
-)) + r"""
-\phantomsection\label{contents}
-\pdfbookmark[1]{Contents}{contents}
-\setcounter{tocdepth}{1}
-\tableofcontents
+A paragraph.
 
+.. |sub| raw:: latex
 
-\section{first section%
-  \label{first-section}%
-}
+   (some raw text)
 
-\end{document}
-"""],
-])
-
-samples['book'] = ({'documentclass': 'book'}, [
-["""\
-.. contents::
-    :depth: 1
-
-first chapter
--------------
+Foo |sub|
+same paragraph.
 """,
-# expected output
-head_template.substitute(dict(parts,
-    head_prefix=r"""\documentclass[a4paper]{book}
-""",
-    requirements=parts['requirements'] + '\\setcounter{secnumdepth}{-1}\n'
-)) + r"""
-\phantomsection\label{contents}
-\pdfbookmark[1]{Contents}{contents}
-\setcounter{tocdepth}{0}
-\tableofcontents
+r"""
+$E=mc^2$
 
+A paragraph.
 
-\chapter{first chapter%
-  \label{first-chapter}%
-}
-
-\end{document}
+Foo (some raw text)
+same paragraph.
 """],
 ])
 
 
-samples['no_sectnum'] = ({'use_latex_toc': False,
-                          'sectnum_xform': False}, [
-["""\
-some text
-
-first section
--------------
-""",
-# expected output
-head_template.substitute(dict(parts, requirements=parts['requirements']
-                              + '\\setcounter{secnumdepth}{0}\n')) + r"""
-some text
-
-
-\section{first section%
-  \label{first-section}%
-}
-
-\end{document}
-"""],
-])
-
 samples['latex_sectnum'] = ({'sectnum_xform': False}, [
 ["""\
 .. sectnum::
@@ -502,8 +141,7 @@
 first section
 -------------
 """,
-# expected output
-head + r"""
+r"""
 some text
 
 
@@ -510,8 +148,6 @@
 \section{first section%
   \label{first-section}%
 }
-
-\end{document}
 """],
 ])
 
@@ -523,8 +159,7 @@
 .. [my_cite2006]
    The underscore is mishandled.
 """,
-# expected output
-head + r"""
+r"""
 Just a test citation \cite{my_cite2006}.
 
 \begin{thebibliography}{my\_cite2006}
@@ -532,8 +167,6 @@
 The underscore is mishandled.
 }
 \end{thebibliography}
-
-\end{document}
 """],
 ["""\
 Two non-citations: [MeYou2007]_[YouMe2007]_.
@@ -548,8 +181,7 @@
 .. [MeYou2007] not.
 .. [YouMe2007] important.
 """,
-# expected output
-head + r"""
+r"""
 Two non-citations: {[}MeYou2007{]}\_{[}YouMe2007{]}\_.
 
 Need to be separated for grouping: \cite{MeYou2007,YouMe2007}.
@@ -566,656 +198,25 @@
 important.
 }
 \end{thebibliography}
-
-\end{document}
 """],
 ])
 
 
-samples['enumerated_lists'] = ({}, [
-["""\
-1. Item 1.
-2. Second to the previous item this one will explain
-
-  a) nothing.
-  b) or some other.
-
-3. Third is
-
-  (I) having pre and postfixes
-  (II) in roman numerals.
-""",
-# expected output
-head + r"""
-\begin{enumerate}
-\item Item 1.
-
-\item Second to the previous item this one will explain
-\end{enumerate}
-
-\begin{quote}
-\begin{enumerate}
-\renewcommand{\labelenumi}{\alph{enumi})}
-\item nothing.
-
-\item or some other.
-\end{enumerate}
-\end{quote}
-
-\begin{enumerate}
-\setcounter{enumi}{2}
-\item Third is
-\end{enumerate}
-
-\begin{quote}
-\begin{enumerate}
-\renewcommand{\labelenumi}{(\Roman{enumi})}
-\item having pre and postfixes
-
-\item in roman numerals.
-\end{enumerate}
-\end{quote}
-
-\end{document}
-"""],
-])
-
-# TODO: need to test for quote replacing if the language uses "ASCII-quotes"
-# as active character (e.g. de (ngerman)).
-
-
-samples['tables'] = ({}, [
-["""\
-.. table:: Foo
-
-   +-----+-----+
-   |     |     |
-   +-----+-----+
-   |     |     |
-   +-----+-----+
-""",
-head_table + r"""
-\setlength{\DUtablewidth}{\linewidth}%
-\begin{longtable}{|p{0.075\DUtablewidth}|p{0.075\DUtablewidth}|}
-\caption{Foo}\\
-\hline
- &  \\
-\hline
- &  \\
-\hline
-\end{longtable}
-
-\end{document}
-"""],
-["""\
-.. table::
-   :class: borderless
-
-   +-----+-----+
-   |  1  |  2  |
-   +-----+-----+
-   |  3  |  4  |
-   +-----+-----+
-""",
-head_table + """
-\\setlength{\\DUtablewidth}{\\linewidth}%
-\\begin{longtable*}{p{0.075\\DUtablewidth}p{0.075\\DUtablewidth}}
-
-1
- & \n\
-2
- \\\\
-
-3
- & \n\
-4
- \\\\
-\\end{longtable*}
-
-\\end{document}
-"""],
-["""\
-.. table::
-   :class: booktabs
-
-   +-----+-+
-   |  1  |2|
-   +-----+-+
-""",
-head_booktabs + """
-\\setlength{\\DUtablewidth}{\\linewidth}%
-\\begin{longtable*}{p{0.075\\DUtablewidth}p{0.028\\DUtablewidth}}
-\\toprule
-
-1
- & \n\
-2
- \\\\
-\\bottomrule
-\\end{longtable*}
-
-\\end{document}
-"""],
-["""\
-.. table::
-   :class: colwidths-auto
-
-   +-----+-+
-   |  1  |2|
-   +-----+-+
-""",
-head_table + r"""
-\begin{longtable*}{|l|l|}
-\hline
-1 & 2 \\
-\hline
-\end{longtable*}
-
-\end{document}
-"""],
-["""\
-.. table::
-   :widths: auto
-
-   +-----+-+
-   |  1  |2|
-   +-----+-+
-""",
-head_table + r"""
-\begin{longtable*}{|l|l|}
-\hline
-1 & 2 \\
-\hline
-\end{longtable*}
-
-\end{document}
-"""],
-["""\
-.. table::
-   :widths: 15, 30
-
-   +-----+-----+
-   |  1  |  2  |
-   +-----+-----+
-""",
-head_table + """
-\\setlength{\\DUtablewidth}{\\linewidth}%
-\\begin{longtable*}{|p{0.191\\DUtablewidth}|p{0.365\\DUtablewidth}|}
-\\hline
-
-1
- & \n\
-2
- \\\\
-\\hline
-\\end{longtable*}
-
-\\end{document}
-"""],
-])
-
-
-samples['booktabs'] = ({'table_style': ['booktabs']}, [
-# borderless overrides "booktabs" table_style
-["""\
-.. table::
-   :class: borderless
-
-   +-----+-----+
-   |  1  |  2  |
-   +-----+-----+
-   |  3  |  4  |
-   +-----+-----+
-""",
-head_table + """
-\\setlength{\\DUtablewidth}{\\linewidth}%
-\\begin{longtable*}{p{0.075\\DUtablewidth}p{0.075\\DUtablewidth}}
-
-1
- & \n\
-2
- \\\\
-
-3
- & \n\
-4
- \\\\
-\\end{longtable*}
-
-\\end{document}
-"""],
-["""\
-.. table::
-   :widths: auto
-
-   +-----+-+
-   |  1  |2|
-   +-----+-+
-""",
-head_booktabs + r"""
-\begin{longtable*}{ll}
-\toprule
-1 & 2 \\
-\bottomrule
-\end{longtable*}
-
-\end{document}
-"""],
-["""\
-.. table::
-   :widths: 15, 30
-
-   +-----+-----+
-   |  1  |  2  |
-   +-----+-----+
-""",
-head_booktabs + """
-\\setlength{\\DUtablewidth}{\\linewidth}%
-\\begin{longtable*}{p{0.191\\DUtablewidth}p{0.365\\DUtablewidth}}
-\\toprule
-
-1
- & \n\
-2
- \\\\
-\\bottomrule
-\\end{longtable*}
-
-\\end{document}
-"""],
-])
-
-
-samples['colwidths_auto'] = ({'table_style': ['colwidths-auto']}, [
-["""\
-.. table::
-   :class: borderless
-
-   +-----+-----+
-   |  1  |  2  |
-   +-----+-----+
-   |  3  |  4  |
-   +-----+-----+
-""",
-head_table + r"""
-\begin{longtable*}{ll}
-1 & 2 \\
-3 & 4 \\
-\end{longtable*}
-
-\end{document}
-"""],
-["""\
-.. table::
-   :class: booktabs
-
-   +-----+-+
-   |  1  |2|
-   +-----+-+
-""",
-head_booktabs + r"""
-\begin{longtable*}{ll}
-\toprule
-1 & 2 \\
-\bottomrule
-\end{longtable*}
-
-\end{document}
-"""],
-# given width overrides "colwidth-auto"
-["""\
-.. table::
-   :widths: 15, 30
-
-   +-----+-----+
-   |  1  |  2  |
-   +-----+-----+
-""",
-head_table + """
-\\setlength{\\DUtablewidth}{\\linewidth}%
-\\begin{longtable*}{|p{0.191\\DUtablewidth}|p{0.365\\DUtablewidth}|}
-\\hline
-
-1
- & \n\
-2
- \\\\
-\\hline
-\\end{longtable*}
-
-\\end{document}
-"""],
-])
-
-samples['table_align'] = ({}, [
-["""\
-.. table::
-   :align: right
-
-   +-----+-----+
-   |  1  |  2  |
-   +-----+-----+
-""",
-head_table + """
-\\setlength{\\DUtablewidth}{\\linewidth}%
-\\begin{longtable*}[r]{|p{0.075\\DUtablewidth}|p{0.075\\DUtablewidth}|}
-\\hline
-
-1
- & \n\
-2
- \\\\
-\\hline
-\\end{longtable*}
-
-\\end{document}
-"""],
-])
-
-samples['table_empty_cells'] = ({}, [
-["""\
-===== ======
-Title
-===== ======
-entry value1
-===== ======
-""",
-head_table + """
-\\setlength{\\DUtablewidth}{\\linewidth}%
-\\begin{longtable*}{|p{0.075\\DUtablewidth}|p{0.086\\DUtablewidth}|}
-\\hline
-\\textbf{%
-Title
-} &  \\\\
-\\hline
-\\endfirsthead
-\\hline
-\\textbf{%
-Title
-} &  \\\\
-\\hline
-\\endhead
-\\multicolumn{2}{p{0.16\\DUtablewidth}}{\\raggedleft\\ldots continued on next page}\\\\
-\\endfoot
-\\endlastfoot
-
-entry
- & \n\
-value1
- \\\\
-\\hline
-\\end{longtable*}
-
-\\end{document}
-"""],
-["""\
-+----+----+
-| c3 | c4 |
-+----+----+
-|         |
-+---------+
-""",
-head_table + """
-\\setlength{\\DUtablewidth}{\\linewidth}%
-\\begin{longtable*}{|p{0.063\\DUtablewidth}|p{0.063\\DUtablewidth}|}
-\\hline
-
-c3
- & \n\
-c4
- \\\\
-\\hline
-\\multicolumn{2}{|p{0.13\\DUtablewidth}|}{} \\\\
-\\hline
-\\end{longtable*}
-
-\\end{document}
-"""],
-])
-
-samples['table_nonstandard_class'] = ({}, [
-["""\
-.. table::
-   :class: my-class
-
-   +-----+-----+
-   |  1  |  2  |
-   +-----+-----+
-   |  3  |  4  |
-   +-----+-----+
-""",
-head_template.substitute(
-    dict(
-        parts,
-        requirements=parts['requirements'] + parts['longtable'],
-        fallbacks=r"""
-% class handling for environments (block-level elements)
-% \begin{DUclass}{spam} tries \DUCLASSspam and
-% \end{DUclass}{spam} tries \endDUCLASSspam
-\ifdefined\DUclass
-\else % poor man's "provideenvironment"
-  \newenvironment{DUclass}[1]%
-    {% "#1" does not work in end-part of environment.
-     \def\DocutilsClassFunctionName{DUCLASS#1}
-     \csname \DocutilsClassFunctionName \endcsname}%
-    {\csname end\DocutilsClassFunctionName \endcsname}%
-\fi
-"""
-    )
-) + """
-\\begin{DUclass}{my-class}
-\\setlength{\\DUtablewidth}{\\linewidth}%
-\\begin{longtable*}{|p{0.075\\DUtablewidth}|p{0.075\\DUtablewidth}|}
-\\hline
-
-1
- & \n\
-2
- \\\\
-\\hline
-
-3
- & \n\
-4
- \\\\
-\\hline
-\\end{longtable*}
-\\end{DUclass}
-
-\\end{document}
-"""],
-])
-
-# The "[" needs to be protected (otherwise it will be seen as an
-# option to "\\", "\item", etc. ).
-samples['bracket_protection'] = ({}, [
-["""
-* [no option] to this item
-""",
-head + r"""
-\begin{itemize}
-\item {[}no option{]} to this item
-\end{itemize}
-
-\end{document}
-"""],
-])
-
-samples['literal_block'] = ({}, [
-["""\
-Test special characters { [ \\\\ ] } in literal block::
-
-  { [ ( \\macro
-
-  } ] )
-""",
-head_alltt + r"""
-Test special characters \{ {[} \textbackslash{} {]} \} in literal block:
-
-\begin{quote}
-\begin{alltt}
-\{ [ ( \textbackslash{}macro
-
-\} ] )
-\end{alltt}
-\end{quote}
-
-\end{document}
-"""],
-])
-
-samples['raw'] = ({}, [
-[r""".. raw:: latex
-
-   $E=mc^2$
-
-A paragraph.
-
-.. |sub| raw:: latex
-
-   (some raw text)
-
-Foo |sub|
-same paragraph.
-""",
-head + r"""
-$E=mc^2$
-
-A paragraph.
-
-Foo (some raw text)
-same paragraph.
-
-\end{document}
-"""],
-[r""".. compound::
-
-  Compound paragraph
-
-  .. raw:: LaTeX
-
-     raw LaTeX block
-
-  compound paragraph continuation.
-""",
-head_template.substitute(
-    dict(parts,
-         fallbacks=r"""
-% class handling for environments (block-level elements)
-% \begin{DUclass}{spam} tries \DUCLASSspam and
-% \end{DUclass}{spam} tries \endDUCLASSspam
-\ifdefined\DUclass
-\else % poor man's "provideenvironment"
-  \newenvironment{DUclass}[1]%
-    {% "#1" does not work in end-part of environment.
-     \def\DocutilsClassFunctionName{DUCLASS#1}
-     \csname \DocutilsClassFunctionName \endcsname}%
-    {\csname end\DocutilsClassFunctionName \endcsname}%
-\fi
-""")
-) + r"""
-\begin{DUclass}{compound}
-Compound paragraph
-raw LaTeX block
-compound paragraph continuation.
-\end{DUclass}
-
-\end{document}
-"""],
-])
-
-samples['title_with_inline_markup'] = ({}, [
-["""\
-This is the *Title*
-===================
-
-This is the *Subtitle*
-----------------------
-
-This is a *section title*
-~~~~~~~~~~~~~~~~~~~~~~~~~
-
-This is the *document*.
-""",
-head_template.substitute(dict(parts,
-    requirements=parts['requirements'] + '\\setcounter{secnumdepth}{0}\n',
-    fallbacks=r"""
-% subtitle (in document title)
-\providecommand*{\DUdocumentsubtitle}[1]{{\large #1}}
-""",
-    pdfsetup=parts['pdfsetup'] + r"""\hypersetup{
-  pdftitle={This is the Title},
-}
-""", titledata=r"""\title{This is the \emph{Title}%
-  \label{this-is-the-title}%
-  \\%
-  \DUdocumentsubtitle{This is the \emph{Subtitle}}%
-  \label{this-is-the-subtitle}}
-\author{}
-\date{}
-""")) + r"""\maketitle
-
-
-\section{This is a \emph{section title}%
-  \label{this-is-a-section-title}%
-}
-
-This is the \emph{document}.
-
-\end{document}
-"""],
-])
-
-
-samples['stylesheet_path'] = ({'stylesheet_path': f'{spam},{ham}'}, [
-["""two stylesheet links in the header""",
-head_template.substitute(dict(parts,
-stylesheet=r"""\usepackage{%s}
-\input{%s}
-""" % (spam, ham))) + r"""
-two stylesheet links in the header
-
-\end{document}
-"""],
-])
-
-samples['embed_stylesheet'] = ({'stylesheet_path': f'{spam},{ham}',
-                                'embed_stylesheet': True,
-                                'warning_stream': ''}, [
-["""two stylesheets embedded in the header""",
-head_template.substitute(dict(parts,
-stylesheet=f"""\
-% Cannot embed stylesheet:
-%  [Errno 2] No such file or directory: {spampath}
-% embedded stylesheet: {ham}
-\\newcommand{{\\ham}}{{wonderful ham}}
-
-""")) + r"""
-two stylesheets embedded in the header
-
-\end{document}
-"""],
-])
-
 samples['bibtex'] = ({'use_bibtex': ['alpha', 'xampl']}, [
 ["""\
 Just a test citation [book-full]_.
 """,
-head + r"""
+r"""
 Just a test citation \cite{book-full}.
 
 \bibliographystyle{alpha}
 \bibliography{xampl}
-
-\end{document}
 """],
 ["""\
 No bibliography if there is no citation.
 """,
-head + r"""
+r"""
 No bibliography if there is no citation.
-
-\end{document}
 """],
 ])
 

Modified: trunk/docutils/test/test_writers/test_latex2e_misc.py
===================================================================
--- trunk/docutils/test/test_writers/test_latex2e_misc.py	2024-12-16 22:33:32 UTC (rev 10002)
+++ trunk/docutils/test/test_writers/test_latex2e_misc.py	2024-12-18 22:03:08 UTC (rev 10003)
@@ -94,36 +94,7 @@
         self.assertNotIn(r'\item \hyperref[foo]{foo}', result)
         self.assertIn(r'\tableofcontents', result)
 
-    def test_publish_parts(self):
-        """Check for the presence of documented parts.
-        """
-        parts = core.publish_parts(sample_multiterm,
-                                   writer=latex2e.Writer(),
-                                   settings_overrides=self.settings)
-        documented_parts = [
-            'abstract',
-            'body',
-            'body_pre_docinfo',
-            'dedication',
-            'docinfo',
-            'encoding',
-            'errors',
-            'fallbacks',
-            'head_prefix',
-            'latex_preamble',
-            'pdfsetup',
-            'requirements',
-            'stylesheet',
-            'subtitle',
-            'title',
-            'titledata',
-            'version',
-            'whole'
-            ]
-        self.assertEqual(documented_parts, sorted(parts.keys()))
-        self.assertEqual(expected_multiterm, parts['body'])
 
-
 class WarningsTestCase(unittest.TestCase):
 
     def test_future_warnings(self):

Modified: trunk/docutils/test/test_writers/test_latex2e_parts.py
===================================================================
--- trunk/docutils/test/test_writers/test_latex2e_parts.py	2024-12-16 22:33:32 UTC (rev 10002)
+++ trunk/docutils/test/test_writers/test_latex2e_parts.py	2024-12-18 22:03:08 UTC (rev 10003)
@@ -19,6 +19,7 @@
 """
 
 from pathlib import Path
+import os
 import sys
 import unittest
 
@@ -32,35 +33,18 @@
 from docutils.core import publish_parts
 from docutils.writers import latex2e
 
+# DATA_ROOT is ./test/data from the docutils root
+DATA_ROOT = Path(__file__).resolve().parents[1] / 'data'
 
-class LaTeXWriterPublishPartsTestCase(unittest.TestCase):
-    """Test LaTeX writer `publish_parts()` interface."""
+ham = os.path.relpath(DATA_ROOT/'ham.tex').replace('\\', '/')
+spam = os.path.relpath(DATA_ROOT/'spam').replace('\\', '/')
+# workaround for PyPy (cf. https://sourceforge.net/p/docutils/bugs/471/)
+if sys.implementation.name == "pypy" and sys.version_info < (3, 10):
+    spampath = f"PosixPath('{spam}.sty')"
+else:
+    spampath = f"'{spam}.sty'"
 
-    maxDiff = None
-    settings = {'_disable_config': True,
-                'strict_visitor': True,
-                # avoid latex writer future warnings:
-                'use_latex_citations': False,
-                'legacy_column_widths': True,
-                }
-
-    def test_publish_parts(self):
-        for name, (settings_overrides, cases) in samples.items():
-            for casenum, (case_input, expected_parts) in enumerate(cases):
-                parts = publish_parts(
-                    source=case_input,
-                    writer=latex2e.Writer(),
-                    settings_overrides=self.settings|settings_overrides,
-                    )
-                expected = default_parts | expected_parts
-                expected['whole'] = expected['whole'].format(**expected)
-
-                for key in parts.keys():
-                    with self.subTest(id=f'samples[{name!r}][{casenum}][{key}]'):
-                        self.assertEqual(f'{expected[key]}', f'{parts[key]}')
-
-
-default_parts = {
+DEFAULT_PARTS = {
     'abstract': '',
     'body': '',
     'body_pre_docinfo': '',
@@ -74,13 +58,14 @@
                       '\\usepackage{mathptmx} % Times\n'
                       '\\usepackage[scaled=.90]{helvet}\n'
                       '\\usepackage{courier}\n',
-    'pdfsetup': '% hyperlinks:\n'
-                '\\ifdefined\\hypersetup\n'
-                '\\else\n'
-                '  \\usepackage[colorlinks=true,linkcolor=blue,urlcolor=blue]{hyperref}\n'
-                '  \\usepackage{bookmark}\n'
-                '  \\urlstyle{same} % normal text font (alternatives: tt, rm, sf)\n'
-                '\\fi\n',
+    'pdfsetup': r"""% hyperlinks:
+\ifdefined\hypersetup
+\else
+  \usepackage[colorlinks=true,linkcolor=blue,urlcolor=blue]{hyperref}
+  \usepackage{bookmark}
+  \urlstyle{same} % normal text font (alternatives: tt, rm, sf)
+\fi
+""",
     'requirements': '\\usepackage[T1]{fontenc}\n',
     'stylesheet': '',
     'subtitle': '',
@@ -87,23 +72,43 @@
     'title': '',
     'titledata': '',
     'version': f'{docutils.__version__}',
-    'whole': '{head_prefix}'
-             '% generated by Docutils <https://docutils.sourceforge.io/>\n'
-             '\\usepackage{{cmap}} % fix search and cut-and-paste in Acrobat\n'
-             '{requirements}\n'
-             '%%% Custom LaTeX preamble\n'
-             '{latex_preamble}\n'
-             '%%% User specified packages and stylesheets\n'
-             '{stylesheet}\n'
-             '%%% Fallback definitions for Docutils-specific commands\n'
-             '{fallbacks}\n'
-             '{pdfsetup}\n'
-             '%%% Body\n'
-             '\\begin{{document}}\n'
-             '{body}\n'
-             '\\end{{document}}\n'
-             }
+    }
 
+
+REQUIREMENTS_TABLE = r"""\usepackage{longtable,ltcaption,array}
+\setlength{\extrarowheight}{2pt}
+\newlength{\DUtablewidth} % internal use in tables
+"""
+
+
+class LaTeXWriterPublishPartsTestCase(unittest.TestCase):
+    """Test LaTeX writer `publish_parts()` interface."""
+
+    maxDiff = None
+    settings = {'_disable_config': True,
+                'strict_visitor': True,
+                # avoid latex writer future warnings:
+                'use_latex_citations': False,
+                'legacy_column_widths': True,
+                }
+
+    def test_publish_parts(self):
+        for name, (settings_overrides, cases) in samples.items():
+            for casenum, (case_input, expected) in enumerate(cases):
+                parts = publish_parts(
+                    source=case_input,
+                    writer=latex2e.Writer(),
+                    settings_overrides=self.settings|settings_overrides,
+                    )
+                expected = DEFAULT_PARTS | expected
+                with self.subTest(id=f'samples[{name!r}][{casenum}]'):
+                    for key in parts.keys():
+                        if key == 'whole':
+                            continue  # assembly tested in functional tests
+                        self.assertEqual(f'{expected[key]}', f'{parts[key]}',
+                                         msg=f'Differences in part "{key}"!')
+
+
 samples = {}
 
 samples['default'] = ({}, [
@@ -110,8 +115,883 @@
 ['',  # empty input string
  {}   # results in default parts
  ],
+['2 µm is just 2/1000000 m',
+ {'body': '\n2 µm is just 2/1000000 m\n',
+  'requirements': '\\usepackage[T1]{fontenc}\n'
+                  '\\usepackage{textcomp} % text symbol macros\n',
+  }
+ ],
+# load "babel" if there is a text part in a foreign language
+["""\
+.. role:: language-es
+
+Und damit :language-es:`basta`!
+""",
+ {'body': r"""
+Und damit \foreignlanguage{spanish}{basta}!
+""",
+  'requirements': '\\usepackage[T1]{fontenc}\n'
+                  '\\usepackage[spanish,main=english]{babel}\n'
+                  '\\AtBeginDocument{\\shorthandoff{.<>}}\n'
+  }],
+# load requirements for code syntax higlight
+[':code:`x=1`',
+ {'body': '\n\\texttt{\\DUrole{code}{x=1}}\n',
+  'fallbacks': r"""
+% basic code highlight:
+\providecommand*\DUrolecomment[1]{\textcolor[rgb]{0.40,0.40,0.40}{#1}}
+\providecommand*\DUroledeleted[1]{\textcolor[rgb]{0.40,0.40,0.40}{#1}}
+\providecommand*\DUrolekeyword[1]{\textbf{#1}}
+\providecommand*\DUrolestring[1]{\textit{#1}}
+
+% custom inline roles: \DUrole{#1}{#2} tries \DUrole#1{#2}
+\providecommand*{\DUrole}[2]{%
+  \ifcsname DUrole#1\endcsname%
+    \csname DUrole#1\endcsname{#2}%
+  \else%
+    #2%
+  \fi%
+}
+""",
+  'requirements': '\\usepackage[T1]{fontenc}\n'
+                  '\\usepackage{color}\n'
+  }],
+# footnote text
+["""\
+.. [1] paragraph
+
+.. [2] 1. enumeration
+""",
+ {'body': r"""%
+\DUfootnotetext{footnote-1}{footnote-1}{1}{%
+paragraph
+}
+%
+\DUfootnotetext{footnote-2}{footnote-2}{2}{
+\begin{enumerate}
+\item enumeration
+\end{enumerate}
+}
+""",
+  'fallbacks': r"""
+% numerical or symbol footnotes with hyperlinks and backlinks
+\providecommand*{\DUfootnotemark}[3]{%
+  \raisebox{1em}{\hypertarget{#1}{}}%
+  \hyperlink{#2}{\textsuperscript{#3}}%
+}
+\providecommand{\DUfootnotetext}[4]{%
+  \begingroup%
+  \renewcommand{\thefootnote}{%
+    \protect\raisebox{1em}{\protect\hypertarget{#1}{}}%
+    \protect\hyperlink{#2}{#3}}%
+  \footnotetext{#4}%
+  \endgroup%
+}
+""",
+  }],
+# no section numbering: switch off section numbering in requirements
+["""\
+.. contents::
+
+unnumbered section
+------------------
+""",
+ {'body': r"""
+\phantomsection\label{contents}
+\pdfbookmark[1]{Contents}{contents}
+\tableofcontents
+
+
+\section{unnumbered section%
+  \label{unnumbered-section}%
+}
+""",
+  'requirements': '\\usepackage[T1]{fontenc}\n'
+                  '\\setcounter{secnumdepth}{0}\n',
+  }],
+# Docutils section numbering: switch off section numbering in requirements
+["""\
+.. contents::
+.. sectnum::
+
+first section
+-------------
+""",
+ {'body': r"""
+\phantomsection\label{contents}
+\pdfbookmark[1]{Contents}{contents}
+\tableofcontents
+
+
+\section{1   first section%
+  \label{first-section}%
+}
+""",
+  'requirements': '\\usepackage[T1]{fontenc}\n'
+                  '\\setcounter{secnumdepth}{0}\n',
+  }],
+# LaTeX ToC with limited depth, no section numbers
+["""\
+.. contents::
+    :depth: 1
+
+first section
+-------------
+""",
+ {'body': r"""
+\phantomsection\label{contents}
+\pdfbookmark[1]{Contents}{contents}
+\setcounter{tocdepth}{1}
+\tableofcontents
+
+
+\section{first section%
+  \label{first-section}%
+}
+""",
+  'requirements': '\\usepackage[T1]{fontenc}\n'
+                  '\\setcounter{secnumdepth}{0}\n',
+  }],
+# local ToC reqires "minitoc" and \tableofcontents or \faketableofcontents
+["""\
+section with local ToC
+======================
+
+.. contents::
+   :local:
+
+section not in local toc
+========================
+""",
+ {'body': r"""
+
+\section{section with local ToC%
+  \label{section-with-local-toc}%
+}
+
+\phantomsection\label{contents}
+\mtcsettitle{secttoc}{}
+\secttoc
+
+
+\section{section not in local toc%
+  \label{section-not-in-local-toc}%
+}
+
+\faketableofcontents % for local ToCs
+""",
+  'requirements': '\\usepackage[T1]{fontenc}\n'
+                  '%% local table of contents\n'
+                  '\\usepackage{minitoc}\n'
+                  '\\dosecttoc\n'
+                  '\\mtcsetdepth{secttoc}{5}\n'
+                  '\\setcounter{secnumdepth}{0}\n'
+  }],
+# include images with "\includegraphics", load "graphicx" package
+["""\
+.. image:: blue%20square.png
+.. image:: vectors.svg
+""",
+ {'body': r"""
+\includegraphics{blue square.png}
+
+\includegraphics{vectors.svg}
+""",
+  'requirements': '\\usepackage[T1]{fontenc}\n'
+                  '\\usepackage{graphicx}\n',
+  }],
+# table with caption
+["""\
+.. table:: Foo
+
+   +-----+-----+
+   |     |     |
+   +-----+-----+
+   |     |     |
+   +-----+-----+
+""",
+ {'body': r"""
+\setlength{\DUtablewidth}{\linewidth}%
+\begin{longtable}{|p{0.075\DUtablewidth}|p{0.075\DUtablewidth}|}
+\caption{Foo}\\
+\hline
+ &  \\
+\hline
+ &  \\
+\hline
+\end{longtable}
+""",
+  'requirements': '\\usepackage[T1]{fontenc}\n'
+                  + REQUIREMENTS_TABLE,
+  }],
+# borderless table
+["""\
+.. table::
+   :class: borderless
+
+   +-----+-----+
+   |  1  |  2  |
+   +-----+-----+
+   |  3  |  4  |
+   +-----+-----+
+""",
+ {'body': """
+\\setlength{\\DUtablewidth}{\\linewidth}%
+\\begin{longtable*}{p{0.075\\DUtablewidth}p{0.075\\DUtablewidth}}
+
+1
+ & \n\
+2
+ \\\\
+
+3
+ & \n\
+4
+ \\\\
+\\end{longtable*}
+""",
+  'requirements': '\\usepackage[T1]{fontenc}\n'
+                  + REQUIREMENTS_TABLE
+  }],
+# table style "booktabs"
+["""\
+.. table::
+   :class: booktabs
+
+   +-----+-+
+   |  1  |2|
+   +-----+-+
+""",
+ {'body': """
+\\setlength{\\DUtablewidth}{\\linewidth}%
+\\begin{longtable*}{p{0.075\\DUtablewidth}p{0.028\\DUtablewidth}}
+\\toprule
+
+1
+ & \n\
+2
+ \\\\
+\\bottomrule
+\\end{longtable*}
+""",
+  'requirements': '\\usepackage[T1]{fontenc}\n'
+                  '\\usepackage{booktabs}\n'
+                  + REQUIREMENTS_TABLE
+  }],
+# collumn widths determined by LaTeX (via class argument)
+["""\
+.. table::
+   :class: colwidths-auto
+
+   +-----+-+
+   |  1  |2|
+   +-----+-+
+""",
+ {'body': r"""
+\begin{longtable*}{|l|l|}
+\hline
+1 & 2 \\
+\hline
+\end{longtable*}
+""",
+  'requirements': '\\usepackage[T1]{fontenc}\n'
+                  + REQUIREMENTS_TABLE
+  }],
+# collumn widths determined by LaTeX (via "width" option)
+["""\
+.. table::
+   :widths: auto
+
+   +-----+-+
+   |  1  |2|
+   +-----+-+
+""",
+ {'body': r"""
+\begin{longtable*}{|l|l|}
+\hline
+1 & 2 \\
+\hline
+\end{longtable*}
+""",
+  'requirements': '\\usepackage[T1]{fontenc}\n'
+                  + REQUIREMENTS_TABLE
+  }],
+# collumn widths specified via "width" option
+["""\
+.. table::
+   :widths: 15, 30
+
+   +-----+-----+
+   |  1  |  2  |
+   +-----+-----+
+""",
+ {'body': """
+\\setlength{\\DUtablewidth}{\\linewidth}%
+\\begin{longtable*}{|p{0.191\\DUtablewidth}|p{0.365\\DUtablewidth}|}
+\\hline
+
+1
+ & \n\
+2
+ \\\\
+\\hline
+\\end{longtable*}
+""",
+  'requirements': '\\usepackage[T1]{fontenc}\n'
+                  + REQUIREMENTS_TABLE
+  }],
+# table alignment
+["""\
+.. table::
+   :align: right
+
+   +-----+-----+
+   |  1  |  2  |
+   +-----+-----+
+""",
+ {'body': """
+\\setlength{\\DUtablewidth}{\\linewidth}%
+\\begin{longtable*}[r]{|p{0.075\\DUtablewidth}|p{0.075\\DUtablewidth}|}
+\\hline
+
+1
+ & \n\
+2
+ \\\\
+\\hline
+\\end{longtable*}
+""",
+  'requirements': '\\usepackage[T1]{fontenc}\n'
+                  + REQUIREMENTS_TABLE
+  }],
+# table with title row and empty cell
+["""\
+===== ======
+Title
+===== ======
+entry value1
+===== ======
+""",
+ {'body': """
+\\setlength{\\DUtablewidth}{\\linewidth}%
+\\begin{longtable*}{|p{0.075\\DUtablewidth}|p{0.086\\DUtablewidth}|}
+\\hline
+\\textbf{%
+Title
+} &  \\\\
+\\hline
+\\endfirsthead
+\\hline
+\\textbf{%
+Title
+} &  \\\\
+\\hline
+\\endhead
+\\multicolumn{2}{p{0.16\\DUtablewidth}}{\\raggedleft\\ldots continued on next page}\\\\
+\\endfoot
+\\endlastfoot
+
+entry
+ & \n\
+value1
+ \\\\
+\\hline
+\\end{longtable*}
+""",
+  'requirements': '\\usepackage[T1]{fontenc}\n'
+                  + REQUIREMENTS_TABLE
+  }],
+# table with emtpy rowspanning cell
+["""\
++----+----+
+| c3 | c4 |
++----+----+
+|         |
++---------+
+""",
+ {'body': """
+\\setlength{\\DUtablewidth}{\\linewidth}%
+\\begin{longtable*}{|p{0.063\\DUtablewidth}|p{0.063\\DUtablewidth}|}
+\\hline
+
+c3
+ & \n\
+c4
+ \\\\
+\\hline
+\\multicolumn{2}{|p{0.13\\DUtablewidth}|}{} \\\\
+\\hline
+\\end{longtable*}
+""",
+  'requirements': '\\usepackage[T1]{fontenc}\n'
+                  + REQUIREMENTS_TABLE
+  }],
+# table with custom class value
+["""\
+.. table::
+   :class: my-class
+
+   +-----+-----+
+   |  1  |  2  |
+   +-----+-----+
+   |  3  |  4  |
+   +-----+-----+
+""",
+ {'body': """
+\\begin{DUclass}{my-class}
+\\setlength{\\DUtablewidth}{\\linewidth}%
+\\begin{longtable*}{|p{0.075\\DUtablewidth}|p{0.075\\DUtablewidth}|}
+\\hline
+
+1
+ & \n\
+2
+ \\\\
+\\hline
+
+3
+ & \n\
+4
+ \\\\
+\\hline
+\\end{longtable*}
+\\end{DUclass}
+""",
+  'fallbacks': r"""
+% class handling for environments (block-level elements)
+% \begin{DUclass}{spam} tries \DUCLASSspam and
+% \end{DUclass}{spam} tries \endDUCLASSspam
+\ifdefined\DUclass
+\else % poor man's "provideenvironment"
+  \newenvironment{DUclass}[1]%
+    {% "#1" does not work in end-part of environment.
+     \def\DocutilsClassFunctionName{DUCLASS#1}
+     \csname \DocutilsClassFunctionName \endcsname}%
+    {\csname end\DocutilsClassFunctionName \endcsname}%
+\fi
+""",
+  'requirements': '\\usepackage[T1]{fontenc}\n'
+                  + REQUIREMENTS_TABLE
+  }],
+# literal block
+["""\
+Test special characters { [ \\\\ ] } in literal block::
+
+  { [ ( \\macro
+
+  } ] )
+""",
+ {'body': r"""
+Test special characters \{ {[} \textbackslash{} {]} \} in literal block:
+
+\begin{quote}
+\begin{alltt}
+\{ [ ( \textbackslash{}macro
+
+\} ] )
+\end{alltt}
+\end{quote}
+""",
+  'requirements': '\\usepackage[T1]{fontenc}\n'
+                  '\\usepackage{alltt}\n'
+  }],
+# raw block in compound directive
+["""\
+.. compound::
+
+  Compound paragraph
+
+  .. raw:: LaTeX
+
+     raw LaTeX block
+
+  compound paragraph continuation.
+""",
+ {'body': r"""
+\begin{DUclass}{compound}
+Compound paragraph
+raw LaTeX block
+compound paragraph continuation.
+\end{DUclass}
+""",
+  'fallbacks': r"""
+% class handling for environments (block-level elements)
+% \begin{DUclass}{spam} tries \DUCLASSspam and
+% \end{DUclass}{spam} tries \endDUCLASSspam
+\ifdefined\DUclass
+\else % poor man's "provideenvironment"
+  \newenvironment{DUclass}[1]%
+    {% "#1" does not work in end-part of environment.
+     \def\DocutilsClassFunctionName{DUCLASS#1}
+     \csname \DocutilsClassFunctionName \endcsname}%
+    {\csname end\DocutilsClassFunctionName \endcsname}%
+\fi
+""",
+  }],
+# titles with inline markup
+["""\
+This is the *Title*
+===================
+
+This is the *Subtitle*
+----------------------
+
+This is a *section title*
+~~~~~~~~~~~~~~~~~~~~~~~~~
+
+This is the *document*.
+""",
+ {'body': r"""
+
+\section{This is a \emph{section title}%
+  \label{this-is-a-section-title}%
+}
+
+This is the \emph{document}.
+""",
+  'body_pre_docinfo': '\\maketitle\n',
+  'fallbacks': r"""
+% subtitle (in document title)
+\providecommand*{\DUdocumentsubtitle}[1]{{\large #1}}
+""",
+  'pdfsetup': DEFAULT_PARTS['pdfsetup'] + r"""\hypersetup{
+  pdftitle={This is the Title},
+}
+""",
+  'requirements': '\\usepackage[T1]{fontenc}\n'
+                  '\\setcounter{secnumdepth}{0}\n',
+  'subtitle': 'This is the \\emph{Subtitle}',
+  'title': 'This is the \\emph{Title}',
+  'titledata': r"""\title{This is the \emph{Title}%
+  \label{this-is-the-title}%
+  \\%
+  \DUdocumentsubtitle{This is the \emph{Subtitle}}%
+  \label{this-is-the-subtitle}}
+\author{}
+\date{}
+"""
+  }],
+
+# template
+["""\
+""",
+ {'body': '',
+  'requirements': '\\usepackage[T1]{fontenc}\n'
+  }],
+
 ])
 
+samples['book'] = ({'documentclass': 'book'}, [
+# Top section level in LaTeX "book" class is 0 (chapter)
+["""\
+.. contents::
+    :depth: 1
 
+first chapter
+-------------
+""",
+ {'body': r"""
+\phantomsection\label{contents}
+\pdfbookmark[1]{Contents}{contents}
+\setcounter{tocdepth}{0}
+\tableofcontents
+
+
+\chapter{first chapter%
+  \label{first-chapter}%
+}
+""",
+  'head_prefix': '\\documentclass[a4paper]{book}\n',
+  'requirements': '\\usepackage[T1]{fontenc}\n'
+                  '\\setcounter{secnumdepth}{-1}\n',
+  }],
+])
+
+samples['booktabs'] = ({'table_style': ['booktabs']}, [
+# column width determined by LaTeX
+["""\
+.. table::
+   :widths: auto
+
+   +-----+-+
+   |  1  |2|
+   +-----+-+
+""",
+ {'body': r"""
+\begin{longtable*}{ll}
+\toprule
+1 & 2 \\
+\bottomrule
+\end{longtable*}
+""",
+  'requirements': '\\usepackage[T1]{fontenc}\n'
+                  '\\usepackage{booktabs}\n'
+                  + REQUIREMENTS_TABLE
+  }],
+# column widhts specified via option
+["""\
+.. table::
+   :widths: 15, 30
+
+   +-----+-----+
+   |  1  |  2  |
+   +-----+-----+
+""",
+ {'body': """
+\\setlength{\\DUtablewidth}{\\linewidth}%
+\\begin{longtable*}{p{0.191\\DUtablewidth}p{0.365\\DUtablewidth}}
+\\toprule
+
+1
+ & \n\
+2
+ \\\\
+\\bottomrule
+\\end{longtable*}
+""",
+  'requirements': '\\usepackage[T1]{fontenc}\n'
+                  '\\usepackage{booktabs}\n'
+                  + REQUIREMENTS_TABLE
+  }],
+# borderless overrides "booktabs" table_style
+["""\
+.. table::
+   :class: borderless
+
+   +-----+-----+
+   |  1  |  2  |
+   +-----+-----+
+   |  3  |  4  |
+   +-----+-----+
+""",
+ {'body': """
+\\setlength{\\DUtablewidth}{\\linewidth}%
+\\begin{longtable*}{p{0.075\\DUtablewidth}p{0.075\\DUtablewidth}}
+
+1
+ & \n\
+2
+ \\\\
+
+3
+ & \n\
+4
+ \\\\
+\\end{longtable*}
+""",
+  'requirements': '\\usepackage[T1]{fontenc}\n'
+                  + REQUIREMENTS_TABLE
+  }],
+
+])
+
+
+samples['colwidths_auto'] = ({'table_style': ['colwidths-auto']}, [
+# borderless table with auto-width columns
+["""\
+.. table::
+   :class: borderless
+
+   +-----+-----+
+   |  1  |  2  |
+   +-----+-----+
+   |  3  |  4  |
+   +-----+-----+
+""",
+ {'body': r"""
+\begin{longtable*}{ll}
+1 & 2 \\
+3 & 4 \\
+\end{longtable*}
+""",
+  'requirements': '\\usepackage[T1]{fontenc}\n'
+                  + REQUIREMENTS_TABLE
+  }],
+# booktabs style table with auto-width columns
+["""\
+.. table::
+   :class: booktabs
+
+   +-----+-+
+   |  1  |2|
+   +-----+-+
+""",
+ {'body': r"""
+\begin{longtable*}{ll}
+\toprule
+1 & 2 \\
+\bottomrule
+\end{longtable*}
+""",
+  'requirements': '\\usepackage[T1]{fontenc}\n'
+                  '\\usepackage{booktabs}\n'
+                  + REQUIREMENTS_TABLE
+  }],
+# given width overrides "colwidth-auto"
+["""\
+.. table::
+   :widths: 15, 30
+
+   +-----+-----+
+   |  1  |  2  |
+   +-----+-----+
+""",
+ {'body': """
+\\setlength{\\DUtablewidth}{\\linewidth}%
+\\begin{longtable*}{|p{0.191\\DUtablewidth}|p{0.365\\DUtablewidth}|}
+\\hline
+
+1
+ & \n\
+2
+ \\\\
+\\hline
+\\end{longtable*}
+""",
+  'requirements': '\\usepackage[T1]{fontenc}\n'
+                  + REQUIREMENTS_TABLE
+  }],
+])
+
+samples['Docutils ToC and sectnum'] = ({'use_latex_toc': False}, [
+["""\
+.. contents:: Table of Contents
+
+Title 1
+=======
+Paragraph 1.
+
+Title 2
+-------
+Paragraph 2.
+""",
+ {'body': r"""
+\phantomsection\label{table-of-contents}
+\pdfbookmark[1]{Table of Contents}{table-of-contents}
+
+\begin{DUclass}{contents}
+
+\DUtitle{Table of Contents}
+
+\begin{itemize}
+\item \hyperref[title-1]{Title 1}
+
+\begin{itemize}
+\item \hyperref[title-2]{Title 2}
+\end{itemize}
+\end{itemize}
+\end{DUclass}
+
+
+\section{Title 1%
+  \label{title-1}%
+}
+
+Paragraph 1.
+
+
+\subsection{Title 2%
+  \label{title-2}%
+}
+
+Paragraph 2.
+""",
+  'fallbacks': r"""
+% class handling for environments (block-level elements)
+% \begin{DUclass}{spam} tries \DUCLASSspam and
+% \end{DUclass}{spam} tries \endDUCLASSspam
+\ifdefined\DUclass
+\else % poor man's "provideenvironment"
+  \newenvironment{DUclass}[1]%
+    {% "#1" does not work in end-part of environment.
+     \def\DocutilsClassFunctionName{DUCLASS#1}
+     \csname \DocutilsClassFunctionName \endcsname}%
+    {\csname end\DocutilsClassFunctionName \endcsname}%
+\fi
+
+% title for topics, admonitions, unsupported section levels, and sidebar
+\providecommand*{\DUtitle}[1]{%
+  \smallskip\noindent\textbf{#1}\smallskip}
+
+\providecommand*{\DUCLASScontents}{%
+  \renewenvironment{itemize}%
+    {\begin{list}{}{\setlength{\partopsep}{0pt}
+                    \setlength{\parsep}{0pt}}
+                   }%
+    {\end{list}}%
+}
+""",
+  'requirements': '\\usepackage[T1]{fontenc}\n'
+                  '\\setcounter{secnumdepth}{0}\n',
+  }],
+])
+
+
+samples['embed_stylesheet'] = ({'stylesheet_path': f'{spam},{ham}',
+                                'embed_stylesheet': True,
+                                'warning_stream': ''}, [
+['two stylesheets embedded in the header',
+ {'body': '\ntwo stylesheets embedded in the header\n',
+  'stylesheet': f"""\
+% Cannot embed stylesheet:
+%  [Errno 2] No such file or directory: {spampath}
+% embedded stylesheet: {ham}
+\\newcommand{{\\ham}}{{wonderful ham}}
+
+""",
+  }],
+])
+
+
+samples['sectnum_xform False'] = ({'sectnum_xform': False}, [
+# no section numbers
+["""\
+some text
+
+first section
+-------------
+""",
+ {'body': r"""
+some text
+
+
+\section{first section%
+  \label{first-section}%
+}
+""",
+  'requirements': '\\usepackage[T1]{fontenc}\n'
+                  '\\setcounter{secnumdepth}{0}\n',
+  }],
+])
+
+samples['stylesheet_path'] = ({'stylesheet_path': f'{spam},{ham}'}, [
+['two stylesheet links in the header',
+ {'body': '\ntwo stylesheet links in the header\n',
+  'stylesheet': f'\\usepackage{{{spam}}}\n'
+                f'\\input{{{ham}}}\n'
+  }],
+])
+
+# if "svg" package is listed, include SVG images with "\includesvg"
+samples['svg-image'] = ({'stylesheet': 'svg'}, [
+["""\
+.. image:: blue%20square.png
+.. image:: vectors.svg
+""",
+ {'body': r"""
+\includegraphics{blue square.png}
+
+\includesvg{vectors.svg}
+""",
+  'requirements': '\\usepackage[T1]{fontenc}\n'
+                  '\\usepackage{graphicx}\n',
+  'stylesheet': '\\usepackage{svg}\n'
+  }],
+])
+
+# TODO: test for quote replacing if the language uses "ASCII-quotes"
+# as active character (e.g. de (ngerman)).
+
+
 if __name__ == '__main__':
     unittest.main()

This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site.



_______________________________________________
Docutils-checkins mailing list
[email protected]
https://lists.sourceforge.net/lists/listinfo/docutils-checkins
lmpx.com only provides a reader for public news (NNTP) servers. It is not affiliated with the servers or forums shown here and is not responsible for the content of articles, which is written by their respective authors.