Skip to content

PowerPoint2007 Reader : Read a slide number and a date with their styling - #916

Open
dkulyk wants to merge 3 commits into
PHPOffice:masterfrom
sapientpro:fix/reader-field
Open

PowerPoint2007 Reader : Read a slide number and a date with their styling#916
dkulyk wants to merge 3 commits into
PHPOffice:masterfrom
sapientpro:fix/reader-field

Conversation

@dkulyk

@dkulyk dkulyk commented Aug 22, 2026

Copy link
Copy Markdown
Contributor

Description

Depends on #914 — it rewrites the body of the same loop. The first two commits are that PR (and @Wilkolicious's #871 under it); only the last one belongs to this review.

What is wrong

a:fld is a run whose text the application recomputes: the number of the slide it ended up on, the date the file is opened on. The Writer emits one for every sldNum or dt placeholder (AbstractSlide.php:326), but the Reader walked (a:r|a:br) only, so the element was skipped and everything inside it with it.

The shape survives the round trip — the placeholder is restored from p:ph further up, and the Writer re-derives the field from it — so a deck keeps its numbering. What it loses is how that number looks. Measured on a sldNum placeholder styled Georgia 18pt bold red:

before after
placeholder read back sldNum sldNum
runs in the paragraph 0 1
a:fld on re-save present, no a:rPr at all full a:rPr

dt behaves identically.

The change

CT_TextField holds the same a:rPr and a:t as CT_RegularTextRun, so the element is read by the branch that already reads a run — the XPath and the condition, nothing else. Both field types are covered at once, the kind being an attribute rather than a separate element.

One deliberate consequence

A shape that is not a placeholder but holds a field — page <slidenum> of 12 in a plain text box — now reads its stand-in text as an ordinary run, where it used to be dropped. The text of the file is kept rather than lost, but a field is not expressible in the model, so re-saving that shape freezes it into dead text.

Both outcomes are wrong; I think keeping the text is the better of the two, and it is easy to reverse if you disagree. What actually closes it is a field element of its own next to Run and BreakElement, which is a larger change and a separate PR. For reference, that is how LibreOffice models it — class TextField final : public TextRun, and both its OOXML and its ODF exporters decide per run rather than per shape.

Checklist:

  • My CI is 🟢
  • I have covered by unit tests my new code (check build/coverage for coverage report)

    PowerPoint2007Test::testFieldKeepsItsStyle, over both field types; the fixture is the library's own output, so nothing binary is added. Each half of the fix was checked by breaking it — without a:fld in the XPath, and without it in the condition — and both cases fail with actual size 0.

  • I have updated the documentation to describe the changes

    Reader behaviour; no documented API changed. docs/usage/slides/layout.md already describes the fields, from ODPresentation Writer : Write the number of a slide and its date as fields #913.

  • I have updated the changelog

@coveralls

coveralls commented Aug 22, 2026

Copy link
Copy Markdown

Coverage Status

coverage: 92.159%. remained the same — sapientpro:fix/reader-field into PHPOffice:master

Phil Wilkinson and others added 3 commits August 23, 2026 15:00
The run is now made before `a:rPr` is looked at, which also moved `setText()`
out from under that guard -- so a run without `a:t`, which the schema forbids
but files carry anyway, reached `$oSubSubElement->nodeValue` on null. Look for
the text rather than assume it.

The tests write a deck, take `a:rPr` (or `a:t`) back out of the archive and read
it again, so neither needs a binary fixture. Keynote is the export that hits
this: one of its slides carries runs with text and no `a:rPr` at all, and every
one of them was read as nothing.

The seven `is_object()` checks in the rewritten block are now
`instanceof DOMElement` -- what the other ninety-four checks in this file use,
and what `getElement()` promises.
…n with

`a:fld` is a run whose text the application recomputes -- the number of the
slide it ended up on, the date the file is opened on. The Writer emits one for
every `sldNum` or `dt` placeholder (`AbstractSlide.php:326`), but the Reader
walked `(a:r|a:br)` only, so the element was skipped and everything inside it
with it.

The shape itself survived a round trip: the placeholder is restored from `p:ph`
further up, and the Writer re-derives the field from that, so a deck kept its
numbering. What it lost was how the number looked -- the paragraph came back
with no runs at all, and `a:fld` was written out again with no `a:rPr`, so
Georgia 18pt bold red became the default font.

`CT_TextField` holds the same `a:rPr` and `a:t` as `CT_RegularTextRun` does, so
the element is read by the branch that already reads a run. Both field types are
covered at once, the kind being an attribute rather than a separate element.

One deliberate consequence: a shape that is not a placeholder but holds a field
-- "page 7 of 12" in a plain text box -- now reads the stand-in text as an
ordinary run, where it used to be dropped. The text of the file is kept rather
than lost, but the field is not expressible in the model yet, so re-saving that
shape freezes it. Making a field a text element of its own, next to `Run` and
`BreakElement`, is what would close that; it is a larger change and a separate
one.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Development

Successfully merging this pull request may close these issues.

2 participants