Repository navigation
Conversation
The properties of a hyperlink's run were written by hand, because PHPWord's Link element holds one plain string and cannot carry a label with emphasis in it. They were written in the order they came to mind: the weight, the slant, the strike, the underline, the colour, the size and the typeface. w:rPr is the sequence EG_RPrBase in ECMA-376's wml.xsd and not a set, so a reader walks the children in the order they are written and stops at the first one it did not expect. The typeface comes before the weight, and the underline comes after the size, and a document that gets this wrong is one Word refuses to open. Microsoft's own validator reported "The element has unexpected child element 'w:color'" nineteen times across the example documents, every one of them a link whose label was styled. There are none of them now.
Two of them named a position in the schema's sequence, which is a number that goes stale the moment the schema is read again, and one of them said a run can hold every property, which is a claim about the schema rather than about this library.
|
This was referenced Oct 30, 2026
Collaborator
Author
|
Combined into #49, which carries this and the rest of the campaign in one pull request. Nothing is lost: the commits are in it, and the release notes are folded into the |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.



The defect
src/Writer/HyperlinkPass.phpwrites thew:rProf a hyperlink's run by hand —PHPWord's
Linkelement holds one plain string and cannot carry a label withemphasis in it, so the runs are recorded and written afterwards.
The children were appended in the order the properties came to mind:
w:rPris the sequenceEG_RPrBasein ECMA-376'swml.xsdand not a set. Areader walks the children in the order they are written and stops at the first
one it did not expect, and the schema names them:
So the order was wrong twice over: the typeface was written last where the
schema names it second, and the underline was written before the colour where
the schema names it after the size.
Microsoft's Open XML SDK validator:
Word opens a document that gets this wrong, because Word is lenient where the
schema is not. A reader that walks the sequence — a validator, a converter — does
not.
The fix
The children are written in the schema's order:
The measurement
Over the 29
.docxfiles inexamples/out/documents/(built withphp examples/build.php), with Microsoft's own validator:w:colorunexpected inside a hyperlink run'sw:rPrw:rPrThe three that are left are PHPWord's own: they are in a hyperlink PHPWord
wrote itself, on the path where the label has no emphasis in it, and they are
the same defect in
PhpOffice\PhpWord\Writer\Word2007\Style\Font::writeStyle(),which writes
color,sz,b,bCs,iin that order — and that isPHPOffice/PHPWord#2964. The
rest are defects in PHPWord and in the
.dotxtemplates, and are being fixedseparately.
The test
tests/Unit/hyperlink-run-properties.phpbuilds a document whose link carriesa typeface, a size, a colour, bold, italic, strikethrough and an underline,
and asserts the exact order of the children of the
w:rPrinside thehyperlink's run. All four of its tests fail without the fix and pass with it.
The audit
Every place in the repository that builds a
w:rPror aw:pPrby hand:src/Writer/HyperlinkPass.phpw:rProf a link's runsrc/Writer/BuiltInHeadingPass.phpw:outlineLvlin a heading style'sw:pPr, and thew:pPrin aw:stylesrc/Template/WordFields.phpw:rPris copied wholesrc/Writer/TocPass.phpw:rPrat allsrc/Writer/NumberingMerger.phpw:numinword/numbering.xmlsrc/Template/ContentControl.phpw:sdtmarkupw:rProrw:pPrexamples/build.phpw:pBdrin a style'sw:pPrBuiltInHeadingPassputsw:outlineLvlbefore thew:rProf thew:pPr,which is where
CT_PPrputs it:CT_PPris theCT_PPrBasesequence followedby
rPr?, andw:outlineLvlis the last of theCT_PPrBaseelements aheading style carries. The built documents read
<w:pPr><w:keepNext/><w:spacing/><w:outlineLvl/></w:pPr>and the validatorreports nothing for them.
A shared list would be better
The schema's order now lives in three places and in three forms: this change
puts it in the order the code writes,
BuiltInHeadingPassstates theCT_StyleandCT_PProrder in prose, andexamples/build.phphasAFTER_PBDR, a list of thew:pPrelements that followw:pBdr. A sharedlist of the two sequences would be better than three of them, and would give
BuiltInHeadingPassand the example build the same source. It is a largerchange than this defect, and it wants a test of its own.