python-docx-oss 0.1.0__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (132) hide show
  1. docx/__init__.py +71 -0
  2. docx/api.py +37 -0
  3. docx/blkcntnr.py +101 -0
  4. docx/comments.py +163 -0
  5. docx/dml/__init__.py +0 -0
  6. docx/dml/color.py +112 -0
  7. docx/document.py +285 -0
  8. docx/drawing/__init__.py +59 -0
  9. docx/enum/__init__.py +0 -0
  10. docx/enum/base.py +150 -0
  11. docx/enum/dml.py +103 -0
  12. docx/enum/section.py +86 -0
  13. docx/enum/shape.py +19 -0
  14. docx/enum/style.py +452 -0
  15. docx/enum/table.py +136 -0
  16. docx/enum/text.py +367 -0
  17. docx/exceptions.py +18 -0
  18. docx/image/__init__.py +32 -0
  19. docx/image/bmp.py +43 -0
  20. docx/image/constants.py +172 -0
  21. docx/image/emf.py +46 -0
  22. docx/image/exceptions.py +13 -0
  23. docx/image/gif.py +38 -0
  24. docx/image/helpers.py +86 -0
  25. docx/image/image.py +237 -0
  26. docx/image/jpeg.py +469 -0
  27. docx/image/png.py +253 -0
  28. docx/image/svg.py +118 -0
  29. docx/image/tiff.py +289 -0
  30. docx/opc/__init__.py +0 -0
  31. docx/opc/constants.py +307 -0
  32. docx/opc/coreprops.py +142 -0
  33. docx/opc/customprops.py +59 -0
  34. docx/opc/exceptions.py +12 -0
  35. docx/opc/oxml.py +247 -0
  36. docx/opc/package.py +254 -0
  37. docx/opc/packuri.py +109 -0
  38. docx/opc/part.py +267 -0
  39. docx/opc/parts/__init__.py +0 -0
  40. docx/opc/parts/coreprops.py +48 -0
  41. docx/opc/parts/customprops.py +61 -0
  42. docx/opc/phys_pkg.py +119 -0
  43. docx/opc/pkgreader.py +254 -0
  44. docx/opc/pkgwriter.py +115 -0
  45. docx/opc/rel.py +169 -0
  46. docx/opc/shared.py +31 -0
  47. docx/opc/spec.py +24 -0
  48. docx/oxml/__init__.py +252 -0
  49. docx/oxml/comments.py +124 -0
  50. docx/oxml/coreprops.py +298 -0
  51. docx/oxml/customprops.py +141 -0
  52. docx/oxml/dml/CT_ExtensionList.py +48 -0
  53. docx/oxml/document.py +88 -0
  54. docx/oxml/drawing.py +11 -0
  55. docx/oxml/exceptions.py +10 -0
  56. docx/oxml/ns.py +111 -0
  57. docx/oxml/numbering.py +109 -0
  58. docx/oxml/parser.py +62 -0
  59. docx/oxml/section.py +537 -0
  60. docx/oxml/settings.py +138 -0
  61. docx/oxml/shape.py +426 -0
  62. docx/oxml/shared.py +52 -0
  63. docx/oxml/simpletypes.py +434 -0
  64. docx/oxml/styles.py +320 -0
  65. docx/oxml/table.py +977 -0
  66. docx/oxml/text/__init__.py +0 -0
  67. docx/oxml/text/font.py +358 -0
  68. docx/oxml/text/hyperlink.py +45 -0
  69. docx/oxml/text/pagebreak.py +278 -0
  70. docx/oxml/text/paragraph.py +106 -0
  71. docx/oxml/text/parfmt.py +392 -0
  72. docx/oxml/text/run.py +316 -0
  73. docx/oxml/xmlchemy.py +711 -0
  74. docx/package.py +119 -0
  75. docx/parts/__init__.py +0 -0
  76. docx/parts/comments.py +51 -0
  77. docx/parts/customxml.py +67 -0
  78. docx/parts/document.py +193 -0
  79. docx/parts/hdrftr.py +53 -0
  80. docx/parts/image.py +80 -0
  81. docx/parts/numbering.py +51 -0
  82. docx/parts/settings.py +50 -0
  83. docx/parts/story.py +113 -0
  84. docx/parts/styles.py +42 -0
  85. docx/py.typed +0 -0
  86. docx/section.py +479 -0
  87. docx/settings.py +35 -0
  88. docx/shape.py +103 -0
  89. docx/shared.py +382 -0
  90. docx/styles/__init__.py +40 -0
  91. docx/styles/latent.py +198 -0
  92. docx/styles/style.py +254 -0
  93. docx/styles/styles.py +136 -0
  94. docx/table.py +537 -0
  95. docx/templates/default-comments.xml +12 -0
  96. docx/templates/default-docx-template/[Content_Types].xml +17 -0
  97. docx/templates/default-docx-template/_rels/.rels +7 -0
  98. docx/templates/default-docx-template/customXml/_rels/item1.xml.rels +4 -0
  99. docx/templates/default-docx-template/customXml/item1.xml +2 -0
  100. docx/templates/default-docx-template/customXml/itemProps1.xml +6 -0
  101. docx/templates/default-docx-template/docProps/app.xml +36 -0
  102. docx/templates/default-docx-template/docProps/core.xml +13 -0
  103. docx/templates/default-docx-template/docProps/thumbnail.jpeg +0 -0
  104. docx/templates/default-docx-template/word/_rels/document.xml.rels +11 -0
  105. docx/templates/default-docx-template/word/document.xml +11 -0
  106. docx/templates/default-docx-template/word/fontTable.xml +61 -0
  107. docx/templates/default-docx-template/word/numbering.xml +201 -0
  108. docx/templates/default-docx-template/word/settings.xml +53 -0
  109. docx/templates/default-docx-template/word/styles.xml +11844 -0
  110. docx/templates/default-docx-template/word/stylesWithEffects.xml +11800 -0
  111. docx/templates/default-docx-template/word/theme/theme1.xml +318 -0
  112. docx/templates/default-docx-template/word/webSettings.xml +5 -0
  113. docx/templates/default-footer.xml +27 -0
  114. docx/templates/default-header.xml +27 -0
  115. docx/templates/default-numbering.xml +20 -0
  116. docx/templates/default-settings.xml +26 -0
  117. docx/templates/default-styles.xml +190 -0
  118. docx/templates/default.docx +0 -0
  119. docx/text/__init__.py +0 -0
  120. docx/text/font.py +466 -0
  121. docx/text/hyperlink.py +121 -0
  122. docx/text/pagebreak.py +104 -0
  123. docx/text/paragraph.py +173 -0
  124. docx/text/parfmt.py +286 -0
  125. docx/text/run.py +272 -0
  126. docx/text/tabstops.py +123 -0
  127. docx/types.py +34 -0
  128. python_docx_oss-0.1.0.dist-info/METADATA +57 -0
  129. python_docx_oss-0.1.0.dist-info/RECORD +132 -0
  130. python_docx_oss-0.1.0.dist-info/WHEEL +5 -0
  131. python_docx_oss-0.1.0.dist-info/licenses/LICENSE +21 -0
  132. python_docx_oss-0.1.0.dist-info/top_level.txt +1 -0
