Hello Werner,
I found two XML escaping bugs in the XLSX writer (xlsxooxml.pas) that produce invalid OOXML files, causing openpyxl/lxml to fail with
XMLSyntaxError: attributes construct error.
Bug #1: Unescaped quotes in font namesIn
TsSpreadOOXMLWriter.WriteFont (line ~5783),
AFont.FontName is inserted into an XML attribute without escaping:
if AFont.FontName <> '' then
s := s + Format('<%s val="%s" />', [NAME_TAG[UseInStyleNode], AFont.FontName]);
If the font name contains a double quote, the XML is malformed:
<!-- Produced (invalid): -->
<name val=""Google Sans"" />
<!-- Expected: -->
<name val=""Google Sans"" />
Bug #2: Unescaped ampersands in hyperlink targetsIn
TsSpreadOOXMLWriter.WriteWorksheetRels (line ~7372), the hyperlink
target URL is inserted without escaping:
s := Format('Id="rId%d" Target="%s" TargetMode="External" Type="%s"',
[rId_Hyperlink, target, SCHEMAS_HYPERLINK]);
URLs with query parameters (e.g.
?a=1&b=2) produce invalid XML:
<!-- Produced (invalid): -->
<Relationship Target="https://example.com/page?a=1&b=2" ... />
<!-- Expected: -->
<Relationship Target="https://example.com/page?a=1&b=2" ... />
Suggested fixBoth can be fixed using the existing
UTF8TextToXMLText() function from
fpsxmlcommon (already in the uses clause):
Bug #1:s := s + Format('<%s val="%s" />', [NAME_TAG[UseInStyleNode], UTF8TextToXMLText(AFont.FontName)]);
Bug #2:s := Format('Id="rId%d" Target="%s" TargetMode="External" Type="%s"',
[rId_Hyperlink, UTF8TextToXMLText(target), SCHEMAS_HYPERLINK]);
Reproduction1. Create a workbook with a font name containing
" (e.g.
"Google Sans")
2. Add a hyperlink with a URL containing
& (e.g.
https://example.com/page?a=1&b=2)
3. Save as .xlsx
4. Load with
openpyxl.load_workbook("file.xlsx") →
lxml.etree.XMLSyntaxErrorI've also filed this on GitHub:
https://github.com/veb86/fpspreadsheet/issues/1Thank you!