|
2 | 2 | Hyperlink |
3 | 3 | ========= |
4 | 4 |
|
5 | | -Word allows hyperlinks to be placed in a document wherever paragraphs can appear. |
| 5 | +Word allows a hyperlink to be placed in a document wherever a paragraph can appear. The |
| 6 | +actual hyperlink element is a peer of |Run|. |
6 | 7 |
|
7 | | -The target (URL) of a hyperlink may be external, such as a web site, or internal, to |
8 | | -another location in the document. |
| 8 | +The link may be to an external resource such as a web site, or internal, to another |
| 9 | +location in the document. The link may also be a `mailto:` URI or a reference to a file |
| 10 | +on an accessible local or network filesystem. |
9 | 11 |
|
10 | 12 | The visible text of a hyperlink is held in one or more runs. Technically a hyperlink can |
11 | 13 | have zero runs, but this occurs only in contrived cases (otherwise there would be |
12 | 14 | nothing to click on). As usual, each run can have its own distinct text formatting |
13 | 15 | (font), so for example one word in the hyperlink can be bold, etc. By default, Word |
14 | | -applies the built-in `Hyperlink` character style to a newly inserted hyperlink. |
| 16 | +applies the built-in `Hyperlink` character style to a newly inserted hyperlink. Like |
| 17 | +other text, the hyperlink text may often be broken into multiple runs as a result of |
| 18 | +edits in different "revision-save" editing sessions (between "Save" commands). |
15 | 19 |
|
16 | 20 | Note that rendered page-breaks can occur in the middle of a hyperlink. |
17 | 21 |
|
18 | 22 | A |Hyperlink| is a child of |Paragraph|, a peer of |Run|. |
19 | 23 |
|
20 | 24 |
|
| 25 | +TODO: What about URL-encoding/decoding (like %20) behaviors, if any? |
| 26 | + |
| 27 | + |
21 | 28 | Candidate protocol |
22 | 29 | ------------------ |
23 | 30 |
|
24 | 31 | An external hyperlink has an address and an optional anchor. An internal hyperlink has |
25 | | -only an anchor. An anchor is also known as a *URI fragment* and follows a hash mark |
26 | | -("#"). |
| 32 | +only an anchor. An anchor is more precisely known as a *URI fragment* in a web URL and |
| 33 | +follows a hash mark ("#"). The fragment-separator hash character is not stored in the |
| 34 | +XML. |
| 35 | + |
| 36 | +Note that the anchor and address are stored in two distinct attributes, so you need to |
| 37 | +concatenate `.address` and `.anchor` like `f"{address}#{anchor}"` if you want the whole |
| 38 | +thing. |
27 | 39 |
|
28 | | -Note that the anchor and URL are stored in two distinct attributes, so you need to |
29 | | -concatenate `.address` and `.anchor` if you want the whole thing. |
| 40 | +Also note that Word does not rigorously separate a fragment in a web URI so it may |
| 41 | +appear as part of the address or separately in the anchor attribute, depending on how |
| 42 | +the hyperlink was authored. Hyperlinks inserted using the dialog-box seem to separate it |
| 43 | +and addresses typed into the document directly don't, based on my limited experience. |
30 | 44 |
|
31 | 45 | .. highlight:: python |
32 | 46 |
|
@@ -49,6 +63,16 @@ concatenate `.address` and `.anchor` if you want the whole thing. |
49 | 63 | >>> hyperlink.address |
50 | 64 | 'https://google.com/' |
51 | 65 |
|
| 66 | +**Access hyperlink fragment**:: |
| 67 | + |
| 68 | + >>> hyperlink.fragment |
| 69 | + 'introduction' |
| 70 | + |
| 71 | +**Access hyperlink history (visited or not, True means not visited yet)**:: |
| 72 | + |
| 73 | + >>> hyperlink.history |
| 74 | + True |
| 75 | + |
52 | 76 | **Access hyperlinks runs**:: |
53 | 77 |
|
54 | 78 | >>> hyperlink.runs |
@@ -58,6 +82,11 @@ concatenate `.address` and `.anchor` if you want the whole thing. |
58 | 82 | <docx.text.run.Run at 0x7f...> |
59 | 83 | ] |
60 | 84 |
|
| 85 | +**Access hyperlink URL**:: |
| 86 | + |
| 87 | + >>> hyperlink.url |
| 88 | + 'https://us.com#introduction' |
| 89 | + |
61 | 90 | **Determine whether a hyperlink contains a rendered page-break**:: |
62 | 91 |
|
63 | 92 | >>> hyperlink.contains_page_break |
@@ -68,29 +97,31 @@ concatenate `.address` and `.anchor` if you want the whole thing. |
68 | 97 | >>> hyperlink.text |
69 | 98 | 'an excellent Wikipedia article on ferrets' |
70 | 99 |
|
71 | | -**Add an external hyperlink**:: |
| 100 | +**Add an external hyperlink** (not yet implemented):: |
72 | 101 |
|
73 | 102 | >>> hyperlink = paragraph.add_hyperlink( |
74 | | - 'About', address='http://us.com', anchor='about' |
75 | | - ) |
| 103 | + ... 'About', address='http://us.com', fragment='about' |
| 104 | + ... ) |
76 | 105 | >>> hyperlink |
77 | 106 | <docx.text.hyperlink.Hyperlink at 0x7f...> |
78 | 107 | >>> hyperlink.text |
79 | 108 | 'About' |
80 | 109 | >>> hyperlink.address |
81 | 110 | 'http://us.com' |
82 | | - >>> hyperlink.anchor |
| 111 | + >>> hyperlink.fragment |
83 | 112 | 'about' |
| 113 | + >>> hyperlink.url |
| 114 | + 'http://us.com#about' |
84 | 115 |
|
85 | 116 | **Add an internal hyperlink (to a bookmark)**:: |
86 | 117 |
|
87 | | - >>> hyperlink = paragraph.add_hyperlink('Section 1', anchor='Section_1') |
| 118 | + >>> hyperlink = paragraph.add_hyperlink('Section 1', fragment='Section_1') |
88 | 119 | >>> hyperlink.text |
89 | 120 | 'Section 1' |
90 | | - >>> hyperlink.anchor |
| 121 | + >>> hyperlink.fragment |
91 | 122 | 'Section_1' |
92 | 123 | >>> hyperlink.address |
93 | | - None |
| 124 | + '' |
94 | 125 |
|
95 | 126 | **Modify hyperlink properties**:: |
96 | 127 |
|
@@ -183,8 +214,8 @@ file, keyed by the w:hyperlink@r:id attribute:: |
183 | 214 | <Relationship Id="rId4" Mode="External" Type="http://..." Target="http://google.com/"/> |
184 | 215 | </Relationships> |
185 | 216 |
|
186 | | -A hyperlink can contain multiple runs of text (and a whole lot of other |
187 | | -stuff, including nested hyperlinks, at least as far as the schema indicates):: |
| 217 | +A hyperlink can contain multiple runs of text (and a whole lot of other stuff, at least |
| 218 | +as far as the schema indicates):: |
188 | 219 |
|
189 | 220 | <w:p> |
190 | 221 | <w:hyperlink r:id="rId2"> |
@@ -256,97 +287,97 @@ Schema excerpt |
256 | 287 |
|
257 | 288 | :: |
258 | 289 |
|
259 | | - <xsd:complexType name="CT_P"> |
260 | | - <xsd:sequence> |
261 | | - <xsd:element name="pPr" type="CT_PPr" minOccurs="0"/> |
262 | | - <xsd:group ref="EG_PContent" minOccurs="0" maxOccurs="unbounded"/> |
263 | | - </xsd:sequence> |
264 | | - <xsd:attribute name="rsidRPr" type="ST_LongHexNumber"/> |
265 | | - <xsd:attribute name="rsidR" type="ST_LongHexNumber"/> |
266 | | - <xsd:attribute name="rsidDel" type="ST_LongHexNumber"/> |
267 | | - <xsd:attribute name="rsidP" type="ST_LongHexNumber"/> |
268 | | - <xsd:attribute name="rsidRDefault" type="ST_LongHexNumber"/> |
269 | | - </xsd:complexType> |
270 | | - |
271 | | - <xsd:group name="EG_PContent"> <!-- denormalized --> |
272 | | - <xsd:choice> |
273 | | - <xsd:element name="r" type="CT_R"/> |
274 | | - <xsd:element name="hyperlink" type="CT_Hyperlink"/> |
275 | | - <xsd:element name="fldSimple" type="CT_SimpleField"/> |
276 | | - <xsd:element name="sdt" type="CT_SdtRun"/> |
277 | | - <xsd:element name="customXml" type="CT_CustomXmlRun"/> |
278 | | - <xsd:element name="smartTag" type="CT_SmartTagRun"/> |
279 | | - <xsd:element name="dir" type="CT_DirContentRun"/> |
280 | | - <xsd:element name="bdo" type="CT_BdoContentRun"/> |
281 | | - <xsd:element name="subDoc" type="CT_Rel"/> |
282 | | - <xsd:group ref="EG_RunLevelElts"/> |
283 | | - </xsd:choice> |
284 | | - </xsd:group> |
285 | | - |
286 | | - <xsd:complexType name="CT_Hyperlink"> |
287 | | - <xsd:group ref="EG_PContent" minOccurs="0" maxOccurs="unbounded"/> |
288 | | - <xsd:attribute name="tgtFrame" type="s:ST_String"/> |
289 | | - <xsd:attribute name="tooltip" type="s:ST_String"/> |
290 | | - <xsd:attribute name="docLocation" type="s:ST_String"/> |
291 | | - <xsd:attribute name="history" type="s:ST_OnOff"/> |
292 | | - <xsd:attribute name="anchor" type="s:ST_String"/> |
293 | | - <xsd:attribute ref="r:id"/> |
294 | | - </xsd:complexType> |
295 | | - |
296 | | - <xsd:group name="EG_RunLevelElts"> |
297 | | - <xsd:choice> |
298 | | - <xsd:element name="proofErr" type="CT_ProofErr"/> |
299 | | - <xsd:element name="permStart" type="CT_PermStart"/> |
300 | | - <xsd:element name="permEnd" type="CT_Perm"/> |
301 | | - <xsd:element name="bookmarkStart" type="CT_Bookmark"/> |
302 | | - <xsd:element name="bookmarkEnd" type="CT_MarkupRange"/> |
303 | | - <xsd:element name="moveFromRangeStart" type="CT_MoveBookmark"/> |
304 | | - <xsd:element name="moveFromRangeEnd" type="CT_MarkupRange"/> |
305 | | - <xsd:element name="moveToRangeStart" type="CT_MoveBookmark"/> |
306 | | - <xsd:element name="moveToRangeEnd" type="CT_MarkupRange"/> |
307 | | - <xsd:element name="commentRangeStart" type="CT_MarkupRange"/> |
308 | | - <xsd:element name="commentRangeEnd" type="CT_MarkupRange"/> |
309 | | - <xsd:element name="customXmlInsRangeStart" type="CT_TrackChange"/> |
310 | | - <xsd:element name="customXmlInsRangeEnd" type="CT_Markup"/> |
311 | | - <xsd:element name="customXmlDelRangeStart" type="CT_TrackChange"/> |
312 | | - <xsd:element name="customXmlDelRangeEnd" type="CT_Markup"/> |
313 | | - <xsd:element name="customXmlMoveFromRangeStart" type="CT_TrackChange"/> |
314 | | - <xsd:element name="customXmlMoveFromRangeEnd" type="CT_Markup"/> |
315 | | - <xsd:element name="customXmlMoveToRangeStart" type="CT_TrackChange"/> |
316 | | - <xsd:element name="customXmlMoveToRangeEnd" type="CT_Markup"/> |
317 | | - <xsd:element name="ins" type="CT_RunTrackChange"/> |
318 | | - <xsd:element name="del" type="CT_RunTrackChange"/> |
319 | | - <xsd:element name="moveFrom" type="CT_RunTrackChange"/> |
320 | | - <xsd:element name="moveTo" type="CT_RunTrackChange"/> |
321 | | - <xsd:group ref="EG_MathContent" minOccurs="0" maxOccurs="unbounded"/> |
322 | | - </xsd:choice> |
323 | | - </xsd:group> |
324 | | - |
325 | | - <xsd:complexType name="CT_R"> |
326 | | - <xsd:sequence> |
327 | | - <xsd:group ref="EG_RPr" minOccurs="0"/> |
328 | | - <xsd:group ref="EG_RunInnerContent" minOccurs="0" maxOccurs="unbounded"/> |
329 | | - </xsd:sequence> |
330 | | - <xsd:attribute name="rsidRPr" type="ST_LongHexNumber"/> |
331 | | - <xsd:attribute name="rsidDel" type="ST_LongHexNumber"/> |
332 | | - <xsd:attribute name="rsidR" type="ST_LongHexNumber"/> |
333 | | - </xsd:complexType> |
334 | | - |
335 | | - <xsd:simpleType name="ST_OnOff"> |
336 | | - <xsd:union memberTypes="xsd:boolean ST_OnOff1"/> |
337 | | - </xsd:simpleType> |
338 | | - |
339 | | - <xsd:simpleType name="ST_OnOff1"> |
340 | | - <xsd:restriction base="xsd:string"> |
341 | | - <xsd:enumeration value="on"/> |
342 | | - <xsd:enumeration value="off"/> |
343 | | - </xsd:restriction> |
344 | | - </xsd:simpleType> |
345 | | - |
346 | | - <xsd:simpleType name="ST_RelationshipId"> |
347 | | - <xsd:restriction base="xsd:string"/> |
348 | | - </xsd:simpleType> |
349 | | - |
350 | | - <xsd:simpleType name="ST_String"> |
351 | | - <xsd:restriction base="xsd:string"/> |
352 | | - </xsd:simpleType> |
| 290 | + <xsd:complexType name="CT_P"> |
| 291 | + <xsd:sequence> |
| 292 | + <xsd:element name="pPr" type="CT_PPr" minOccurs="0"/> |
| 293 | + <xsd:group ref="EG_PContent" minOccurs="0" maxOccurs="unbounded"/> |
| 294 | + </xsd:sequence> |
| 295 | + <xsd:attribute name="rsidRPr" type="ST_LongHexNumber"/> |
| 296 | + <xsd:attribute name="rsidR" type="ST_LongHexNumber"/> |
| 297 | + <xsd:attribute name="rsidDel" type="ST_LongHexNumber"/> |
| 298 | + <xsd:attribute name="rsidP" type="ST_LongHexNumber"/> |
| 299 | + <xsd:attribute name="rsidRDefault" type="ST_LongHexNumber"/> |
| 300 | + </xsd:complexType> |
| 301 | + |
| 302 | + <xsd:group name="EG_PContent"> <!-- denormalized --> |
| 303 | + <xsd:choice> |
| 304 | + <xsd:element name="r" type="CT_R"/> |
| 305 | + <xsd:element name="hyperlink" type="CT_Hyperlink"/> |
| 306 | + <xsd:element name="fldSimple" type="CT_SimpleField"/> |
| 307 | + <xsd:element name="sdt" type="CT_SdtRun"/> |
| 308 | + <xsd:element name="customXml" type="CT_CustomXmlRun"/> |
| 309 | + <xsd:element name="smartTag" type="CT_SmartTagRun"/> |
| 310 | + <xsd:element name="dir" type="CT_DirContentRun"/> |
| 311 | + <xsd:element name="bdo" type="CT_BdoContentRun"/> |
| 312 | + <xsd:element name="subDoc" type="CT_Rel"/> |
| 313 | + <xsd:group ref="EG_RunLevelElts"/> |
| 314 | + </xsd:choice> |
| 315 | + </xsd:group> |
| 316 | + |
| 317 | + <xsd:complexType name="CT_Hyperlink"> |
| 318 | + <xsd:group ref="EG_PContent" minOccurs="0" maxOccurs="unbounded"/> |
| 319 | + <xsd:attribute name="tgtFrame" type="s:ST_String"/> |
| 320 | + <xsd:attribute name="tooltip" type="s:ST_String"/> |
| 321 | + <xsd:attribute name="docLocation" type="s:ST_String"/> |
| 322 | + <xsd:attribute name="history" type="s:ST_OnOff"/> |
| 323 | + <xsd:attribute name="anchor" type="s:ST_String"/> |
| 324 | + <xsd:attribute ref="r:id"/> |
| 325 | + </xsd:complexType> |
| 326 | + |
| 327 | + <xsd:group name="EG_RunLevelElts"> |
| 328 | + <xsd:choice> |
| 329 | + <xsd:element name="proofErr" type="CT_ProofErr"/> |
| 330 | + <xsd:element name="permStart" type="CT_PermStart"/> |
| 331 | + <xsd:element name="permEnd" type="CT_Perm"/> |
| 332 | + <xsd:element name="bookmarkStart" type="CT_Bookmark"/> |
| 333 | + <xsd:element name="bookmarkEnd" type="CT_MarkupRange"/> |
| 334 | + <xsd:element name="moveFromRangeStart" type="CT_MoveBookmark"/> |
| 335 | + <xsd:element name="moveFromRangeEnd" type="CT_MarkupRange"/> |
| 336 | + <xsd:element name="moveToRangeStart" type="CT_MoveBookmark"/> |
| 337 | + <xsd:element name="moveToRangeEnd" type="CT_MarkupRange"/> |
| 338 | + <xsd:element name="commentRangeStart" type="CT_MarkupRange"/> |
| 339 | + <xsd:element name="commentRangeEnd" type="CT_MarkupRange"/> |
| 340 | + <xsd:element name="customXmlInsRangeStart" type="CT_TrackChange"/> |
| 341 | + <xsd:element name="customXmlInsRangeEnd" type="CT_Markup"/> |
| 342 | + <xsd:element name="customXmlDelRangeStart" type="CT_TrackChange"/> |
| 343 | + <xsd:element name="customXmlDelRangeEnd" type="CT_Markup"/> |
| 344 | + <xsd:element name="customXmlMoveFromRangeStart" type="CT_TrackChange"/> |
| 345 | + <xsd:element name="customXmlMoveFromRangeEnd" type="CT_Markup"/> |
| 346 | + <xsd:element name="customXmlMoveToRangeStart" type="CT_TrackChange"/> |
| 347 | + <xsd:element name="customXmlMoveToRangeEnd" type="CT_Markup"/> |
| 348 | + <xsd:element name="ins" type="CT_RunTrackChange"/> |
| 349 | + <xsd:element name="del" type="CT_RunTrackChange"/> |
| 350 | + <xsd:element name="moveFrom" type="CT_RunTrackChange"/> |
| 351 | + <xsd:element name="moveTo" type="CT_RunTrackChange"/> |
| 352 | + <xsd:group ref="EG_MathContent" minOccurs="0" maxOccurs="unbounded"/> |
| 353 | + </xsd:choice> |
| 354 | + </xsd:group> |
| 355 | + |
| 356 | + <xsd:complexType name="CT_R"> |
| 357 | + <xsd:sequence> |
| 358 | + <xsd:group ref="EG_RPr" minOccurs="0"/> |
| 359 | + <xsd:group ref="EG_RunInnerContent" minOccurs="0" maxOccurs="unbounded"/> |
| 360 | + </xsd:sequence> |
| 361 | + <xsd:attribute name="rsidRPr" type="ST_LongHexNumber"/> |
| 362 | + <xsd:attribute name="rsidDel" type="ST_LongHexNumber"/> |
| 363 | + <xsd:attribute name="rsidR" type="ST_LongHexNumber"/> |
| 364 | + </xsd:complexType> |
| 365 | + |
| 366 | + <xsd:simpleType name="ST_OnOff"> |
| 367 | + <xsd:union memberTypes="xsd:boolean ST_OnOff1"/> |
| 368 | + </xsd:simpleType> |
| 369 | + |
| 370 | + <xsd:simpleType name="ST_OnOff1"> |
| 371 | + <xsd:restriction base="xsd:string"> |
| 372 | + <xsd:enumeration value="on"/> |
| 373 | + <xsd:enumeration value="off"/> |
| 374 | + </xsd:restriction> |
| 375 | + </xsd:simpleType> |
| 376 | + |
| 377 | + <xsd:simpleType name="ST_RelationshipId"> |
| 378 | + <xsd:restriction base="xsd:string"/> |
| 379 | + </xsd:simpleType> |
| 380 | + |
| 381 | + <xsd:simpleType name="ST_String"> |
| 382 | + <xsd:restriction base="xsd:string"/> |
| 383 | + </xsd:simpleType> |