docx/preserve-thumbnail

A Word-authored .docx with a docProps/thumbnail.jpeg preview image must still carry that thumbnail part (and its package-level rel) after a python-docx load + save round-trip.

Library verdicts: python-docx: — docxjs: —

Metadata

Feature iddocx/preserve-thumbnail
Formatdocx
Categoryroundtrip-fidelity
Spececma-376-5-part-2 § 9.2 thumbnail

XPath assertions

ID / partPredicateXPathpython-docxdocxjs
thumbnail-rel-preserved
_rels/.rels
exist
The package-level thumbnail rel must still exist in _rels/.rels after round-trip.
//pr:Relationship[@Type='http://schemas.openxmlformats.org/package/2006/relationships/metadata/thumbnail']
thumbnail-content-type-preserved
[Content_Types].xml
exist
The Default ContentType for .jpeg must still be declared so the thumbnail part is legal.
//ct:Default[@Extension='jpeg']

Render assertions

No render assertions declared.

Generator source

scripts/gen_preserve_thumbnail.py

#!/usr/bin/env python3
"""Generate ``fixtures/docx/preserve-thumbnail.docx``.

The machine fixture is a Word-authored .docx that has been round-tripped
through python-docx: open with ``docx.Document(<office>)`` then save back
with ``reproducible=True``. The source (.office.docx companion) is a
verbatim Word 2013-authored file that carries ``docProps/thumbnail.jpeg``;
the corresponding manifest asserts that the thumbnail relationship and
the image part's ``jpeg`` content-type default survive the round-trip.

Runs against current python-docx master will FAIL the manifest — that is
the point. python-docx currently drops the thumbnail unconditionally in
``opc/package.py::Package._drop_unused_package_rels``.
"""

from __future__ import annotations

from pathlib import Path

from docx import Document

_REPO_ROOT = Path(__file__).resolve().parent.parent
_OFFICE_SRC = _REPO_ROOT / "fixtures" / "docx" / "preserve-thumbnail.office.docx"
_OUT = _REPO_ROOT / "fixtures" / "docx" / "preserve-thumbnail.docx"


def main() -> int:
    doc = Document(str(_OFFICE_SRC))
    _OUT.parent.mkdir(parents=True, exist_ok=True)
    doc.save(str(_OUT), reproducible=True)
    print(_OUT)
    return 0


if __name__ == "__main__":
    raise SystemExit(main())

Manifest (expanded)

{
  "$schema": "../manifest.schema.json",
  "id": "docx/preserve-thumbnail",
  "title": "Preserve package thumbnail on load+save",
  "format": "docx",
  "category": "roundtrip-fidelity",
  "roles": [
    "authoring"
  ],
  "summary": "A Word-authored .docx with a docProps/thumbnail.jpeg preview image must still carry that thumbnail part (and its package-level rel) after a python-docx load + save round-trip.",
  "spec": {
    "source": "ecma-376-5-part-2",
    "clause": "9.2",
    "element": "thumbnail",
    "notes": "Open Packaging Conventions (ECMA-376 Part 2, clause 9.2) defines a package-level thumbnail relationship of type 'http://schemas.openxmlformats.org/package/2006/relationships/metadata/thumbnail'. Word renders this JPEG in Windows Explorer previews, Outlook attachment chips, and SharePoint thumbnails. The relationship, the target image part (docProps/thumbnail.jpeg by Word convention), and the 'jpeg' default content-type MUST survive a naive library round-trip. python-docx currently drops the thumbnail unconditionally in opc/package.py::Package._drop_unused_package_rels (the rationale 'python-docx has no thumbnail renderer' applies only when creating a new document; it should not throw away an incoming thumbnail on round-trip). The .office.docx companion is a verbatim Word 2013-authored file; the 'machine' fixture is produced by opening that file with python-docx and saving it back with reproducible=True."
  },
  "fixtures": {
    "machine": "docx/preserve-thumbnail",
    "office": "docx/preserve-thumbnail"
  },
  "generator": {
    "python": "scripts/gen_preserve_thumbnail.py"
  },
  "assertions": [
    {
      "id": "thumbnail-rel-preserved",
      "part": "_rels/.rels",
      "namespaces": {
        "pr": "http://schemas.openxmlformats.org/package/2006/relationships"
      },
      "xpath": "//pr:Relationship[@Type='http://schemas.openxmlformats.org/package/2006/relationships/metadata/thumbnail']",
      "must": "exist",
      "description": "The package-level thumbnail rel must still exist in _rels/.rels after round-trip."
    },
    {
      "id": "thumbnail-content-type-preserved",
      "part": "[Content_Types].xml",
      "namespaces": {
        "ct": "http://schemas.openxmlformats.org/package/2006/content-types"
      },
      "xpath": "//ct:Default[@Extension='jpeg']",
      "must": "exist",
      "description": "The Default ContentType for .jpeg must still be declared so the thumbnail part is legal."
    }
  ]
}

Fixture

Download preserve-thumbnail.docx (84.3 KB)

Reference preview

No rendered reference is available for this case.

Spec notes

Open Packaging Conventions (ECMA-376 Part 2, clause 9.2) defines a package-level thumbnail relationship of type 'http://schemas.openxmlformats.org/package/2006/relationships/metadata/thumbnail'. Word renders this JPEG in Windows Explorer previews, Outlook attachment chips, and SharePoint thumbnails. The relationship, the target image part (docProps/thumbnail.jpeg by Word convention), and the 'jpeg' default content-type MUST survive a naive library round-trip. python-docx currently drops the thumbnail unconditionally in opc/package.py::Package._drop_unused_package_rels (the rationale 'python-docx has no thumbnail renderer' applies only when creating a new document; it should not throw away an incoming thumbnail on round-trip). The .office.docx companion is a verbatim Word 2013-authored file; the 'machine' fixture is produced by opening that file with python-docx and saving it back with reproducible=True.