Skip to content

load() + save() round-trip duplicates the manifest root entry (full-path="/"), producing files LibreOffice reports as corrupt #153

Description

@braklo

Summary

Loading an existing ODF document with load() and saving it produces a META-INF/manifest.xml containing two <manifest:file-entry manifest:full-path="/"> entries. LibreOffice then reports the resulting file as "damaged … repair?". Only the root / entry is duplicated; all other entries are correct.

Version

  • odfpy 1.4.2 (PyPI / openSUSE python313-odfpy)
  • Python 3.13.13, Linux
  • Confirmed against LibreOffice 26.2

Minimal reproduction (self-contained, no external file)

from odf.opendocument import OpenDocumentText, load
from odf.text import P
import zipfile

d = OpenDocumentText()
d.text.addElement(P(text="hello"))
d.save("min1.odt")                    # manifest: 1x full-path="/"  (OK)
load("min1.odt").save("min2.odt")     # manifest: 2x full-path="/"  (BUG)

def roots(p):
    return zipfile.ZipFile(p).read("META-INF/manifest.xml").decode().count('full-path="/"')

print(roots("min1.odt"), roots("min2.odt"))   # -> 1 2

min2.odt opens in LibreOffice with a "the file is corrupt and cannot be opened … repair?" dialog.

Expected vs actual

  • Expected: exactly one root file-entry after a round-trip.
  • Actual: two full-path="/" entries → LibreOffice flags the file as corrupt.

Root cause

In load() (opendocument.py, ~L1004–1008), every manifest entry that isn't otherwise handled is appended to doc._extra as an OpaqueObject. The manifest's root entry full-path="/" falls into this else branch (its path ends in /), so it is added to _extra.

On save, __manifestxml() emits the root entry itself (opendocument.py L672: FileEntry(fullpath="/", mediatype=...)) and re-emits the copy collected in _extra → duplicate.

Suggested fix

Skip the root entry when populating _extra during load, e.g. guard the else branch with and mentry != "/", so the root is produced only once by __manifestxml().

Impact

Any load → modify → save workflow (e.g. filling an existing template) produces files LibreOffice considers corrupt.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions