Release note · August 1, 2026

The external picture link your Word review misses

By SybilGambleyyu · DocFence 0.21.0

An external-relationship total is a useful warning, but it cannot tell a reviewer whether an external target is an image source, a hyperlink, an attached template, or an unused relationship. Word has a direct DrawingML marker for linked pictures: a:blip with r:link. It can be present while every familiar hyperlink inventory is empty.

DocFence 0.21 adds a privacy-safe inventory for that precise stored boundary: direct DrawingML linked-picture markers in supported Word stories.

The marker names a picture that is not in the package

The Open XML SDK documents a:blip/@r:link as a linked-picture reference for an image that does not reside in the file. That differs from r:embed, the reference for an image stored locally in the package. The relationship ID is only a pointer; the target lives in the story part’s relationships.

ECMA-376’s Image Part contract allows a standard image relationship to have an internal or external target mode. A careful review should therefore retain both the direct r:link marker and the stored relationship class, without leaking an image path into a CI artifact.

Generic external counts lose the important context

A package can have an external image relationship with no direct picture marker at all. It can have a normal embedded picture with r:embed and no linked-picture marker. And it can have several direct r:link markers sharing one relationship. Those are different review facts.

DocFence scans every direct a:blip/@r:link marker in body, header, footer, footnote, endnote, comment, and glossary stories. It reports only aggregate marker/story counts and whether each resolved backing relationship is a standard image relationship with stored external mode, stored internal mode, or an unsupported relationship. It does not count an orphan image relationship or an r:embed-only image as a linked picture.

Targets, relationship IDs, surrounding drawing markup, story paths, and fingerprints remain private. The private signature catches same-count target or markup changes while treating an ID-only renumbering with identical semantics as unchanged.

Two policies for two handoff modes

For a handoff that must not contain a stored linked-picture marker:

rules:
  require_no_word_drawing_linked_pictures: true

This produces DFP055. For an approved template that intentionally retains linked pictures:

rules:
  no_word_drawing_linked_picture_changes: true

DFP056 protects the private baseline. Together, these rules add context to a generic external-relationship gate rather than replacing it.

Evidence without pretending to render Word

The release suite has 47 tests. The new regression case covers direct markers backed by external and internal standard image relationships, a resolved unsupported relationship, a dual r:link/r:embed element, an r:embed-only element, body/header stories, Transitional and Strict namespaces, orphan-relationship exclusion, same-count target and markup changes, relationship-ID renumbering, policy/SARIF output, and redaction.

It is also exercised against a real public Word package from pea-sys’s linked-picture investigation. At that immutable commit, the package has two direct a:blip/@r:link markers and two external standard image relationships; DocFence reports exactly those two markers without emitting their paths or relationship IDs. The downloaded fixture SHA-256 was 8554932d5de0f1c81dbdcbf7b480c17f9008abca18c66a7f03b2e881cfe5147b.

This is a stored-package control, not a Word renderer or network monitor. DocFence does not choose a Markup Compatibility branch, associate a marker with a visual object, resolve or retrieve an image, update a link, render a picture, or claim that a Word client will load, reach, or honor a target.

Hosted CI passed on Python 3.11 and 3.13 for the release commit and tag, including distribution validation. The wheel and source archive were rebuilt independently from the tagged source and matched byte-for-byte; an isolated installed wheel reproduced the two-marker public-fixture result.

python -m pip install https://github.com/SybilGambleyyu/docfence/releases/download/v0.21.0/docfence-0.21.0-py3-none-any.whl

docfence profile candidate.docx --format markdown
docfence check approved.docx candidate.docx --policy docfence.yml --format sarif --output docfence.sarif

The public release includes the wheel, source archive, and SHA-256 manifest. The exact behavior and non-goals are in the tagged policy reference, threat model, and validation notes.