docx/text/font.py ADDED
@@ -0,0 +1,466 @@
1
+ """Font-related proxy objects."""
2
+
3
+ from __future__ import annotations
4
+
5
+ from typing import TYPE_CHECKING, Any
6
+
7
+ from docx.dml.color import ColorFormat
8
+ from docx.enum.text import WD_UNDERLINE
9
+ from docx.shared import ElementProxy, Emu
10
+
11
+ if TYPE_CHECKING:
12
+ from docx.enum.text import WD_COLOR_INDEX
13
+ from docx.oxml.text.run import CT_R
14
+ from docx.shared import Length
15
+
16
+
17
+ class Font(ElementProxy):
18
+ """Proxy object for parent of a `<w:rPr>` element and providing access to
19
+ character properties such as font name, font size, bold, and subscript."""
20
+
21
+ def __init__(self, r: CT_R, parent: Any | None = None):
22
+ super().__init__(r, parent)
23
+ self._element = r
24
+ self._r = r
25
+
26
+ @property
27
+ def all_caps(self) -> bool | None:
28
+ """Read/write.
29
+
30
+ Causes text in this font to appear in capital letters.
31
+ """
32
+ return self._get_bool_prop("caps")
33
+
34
+ @all_caps.setter
35
+ def all_caps(self, value: bool | None) -> None:
36
+ self._set_bool_prop("caps", value)
37
+
38
+ @property
39
+ def bold(self) -> bool | None:
40
+ """Read/write.
41
+
42
+ Causes text in this font to appear in bold.
43
+ """
44
+ return self._get_bool_prop("b")
45
+
46
+ @bold.setter
47
+ def bold(self, value: bool | None) -> None:
48
+ self._set_bool_prop("b", value)
49
+
50
+ @property
51
+ def color(self):
52
+ """A |ColorFormat| object providing a way to get and set the text color for this
53
+ font."""
54
+ return ColorFormat(self._element)
55
+
56
+ @property
57
+ def complex_script(self) -> bool | None:
58
+ """Read/write tri-state value.
59
+
60
+ When |True|, causes the characters in the run to be treated as complex script
61
+ regardless of their Unicode values.
62
+ """
63
+ return self._get_bool_prop("cs")
64
+
65
+ @complex_script.setter
66
+ def complex_script(self, value: bool | None) -> None:
67
+ self._set_bool_prop("cs", value)
68
+
69
+ @property
70
+ def cs_bold(self) -> bool | None:
71
+ """Read/write tri-state value.
72
+
73
+ When |True|, causes the complex script characters in the run to be displayed in
74
+ bold typeface.
75
+ """
76
+ return self._get_bool_prop("bCs")
77
+
78
+ @cs_bold.setter
79
+ def cs_bold(self, value: bool | None) -> None:
80
+ self._set_bool_prop("bCs", value)
81
+
82
+ @property
83
+ def cs_italic(self) -> bool | None:
84
+ """Read/write tri-state value.
85
+
86
+ When |True|, causes the complex script characters in the run to be displayed in
87
+ italic typeface.
88
+ """
89
+ return self._get_bool_prop("iCs")
90
+
91
+ @cs_italic.setter
92
+ def cs_italic(self, value: bool | None) -> None:
93
+ self._set_bool_prop("iCs", value)
94
+
95
+ @property
96
+ def double_strike(self) -> bool | None:
97
+ """Read/write tri-state value.
98
+
99
+ When |True|, causes the text in the run to appear with double strikethrough.
100
+ """
101
+ return self._get_bool_prop("dstrike")
102
+
103
+ @double_strike.setter
104
+ def double_strike(self, value: bool | None) -> None:
105
+ self._set_bool_prop("dstrike", value)
106
+
107
+ @property
108
+ def emboss(self) -> bool | None:
109
+ """Read/write tri-state value.
110
+
111
+ When |True|, causes the text in the run to appear as if raised off the page in
112
+ relief.
113
+ """
114
+ return self._get_bool_prop("emboss")
115
+
116
+ @emboss.setter
117
+ def emboss(self, value: bool | None) -> None:
118
+ self._set_bool_prop("emboss", value)
119
+
120
+ @property
121
+ def hidden(self) -> bool | None:
122
+ """Read/write tri-state value.
123
+
124
+ When |True|, causes the text in the run to be hidden from display, unless
125
+ applications settings force hidden text to be shown.
126
+ """
127
+ return self._get_bool_prop("vanish")
128
+
129
+ @hidden.setter
130
+ def hidden(self, value: bool | None) -> None:
131
+ self._set_bool_prop("vanish", value)
132
+
133
+ @property
134
+ def highlight_color(self) -> WD_COLOR_INDEX | None:
135
+ """Color of highlighing applied or |None| if not highlighted."""
136
+ rPr = self._element.rPr
137
+ if rPr is None:
138
+ return None
139
+ return rPr.highlight_val
140
+
141
+ @highlight_color.setter
142
+ def highlight_color(self, value: WD_COLOR_INDEX | None):
143
+ rPr = self._element.get_or_add_rPr()
144
+ rPr.highlight_val = value
145
+
146
+ @property
147
+ def italic(self) -> bool | None:
148
+ """Read/write tri-state value.
149
+
150
+ When |True|, causes the text of the run to appear in italics. |None| indicates
151
+ the effective value is inherited from the style hierarchy.
152
+ """
153
+ return self._get_bool_prop("i")
154
+
155
+ @italic.setter
156
+ def italic(self, value: bool | None) -> None:
157
+ self._set_bool_prop("i", value)
158
+
159
+ @property
160
+ def imprint(self) -> bool | None:
161
+ """Read/write tri-state value.
162
+
163
+ When |True|, causes the text in the run to appear as if pressed into the page.
164
+ """
165
+ return self._get_bool_prop("imprint")
166
+
167
+ @imprint.setter
168
+ def imprint(self, value: bool | None) -> None:
169
+ self._set_bool_prop("imprint", value)
170
+
171
+ @property
172
+ def math(self) -> bool | None:
173
+ """Read/write tri-state value.
174
+
175
+ When |True|, specifies this run contains WML that should be handled as though it
176
+ was Office Open XML Math.
177
+ """
178
+ return self._get_bool_prop("oMath")
179
+
180
+ @math.setter
181
+ def math(self, value: bool | None) -> None:
182
+ self._set_bool_prop("oMath", value)
183
+
184
+ @property
185
+ def name(self) -> str | None:
186
+ """The typeface name for this |Font|.
187
+
188
+ Causes the text it controls to appear in the named font, if a matching font is
189
+ found. |None| indicates the typeface is inherited from the style hierarchy.
190
+
191
+ Returns the value of `ascii` font. To access other font settings, use `eastAsia`
192
+ or `hAnsi` properties.
193
+ """
194
+ rPr = self._element.rPr
195
+ if rPr is None:
196
+ return None
197
+ return rPr.rFonts_ascii
198
+
199
+ @name.setter
200
+ def name(self, value: dict[str, str | None] | str | None) -> None:
201
+ rPr = self._element.get_or_add_rPr()
202
+ if isinstance(value, dict):
203
+ if "ascii" in value:
204
+ rPr.rFonts_ascii = value["ascii"]
205
+ if "hAnsi" in value:
206
+ rPr.rFonts_hAnsi = value["hAnsi"]
207
+ if "eastAsia" in value:
208
+ rPr.rFonts_eastAsia = value["eastAsia"]
209
+ else:
210
+ rPr.rFonts_ascii = value
211
+ rPr.rFonts_hAnsi = value
212
+ rPr.rFonts_eastAsia = value
213
+
214
+ @property
215
+ def eastAsia(self) -> str | None:
216
+ """The East Asian typeface name for this |Font|."""
217
+ rPr = self._element.rPr
218
+ if rPr is None:
219
+ return None
220
+ return rPr.rFonts_eastAsia
221
+
222
+ @eastAsia.setter
223
+ def eastAsia(self, value: str | None) -> None:
224
+ rPr = self._element.get_or_add_rPr()
225
+ rPr.rFonts_eastAsia = value
226
+
227
+ @property
228
+ def hAnsi(self) -> str | None:
229
+ """The high ANSI typeface name for this |Font|."""
230
+ rPr = self._element.rPr
231
+ if rPr is None:
232
+ return None
233
+ return rPr.rFonts_hAnsi
234
+
235
+ @hAnsi.setter
236
+ def hAnsi(self, value: str | None) -> None:
237
+ rPr = self._element.get_or_add_rPr()
238
+ rPr.rFonts_hAnsi = value
239
+
240
+ @property
241
+ def no_proof(self) -> bool | None:
242
+ """Read/write tri-state value.
243
+
244
+ When |True|, specifies that the contents of this run should not report any
245
+ errors when the document is scanned for spelling and grammar.
246
+ """
247
+ return self._get_bool_prop("noProof")
248
+
249
+ @no_proof.setter
250
+ def no_proof(self, value: bool | None) -> None:
251
+ self._set_bool_prop("noProof", value)
252
+
253
+ @property
254
+ def outline(self) -> bool | None:
255
+ """Read/write tri-state value.
256
+
257
+ When |True| causes the characters in the run to appear as if they have an
258
+ outline, by drawing a one pixel wide border around the inside and outside
259
+ borders of each character glyph.
260
+ """
261
+ return self._get_bool_prop("outline")
262
+
263
+ @outline.setter
264
+ def outline(self, value: bool | None) -> None:
265
+ self._set_bool_prop("outline", value)
266
+
267
+ @property
268
+ def rtl(self) -> bool | None:
269
+ """Read/write tri-state value.
270
+
271
+ When |True| causes the text in the run to have right-to-left characteristics.
272
+ """
273
+ return self._get_bool_prop("rtl")
274
+
275
+ @rtl.setter
276
+ def rtl(self, value: bool | None) -> None:
277
+ self._set_bool_prop("rtl", value)
278
+
279
+ @property
280
+ def shadow(self) -> bool | None:
281
+ """Read/write tri-state value.
282
+
283
+ When |True| causes the text in the run to appear as if each character has a
284
+ shadow.
285
+ """
286
+ return self._get_bool_prop("shadow")
287
+
288
+ @shadow.setter
289
+ def shadow(self, value: bool | None) -> None:
290
+ self._set_bool_prop("shadow", value)
291
+
292
+ @property
293
+ def size(self) -> Length | None:
294
+ """Font height in English Metric Units (EMU).
295
+
296
+ |None| indicates the font size should be inherited from the style hierarchy.
297
+ |Length| is a subclass of |int| having properties for convenient conversion into
298
+ points or other length units. The :class:`docx.shared.Pt` class allows
299
+ convenient specification of point values::
300
+
301
+ >>> font.size = Pt(24)
302
+ >>> font.size
303
+ 304800
304
+ >>> font.size.pt
305
+ 24.0
306
+
307
+ """
308
+ rPr = self._element.rPr
309
+ if rPr is None:
310
+ return None
311
+ return rPr.sz_val
312
+
313
+ @size.setter
314
+ def size(self, emu: int | Length | None) -> None:
315
+ rPr = self._element.get_or_add_rPr()
316
+ rPr.sz_val = None if emu is None else Emu(emu)
317
+
318
+ @property
319
+ def small_caps(self) -> bool | None:
320
+ """Read/write tri-state value.
321
+
322
+ When |True| causes the lowercase characters in the run to appear as capital
323
+ letters two points smaller than the font size specified for the run.
324
+ """
325
+ return self._get_bool_prop("smallCaps")
326
+
327
+ @small_caps.setter
328
+ def small_caps(self, value: bool | None) -> None:
329
+ self._set_bool_prop("smallCaps", value)
330
+
331
+ @property
332
+ def snap_to_grid(self) -> bool | None:
333
+ """Read/write tri-state value.
334
+
335
+ When |True| causes the run to use the document grid characters per line settings
336
+ defined in the docGrid element when laying out the characters in this run.
337
+ """
338
+ return self._get_bool_prop("snapToGrid")
339
+
340
+ @snap_to_grid.setter
341
+ def snap_to_grid(self, value: bool | None) -> None:
342
+ self._set_bool_prop("snapToGrid", value)
343
+
344
+ @property
345
+ def spec_vanish(self) -> bool | None:
346
+ """Read/write tri-state value.
347
+
348
+ When |True|, specifies that the given run shall always behave as if it is
349
+ hidden, even when hidden text is being displayed in the current document. The
350
+ property has a very narrow, specialized use related to the table of contents.
351
+ Consult the spec (§17.3.2.36) for more details.
352
+ """
353
+ return self._get_bool_prop("specVanish")
354
+
355
+ @spec_vanish.setter
356
+ def spec_vanish(self, value: bool | None) -> None:
357
+ self._set_bool_prop("specVanish", value)
358
+
359
+ @property
360
+ def strike(self) -> bool | None:
361
+ """Read/write tri-state value.
362
+
363
+ When |True| causes the text in the run to appear with a single horizontal line
364
+ through the center of the line.
365
+ """
366
+ return self._get_bool_prop("strike")
367
+
368
+ @strike.setter
369
+ def strike(self, value: bool | None) -> None:
370
+ self._set_bool_prop("strike", value)
371
+
372
+ @property
373
+ def subscript(self) -> bool | None:
374
+ """Boolean indicating whether the characters in this |Font| appear as subscript.
375
+
376
+ |None| indicates the subscript/subscript value is inherited from the style
377
+ hierarchy.
378
+ """
379
+ rPr = self._element.rPr
380
+ if rPr is None:
381
+ return None
382
+ return rPr.subscript
383
+
384
+ @subscript.setter
385
+ def subscript(self, value: bool | None) -> None:
386
+ rPr = self._element.get_or_add_rPr()
387
+ rPr.subscript = value
388
+
389
+ @property
390
+ def superscript(self) -> bool | None:
391
+ """Boolean indicating whether the characters in this |Font| appear as
392
+ superscript.
393
+
394
+ |None| indicates the subscript/superscript value is inherited from the style
395
+ hierarchy.
396
+ """
397
+ rPr = self._element.rPr
398
+ if rPr is None:
399
+ return None
400
+ return rPr.superscript
401
+
402
+ @superscript.setter
403
+ def superscript(self, value: bool | None) -> None:
404
+ rPr = self._element.get_or_add_rPr()
405
+ rPr.superscript = value
406
+
407
+ @property
408
+ def underline(self) -> bool | WD_UNDERLINE | None:
409
+ """The underline style for this |Font|.
410
+
411
+ The value is one of |None|, |True|, |False|, or a member of :ref:`WdUnderline`.
412
+
413
+ |None| indicates the font inherits its underline value from the style hierarchy.
414
+ |False| indicates no underline. |True| indicates single underline. The values
415
+ from :ref:`WdUnderline` are used to specify other outline styles such as double,
416
+ wavy, and dotted.
417
+ """
418
+ rPr = self._element.rPr
419
+ if rPr is None:
420
+ return None
421
+ val = rPr.u_val
422
+ return (
423
+ None
424
+ if val == WD_UNDERLINE.INHERITED
425
+ else True
426
+ if val == WD_UNDERLINE.SINGLE
427
+ else False
428
+ if val == WD_UNDERLINE.NONE
429
+ else val
430
+ )
431
+
432
+ @underline.setter
433
+ def underline(self, value: bool | WD_UNDERLINE | None) -> None:
434
+ rPr = self._element.get_or_add_rPr()
435
+ # -- works fine without these two mappings, but only because True == 1 and
436
+ # -- False == 0, which happen to match the mapping for WD_UNDERLINE.SINGLE
437
+ # -- and .NONE respectively.
438
+ val = (
439
+ WD_UNDERLINE.SINGLE if value is True else WD_UNDERLINE.NONE if value is False else value
440
+ )
441
+ rPr.u_val = val
442
+
443
+ @property
444
+ def web_hidden(self) -> bool | None:
445
+ """Read/write tri-state value.
446
+
447
+ When |True|, specifies that the contents of this run shall be hidden when the
448
+ document is displayed in web page view.
449
+ """
450
+ return self._get_bool_prop("webHidden")
451
+
452
+ @web_hidden.setter
453
+ def web_hidden(self, value: bool | None) -> None:
454
+ self._set_bool_prop("webHidden", value)
455
+
456
+ def _get_bool_prop(self, name: str) -> bool | None:
457
+ """Return the value of boolean child of `w:rPr` having `name`."""
458
+ rPr = self._element.rPr
459
+ if rPr is None:
460
+ return None
461
+ return rPr._get_bool_val(name) # pyright: ignore[reportPrivateUsage]
462
+
463
+ def _set_bool_prop(self, name: str, value: bool | None):
464
+ """Assign `value` to the boolean child `name` of `w:rPr`."""
465
+ rPr = self._element.get_or_add_rPr()
466
+ rPr._set_bool_val(name, value) # pyright: ignore[reportPrivateUsage]
docx/text/hyperlink.py ADDED
@@ -0,0 +1,121 @@
1
+ """Hyperlink-related proxy objects for python-docx, Hyperlink in particular.
2
+
3
+ A hyperlink occurs in a paragraph, at the same level as a Run, and a hyperlink itself
4
+ contains runs, which is where the visible text of the hyperlink is stored. So it's kind
5
+ of in-between, less than a paragraph and more than a run. So it gets its own module.
6
+ """
7
+
8
+ from __future__ import annotations
9
+
10
+ from typing import TYPE_CHECKING
11
+
12
+ from docx.shared import Parented
13
+ from docx.text.run import Run
14
+
15
+ if TYPE_CHECKING:
16
+ import docx.types as t
17
+ from docx.oxml.text.hyperlink import CT_Hyperlink
18
+
19
+
20
+ class Hyperlink(Parented):
21
+ """Proxy object wrapping a `<w:hyperlink>` element.
22
+
23
+ A hyperlink occurs as a child of a paragraph, at the same level as a Run. A
24
+ hyperlink itself contains runs, which is where the visible text of the hyperlink is
25
+ stored.
26
+ """
27
+
28
+ def __init__(self, hyperlink: CT_Hyperlink, parent: t.ProvidesStoryPart):
29
+ super().__init__(parent)
30
+ self._parent = parent
31
+ self._hyperlink = self._element = hyperlink
32
+
33
+ @property
34
+ def address(self) -> str:
35
+ """The "URL" of the hyperlink (but not necessarily a web link).
36
+
37
+ While commonly a web link like "https://google.com" the hyperlink address can
38
+ take a variety of forms including "internal links" to bookmarked locations
39
+ within the document. When this hyperlink is an internal "jump" to for example a
40
+ heading from the table-of-contents (TOC), the address is blank. The bookmark
41
+ reference (like "_Toc147925734") is stored in the `.fragment` property.
42
+ """
43
+ rId = self._hyperlink.rId
44
+ return self._parent.part.rels[rId].target_ref if rId else ""
45
+
46
+ @property
47
+ def contains_page_break(self) -> bool:
48
+ """True when the text of this hyperlink is broken across page boundaries.
49
+
50
+ This is not uncommon and can happen for example when the hyperlink text is
51
+ multiple words and occurs in the last line of a page. Theoretically, a hyperlink
52
+ can contain more than one page break but that would be extremely uncommon in
53
+ practice. Still, this value should be understood to mean that "one-or-more"
54
+ rendered page breaks are present.
55
+ """
56
+ return bool(self._hyperlink.lastRenderedPageBreaks)
57
+
58
+ @property
59
+ def fragment(self) -> str:
60
+ """Reference like `#glossary` at end of URL that refers to a sub-resource.
61
+
62
+ Note that this value does not include the fragment-separator character ("#").
63
+
64
+ This value is known as a "named anchor" in an HTML context and "anchor" in the
65
+ MS API, but an "anchor" element (`<a>`) represents a full hyperlink in HTML so
66
+ we avoid confusion by using the more precise RFC 3986 naming "URI fragment".
67
+
68
+ These are also used to refer to bookmarks within the same document, in which
69
+ case the `.address` value with be blank ("") and this property will hold a
70
+ value like "_Toc147925734".
71
+
72
+ To reliably get an entire web URL you will need to concatenate this with the
73
+ `.address` value, separated by "#" when both are present. Consider using the
74
+ `.url` property for that purpose.
75
+
76
+ Word sometimes stores a fragment in this property (an XML attribute) and
77
+ sometimes with the address, depending on how the URL is inserted, so don't
78
+ depend on this field being empty to indicate no fragment is present.
79
+ """
80
+ return self._hyperlink.anchor or ""
81
+
82
+ @property
83
+ def runs(self) -> list[Run]:
84
+ """List of |Run| instances in this hyperlink.
85
+
86
+ Together these define the visible text of the hyperlink. The text of a hyperlink
87
+ is typically contained in a single run will be broken into multiple runs if for
88
+ example part of the hyperlink is bold or the text was changed after the document
89
+ was saved.
90
+ """
91
+ return [Run(r, self._parent) for r in self._hyperlink.r_lst]
92
+
93
+ @property
94
+ def text(self) -> str:
95
+ """String formed by concatenating the text of each run in the hyperlink.
96
+
97
+ Tabs and line breaks in the XML are mapped to ``\\t`` and ``\\n`` characters
98
+ respectively. Note that rendered page-breaks can occur within a hyperlink but
99
+ they are not reflected in this text.
100
+ """
101
+ return self._hyperlink.text
102
+
103
+ @property
104
+ def url(self) -> str:
105
+ """Convenience property to get web URLs from hyperlinks that contain them.
106
+
107
+ This value is the empty string ("") when there is no address portion, so its
108
+ boolean value can also be used to distinguish external URIs from internal "jump"
109
+ hyperlinks like those found in a table-of-contents.
110
+
111
+ Note that this value may also be a link to a file, so if you only want web-urls
112
+ you'll need to check for a protocol prefix like `https://`.
113
+
114
+ When both an address and fragment are present, the return value joins the two
115
+ separated by the fragment-separator hash ("#"). Otherwise this value is the same
116
+ as that of the `.address` property.
117
+ """
118
+ address, fragment = self.address, self.fragment
119
+ if not address:
120
+ return ""
121
+ return f"{address}#{fragment}" if fragment else address
docx/text/pagebreak.py ADDED
@@ -0,0 +1,104 @@
1
+ """Proxy objects related to rendered page-breaks."""
2
+
3
+ from __future__ import annotations
4
+
5
+ from typing import TYPE_CHECKING
6
+
7
+ from docx.oxml.text.pagebreak import CT_LastRenderedPageBreak
8
+ from docx.shared import Parented
9
+
10
+ if TYPE_CHECKING:
11
+ import docx.types as t
12
+ from docx.text.paragraph import Paragraph
13
+
14
+
15
+ class RenderedPageBreak(Parented):
16
+ """A page-break inserted by Word during page-layout for print or display purposes.
17
+
18
+ This usually does not correspond to a "hard" page-break inserted by the document
19
+ author, rather just that Word ran out of room on one page and needed to start
20
+ another. The position of these can change depending on the printer and page-size, as
21
+ well as margins, etc. They also will change in response to edits, but not until Word
22
+ loads and saves the document.
23
+
24
+ Note these are never inserted by `python-docx` because it has no rendering function.
25
+ These are generally only useful for text-extraction of existing documents when
26
+ `python-docx` is being used solely as a document "reader".
27
+
28
+ NOTE: a rendered page-break can occur within a hyperlink; consider a multi-word
29
+ hyperlink like "excellent Wikipedia article on LLMs" that happens to fall close to
30
+ the end of the last line on a page such that the page breaks between "Wikipedia" and
31
+ "article". In such a "page-breaks-in-hyperlink" case, THESE METHODS WILL "MOVE" THE
32
+ PAGE-BREAK to occur after the hyperlink, such that the entire hyperlink appears in
33
+ the paragraph returned by `.preceding_paragraph_fragment`. While this places the
34
+ "tail" text of the hyperlink on the "wrong" page, it avoids having two hyperlinks
35
+ each with a fragment of the actual text and pointing to the same address.
36
+ """
37
+
38
+ def __init__(
39
+ self,
40
+ lastRenderedPageBreak: CT_LastRenderedPageBreak,
41
+ parent: t.ProvidesStoryPart,
42
+ ):
43
+ super().__init__(parent)
44
+ self._element = lastRenderedPageBreak
45
+ self._lastRenderedPageBreak = lastRenderedPageBreak
46
+
47
+ @property
48
+ def preceding_paragraph_fragment(self) -> Paragraph | None:
49
+ """A "loose" paragraph containing the content preceding this page-break.
50
+
51
+ Compare `.following_paragraph_fragment` as these two are intended to be used
52
+ together.
53
+
54
+ This value is `None` when no content precedes this page-break. This case is
55
+ common and occurs whenever a page breaks on an even paragraph boundary.
56
+ Returning `None` for this case avoids "inserting" a non-existent paragraph into
57
+ the content stream. Note that content can include DrawingML items like images or
58
+ charts.
59
+
60
+ Note the returned paragraph *is divorced from the document body*. Any changes
61
+ made to it will not be reflected in the document. It is intended to provide a
62
+ familiar container (`Paragraph`) to interrogate for the content preceding this
63
+ page-break in the paragraph in which it occured.
64
+
65
+ Contains the entire hyperlink when this break occurs within a hyperlink.
66
+ """
67
+ if self._lastRenderedPageBreak.precedes_all_content:
68
+ return None
69
+
70
+ from docx.text.paragraph import Paragraph
71
+
72
+ return Paragraph(self._lastRenderedPageBreak.preceding_fragment_p, self._parent)
73
+
74
+ @property
75
+ def following_paragraph_fragment(self) -> Paragraph | None:
76
+ """A "loose" paragraph containing the content following this page-break.
77
+
78
+ HAS POTENTIALLY SURPRISING BEHAVIORS so read carefully to be sure this is what
79
+ you want. This is primarily targeted toward text-extraction use-cases for which
80
+ precisely associating text with the page it occurs on is important.
81
+
82
+ Compare `.preceding_paragraph_fragment` as these two are intended to be used
83
+ together.
84
+
85
+ This value is `None` when no content follows this page-break. This case is
86
+ unlikely to occur in practice because Word places even-paragraph-boundary
87
+ page-breaks on the paragraph *following* the page-break. Still, it is possible
88
+ and must be checked for. Returning `None` for this case avoids "inserting" an
89
+ extra, non-existent paragraph into the content stream. Note that content can
90
+ include DrawingML items like images or charts, not just text.
91
+
92
+ The returned paragraph *is divorced from the document body*. Any changes made to
93
+ it will not be reflected in the document. It is intended to provide a container
94
+ (`Paragraph`) with familiar properties and methods that can be used to
95
+ characterize the paragraph content following a mid-paragraph page-break.
96
+
97
+ Contains no portion of the hyperlink when this break occurs within a hyperlink.
98
+ """
99
+ if self._lastRenderedPageBreak.follows_all_content:
100
+ return None
101
+
102
+ from docx.text.paragraph import Paragraph
103
+
104
+ return Paragraph(self._lastRenderedPageBreak.following_fragment_p, self._parent)