From d14032fe2e28a61c150e16d927a9118273b3f842 Mon Sep 17 00:00:00 2001 From: Mats Wichmann Date: Sun, 26 Jul 2026 16:36:13 -0600 Subject: [PATCH] Eliminate xml table conflict This is an AI analysis of errors reported by Apache fop. A conflict arises when building PDFs of docs - informaltable elements want to use proportional columns, FOP doesn't support these, so it falls back to fixed widths which are incompatible with some Docbook syntax - this isn't SCons' problem per se, just a hole we fall into because of using fop, plus wrapping lxml - something that wasn't the default until quite a bit after this code was merged in the first place. [ERROR] FOUserAgent - Invalid property value encountered in column-width="proportional-column-width(1)": org.apache.fop.fo.expr.PropertyException: file:/home/mats/github/scons/build/doc/user/scons-user.fo:12435:16: proportional-column-width() function may only be used when fo:table has table-layout="fixed".; property:'column-width' (See position 12435:95) Various approaches were explored - some work in this direction had been partially done, as $DOCBOOK_XSLTFLAGS provides a way to pass flags through, but the docbook tool doesn't actually parse that setting ("parampass" is initialized to an empty dict but never populated with parse results - it now has a comment to that effect in case it turns up needed in future)). Fixing that the "obvious" way opens up some other holes, so instead a fairly simplminded postprocessing step is added to the docbook tool. Various approaches were explored - some work in this direction had been partially done, as `$DOCBOOK_XSLTPROCFLAGS` provides a way to pass flags through, but while the docbook tool recognizes that in general, it doesn't actually parse it when the lxml wrapping is used: `parampass` is initialized to an empty dict but never populated with parse results - it now has a comment to that effect in case it turns up needed in future. lxml usage wasn't the default before, it became so after the older approach was retired by upstream. Fixing that the "obvious" way, by adding the flags in each doc SConstruct and parsing the value, opens up some other holes, so instead a fairly simplminded postprocessing step is added to the docbook tool. Signed-off-by: Mats Wichmann Assisted-by: OpenCode 1.18.5 driving Qwen 3.8 --- CHANGES.txt | 3 +- RELEASE.txt | 3 +- SCons/Tool/docbook/__init__.py | 14 +++++++- doc/design/SConstruct | 63 ++++++++++++---------------------- doc/developer/SConstruct | 40 ++++++--------------- doc/man/SConstruct | 8 +---- doc/python10/SConstruct | 40 ++++++--------------- doc/reference/SConstruct | 57 ++++++++++++------------------ doc/reference/copyright.xml | 2 +- doc/user/SConstruct | 7 +--- 10 files changed, 82 insertions(+), 155 deletions(-) diff --git a/CHANGES.txt b/CHANGES.txt index f03b8164ad..f74ec9eda4 100644 --- a/CHANGES.txt +++ b/CHANGES.txt @@ -149,7 +149,8 @@ RELEASE VERSION/DATE TO BE FILLED IN LATER - Clarify internal usage of zipimporter (not user-visible code) - Fixed an old problem in PDF doc generation where the path to an image file ended up with a "not found" error. - - Fixed a PDF doc build problem relating to illegal margin widths (""). + - Fixed PDF doc build problems relating to illegal margin widths ("") + and table column alignment. - Reference manual improvements: * More clarifications in Builder Methods section. * Clarify VariantDir behavior when switching from duplicate=True (the diff --git a/RELEASE.txt b/RELEASE.txt index a12b4c655f..382d791b83 100644 --- a/RELEASE.txt +++ b/RELEASE.txt @@ -187,7 +187,8 @@ DOCUMENTATION - Fixed an old problem in PDF doc generation where the path to an image file ended up with a "not found" error. -- Fixed a PDF doc build problem relating to illegal margin widths (""). +- Fixed PDF doc build problems relating to illegal margin widths ("") + and table column alignment. DEVELOPMENT ----------- diff --git a/SCons/Tool/docbook/__init__.py b/SCons/Tool/docbook/__init__.py index f0e9ef5077..6e0e4672d7 100644 --- a/SCons/Tool/docbook/__init__.py +++ b/SCons/Tool/docbook/__init__.py @@ -313,13 +313,25 @@ def __build_lxml(target, source, env): xsl_tree = etree.parse(xsl_style) transform = etree.XSLT(xsl_tree, access_control=xslt_ac) doc = etree.parse(str(source[0])) - # Support for additional parameters + # NOTE: if someone ever wants to pass XSLT params via DOCBOOK_XSLTPROCFLAGS + # we should actually parse that here - look for --stringparam flags. parampass = {} if parampass: result = transform(doc, **parampass) else: result = transform(doc) + # Ensure all fo:table elements have table-layout="fixed". + # FOP uses proportional-column-width(1) for unspecified columns even + # without table-layout="fixed", which is invalid per XSL-FO spec and + # produces SEVERE errors. The docbook-xsl stylesheets only add + # table-layout="fixed" when there are proportional columns or fop + # extensions are enabled; FOP extension params generate fox: namespace + # elements that FOP itself warns about, so we fix it here instead. + for table in result.iter('{http://www.w3.org/1999/XSL/Format}table'): + if table.get('table-layout') is None: + table.set('table-layout', 'fixed') + try: with open(str(target[0]), "wb") as of: of.write(etree.tostring(result, encoding="utf-8", pretty_print=True)) diff --git a/doc/design/SConstruct b/doc/design/SConstruct index bda67d7870..5418ccf4c1 100644 --- a/doc/design/SConstruct +++ b/doc/design/SConstruct @@ -1,54 +1,33 @@ -# -# SConstruct file for building SCons documentation. -# +# SPDX-FileCopyrightText: Copyright The SCons Foundation (https://scons.org) +# SPDX-License-Identifier: MIT -# -# __COPYRIGHT__ -# -# Permission is hereby granted, free of charge, to any person obtaining -# a copy of this software and associated documentation files (the -# "Software"), to deal in the Software without restriction, including -# without limitation the rights to use, copy, modify, merge, publish, -# distribute, sublicense, and/or sell copies of the Software, and to -# permit persons to whom the Software is furnished to do so, subject to -# the following conditions: -# -# The above copyright notice and this permission notice shall be included -# in all copies or substantial portions of the Software. -# -# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY -# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE -# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND -# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE -# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION -# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION -# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - -""" -SConstruct file for building SCons documentation. """ +SConstruct file for building SCons design document. +Currently way out of date and not built during a doc build. + +If invoked directly, can add SKIP_PDF=1 to avoid pdf and epub generation. +""" import os -env = Environment(ENV={'PATH' : os.environ['PATH']}, - tools=['docbook'], - toolpath=['../../SCons/Tool'], - DOCBOOK_DEFAULT_XSL_HTML='html.xsl', - DOCBOOK_DEFAULT_XSL_HTMLCHUNKED='chtml.xsl', - DOCBOOK_DEFAULT_XSL_PDF='pdf.xsl') +env = Environment( + ENV={'PATH': os.environ['PATH']}, + tools=['docbook'], + toolpath=['../../SCons/Tool'], + # DOCBOOK_XSLTPROCFLAGS="--stringparam fop.extensions 1", + DOCBOOK_DEFAULT_XSL_HTML='html.xsl', + DOCBOOK_DEFAULT_XSL_HTMLCHUNKED='chtml.xsl', + DOCBOOK_DEFAULT_XSL_PDF='pdf.xsl', +) +skip_pdf = ARGUMENTS.get('SKIP_PDF', False) has_pdf = False -if (env.WhereIs('fop') or - env.WhereIs('xep')): +if not skip_pdf and any((env.WhereIs('fop'), env.WhereIs('xep'))): has_pdf = True -# -# Create document -# env.DocbookXInclude('design_xi.xml', 'main.xml') -env.DocbookXslt('design.xml', 'design_xi.xml', - xsl='../xslt/to_docbook.xslt') -env.DocbookHtmlChunked('index.html','design.xml', base_dir='scons-design/') +env.DocbookXslt('design.xml', 'design_xi.xml', xsl='../xslt/to_docbook.xslt') +env.DocbookHtmlChunked('index.html', 'design.xml', base_dir='scons-design/') if has_pdf: - env.DocbookPdf('scons-design.pdf','design.xml') + env.DocbookPdf('scons-design.pdf', 'design.xml') diff --git a/doc/developer/SConstruct b/doc/developer/SConstruct index bd1c904549..8480a2e37d 100644 --- a/doc/developer/SConstruct +++ b/doc/developer/SConstruct @@ -1,45 +1,25 @@ -# -# SConstruct file for building SCons documentation. -# +# SPDX-FileCopyrightText: Copyright The SCons Foundation (https://scons.org) +# SPDX-License-Identifier: MIT -# -# __COPYRIGHT__ -# -# Permission is hereby granted, free of charge, to any person obtaining -# a copy of this software and associated documentation files (the -# "Software"), to deal in the Software without restriction, including -# without limitation the rights to use, copy, modify, merge, publish, -# distribute, sublicense, and/or sell copies of the Software, and to -# permit persons to whom the Software is furnished to do so, subject to -# the following conditions: -# -# The above copyright notice and this permission notice shall be included -# in all copies or substantial portions of the Software. -# -# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY -# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE -# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND -# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE -# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION -# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION -# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +""" +SConstruct file for building SCons documentation. + +The Developer Guide is only a stub, not build during a doc build. +""" import os env = Environment(ENV={'PATH' : os.environ['PATH']}, - tools=['docbook'], + tools=['docbook'], toolpath=['../../SCons/Tool']) has_pdf = False -if (env.WhereIs('fop') or +if (env.WhereIs('fop') or env.WhereIs('xep')): has_pdf = True -# -# Create document -# env.DocbookXInclude('developer_xi.xml', 'main.xml') -env.DocbookXslt('developer.xml', 'developer_xi.xml', +env.DocbookXslt('developer.xml', 'developer_xi.xml', xsl='../xslt/to_docbook.xslt') env.DocbookHtml('developer.html','developer.xml') if has_pdf: diff --git a/doc/man/SConstruct b/doc/man/SConstruct index 5c6d4fabb9..df07ffaf61 100644 --- a/doc/man/SConstruct +++ b/doc/man/SConstruct @@ -15,6 +15,7 @@ env = Environment( ENV={'PATH': os.environ['PATH']}, tools=['docbook', 'gs', 'zip'], toolpath=['../../SCons/Tool'], + # DOCBOOK_XSLTPROCFLAGS="--stringparam fop.extensions 1", DOCBOOK_DEFAULT_XSL_HTML='html.xsl', DOCBOOK_DEFAULT_XSL_PDF='pdf.xsl', ) @@ -35,10 +36,6 @@ def createManPages(env, target): if has_pdf: env.DocbookPdf('scons-%s.pdf' % target, '%s_db.xml' % target) - -# -# Create MAN pages -# createManPages(env, "scons") createManPages(env, "sconsign") createManPages(env, "scons-time") @@ -47,9 +44,6 @@ has_gs = False if env.WhereIs('gs'): has_gs = True -# -# Create the EPUB format -# if has_gs and has_pdf: jpg = env.Gs( 'OEBPS/cover.jpg', diff --git a/doc/python10/SConstruct b/doc/python10/SConstruct index 2928bda4e6..2bfb2e1d39 100644 --- a/doc/python10/SConstruct +++ b/doc/python10/SConstruct @@ -1,45 +1,25 @@ -# -# SConstruct file for building SCons documentation. -# +# SPDX-FileCopyrightText: Copyright The SCons Foundation (https://scons.org) +# SPDX-License-Identifier: MIT -# -# __COPYRIGHT__ -# -# Permission is hereby granted, free of charge, to any person obtaining -# a copy of this software and associated documentation files (the -# "Software"), to deal in the Software without restriction, including -# without limitation the rights to use, copy, modify, merge, publish, -# distribute, sublicense, and/or sell copies of the Software, and to -# permit persons to whom the Software is furnished to do so, subject to -# the following conditions: -# -# The above copyright notice and this permission notice shall be included -# in all copies or substantial portions of the Software. -# -# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY -# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE -# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND -# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE -# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION -# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION -# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +""" +SConstruct file for building SCons documentation. + +This overview is not maintained and is not built during the doc build. +""" import os env = Environment(ENV={'PATH' : os.environ['PATH']}, - tools=['docbook'], + tools=['docbook'], toolpath=['../../SCons/Tool']) has_pdf = False -if (env.WhereIs('fop') or +if (env.WhereIs('fop') or env.WhereIs('xep')): has_pdf = True -# -# Create document -# env.DocbookXInclude('python10_xi.xml', 'main.xml') -env.DocbookXslt('python10.xml', 'python10_xi.xml', +env.DocbookXslt('python10.xml', 'python10_xi.xml', xsl='../xslt/to_docbook.xslt') env.DocbookHtml('python10.html','python10.xml') if has_pdf: diff --git a/doc/reference/SConstruct b/doc/reference/SConstruct index edc21a0db5..b6c61100a0 100644 --- a/doc/reference/SConstruct +++ b/doc/reference/SConstruct @@ -1,48 +1,33 @@ -# -# SConstruct file for building SCons documentation. -# +# SPDX-FileCopyrightText: Copyright The SCons Foundation (https://scons.org) +# SPDX-License-Identifier: MIT -# -# __COPYRIGHT__ -# -# Permission is hereby granted, free of charge, to any person obtaining -# a copy of this software and associated documentation files (the -# "Software"), to deal in the Software without restriction, including -# without limitation the rights to use, copy, modify, merge, publish, -# distribute, sublicense, and/or sell copies of the Software, and to -# permit persons to whom the Software is furnished to do so, subject to -# the following conditions: -# -# The above copyright notice and this permission notice shall be included -# in all copies or substantial portions of the Software. -# -# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY -# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE -# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND -# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE -# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION -# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION -# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +""" +SConstruct file for building SCons Reference Manual. + +This is mostly a stub, and is not built as part of the doc build. + +If invoked directly, can add SKIP_PDF=1 to avoid pdf and epub generation. +""" import os -env = Environment(ENV={'PATH' : os.environ['PATH']}, - tools=['docbook'], - toolpath=['../../SCons/Tool'], - DOCBOOK_DEFAULT_XSL_HTML='html.xsl', - DOCBOOK_DEFAULT_XSL_HTMLCHUNKED='chtml.xsl', - DOCBOOK_DEFAULT_XSL_PDF='pdf.xsl') +env = Environment( + ENV={'PATH': os.environ['PATH']}, + tools=['docbook'], + toolpath=['../../SCons/Tool'], + # DOCBOOK_XSLTPROCFLAGS="--stringparam fop.extensions 1", + DOCBOOK_DEFAULT_XSL_HTML='html.xsl', + DOCBOOK_DEFAULT_XSL_HTMLCHUNKED='chtml.xsl', + DOCBOOK_DEFAULT_XSL_PDF='pdf.xsl', +) +skip_pdf = ARGUMENTS.get('SKIP_PDF', False) has_pdf = False -if (env.WhereIs('fop') or - env.WhereIs('xep')): +if not skip_pdf and any((env.WhereIs('fop'), env.WhereIs('xep'))): has_pdf = True -# -# Create document -# env.DocbookXInclude('reference_xi.xml', 'main.xml') -env.DocbookXslt('reference.xml', 'reference_xi.xml', +env.DocbookXslt('reference.xml', 'reference_xi.xml', xsl='../xslt/to_docbook.xslt') env.DocbookHtml('index.html','reference.xml') env.DocbookHtmlChunked('index.html', 'reference.xml', base_dir='scons-reference/') diff --git a/doc/reference/copyright.xml b/doc/reference/copyright.xml index dcf169d540..511360adb5 100644 --- a/doc/reference/copyright.xml +++ b/doc/reference/copyright.xml @@ -36,7 +36,7 @@
- SCons User's Guide Copyright (c) 2003 Steven Knight + SCons Reference Manual Copyright (c) 2003 Steven Knight
diff --git a/doc/user/SConstruct b/doc/user/SConstruct index 69792ae3e6..f479b26234 100644 --- a/doc/user/SConstruct +++ b/doc/user/SConstruct @@ -15,7 +15,7 @@ env = Environment( ENV={'PATH': os.environ['PATH']}, tools=['docbook', 'gs', 'zip'], toolpath=['../../SCons/Tool'], - # DOCBOOK_XSLTPROCFLAGS="--stringparam fop.extensions 1", + # DOCBOOK_XSLTPROCFLAGS="--stringparam fop.extensions 1", DOCBOOK_DEFAULT_XSL_HTML='html.xsl', DOCBOOK_DEFAULT_XSL_HTMLCHUNKED='chtml.xsl', DOCBOOK_DEFAULT_XSL_PDF='pdf.xsl', @@ -26,9 +26,7 @@ has_pdf = False if not skip_pdf and any((env.WhereIs('fop'), env.WhereIs('xep'))): has_pdf = True -# # UserGuide for SCons -# env.DocbookXInclude('scons_xi.xml', 'main.xml') env.DocbookXslt('scons_ex.xml', 'scons_xi.xml', xsl='../xslt/xinclude_examples.xslt') env.DocbookXInclude('scons_exi.xml', 'scons_ex.xml') @@ -42,9 +40,6 @@ has_gs = False if env.WhereIs('gs'): has_gs = True -# -# Create the EPUB format -# if has_gs and has_pdf: jpg = env.Gs( 'OEBPS/cover.jpg',