Release note · August 1, 2026

One Word image link can be two stored markers

By SybilGambleyyu · DocFence 0.19.0

“This image has a hyperlink” sounds like one fact. In a Word OOXML package, it can be more than one stored action marker. That distinction matters when a document is reviewed in CI: a package relationship is not enough evidence, and a visible-link count is not the same thing as the markup stored in the file.

DocFence 0.19 adds a privacy-safe inventory for that missing layer: direct DrawingML a:hlinkClick, a:hlinkHover, and a:hlinkMouseOver markers inside supported Word stories.

A real image has two click markers

LibreOffice’s public tdf78657_picture_hyperlink.docx regression fixture is identified by its commit as a Microsoft Word 2016 test file. Its one image hyperlink relationship is referenced by two stored a:hlinkClick elements: one on WordprocessingDrawing’s wp:docPr, and another on the picture’s non-visual properties.

That does not mean a user sees two links. It means the package contains two action markers. A review tool should preserve that stored evidence rather than silently deduplicating it into a rendered-link guess. One marker can be changed or removed while the other remains; that package-level change is precisely what a controlled review needs to see.

Count the stored action, keep its material private

The DrawingML inventory reports only aggregate marker and story counts, click/hover/mouse-over kinds, external/internal/unsupported relationship classes, missing-r:id markers, and the presence of action and invalidUrl attributes. A package relationship does not count unless a direct marker references it.

Targets, invalid URLs, action strings, tooltips, frame names, history settings, relationship IDs, story paths, and fingerprints stay private. The private signature catches a same-count target or attribute rewrite, while a relationship-ID renumbering with identical resolved semantics stays quiet.

The OOXML a:hlinkClick definition documents the relationship target and action-related attributes; its hover counterpart and the Open XML SDK’s mouse-over contract complete the family. DocFence records direct stored elements in supported Word stories, but does not select a Markup Compatibility branch, resolve a target, associate markers with a rendered object, validate a URL, or execute an action.

Two policy choices

For a handoff that must carry no stored DrawingML action markup:

rules:
  require_no_word_drawing_hyperlinks: true

This produces DFP051. For a governed template that intentionally retains image or shape actions:

rules:
  no_word_drawing_hyperlink_changes: true

DFP052 protects the private marker baseline. These rules complement the generic relationship gate, direct w:hyperlink markup gate, and HYPERLINK field gate because each represents different stored evidence.

Release evidence

The 0.19 suite has 45 tests. It covers click, hover, and mouse-over marker kinds; external and internal hyperlink relationships; a resolved type mismatch; a missing relationship ID; action and invalid-URL attribute presence; body/header stories; Transitional and Strict namespaces; orphaned-relationship exclusion; same-count target/action mutations; relationship-ID renumbering; policy/SARIF output; and redaction.

The released wheel profiles the public Word 2016 image fixture and reports two click markers and two external relationship-backed marker references without emitting its target. Hosted CI passed on Python 3.11 and 3.13, including distribution validation. The wheel and source archive were independently rebuilt from the tagged commit and matched byte-for-byte; fresh GitHub release downloads passed the published SHA-256 manifest, package checks, and an isolated-wheel fixture smoke test.

python -m pip install https://github.com/SybilGambleyyu/docfence/releases/download/v0.19.0/docfence-0.19.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. Read the exact contract in the policy reference and threat model.