pptx/hyperlink-slide-jump

A run on slide 1 carries an a:hlinkClick that jumps to slide 2 when clicked in slide-show mode. The click-action is identified by action='ppaction://hlinksldjump' and resolved via an internal package relationship (TargetMode absent), distinguishing it from external-URL hyperlinks.

Library verdicts: python-pptx: — pptxjs: —

Metadata

Feature idpptx/hyperlink-slide-jump
Formatpptx
Categoryreferences
Spececma-376-5-part-1 § 21.1.2.3.5 a:hlinkClick

XPath assertions

ID / partPredicateXPathpython-pptxpptxjs
hlinkclick-action-is-sldjump
ppt/slides/slide1.xml
equal = ppaction://hlinksldjump
The click-action on the run must be identified by the ppaction://hlinksldjump URI.
string(//a:hlinkClick/@action)
hlinkclick-has-rid
ppt/slides/slide1.xml
exist
The slide-jump click-action must carry an r:id pointing at the internal slide relationship.
//a:hlinkClick[@r:id and @action='ppaction://hlinksldjump']
slide-rel-type-is-slide
ppt/slides/_rels/slide1.xml.rels
exist
There must be a <Relationship Type='.../slide' Target='slide2.xml'/> entry in slide1's rels file.
//pr:Relationship[@Type='http://schemas.openxmlformats.org/officeDocument/2006/relationships/slide' and @Target='slide2.xml']
slide-rel-is-internal-no-targetmode
ppt/slides/_rels/slide1.xml.rels
equal =
Slide-jump relationships are INTERNAL: they must not carry TargetMode='External'. This assertion fails if TargetMode is present at all -- string() of a non-existent attribute is the empty string.
string(//pr:Relationship[@Type='http://schemas.openxmlformats.org/officeDocument/2006/relationships/slide' and @Target='slide2.xml']/@TargetMode)
no-hyperlink-rel-leaks-into-slide
ppt/slides/_rels/slide1.xml.rels
absent
A slide-jump must NOT be modelled as an external-hyperlink relationship. If this assertion fails the library is confusing internal slide jumps with external URLs.
//pr:Relationship[@Type='http://schemas.openxmlformats.org/officeDocument/2006/relationships/hyperlink']

Render assertions

No render assertions declared.

Generator source

scripts/gen_hyperlink_slide_jump.py

#!/usr/bin/env python3
"""Generate ``fixtures/pptx/hyperlink-slide-jump.pptx``.

A two-slide presentation where a run of text on slide 1 is decorated
with a click-action that jumps to slide 2. The action URI is
``ppaction://hlinksldjump`` and the jump target is resolved via an
INTERNAL package relationship (Type=.../slide, no TargetMode) --
distinct from the external-URL hyperlink case which uses
Type=.../hyperlink and TargetMode='External'.

Uses python-pptx's ``run.hyperlink.target_slide = slide`` high-level
API.

.. versionadded:: 2026.05.0
"""

from __future__ import annotations

import datetime as _dt
from pathlib import Path

from pptx import Presentation
from pptx.util import Inches

_REPO_ROOT = Path(__file__).resolve().parent.parent
_OUT = _REPO_ROOT / "fixtures" / "pptx" / "hyperlink-slide-jump.pptx"
_REPRODUCIBLE_DT = _dt.datetime(2000, 1, 1, 0, 0, 0)


def main() -> int:
    prs = Presentation()
    slide1 = prs.slides.add_slide(prs.slide_layouts[5])  # Title Only
    slide2 = prs.slides.add_slide(prs.slide_layouts[5])

    textbox = slide1.shapes.add_textbox(Inches(1), Inches(2), Inches(6), Inches(1))
    paragraph = textbox.text_frame.paragraphs[0]
    run = paragraph.add_run()
    run.text = "Jump to slide 2"
    # -- the high-level API: issues a Type=.../slide rel + hlinkClick with
    # -- action='ppaction://hlinksldjump' in one go.
    run.hyperlink.target_slide = slide2

    _OUT.parent.mkdir(parents=True, exist_ok=True)
    prs.save(_OUT, zip_date_time=_REPRODUCIBLE_DT)
    print(_OUT)
    return 0


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

Manifest (expanded)

{
  "$schema": "../manifest.schema.json",
  "id": "pptx/hyperlink-slide-jump",
  "title": "Slide-jump click-action hyperlink",
  "format": "pptx",
  "category": "references",
  "summary": "A run on slide 1 carries an a:hlinkClick that jumps to slide 2 when clicked in slide-show mode. The click-action is identified by action='ppaction://hlinksldjump' and resolved via an internal package relationship (TargetMode absent), distinguishing it from external-URL hyperlinks.",
  "spec": {
    "source": "ecma-376-5-part-1",
    "clause": "21.1.2.3.5",
    "element": "a:hlinkClick",
    "rnc_reference": "spec/ecma-376-5/part-1/rnc/DrawingML.rnc",
    "xsd_reference": "spec/ecma-376-5/part-1/xsd/dml-main.xsd",
    "notes": "PowerPoint's 'Place in This Document' hyperlink target uses the a:hlinkClick element with action='ppaction://hlinksldjump'. The a:hlinkClick/@r:id attribute resolves to a <Relationship Type='.../slide' Target='slide2.xml'/> in the slide's rels. Critically the slide-jump relationship has NO TargetMode attribute -- it is an INTERNAL package relationship, unlike external hyperlinks which require TargetMode='External'. This distinction is what separates hyperlink-slide-jump from a run-level external-URL hyperlink."
  },
  "fixtures": {
    "machine": "pptx/hyperlink-slide-jump"
  },
  "generator": {
    "python": "scripts/gen_hyperlink_slide_jump.py"
  },
  "assertions": [
    {
      "id": "hlinkclick-action-is-sldjump",
      "part": "ppt/slides/slide1.xml",
      "namespaces": {
        "a": "http://schemas.openxmlformats.org/drawingml/2006/main",
        "r": "http://schemas.openxmlformats.org/officeDocument/2006/relationships"
      },
      "xpath": "string(//a:hlinkClick/@action)",
      "must": "equal",
      "value": "ppaction://hlinksldjump",
      "description": "The click-action on the run must be identified by the ppaction://hlinksldjump URI."
    },
    {
      "id": "hlinkclick-has-rid",
      "part": "ppt/slides/slide1.xml",
      "namespaces": {
        "a": "http://schemas.openxmlformats.org/drawingml/2006/main",
        "r": "http://schemas.openxmlformats.org/officeDocument/2006/relationships"
      },
      "xpath": "//a:hlinkClick[@r:id and @action='ppaction://hlinksldjump']",
      "must": "exist",
      "description": "The slide-jump click-action must carry an r:id pointing at the internal slide relationship."
    },
    {
      "id": "slide-rel-type-is-slide",
      "part": "ppt/slides/_rels/slide1.xml.rels",
      "namespaces": {
        "pr": "http://schemas.openxmlformats.org/package/2006/relationships"
      },
      "xpath": "//pr:Relationship[@Type='http://schemas.openxmlformats.org/officeDocument/2006/relationships/slide' and @Target='slide2.xml']",
      "must": "exist",
      "description": "There must be a <Relationship Type='.../slide' Target='slide2.xml'/> entry in slide1's rels file."
    },
    {
      "id": "slide-rel-is-internal-no-targetmode",
      "part": "ppt/slides/_rels/slide1.xml.rels",
      "namespaces": {
        "pr": "http://schemas.openxmlformats.org/package/2006/relationships"
      },
      "xpath": "string(//pr:Relationship[@Type='http://schemas.openxmlformats.org/officeDocument/2006/relationships/slide' and @Target='slide2.xml']/@TargetMode)",
      "must": "equal",
      "value": "",
      "description": "Slide-jump relationships are INTERNAL: they must not carry TargetMode='External'. This assertion fails if TargetMode is present at all -- string() of a non-existent attribute is the empty string."
    },
    {
      "id": "no-hyperlink-rel-leaks-into-slide",
      "part": "ppt/slides/_rels/slide1.xml.rels",
      "namespaces": {
        "pr": "http://schemas.openxmlformats.org/package/2006/relationships"
      },
      "xpath": "//pr:Relationship[@Type='http://schemas.openxmlformats.org/officeDocument/2006/relationships/hyperlink']",
      "must": "absent",
      "description": "A slide-jump must NOT be modelled as an external-hyperlink relationship. If this assertion fails the library is confusing internal slide jumps with external URLs."
    }
  ]
}

Fixture

Download hyperlink-slide-jump.pptx (28.4 KB)

Reference preview

No rendered reference is available for this case.

Spec notes

PowerPoint's 'Place in This Document' hyperlink target uses the a:hlinkClick element with action='ppaction://hlinksldjump'. The a:hlinkClick/@r:id attribute resolves to a <Relationship Type='.../slide' Target='slide2.xml'/> in the slide's rels. Critically the slide-jump relationship has NO TargetMode attribute -- it is an INTERNAL package relationship, unlike external hyperlinks which require TargetMode='External'. This distinction is what separates hyperlink-slide-jump from a run-level external-URL hyperlink.