From: <gr...@us...> - 2024-08-15 08:43:44
|
Revision: 9906 http://sourceforge.net/p/docutils/code/9906 Author: grubert Date: 2024-08-15 08:43:38 +0000 (Thu, 15 Aug 2024) Log Message: ----------- rename txt to rst Modified Paths: -------------- trunk/docutils/docs/user/Makefile.docutils-update trunk/docutils/docutils/transforms/references.py trunk/docutils/docutils/writers/latex2e/__init__.py trunk/docutils/docutils/writers/odf_odt/__init__.py trunk/docutils/docutils.conf trunk/docutils/pyproject.toml trunk/docutils/test/functional/expected/compact_lists.html trunk/docutils/test/functional/expected/dangerous.html trunk/docutils/test/functional/expected/field_name_limit.html trunk/docutils/test/functional/expected/latex_cornercases.tex trunk/docutils/test/functional/expected/latex_memoir.tex trunk/docutils/test/functional/expected/math_experiments_mathml_blahtexml.html trunk/docutils/test/functional/expected/math_experiments_mathml_pandoc.html trunk/docutils/test/functional/expected/math_experiments_mathml_ttm.html trunk/docutils/test/functional/expected/mathematics_mathml_blahtexml.html trunk/docutils/test/functional/expected/mathematics_mathml_pandoc.html trunk/docutils/test/functional/expected/mathematics_mathml_ttm.html trunk/docutils/test/functional/expected/standalone_rst_docutils_xml.xml trunk/docutils/test/functional/expected/standalone_rst_html4css1.html trunk/docutils/test/functional/expected/standalone_rst_html5.html trunk/docutils/test/functional/expected/standalone_rst_latex.tex trunk/docutils/test/functional/expected/standalone_rst_xetex.tex trunk/docutils/test/functional/tests/compact_lists.py trunk/docutils/test/functional/tests/dangerous.py trunk/docutils/test/functional/tests/field_name_limit.py trunk/docutils/test/functional/tests/footnotes_html5.py trunk/docutils/test/functional/tests/latex_babel.py trunk/docutils/test/functional/tests/latex_cornercases.py trunk/docutils/test/functional/tests/latex_cyrillic.py trunk/docutils/test/functional/tests/latex_docinfo.py trunk/docutils/test/functional/tests/latex_leavevmode.py trunk/docutils/test/functional/tests/latex_literal_block.py trunk/docutils/test/functional/tests/latex_literal_block_fancyvrb.py trunk/docutils/test/functional/tests/latex_literal_block_listings.py trunk/docutils/test/functional/tests/latex_literal_block_verbatim.py trunk/docutils/test/functional/tests/latex_literal_block_verbatimtab.py trunk/docutils/test/functional/tests/latex_memoir.py trunk/docutils/test/functional/tests/math_output_html.py trunk/docutils/test/functional/tests/math_output_latex.py trunk/docutils/test/functional/tests/math_output_mathjax.py trunk/docutils/test/functional/tests/math_output_mathml.py trunk/docutils/test/functional/tests/misc_rst_html4css1.py trunk/docutils/test/functional/tests/misc_rst_html5.py trunk/docutils/test/functional/tests/pep_html.py trunk/docutils/test/functional/tests/standalone_rst_docutils_xml.py trunk/docutils/test/functional/tests/standalone_rst_html4css1.py trunk/docutils/test/functional/tests/standalone_rst_html5.py trunk/docutils/test/functional/tests/standalone_rst_html5_tuftig.py trunk/docutils/test/functional/tests/standalone_rst_latex.py trunk/docutils/test/functional/tests/standalone_rst_manpage.py trunk/docutils/test/functional/tests/standalone_rst_pseudoxml.py trunk/docutils/test/functional/tests/standalone_rst_s5_html_1.py trunk/docutils/test/functional/tests/standalone_rst_s5_html_2.py trunk/docutils/test/functional/tests/standalone_rst_xetex.py trunk/docutils/test/functional/tests/xetex_cyrillic.py trunk/docutils/test/test_CLI.py trunk/docutils/test/test_dependencies.py trunk/docutils/test/test_functional.py trunk/docutils/test/test_io.py trunk/docutils/test/test_parsers/test_rst/test_directives/test_include.py trunk/docutils/test/test_parsers/test_rst/test_directives/test_raw.py trunk/docutils/test/test_parsers/test_rst/test_directives/test_tables.py trunk/docutils/test/test_parsers/test_rst/test_root_prefix.py trunk/docutils/test/test_parsers/test_rst/test_source_line.py trunk/docutils/test/test_parsers/test_rst/test_tables.py trunk/docutils/test/test_publisher.py trunk/docutils/test/test_readers/test_pep/test_inline_markup.py trunk/docutils/test/test_settings.py trunk/docutils/test/test_transforms/test_hyperlinks.py trunk/docutils/test/test_utils/test__init__.py trunk/docutils/test/test_utils/test_math/test_tex2mathml_extern.py trunk/docutils/test/test_writers/test_html4css1_template.py trunk/docutils/test/test_writers/test_html5_template.py trunk/docutils/test/test_writers/test_odt.py trunk/docutils/tools/dev/profile_docutils.py trunk/docutils/tools/dev/unicode2rstsubs.py trunk/docutils/tools/test/test_buildhtml.py Added Paths: ----------- trunk/docutils/BUGS.rst trunk/docutils/COPYING.rst trunk/docutils/FAQ.rst trunk/docutils/HISTORY.rst trunk/docutils/README.rst trunk/docutils/RELEASE-NOTES.rst trunk/docutils/THANKS.rst trunk/docutils/docs/api/publisher.rst trunk/docutils/docs/api/runtime-settings.rst trunk/docutils/docs/api/transforms.rst trunk/docutils/docs/dev/distributing.rst trunk/docutils/docs/dev/enthought-plan.rst trunk/docutils/docs/dev/enthought-rfp.rst trunk/docutils/docs/dev/hacking.rst trunk/docutils/docs/dev/policies.rst trunk/docutils/docs/dev/pysource.rst trunk/docutils/docs/dev/release.rst trunk/docutils/docs/dev/repository.rst trunk/docutils/docs/dev/rst/alternatives.rst trunk/docutils/docs/dev/rst/problems.rst trunk/docutils/docs/dev/runtime-settings-processing.rst trunk/docutils/docs/dev/semantics.rst trunk/docutils/docs/dev/testing.rst trunk/docutils/docs/dev/todo.rst trunk/docutils/docs/dev/website.rst trunk/docutils/docs/header.rst trunk/docutils/docs/header0.rst trunk/docutils/docs/header2.rst trunk/docutils/docs/howto/cmdline-tool.rst trunk/docutils/docs/howto/html-stylesheets.rst trunk/docutils/docs/howto/i18n.rst trunk/docutils/docs/howto/rst-directives.rst trunk/docutils/docs/howto/rst-roles.rst trunk/docutils/docs/howto/security.rst trunk/docutils/docs/index.rst trunk/docutils/docs/peps/pep-0256.rst trunk/docutils/docs/peps/pep-0257.rst trunk/docutils/docs/peps/pep-0258.rst trunk/docutils/docs/peps/pep-0287.rst trunk/docutils/docs/ref/doctree.rst trunk/docutils/docs/ref/rst/definitions.rst trunk/docutils/docs/ref/rst/directives.rst trunk/docutils/docs/ref/rst/history.rst trunk/docutils/docs/ref/rst/introduction.rst trunk/docutils/docs/ref/rst/mathematics.rst trunk/docutils/docs/ref/rst/restructuredtext.rst trunk/docutils/docs/ref/rst/roles.rst trunk/docutils/docs/user/config.rst trunk/docutils/docs/user/emacs.rst trunk/docutils/docs/user/html.rst trunk/docutils/docs/user/latex.rst trunk/docutils/docs/user/links.rst trunk/docutils/docs/user/mailing-lists.rst trunk/docutils/docs/user/manpage.rst trunk/docutils/docs/user/odt.rst trunk/docutils/docs/user/rst/cheatsheet.rst trunk/docutils/docs/user/rst/demo.rst trunk/docutils/docs/user/rst/quickstart.rst trunk/docutils/docs/user/slide-shows.rst trunk/docutils/docs/user/smartquotes.rst trunk/docutils/docs/user/todo-lists.rst trunk/docutils/docs/user/tools.rst trunk/docutils/docutils/writers/s5_html/themes/README.rst trunk/docutils/licenses/BSD-0-Clause.rst trunk/docutils/licenses/BSD-2-Clause.rst trunk/docutils/licenses/ZPL-2-1.rst trunk/docutils/licenses/gpl-3-0.rst trunk/docutils/test/data/config_1.rst trunk/docutils/test/data/config_2.rst trunk/docutils/test/data/config_encoding.rst trunk/docutils/test/data/config_encoding_2.rst trunk/docutils/test/data/config_list.rst trunk/docutils/test/data/config_list_2.rst trunk/docutils/test/data/config_old.rst trunk/docutils/test/data/config_syntax_error.rst trunk/docutils/test/data/csv_dep.rst trunk/docutils/test/data/dependencies.rst trunk/docutils/test/data/full-template.rst trunk/docutils/test/data/help/docutils.rst trunk/docutils/test/data/help/rst2html.rst trunk/docutils/test/data/help/rst2latex.rst trunk/docutils/test/data/include.rst trunk/docutils/test/data/latin1.rst trunk/docutils/test/data/latin2.rst trunk/docutils/test/data/raw.rst trunk/docutils/test/data/stylesheet.rst trunk/docutils/test/data/utf-16-le-sig.rst trunk/docutils/test/data/utf-8-sig.rst trunk/docutils/test/data/utf8.rst trunk/docutils/test/functional/README.rst trunk/docutils/test/functional/expected/standalone_rst_pseudoxml.rst trunk/docutils/test/functional/input/compact_lists.rst trunk/docutils/test/functional/input/cyrillic.rst trunk/docutils/test/functional/input/dangerous.rst trunk/docutils/test/functional/input/data/classes_latex.rst trunk/docutils/test/functional/input/data/comprehensive-math-test.rst trunk/docutils/test/functional/input/data/custom_roles.rst trunk/docutils/test/functional/input/data/custom_roles_latex.rst trunk/docutils/test/functional/input/data/embed_images.rst trunk/docutils/test/functional/input/data/errors.rst trunk/docutils/test/functional/input/data/header_footer.rst trunk/docutils/test/functional/input/data/html5-features.rst trunk/docutils/test/functional/input/data/html5-text-level-tags.rst trunk/docutils/test/functional/input/data/hyperlinking.rst trunk/docutils/test/functional/input/data/latex-problematic.rst trunk/docutils/test/functional/input/data/latex_encoding.rst trunk/docutils/test/functional/input/data/list_table.rst trunk/docutils/test/functional/input/data/math.rst trunk/docutils/test/functional/input/data/math_experiments.rst trunk/docutils/test/functional/input/data/nonalphanumeric.rst trunk/docutils/test/functional/input/data/option_lists.rst trunk/docutils/test/functional/input/data/section_titles.rst trunk/docutils/test/functional/input/data/standard.rst trunk/docutils/test/functional/input/data/svg_images.rst trunk/docutils/test/functional/input/data/swf_images.rst trunk/docutils/test/functional/input/data/table_colspan.rst trunk/docutils/test/functional/input/data/table_complex.rst trunk/docutils/test/functional/input/data/table_rowspan.rst trunk/docutils/test/functional/input/data/tables_latex.rst trunk/docutils/test/functional/input/data/unicode.rst trunk/docutils/test/functional/input/data/urls.rst trunk/docutils/test/functional/input/data/video.rst trunk/docutils/test/functional/input/field_list.rst trunk/docutils/test/functional/input/footnotes.rst trunk/docutils/test/functional/input/latex_babel.rst trunk/docutils/test/functional/input/latex_cornercases.rst trunk/docutils/test/functional/input/latex_docinfo.rst trunk/docutils/test/functional/input/latex_leavevmode.rst trunk/docutils/test/functional/input/latex_literal_block.rst trunk/docutils/test/functional/input/misc_rst_html4css1.rst trunk/docutils/test/functional/input/misc_rst_html5.rst trunk/docutils/test/functional/input/odt_basic.rst trunk/docutils/test/functional/input/odt_classifier.rst trunk/docutils/test/functional/input/odt_contents.rst trunk/docutils/test/functional/input/odt_custom_headfoot.rst trunk/docutils/test/functional/input/odt_footnotes.rst trunk/docutils/test/functional/input/odt_header_footer.rst trunk/docutils/test/functional/input/odt_image.rst trunk/docutils/test/functional/input/odt_literal_block.rst trunk/docutils/test/functional/input/odt_nested_class.rst trunk/docutils/test/functional/input/odt_no_class.rst trunk/docutils/test/functional/input/odt_raw.rst trunk/docutils/test/functional/input/odt_tables1.rst trunk/docutils/test/functional/input/odt_unnested_class.rst trunk/docutils/test/functional/input/pep_html.rst trunk/docutils/test/functional/input/rst_html5_tuftig.rst trunk/docutils/test/functional/input/simple.rst trunk/docutils/test/functional/input/standalone_rst_docutils_xml.rst trunk/docutils/test/functional/input/standalone_rst_html4css1.rst trunk/docutils/test/functional/input/standalone_rst_html5.rst trunk/docutils/test/functional/input/standalone_rst_latex.rst trunk/docutils/test/functional/input/standalone_rst_manpage.rst trunk/docutils/test/functional/input/standalone_rst_pseudoxml.rst trunk/docutils/test/functional/input/standalone_rst_s5_html.rst trunk/docutils/test/functional/input/standalone_rst_xetex.rst trunk/docutils/test/functional/output/README.rst trunk/docutils/test/test_parsers/test_rst/includes/include14.rst trunk/docutils/test/test_parsers/test_rst/includes/include9.rst trunk/docutils/test/test_parsers/test_rst/test_directives/empty.rst trunk/docutils/test/test_parsers/test_rst/test_directives/include 11.rst trunk/docutils/test/test_parsers/test_rst/test_directives/include1.rst trunk/docutils/test/test_parsers/test_rst/test_directives/include10.rst trunk/docutils/test/test_parsers/test_rst/test_directives/include12.rst trunk/docutils/test/test_parsers/test_rst/test_directives/include13.rst trunk/docutils/test/test_parsers/test_rst/test_directives/include2.rst trunk/docutils/test/test_parsers/test_rst/test_directives/include3.rst trunk/docutils/test/test_parsers/test_rst/test_directives/include8.rst trunk/docutils/test/test_parsers/test_rst/test_directives/include_literal.rst trunk/docutils/test/test_parsers/test_rst/test_directives/includes/include14.rst trunk/docutils/test/test_parsers/test_rst/test_directives/includes/include15.rst trunk/docutils/test/test_parsers/test_rst/test_directives/includes/include16.rst trunk/docutils/test/test_parsers/test_rst/test_directives/includes/include4.rst trunk/docutils/test/test_parsers/test_rst/test_directives/includes/include5.rst trunk/docutils/test/test_parsers/test_rst/test_directives/includes/more/include6.rst trunk/docutils/test/test_parsers/test_rst/test_directives/includes/sibling/include7.rst trunk/docutils/test/test_parsers/test_rst/test_directives/raw1.rst trunk/docutils/tools/dev/README.rst trunk/docutils/tools/editors/README.rst trunk/docutils/tools/editors/emacs/README.rst trunk/docutils/tools/editors/emacs/tests/README.rst Removed Paths: ------------- trunk/docutils/BUGS.txt trunk/docutils/COPYING.txt trunk/docutils/FAQ.txt trunk/docutils/HISTORY.txt trunk/docutils/README.txt trunk/docutils/RELEASE-NOTES.txt trunk/docutils/THANKS.txt trunk/docutils/docs/api/publisher.txt trunk/docutils/docs/api/runtime-settings.txt trunk/docutils/docs/api/transforms.txt trunk/docutils/docs/dev/distributing.txt trunk/docutils/docs/dev/enthought-plan.txt trunk/docutils/docs/dev/enthought-rfp.txt trunk/docutils/docs/dev/hacking.txt trunk/docutils/docs/dev/policies.txt trunk/docutils/docs/dev/pysource.txt trunk/docutils/docs/dev/release.txt trunk/docutils/docs/dev/repository.txt trunk/docutils/docs/dev/rst/alternatives.txt trunk/docutils/docs/dev/rst/problems.txt trunk/docutils/docs/dev/runtime-settings-processing.txt trunk/docutils/docs/dev/semantics.txt trunk/docutils/docs/dev/testing.txt trunk/docutils/docs/dev/todo.txt trunk/docutils/docs/dev/website.txt trunk/docutils/docs/header.txt trunk/docutils/docs/header0.txt trunk/docutils/docs/header2.txt trunk/docutils/docs/howto/cmdline-tool.txt trunk/docutils/docs/howto/html-stylesheets.txt trunk/docutils/docs/howto/i18n.txt trunk/docutils/docs/howto/rst-directives.txt trunk/docutils/docs/howto/rst-roles.txt trunk/docutils/docs/howto/security.txt trunk/docutils/docs/index.txt trunk/docutils/docs/peps/pep-0256.txt trunk/docutils/docs/peps/pep-0257.txt trunk/docutils/docs/peps/pep-0258.txt trunk/docutils/docs/peps/pep-0287.txt trunk/docutils/docs/ref/doctree.txt trunk/docutils/docs/ref/rst/definitions.txt trunk/docutils/docs/ref/rst/directives.txt trunk/docutils/docs/ref/rst/history.txt trunk/docutils/docs/ref/rst/introduction.txt trunk/docutils/docs/ref/rst/mathematics.txt trunk/docutils/docs/ref/rst/restructuredtext.txt trunk/docutils/docs/ref/rst/roles.txt trunk/docutils/docs/user/config.txt trunk/docutils/docs/user/emacs.txt trunk/docutils/docs/user/html.txt trunk/docutils/docs/user/latex.txt trunk/docutils/docs/user/links.txt trunk/docutils/docs/user/mailing-lists.txt trunk/docutils/docs/user/manpage.txt trunk/docutils/docs/user/odt.txt trunk/docutils/docs/user/rst/cheatsheet.txt trunk/docutils/docs/user/rst/demo.txt trunk/docutils/docs/user/rst/quickstart.txt trunk/docutils/docs/user/slide-shows.txt trunk/docutils/docs/user/smartquotes.txt trunk/docutils/docs/user/todo-lists.txt trunk/docutils/docs/user/tools.txt trunk/docutils/docutils/writers/s5_html/themes/README.txt trunk/docutils/licenses/BSD-0-Clause.txt trunk/docutils/licenses/BSD-2-Clause.txt trunk/docutils/licenses/ZPL-2-1.txt trunk/docutils/licenses/gpl-3-0.txt trunk/docutils/test/data/config_1.txt trunk/docutils/test/data/config_2.txt trunk/docutils/test/data/config_encoding.txt trunk/docutils/test/data/config_encoding_2.txt trunk/docutils/test/data/config_list.txt trunk/docutils/test/data/config_list_2.txt trunk/docutils/test/data/config_old.txt trunk/docutils/test/data/config_syntax_error.txt trunk/docutils/test/data/csv_dep.txt trunk/docutils/test/data/dependencies.txt trunk/docutils/test/data/full-template.txt trunk/docutils/test/data/help/docutils.txt trunk/docutils/test/data/help/rst2html.txt trunk/docutils/test/data/help/rst2latex.txt trunk/docutils/test/data/include.txt trunk/docutils/test/data/latin1.txt trunk/docutils/test/data/latin2.txt trunk/docutils/test/data/raw.txt trunk/docutils/test/data/stylesheet.txt trunk/docutils/test/data/utf-16-le-sig.txt trunk/docutils/test/data/utf-8-sig.txt trunk/docutils/test/data/utf8.txt trunk/docutils/test/functional/README.txt trunk/docutils/test/functional/expected/standalone_rst_pseudoxml.txt trunk/docutils/test/functional/input/compact_lists.txt trunk/docutils/test/functional/input/cyrillic.txt trunk/docutils/test/functional/input/dangerous.txt trunk/docutils/test/functional/input/data/classes_latex.txt trunk/docutils/test/functional/input/data/comprehensive-math-test.txt trunk/docutils/test/functional/input/data/custom_roles.txt trunk/docutils/test/functional/input/data/custom_roles_latex.txt trunk/docutils/test/functional/input/data/embed_images.txt trunk/docutils/test/functional/input/data/errors.txt trunk/docutils/test/functional/input/data/header_footer.txt trunk/docutils/test/functional/input/data/html5-features.txt trunk/docutils/test/functional/input/data/html5-text-level-tags.txt trunk/docutils/test/functional/input/data/hyperlinking.txt trunk/docutils/test/functional/input/data/latex-problematic.txt trunk/docutils/test/functional/input/data/latex_encoding.txt trunk/docutils/test/functional/input/data/list_table.txt trunk/docutils/test/functional/input/data/math.txt trunk/docutils/test/functional/input/data/math_experiments.txt trunk/docutils/test/functional/input/data/nonalphanumeric.txt trunk/docutils/test/functional/input/data/option_lists.txt trunk/docutils/test/functional/input/data/section_titles.txt trunk/docutils/test/functional/input/data/standard.txt trunk/docutils/test/functional/input/data/svg_images.txt trunk/docutils/test/functional/input/data/swf_images.txt trunk/docutils/test/functional/input/data/table_colspan.txt trunk/docutils/test/functional/input/data/table_complex.txt trunk/docutils/test/functional/input/data/table_rowspan.txt trunk/docutils/test/functional/input/data/tables_latex.txt trunk/docutils/test/functional/input/data/unicode.txt trunk/docutils/test/functional/input/data/urls.txt trunk/docutils/test/functional/input/data/video.txt trunk/docutils/test/functional/input/field_list.txt trunk/docutils/test/functional/input/footnotes.txt trunk/docutils/test/functional/input/latex_babel.txt trunk/docutils/test/functional/input/latex_cornercases.txt trunk/docutils/test/functional/input/latex_docinfo.txt trunk/docutils/test/functional/input/latex_leavevmode.txt trunk/docutils/test/functional/input/latex_literal_block.txt trunk/docutils/test/functional/input/misc_rst_html4css1.txt trunk/docutils/test/functional/input/misc_rst_html5.txt trunk/docutils/test/functional/input/odt_basic.txt trunk/docutils/test/functional/input/odt_classifier.txt trunk/docutils/test/functional/input/odt_contents.txt trunk/docutils/test/functional/input/odt_custom_headfoot.txt trunk/docutils/test/functional/input/odt_footnotes.txt trunk/docutils/test/functional/input/odt_header_footer.txt trunk/docutils/test/functional/input/odt_image.txt trunk/docutils/test/functional/input/odt_literal_block.txt trunk/docutils/test/functional/input/odt_nested_class.txt trunk/docutils/test/functional/input/odt_no_class.txt trunk/docutils/test/functional/input/odt_raw.txt trunk/docutils/test/functional/input/odt_tables1.txt trunk/docutils/test/functional/input/odt_unnested_class.txt trunk/docutils/test/functional/input/pep_html.txt trunk/docutils/test/functional/input/rst_html5_tuftig.txt trunk/docutils/test/functional/input/simple.txt trunk/docutils/test/functional/input/standalone_rst_docutils_xml.txt trunk/docutils/test/functional/input/standalone_rst_html4css1.txt trunk/docutils/test/functional/input/standalone_rst_html5.txt trunk/docutils/test/functional/input/standalone_rst_latex.txt trunk/docutils/test/functional/input/standalone_rst_manpage.txt trunk/docutils/test/functional/input/standalone_rst_pseudoxml.txt trunk/docutils/test/functional/input/standalone_rst_s5_html.txt trunk/docutils/test/functional/input/standalone_rst_xetex.txt trunk/docutils/test/functional/output/README.txt trunk/docutils/test/test_parsers/test_rst/includes/include14.txt trunk/docutils/test/test_parsers/test_rst/includes/include9.txt trunk/docutils/test/test_parsers/test_rst/test_directives/empty.txt trunk/docutils/test/test_parsers/test_rst/test_directives/include 11.txt trunk/docutils/test/test_parsers/test_rst/test_directives/include1.txt trunk/docutils/test/test_parsers/test_rst/test_directives/include10.txt trunk/docutils/test/test_parsers/test_rst/test_directives/include12.txt trunk/docutils/test/test_parsers/test_rst/test_directives/include13.txt trunk/docutils/test/test_parsers/test_rst/test_directives/include2.txt trunk/docutils/test/test_parsers/test_rst/test_directives/include3.txt trunk/docutils/test/test_parsers/test_rst/test_directives/include8.txt trunk/docutils/test/test_parsers/test_rst/test_directives/include_literal.txt trunk/docutils/test/test_parsers/test_rst/test_directives/includes/include14.txt trunk/docutils/test/test_parsers/test_rst/test_directives/includes/include15.txt trunk/docutils/test/test_parsers/test_rst/test_directives/includes/include16.txt trunk/docutils/test/test_parsers/test_rst/test_directives/includes/include4.txt trunk/docutils/test/test_parsers/test_rst/test_directives/includes/include5.txt trunk/docutils/test/test_parsers/test_rst/test_directives/includes/more/include6.txt trunk/docutils/test/test_parsers/test_rst/test_directives/includes/sibling/include7.txt trunk/docutils/test/test_parsers/test_rst/test_directives/raw1.txt trunk/docutils/tools/dev/README.txt trunk/docutils/tools/editors/README.txt trunk/docutils/tools/editors/emacs/README.txt trunk/docutils/tools/editors/emacs/tests/README.txt Copied: trunk/docutils/BUGS.rst (from rev 9905, trunk/docutils/BUGS.txt) =================================================================== --- trunk/docutils/BUGS.rst (rev 0) +++ trunk/docutils/BUGS.rst 2024-08-15 08:43:38 UTC (rev 9906) @@ -0,0 +1,295 @@ +.. include:: docs/header0.rst + +================ + Docutils_ Bugs +================ + +:Author: David Goodger; open to all Docutils developers +:Contact: go...@py... +:Date: $Date$ +:Revision: $Revision$ +:Copyright: This document has been placed in the public domain. + +.. _Docutils: https://docutils.sourceforge.io/ + + +Bugs in Docutils?!? Yes, we do have a few. Some are old-timers that +tend to stay in the shadows and don't bother anybody. Once in a while +new bugs are born. From time to time some bugs (new and old) crawl +out into the light and must be dealt with. Icky. + +This document describes how to report a bug, and lists known bugs. + +.. contents:: + + +How To Report A Bug +=================== + +If you think you've discovered a bug, please read through these +guidelines before reporting it. + +First, make sure it's a new bug: + +* Please check the list of `known bugs`_ below and the `SourceForge + Bug Tracker`_ to see if it has already been reported. + +* Are you using the very latest version of Docutils? The bug may have + already been fixed. Please get the latest version of Docutils from + the repository_ or from the current snapshot_ and check again. Even + if your bug has not been fixed, others probably have, and you're + better off with the most up-to-date code. + + If you don't have time to check the latest snapshot, please report + the bug anyway. We'd rather tell you that it's already fixed than + miss reports of unfixed bugs. + +* If Docutils does not behave the way you expect, look in the + documentation_ (don't forget the FAQ_!) and `mailing list archives`_ + for evidence that it should behave the way you expect. + +If you're not sure, please ask on the Docutils-users_ mailing list +first. + +--------------------------------------------------------------------- + +If it's a new bug, the most important thing you can do is to write a +simple description and a recipe that reproduces the bug. Try to +create a `minimal example`_ that demonstrates the bug. The easier you +make it to understand and track down the bug, the more likely a fix +will be. + +.. _minimal example: + +.. sidebar:: minimal example + + A `minimal working example` is a complete example which is as as small and + simple as possible. It should be complete and working, so that + + * you cannot accidentally omit information important to diagnosing + the problem and + * the person responding can just copy-and-paste the code to try it out. + + To construct an example which is as small as possible, the rule + quite simple: *remove/leave out anything which is not necessary*. + + See also: `What is a minimal working example?`__, `LaTeX FAQ`__ + + __ http://www.minimalbeispiel.de/mini-en.html + __ http://www.tex.ac.uk/cgi-bin/texfaq2html?label=minxampl + +Now you're ready to write the bug report. Please include: + +* A clear description of the bug. Describe how you expected Docutils + to behave, and contrast that with how it actually behaved. While + the bug may seem obvious to you, it may not be so obvious to someone + else, so it's best to avoid a guessing game. + +* A complete description of the environment in which you reproduced + the bug: + + - Your operating system & version. + - The version of Python (``python -V``). + - The version of Docutils (use the "-V" option to most Docutils + front-end tools). + - Any private modifications you made to Docutils. + - Anything else that could possibly be relevant. Err on the side + of too much information, rather than too little. + +* A literal transcript of the *exact* command you ran, and the *exact* + output. Use the "--traceback" option to get a complete picture. + +* The exact input and output files. Create a `minimal example`_ + of the failing behaviour — it is better to attach complete files + to your bug report than to include just a summary or excerpt. + +* If you also want to include speculation as to the cause, and even a + patch to fix the bug, that would be great! + +The best place to send your bug report is to the `SourceForge Bug +Tracker`_. That way, it won't be misplaced or forgotten. In fact, an +open bug report on SourceForge is a constant irritant that begs to be +squashed. + +Thank you! + +(This section was inspired by the `Subversion project's`__ BUGS__ +file.) + +__ http://subversion.tigris.org/ +__ http://svn.collab.net/viewcvs/svn/trunk/BUGS?view=markup + +.. _repository: docs/dev/repository.html +.. _snapshot: https://docutils.sourceforge.io/#download +.. _documentation: docs/ +.. _FAQ: FAQ.html +.. _mailing list archives: https://docutils.sourceforge.io/#mailing-lists +.. _Docutils-users: docs/user/mailing-lists.html#docutils-users +.. _SourceForge Bug Tracker: + https://sourceforge.net/p/docutils/bugs/ + + +Known Bugs +========== + +Also see the `SourceForge Bug Tracker`_. + +* .. _error reporting: + + Calling rst2s5 with a non-existent theme (``--theme + does_not_exist``) + causes exceptions. Such errors should be handled more gracefully. + +* The "stylesheet" setting (a URL, to be used verbatim) should be + allowed to be combined with "embed_stylesheet". The stylesheet data + should be read in using urllib. There was an assumption that a + stylesheet to be embedded should exist as a file on the local + system, and only the "stylesheet_path" setting should be used. + +* ``utils.relative_path()`` sometimes returns absolute _`paths on + Windows` (like ``C:/test/foo.css``) where it could have chosen a + relative path. + + Furthermore, absolute pathnames are inserted verbatim, like + ``href="C:/test/foo.css"`` instead of + ``href="file:///C:/test/foo.css"``. + + .. gmane web interface is down. + TODO: find this article in the Sourceforge mail archives + For details, see `this posting by Alan G. Isaac + <http://article.gmane.org/gmane.text.docutils.user/1569>`_. + +* Footnote label "5" should be "4" when processing the following + input:: + + ref [#abc]_ [#]_ [1]_ [#4]_ + + .. [#abc] footnote + .. [#] two + .. [1] one + .. [#4] four + + Output:: + + <document source="<stdin>"> + <paragraph> + ref + <footnote_reference auto="1" ids="id1" refid="abc"> + 2 + + <footnote_reference auto="1" ids="id2" refid="id5"> + 3 + + <footnote_reference ids="id3" refid="id6"> + 1 + + <footnote_reference auto="1" ids="id4" refid="id7"> + 5 + <footnote auto="1" backrefs="id1" ids="abc" names="abc"> + <label> + 2 + <paragraph> + footnote + <footnote auto="1" backrefs="id2" ids="id5" names="3"> + <label> + 3 + <paragraph> + two + <footnote backrefs="id3" ids="id6" names="1"> + <label> + 1 + <paragraph> + one + <footnote auto="1" backrefs="id4" ids="id7" names="4"> + <label> + 5 + <paragraph> + four + +* IDs are based on names. Explicit hyperlink targets have priority + over implicit targets. But if an explicit target comes after an + implicit target with the same name, the ID of the first (implicit) + target remains based on the implicit name. Since HTML fragment + identifiers are based on the IDs, the first target keeps the name. + For example:: + + .. contents:: + + Section + ======= + + .. _contents: + + Subsection + ---------- + + text with a reference to contents_ and section_ + + .. _section: + + This paragraph is explicitly targeted with the name "section". + + When processed to HTML, the 2 internal hyperlinks (to "contents" & + "section") will work fine, but hyperlinks from outside the document + using ``href="...#contents"`` and ``href="...#section"`` won't work. + Such external links will connect to the implicit targets (table of + contents and "Section" title) instead of the explicit targets + ("Subsection" title and last paragraph). + + Hyperlink targets with duplicate names should be assigned new IDs + unrelated to the target names (i.e., "id"-prefix serial IDs). + +* The "contents" ID of the local table of contents in + ``test/functional/expected/standalone_rst_pseudoxml.rst`` is lost in + the HTML output at + ``test/functional/expected/standalone_rst_html4css1.html``. + +* _`Blank first columns` in simple tables with explicit row separators + silently swallow their input. They should at least produce system + error messages. But, with explicit row separators, the meaning is + unambiguous and ought to be supported:: + + ============== ========== + Table with row separators + ============== ========== + and blank + -------------- ---------- + entries + -------------- ---------- + in first + -------------- ---------- + columns. + ============== ========== + + Added a commented-out test case to + test/test_parsers/test_rst/test_SimpleTableParser.py. + +* _`Footnote references with hyperlink targets` cause a possibly + invalid node tree and make the HTML writer crash:: + + $ rst2pseudoxml + [1]_ + + .. _1: URI + <document source="<stdin>"> + <paragraph> + <footnote_reference ids="id1" refuri="URI"> + 1 + <target ids="id2" names="1" refuri="URI"> + +* Anonymous references have "name" attributes. Should they? Are they + used? See ``test/test_parsers/test_rst/test_inline_markup.py``. + +* <reference> elements have a "name" attribute, not "names". The + attribute should be "names"; this is an inconsistency. + + +.. Emacs settings + + Local Variables: + mode: indented-text + mode: rst + indent-tabs-mode: nil + sentence-end-double-space: t + fill-column: 70 + End: Deleted: trunk/docutils/BUGS.txt =================================================================== --- trunk/docutils/BUGS.txt 2024-08-15 07:31:04 UTC (rev 9905) +++ trunk/docutils/BUGS.txt 2024-08-15 08:43:38 UTC (rev 9906) @@ -1,295 +0,0 @@ -.. include:: docs/header0.txt - -================ - Docutils_ Bugs -================ - -:Author: David Goodger; open to all Docutils developers -:Contact: go...@py... -:Date: $Date$ -:Revision: $Revision$ -:Copyright: This document has been placed in the public domain. - -.. _Docutils: https://docutils.sourceforge.io/ - - -Bugs in Docutils?!? Yes, we do have a few. Some are old-timers that -tend to stay in the shadows and don't bother anybody. Once in a while -new bugs are born. From time to time some bugs (new and old) crawl -out into the light and must be dealt with. Icky. - -This document describes how to report a bug, and lists known bugs. - -.. contents:: - - -How To Report A Bug -=================== - -If you think you've discovered a bug, please read through these -guidelines before reporting it. - -First, make sure it's a new bug: - -* Please check the list of `known bugs`_ below and the `SourceForge - Bug Tracker`_ to see if it has already been reported. - -* Are you using the very latest version of Docutils? The bug may have - already been fixed. Please get the latest version of Docutils from - the repository_ or from the current snapshot_ and check again. Even - if your bug has not been fixed, others probably have, and you're - better off with the most up-to-date code. - - If you don't have time to check the latest snapshot, please report - the bug anyway. We'd rather tell you that it's already fixed than - miss reports of unfixed bugs. - -* If Docutils does not behave the way you expect, look in the - documentation_ (don't forget the FAQ_!) and `mailing list archives`_ - for evidence that it should behave the way you expect. - -If you're not sure, please ask on the Docutils-users_ mailing list -first. - ---------------------------------------------------------------------- - -If it's a new bug, the most important thing you can do is to write a -simple description and a recipe that reproduces the bug. Try to -create a `minimal example`_ that demonstrates the bug. The easier you -make it to understand and track down the bug, the more likely a fix -will be. - -.. _minimal example: - -.. sidebar:: minimal example - - A `minimal working example` is a complete example which is as as small and - simple as possible. It should be complete and working, so that - - * you cannot accidentally omit information important to diagnosing - the problem and - * the person responding can just copy-and-paste the code to try it out. - - To construct an example which is as small as possible, the rule - quite simple: *remove/leave out anything which is not necessary*. - - See also: `What is a minimal working example?`__, `LaTeX FAQ`__ - - __ http://www.minimalbeispiel.de/mini-en.html - __ http://www.tex.ac.uk/cgi-bin/texfaq2html?label=minxampl - -Now you're ready to write the bug report. Please include: - -* A clear description of the bug. Describe how you expected Docutils - to behave, and contrast that with how it actually behaved. While - the bug may seem obvious to you, it may not be so obvious to someone - else, so it's best to avoid a guessing game. - -* A complete description of the environment in which you reproduced - the bug: - - - Your operating system & version. - - The version of Python (``python -V``). - - The version of Docutils (use the "-V" option to most Docutils - front-end tools). - - Any private modifications you made to Docutils. - - Anything else that could possibly be relevant. Err on the side - of too much information, rather than too little. - -* A literal transcript of the *exact* command you ran, and the *exact* - output. Use the "--traceback" option to get a complete picture. - -* The exact input and output files. Create a `minimal example`_ - of the failing behaviour — it is better to attach complete files - to your bug report than to include just a summary or excerpt. - -* If you also want to include speculation as to the cause, and even a - patch to fix the bug, that would be great! - -The best place to send your bug report is to the `SourceForge Bug -Tracker`_. That way, it won't be misplaced or forgotten. In fact, an -open bug report on SourceForge is a constant irritant that begs to be -squashed. - -Thank you! - -(This section was inspired by the `Subversion project's`__ BUGS__ -file.) - -__ http://subversion.tigris.org/ -__ http://svn.collab.net/viewcvs/svn/trunk/BUGS?view=markup - -.. _repository: docs/dev/repository.html -.. _snapshot: https://docutils.sourceforge.io/#download -.. _documentation: docs/ -.. _FAQ: FAQ.html -.. _mailing list archives: https://docutils.sourceforge.io/#mailing-lists -.. _Docutils-users: docs/user/mailing-lists.html#docutils-users -.. _SourceForge Bug Tracker: - https://sourceforge.net/p/docutils/bugs/ - - -Known Bugs -========== - -Also see the `SourceForge Bug Tracker`_. - -* .. _error reporting: - - Calling rst2s5 with a non-existent theme (``--theme - does_not_exist``) - causes exceptions. Such errors should be handled more gracefully. - -* The "stylesheet" setting (a URL, to be used verbatim) should be - allowed to be combined with "embed_stylesheet". The stylesheet data - should be read in using urllib. There was an assumption that a - stylesheet to be embedded should exist as a file on the local - system, and only the "stylesheet_path" setting should be used. - -* ``utils.relative_path()`` sometimes returns absolute _`paths on - Windows` (like ``C:/test/foo.css``) where it could have chosen a - relative path. - - Furthermore, absolute pathnames are inserted verbatim, like - ``href="C:/test/foo.css"`` instead of - ``href="file:///C:/test/foo.css"``. - - .. gmane web interface is down. - TODO: find this article in the Sourceforge mail archives - For details, see `this posting by Alan G. Isaac - <http://article.gmane.org/gmane.text.docutils.user/1569>`_. - -* Footnote label "5" should be "4" when processing the following - input:: - - ref [#abc]_ [#]_ [1]_ [#4]_ - - .. [#abc] footnote - .. [#] two - .. [1] one - .. [#4] four - - Output:: - - <document source="<stdin>"> - <paragraph> - ref - <footnote_reference auto="1" ids="id1" refid="abc"> - 2 - - <footnote_reference auto="1" ids="id2" refid="id5"> - 3 - - <footnote_reference ids="id3" refid="id6"> - 1 - - <footnote_reference auto="1" ids="id4" refid="id7"> - 5 - <footnote auto="1" backrefs="id1" ids="abc" names="abc"> - <label> - 2 - <paragraph> - footnote - <footnote auto="1" backrefs="id2" ids="id5" names="3"> - <label> - 3 - <paragraph> - two - <footnote backrefs="id3" ids="id6" names="1"> - <label> - 1 - <paragraph> - one - <footnote auto="1" backrefs="id4" ids="id7" names="4"> - <label> - 5 - <paragraph> - four - -* IDs are based on names. Explicit hyperlink targets have priority - over implicit targets. But if an explicit target comes after an - implicit target with the same name, the ID of the first (implicit) - target remains based on the implicit name. Since HTML fragment - identifiers are based on the IDs, the first target keeps the name. - For example:: - - .. contents:: - - Section - ======= - - .. _contents: - - Subsection - ---------- - - text with a reference to contents_ and section_ - - .. _section: - - This paragraph is explicitly targeted with the name "section". - - When processed to HTML, the 2 internal hyperlinks (to "contents" & - "section") will work fine, but hyperlinks from outside the document - using ``href="...#contents"`` and ``href="...#section"`` won't work. - Such external links will connect to the implicit targets (table of - contents and "Section" title) instead of the explicit targets - ("Subsection" title and last paragraph). - - Hyperlink targets with duplicate names should be assigned new IDs - unrelated to the target names (i.e., "id"-prefix serial IDs). - -* The "contents" ID of the local table of contents in - ``test/functional/expected/standalone_rst_pseudoxml.txt`` is lost in - the HTML output at - ``test/functional/expected/standalone_rst_html4css1.html``. - -* _`Blank first columns` in simple tables with explicit row separators - silently swallow their input. They should at least produce system - error messages. But, with explicit row separators, the meaning is - unambiguous and ought to be supported:: - - ============== ========== - Table with row separators - ============== ========== - and blank - -------------- ---------- - entries - -------------- ---------- - in first - -------------- ---------- - columns. - ============== ========== - - Added a commented-out test case to - test/test_parsers/test_rst/test_SimpleTableParser.py. - -* _`Footnote references with hyperlink targets` cause a possibly - invalid node tree and make the HTML writer crash:: - - $ rst2pseudoxml - [1]_ - - .. _1: URI - <document source="<stdin>"> - <paragraph> - <footnote_reference ids="id1" refuri="URI"> - 1 - <target ids="id2" names="1" refuri="URI"> - -* Anonymous references have "name" attributes. Should they? Are they - used? See ``test/test_parsers/test_rst/test_inline_markup.py``. - -* <reference> elements have a "name" attribute, not "names". The - attribute should be "names"; this is an inconsistency. - - -.. Emacs settings - - Local Variables: - mode: indented-text - mode: rst - indent-tabs-mode: nil - sentence-end-double-space: t - fill-column: 70 - End: Copied: trunk/docutils/COPYING.rst (from rev 9905, trunk/docutils/COPYING.txt) =================================================================== --- trunk/docutils/COPYING.rst (rev 0) +++ trunk/docutils/COPYING.rst 2024-08-15 08:43:38 UTC (rev 9906) @@ -0,0 +1,159 @@ +.. include:: docs/header0.rst + +================== + Copying Docutils +================== + +:Author: David Goodger +:Contact: go...@py... +:Date: $Date$ +:Web site: https://docutils.sourceforge.io/ +:Copyright: This document has been placed in the public domain. + +Most of the files included in this project have been placed in the +public domain, and therefore have no license requirements and no +restrictions on copying or usage; see the `Public Domain Dedication`_ +below. There are exceptions_, listed below. +Files in the Sandbox_ are not distributed with Docutils releases and +may have different license terms. + + +Public Domain Dedication +======================== + +The persons who have associated their work with this project (the +"Dedicator": David Goodger and the many contributors to the Docutils +project) hereby dedicate the entire copyright, less the exceptions_ +listed below, in the work of authorship known as "Docutils" identified +below (the "Work") to the public domain. + +The primary repository for the Work is the Internet World Wide Web +site <https://docutils.sourceforge.io/>. The Work consists of the +files within the "docutils" module of the Docutils project Subversion +repository (http://svn.code.sf.net/p/docutils/code/), +whose Internet web interface is located at +<https://sourceforge.net/p/docutils/code>. Files dedicated to the +public domain may be identified by the inclusion, near the beginning +of each file, of a declaration of the form:: + + Copyright: This document/module/DTD/stylesheet/file/etc. has been + placed in the public domain. + +Dedicator makes this dedication for the benefit of the public at large +and to the detriment of Dedicator's heirs and successors. Dedicator +intends this dedication to be an overt act of relinquishment in +perpetuity of all present and future rights under copyright law, +whether vested or contingent, in the Work. Dedicator understands that +such relinquishment of all rights includes the relinquishment of all +rights to enforce (by lawsuit or otherwise) those copyrights in the +Work. + +Dedicator recognizes that, once placed in the public domain, the Work +may be freely reproduced, distributed, transmitted, used, modified, +built upon, or otherwise exploited by anyone for any purpose, +commercial or non-commercial, and in any way, including by methods +that have not yet been invented or conceived. + +(This dedication is derived from the text of the `Creative Commons +Public Domain Dedication`. [#]_) + +.. [#] Creative Commons has `retired this legal tool`__ and does not + recommend that it be applied to works: This tool is based on United + States law and may not be applicable outside the US. For dedicating new + works to the public domain, Creative Commons recommend the replacement + Public Domain Dedication CC0_ (CC zero, "No Rights Reserved"). So does + the Free Software Foundation in its license-list_. + + __ http://creativecommons.org/retiredlicenses + .. _CC0: http://creativecommons.org/about/cc0 + +Exceptions +========== + +The exceptions to the `Public Domain Dedication`_ above are: + +* docutils/utils/smartquotes.py + + Copyright © 2011 Günter Milde, + based on `SmartyPants`_ © 2003 John Gruber + (released under a "revised" `BSD 3-Clause License`_ included in the file) + and smartypants.py © 2004, 2007 Chad Miller. + Released under the terms of the `BSD 2-Clause License`_ + (`local copy <licenses/BSD-2-Clause.rst>`__). + + .. _SmartyPants: http://daringfireball.net/projects/smartypants/ + +* docutils/utils/math/latex2mathml.py + + Copyright © Jens Jørgen Mortensen, Günter Milde. + Released under the terms of the `BSD 2-Clause License`_ + (`local copy <licenses/BSD-2-Clause.rst>`__). + +* | docutils/utils/math/math2html.py, + | docutils/writers/html5_polyglot/math.css + + Copyright © 2009,2010 Alex Fernández; 2021 Günter Milde + + These files were part of eLyXer_, released under the `GNU + General Public License`_ version 3 or later. The author relicensed + them for Docutils under the terms of the `BSD 2-Clause License`_ + (`local copy <licenses/BSD-2-Clause.rst>`__). + + .. _eLyXer: https://github.com/alexfernandez/elyxer + +* | docutils/__main__.py, + | docutils/parsers/commonmark_wrapper.py, + | docutils/parsers/recommonmark_wrapper.py, + | docutils/utils/error_reporting.py, + | docutils/utils/math/__init__.py, + | docutils/utils/math/latex2mathml.py, + | docutils/utils/math/tex2mathml_extern.py, + | docutils/utils/punctuation_chars.py, + | docutils/utils/smartquotes.py, + | docutils/writers/html5_polyglot/__init__.py, + | docutils/writers/html5_polyglot/\*.css, + | docutils/writers/latex2e/docutils.sty, + | docutils/writers/xetex/__init__.py, + | test/test_parsers/test_recommonmark/\*.py, + | test/test_parsers/test_rst/test_directives/test__init__.py, + | test/test_parsers/test_rst/test_directives/test_code_parsing.py, + | test/test_parsers/test_rst/test_line_length_limit_default.py, + | test/test_parsers/test_rst/test_line_length_limit.py, + | test/test_writers/test_latex2e_misc.py, + | test/transforms/test_smartquotes.py, + | tools/docutils-cli.py, + | tools/rst2html5.py + + Copyright © Günter Milde. + Released under the terms of the `BSD 2-Clause License`_ + (`local copy <licenses/BSD-2-Clause.rst>`__). + +* docutils/utils/roman.py + + copyright by Mark Pilgrim, released under the + `Zope Public License Version 2.1`_ (`local copy`__). + + __ licenses/ZPL-2-1.rst + +* tools/editors/emacs/rst.el + + copyright by Free Software Foundation, Inc., + released under the `GNU General Public License`_ version 3 or later + (`local copy`__). + + __ licenses/gpl-3-0.rst + +All used licenses are OSI-approved_ and GPL-compatible_. + +Plaintext versions of all the linked-to licenses are provided in the +licenses_ directory. + +.. _sandbox: https://docutils.sourceforge.io/sandbox/README.html +.. _licenses: licenses/ +.. _GNU General Public License: https://www.gnu.org/copyleft/gpl.html +.. _BSD 2-Clause License: http://opensource.org/licenses/BSD-2-Clause +.. _BSD 3-Clause License: https://opensource.org/licenses/BSD-3-Clause +.. _Zope Public License Version 2.1: https://opensource.org/license/zpl-2-1/ +.. _OSI-approved: http://opensource.org/licenses/ +.. _license-list: +.. _GPL-compatible: https://www.gnu.org/licenses/license-list.html Deleted: trunk/docutils/COPYING.txt =================================================================== --- trunk/docutils/COPYING.txt 2024-08-15 07:31:04 UTC (rev 9905) +++ trunk/docutils/COPYING.txt 2024-08-15 08:43:38 UTC (rev 9906) @@ -1,159 +0,0 @@ -.. include:: docs/header0.txt - -================== - Copying Docutils -================== - -:Author: David Goodger -:Contact: go...@py... -:Date: $Date$ -:Web site: https://docutils.sourceforge.io/ -:Copyright: This document has been placed in the public domain. - -Most of the files included in this project have been placed in the -public domain, and therefore have no license requirements and no -restrictions on copying or usage; see the `Public Domain Dedication`_ -below. There are exceptions_, listed below. -Files in the Sandbox_ are not distributed with Docutils releases and -may have different license terms. - - -Public Domain Dedication -======================== - -The persons who have associated their work with this project (the -"Dedicator": David Goodger and the many contributors to the Docutils -project) hereby dedicate the entire copyright, less the exceptions_ -listed below, in the work of authorship known as "Docutils" identified -below (the "Work") to the public domain. - -The primary repository for the Work is the Internet World Wide Web -site <https://docutils.sourceforge.io/>. The Work consists of the -files within the "docutils" module of the Docutils project Subversion -repository (http://svn.code.sf.net/p/docutils/code/), -whose Internet web interface is located at -<https://sourceforge.net/p/docutils/code>. Files dedicated to the -public domain may be identified by the inclusion, near the beginning -of each file, of a declaration of the form:: - - Copyright: This document/module/DTD/stylesheet/file/etc. has been - placed in the public domain. - -Dedicator makes this dedication for the benefit of the public at large -and to the detriment of Dedicator's heirs and successors. Dedicator -intends this dedication to be an overt act of relinquishment in -perpetuity of all present and future rights under copyright law, -whether vested or contingent, in the Work. Dedicator understands that -such relinquishment of all rights includes the relinquishment of all -rights to enforce (by lawsuit or otherwise) those copyrights in the -Work. - -Dedicator recognizes that, once placed in the public domain, the Work -may be freely reproduced, distributed, transmitted, used, modified, -built upon, or otherwise exploited by anyone for any purpose, -commercial or non-commercial, and in any way, including by methods -that have not yet been invented or conceived. - -(This dedication is derived from the text of the `Creative Commons -Public Domain Dedication`. [#]_) - -.. [#] Creative Commons has `retired this legal tool`__ and does not - recommend that it be applied to works: This tool is based on United - States law and may not be applicable outside the US. For dedicating new - works to the public domain, Creative Commons recommend the replacement - Public Domain Dedication CC0_ (CC zero, "No Rights Reserved"). So does - the Free Software Foundation in its license-list_. - - __ http://creativecommons.org/retiredlicenses - .. _CC0: http://creativecommons.org/about/cc0 - -Exceptions -========== - -The exceptions to the `Public Domain Dedication`_ above are: - -* docutils/utils/smartquotes.py - - Copyright © 2011 Günter Milde, - based on `SmartyPants`_ © 2003 John Gruber - (released under a "revised" `BSD 3-Clause License`_ included in the file) - and smartypants.py © 2004, 2007 Chad Miller. - Released under the terms of the `BSD 2-Clause License`_ - (`local copy <licenses/BSD-2-Clause.txt>`__). - - .. _SmartyPants: http://daringfireball.net/projects/smartypants/ - -* docutils/utils/math/latex2mathml.py - - Copyright © Jens Jørgen Mortensen, Günter Milde. - Released under the terms of the `BSD 2-Clause License`_ - (`local copy <licenses/BSD-2-Clause.txt>`__). - -* | docutils/utils/math/math2html.py, - | docutils/writers/html5_polyglot/math.css - - Copyright © 2009,2010 Alex Fernández; 2021 Günter Milde - - These files were part of eLyXer_, released under the `GNU - General Public License`_ version 3 or later. The author relicensed - them for Docutils under the terms of the `BSD 2-Clause License`_ - (`local copy <licenses/BSD-2-Clause.txt>`__). - - .. _eLyXer: https://github.com/alexfernandez/elyxer - -* | docutils/__main__.py, - | docutils/parsers/commonmark_wrapper.py, - | docutils/parsers/recommonmark_wrapper.py, - | docutils/utils/error_reporting.py, - | docutils/utils/math/__init__.py, - | docutils/utils/math/latex2mathml.py, - | docutils/utils/math/tex2mathml_extern.py, - | docutils/utils/punctuation_chars.py, - | docutils/utils/smartquotes.py, - | docutils/writers/html5_polyglot/__init__.py, - | docutils/writers/html5_polyglot/\*.css, - | docutils/writers/latex2e/docutils.sty, - | docutils/writers/xetex/__init__.py, - | test/test_parsers/test_recommonmark/\*.py, - | test/test_parsers/test_rst/test_directives/test__init__.py, - | test/test_parsers/test_rst/test_directives/test_code_parsing.py, - | test/test_parsers/test_rst/test_line_length_limit_default.py, - | test/test_parsers/test_rst/test_line_length_limit.py, - | test/test_writers/test_latex2e_misc.py, - | test/transforms/test_smartquotes.py, - | tools/docutils-cli.py, - | tools/rst2html5.py - - Copyright © Günter Milde. - Released under the terms of the `BSD 2-Clause License`_ - (`local copy <licenses/BSD-2-Clause.txt>`__). - -* docutils/utils/roman.py - - copyright by Mark Pilgrim, released under the - `Zope Public License Version 2.1`_ (`local copy`__). - - __ licenses/ZPL-2-1.txt - -* tools/editors/emacs/rst.el - - copyright by Free Software Foundation, Inc., - released under the `GNU General Public License`_ version 3 or later - (`local copy`__). - - __ licenses/gpl-3-0.txt - -All used licenses are OSI-approved_ and GPL-compatible_. - -Plaintext versions of all the linked-to licenses are provided in the -licenses_ directory. - -.. _sandbox: https://docutils.sourceforge.io/sandbox/README.html -.. _licenses: licenses/ -.. _GNU General Public License: https://www.gnu.org/copyleft/gpl.html -.. _BSD 2-Clause License: http://opensource.org/licenses/BSD-2-Clause -.. _BSD 3-Clause License: https://opensource.org/licenses/BSD-3-Clause -.. _Zope Public License Version 2.1: https://opensource.org/license/zpl-2-1/ -.. _OSI-approved: http://opensource.org/licenses/ -.. _license-list: -.. _GPL-compatible: https://www.gnu.org/licenses/license-list.html Copied: trunk/docutils/FAQ.rst (from rev 9905, trunk/docutils/FAQ.txt) =================================================================== --- trunk/docutils/FAQ.rst (rev 0) +++ trunk/docutils/FAQ.rst 2024-08-15 08:43:38 UTC (rev 9906) @@ -0,0 +1,1339 @@ +.. include:: docs/header0.rst + +.. NOTE TO MAINTAINERS: Please add new questions to the end of their + sections, so section/question numbers remain stable. + +=========================================== + Docutils FAQ (Frequently Asked Questions) +=========================================== + +:Date: $Date$ +:Revision: $Revision$ +:Web site: https://docutils.sourceforge.io/ +:Copyright: This document has been placed in the public domain. + +.. contents:: +.. sectnum:: + + +This is a work in progress. If you are reading a local copy, the +`master copy`_ might be newer. This document uses relative links; +if they don't work, please use the master copy. + +Please feel free to ask questions and/or provide answers; send email +to the `Docutils-users`_ mailing list. Project members should feel +free to edit the source text file directly. + +.. _master copy: https://docutils.sourceforge.io/FAQ.html +.. _let us know: +.. _Docutils-users: docs/user/mailing-lists.html#docutils-users + + + +Docutils +======== + +What is Docutils? +----------------- + +Docutils_ is a system for processing plaintext documentation into +useful formats, such as HTML, XML, and LaTeX. It supports multiple +types of input, such as standalone files, `PEPs (Python Enhancement +Proposals)`_, and string input. Client code may add other input types, +e.g. Sphinx_ comes with an extension to extract inline documentation +from Python modules and packages. + +The Docutils distribution consists of: + +* a library (the "docutils" package), which `can be used by client + code`_; +* several `front-end tools`_ (such as ``rst2html``, which converts + reStructuredText input into HTML output); +* a `test suite`_; and +* documentation_. + +For an overview of the Docutils project implementation, +see :PEP:`258`, "Docutils Design Specification". + +Docutils is implemented in Python_. + +.. _Docutils: https... [truncated message content] |
From: <mi...@us...> - 2024-08-15 16:35:37
|
Revision: 9909 http://sourceforge.net/p/docutils/code/9909 Author: milde Date: 2024-08-15 16:35:34 +0000 (Thu, 15 Aug 2024) Log Message: ----------- Refactor "Include" directive class. Disentangle actions and simplify interface of auxiliary methods. `Include.run()` sets up instance attributes and calls helper functions to read the file content and process it according to the options. (Helper methods depend on setup in `run()`.) Input sanity checks (too long lines and circular inclusions) are only required when when inserting the file content into the "input_lines" of the calling parser (which did these checks on the other "input_lines" before starting to parsing it). Adapt formatting. Adapt tests. Use shorter URL to the "include" directive documentation. Modified Paths: -------------- trunk/docutils/docutils/parsers/rst/directives/misc.py trunk/docutils/test/test_parsers/test_rst/test_directives/test_include.py trunk/docutils/test/test_parsers/test_rst/test_line_length_limit.py Modified: trunk/docutils/docutils/parsers/rst/directives/misc.py =================================================================== --- trunk/docutils/docutils/parsers/rst/directives/misc.py 2024-08-15 14:14:24 UTC (rev 9908) +++ trunk/docutils/docutils/parsers/rst/directives/misc.py 2024-08-15 16:35:34 UTC (rev 9909) @@ -9,6 +9,7 @@ __docformat__ = 'reStructuredText' import re +import sys import time from pathlib import Path from typing import TYPE_CHECKING @@ -23,11 +24,21 @@ if TYPE_CHECKING: import os + if sys.version_info[:2] >= (3, 12): + from typing import TypeAlias + else: + from typing_extensions import TypeAlias from docutils.nodes import Node + PathString: TypeAlias = str | os.PathLike[str] + """File system path.""" -def adapt_path(path, source='', root_prefix='/'): + SourceString: TypeAlias = str | os.PathLike[str] + """Path to or informal description of a Docutils input source.""" + + +def adapt_path(path: str, source='', root_prefix='/') -> str: # Adapt path to files to include or embed. # `root_prefix` is prepended to absolute paths (cf. root_prefix setting), # `source` is the `current_source` of the including directive (which may @@ -52,7 +63,7 @@ start and end line or text to match before and/or after the text to be used. - https://docutils.sourceforge.io/docs/ref/rst/directives.html#including-an-external-document-fragment + https://docutils.sourceforge.io/docs/ref/rst/directives.html#include """ required_arguments = 1 @@ -68,7 +79,7 @@ 'start-after': directives.unchanged_required, 'end-before': directives.unchanged_required, # ignored except for 'literal' or 'code': - 'number-lines': directives.unchanged, # integer or None + 'number-lines': directives.value_or((None,), int), 'class': directives.class_option, 'name': directives.unchanged} @@ -77,14 +88,17 @@ def run(self) -> list[Node]: """Include a file as part of the content of this reST file. - Depending on the options, the file (or a clipping) is + Depending on the options, the file content (or a clipping) is converted to nodes and returned or inserted into the input stream. """ - settings = self.state.document.settings + self.settings = settings = self.state.document.settings if not settings.file_insertion_enabled: raise self.warning('"%s" directive disabled.' % self.name) - tab_width = self.options.get('tab-width', settings.tab_width) - current_source = self.state.document.current_source + self.tab_width = self.options.get('tab-width', settings.tab_width) + self.clip_options = (self.options.get('start-line', None), + self.options.get('end-line', None), + self.options.get('start-after', ''), + self.options.get('end-before', '')) path = directives.path(self.arguments[0]) if path.startswith('<') and path.endswith('>'): path = '/' + path[1:-1] @@ -91,152 +105,92 @@ root_prefix = self.standard_include_path else: root_prefix = settings.root_prefix - path = adapt_path(path, current_source, root_prefix) - rawtext, clip_options = self._read_source(path) - include_lines = statemachine.string2lines(rawtext, tab_width, - convert_whitespace=True) - for i, line in enumerate(include_lines): - if len(line) > settings.line_length_limit: - raise self.warning('"%s": line %d exceeds the' - ' line-length-limit.' % (path, i+1)) + path = adapt_path(path, + self.state.document.current_source, + root_prefix) + self.options['source'] = path + inputstring = self.read_file(path) + if 'literal' in self.options: - return self.include_literal_block( - source=path, - rawtext=rawtext, - tab_width=tab_width, - include_lines=include_lines, - ) - + return self.as_literal_block(inputstring) if 'code' in self.options: - return self.include_code_block( - source=path, - rawtext=rawtext, - tab_width=tab_width, - include_lines=include_lines, - ) - - # Prevent circular inclusion: - include_log = self.state.document.include_log - # log entries are tuples (<source>, <clip-options>) - if not include_log: # new document, initialize with document source - include_log.append((utils.relative_path(None, current_source), - (None, None, None, None))) - if (path, clip_options) in include_log: - master_paths = (pth for (pth, opt) in reversed(include_log)) - inclusion_chain = '\n> '.join((path, *master_paths)) - raise self.warning('circular inclusion in "%s" directive:\n%s' - % (self.name, inclusion_chain)) - + return self.as_code_block(inputstring) if 'parser' in self.options: - return self.include_parsed( - source=path, - include_lines=include_lines, - clip_options=clip_options, - ) - - # Include as rST source: - self.include_text( - rawtext, - source=path, - tab_width=tab_width, - include_lines=include_lines, - ) - # update include-log - include_log.append((path, clip_options)) + return self.custom_parse(inputstring) + self.insert_into_input_lines(inputstring) return [] - def _read_source( - self, source: str | os.PathLike[str], /, - ) -> tuple[str, tuple[int | None, int | None, str | None, str | None]]: - """Read and clip the content of *source*.""" - settings = self.state.document.settings - encoding = self.options.get('encoding', settings.input_encoding) - error_handler = settings.input_encoding_error_handler + def read_file(self, path: PathString) -> str: + """Read text file at `path`. Clip and return content. + + Provisional. + """ + encoding = self.options.get('encoding', self.settings.input_encoding) + error_handler = self.settings.input_encoding_error_handler try: - include_file = io.FileInput( - source_path=source, - encoding=encoding, - error_handler=error_handler, - ) + include_file = io.FileInput(source_path=path, + encoding=encoding, + error_handler=error_handler) except UnicodeEncodeError: raise self.severe(f'Problems with "{self.name}" directive path:\n' - f'Cannot encode input file path "{source}" ' + f'Cannot encode input file path "{path}" ' '(wrong locale?).') except OSError as error: - raise self.severe(f'Problems with "{self.name}" directive ' - f'path:\n{io.error_string(error)}.') + raise self.severe(f'Problems with "{self.name}" directive path:\n' + f'{io.error_string(error)}.') else: - settings.record_dependencies.add(source) - - # Get to-be-included content - startline = self.options.get('start-line', None) - endline = self.options.get('end-line', None) + self.settings.record_dependencies.add(path) try: - if startline or (endline is not None): - lines = include_file.readlines() - rawtext = ''.join(lines[startline:endline]) - else: - rawtext = include_file.read() + text = include_file.read() except UnicodeError as error: raise self.severe(f'Problem with "{self.name}" directive:\n' + io.error_string(error)) + # Clip to-be-included content + startline, endline, starttext, endtext = self.clip_options + if startline or (endline is not None): + lines = text.splitlines() + text = '\n'.join(lines[startline:endline]) # start-after/end-before: no restrictions on newlines in match-text, # and no restrictions on matching inside lines vs. line boundaries - after_text = self.options.get('start-after', None) - if after_text: - # skip content in rawtext before *and incl.* a matching text - after_index = rawtext.find(after_text) + if starttext: + # skip content in text before *and incl.* a matching text + after_index = text.find(starttext) if after_index < 0: - raise self.severe('Problem with "start-after" option of "%s" ' - 'directive:\nText not found.' % self.name) - rawtext = rawtext[after_index + len(after_text):] - before_text = self.options.get('end-before', None) - if before_text: - # skip content in rawtext after *and incl.* a matching text - before_index = rawtext.find(before_text) + raise self.severe('Problem with "start-after" option of ' + f'"{self.name}" directive:\nText not found.') + text = text[after_index + len(starttext):] + if endtext: + # skip content in text after *and incl.* a matching text + before_index = text.find(endtext) if before_index < 0: - raise self.severe('Problem with "end-before" option of "%s" ' - 'directive:\nText not found.' % self.name) - rawtext = rawtext[:before_index] + raise self.severe('Problem with "end-before" option of ' + f'"{self.name}" directive:\nText not found.') + text = text[:before_index] + return text - clip_options = (startline, endline, before_text, after_text) - return rawtext, clip_options + def as_literal_block(self, text: str) -> list[nodes.literal_block]: + """Return list with literal_block containing `text`. - def include_literal_block( - self, - *, - source: str | os.PathLike[str], - rawtext: str, - include_lines: list[str], - tab_width: int, - ) -> list[nodes.literal_block]: - """Return the content from *source* as a literal block.""" - # Don't convert tabs to spaces, if `tab_width` is negative. - if tab_width >= 0: - text = rawtext.expandtabs(tab_width) - else: - text = rawtext + Provisional + """ + source = self.options['source'] + # Convert tabs to spaces unless `tab_width` is negative. + if self.tab_width >= 0: + text = text.expandtabs(self.tab_width) literal_block = nodes.literal_block( - rawtext, source=source, - classes=self.options.get('class', [])) - literal_block.line = 1 + '', source=source, classes=self.options.get('class', [])) + literal_block.source = source + literal_block.line = self.options.get('start-line', 0) + 1 self.add_name(literal_block) if 'number-lines' in self.options: - try: - startline = int(self.options['number-lines'] or 1) - except ValueError: - msg = ':number-lines: with non-integer start value' - raise self.error(msg) - endline = startline + len(include_lines) - if text.endswith('\n'): - text = text[:-1] - tokens = NumberLines([([], text)], startline, endline) + firstline = self.options['number-lines'] or 1 + text = text.removesuffix('\n') + lastline = firstline + len(text.splitlines()) + tokens = NumberLines([([], text)], firstline, lastline) for classes, value in tokens: if classes: - literal_block += nodes.inline( - value, value, classes=classes, - ) + literal_block += nodes.inline('', value, classes=classes) else: literal_block += nodes.Text(value) else: @@ -243,50 +197,41 @@ literal_block += nodes.Text(text) return [literal_block] - def include_code_block( - self, - *, - source: str | os.PathLike[str], - rawtext: str, - include_lines: list[str], - tab_width: int, - ) -> list[nodes.literal_block]: - """Return the content from *source* as a code block.""" - self.options['source'] = source - # Don't convert tabs to spaces, if `tab_width` is negative: - if tab_width < 0: - include_lines = rawtext.splitlines() - codeblock = CodeBlock( - self.name, - [self.options.pop('code')], # arguments - self.options, - include_lines, # content - self.lineno, - self.content_offset, - self.block_text, - self.state, - self.state_machine, - ) + def as_code_block(self, text: str) -> list[nodes.literal_block]: + """Pass `text` to the `CodeBlock` directive class. + + Provisional. + """ + # convert tabs to spaces unless `tab_width` is negative: + if self.tab_width >= 0: + text = text.expandtabs(self.tab_width) + codeblock = CodeBlock(self.name, + [self.options.pop('code')], # pass as argument + self.options, + [text.removesuffix('\n')], # content + self.lineno, + self.content_offset, + self.block_text, + self.state, + self.state_machine, + ) return codeblock.run() - def include_parsed( - self, - *, - source: str | os.PathLike[str], - include_lines: list[str], - clip_options: tuple[int | str, int | str, str, str], - ) -> list[Node]: - """Return content from *source* after parsing with the given parser.""" - settings = self.state.document.settings - include_log = self.state.document.include_log + def custom_parse(self, text: str) -> list[Node]: + """Parse with custom parser. - # parse into a dummy document and return created nodes - _settings = settings.copy() - _settings._source = source - document = utils.new_document(source, _settings) - document.include_log = include_log + [(source, clip_options)] + Parse with ``self.options['parser']`` into a new (dummy) document, + apply the parser's default transforms, + return child elements. + + Provisional. + """ + settings = self.settings.copy() + settings._source = self.options['source'] + document = utils.new_document(settings._source, settings) + document.include_log = self.state.document.include_log parser = self.options['parser']() - parser.parse('\n'.join(include_lines), document) + parser.parse(text, document) self.state.document.parse_messages.extend(document.parse_messages) # clean up doctree and complete parsing document.transformer.populate_from_components((parser,)) @@ -295,26 +240,42 @@ document.transform_messages) return document.children - def include_text( - self, - text: str, - *, - source: str | os.PathLike[str], - tab_width: int, - include_lines: list[str] | None = None, - ) -> None: - """Insert *text* into the input stream.""" - # *text* is passed for use in subclasses, but if we have already - # split the lines and nothing is changing, reuse those lines. - if include_lines is not None: - include_lines = statemachine.string2lines( - text, tab_width, convert_whitespace=True, - ) - # Mark end (cf. parsers.rst.states.Body.comment()) - include_lines += ['', '.. end of inclusion from "%s"' % source] - self.state_machine.insert_input(include_lines, source) + def insert_into_input_lines(self, text: str) -> None: + """Insert file content into the rST input of the calling parser. + Returns an empty list to comply with the API of `Directive.run()`. + Provisional. + """ + source = self.options['source'] + textlines = statemachine.string2lines(text, self.tab_width, + convert_whitespace=True) + # Sanity checks: + # excessively long lines + for i, line in enumerate(textlines): + if len(line) > self.settings.line_length_limit: + line_no = i + 1 + self.options.get('start-line', 0) + raise self.warning(f'"{source}": line {line_no} exceeds the' + ' line-length-limit.') + # circular inclusion + include_log = self.state.document.include_log + if not include_log: # new document, initialize with document source + current_source = utils.relative_path( + None, self.state.document.current_source) + include_log.append((current_source, (None, None, '', ''))) + if (source, self.clip_options) in include_log: + source_chain = (pth for (pth, opt) in reversed(include_log)) + inclusion_chain = '\n> '.join((source, *source_chain)) + raise self.warning(f'circular inclusion in "{self.name}"' + f' directive:\n{inclusion_chain}') + include_log.append((source, self.clip_options)) + # marker for removing log entry (cf. parsers.rst.states.Body.comment()) + textlines += ['', f'.. end of inclusion from "{source}"'] + + self.state_machine.insert_input(textlines, source) + # TODO: if startline != 0, line numbers are wrong. + + class Raw(Directive): """ Modified: trunk/docutils/test/test_parsers/test_rst/test_directives/test_include.py =================================================================== --- trunk/docutils/test/test_parsers/test_rst/test_directives/test_include.py 2024-08-15 14:14:24 UTC (rev 9908) +++ trunk/docutils/test/test_parsers/test_rst/test_directives/test_include.py 2024-08-15 16:35:34 UTC (rev 9909) @@ -1457,7 +1457,6 @@ {include15} > {include16} > {include15} - > test data <literal_block xml:space="preserve"> .. include:: include15.rst <paragraph> Modified: trunk/docutils/test/test_parsers/test_rst/test_line_length_limit.py =================================================================== --- trunk/docutils/test/test_parsers/test_rst/test_line_length_limit.py 2024-08-15 14:14:24 UTC (rev 9908) +++ trunk/docutils/test/test_parsers/test_rst/test_line_length_limit.py 2024-08-15 16:35:34 UTC (rev 9909) @@ -35,6 +35,8 @@ class ParserTestCase(unittest.TestCase): + maxDiff = None + def test_parser(self): parser = Parser() settings = get_default_settings(Parser) @@ -77,7 +79,6 @@ ============ .. include:: {docutils_conf} - :literal: A paragraph. """, @@ -91,10 +92,30 @@ "{docutils_conf}": line 5 exceeds the line-length-limit. <literal_block xml:space="preserve"> .. include:: {docutils_conf} - :literal: <paragraph> A paragraph. """], +[f"""\ +Include Test 2 + +.. include:: {docutils_conf} + :start-line: 3 + +A paragraph. +""", +f"""\ +<document source="test data"> + <paragraph> + Include Test 2 + <system_message level="2" line="3" source="test data" type="WARNING"> + <paragraph> + "{docutils_conf}": line 5 exceeds the line-length-limit. + <literal_block xml:space="preserve"> + .. include:: {docutils_conf} + :start-line: 3 + <paragraph> + A paragraph. +"""], ] This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |
From: <mi...@us...> - 2024-08-21 13:44:40
|
Revision: 9912 http://sourceforge.net/p/docutils/code/9912 Author: milde Date: 2024-08-21 13:44:37 +0000 (Wed, 21 Aug 2024) Log Message: ----------- HTML writer: Fix part "html_head" if "output_encoding" setting is 'unicode'. If the output encoding is not determined, part "head" does not contain a "charset" `<meta>` element. So we must not drop the first element from "head" when completing "html_head". Fixup for [r9240]. Modified Paths: -------------- trunk/docutils/docutils/writers/_html_base.py trunk/docutils/test/test_writers/test_html5_polyglot_parts.py Modified: trunk/docutils/docutils/writers/_html_base.py =================================================================== --- trunk/docutils/docutils/writers/_html_base.py 2024-08-21 08:05:47 UTC (rev 9911) +++ trunk/docutils/docutils/writers/_html_base.py 2024-08-21 13:44:37 UTC (rev 9912) @@ -963,8 +963,12 @@ self.head.extend(self.math_header) else: self.stylesheet.extend(self.math_header) - # skip content-type meta tag with interpolated charset value: - self.html_head.extend(self.head[1:]) + if (self.settings.output_encoding # may be None + and self.settings.output_encoding.lower() != 'unicode'): + # skip content-type meta tag with interpolated charset value: + self.html_head.extend(self.head[1:]) + else: + self.html_head.extend(self.head) self.body_prefix.append(self.starttag(node, **self.documenttag_args)) self.body_suffix.insert(0, f'</{self.documenttag_args["tagname"]}>\n') self.fragment.extend(self.body) # self.fragment is the "naked" body Modified: trunk/docutils/test/test_writers/test_html5_polyglot_parts.py =================================================================== --- trunk/docutils/test/test_writers/test_html5_polyglot_parts.py 2024-08-21 08:05:47 UTC (rev 9911) +++ trunk/docutils/test/test_writers/test_html5_polyglot_parts.py 2024-08-21 13:44:37 UTC (rev 9912) @@ -84,7 +84,7 @@ settings_overrides={ '_disable_config': True, 'strict_visitor': True, - 'stylesheet': '', + 'stylesheet_path': '', 'section_self_link': True, **settings_overrides, } @@ -143,8 +143,7 @@ totest = {} -totest['standard'] = ({'stylesheet_path': '', - 'embed_stylesheet': False}, [ +totest['standard'] = ({}, [ ["""\ Simple String """, @@ -288,12 +287,22 @@ }], ]) -totest['no_title_promotion'] = ({'doctitle_xform': False, - 'stylesheet_path': '', - 'embed_stylesheet': False}, [ +totest['standard'] = ({'output_encoding': 'unicode'}, [ ["""\ Simple String """, +{'fragment': '<p>Simple String</p>\n', + 'meta': """\ +<meta name="generator" content="Docutils 0.22b.dev: https://docutils.sourceforge.io/" /> +<meta name="viewport" content="width=device-width, initial-scale=1" /> +""", +}], +]) + +totest['no_title_promotion'] = ({'doctitle_xform': False}, [ +["""\ +Simple String +""", '<p>Simple String</p>\n', ], ["""\ @@ -574,8 +583,6 @@ totest['lazy_loading'] = ({'image_loading': 'lazy', - 'stylesheet_path': '', - 'embed_stylesheet': False, 'report_level': 4}, [ ["""\ Lazy loading by default, overridden by :loading: option @@ -606,9 +613,7 @@ totest['root_prefix'] = ({'root_prefix': ROOT_PREFIX, 'image_loading': 'embed', - 'stylesheet_path': '', 'warning_stream': '', - 'embed_stylesheet': False }, [ ["""\ .. image:: /data/blue%20square.png @@ -630,9 +635,7 @@ ]) -totest['no_backlinks'] = ({'footnote_backlinks': False, - 'stylesheet_path': '', - 'embed_stylesheet': False}, [ +totest['no_backlinks'] = ({'footnote_backlinks': False}, [ ["""\ Two footnotes [#f1]_ [#f2]_ and two citations [once]_ [twice]_. @@ -673,7 +676,6 @@ totest['syntax_highlight'] = ({'syntax_highlight': 'short', - 'stylesheet_path': '', }, [ ["""\ .. code:: shell @@ -701,9 +703,7 @@ ]) -totest['system_messages'] = ({'stylesheet_path': '', - 'embed_stylesheet': False, - 'math_output': 'mathml', +totest['system_messages'] = ({'math_output': 'mathml', 'warning_stream': '', }, [ ["""\ @@ -748,9 +748,7 @@ """], ]) -totest['system_messages-PIL'] = ({'stylesheet_path': '', - 'embed_stylesheet': False, - 'math_output': 'mathml', +totest['system_messages-PIL'] = ({'math_output': 'mathml', 'warning_stream': '', }, [ ["""\ @@ -816,9 +814,7 @@ ], ]) -totest['no_system_messages'] = ({'stylesheet_path': '', - 'embed_stylesheet': False, - 'math_output': 'mathml', +totest['no_system_messages'] = ({'math_output': 'mathml', 'report_level': 4, 'warning_stream': '', }, [ This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |
From: <mi...@us...> - 2024-08-21 13:44:51
|
Revision: 9913 http://sourceforge.net/p/docutils/code/9913 Author: milde Date: 2024-08-21 13:44:48 +0000 (Wed, 21 Aug 2024) Log Message: ----------- Refactor HTML5 writer tests. New test script "test_html5_polyglot.py": Split basic output tests (which happen to use the "fragment" returned by `publish_parts()` to get the interesting part of the output) from other tests of `publish_parts()` Really test all returned parts in "test_html5_polyglot_parts.py". Fill in parts that are built out of other parts in a helper function instead of pruning parts. Add test cases for input that goes to special parts. Disable default style sheet ("stylesheet_path" setting) by for tests not requiring a stylesheet. Modified Paths: -------------- trunk/docutils/docs/api/publisher.rst trunk/docutils/test/test_writers/test_html5_polyglot_misc.py trunk/docutils/test/test_writers/test_html5_polyglot_parts.py Added Paths: ----------- trunk/docutils/test/test_writers/test_html5_polyglot.py Modified: trunk/docutils/docs/api/publisher.rst =================================================================== --- trunk/docutils/docs/api/publisher.rst 2024-08-21 13:44:37 UTC (rev 9912) +++ trunk/docutils/docs/api/publisher.rst 2024-08-21 13:44:48 UTC (rev 9913) @@ -603,7 +603,7 @@ Default: "restructuredtext". -_`writer` : str | docutils.parsers.Writer +_`writer` : str | docutils.writers.Writer `Writer component name`_ or instance. [#component-names]_ Default: "pseudoxml". Copied: trunk/docutils/test/test_writers/test_html5_polyglot.py (from rev 9912, trunk/docutils/test/test_writers/test_html5_polyglot_parts.py) =================================================================== --- trunk/docutils/test/test_writers/test_html5_polyglot.py (rev 0) +++ trunk/docutils/test/test_writers/test_html5_polyglot.py 2024-08-21 13:44:48 UTC (rev 9913) @@ -0,0 +1,672 @@ +#! /usr/bin/env python3 + +# $Id$ +# Author: reggie dugard <re...@us...> +# Copyright: This module has been placed in the public domain. + +"""Test HTML5 writer output ("fragment" part). + +This is the document body (not HTML <body>). +""" + +from pathlib import Path +import re +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])) + +import docutils +import docutils.core +from docutils.parsers.rst.directives.images import PIL +from docutils.utils.code_analyzer import with_pygments + +if with_pygments: + import pygments + _pv = re.match(r'^([0-9]+)\.([0-9]*)', pygments.__version__) + if (int(_pv[1]), int(_pv[2])) >= (2, 14): + # pygments output changed in version 2.14 + with_pygments = False + +# TEST_ROOT is ./test/ from the docutils root +TEST_ROOT = Path(__file__).parents[1] +DATA_ROOT = TEST_ROOT / 'data' +ROOT_PREFIX = (TEST_ROOT / 'functional/input').as_posix() + +# Pillow/PIL is optional: +if PIL: + REQUIRES_PIL = '' + ONLY_LOCAL = 'Can only read local images.' + DUMMY_PNG_NOT_FOUND = "[Errno 2] No such file or directory: 'dummy.png'" + # Pillow reports the absolute path since version 10.3.0 (cf. [bugs: 485]) + 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: 32.0px; height: 32.0px;" ' + 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 = '' + NO_PIL_SYSTEM_MESSAGE = ( + '<aside class="system-message">\n' + '<p class="system-message-title">System Message:' + ' WARNING/2 (<span class="docutils literal">' + '<string></span>, line 1)</p>\n' + '<p>Cannot scale image!\n' + ' Could not get size from "/data/blue%20square.png":\n' + ' Requires Python Imaging Library.</p>\n' + '</aside>\n') + + +class Html5WriterPublishPartsTestCase(unittest.TestCase): + """Test case for HTML5 writer via the publish_parts() interface.""" + + maxDiff = None + + def test_publish(self): + if not with_pygments: + del totest['syntax_highlight'] + writer = 'html5' + for name, (settings_overrides, cases) in totest.items(): + for casenum, (case_input, case_expected) in enumerate(cases): + with self.subTest(id=f'totest[{name!r}][{casenum}]'): + parts = docutils.core.publish_parts( + source=case_input, + writer=writer, + settings_overrides={ + '_disable_config': True, + 'strict_visitor': True, + 'stylesheet_path': '', + 'section_self_link': True, + **settings_overrides, + } + ) + self.assertEqual(case_expected, parts['body']) + + +totest = {} + +totest['standard'] = ({}, [ +["""\ +Simple String +""", +'<p>Simple String</p>\n', +], +["""\ +Simple String with *markup* +""", +'<p>Simple String with <em>markup</em></p>\n', +], +["""\ +Simple String with an even simpler ``inline literal`` +""", +'<p>Simple String with an even simpler <span class="docutils literal">inline literal</span></p>\n', +], +["""\ +A simple `anonymous reference`__ + +__ http://www.test.com/test_url +""", +'<p>A simple <a class="reference external" href="http://www.test.com/test_url">anonymous reference</a></p>\n', +], +["""\ +One paragraph. + +Two paragraphs. +""", +"""\ +<p>One paragraph.</p> +<p>Two paragraphs.</p> +""", +], +["""\ +A simple `named reference`_ with stuff in between the +reference and the target. + +.. _`named reference`: http://www.test.com/test_url +""", +"""\ +<p>A simple <a class="reference external" href="http://www.test.com/test_url">named reference</a> with stuff in between the +reference and the target.</p> +""", +], +["""\ +.. [CIT2022] A citation. +""", +"""\ +<div role="list" class="citation-list"> +<div class="citation" id="cit2022" role="doc-biblioentry"> +<span class="label"><span class="fn-bracket">[</span>CIT2022<span class="fn-bracket">]</span></span> +<p>A citation.</p> +</div> +</div> +""", +], +]) + + +totest['no_title_promotion'] = ({'doctitle_xform': False}, [ +["""\ ++++++ +Title ++++++ + +Not A Subtitle +============== + +Some stuff + +Section +------- + +Some more stuff + +Another Section +............... + +And even more stuff +""", +"""\ +<section id="title"> +<h2>Title<a class="self-link" title="link to this section" href="#title"></a></h2> +<section id="not-a-subtitle"> +<h3>Not A Subtitle<a class="self-link" title="link to this section" href="#not-a-subtitle"></a></h3> +<p>Some stuff</p> +<section id="section"> +<h4>Section<a class="self-link" title="link to this section" href="#section"></a></h4> +<p>Some more stuff</p> +<section id="another-section"> +<h5>Another Section<a class="self-link" title="link to this section" href="#another-section"></a></h5> +<p>And even more stuff</p> +</section> +</section> +</section> +</section> +""", +], +["""\ +* bullet +* list +""", +"""\ +<ul class="simple"> +<li><p>bullet</p></li> +<li><p>list</p></li> +</ul> +""", +], +["""\ +.. table:: + :align: right + + +-----+-----+ + | 1 | 2 | + +-----+-----+ + | 3 | 4 | + +-----+-----+ +""", +"""\ +<table class="align-right"> +<tbody> +<tr><td><p>1</p></td> +<td><p>2</p></td> +</tr> +<tr><td><p>3</p></td> +<td><p>4</p></td> +</tr> +</tbody> +</table> +""", +], +["""\ +Not a docinfo. + +:This: .. _target: + + is +:a: +:simple: +:field: list +""", +"""\ +<p>Not a docinfo.</p> +<dl class="field-list simple"> +<dt>This<span class="colon">:</span></dt> +<dd><p id="target">is</p> +</dd> +<dt>a<span class="colon">:</span></dt> +<dd><p></p></dd> +<dt>simple<span class="colon">:</span></dt> +<dd><p></p></dd> +<dt>field<span class="colon">:</span></dt> +<dd><p>list</p> +</dd> +</dl> +""", +], +["""\ +Not a docinfo. + +:This is: a +:simple field list with loooong field: names +""", +"""\ +<p>Not a docinfo.</p> +<dl class="field-list simple"> +<dt>This is<span class="colon">:</span></dt> +<dd><p>a</p> +</dd> +<dt>simple field list with loooong field<span class="colon">:</span></dt> +<dd><p>names</p> +</dd> +</dl> +""", +], +["""\ +Not a docinfo. + +.. class:: field-indent-200 + +:This: is a +:simple: field list with custom indent. +""", +"""\ +<p>Not a docinfo.</p> +<dl class="field-list simple" style="--field-indent: 200px;"> +<dt>This<span class="colon">:</span></dt> +<dd><p>is a</p> +</dd> +<dt>simple<span class="colon">:</span></dt> +<dd><p>field list with custom indent.</p> +</dd> +</dl> +""", +], +["""\ +Not a docinfo. + +.. class:: field-indent-200uf + +:This: is a +:simple: field list without custom indent, + because the unit "uf" is invalid. +""", +"""\ +<p>Not a docinfo.</p> +<dl class="field-indent-200uf field-list simple"> +<dt>This<span class="colon">:</span></dt> +<dd><p>is a</p> +</dd> +<dt>simple<span class="colon">:</span></dt> +<dd><p>field list without custom indent, +because the unit "uf" is invalid.</p> +</dd> +</dl> +""", +], +["""\ +.. figure:: dummy.png + + The figure's caption. + + A legend. + + The legend's second paragraph. +""", +"""\ +<figure> +<img alt="dummy.png" src="dummy.png" /> +<figcaption> +<p>The figure's caption.</p> +<div class="legend"> +<p>A legend.</p> +<p>The legend's second paragraph.</p> +</div> +</figcaption> +</figure> +""", +], +["""\ +.. figure:: dummy.png + + The figure's caption, no legend. +""", +"""\ +<figure> +<img alt="dummy.png" src="dummy.png" /> +<figcaption> +<p>The figure's caption, no legend.</p> +</figcaption> +</figure> +""", +], +["""\ +.. figure:: dummy.png + + .. + + A legend without caption. +""", +"""\ +<figure> +<img alt="dummy.png" src="dummy.png" /> +<figcaption> +<div class="legend"> +<p>A legend without caption.</p> +</div> +</figcaption> +</figure> +""", +], +["""\ +.. figure:: dummy.png + +No caption nor legend. +""", +"""\ +<figure> +<img alt="dummy.png" src="dummy.png" /> +</figure> +<p>No caption nor legend.</p> +""", +], +[f"""\ +.. include:: {DATA_ROOT}/multiple-term-definition.xml + :parser: xml +""", +"""\ +<dl> +<dt>New in Docutils 0.22</dt> +<dd><p>A definition list item may contain several +terms with optional classifier(s).</p> +<p>However, there is currently no corresponding +reStructuredText syntax.</p> +</dd> +<dt>term 2a</dt> +<dt>term 2b</dt> +<dd><p>definition 2</p> +</dd> +<dt>term 3a<span class="classifier">classifier 3a</span><span class="classifier">classifier 3aa</span><dt>term 3b<span class="classifier">classifier 3b</span></dt> +<dd><p>definition 3</p> +</dd> +</dl> +""", +], +]) + + +totest['lazy_loading'] = ({'image_loading': 'lazy', + 'report_level': 4}, [ +["""\ +Lazy loading by default, overridden by :loading: option +("cannot embed" warning ignored). + +.. image:: dummy.png +.. image:: dummy.png + :loading: link +.. figure:: dummy.png +.. figure:: dummy.png + :loading: embed +""", +"""\ +<p>Lazy loading by default, overridden by :loading: option +("cannot embed" warning ignored).</p> +<img alt="dummy.png" loading="lazy" src="dummy.png" /> +<img alt="dummy.png" src="dummy.png" /> +<figure> +<img alt="dummy.png" loading="lazy" src="dummy.png" /> +</figure> +<figure> +<img alt="dummy.png" src="dummy.png" /> +</figure> +""", +], +]) + + +totest['root_prefix'] = ({'root_prefix': ROOT_PREFIX, + 'image_loading': 'embed', + 'warning_stream': '', + }, [ +["""\ +.. image:: /data/blue%20square.png + :scale: 100% +.. figure:: /data/blue%20square.png +""", +'<img alt="/data/blue%20square.png" src="data:image/png;base64,' +'iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAIAAAD8GO2jAAAALElEQVR4nO3NMQ' +'EAMAjAsDFjvIhHFCbgSwU0kdXvsn96BwAAAAAAAAAAAIsNnEwBk52VRuMAAAAA' +'SUVORK5CYII="' +f' {SCALING_OUTPUT}/>\n{NO_PIL_SYSTEM_MESSAGE}' +'<figure>\n' +'<img alt="/data/blue%20square.png" src="data:image/png;base64,' +'iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAIAAAD8GO2jAAAALElEQVR4nO3NMQ' +'EAMAjAsDFjvIhHFCbgSwU0kdXvsn96BwAAAAAAAAAAAIsNnEwBk52VRuMAAAAA' +'SUVORK5CYII=" />\n' +'</figure>\n', +], +]) + + +totest['no_backlinks'] = ({'footnote_backlinks': False}, [ + +["""\ +Two footnotes [#f1]_ [#f2]_ and two citations [once]_ [twice]_. + +The latter are referenced a second time [#f2]_ [twice]_. + +.. [#f1] referenced once +.. [#f2] referenced twice +.. [once] citation referenced once +.. [twice] citation referenced twice +""", +"""\ +<p>Two footnotes <a class="brackets" href="#f1" id="footnote-reference-1" role="doc-noteref"><span class="fn-bracket">[</span>1<span class="fn-bracket">]</span></a> <a class="brackets" href="#f2" id="footnote-reference-2" role="doc-noteref"><span class="fn-bracket">[</span>2<span class="fn-bracket">]</span></a> and two citations <a class="citation-reference" href="#once" id="citation-reference-1" role="doc-biblioref">[once]</a> <a class="citation-reference" href="#twice" id="citation-reference-2" role="doc-biblioref">[twice]</a>.</p> +<p>The latter are referenced a second time <a class="brackets" href="#f2" id="footnote-reference-3" role="doc-noteref"><span class="fn-bracket">[</span>2<span class="fn-bracket">]</span></a> <a class="citation-reference" href="#twice" id="citation-reference-3" role="doc-biblioref">[twice]</a>.</p> +<aside class="footnote-list brackets"> +<aside class="footnote brackets" id="f1" role="doc-footnote"> +<span class="label"><span class="fn-bracket">[</span>1<span class="fn-bracket">]</span></span> +<p>referenced once</p> +</aside> +<aside class="footnote brackets" id="f2" role="doc-footnote"> +<span class="label"><span class="fn-bracket">[</span>2<span class="fn-bracket">]</span></span> +<p>referenced twice</p> +</aside> +</aside> +<div role="list" class="citation-list"> +<div class="citation" id="once" role="doc-biblioentry"> +<span class="label"><span class="fn-bracket">[</span>once<span class="fn-bracket">]</span></span> +<p>citation referenced once</p> +</div> +<div class="citation" id="twice" role="doc-biblioentry"> +<span class="label"><span class="fn-bracket">[</span>twice<span class="fn-bracket">]</span></span> +<p>citation referenced twice</p> +</div> +</div> +""", +], +]) + + +totest['syntax_highlight'] = ({'syntax_highlight': 'short', + }, [ +["""\ +.. code:: shell + + cat <<EOF + Hello World + EOF +""", +"""\ +<pre class="code shell literal-block"><code>cat <span class="s"><<EOF +Hello World +EOF</span></code></pre> +""", +], +["""\ +.. role:: shell(code) + :language: shell + +:shell:`cat <<EOF Hello World EOF` +""", +"""\ +<p><code class="shell">cat <span class="s"><<EOF Hello World EOF</span></code></p> +""", +], +]) + + +totest['system_messages'] = ({'math_output': 'mathml', + 'warning_stream': '', + }, [ +["""\ +.. image:: https://dummy.png + :loading: embed +""", +"""\ +<img alt="https://dummy.png" src="https://dummy.png" /> +<aside class="system-message"> +<p class="system-message-title">System Message: ERROR/3 \ +(<span class="docutils literal"><string></span>, line 1)</p> +<p>Cannot embed image "https://dummy.png": + Can only read local images.</p> +</aside> +""", +], +[f"""\ +.. image:: {DATA_ROOT.as_uri()}/circle-broken.svg + :loading: embed +""", +f"""\ +<svg xmlns="http://www.w3.org/2000/svg" + viewBox="0 0 10 10"> + <circle cx="5" cy="5" r="4" fill="lightblue" x/> +</svg> + +<aside class="system-message"> +<p class="system-message-title">System Message: ERROR/3 (<span class="docutils literal"><string></span>, line 1)</p> +<p>Cannot parse SVG image "{DATA_ROOT.as_uri()}/circle-broken.svg": + not well-formed (invalid token): line 3, column 48</p> +</aside> +""" +], +[r"""Broken :math:`\sin \my`. +""", +"""\ +<p>Broken <span class="math problematic">\\sin \\my</span>.</p> +<aside class="system-message"> +<p class="system-message-title">System Message: WARNING/2 (<span class="docutils literal"><string></span>, line 1)</p> +<p>Unknown LaTeX command "\\my".</p> +</aside> +"""], +]) + +totest['system_messages-PIL'] = ({'math_output': 'mathml', + 'warning_stream': '', + }, [ +["""\ +.. image:: dummy.png + :scale: 100% + :loading: embed +""", +f"""\ +<img alt="dummy.png" src="dummy.png" /> +<aside class="system-message"> +<p class="system-message-title">System Message: WARNING/2 \ +(<span class="docutils literal"><string></span>, line 1)</p> +<p>Cannot scale image! + Could not get size from "dummy.png": + {DUMMY_PNG_NOT_FOUND}</p> +</aside> +<aside class="system-message"> +<p class="system-message-title">System Message: ERROR/3 \ +(<span class="docutils literal"><string></span>, line 1)</p> +<p>Cannot embed image "dummy.png": + [Errno 2] No such file or directory: 'dummy.png'</p> +</aside> +""", +], +["""\ +.. image:: dummy.mp4 + :scale: 100% +""", +f"""\ +<video src="dummy.mp4" title="dummy.mp4"> +<a href="dummy.mp4">dummy.mp4</a> +</video> +<aside class="system-message"> +<p class="system-message-title">System Message: WARNING/2 \ +(<span class="docutils literal"><string></span>, line 1)</p> +<p>Cannot scale image! + Could not get size from "dummy.mp4":{REQUIRES_PIL} + PIL cannot read video images.</p> +</aside> +""", +], +["""\ +.. image:: https://dummy.png + :scale: 100% + :loading: embed +""", +f"""\ +<img alt="https://dummy.png" src="https://dummy.png" /> +<aside class="system-message"> +<p class="system-message-title">System Message: WARNING/2 \ +(<span class="docutils literal"><string></span>, line 1)</p> +<p>Cannot scale image! + Could not get size from "https://dummy.png": + {ONLY_LOCAL}</p> +</aside> +<aside class="system-message"> +<p class="system-message-title">System Message: ERROR/3 \ +(<span class="docutils literal"><string></span>, line 1)</p> +<p>Cannot embed image "https://dummy.png": + Can only read local images.</p> +</aside> +""", +], +]) + +totest['no_system_messages'] = ({'math_output': 'mathml', + 'report_level': 4, + 'warning_stream': '', + }, [ +["""\ +.. image:: dummy.png + :scale: 100% + :loading: embed + +.. image:: dummy.mp4 + :scale: 100% +""", +"""\ +<img alt="dummy.png" src="dummy.png" /> +<video src="dummy.mp4" title="dummy.mp4"> +<a href="dummy.mp4">dummy.mp4</a> +</video> +""", +], +[f"""\ +.. image:: {DATA_ROOT.as_uri()}/circle-broken.svg + :loading: embed +""", +"""\ +<svg xmlns="http://www.w3.org/2000/svg" + viewBox="0 0 10 10"> + <circle cx="5" cy="5" r="4" fill="lightblue" x/> +</svg> + +"""], +[r'Broken :math:`\sin \my`.', +'<p>Broken <tt class="math">\\sin \\my</tt>.</p>\n' +], +]) + + +if __name__ == '__main__': + unittest.main() Property changes on: trunk/docutils/test/test_writers/test_html5_polyglot.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 Modified: trunk/docutils/test/test_writers/test_html5_polyglot_misc.py =================================================================== --- trunk/docutils/test/test_writers/test_html5_polyglot_misc.py 2024-08-21 13:44:37 UTC (rev 9912) +++ trunk/docutils/test/test_writers/test_html5_polyglot_misc.py 2024-08-21 13:44:48 UTC (rev 9913) @@ -36,7 +36,7 @@ # error handler. settings_overrides = { 'output_encoding': 'latin1', - 'stylesheet': '', + 'stylesheet_path': '', '_disable_config': True} result = core.publish_string( 'EUR = \u20ac', writer='html5_polyglot', @@ -49,7 +49,6 @@ class MovingArgsTestCase(unittest.TestCase): mys = {'stylesheet_path': '', - # 'embed_stylesheet': False, '_disable_config': True, } @@ -99,8 +98,8 @@ mys = {'_disable_config': True} styles = core.publish_parts(self.data, writer='html5_polyglot', settings_overrides=mys)['stylesheet'] - self.assertIn('Minimal style sheet ' - 'for the HTML output of Docutils.', styles) + self.assertIn('Minimal style sheet for the HTML output of Docutils.', + styles) def test_default_stylesheet_linked(self): # default style sheet, linked @@ -141,7 +140,8 @@ styles = core.publish_parts(self.data, writer='html5_polyglot', settings_overrides=mys)['stylesheet'] if (TEST_ROOT / '../docutils/writers/html5_polyglot/').is_dir(): - self.assertIn('docutils/writers/html5_polyglot/minimal.css', styles) + self.assertIn('docutils/writers/html5_polyglot/minimal.css', + styles) self.assertIn(f'href="{ham_css}"', styles) def test_custom_stylesheet_dir_embedded(self): Modified: trunk/docutils/test/test_writers/test_html5_polyglot_parts.py =================================================================== --- trunk/docutils/test/test_writers/test_html5_polyglot_parts.py 2024-08-21 13:44:37 UTC (rev 9912) +++ trunk/docutils/test/test_writers/test_html5_polyglot_parts.py 2024-08-21 13:44:48 UTC (rev 9913) @@ -5,15 +5,12 @@ # Copyright: This module has been placed in the public domain. """ -Test for fragment code in HTML writer. +Test `core.publish_parts()`__ with the HTML5 writer. -Note: the 'body' and 'whole' entries have been removed from the parts -dictionaries (redundant), along with 'meta' and 'stylesheet' entries with -standard values, and any entries with empty values. +__ https://docutils.sourceforge.io/docs/api/publisher.html#publish-parts """ from pathlib import Path -import re import sys import unittest @@ -24,182 +21,136 @@ import docutils import docutils.core -from docutils.parsers.rst.directives.images import PIL -from docutils.utils.code_analyzer import with_pygments +from docutils.writers import html5_polyglot -if with_pygments: - import pygments - _pv = re.match(r'^([0-9]+)\.([0-9]*)', pygments.__version__) - if (int(_pv[1]), int(_pv[2])) >= (2, 14): - # pygments output changed in version 2.14 - with_pygments = False - # TEST_ROOT is ./test/ from the docutils root TEST_ROOT = Path(__file__).parents[1] DATA_ROOT = TEST_ROOT / 'data' ROOT_PREFIX = (TEST_ROOT / 'functional/input').as_posix() -# Pillow/PIL is optional: -if PIL: - REQUIRES_PIL = '' - ONLY_LOCAL = 'Can only read local images.' - DUMMY_PNG_NOT_FOUND = "[Errno 2] No such file or directory: 'dummy.png'" - # Pillow reports the absolute path since version 10.3.0 (cf. [bugs: 485]) - 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: 32.0px; height: 32.0px;" ' - 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 = '' - NO_PIL_SYSTEM_MESSAGE = ( - '<aside class="system-message">\n' - '<p class="system-message-title">System Message:' - ' WARNING/2 (<span class="docutils literal">' - '<string></span>, line 1)</p>\n' - '<p>Cannot scale image!\n' - ' Could not get size from "/data/blue%20square.png":\n' - ' Requires Python Imaging Library.</p>\n' - '</aside>\n') +# Parts returned by `publish_parts()` for the HTML5 writer by default: +# * empty input string +# * default configuration settings. +# See format_parts() below for the substitution of unresolved format markers. +default_parts = { + 'body': '{fragment}', + 'body_pre_docinfo': '', + 'body_prefix': '</head>\n<body>\n{header}<main>\n', + 'body_suffix': '</main>\n{footer}</body>\n</html>\n', + 'docinfo': '', + 'encoding': 'utf-8', + 'errors': 'xmlcharrefreplace', + 'footer': '', + 'fragment': '', + 'head': '{meta}<title>{metatitle}</title>\n', + 'head_prefix': + '<!DOCTYPE html>\n' + '<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">\n' + '<head>\n', + 'header': '', + 'html_body': '{header}<main>\n{fragment}</main>\n{footer}', + 'html_head': '{meta}<title>{metatitle}</title>\n', + 'html_prolog': '<!DOCTYPE html>\n', + 'html_subtitle': '', + 'html_title': '', + 'meta': '<meta charset="%s" />\n' + f'<meta name="generator" content="Docutils {docutils.__version__}: https://docutils.sourceforge.io/" />\n' + '<meta name="viewport" content="width=device-width, initial-scale=1" />\n', + 'stylesheet': '', + 'subtitle': '', + 'title': '', + 'version': f'{docutils.__version__}', + 'whole': + '{head_prefix}\n' + '{head}\n' + '{stylesheet}\n' + '{body_prefix}\n' + '{body_pre_docinfo}\n' + '{docinfo}\n' + '{body}\n' + '{body_suffix}\n', + } + +def format_parts(parts): + # fill in part values that depend on other parts + # https://docutils.sourceforge.io/docs/api/publisher.html#html4-writer + + # metadata title: either the visible title or document['source'] + metatitle = parts['title'] or '<string>' + + # "html_head" leaves the encoding unresolved, as "%s": + parts['html_head'] = parts['html_head'].format(metatitle=metatitle, + **parts) + # now resolve encoding: + try: + parts['meta'] = parts['meta'] % parts['encoding'] + except TypeError: # charset-<meta> missing if encoding is 'unicode' + pass + parts['head'] = parts['head'].format(metatitle=metatitle, **parts) + parts['body_prefix'] = parts['body_prefix'].format(**parts) + parts['body'] = parts['body'].format(**parts) + parts['body_suffix'] = parts['body_suffix'].format(**parts) + parts['html_body'] = parts['html_body'].format(**parts) + # newlines are stripped when parts are used to expand the template file + parts['whole'] = parts['whole'].format(**{k: v.rstrip('\n') + for k, v in parts.items()}) + return parts + + class Html5WriterPublishPartsTestCase(unittest.TestCase): - """Test case for HTML writer via the publish_parts interface.""" + """Test HTML5 writer `publish_parts()` interface.""" maxDiff = None - def test_publish(self): - if not with_pygments: - del totest['syntax_highlight'] - writer = 'html5' + def test_publish_parts(self): for name, (settings_overrides, cases) in totest.items(): - for casenum, (case_input, case_expected) in enumerate(cases): - with self.subTest(id=f'totest[{name!r}][{casenum}]'): - parts = docutils.core.publish_parts( - source=case_input, - writer=writer, - settings_overrides={ - '_disable_config': True, - 'strict_visitor': True, - 'stylesheet_path': '', - 'section_self_link': True, - **settings_overrides, - } - ) - # if the formatted output is a just single fragment, - # compare the text directly for a nicer diff on failure - formatted = self.format_output(parts) - if len(formatted) == 1 and 'fragment' in formatted: - fragment = formatted['fragment'] - self.assertEqual(case_expected, fragment) - else: - self.assertEqual(case_expected, formatted) + for casenum, (case_input, expected_parts) in enumerate(cases): + _stgns = {'_disable_config': True, + 'strict_visitor': True, + 'stylesheet_path': '', + 'section_self_link': True, + **settings_overrides, + } + parts = docutils.core.publish_parts(source=case_input, + writer=html5_polyglot.Writer(), + settings_overrides=_stgns, + ) + expected = format_parts(default_parts | expected_parts) + for key in parts.keys(): + with self.subTest(id=f'totest[{name!r}][{casenum}][{key}]'): + self.assertEqual(f'{expected[key]}', + f'{parts[key]}') - standard_content_type_template = '<meta charset="%s" />\n' - standard_generator_template = ( - '<meta name="generator"' - f' content="Docutils {docutils.__version__}: ' - 'https://docutils.sourceforge.io/" />\n') - standard_viewport_value = ( - '<meta name="viewport"' - ' content="width=device-width, initial-scale=1" />\n') - standard_html_meta_value = (standard_content_type_template - + standard_generator_template - + standard_viewport_value) - standard_meta_value = standard_html_meta_value % 'utf-8' - standard_html_prolog = '<!DOCTYPE html>\n' - standard_html_body_template = '<main>\n%s</main>\n' - def format_output(self, parts): - """Minimize & standardize the output.""" - # remove redundant parts & uninteresting parts: - del parts['whole'] - assert parts['body'] == parts['fragment'] - del parts['body'] - del parts['body_pre_docinfo'] - del parts['body_prefix'] - del parts['body_suffix'] - del parts['head'] - del parts['head_prefix'] - del parts['encoding'] - del parts['errors'] - del parts['version'] - # remove standard portions: - parts['meta'] = parts['meta'].replace(self.standard_meta_value, '') - parts['html_head'] = parts['html_head'].replace( - self.standard_html_meta_value, '...') - parts['html_head'] = parts['html_head'].replace( - '...<title><string></title>\n', '') - parts['html_prolog'] = parts['html_prolog'].replace( - self.standard_html_prolog, '') - parts['html_body'] = parts['html_body'].replace( - self.standard_html_body_template % parts['fragment'], '') - # remove empty keys and return - return {k: v for k, v in parts.items() if v} - - totest = {} totest['standard'] = ({}, [ -["""\ -Simple String -""", -'<p>Simple String</p>\n', -], -["""\ -Simple String with *markup* -""", -'<p>Simple String with <em>markup</em></p>\n', -], -["""\ -Simple String with an even simpler ``inline literal`` -""", -'<p>Simple String with an even simpler <span class="docutils literal">inline literal</span></p>\n', -], -["""\ -A simple `anonymous reference`__ - -__ http://www.test.com/test_url -""", -'<p>A simple <a class="reference external" href="http://www.test.com/test_url">anonymous reference</a></p>\n', -], -["""\ -One paragraph. - -Two paragraphs. -""", -"""\ -<p>One paragraph.</p> -<p>Two paragraphs.</p> -""", -], -["""\ -A simple `named reference`_ with stuff in between the -reference and the target. - -.. _`named reference`: http://www.test.com/test_url -""", -"""\ -<p>A simple <a class="reference external" href="http://www.test.com/test_url">named reference</a> with stuff in between the -reference and the target.</p> -""", -], -["""\ -.. [CIT2022] A citation. -""", -"""\ -<div role="list" class="citation-list"> -<div class="citation" id="cit2022" role="doc-biblioentry"> -<span class="label"><span class="fn-bracket">[</span>CIT2022<span class="fn-bracket">]</span></span> -<p>A citation.</p> -</div> -</div> -""", -], -["""\ + ['', # empty input string + {} # results in default parts + ], + ['Simple String with *markup*', + {'fragment': '<p>Simple String with <em>markup</em></p>\n'} + ], + ['.. header:: custom document header\n\n' + 'A paragraph.', + {'header': '<header>\n<p>custom document header</p>\n</header>\n', + 'body_prefix': '</head>\n' + '<body>\n' + '<header>\n' + '<p>custom document header</p>\n</header>\n' + '<main>\n', + 'fragment': '<p>A paragraph.</p>\n', + } + ], + ['.. footer:: custom document footer\n\n' + 'A paragraph.', + {'footer': '<footer>\n<p>custom document footer</p>\n</footer>\n', + 'fragment': '<p>A paragraph.</p>\n', + } + ], + ["""\ +++++ Title +++++ @@ -219,7 +170,10 @@ And even more stuff """, -{'fragment': """\ + {'body_pre_docinfo': '<h1 class="title">Title</h1>\n' + '<p class="subtitle" id="subtitle">Subtitle</p>\n', + 'body_prefix': '</head>\n<body>\n<main id="title">\n', + 'fragment': """\ <p>Some stuff</p> <section id="section"> <h2>Section<a class="self-link" title="link to this section" href="#section"></a></h2> @@ -230,7 +184,7 @@ </section> </section> """, - 'html_body': """\ + 'html_body': """\ <main id="title"> <h1 class="title">Title</h1> <p class="subtitle" id="subtitle">Subtitle</p> @@ -245,13 +199,12 @@ </section> </main> """, - 'html_head': '...<title>Title</title>\n', - 'html_subtitle': '<p class="subtitle" id="subtitle">Subtitle</p>\n', - 'html_title': '<h1 class="title">Title</h1>\n', - 'subtitle': 'Subtitle', - 'title': 'Title' -}], -["""\ + 'html_subtitle': '<p class="subtitle" id="subtitle">Subtitle</p>\n', + 'html_title': '<h1 class="title">Title</h1>\n', + 'subtitle': 'Subtitle', + 'title': 'Title' + }], + ["""\ +++++ Title +++++ @@ -260,14 +213,16 @@ Some stuff """, -{'docinfo': """\ + {'body_pre_docinfo': '<h1 class="title">Title</h1>\n', + 'body_prefix': '</head>\n<body>\n<main id="title">\n', + 'docinfo': """\ <dl class="docinfo simple"> <dt class="author">Author<span class="colon">:</span></dt> <dd class="author"><p>me</p></dd> </dl> """, - 'fragment': '<p>Some stuff</p>\n', - 'html_body': """\ + 'fragment': '<p>Some stuff</p>\n', + 'html_body': """\ <main id="title"> <h1 class="title">Title</h1> <dl class="docinfo simple"> @@ -277,63 +232,14 @@ <p>Some stuff</p> </main> """, - 'html_head': """\ -...<meta name="author" content="me" /> -<title>Title</title> -""", - 'html_title': '<h1 class="title">Title</h1>\n', - 'meta': '<meta name="author" content="me" />\n', - 'title': 'Title' -}], -]) + 'html_title': '<h1 class="title">Title</h1>\n', + 'meta': default_parts['meta'] + '<meta name="author" content="me" />\n', + 'title': 'Title' + }], + ]) -totest['standard'] = ({'output_encoding': 'unicode'}, [ -["""\ -Simple String -""", -{'fragment': '<p>Simple String</p>\n', - 'meta': """\ -<meta name="generator" content="Docutils 0.22b.dev: https://docutils.sourceforge.io/" /> -<meta name="viewport" content="width=device-width, initial-scale=1" /> -""", -}], -]) - totest['no_title_promotion'] = ({'doctitle_xform': False}, [ -["""\ -Simple String -""", -'<p>Simple String</p>\n', -], -["""\ -Simple String with *markup* -""", -'<p>Simple String with <em>markup</em></p>\n', -], -["""\ -Simple String with an even simpler ``inline literal`` -""", -'<p>Simple String with an even simpler <span class="docutils literal">inline literal</span></p>\n', -], -["""\ -A simple `anonymous reference`__ - -__ http://www.test.com/test_url -""", -'<p>A simple <a class="reference external" href="http://www.test.com/test_url">anonymous reference</a></p>\n', -], -["""\ -A simple `named reference`_ with stuff in between the -reference and the target. - -.. _`named reference`: http://www.test.com/test_url -""", -"""\ -<p>A simple <a class="reference external" href="http://www.test.com/test_url">named reference</a> with stuff in between the -reference and the target.</p> -""", -], -["""\ + ["""\ +++++ Title +++++ @@ -353,7 +259,7 @@ And even more stuff """, -"""\ + {'fragment': """\ <section id="title"> <h2>Title<a class="self-link" title="link to this section" href="#title"></a></h2> <section id="not-a-subtitle"> @@ -369,486 +275,19 @@ </section> </section> </section> -""", -], -["""\ -* bullet -* list -""", -"""\ -<ul class="simple"> -<li><p>bullet</p></li> -<li><p>list</p></li> -</ul> -""", -], -["""\ -.. table:: - :align: right +"""}, + ], + ]) - +-----+-----+ - | 1 | 2 | - +-----+-----+ - | 3 | 4 | - +-----+-----+ -""", -"""\ -<table class="align-right"> -<tbody> -<tr><td><p>1</p></td> -<td><p>2</p></td> -</tr> -<tr><td><p>3</p></td> -<td><p>4</p></td> -</tr> -</tbody> -</table> -""", -], -["""\ -Not a docinfo. +totest['unknown-encoding'] = ({'output_encoding': 'unicode'}, [ + ['Simple String\n', + {'encoding': 'unicode', + 'fragment': '<p>Simple String</p>\n', + 'html_head': f'{default_parts["meta"]}<title>{{metatitle}}</title>\n', + 'meta': default_parts['meta'].removeprefix('<meta charset="%s" />\n'), + }], + ]) -:This: .. _target: - is -:a: -:simple: -:field: list -""", -"""\ -<p>Not a docinfo.</p> -<dl class="field-list simple"> -<dt>This<span class="colon">:</span></dt> -<dd><p id="target">is</p> -</dd> -<dt>a<span class="colon">:</span></dt> -<dd><p></p></dd> -<dt>simple<span class="colon">:</span></dt> -<dd><p></p></dd> -<dt>field<span class="colon">:</span></dt> -<dd><p>list</p> -</dd> -</dl> -""", -], -["""\ -Not a docinfo. - -:This is: a -:simple field list with loooong field: names -""", -"""\ -<p>Not a docinfo.</p> -<dl class="field-list simple"> -<dt>This is<span class="colon">:</span></dt> -<dd><p>a</p> -</dd> -<dt>simple field list with loooong field<span class="colon">:</span></dt> -<dd><p>names</p> -</dd> -</dl> -""", -], -["""\ -Not a docinfo. - -.. class:: field-indent-200 - -:This: is a -:simple: field list with custom indent. -""", -"""\ -<p>Not a docinfo.</p> -<dl class="field-list simple" style="--field-indent: 200px;"> -<dt>This<span class="colon">:</span></dt> -<dd><p>is a</p> -</dd> -<dt>simple<span class="colon">:</span></dt> -<dd><p>field list with custom indent.</p> -</dd> -</dl> -""", -], -["""\ -Not a docinfo. - -.. class:: field-indent-200uf - -:This: is a -:simple: field list without custom indent, - because the unit "uf" is invalid. -""", -"""\ -<p>Not a docinfo.</p> -<dl class="field-indent-200uf field-list simple"> -<dt>This<span class="colon">:</span></dt> -<dd><p>is a</p> -</dd> -<dt>simple<span class="colon">:</span></dt> -<dd><p>field list without custom indent, -because the unit "uf" is invalid.</p> -</dd> -</dl> -""", -], -["""\ -.. figure:: dummy.png - - The figure's caption. - - A legend. - - The legend's second paragraph. -""", -"""\ -<figure> -<img alt="dummy.png" src="dummy.png" /> -<figcaption> -<p>The figure's caption.</p> -<div class="legend"> -<p>A legend.</p> -<p>The legend's second paragraph.</p> -</div> -</figcaption> -</figure> -""", -], -["""\ -.. figure:: dummy.png - - The figure's caption, no legend. -""", -"""\ -<figure> -<img alt="dummy.png" src="dummy.png" /> -<figcaption> -<p>The figure's caption, no legend.</p> -</figcaption> -</figure> -""", -], -["""\ -.. figure:: dummy.png - - .. - - A legend without caption. -""", -"""\ -<figure> -<img alt="dummy.png" src="dummy.png" /> -<figcaption> -<div class="legend"> -<p>A legend without caption.</p> -</div> -</figcaption> -</figure> -""", -], -["""\ -.. figure:: dummy.png - -No caption nor legend. -""", -"""\ -<figure> -<img alt="dummy.png" src="dummy.png" /> -</figure> -<p>No caption nor legend.</p> -""", -], -[f"""\ -.. include:: {DATA_ROOT}/multiple-term-definition.xml - :parser: xml -""", -"""\ -<dl> -<dt>New in Docutils 0.22</dt> -<dd><p>A definition list item may contain several -terms with optional classifier(s).</p> -<p>However, there is currently no corresponding -reStructuredText syntax.</p> -</dd> -<dt>term 2a</dt> -<dt>term 2b</dt> -<dd><p>definition 2</p> -</dd> -<dt>term 3a<span class="classifier">classifier 3a</span><span class="classifier">classifier 3aa</span><dt>term 3b<span class="classifier">classifier 3b</span></dt> -<dd><p>definition 3</p> -</dd> -</dl> -""", -], -]) - - -totest['lazy_loading'] = ({'image_loading': 'lazy', - 'report_level': 4}, [ -["""\ -Lazy loading by default, overridden by :loading: option -("cannot embed" warning ignored). - -.. image:: dummy.png -.. image:: dummy.png - :loading: link -.. figure:: dummy.png -.. figure:: dummy.png - :loading: embed -""", -"""\ -<p>Lazy loading by default, overridden by :loading: option -("cannot embed" warning ignored).</p> -<img alt="dummy.png" loading="lazy" src="dummy.png" /> -<img alt="dummy.png" src="dummy.png" /> -<figure> -<img alt="dummy.png" loading="lazy" src="dummy.png" /> -</figure> -<figure> -<img alt="dummy.png" src="dummy.png" /> -</figure> -""", -], -]) - - -totest['root_prefix'] = ({'root_prefix': ROOT_PREFIX, - 'image_loading': 'embed', - 'warning_stream': '', - }, [ -["""\ -.. image:: /data/blue%20square.png - :scale: 100% -.. figure:: /data/blue%20square.png -""", -'<img alt="/data/blue%20square.png" src="data:image/png;base64,' -'iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAIAAAD8GO2jAAAALElEQVR4nO3NMQ' -'EAMAjAsDFjvIhHFCbgSwU0kdXvsn96BwAAAAAAAAAAAIsNnEwBk52VRuMAAAAA' -'SUVORK5CYII="' -f' {SCALING_OUTPUT}/>\n{NO_PIL_SYSTEM_MESSAGE}' -'<figure>\n' -'<img alt="/data/blue%20square.png" src="data:image/png;base64,' -'iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAIAAAD8GO2jAAAALElEQVR4nO3NMQ' -'EAMAjAsDFjvIhHFCbgSwU0kdXvsn96BwAAAAAAAAAAAIsNnEwBk52VRuMAAAAA' -'SUVORK5CYII=" />\n' -'</figure>\n', -], -]) - - -totest['no_backlinks'] = ({'footnote_backlinks': False}, [ - -["""\ -Two footnotes [#f1]_ [#f2]_ and two citations [once]_ [twice]_. - -The latter are referenced a second time [#f2]_ [twice]_. - -.. [#f1] referenced once -.. [#f2] referenced twice -.. [once] citation referenced once -.. [twice] citation referenced twice -""", -"""\ -<p>Two footnotes <a class="brackets" href="#f1" id="footnote-reference-1" role="doc-noteref"><span class="fn-bracket">[</span>1<span class="fn-bracket">]</span></a> <a class="brackets" href="#f2" id="footnote-reference-2" role="doc-noteref"><span class="fn-bracket">[</span>2<span class="fn-bracket">]</span></a> and two citations <a class="citation-reference" href="#once" id="citation-reference-1" role="doc-biblioref">[once]</a> <a class="citation-reference" href="#twice" id="citation-reference-2" role="doc-biblioref">[twice]</a>.</p> -<p>The latter are referenced a second time <a class="brackets" href="#f2" id="footnote-reference-3" role="doc-noteref"><span class="fn-bracket">[</span>2<span class="fn-bracket">]</span></a> <a class="citation-reference" href="#twice" id="citation-reference-3" role="doc-biblioref">[twice]</a>.</p> -<aside class="footnote-list brackets"> -<aside class="footnote brackets" id="f1" role="doc-footnote"> -<span class="label"><span class="fn-bracket">[</span>1<span class="fn-bracket">]</span></span> -<p>referenced once</p> -</aside> -<aside class="footnote brackets" id="f2" role="doc-footnote"> -<span class="label"><span class="fn-bracket">[</span>2<span class="fn-bracket">]</span></span> -<p>referenced twice</p> -</aside> -</aside> -<div role="list" class="citation-list"> -<div class="citation" id="once" role="doc-biblioentry"> -<span class="label"><span class="fn-bracket">[</span>once<span class="fn-bracket">]</span></span> -<p>citation referenced once</p> -</div> -<div class="citation" id="twice" role="doc-biblioentry"> -<span class="label"><span class="fn-bracket">[</span>twice<span class="fn-bracket">]</span></span> -<p>citation referenced twice</p> -</div> -</div> -""", -], -]) - - -totest['syntax_highlight'] = ({'syntax_highlight': 'short', - }, [ -["""\ -.. code:: shell - - cat <<EOF - Hello World - EOF -""", -"""\ -<pre class="code shell literal-block"><code>cat <span class="s"><<EOF -Hello World -EOF</span></code></pre> -""", -], -["""\ -.. role:: shell(code) - :language: shell - -:shell:`cat <<EOF Hello World EOF` -""", -"""\ -<p><code class="shell">cat <span class="s"><<EOF Hello World EOF</span></code></p> -""", -], -]) - - -totest['system_messages'] = ({'math_output': 'mathml', - 'warning_stream': '', - }, [ -["""\ -.. image:: https://dummy.png - :loading: embed -""", -"""\ -<img alt="https://dummy.png" src="https://dummy.png" /> -<aside class="system-message"> -<p class="system-message-title">System Message: ERROR/3 \ -(<span class="docutils literal"><string></span>, line 1)</p> -<p>Cannot embed image "https://dummy.png": - Can only read local images.</p> -</aside> -""", -], -[f"""\ -.. image:: {DATA_ROOT.as_uri()}/circle-broken.svg - :loading: embed -""", -f"""\ -<svg xmlns="http://www.w3.org/2000/svg" - viewBox="0 0 10 10"> - <circle cx="5" cy="5" r="4" fill="lightblue" x/> -</svg> - -<aside class="system-message"> -<p class="system-message-title">System Message: ERROR/3 (<span class="docutils literal"><string></span>, line 1)</p> -<p>Cannot parse SVG image "{DATA_ROOT.as_uri()}/circle-broken.svg": - not well-formed (invalid token): line 3, column 48</p> -</aside> -""" -], -[r"""Broken :math:`\sin \my`. -""", -"""\ -<p>Broken <span class="math problematic">\\sin \\my</span>.</p> -<aside class="system-message"> -<p class="system-message-title">System Message: WARNING/2 (<span class="docutils literal"><string></span>, line 1)</p> -<p>Unknown LaTeX command "\\my".</p> -</aside> -"""], -]) - -totest['system_messages-PIL'] = ({'math_output': 'mathml', - 'warning_stream': '', - }, [ -["""\ -.. image:: dummy.png - :scale: 100% - :loading: embed -""", -f"""\ -<img alt="dummy.png" src="dummy.png" /> -<aside class="system-message"> -<p class="system-message-title">System Message: WARNING/2 \ -(<span class="docutils literal"><string></span>, line 1)</p> -<p>Cannot scale image! - Could not get size from "dummy.png": - {DUMMY_PNG_NOT_FOUND}</p> -</aside> -<aside class="system-message"> -<p class="system-message-title">System Message: ERROR/3 \ -(<span class="docutils literal"><string></span>, line 1)</p> -<p>Cannot embed image "dummy.png": - [Errno 2] No such file or directory: 'dummy.png'</p> -</aside> -""", -], -["""\ -.. image:: dummy.mp4 - :scale: 100% -""", -f"""\ -<video src="dummy.mp4" title="dummy.mp4"> -<a href="dummy.mp4">dummy.mp4</a> -</video> -<aside class="system-message"> -<p class="system-message-title">System Message: WARNING/2 \ -(<span class="docutils literal"><string></span>, line 1)</p> -<p>Cannot scale image! - Could not get size from "dummy.mp4":{REQUIRES_PIL} - PIL cannot read video images.</p> -</aside> -""", -], -["""\ -.. image:: https://dummy.png - :scale: 100% - :loading: embed -""", -f"""\ -<img alt="https://dummy.png" src="https://dummy.png" /> -<aside class="system-message"> -<p class="system-message-title">System Message: WARNING/2 \ -(<span class="docutils literal"><string></span>, line 1)</p> -<p>Cannot scale image! - Could not get size from "https://dummy.png": - {ONLY_LOCAL}</p> -</aside> -<aside class="system-message"> -<p class="system-message-title">System Message: ERROR/3 \ -(<span class="docutils literal"><string></span>, line 1)</p> -<p>Cannot embed image "https://dummy.png": - Can only read local images.</p> -</aside> -""", -], -]) - -totest['no_system_messages'] = ({'math_output': 'mathml', - 'report_level': 4, - 'warning_stream': '', - }, [ -["""\ -.. image:: dummy.png - :scale: 100% - :loading: embed - -.. image:: dummy.mp4 - :scale: 100% -""", -"""\ -<img alt="dummy.png" src="dummy.png" /> -<video src="dummy.mp4" title="dummy.mp4"> -<a href="dummy.mp4">dummy.mp4</a> -</video> -""", -], -[f"""\ -.. image:: {DATA_ROOT.as_uri()}/circle-broken.svg - :loading: embed -""", -"""\ -<svg xmlns="http://www.w3.org/2000/svg" - viewBox="0 0 10 10"> - <circle cx="5" cy="5" r="4" fill="lightblue" x/> -</svg> - -"""], -[r'Broken :math:`\sin \my`.', -'<p>Broken <tt class="math">\\sin \\my</tt>.</p>\n' -], -]) - - if __name__ == '__main__': unittest.main() This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |
From: <mi...@us...> - 2024-09-04 19:33:14
|
Revision: 9919 http://sourceforge.net/p/docutils/code/9919 Author: milde Date: 2024-09-04 19:33:12 +0000 (Wed, 04 Sep 2024) Log Message: ----------- New function `nodes.parse_measure()`. Relax `validate_measure()`. Allow "arbitrary" units: Check for a run of ASCII-letters or a percent sign. (The definition of "measure" in docutils.dtd and its description in doctree.txt do not include a restriction of the valid units.) Modified Paths: -------------- trunk/docutils/HISTORY.rst trunk/docutils/docutils/nodes.py trunk/docutils/docutils/parsers/rst/directives/__init__.py trunk/docutils/test/test_nodes.py trunk/docutils/test/test_parsers/test_docutils_xml/test_parse_element.py Modified: trunk/docutils/HISTORY.rst =================================================================== --- trunk/docutils/HISTORY.rst 2024-09-04 06:16:29 UTC (rev 9918) +++ trunk/docutils/HISTORY.rst 2024-09-04 19:33:12 UTC (rev 9919) @@ -63,6 +63,7 @@ convert string representations to correct data type, normalize values, raise ValueError for invalid attribute names or values. + - New function `parse_measure()`. - Removed `Element.set_class()`. * docutils/parsers/docutils_xml.py Modified: trunk/docutils/docutils/nodes.py =================================================================== --- trunk/docutils/docutils/nodes.py 2024-09-04 06:16:29 UTC (rev 9918) +++ trunk/docutils/docutils/nodes.py 2024-09-04 19:33:12 UTC (rev 9919) @@ -3074,6 +3074,22 @@ return '"%s"' % value +def parse_measure(measure: str) -> tuple[float, str]: + """Parse a measure__, return value + optional unit. + + __ https://docutils.sourceforge.io/docs/ref/doctree.html#measure + + Provisional. + """ + match = re.fullmatch('(-?[0-9.]+) *([a-zA-Zµ]*|%?)', measure) + try: + value = float(match.group(1)) + unit = match.group(2) + except (AttributeError, ValueError): + raise ValueError(f'"{measure}" is no valid measure.') + return value, unit + + # Methods to validate `Element attribute`__ values. # Ensure the expected Python `data type`__, normalize, and check for @@ -3139,24 +3155,25 @@ return value -def validate_measure(value: str) -> str: +def validate_measure(measure: str) -> str: """ - Validate a length measure__ (number + recognized unit). + Validate a measure__ (number + optional unit). Return normalized `str`. - __ https://docutils.sourceforge.io/docs/ref/doctree.html#measure + See `parse_measure()` for a function returning a "number + unit" tuple. + The unit may be any run of letters or a percent sign. + Provisional. + + __ https://docutils.sourceforge.io/docs/ref/doctree.html#measure """ - units = 'em|ex|px|in|cm|mm|pt|pc|%' - if not re.fullmatch(f'[-0-9.]+ *({units}?)', value): - raise ValueError(f'"{value}" is no valid measure. ' - f'Valid units: {units.replace("|", " ")}.') - return value.replace(' ', '').strip() + value, unit = parse_measure(measure) + return f'{value:g}{unit}' def validate_NMTOKEN(value: str) -> str: """ - Validate a "name token": a `str` of letters, digits, and [-._]. + Validate a "name token": a `str` of ASCII letters, digits, and [-._]. Provisional. """ Modified: trunk/docutils/docutils/parsers/rst/directives/__init__.py =================================================================== --- trunk/docutils/docutils/parsers/rst/directives/__init__.py 2024-09-04 06:16:29 UTC (rev 9918) +++ trunk/docutils/docutils/parsers/rst/directives/__init__.py 2024-09-04 19:33:12 UTC (rev 9919) @@ -261,8 +261,8 @@ float(match.group(1)) except (AttributeError, ValueError): raise ValueError( - 'not a positive measure of one of the following units:\n%s' - % ' '.join('"%s"' % i for i in units)) + 'not a positive measure of one of the following units:\n"%s"' + % '" "'.join(units)) return match.group(1) + match.group(2) Modified: trunk/docutils/test/test_nodes.py =================================================================== --- trunk/docutils/test/test_nodes.py 2024-09-04 06:16:29 UTC (rev 9918) +++ trunk/docutils/test/test_nodes.py 2024-09-04 19:33:12 UTC (rev 9919) @@ -502,11 +502,10 @@ node.validate() def test_validate_wrong_attribute_value(self): - node = nodes.image(uri='test.png', width='20 inch') # invalid unit + node = nodes.image(uri='test.png', width='1in 3pt') with self.assertRaisesRegex(nodes.ValidationError, 'Element <image.*> invalid:\n' - '.*"width" has invalid value "20 inch".\n' - '.*Valid units: em ex '): + '.*"width" has invalid value "1in 3pt".'): node.validate() def test_validate_spurious_element(self): @@ -1114,7 +1113,25 @@ self.assertEqual(nodes.split_name_list(r'a\ n\ame two\\ n\\ames'), ['a name', 'two\\', r'n\ames']) + def test_parse_measure(self): + # measure is number + optional unit (letter(s) or percentage) + self.assertEqual(nodes.parse_measure('8ex'), (8, 'ex')) + self.assertEqual(nodes.parse_measure('2.5'), (2.5, '')) + self.assertEqual(nodes.parse_measure('-2s'), (-2, 's')) + self.assertEqual(nodes.parse_measure('2 µF'), (2, 'µF')) + self.assertEqual(nodes.parse_measure('10 EUR'), (10, 'EUR')) + self.assertEqual(nodes.parse_measure('.5 %'), (.5, '%')) + # scientific notation not supported + with self.assertRaisesRegex(ValueError, '"3e-4 mm" is no valid '): + nodes.parse_measure('3e-4 mm') + # unit must follow the number + with self.assertRaisesRegex(ValueError, '"EUR 23" is no valid '): + nodes.parse_measure('EUR 23') + # only single percent sign allowed + with self.assertRaisesRegex(ValueError, '"2%%" is no valid measure'): + nodes.parse_measure('2%%') + class AttributeTypeTests(unittest.TestCase): """Test validator functions for the supported `attribute data types`__ @@ -1154,17 +1171,19 @@ nodes.validate_identifier_list(s2) def test_validate_measure(self): - # number (may be decimal fraction) + optional CSS2 length unit + # number (may be decimal fraction) + optional unit self.assertEqual(nodes.validate_measure('8ex'), '8ex') + self.assertEqual(nodes.validate_measure('2'), '2') + # internal whitespace is removed self.assertEqual(nodes.validate_measure('3.5 %'), '3.5%') - self.assertEqual(nodes.validate_measure('2'), '2') - with self.assertRaisesRegex(ValueError, '"2km" is no valid measure. ' - 'Valid units: em ex '): - nodes.validate_measure('2km') - # negative numbers are currently not supported - # TODO: allow? the spec doesnot mention negative numbers. - # but a negative width or height of an image is odd. - # nodes.validate_measure('-2') + # padding whitespace is not valid + with self.assertRaisesRegex(ValueError, '"8ex " is no valid measure'): + nodes.validate_measure('8ex ') + # Negative numbers: + # * ``doctree.txt`` does not mention negative numbers, + # * in rST, negative numbers are not valid. + # Provisional: currently valid but may become invalid! + # self.assertEqual(nodes.validate_measure('-2'), '-2') def test_validate_NMTOKEN(self): # str with ASCII-letters, digits, hyphen, underscore, and full-stop. Modified: trunk/docutils/test/test_parsers/test_docutils_xml/test_parse_element.py =================================================================== --- trunk/docutils/test/test_parsers/test_docutils_xml/test_parse_element.py 2024-09-04 06:16:29 UTC (rev 9918) +++ trunk/docutils/test/test_parsers/test_docutils_xml/test_parse_element.py 2024-09-04 19:33:12 UTC (rev 9919) @@ -93,12 +93,10 @@ """ xml = ('<image breadth="3 cm" height="3 inch"/>') node = docutils_xml.parse_element(xml) - self.assertEqual(xml, str(node)) + self.assertEqual(xml.replace('3 inch', '3inch'), str(node)) with self.assertRaisesRegex(ValueError, 'Element <image breadth="3 cm".*invalid:\n' - '.*"breadth" not one of "ids",.*\n' - '.*"height" has invalid value "3 inch".\n' - '.*Valid units: em ex px in cm mm pt ' + '.*"breadth" not one of "ids", ' ): node.validate() This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |
From: <mi...@us...> - 2024-09-04 19:33:33
|
Revision: 9920 http://sourceforge.net/p/docutils/code/9920 Author: milde Date: 2024-09-04 19:33:30 +0000 (Wed, 04 Sep 2024) Log Message: ----------- Refactor `HTMLTranslator.image_size()`. Use `nodes.parse_measure()` instead of ad-hoc parsing when scaling size values. Split "spaghetti code" into auxiliary method `read_size_with_PIL()`. Modified Paths: -------------- trunk/docutils/docutils/writers/_html_base.py trunk/docutils/test/test_writers/test_html5_polyglot.py trunk/docutils/test/test_writers/test_html5_polyglot_misc.py Modified: trunk/docutils/docutils/writers/_html_base.py =================================================================== --- trunk/docutils/docutils/writers/_html_base.py 2024-09-04 19:33:12 UTC (rev 9919) +++ trunk/docutils/docutils/writers/_html_base.py 2024-09-04 19:33:30 UTC (rev 9920) @@ -14,18 +14,23 @@ # # .. _2-Clause BSD license: https://opensource.org/licenses/BSD-2-Clause -"""common definitions for Docutils HTML writers""" +"""Common definitions for Docutils HTML writers.""" +from __future__ import annotations + +__docformat__ = 'reStructuredText' + import base64 import mimetypes import os import os.path -from pathlib import Path import re import urllib.parse import urllib.request import warnings import xml.etree.ElementTree as ET # TODO: lazy import in prepare_svg()? +from pathlib import Path +from typing import TYPE_CHECKING import docutils from docutils import frontend, languages, nodes, utils, writers @@ -36,6 +41,10 @@ unichar2tex, wrap_math_code, MathError) +if TYPE_CHECKING: + from numbers import Real + + class Writer(writers.Writer): supported = ('html', 'xhtml') # update in subclass @@ -404,58 +413,67 @@ text = str(text) return text.translate(self.special_characters) - def image_size(self, node): - # Determine the image size from the node arguments or the image file. - # Return a size declaration suitable as "style" argument value, - # e.g., ``'width: 4px; height: 2em;'``. - # TODO: consider feature-request #102? - size = [node.get('width', None), node.get('height', None)] - if 'scale' in node: - if 'width' not in node or 'height' not in node: - # try reading size from image file - reading_problems = [] - uri = node['uri'] - if not PIL: - reading_problems.append('Requires Python Imaging Library.') - if mimetypes.guess_type(uri)[0] in self.videotypes: - reading_problems.append('PIL cannot read video images.') - if not self.settings.file_insertion_enabled: - reading_problems.append('Reading external files disabled.') - if not reading_problems: - try: - imagepath = self.uri2imagepath(uri) - with PIL.Image.open(imagepath) as img: - imgsize = img.size - except (ValueError, OSError, UnicodeEncodeError) as err: - reading_problems.append(str(err)) - else: - self.settings.record_dependencies.add( - imagepath.replace('\\', '/')) - if reading_problems: - msg = ['Cannot scale image!', - f'Could not get size from "{uri}":', - *reading_problems] - self.messages.append(self.document.reporter.warning( - '\n '.join(msg), base_node=node)) - else: - for i in range(2): - size[i] = size[i] or '%dpx' % imgsize[i] - # scale provided/determined size values: - factor = float(node['scale']) / 100 - for i in range(2): - if size[i]: - match = re.match(r'([0-9.]+)(\S*)$', size[i]) - size[i] = '%s%s' % (factor * float(match.group(1)), - match.group(2)) - size_declarations = [] - for i, dimension in enumerate(('width', 'height')): - if size[i]: - # Interpret unitless values as pixels: - if re.match(r'^[0-9.]+$', size[i]): - size[i] += 'px' - size_declarations.append(f'{dimension}: {size[i]};') - return ' '.join(size_declarations) + def image_size(self, node: nodes.image) -> str: + """Determine the image size from node arguments or the image file. + Auxiliary method called from `self.visit_image()`. + + 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') + for i, dimension in enumerate(dimensions): + if dimension in node: + measures[i] = nodes.parse_measure(node[dimension]) + if None in measures and 'scale' in node: + # 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 CSS declarations and return + return ' '.join(f'{dimension}: {measure[0]:g}{measure[1] or "px"};' + for dimension, measure in zip(dimensions, measures) + if measure) + + def read_size_with_PIL(self, node) -> tuple[int, int] | None: + # Try reading size from image file. + # Internal auxiliary method called from `self.image_size()`. + reading_problems = [] + uri = node['uri'] + if not PIL: + reading_problems.append('Requires Python Imaging Library.') + if mimetypes.guess_type(uri)[0] in self.videotypes: + reading_problems.append('PIL cannot read video images.') + if not self.settings.file_insertion_enabled: + reading_problems.append('Reading external files disabled.') + if not reading_problems: + try: + imagepath = self.uri2imagepath(uri) + with PIL.Image.open(imagepath) as img: + imgsize = img.size + except (ValueError, OSError, UnicodeEncodeError) as err: + reading_problems.append(str(err)) + else: + self.settings.record_dependencies.add(imagepath) + if reading_problems: + msg = ['Cannot scale image!', + f'Could not get size from "{uri}":', + *reading_problems] + self.messages.append(self.document.reporter.warning( + '\n '.join(msg), base_node=node)) + return None + return imgsize + def prepare_svg(self, node, imagedata, size_declaration): # Edit `imagedata` for embedding as SVG image. # Use ElementTree to add node attributes. @@ -607,7 +625,7 @@ child['classes'].append(class_) def uri2imagepath(self, uri): - """Get filesystem path corresponding to an URI. + """Get POSIX filesystem path corresponding to an URI. The image directive expects an image URI__. Some writers require the corresponding image path to read the image size from the file or to Modified: trunk/docutils/test/test_writers/test_html5_polyglot.py =================================================================== --- trunk/docutils/test/test_writers/test_html5_polyglot.py 2024-09-04 19:33:12 UTC (rev 9919) +++ trunk/docutils/test/test_writers/test_html5_polyglot.py 2024-09-04 19:33:30 UTC (rev 9920) @@ -46,7 +46,7 @@ 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: 32.0px; height: 32.0px;" ' + SCALING_OUTPUT = 'style="width: 32px; height: 32px;" ' NO_PIL_SYSTEM_MESSAGE = '' else: REQUIRES_PIL = '\n Requires Python Imaging Library.' Modified: trunk/docutils/test/test_writers/test_html5_polyglot_misc.py =================================================================== --- trunk/docutils/test/test_writers/test_html5_polyglot_misc.py 2024-09-04 19:33:12 UTC (rev 9919) +++ trunk/docutils/test/test_writers/test_html5_polyglot_misc.py 2024-09-04 19:33:30 UTC (rev 9920) @@ -18,8 +18,8 @@ # 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 html5_polyglot +from docutils import core, frontend, nodes, utils +from docutils.writers import html5_polyglot, _html_base # TEST_ROOT is ./test/ from the docutils root TEST_ROOT = Path(__file__).parents[1] @@ -241,5 +241,21 @@ self.assertNotIn('MathJax', head) +class ImagesTestCase(unittest.TestCase): + """Test image handling routines.""" + + settings = frontend.get_default_settings(_html_base.Writer) + document = utils.new_document('test data', settings) + translator = _html_base.HTMLTranslator(document) + + def test_image_size(self): + image = nodes.image(height='3', width='4em') + self.assertEqual(self.translator.image_size(image), + 'width: 4em; height: 3px;') + image = nodes.image(height='3', width='4em', scale=50) + self.assertEqual(self.translator.image_size(image), + 'width: 2em; height: 1.5px;') + + if __name__ == '__main__': unittest.main() This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |
From: <mi...@us...> - 2024-09-04 19:33:42
|
Revision: 9921 http://sourceforge.net/p/docutils/code/9921 Author: milde Date: 2024-09-04 19:33:39 +0000 (Wed, 04 Sep 2024) Log Message: ----------- Revise `HTMLTranslator.prepare_svg()`. Change oder of method arguments (sort by importance). Use a dictionary to update the `<svg style="..."> declarations keeping exisisting declarations intact. Simplify conversion of "align" attribute. Add test cases. Modified Paths: -------------- trunk/docutils/docutils/writers/_html_base.py trunk/docutils/test/test_writers/test_html5_polyglot.py trunk/docutils/test/test_writers/test_html5_polyglot_misc.py Modified: trunk/docutils/docutils/writers/_html_base.py =================================================================== --- trunk/docutils/docutils/writers/_html_base.py 2024-09-04 19:33:30 UTC (rev 9920) +++ trunk/docutils/docutils/writers/_html_base.py 2024-09-04 19:33:39 UTC (rev 9921) @@ -28,7 +28,7 @@ import urllib.parse import urllib.request import warnings -import xml.etree.ElementTree as ET # TODO: lazy import in prepare_svg()? +import xml.etree.ElementTree as ET from pathlib import Path from typing import TYPE_CHECKING @@ -474,39 +474,36 @@ return None return imgsize - def prepare_svg(self, node, imagedata, size_declaration): - # Edit `imagedata` for embedding as SVG image. - # Use ElementTree to add node attributes. - # ET also removes comments and preamble code. + def prepare_svg(self, code: str, node: nodes.Element, atts: dict) -> str: + # Parse SVG source `code` (ignoring comments and preamble code), + # add relevant attributes from `node` and `atts` to the root element. # - # Internal: interface and behaviour may change without notice. - - # SVG namespace + # Internal auxiliary method called from `self.visit_image()`. svg_ns = {'': 'http://www.w3.org/2000/svg', - 'xlink': 'http://www.w3.org/1999/xlink'} + 'xlink': 'http://www.w3.org/1999/xlink'} # SVG namespace # don't add SVG namespace to all elements - ET.register_namespace('', svg_ns['']) - ET.register_namespace('xlink', svg_ns['xlink']) + for key, value in svg_ns.items(): + ET.register_namespace(key, value) + # Parse: try: - svg = ET.fromstring(imagedata) + svg = ET.fromstring(code) except ET.ParseError as err: self.messages.append(self.document.reporter.error( f'Cannot parse SVG image "{node["uri"]}":\n {err}', base_node=node)) - return imagedata - # apply image node attributes: - if size_declaration: # append to style, replacing width & height - declarations = [d.strip() for d in svg.get('style', '').split(';')] - declarations = [d for d in declarations - if d - and not d.startswith('width') - and not d.startswith('height')] - svg.set('style', '; '.join(declarations+[size_declaration])) - if node['classes'] or 'align' in node: + return code + # Apply image node attributes: + if 'style' in atts: + # update style declarations + style_att = f"{svg.get('style', '')}; {atts['style']}" + style_att = [d.partition(':') for d in style_att.split(';') + if d.strip()] + 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) + if 'classes' in atts or node['classes']: classes = svg.get('class', '').split() - classes += node.get('classes', []) - if 'align' in node: - classes.append(f'align-{node["align"]}') + classes += node['classes'] + atts.get('classes', []) svg.set('class', ' '.join(classes)) if 'alt' in node and svg.find('title', svg_ns) is None: svg_title = ET.Element('title') @@ -598,7 +595,7 @@ infix = ' /' else: infix = '' - return ''.join(prefix) + '<%s%s>' % (' '.join(parts), infix) + suffix + return f"{''.join(prefix)}<{' '.join(parts)}{infix}>{suffix}" def emptytag(self, node, tagname, suffix='\n', **attributes): """Construct and return an XML-compatible empty tag.""" @@ -1173,14 +1170,11 @@ mimetype = mimetypes.guess_type(uri)[0] element = '' # the HTML element (including potential children) atts = {} # attributes for the HTML tag - # alignment is handled by CSS rules - if 'align' in node: - atts['class'] = 'align-%s' % node['align'] - # set size with "style" attribute (more universal, accepts dimensions) size_declaration = self.image_size(node) if size_declaration: atts['style'] = size_declaration - + if 'align' in node: + atts['classes'] = [f"align-{node['align']}"] # ``:loading:`` option (embed, link, lazy), default from setting, # exception: only embed videos if told via directive option loading = 'link' if mimetype in self.videotypes else self.image_loading @@ -1201,8 +1195,7 @@ else: self.settings.record_dependencies.add(imagepath) if mimetype == 'image/svg+xml': - element = self.prepare_svg(node, imagedata, - size_declaration) + element = self.prepare_svg(imagedata, node, atts) else: data64 = base64.b64encode(imagedata).decode() uri = f'data:{mimetype};base64,{data64}' Modified: trunk/docutils/test/test_writers/test_html5_polyglot.py =================================================================== --- trunk/docutils/test/test_writers/test_html5_polyglot.py 2024-09-04 19:33:30 UTC (rev 9920) +++ trunk/docutils/test/test_writers/test_html5_polyglot.py 2024-09-04 19:33:39 UTC (rev 9921) @@ -2,9 +2,10 @@ # $Id$ # Author: reggie dugard <re...@us...> +# Maintainer: doc...@li... # Copyright: This module has been placed in the public domain. -"""Test HTML5 writer output ("fragment" part). +"""Test HTML5 writer output ("fragment"/"body" part). This is the document body (not HTML <body>). """ @@ -89,7 +90,7 @@ self.assertEqual(case_expected, parts['body']) -totest = {} +totest = {} # expected samples contain only the "body" part of the HMTL output totest['standard'] = ({}, [ ["""\ @@ -147,6 +148,18 @@ </div> """, ], +[f"""\ +.. image:: {DATA_ROOT.as_uri()}/circle.svg + :loading: embed + :width: 50% + :height: 30 + :align: left +""", +"""\ +<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 10 10" style="width: 50%; height: 30px;" class="align-left"> + <circle cx="5" cy="5" r="4" fill="lightblue" /> +</svg> +"""], ]) Modified: trunk/docutils/test/test_writers/test_html5_polyglot_misc.py =================================================================== --- trunk/docutils/test/test_writers/test_html5_polyglot_misc.py 2024-09-04 19:33:30 UTC (rev 9920) +++ trunk/docutils/test/test_writers/test_html5_polyglot_misc.py 2024-09-04 19:33:39 UTC (rev 9921) @@ -247,6 +247,19 @@ settings = frontend.get_default_settings(_html_base.Writer) document = utils.new_document('test data', settings) translator = _html_base.HTMLTranslator(document) + 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"> + <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"> + <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') @@ -256,6 +269,27 @@ self.assertEqual(self.translator.image_size(image), 'width: 2em; height: 1.5px;') + def test_prepare_svg(self): + # Internal method: the test is no guaranty for stability, + # 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)} + rv = self.translator.prepare_svg(self.svg_sample, image, atts) + self.assertEqual(rv, self.expected) + def test_prepare_svg_syntax_variants(self): + # parsing "style" declarations must be robust: + svg_sample = '<svg style="width:3em"></svg>' + + atts = {'style': 'colon:in:value'} + rv = self.translator.prepare_svg(svg_sample, nodes.image(), atts) + self.assertEqual(rv, '<svg style="width: 3em; colon: in:value;" />') + + atts = {'style': 'no-colon;'} + rv = self.translator.prepare_svg(svg_sample, nodes.image(), atts) + self.assertEqual(rv, '<svg style="width: 3em; no-colon: ;" />') + + if __name__ == '__main__': unittest.main() This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |
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. |
From: <mi...@us...> - 2024-09-13 08:13:06
|
Revision: 9930 http://sourceforge.net/p/docutils/code/9930 Author: milde Date: 2024-09-13 08:13:03 +0000 (Fri, 13 Sep 2024) Log Message: ----------- Use "unknown_reference_resolver" for LaTeX writer option "use-bibtex". The "latex" writer provides the (provisional) setting "use_bibtex" to defer the resolution of citation references to the backend (LaTeX/BibTeX/LaTeX). Use the `unknown_reference_resolvers` hook of the `TransformSpec` to mark citation references as resolved if BibTeX is used. Advantages: * replaces hard-coded special-case in `transforms.references.DanglingReferencesVisitor` introduced in [r9312], * provides a usage example, * tests the hook with the Docutils test suite without additional overhead. Update docstrings and comments. Modified Paths: -------------- trunk/docutils/HISTORY.rst trunk/docutils/docutils/__init__.py trunk/docutils/docutils/transforms/__init__.py trunk/docutils/docutils/transforms/references.py trunk/docutils/docutils/writers/latex2e/__init__.py Modified: trunk/docutils/HISTORY.rst =================================================================== --- trunk/docutils/HISTORY.rst 2024-09-12 12:57:48 UTC (rev 9929) +++ trunk/docutils/HISTORY.rst 2024-09-13 08:13:03 UTC (rev 9930) @@ -158,6 +158,8 @@ (ignored since at least 2012). - Don't wrap references with custom reference-label_ in a ``\hyperref`` command. + - Provide an "unknown_references_resolver" (cf. `docutils/TransformSpec`) + for citation references resolved with BibTeX (cf. `use_bibtex`_ setting). .. _reference-label: docs/user/config.html#reference-label @@ -3856,9 +3858,9 @@ * docutils/transforms/__init__.py: - Added ``Transformer`` class and completed transform reform. - - Added unknown_reference_resolvers list for each transformer. This list holds - the list of functions provided by each component of the transformer that - help resolve references. + - Added unknown_reference_resolvers list for each transformer. + This list holds the list of functions provided by each component + of the transformer that help resolve references. * docutils/transforms/frontmatter.py: Modified: trunk/docutils/docutils/__init__.py =================================================================== --- trunk/docutils/docutils/__init__.py 2024-09-12 12:57:48 UTC (rev 9929) +++ trunk/docutils/docutils/__init__.py 2024-09-13 08:13:03 UTC (rev 9930) @@ -282,8 +282,11 @@ default_transforms: ClassVar[tuple[()]] = () unknown_reference_resolvers: Sequence[_UnknownReferenceResolver] = () - """List of functions to try to resolve unknown references. + """List of hook functions which assist in resolving references. + Override in subclasses to implement component-specific resolving of + unknown references. + Unknown references have a 'refname' attribute which doesn't correspond to any target in the document. Called when the transforms in `docutils.transforms.references` are unable to find a correct target. @@ -291,7 +294,7 @@ The list should contain functions which will try to resolve unknown references, with the following signature:: - def reference_resolver(node): + def reference_resolver(node: nodes.Element) -> bool: '''Returns boolean: true if resolved, false if not.''' If the function is able to resolve the reference, it should also remove @@ -301,13 +304,20 @@ node.resolved = True Each function must have a "priority" attribute which will affect the order - the unknown_reference_resolvers are run:: + the unknown_reference_resolvers are run + cf. ../docs/api/transforms.html#transform-priority-range-categories :: - reference_resolver.priority = 100 + reference_resolver.priority = 500 - This hook is provided for 3rd party extensions. - Example use case: the `MoinMoin - ReStructured Text Parser` - https://github.com/moinwiki/moin + Examples: + `writers.latex2e.Writer` defines a resolver to mark citation references + as resolved by BibTeX if the "use_bibtex" configuration setting is set. + + The `MoinMoin ReStructured Text Parser`__ provides a resolver for + "WikiWiki links" (currently only in the outdated 1.9 version). + + __ https://github.com/moinwiki/moin-1.9/blob/1.9.11/MoinMoin/parser/ + text_rst.py """ Modified: trunk/docutils/docutils/transforms/__init__.py =================================================================== --- trunk/docutils/docutils/transforms/__init__.py 2024-09-12 12:57:48 UTC (rev 9929) +++ trunk/docutils/docutils/transforms/__init__.py 2024-09-13 08:13:03 UTC (rev 9930) @@ -144,7 +144,7 @@ def populate_from_components(self, components) -> None: """ - Store each component's default transforms and reference resolvers + Store each component's default transforms and reference resolvers. Transforms are stored with default priorities for later sorting. "Unknown reference resolvers" are sorted and stored. @@ -161,7 +161,7 @@ resolvers.extend(component.unknown_reference_resolvers) self.sorted = False # sort transform list in self.apply_transforms() - # Sort and add helper functions to help resolve unknown references. + # Sort and add hook functions helping to resolve unknown references. def keyfun(f): return f.priority resolvers.sort(key=keyfun) Modified: trunk/docutils/docutils/transforms/references.py =================================================================== --- trunk/docutils/docutils/transforms/references.py 2024-09-12 12:57:48 UTC (rev 9929) +++ trunk/docutils/docutils/transforms/references.py 2024-09-13 08:13:03 UTC (rev 9930) @@ -888,37 +888,33 @@ return refname = node['refname'] id = self.document.nameids.get(refname) - if id is None: - for resolver_function in self.unknown_reference_resolvers: - if resolver_function(node): - break - else: - if (getattr(self.document.settings, 'use_bibtex', False) - and isinstance(node, nodes.citation_reference)): - # targets added from BibTeX database by LaTeX - node.resolved = True - return - if refname in self.document.nameids: - msg = self.document.reporter.error( - 'Duplicate target name, cannot be used as a unique ' - 'reference: "%s".' % (node['refname']), base_node=node) - else: - msg = self.document.reporter.error( - f'Unknown target name: "{node["refname"]}".', - base_node=node) - msgid = self.document.set_id(msg) - prb = nodes.problematic( - node.rawsource, node.rawsource, refid=msgid) - try: - prbid = node['ids'][0] - except IndexError: - prbid = self.document.set_id(prb) - msg.add_backref(prbid) - node.replace_self(prb) - else: + if id is not None: + # target found, set refid del node['refname'] node['refid'] = id self.document.ids[id].note_referenced_by(id=id) node.resolved = True + return + # Apply component-specific resolving functions (cf. TransformSpec): + for resolver_function in self.unknown_reference_resolvers: + if resolver_function(node): + return + # Report unresolved references: + if refname in self.document.nameids: + msg = self.document.reporter.error( + 'Duplicate target name, cannot be used as a unique ' + 'reference: "%s".' % (node['refname']), base_node=node) + else: + msg = self.document.reporter.error( + f'Unknown target name: "{node["refname"]}".', + base_node=node) + msgid = self.document.set_id(msg) + prb = nodes.problematic(node.rawsource, node.rawsource, refid=msgid) + try: + prbid = node['ids'][0] + except IndexError: + prbid = self.document.set_id(prb) + msg.add_backref(prbid) + node.replace_self(prb) visit_footnote_reference = visit_citation_reference = visit_reference Modified: trunk/docutils/docutils/writers/latex2e/__init__.py =================================================================== --- trunk/docutils/docutils/writers/latex2e/__init__.py 2024-09-12 12:57:48 UTC (rev 9929) +++ trunk/docutils/docutils/writers/latex2e/__init__.py 2024-09-13 08:13:03 UTC (rev 9930) @@ -253,7 +253,23 @@ def __init__(self) -> None: writers.Writer.__init__(self) self.translator_class = LaTeXTranslator + self.unknown_reference_resolvers = [self.bibtex_reference_resolver] + self.bibtex_reference_resolver.priority = 400 + @staticmethod + def bibtex_reference_resolver(node: nodes.Element) -> bool: + """Mark citation references as resolved if BibTeX is used. + + Cf. `TransformSpec.unknown_reference_resolvers`. + """ + if (isinstance(node, nodes.citation_reference) + and node.document.settings.use_bibtex): + del node['refname'] + node.resolved = True + return True + else: + return False + def get_transforms(self): # Override parent method to add latex-specific transforms return super().get_transforms() + [ This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |
From: <mi...@us...> - 2024-09-15 11:13:45
|
Revision: 9931 http://sourceforge.net/p/docutils/code/9931 Author: milde Date: 2024-09-15 11:13:43 +0000 (Sun, 15 Sep 2024) Log Message: ----------- HTML writers: Add "px" to unitless table "width" values. The table "width" attribute is of type "length", with optional unit. The writers must ensure there is a unit when converting a length to a HTML "style" attribute rule. For HTML writers, the default length unit is "px". Modified Paths: -------------- trunk/docutils/HISTORY.rst trunk/docutils/docutils/writers/_html_base.py trunk/docutils/docutils/writers/html4css1/__init__.py trunk/docutils/docutils/writers/html5_polyglot/__init__.py trunk/docutils/test/test_writers/test_html4css1.py trunk/docutils/test/test_writers/test_html5_polyglot.py Modified: trunk/docutils/HISTORY.rst =================================================================== --- trunk/docutils/HISTORY.rst 2024-09-13 08:13:03 UTC (rev 9930) +++ trunk/docutils/HISTORY.rst 2024-09-15 11:13:43 UTC (rev 9931) @@ -147,10 +147,12 @@ - Make MathML the default math_output_. - Revise image size handling methods, use "width" and "height" attributes for unitless values. + - Add "px" to unitless table "width" values. * docutils/writers/html4css1/__init__.py - Keep default math_output_ value "HTML math.css". + - Add "px" to unitless table "width" values. * docutils/writers/latex2e/__init__.py Modified: trunk/docutils/docutils/writers/_html_base.py =================================================================== --- trunk/docutils/docutils/writers/_html_base.py 2024-09-13 08:13:03 UTC (rev 9930) +++ trunk/docutils/docutils/writers/_html_base.py 2024-09-15 11:13:43 UTC (rev 9931) @@ -1693,7 +1693,10 @@ if 'align' in node: atts['classes'].append('align-%s' % node['align']) if 'width' in node: - atts['style'] = 'width: %s;' % node['width'] + width = node['width'] + if width[-1:] in '0123456789.': # unitless value + width += 'px' # add default length unit + atts['style'] = f'width: {width};' tag = self.starttag(node, 'table', **atts) self.body.append(tag) Modified: trunk/docutils/docutils/writers/html4css1/__init__.py =================================================================== --- trunk/docutils/docutils/writers/html4css1/__init__.py 2024-09-13 08:13:03 UTC (rev 9930) +++ trunk/docutils/docutils/writers/html4css1/__init__.py 2024-09-15 11:13:43 UTC (rev 9931) @@ -866,7 +866,10 @@ if 'align' in node: classes.append('align-%s' % node['align']) if 'width' in node: - atts['style'] = 'width: %s' % node['width'] + width = node['width'] + if width[-1:] in '0123456789.': # unitless value + width += 'px' # add default length unit + atts['style'] = f'width: {width}' self.body.append( self.starttag(node, 'table', CLASS=' '.join(classes), **atts)) Modified: trunk/docutils/docutils/writers/html5_polyglot/__init__.py =================================================================== --- trunk/docutils/docutils/writers/html5_polyglot/__init__.py 2024-09-13 08:13:03 UTC (rev 9930) +++ trunk/docutils/docutils/writers/html5_polyglot/__init__.py 2024-09-15 11:13:43 UTC (rev 9931) @@ -200,7 +200,7 @@ # use new HTML5 <figure> and <figcaption> elements def visit_figure(self, node) -> None: atts = {} - if node.get('width'): + if 'width' in node: atts['style'] = f"width: {node['width']}" if node.get('align'): atts['class'] = f"align-{node['align']}" Modified: trunk/docutils/test/test_writers/test_html4css1.py =================================================================== --- trunk/docutils/test/test_writers/test_html4css1.py 2024-09-13 08:13:03 UTC (rev 9930) +++ trunk/docutils/test/test_writers/test_html4css1.py 2024-09-15 11:13:43 UTC (rev 9931) @@ -183,6 +183,7 @@ ["""\ .. table:: :align: right + :width: 320 +-----+-----+ | 1 | 2 | @@ -191,7 +192,7 @@ +-----+-----+ """, """\ -<table border="1" class="docutils align-right"> +<table border="1" class="docutils align-right" style="width: 320px"> <colgroup> <col width="50%" /> <col width="50%" /> Modified: trunk/docutils/test/test_writers/test_html5_polyglot.py =================================================================== --- trunk/docutils/test/test_writers/test_html5_polyglot.py 2024-09-13 08:13:03 UTC (rev 9930) +++ trunk/docutils/test/test_writers/test_html5_polyglot.py 2024-09-15 11:13:43 UTC (rev 9931) @@ -218,6 +218,7 @@ ["""\ .. table:: :align: right + :width: 320 +-----+-----+ | 1 | 2 | @@ -226,7 +227,7 @@ +-----+-----+ """, """\ -<table class="align-right"> +<table class="align-right" style="width: 320px;"> <tbody> <tr><td><p>1</p></td> <td><p>2</p></td> This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |