Skip to content

Touching an OddFooter/OddHeader section (even just Center) corrupts on save #2479

Description

@dbronson4672

EPPlus usage

Commercial use (I have a commercial license)

Environment

Linux

Epplus version

8.7.0

Spreadsheet application

Excel

Description

Touching an OddFooter/OddHeader section (even just Center) corrupts its unread Left section on save

Confirmed on EPPlus 8.7.0.0.

Behavior

Reading a property on a worksheet's OddFooter (or OddHeader) -- even just CenteredText,
never touching LeftAlignedText/RightAlignedText at all, and never writing anything -- can
cause that footer's Left-aligned picture to silently disappear from the file on the next
SaveAs().

See it yourself: open the two attached files side by side in Excel.

  • oddfooter-left-corruption-template.xlsx -- the original, untouched template. All 4 footer
    corner images are visible.
  • oddfooter-left-corruption-after.xlsx -- produced by oddfooter-left-corruption-repro.ps1,
    which copies that template's worksheet into a new workbook, reads one header/footer text
    property, and saves. The bottom-left corner image is gone.

The picture is not merely hidden -- it is genuinely missing from what renders. And it isn't
missing from the object model either: HeaderFooter.Pictures.Count on the saved file still
reports the same count as before (the script prints this when it runs). So the object model
says the picture is still there, but it does not visually render, and nothing in either file
was ever explicitly deleted.

Confirmed triggers

Isolated across several variants of the repro:

  • Reading only LeftAlignedText (never touching CenteredText/RightAlignedText) -- the
    Left picture is lost on save.
  • Reading only CenteredText (never touching LeftAlignedText/RightAlignedText at all) --
    the Left picture is still lost on save.
  • Never touching the HeaderFooter object at all -- the file saves correctly, nothing lost.
  • Writing a new value to CenteredText (a normal find/replace on that text) -- the new value
    is correctly saved, but the Left picture is lost regardless.

So the trigger is touching the OddFooter/OddHeader object at all -- via any of its three
text properties, read or write -- not something specific to LeftAlignedText.

Real-world severity varies -- 1, 2, or all 4 corners can be lost

The minimal repro above always loses exactly one corner picture. In real generated documents
we've seen this same class of loss affect more than one corner, up to all 4, on sheets that
share an identical header/footer structure with each other and with the source template used
in the minimal repro above.

Example: 08941144.form-generator.xlsx, generated from a template whose header/footer
structure matches the attached repro template, ended up with all 4 corner pictures unreadable
by the object model's own text properties, even though HeaderFooter.Pictures.Count still
reported 4. The document-generation run that produced it made roughly 1,200 read/write calls
against header/footer text properties for that one sheet (once per field name attempted against
that document).

We have not been able to reliably reproduce a specific number of touches, or a specific
sequence, that reliably escalates the loss from 1 corner to 2 or to all 4. What we can say:

  • The same header/footer structure does not always produce the same amount of loss.
  • Documents/sheets that undergo many more header/footer property touches during generation are
    the ones we've observed losing more than one corner.
  • We are not claiming a confirmed cause-and-effect between touch count and severity -- only
    that this is the pattern observed so far, and we could not isolate a minimal repro for
    anything beyond the single-corner case documented above.

Repro

oddfooter-left-corruption-repro.ps1 (attached) against oddfooter-left-corruption-template.xlsx
(attached, a form template with a footer that has a Left-aligned picture and Center-aligned
text):

param(
    [string]$EPPlusDllPath = "/opt/EPPlus/EPPlus.dll",
    [string]$TemplatePath = (Join-Path $PSScriptRoot "oddfooter-left-corruption-template.xlsx"),
    [string]$OutputPath = (Join-Path $PSScriptRoot "oddfooter-left-corruption-after.xlsx")
)

Add-Type -Path $EPPlusDllPath
[OfficeOpenXml.ExcelPackage]::License.SetNonCommercialPersonal("Repro")

$src = [OfficeOpenXml.ExcelPackage]::new([IO.FileInfo]::new($TemplatePath))
$target = [OfficeOpenXml.ExcelPackage]::new()
$copied = $target.Workbook.Worksheets.Add("Copied", $src.Workbook.Worksheets[0])

# The ONLY interaction with HeaderFooter in this whole script: one read, no write.
$null = $copied.HeaderFooter.OddFooter.LeftAlignedText

if (Test-Path $OutputPath) { Remove-Item $OutputPath -Force }
$target.SaveAs([IO.FileInfo]::new($OutputPath))

Running it prints the object model's picture count (unchanged) and writes
oddfooter-left-corruption-after.xlsx, which is missing the bottom-left footer image when
opened in Excel.

oddfooter-left-corruption-after.xlsx
oddfooter-left-corruption-template.xlsx

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't working

    Type

    No type

    Projects

    Status
    Backlog

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions