From: <mi...@us...> - 2024-09-11 07:20:10
|
Revision: 9928 http://sourceforge.net/p/docutils/code/9928 Author: milde Date: 2024-09-11 07:20:07 +0000 (Wed, 11 Sep 2024) Log Message: ----------- HTML5: use "width" and "height" for unitless image size values. Fixes [feature-requests:#102] The HTML `<img>` element supports "width" and "height" attributes to specify the size or ratio of an image. Values are restricted to integers (size in pixels). In Docutils, "width" and "hight" attributes also support a set of CSS compatible units. Therefore, all size values were passed to HTML via the common attribute "style". Using HTML "width" and "hight" attributes for values without unit provides a means to specify an image's *aspect ratio* without overriding a size declaration in a CSS style sheet. This change is not backported to the "html4css1" writer to keep the output backwards compatible. The provisional method `HTMLTranslator.image_size()` now returns a dictionary of attribute values. Modified Paths: -------------- trunk/docutils/HISTORY.rst trunk/docutils/RELEASE-NOTES.rst trunk/docutils/docs/user/html.rst trunk/docutils/docutils/writers/_html_base.py trunk/docutils/test/functional/expected/rst_html5_tuftig.html trunk/docutils/test/functional/expected/standalone_rst_html5.html trunk/docutils/test/test_writers/test_html5_polyglot.py trunk/docutils/test/test_writers/test_html5_polyglot_misc.py Modified: trunk/docutils/HISTORY.rst =================================================================== --- trunk/docutils/HISTORY.rst 2024-09-11 07:19:52 UTC (rev 9927) +++ trunk/docutils/HISTORY.rst 2024-09-11 07:20:07 UTC (rev 9928) @@ -145,6 +145,8 @@ * docutils/writers/_html_base.py - Make MathML the default math_output_. + - Revise image size handling methods, + use "width" and "height" attributes for unitless values. * docutils/writers/html4css1/__init__.py Modified: trunk/docutils/RELEASE-NOTES.rst =================================================================== --- trunk/docutils/RELEASE-NOTES.rst 2024-09-11 07:19:52 UTC (rev 9927) +++ trunk/docutils/RELEASE-NOTES.rst 2024-09-11 07:20:07 UTC (rev 9928) @@ -85,20 +85,6 @@ __ https://www.w3.org/TR/2014/REC-html5-20141028/grouping-content.html #the-blockquote-element - - Unitless image_ :width: and :hight: values and dimensions - read from the image due to a :scale: option will be written as - "width" and "hight" attributes instead of "style" rules to allow - specification of the aspect ratio to `prevent jank when loading - images`__ without overwriting an image size set in a CSS stylesheet - in Docutils 0.22 (cf. `feature-requests:102`__). - The current behaviour is kept for dimensions with units, so - users may specify, e.g. ``:width: 50px`` instead of ``:width: 50`` - to override CSS stylesheet rules. - - __ https://developer.mozilla.org/en-US/docs/Learn/Performance/Multimedia - #rendering_strategy_preventing_jank_when_loading_images - __ https://sourceforge.net/p/docutils/feature-requests/102/ - - Change the default value of the initial_header_level_ setting to None (<h2> if there is a document title, else <h1>) in Docutils 1.0. @@ -202,22 +188,30 @@ (See `command line interface`_ for the rationale.) Output changes - "manpage" writer: + LaTeX: + Don't wrap references with custom reference-label_ in a ``\hyperref`` + command. The "hyperref" package generates hyperlinks for labels by + default, so there is no change in the PDF (except for "ref*"). + + .. _reference-label: docs/user/config.html#reference-label + + HTML5: + Unitless image_ size measures__ are written as <img> "width" and + "hight" values instead of "style" rules. The current behaviour + is kept for values with units, so users may specify, e.g. ``:width: + 50px`` instead of ``:width: 50`` to override CSS stylesheet rules. + + __ docs/ref/doctree.html#measure + + manpage: Don't UPPERCASE section headings. - "null" writer: - output changed from None to the empty string. + null: + The "null" writer output changed from None to the empty string. `publish_string()` now returns a `bytes` or `str` instance for all writers (as documented). - "latex" writer: - Don't wrap references with custom reference-label_ in a ``\hyperref`` - command. The "hyperref" package generates hyperlinks for labels by - default, so there is no change in the PDF (except for "ref*"). - - .. _reference-label: docs/user/config.html#reference-label - New objects `parsers.docutils_xml` parser for `Docutils XML`_ (e.g., the output of the "xml" writer). Modified: trunk/docutils/docs/user/html.rst =================================================================== --- trunk/docutils/docs/user/html.rst 2024-09-11 07:19:52 UTC (rev 9927) +++ trunk/docutils/docs/user/html.rst 2024-09-11 07:20:07 UTC (rev 9928) @@ -52,6 +52,13 @@ better legibility. Adaption of the layout is possible with `custom style sheets`_. [#safetext]_ +Image_ size values with unit are converted to "style" rules, +values without unit are rounded to the nearest integer and +written as "width" and "height" attributes instead. +This allows the specification of the image's aspect ratio +(to `prevent jank when loading images`__) without overwriting +size declarations in a CSS stylesheet. + .. [#safetext] The validity of raw HTML and custom stylesheets must be ensured by the author. @@ -69,6 +76,9 @@ .. _custom style sheets: ../howto/html-stylesheets.html .. _viewable with any browser: http://www.anybrowser.org/campaign .. _Benefits of polyglot XHTML5: http://xmlplease.com/xhtml/xhtml5polyglot/ +.. _image: ../ref/rst/directives.html#image +__ https://developer.mozilla.org/en-US/docs/Learn/Performance/Multimedia + #rendering_strategy_preventing_jank_when_loading_images html4css1 Modified: trunk/docutils/docutils/writers/_html_base.py =================================================================== --- trunk/docutils/docutils/writers/_html_base.py 2024-09-11 07:19:52 UTC (rev 9927) +++ trunk/docutils/docutils/writers/_html_base.py 2024-09-11 07:20:07 UTC (rev 9928) @@ -413,7 +413,7 @@ text = str(text) return text.translate(self.special_characters) - def image_size(self, node: nodes.image) -> str: + def image_size(self, node: nodes.image) -> dict[str, str]: """Determine the image size from node arguments or the image file. Auxiliary method called from `self.visit_image()`. @@ -420,9 +420,6 @@ Provisional. """ - # TODO: Use "width" and "hight" for unitless integers? - # [feature-requests:#102] - # List with optional width and height measures ((value, unit)-tuples) measures: list[tuple[Real, str] | None] = [None, None] dimensions = ('width', 'height') @@ -440,10 +437,22 @@ if factor != 1: measures = [(measure[0] * factor, measure[1]) for measure in measures if measure] - # format as CSS declarations and return - return ' '.join(f'{dimension}: {measure[0]:g}{measure[1] or "px"};' - for dimension, measure in zip(dimensions, 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" + declarations = [] # declarations for the "style" attribute + for dimension, measure in zip(dimensions, measures): + if measure is None: + continue + value, unit = measure + if unit: + declarations.append(f'{dimension}: {value:g}{unit};') + else: + size_atts[dimension] = f'{round(value)}' + if declarations: + size_atts['style'] = ' '.join(declarations) + return size_atts def read_size_with_PIL(self, node) -> tuple[int, int] | None: # Try reading size from image file. @@ -501,6 +510,9 @@ style_att = dict((k.strip(), v.strip()) for k, p, v in style_att) style_att = ' '.join(f'{k}: {v};' for k, v in style_att.items()) svg.set('style', style_att) + for dimension in ('width', 'height'): + if dimension in atts: + svg.set(dimension, atts[dimension]) if 'classes' in atts or node['classes']: classes = svg.get('class', '').split() classes += node['classes'] + atts.get('classes', []) @@ -1169,10 +1181,9 @@ alt = node.get('alt', uri) mimetype = mimetypes.guess_type(uri)[0] element = '' # the HTML element (including potential children) - atts = {} # attributes for the HTML tag - size_declaration = self.image_size(node) - if size_declaration: - atts['style'] = size_declaration + # attributes for the HTML tag: + atts = self.image_size(node) + # alignment is handled by CSS rules if 'align' in node: atts['classes'] = [f"align-{node['align']}"] # ``:loading:`` option (embed, link, lazy), default from setting, Modified: trunk/docutils/test/functional/expected/rst_html5_tuftig.html =================================================================== --- trunk/docutils/test/functional/expected/rst_html5_tuftig.html 2024-09-11 07:19:52 UTC (rev 9927) +++ trunk/docutils/test/functional/expected/rst_html5_tuftig.html 2024-09-11 07:20:07 UTC (rev 9928) @@ -137,7 +137,7 @@ </section> </main> <footer> -<p><a class="reference external image-reference" href="http://www.w3.org/TR/html5/"><img alt="Conforms to HTML 5" src="http://www.w3.org/html/logo/badge/html5-badge-h-css3-semantics.png" style="width: 88px; height: 31px;" /></a> <a class="reference external image-reference" href="http://validator.w3.org/check?uri=referer"><img alt="Check validity!" src="https://www.w3.org/Icons/ValidatorSuite/vs-blue-190.png" style="width: 88px; height: 31px;" /></a> <a class="reference external image-reference" href="http://jigsaw.w3.org/css-validator/check/referer"><img alt="Valid CSS 2.1!" src="http://jigsaw.w3.org/css-validator/images/vcss" style="width: 88px; height: 31px;" /></a></p> +<p><a class="reference external image-reference" href="http://www.w3.org/TR/html5/"><img alt="Conforms to HTML 5" height="31" src="http://www.w3.org/html/logo/badge/html5-badge-h-css3-semantics.png" width="88" /></a> <a class="reference external image-reference" href="http://validator.w3.org/check?uri=referer"><img alt="Check validity!" height="31" src="https://www.w3.org/Icons/ValidatorSuite/vs-blue-190.png" width="88" /></a> <a class="reference external image-reference" href="http://jigsaw.w3.org/css-validator/check/referer"><img alt="Valid CSS 2.1!" height="31" src="http://jigsaw.w3.org/css-validator/images/vcss" width="88" /></a></p> </footer> </body> </html> Modified: trunk/docutils/test/functional/expected/standalone_rst_html5.html =================================================================== --- trunk/docutils/test/functional/expected/standalone_rst_html5.html 2024-09-11 07:19:52 UTC (rev 9927) +++ trunk/docutils/test/functional/expected/standalone_rst_html5.html 2024-09-11 07:20:07 UTC (rev 9928) @@ -614,7 +614,7 @@ media, figures might float to a different position if this helps the page layout.</p> <figure class="figclass1 figclass2"> -<img alt="reStructuredText, the markup syntax" class="class1 class2" src="../../../docs/user/rst/images/title.png" style="width: 258px;" /> +<img alt="reStructuredText, the markup syntax" class="class1 class2" src="../../../docs/user/rst/images/title.png" width="258" /> <figcaption> <p>Plaintext markup syntax and parser system.</p> <div class="legend"> @@ -1302,7 +1302,7 @@ <li><img alt="blue square" class="align-right" src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAIAAAD8GO2jAAAALElEQVR4nO3NMQEAMAjAsDFjvIhHFCbgSwU0kdXvsn96BwAAAAAAAAAAAIsNnEwBk52VRuMAAAAASUVORK5CYII=" /> <p>Embed images or defer fetching images with the <a class="reference external" href="https://docutils.sourceforge.io/docs/user/config.html#image-loading">image-loading</a> <a class="brackets" href="#footnote-9" id="footnote-reference-22" role="doc-noteref"><span class="fn-bracket">[</span>9<span class="fn-bracket">]</span></a> configuration setting or the "loading" option of the "image" directive.</p> -<img alt="../../../docs/user/rst/images/biohazard.png" class="align-right" loading="lazy" src="../../../docs/user/rst/images/biohazard.png" style="width: 16px; height: 16px;" /> +<img alt="../../../docs/user/rst/images/biohazard.png" class="align-right" height="16" loading="lazy" src="../../../docs/user/rst/images/biohazard.png" width="16" /> <p>Especially with "lazy" loading, it is strongly recommended to specify both width and height of the image to prevent content layout shifts or use the "scale" option to let the writer insert the size @@ -1930,7 +1930,7 @@ </main> <footer> <p>Document footer</p> -<p><a class="reference external image-reference" href="http://www.w3.org/TR/html5/"><img alt="Conforms to HTML 5" src="http://www.w3.org/html/logo/badge/html5-badge-h-css3-semantics.png" style="width: 88px; height: 31px;" /></a> <a class="reference external image-reference" href="http://validator.w3.org/check?uri=referer"><img alt="Check validity!" src="https://www.w3.org/Icons/ValidatorSuite/vs-blue-190.png" style="width: 88px; height: 31px;" /></a> <a class="reference external image-reference" href="http://jigsaw.w3.org/css-validator/check/referer"><img alt="Valid CSS 2.1!" src="http://jigsaw.w3.org/css-validator/images/vcss" style="width: 88px; height: 31px;" /></a></p> +<p><a class="reference external image-reference" href="http://www.w3.org/TR/html5/"><img alt="Conforms to HTML 5" height="31" src="http://www.w3.org/html/logo/badge/html5-badge-h-css3-semantics.png" width="88" /></a> <a class="reference external image-reference" href="http://validator.w3.org/check?uri=referer"><img alt="Check validity!" height="31" src="https://www.w3.org/Icons/ValidatorSuite/vs-blue-190.png" width="88" /></a> <a class="reference external image-reference" href="http://jigsaw.w3.org/css-validator/check/referer"><img alt="Valid CSS 2.1!" height="31" src="http://jigsaw.w3.org/css-validator/images/vcss" width="88" /></a></p> </footer> </body> </html> Modified: trunk/docutils/test/test_writers/test_html5_polyglot.py =================================================================== --- trunk/docutils/test/test_writers/test_html5_polyglot.py 2024-09-11 07:19:52 UTC (rev 9927) +++ trunk/docutils/test/test_writers/test_html5_polyglot.py 2024-09-11 07:20:07 UTC (rev 9928) @@ -47,13 +47,15 @@ if (tuple(int(i) for i in PIL.__version__.split('.')) >= (10, 3)): DUMMY_PNG_NOT_FOUND = ("[Errno 2] No such file or directory: '%s'" % Path('dummy.png').resolve()) - SCALING_OUTPUT = 'style="width: 32px; height: 32px;" ' + HEIGHT_ATTR = 'height="32" ' + WIDTH_ATTR = 'width="32" ' NO_PIL_SYSTEM_MESSAGE = '' else: REQUIRES_PIL = '\n Requires Python Imaging Library.' ONLY_LOCAL = 'Requires Python Imaging Library.' DUMMY_PNG_NOT_FOUND = 'Requires Python Imaging Library.' - SCALING_OUTPUT = '' + HEIGHT_ATTR = '' + WIDTH_ATTR = '' NO_PIL_SYSTEM_MESSAGE = ( '<aside class="system-message">\n' '<p class="system-message-title">System Message:' @@ -156,7 +158,7 @@ :align: left """, """\ -<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 10 10" style="width: 50%; height: 30px;" class="align-left"> +<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 10 10" style="width: 50%;" height="30" class="align-left"> <circle cx="5" cy="5" r="4" fill="lightblue" /> </svg> """], @@ -451,11 +453,11 @@ :scale: 100% .. figure:: /data/blue%20square.png """, -'<img alt="/data/blue%20square.png" src="data:image/png;base64,' +f'<img alt="/data/blue%20square.png" {HEIGHT_ATTR}src="data:image/png;base64,' 'iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAIAAAD8GO2jAAAALElEQVR4nO3NMQ' 'EAMAjAsDFjvIhHFCbgSwU0kdXvsn96BwAAAAAAAAAAAIsNnEwBk52VRuMAAAAA' 'SUVORK5CYII="' -f' {SCALING_OUTPUT}/>\n{NO_PIL_SYSTEM_MESSAGE}' +f' {WIDTH_ATTR}/>\n{NO_PIL_SYSTEM_MESSAGE}' '<figure>\n' '<img alt="/data/blue%20square.png" src="data:image/png;base64,' 'iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAIAAAD8GO2jAAAALElEQVR4nO3NMQ' Modified: trunk/docutils/test/test_writers/test_html5_polyglot_misc.py =================================================================== --- trunk/docutils/test/test_writers/test_html5_polyglot_misc.py 2024-09-11 07:19:52 UTC (rev 9927) +++ trunk/docutils/test/test_writers/test_html5_polyglot_misc.py 2024-09-11 07:20:07 UTC (rev 9928) @@ -243,7 +243,7 @@ class ImagesTestCase(unittest.TestCase): """Test image handling routines.""" - + maxDiff = None settings = frontend.get_default_settings(_html_base.Writer) document = utils.new_document('test data', settings) translator = _html_base.HTMLTranslator(document) @@ -250,24 +250,28 @@ svg_sample = """\ <?xml version="1.0" encoding="UTF-8"?> <svg xmlns="http://www.w3.org/2000/svg" - style="background: blue; width: 5ex" viewBox="0 0 10 10"> + style="background: blue; width: 5ex; height: 3ex" viewBox="0 0 10 10"> <circle cx="5" cy="5" r="4" fill="lightblue" /> <!-- comments are ignored --> </svg> """ expected = """\ -<svg xmlns="http://www.w3.org/2000/svg" style="background: blue; width: 4em; height: 32px;" viewBox="0 0 10 10" class="test me"> +<svg xmlns="http://www.w3.org/2000/svg" style="background: blue; width: 4em; height: 3ex;" viewBox="0 0 10 10" height="32" class="test me"> <title>blue circle</title><circle cx="5" cy="5" r="4" fill="lightblue" /> \n\ </svg>""" def test_image_size(self): - image = nodes.image(height='3', width='4em') + # HTML attributes "width" and "height" take only integer values: + # -> height value is passed as "height" attribute, + # value with unit is passed via "style". + image = nodes.image(width='4em', height='3', scale=200) self.assertEqual(self.translator.image_size(image), - 'width: 4em; height: 3px;') - image = nodes.image(height='3', width='4em', scale=50) + {'height': '6', 'style': 'width: 8em;'}) + # "style" declaration also used for unitless non-interger value + image = nodes.image(width='4em', height='3', scale=50) self.assertEqual(self.translator.image_size(image), - 'width: 2em; height: 1.5px;') + {'style': 'width: 2em;', 'height': '2'}) def test_prepare_svg(self): # Internal method: the test is no guaranty for stability, @@ -274,7 +278,7 @@ # interface and behaviour may change without notice. image = nodes.image(height='32', width='4em', alt='blue circle', align='left', classes=['test', 'me']) - atts = {'style': self.translator.image_size(image)} + atts = self.translator.image_size(image) rv = self.translator.prepare_svg(self.svg_sample, image, atts) self.assertEqual(rv, self.expected) This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |