SF.net SVN: docutils:[9932] trunk/docutils

milde--- via Docutils-checkins <[email protected]>
Newsgroups gmane.text.docutils.cvs
Message-ID <[email protected]>
Revision: 9932
          http://sourceforge.net/p/docutils/code/9932
Author:   milde
Date:     2024-09-19 10:20:44 +0000 (Thu, 19 Sep 2024)
Log Message:
-----------
Revise/fix handling of length specifications.

Don't use the generic number-formatter "g" to drop trailing zeroes
in `nodes.validate_measure()` to avoid writing large values in
scientific format (which is not supported in reStructuredText).
Instead, `nodes.parse_measure()` returns an `int` if possible so that
converting to `str` does not add a trailing zero in the first place.

Refactor `writers._html_base.HTMLTranslator.image_size()`.
(Using the "g"-formatter in the HTML writer is no problem, as the
scientific format is valid in CSS and "style" attribute values.)

Simplify `writers.latex2e.LaTeXTranslator.to_latex_length()`:
Use `nodes.parse_measure()`.
Move XeTeX-specific code to the "XeTeX" writer.
Drop trailing zeroes.
(Using "g" with percentage values is considered safe, as a "width"
value larger than 10 000 000 % leads to output problems anyway.)

Modified Paths:
--------------
    trunk/docutils/HISTORY.rst
    trunk/docutils/RELEASE-NOTES.rst
    trunk/docutils/docutils/nodes.py
    trunk/docutils/docutils/writers/_html_base.py
    trunk/docutils/docutils/writers/latex2e/__init__.py
    trunk/docutils/docutils/writers/xetex/__init__.py
    trunk/docutils/test/functional/expected/latex_cornercases.tex
    trunk/docutils/test/functional/expected/latex_memoir.tex
    trunk/docutils/test/functional/expected/standalone_rst_latex.tex
    trunk/docutils/test/functional/expected/standalone_rst_xetex.tex

Added Paths:
-----------
    trunk/docutils/test/test_writers/test_xetex_misc.py

Modified: trunk/docutils/HISTORY.rst
===================================================================
--- trunk/docutils/HISTORY.rst	2024-09-15 11:13:43 UTC (rev 9931)
+++ trunk/docutils/HISTORY.rst	2024-09-19 10:20:44 UTC (rev 9932)
@@ -156,8 +156,10 @@
 
 * docutils/writers/latex2e/__init__.py
 
-  - Remove optional argument `pxunit` of `LaTeXTranslator.to_latex_length()`
-    (ignored since at least 2012).
+  - `LaTeXTranslator.to_latex_length()`:
+    remove optional argument `pxunit` (ignored since at least 2012),
+    drop trailing zeroes from length values,
+    move XeTeX-specific code to the "xetex" writer.
   - Don't wrap references with custom reference-label_ in
     a ``\hyperref`` command.
   - Provide an "unknown_references_resolver" (cf. `docutils/TransformSpec`)
@@ -179,7 +181,7 @@
 
   - `null.Writer.translate()` sets `self.output` to the empty string.
 
-* tools/rst2odt_prepstyles.py
+* tools/rst2odt.py
 
   - Use `core.publish_file()` instead of `core.publish_file_to_binary()`.
 

Modified: trunk/docutils/RELEASE-NOTES.rst
===================================================================
--- trunk/docutils/RELEASE-NOTES.rst	2024-09-15 11:13:43 UTC (rev 9931)
+++ trunk/docutils/RELEASE-NOTES.rst	2024-09-19 10:20:44 UTC (rev 9932)
@@ -102,6 +102,11 @@
   - Remove ``use_verbatim_when_possible`` setting
     (use literal_block_env_: verbatim) in Docutils 2.0.
 
+  - The `default length unit`__ will change from "bp" (DTP point)
+    to "px" (pixel unit) in Docutils 1.0.
+
+    __ docs/user/latex.html#length-units
+
 Misc
 ----
 

Modified: trunk/docutils/docutils/nodes.py
===================================================================
--- trunk/docutils/docutils/nodes.py	2024-09-15 11:13:43 UTC (rev 9931)
+++ trunk/docutils/docutils/nodes.py	2024-09-19 10:20:44 UTC (rev 9932)
@@ -37,6 +37,7 @@
 # import docutils.transforms # -> conditional import in document.__init__()
 
 if TYPE_CHECKING:
+    import numbers
     from collections.abc import (Callable, Iterable, Iterator,
                                  Mapping, Sequence)
     from types import ModuleType
@@ -3074,7 +3075,7 @@
     return '"%s"' % value
 
 
-def parse_measure(measure: str) -> tuple[float, str]:
+def parse_measure(measure: str) -> tuple[numbers.Rational, str]:
     """Parse a measure__, return value + optional unit.
 
     __ https://docutils.sourceforge.io/docs/ref/doctree.html#measure
@@ -3083,7 +3084,10 @@
     """
     match = re.fullmatch('(-?[0-9.]+) *([a-zA-Zµ]*|%?)', measure)
     try:
-        value = float(match.group(1))
+        try:
+            value = int(match.group(1))
+        except ValueError:
+            value = float(match.group(1))
         unit = match.group(2)
     except (AttributeError, ValueError):
         raise ValueError(f'"{measure}" is no valid measure.')
@@ -3168,7 +3172,7 @@
     __ https://docutils.sourceforge.io/docs/ref/doctree.html#measure
     """
     value, unit = parse_measure(measure)
-    return f'{value:g}{unit}'
+    return f'{value}{unit}'
 
 
 def validate_NMTOKEN(value: str) -> str:

Modified: trunk/docutils/docutils/writers/_html_base.py
===================================================================
--- trunk/docutils/docutils/writers/_html_base.py	2024-09-15 11:13:43 UTC (rev 9931)
+++ trunk/docutils/docutils/writers/_html_base.py	2024-09-19 10:20:44 UTC (rev 9932)
@@ -40,9 +40,8 @@
 from docutils.utils.math import (latex2mathml, math2html, tex2mathml_extern,
                                  unichar2tex, wrap_math_code, MathError)
 
-
 if TYPE_CHECKING:
-    from numbers import Real
+    from docutils.transforms import Transform
 
 
 class Writer(writers.Writer):
@@ -157,7 +156,7 @@
         'html_prolog', 'html_head', 'html_title', 'html_subtitle',
         'html_body')
 
-    def get_transforms(self):
+    def get_transforms(self) -> list[type[Transform]]:
         return super().get_transforms() + [writer_aux.Admonitions]
 
     def translate(self) -> None:
@@ -167,11 +166,10 @@
             setattr(self, attr, getattr(visitor, attr))
         self.output = self.apply_template()
 
-    def apply_template(self):
+    def apply_template(self) -> str:
         template_path = Path(self.document.settings.template)
         template = template_path.read_text(encoding='utf-8')
-        subs = self.interpolation_dict()
-        return template % subs
+        return template % self.interpolation_dict()
 
     def interpolation_dict(self):
         subs = {}
@@ -416,36 +414,31 @@
     def image_size(self, node: nodes.image) -> dict[str, str]:
         """Determine the image size from node arguments or the image file.
 
