返回 ppt-master
hyperlinks.py
根目录 / skills / ppt-master / scripts / pptx_to_svg / hyperlinks.py
1 #!/usr/bin/env python3
2 """
3 PPT Master - PPTX Hyperlink Resolver
4
5 Resolve native DrawingML click actions into the canonical SVG ``href`` form
6 used by the authoring and round-trip pipelines.
7
8 Usage:
9 from .hyperlinks import resolve_click_hyperlink
10
11 Examples:
12 result = resolve_click_hyperlink(rels, "rId3", "", slide_index_by_part=roster)
13
14 Dependencies:
15 PPT Master hyperlink contract.
16 """
17
18 from __future__ import annotations
19
20 from dataclasses import dataclass
21
22 from hyperlink_contract import (
23 HYPERLINK_REL_TYPE,
24 HyperlinkContractError,
25 SLIDE_JUMP_ACTION,
26 SLIDE_REL_TYPE,
27 parse_hyperlink_target,
28 )
29
30
31 @dataclass(frozen=True)
32 class HyperlinkResolution:
33 """Resolved SVG target or one actionable source-package diagnostic."""
34
35 href: str | None = None
36 error: str | None = None
37
38
39 def resolve_click_hyperlink(
40 relationships: dict[str, dict[str, str]],
41 relationship_id: str,
42 action: str,
43 *,
44 slide_index_by_part: dict[str, int],
45 ) -> HyperlinkResolution:
46 """Resolve a click hyperlink relationship from one OOXML source part."""
47 if action == "ppaction://media":
48 return HyperlinkResolution()
49 if not relationship_id:
50 return HyperlinkResolution(error="click hyperlink has no relationship id")
51 relationship = relationships.get(relationship_id)
52 if relationship is None:
53 return HyperlinkResolution(
54 error=f"click hyperlink relationship {relationship_id!r} is missing"
55 )
56 rel_type = relationship.get("type", "")
57 target = relationship.get("target", "")
58 external = relationship.get("external") == "1"
59
60 if action == SLIDE_JUMP_ACTION:
61 if external or rel_type != SLIDE_REL_TYPE:
62 return HyperlinkResolution(
63 error="hlinksldjump does not reference an internal slide"
64 )
65 slide_index = slide_index_by_part.get(target)
66 if slide_index is None:
67 return HyperlinkResolution(
68 error=f"slide jump target {target!r} is not in the presentation roster"
69 )
70 return HyperlinkResolution(href=f"#slide-{slide_index}")
71
72 if action.startswith("ppaction://") and action not in {
73 "ppaction://hlinkurl",
74 }:
75 return HyperlinkResolution(
76 error=f"unsupported PowerPoint click action {action!r}"
77 )
78 if not external or rel_type != HYPERLINK_REL_TYPE:
79 return HyperlinkResolution(
80 error="click hyperlink does not reference an external hyperlink"
81 )
82 try:
83 parsed = parse_hyperlink_target(target)
84 except HyperlinkContractError as exc:
85 return HyperlinkResolution(error=str(exc))
86 if parsed.kind != "external":
87 return HyperlinkResolution(
88 error="external hyperlink relationship resolved to a slide target"
89 )
90 return HyperlinkResolution(href=parsed.raw)
91
92
93 __all__ = ["HyperlinkResolution", "resolve_click_hyperlink"]
94
94 lines PYTHON