+        Return as dictionary of <img> attributes,
+        e.g., ``{height': '32', 'style': 'width: 4 em;'}``.
+
         Auxiliary method called from `self.visit_image()`.
-
         Provisional.
         """
-        # List with optional width and height measures ((value, unit)-tuples)
-        measures: list[tuple[Real, str] | None] = [None, None]
         dimensions = ('width', 'height')
-        for i, dimension in enumerate(dimensions):
+        measures = {}  # (value, unit)-tuples) for width and height
+        for dimension in dimensions:
             if dimension in node:
-                measures[i] = nodes.parse_measure(node[dimension])
-        if None in measures and 'scale' in node:
+                measures[dimension] = nodes.parse_measure(node[dimension])
+        if 'scale' in node and len(measures) < 2:
             # supplement with (unitless) values read from image file
             imgsize = self.read_size_with_PIL(node)
             if imgsize:
-                measures = [measure or (imgvalue, '')
-                            for measure, imgvalue in zip(measures, imgsize)]
-        # scale values
-        factor = node.get('scale', 100) / 100  # scaling factor
-        if factor != 1:
-            measures = [(measure[0] * factor, measure[1])
-                        for measure in measures if measure]
-        # format as <img> attributes,
-        # use "width" and "hight" for unitless values and "style" else,
-        # e.g., height': '32' 'style': 'width: 4 em;'}``:
-        size_atts = {}  # attributes "width", "height", or "style"
+                for dimension, value in zip(dimensions, imgsize):
+                    if dimension not in measures:
+                        measures[dimension] = (value, '')
+        # Scale and format as <img> attributes,
+        # use "width" and "hight" for unitless values and "style" else:
+        scaling_factor = node.get('scale', 100) / 100
+        size_atts = {}
         declarations = []  # declarations for the "style" attribute
-        for dimension, measure in zip(dimensions, measures):
-            if measure is None:
-                continue
-            value, unit = measure
+        for dimension, (value, unit) in measures.items():
+            value *= scaling_factor
             if unit:
                 declarations.append(f'{dimension}: {value:g}{unit};')
             else:

Modified: trunk/docutils/docutils/writers/latex2e/__init__.py
===================================================================
--- trunk/docutils/docutils/writers/latex2e/__init__.py	2024-09-15 11:13:43 UTC (rev 9931)
+++ trunk/docutils/docutils/writers/latex2e/__init__.py	2024-09-19 10:20:44 UTC (rev 9932)
@@ -2390,27 +2390,17 @@
         self.requirements['~header'] = ''.join(self.out)
         self.pop_output_collector()
 
-    def to_latex_length(self, length_str):
-        """Convert `length_str` with rst length to LaTeX length
+    def to_latex_length(self, length_str: str) -> str:
+        """Convert "measure" `length_str` to LaTeX length specification.
+
+        Note: the default length unit will change from "bp"
+        (Postscript point) to "px" in Docutils 1.0.
         """
-        match = re.match(r'(\d*\.?\d*)\s*(\S*)', length_str)
-        if not match:
-            return length_str
-        value, unit = match.groups()[:2]
-        # no unit or "DTP" points (called 'bp' in TeX):
-        if unit in ('', 'pt'):
-            length_str = '%sbp' % value
-        # percentage: relate to current line width
-        elif unit == '%':
-            length_str = '%.3f\\linewidth' % (float(value)/100.0)
-        elif self.is_xetex and unit == 'px':
-            # XeTeX does not know the length unit px.
-            # Use \pdfpxdimen, the macro to set the value of 1 px in pdftex.
-            # This way, configuring works the same for pdftex and xetex.
-            if not self.fallback_stylesheet:
-                self.fallbacks['_providelength'] = PreambleCmds.providelength
-            self.fallbacks['px'] = '\n\\DUprovidelength{\\pdfpxdimen}{1bp}\n'
-            length_str = r'%s\pdfpxdimen' % value
+        value, unit = nodes.parse_measure(length_str)
+        if unit in ('', 'pt'):  # no unit or "Postscript points"
+            return f'{value}bp'  # LaTeX uses symbol "bp"
+        if unit == '%':  # percentage: relate to current line width
+            return f'{value/100:g}\\linewidth'
         return length_str
 
     def visit_image(self, node) -> None:

Modified: trunk/docutils/docutils/writers/xetex/__init__.py
===================================================================
--- trunk/docutils/docutils/writers/xetex/__init__.py	2024-09-15 11:13:43 UTC (rev 9931)
+++ trunk/docutils/docutils/writers/xetex/__init__.py	2024-09-19 10:20:44 UTC (rev 9932)
@@ -24,6 +24,7 @@
 
 from docutils import frontend
 from docutils.writers import latex2e
+from docutils.writers.latex2e import PreambleCmds
 
 
 class Writer(latex2e.Writer):
@@ -145,3 +146,18 @@
         else:
             self.requirements['_inputenc'] = (r'\XeTeXinputencoding %s '
                                               % self.latex_encoding)
+
+    def to_latex_length(self, length_str: str) -> str:
+        """Convert "measure" `length_str` to LaTeX length specification.
+
+        XeTeX does not know the length unit px.
+        Use ``\\pdfpxdimen``, the macro holding the value of 1 px in pdfTeX.
+        This way, configuring works the same for pdftex and xetex.
+        """
+        length_str = super().to_latex_length(length_str)
+        if length_str.endswith('px'):
+            if not self.fallback_stylesheet:
+                self.fallbacks['_providelength'] = PreambleCmds.providelength
+            self.fallbacks['px'] = '\n\\DUprovidelength{\\pdfpxdimen}{1bp}\n'
+            return length_str.replace('px', '\\pdfpxdimen')
+        return length_str

Modified: trunk/docutils/test/functional/expected/latex_cornercases.tex
===================================================================
--- trunk/docutils/test/functional/expected/latex_cornercases.tex	2024-09-15 11:13:43 UTC (rev 9931)
+++ trunk/docutils/test/functional/expected/latex_cornercases.tex	2024-09-19 10:20:44 UTC (rev 9932)
@@ -305,11 +305,11 @@
 
 Image with 20\% width:
 
-\includegraphics[width=0.200\linewidth]{../../../docs/user/rst/images/title.png}
+\includegraphics[width=0.2\linewidth]{../../../docs/user/rst/images/title.png}
 
 Image with 100\% width:
 
-\includegraphics[width=1.000\linewidth]{../../../docs/user/rst/images/title.png}
+\includegraphics[width=1\linewidth]{../../../docs/user/rst/images/title.png}
 
 
 \section{Tables%
@@ -548,7 +548,7 @@
 The \DUroletitlereference{width} option overrides \textquotedbl{}auto\textquotedbl{} \DUroletitlereference{widths} as standard LaTeX tables
 don't have a global width setting:
 
-\setlength{\DUtablewidth}{\dimexpr0.600\linewidth-5\arrayrulewidth\relax}%
+\setlength{\DUtablewidth}{\dimexpr0.6\linewidth-5\arrayrulewidth\relax}%
 \begin{longtable}{|p{\DUcolumnwidth{0.400}}|p{\DUcolumnwidth{0.200}}|p{\DUcolumnwidth{0.200}}|p{\DUcolumnwidth{0.200}}|}
 \caption{This table has \DUroletitlereference{widths} \textquotedbl{}auto\textquotedbl{} (ignored) and \DUroletitlereference{width} 60\%.}\\
 \hline

Modified: trunk/docutils/test/functional/expected/latex_memoir.tex
===================================================================
--- trunk/docutils/test/functional/expected/latex_memoir.tex	2024-09-15 11:13:43 UTC (rev 9931)
+++ trunk/docutils/test/functional/expected/latex_memoir.tex	2024-09-19 10:20:44 UTC (rev 9932)
@@ -816,7 +816,7 @@
 
 An image directive (also clickable -{}- a hyperlink reference):
 
-\hyperref[directives]{\includegraphics[width=0.700\linewidth]{../../../docs/user/rst/images/title.png}}
+\hyperref[directives]{\includegraphics[width=0.7\linewidth]{../../../docs/user/rst/images/title.png}}
 
 Image with multiple IDs:
 
@@ -858,7 +858,7 @@
 Relative units allow adaption of the image to the screen or paper size.
 An image occupying 50\% of the line width:
 
-\includegraphics[width=0.500\linewidth]{../../../docs/user/rst/images/title.png}
+\includegraphics[width=0.5\linewidth]{../../../docs/user/rst/images/title.png}
 
 A \emph{figure} is an image with a caption and/or a legend.  With page-based output
 media, figures might float to a different position if this helps the page
@@ -1670,7 +1670,7 @@
 Here's a list table exercising all features:
 
 \begin{DUclass}{test}
-\setlength{\DUtablewidth}{0.950\linewidth}%
+\setlength{\DUtablewidth}{0.95\linewidth}%
 \begin{longtable}{|p{0.133\DUtablewidth}|p{0.110\DUtablewidth}|p{0.249\DUtablewidth}|}
 \caption{list table with integral header}\\
 \hline
@@ -1832,7 +1832,7 @@
 
 \begin{description}
 \item[{Math-Accents:}] \leavevmode
-\setlength{\DUtablewidth}{1.000\linewidth}%
+\setlength{\DUtablewidth}{1\linewidth}%
 \begin{longtable*}{p{0.315\DUtablewidth}p{0.315\DUtablewidth}p{0.315\DUtablewidth}}
 
 $\acute{a}$      \texttt{\textbackslash{}acute\{a\}}

Modified: trunk/docutils/test/functional/expected/standalone_rst_latex.tex
===================================================================
--- trunk/docutils/test/functional/expected/standalone_rst_latex.tex	2024-09-15 11:13:43 UTC (rev 9931)
+++ trunk/docutils/test/functional/expected/standalone_rst_latex.tex	2024-09-19 10:20:44 UTC (rev 9932)
@@ -817,7 +817,7 @@
 
 An image directive (also clickable – a hyperlink reference):
 
-\hyperref[directives]{\includegraphics[width=0.700\linewidth]{../../../docs/user/rst/images/title.png}}
+\hyperref[directives]{\includegraphics[width=0.7\linewidth]{../../../docs/user/rst/images/title.png}}
 
 Image with multiple IDs:
 
@@ -859,7 +859,7 @@
 Relative units allow adaption of the image to the screen or paper size.
 An image occupying 50\% of the line width:
 
-\includegraphics[width=0.500\linewidth]{../../../docs/user/rst/images/title.png}
+\includegraphics[width=0.5\linewidth]{../../../docs/user/rst/images/title.png}
 
 A \emph{figure} is an image with a caption and/or a legend.  With page-based output
 media, figures might float to a different position if this helps the page
@@ -1692,7 +1692,7 @@
 Here’s a list table exercising all features:
 
 \begin{DUclass}{test}
-\setlength{\DUtablewidth}{0.950\linewidth}%
+\setlength{\DUtablewidth}{0.95\linewidth}%
 \begin{longtable}{|p{0.133\DUtablewidth}|p{0.110\DUtablewidth}|p{0.249\DUtablewidth}|}
 \caption{list table with integral header}\\
 \hline
@@ -1854,7 +1854,7 @@
 
 \begin{description}
 \item[{Math-Accents:}] \leavevmode
-\setlength{\DUtablewidth}{1.000\linewidth}%
+\setlength{\DUtablewidth}{1\linewidth}%
 \begin{longtable*}{p{0.315\DUtablewidth}p{0.315\DUtablewidth}p{0.315\DUtablewidth}}
 
 $\acute{a}$      \texttt{\textbackslash{}acute\{a\}}

Modified: trunk/docutils/test/functional/expected/standalone_rst_xetex.tex
===================================================================
--- trunk/docutils/test/functional/expected/standalone_rst_xetex.tex	2024-09-15 11:13:43 UTC (rev 9931)
+++ trunk/docutils/test/functional/expected/standalone_rst_xetex.tex	2024-09-19 10:20:44 UTC (rev 9932)
@@ -852,7 +852,7 @@
 
 An image directive (also clickable – a hyperlink reference):
 
-\hyperref[directives]{\includegraphics[width=0.700\linewidth]{../../../docs/user/rst/images/title.png}}
+\hyperref[directives]{\includegraphics[width=0.7\linewidth]{../../../docs/user/rst/images/title.png}}
 
 Image with multiple IDs:
 
@@ -894,7 +894,7 @@
 Relative units allow adaption of the image to the screen or paper size.
 An image occupying 50\% of the line width:
 
-\includegraphics[width=0.500\linewidth]{../../../docs/user/rst/images/title.png}
+\includegraphics[width=0.5\linewidth]{../../../docs/user/rst/images/title.png}
 
 A \emph{figure} is an image with a caption and/or a legend.  With page-based output
 media, figures might float to a different position if this helps the page
@@ -1731,7 +1731,7 @@
 Here’s a list table exercising all features:
 
 \begin{DUclass}{test}
-\setlength{\DUtablewidth}{0.950\linewidth}%
+\setlength{\DUtablewidth}{0.95\linewidth}%
 \begin{longtable}{|p{0.133\DUtablewidth}|p{0.110\DUtablewidth}|p{0.249\DUtablewidth}|}
 \caption{list table with integral header}\\
 \hline
@@ -1893,7 +1893,7 @@
 
 \begin{description}
 \item[{Math-Accents:}] \leavevmode
-\setlength{\DUtablewidth}{1.000\linewidth}%
+\setlength{\DUtablewidth}{1\linewidth}%
 \begin{longtable*}{p{0.315\DUtablewidth}p{0.315\DUtablewidth}p{0.315\DUtablewidth}}
 
 $\acute{a}$      \texttt{\textbackslash{}acute\{a\}}

Added: trunk/docutils/test/test_writers/test_xetex_misc.py
===================================================================
--- trunk/docutils/test/test_writers/test_xetex_misc.py	                        (rev 0)
+++ trunk/docutils/test/test_writers/test_xetex_misc.py	2024-09-19 10:20:44 UTC (rev 9932)
@@ -0,0 +1,78 @@
+#! /usr/bin/env python3
+# $Id$
+# Author: Günter Milde
+# Maintainer: [email protected]
+# :Copyright: 2024 Günter Milde,
+# :License: Released under the terms of the `2-Clause BSD license`_, in short:
+#
+#    Copying and distribution of this file, with or without modification,
+#    are permitted in any medium without royalty provided the copyright
+#    notice and this notice are preserved.
+#    This file is offered as-is, without any warranty.
+#
+# .. _2-Clause BSD license: https://opensource.org/licenses/BSD-2-Clause
+
+"""
+Miscellaneous XeTeX/LuaTeX writer tests.
+"""
+
+from pathlib import Path
+import sys
+import unittest
+
+
+if __name__ == '__main__':
+    # prepend the "docutils root" to the Python library path
+    # so we import the local `docutils` package.
+    sys.path.insert(0, str(Path(__file__).resolve().parents[2]))
+
+from docutils import core
+from docutils.writers import xetex
+
+# TEST_ROOT is ./test/ from the docutils root
+TEST_ROOT = Path(__file__).parents[1]
+DATA_ROOT = TEST_ROOT / 'data'
+
+px_sample = """\
+.. image:: foo.pdf
+   :width: 250 px
+   :height: 50pt
+"""
+
+px_body = r"""
+\includegraphics[height=50bp,width=250\pdfpxdimen]{foo.pdf}
+"""
+
+px_fallback = r"""
+% Provide a length variable and set default, if it is new
+\providecommand*{\DUprovidelength}[2]{
+  \ifthenelse{\isundefined{#1}}{\newlength{#1}\setlength{#1}{#2}}{}
+}
+
+\DUprovidelength{\pdfpxdimen}{1bp}
+
+
+"""
+
+
+class PublishTestCase(unittest.TestCase):
+    maxDiff = None
+
+    settings = {'_disable_config': True,
+                # avoid latex writer future warnings:
+                'use_latex_citations': False,
+                'legacy_column_widths': False,
+                }
+
+    def test_px_workaround(self):
+        """Check the workaround for length unit 'px' missing in XeTeX.
+        """
+        parts = core.publish_parts(px_sample,
+                                   writer=xetex.Writer(),
+                                   settings_overrides=self.settings)
+        self.assertEqual(px_body, parts['body'])
+        self.assertEqual(px_fallback, parts['fallbacks'])
+
+
+if __name__ == '__main__':
+    unittest.main()


Property changes on: trunk/docutils/test/test_writers/test_xetex_misc.py
___________________________________________________________________
Added: svn:eol-style
## -0,0 +1 ##
+native
\ No newline at end of property
Added: svn:keywords
## -0,0 +1 ##
+Author Date Id Revision
\ No newline at end of property
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.