ps-python-pptx 1.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 (117) hide show
  1. pptx/__init__.py +84 -0
  2. pptx/action.py +270 -0
  3. pptx/api.py +49 -0
  4. pptx/chart/__init__.py +0 -0
  5. pptx/chart/axis.py +523 -0
  6. pptx/chart/category.py +200 -0
  7. pptx/chart/chart.py +280 -0
  8. pptx/chart/data.py +864 -0
  9. pptx/chart/datalabel.py +288 -0
  10. pptx/chart/legend.py +79 -0
  11. pptx/chart/marker.py +70 -0
  12. pptx/chart/plot.py +412 -0
  13. pptx/chart/point.py +101 -0
  14. pptx/chart/series.py +258 -0
  15. pptx/chart/xlsx.py +272 -0
  16. pptx/chart/xmlwriter.py +1840 -0
  17. pptx/dml/__init__.py +0 -0
  18. pptx/dml/chtfmt.py +40 -0
  19. pptx/dml/color.py +301 -0
  20. pptx/dml/effect.py +41 -0
  21. pptx/dml/fill.py +468 -0
  22. pptx/dml/line.py +100 -0
  23. pptx/enum/__init__.py +0 -0
  24. pptx/enum/action.py +71 -0
  25. pptx/enum/base.py +175 -0
  26. pptx/enum/chart.py +492 -0
  27. pptx/enum/dml.py +405 -0
  28. pptx/enum/lang.py +685 -0
  29. pptx/enum/shapes.py +1029 -0
  30. pptx/enum/text.py +371 -0
  31. pptx/exc.py +23 -0
  32. pptx/media.py +197 -0
  33. pptx/opc/__init__.py +0 -0
  34. pptx/opc/constants.py +331 -0
  35. pptx/opc/oxml.py +188 -0
  36. pptx/opc/package.py +762 -0
  37. pptx/opc/packuri.py +109 -0
  38. pptx/opc/serialized.py +296 -0
  39. pptx/opc/shared.py +20 -0
  40. pptx/opc/spec.py +44 -0
  41. pptx/oxml/__init__.py +492 -0
  42. pptx/oxml/action.py +53 -0
  43. pptx/oxml/chart/__init__.py +0 -0
  44. pptx/oxml/chart/axis.py +297 -0
  45. pptx/oxml/chart/chart.py +282 -0
  46. pptx/oxml/chart/datalabel.py +252 -0
  47. pptx/oxml/chart/legend.py +72 -0
  48. pptx/oxml/chart/marker.py +61 -0
  49. pptx/oxml/chart/plot.py +345 -0
  50. pptx/oxml/chart/series.py +254 -0
  51. pptx/oxml/chart/shared.py +219 -0
  52. pptx/oxml/coreprops.py +288 -0
  53. pptx/oxml/dml/__init__.py +0 -0
  54. pptx/oxml/dml/color.py +111 -0
  55. pptx/oxml/dml/fill.py +197 -0
  56. pptx/oxml/dml/line.py +12 -0
  57. pptx/oxml/ns.py +129 -0
  58. pptx/oxml/presentation.py +130 -0
  59. pptx/oxml/shapes/__init__.py +19 -0
  60. pptx/oxml/shapes/autoshape.py +455 -0
  61. pptx/oxml/shapes/connector.py +107 -0
  62. pptx/oxml/shapes/graphfrm.py +342 -0
  63. pptx/oxml/shapes/groupshape.py +280 -0
  64. pptx/oxml/shapes/picture.py +270 -0
  65. pptx/oxml/shapes/shared.py +523 -0
  66. pptx/oxml/simpletypes.py +740 -0
  67. pptx/oxml/slide.py +347 -0
  68. pptx/oxml/table.py +588 -0
  69. pptx/oxml/text.py +748 -0
  70. pptx/oxml/theme.py +29 -0
  71. pptx/oxml/xmlchemy.py +717 -0
  72. pptx/package.py +222 -0
  73. pptx/parts/__init__.py +0 -0
  74. pptx/parts/chart.py +95 -0
  75. pptx/parts/coreprops.py +167 -0
  76. pptx/parts/embeddedpackage.py +93 -0
  77. pptx/parts/image.py +275 -0
  78. pptx/parts/media.py +37 -0
  79. pptx/parts/presentation.py +126 -0
  80. pptx/parts/slide.py +297 -0
  81. pptx/parts/theme.py +13 -0
  82. pptx/presentation.py +113 -0
  83. pptx/py.typed +0 -0
  84. pptx/shapes/__init__.py +26 -0
  85. pptx/shapes/autoshape.py +355 -0
  86. pptx/shapes/base.py +293 -0
  87. pptx/shapes/connector.py +297 -0
  88. pptx/shapes/freeform.py +337 -0
  89. pptx/shapes/graphfrm.py +166 -0
  90. pptx/shapes/group.py +69 -0
  91. pptx/shapes/picture.py +203 -0
  92. pptx/shapes/placeholder.py +407 -0
  93. pptx/shapes/shapetree.py +1190 -0
  94. pptx/shared.py +82 -0
  95. pptx/slide.py +577 -0
  96. pptx/spec.py +632 -0
  97. pptx/table.py +520 -0
  98. pptx/templates/default.pptx +0 -0
  99. pptx/templates/docx-icon.emf +0 -0
  100. pptx/templates/generic-icon.emf +0 -0
  101. pptx/templates/notes.xml +23 -0
  102. pptx/templates/notesMaster.xml +352 -0
  103. pptx/templates/pptx-icon.emf +0 -0
  104. pptx/templates/theme.xml +321 -0
  105. pptx/templates/xlsx-icon.emf +0 -0
  106. pptx/text/__init__.py +0 -0
  107. pptx/text/fonts.py +399 -0
  108. pptx/text/layout.py +325 -0
  109. pptx/text/text.py +770 -0
  110. pptx/theme.py +135 -0
  111. pptx/types.py +36 -0
  112. pptx/util.py +284 -0
  113. ps_python_pptx-1.1.0.dist-info/METADATA +75 -0
  114. ps_python_pptx-1.1.0.dist-info/RECORD +117 -0
  115. ps_python_pptx-1.1.0.dist-info/WHEEL +5 -0
  116. ps_python_pptx-1.1.0.dist-info/licenses/LICENSE +20 -0
  117. ps_python_pptx-1.1.0.dist-info/top_level.txt +1 -0
pptx/text/fonts.py ADDED
@@ -0,0 +1,399 @@
1
+ """Objects related to system font file lookup."""
2
+
3
+ from __future__ import annotations
4
+
5
+ import os
6
+ import sys
7
+ from struct import calcsize, unpack_from
8
+
9
+ from pptx.util import lazyproperty
10
+
11
+
12
+ class FontFiles(object):
13
+ """A class-based singleton serving as a lazy cache for system font details."""
14
+
15
+ _font_files = None
16
+
17
+ @classmethod
18
+ def find(cls, family_name: str, is_bold: bool, is_italic: bool) -> str:
19
+ """Return the absolute path to an installed OpenType font.
20
+
21
+ File is matched by `family_name` and the styles `is_bold` and `is_italic`.
22
+ """
23
+ if cls._font_files is None:
24
+ cls._font_files = cls._installed_fonts()
25
+ return cls._font_files[(family_name, is_bold, is_italic)]
26
+
27
+ @classmethod
28
+ def _installed_fonts(cls):
29
+ """
30
+ Return a dict mapping a font descriptor to its font file path,
31
+ containing all the font files resident on the current machine. The
32
+ font descriptor is a (family_name, is_bold, is_italic) 3-tuple.
33
+ """
34
+ fonts = {}
35
+ for d in cls._font_directories():
36
+ for key, path in cls._iter_font_files_in(d):
37
+ fonts[key] = path
38
+ return fonts
39
+
40
+ @classmethod
41
+ def _font_directories(cls):
42
+ """
43
+ Return a sequence of directory paths likely to contain fonts on the
44
+ current platform.
45
+ """
46
+ if sys.platform.startswith("darwin"):
47
+ return cls._os_x_font_directories()
48
+ if sys.platform.startswith("win32"):
49
+ return cls._windows_font_directories()
50
+ raise OSError("unsupported operating system")
51
+
52
+ @classmethod
53
+ def _iter_font_files_in(cls, directory):
54
+ """
55
+ Generate the OpenType font files found in and under *directory*. Each
56
+ item is a key/value pair. The key is a (family_name, is_bold,
57
+ is_italic) 3-tuple, like ('Arial', True, False), and the value is the
58
+ absolute path to the font file.
59
+ """
60
+ for root, dirs, files in os.walk(directory):
61
+ for filename in files:
62
+ file_ext = os.path.splitext(filename)[1]
63
+ if file_ext.lower() not in (".otf", ".ttf"):
64
+ continue
65
+ path = os.path.abspath(os.path.join(root, filename))
66
+ with _Font.open(path) as f:
67
+ yield ((f.family_name, f.is_bold, f.is_italic), path)
68
+
69
+ @classmethod
70
+ def _os_x_font_directories(cls):
71
+ """
72
+ Return a sequence of directory paths on a Mac in which fonts are
73
+ likely to be located.
74
+ """
75
+ os_x_font_dirs = [
76
+ "/Library/Fonts",
77
+ "/Network/Library/Fonts",
78
+ "/System/Library/Fonts",
79
+ ]
80
+ home = os.environ.get("HOME")
81
+ if home is not None:
82
+ os_x_font_dirs.extend(
83
+ [os.path.join(home, "Library", "Fonts"), os.path.join(home, ".fonts")]
84
+ )
85
+ return os_x_font_dirs
86
+
87
+ @classmethod
88
+ def _windows_font_directories(cls):
89
+ """
90
+ Return a sequence of directory paths on Windows in which fonts are
91
+ likely to be located.
92
+ """
93
+ return [r"C:\Windows\Fonts"]
94
+
95
+
96
+ class _Font(object):
97
+ """
98
+ A wrapper around an OTF/TTF font file stream that knows how to parse it
99
+ for its name and style characteristics, e.g. bold and italic.
100
+ """
101
+
102
+ def __init__(self, stream):
103
+ self._stream = stream
104
+
105
+ def __enter__(self):
106
+ return self
107
+
108
+ def __exit__(self, exception_type, exception_value, exception_tb):
109
+ self._stream.close()
110
+
111
+ @property
112
+ def is_bold(self):
113
+ """
114
+ |True| if this font is marked as a bold style of its font family.
115
+ """
116
+ try:
117
+ return self._tables["head"].is_bold
118
+ except KeyError:
119
+ # some files don't have a head table
120
+ return False
121
+
122
+ @property
123
+ def is_italic(self):
124
+ """
125
+ |True| if this font is marked as an italic style of its font family.
126
+ """
127
+ try:
128
+ return self._tables["head"].is_italic
129
+ except KeyError:
130
+ # some files don't have a head table
131
+ return False
132
+
133
+ @classmethod
134
+ def open(cls, font_file_path):
135
+ """
136
+ Return a |_Font| instance loaded from *font_file_path*.
137
+ """
138
+ return cls(_Stream.open(font_file_path))
139
+
140
+ @property
141
+ def family_name(self):
142
+ """
143
+ The name of the typeface family for this font, e.g. 'Arial'. The full
144
+ typeface name includes optional style names, such as 'Regular' or
145
+ 'Bold Italic'. This attribute is only the common base name shared by
146
+ all fonts in the family.
147
+ """
148
+ return self._tables["name"].family_name
149
+
150
+ @lazyproperty
151
+ def _fields(self):
152
+ """5-tuple containing the fields read from the font file header.
153
+
154
+ Also known as the offset table.
155
+ """
156
+ # sfnt_version, tbl_count, search_range, entry_selector, range_shift
157
+ return self._stream.read_fields(">4sHHHH", 0)
158
+
159
+ def _iter_table_records(self):
160
+ """
161
+ Generate a (tag, offset, length) 3-tuple for each of the tables in
162
+ this font file.
163
+ """
164
+ count = self._table_count
165
+ bufr = self._stream.read(offset=12, length=count * 16)
166
+ tmpl = ">4sLLL"
167
+ for i in range(count):
168
+ offset = i * 16
169
+ tag, checksum, off, len_ = unpack_from(tmpl, bufr, offset)
170
+ yield tag.decode("utf-8"), off, len_
171
+
172
+ @lazyproperty
173
+ def _tables(self):
174
+ """
175
+ A mapping of OpenType table tag, e.g. 'name', to a table object
176
+ providing access to the contents of that table.
177
+ """
178
+ return dict(
179
+ (tag, _TableFactory(tag, self._stream, off, len_))
180
+ for tag, off, len_ in self._iter_table_records()
181
+ )
182
+
183
+ @property
184
+ def _table_count(self):
185
+ """
186
+ The number of tables in this OpenType font file.
187
+ """
188
+ return self._fields[1]
189
+
190
+
191
+ class _Stream(object):
192
+ """A thin wrapper around a binary file that facilitates reading C-struct values."""
193
+
194
+ def __init__(self, file):
195
+ self._file = file
196
+
197
+ @classmethod
198
+ def open(cls, path):
199
+ """Return |_Stream| providing binary access to contents of file at `path`."""
200
+ return cls(open(path, "rb"))
201
+
202
+ def close(self):
203
+ """
204
+ Close the wrapped file. Using the stream after closing raises an
205
+ exception.
206
+ """
207
+ self._file.close()
208
+
209
+ def read(self, offset, length):
210
+ """
211
+ Return *length* bytes from this stream starting at *offset*.
212
+ """
213
+ self._file.seek(offset)
214
+ return self._file.read(length)
215
+
216
+ def read_fields(self, template, offset=0):
217
+ """
218
+ Return a tuple containing the C-struct fields in this stream
219
+ specified by *template* and starting at *offset*.
220
+ """
221
+ self._file.seek(offset)
222
+ bufr = self._file.read(calcsize(template))
223
+ return unpack_from(template, bufr)
224
+
225
+
226
+ class _BaseTable(object):
227
+ """
228
+ Base class for OpenType font file table objects.
229
+ """
230
+
231
+ def __init__(self, tag, stream, offset, length):
232
+ self._tag = tag
233
+ self._stream = stream
234
+ self._offset = offset
235
+ self._length = length
236
+
237
+
238
+ class _HeadTable(_BaseTable):
239
+ """
240
+ OpenType font table having the tag 'head' and containing certain header
241
+ information for the font, including its bold and/or italic style.
242
+ """
243
+
244
+ def __init__(self, tag, stream, offset, length):
245
+ super(_HeadTable, self).__init__(tag, stream, offset, length)
246
+
247
+ @property
248
+ def is_bold(self):
249
+ """
250
+ |True| if this font is marked as having emboldened characters.
251
+ """
252
+ return bool(self._macStyle & 1)
253
+
254
+ @property
255
+ def is_italic(self):
256
+ """
257
+ |True| if this font is marked as having italicized characters.
258
+ """
259
+ return bool(self._macStyle & 2)
260
+
261
+ @lazyproperty
262
+ def _fields(self):
263
+ """
264
+ A 17-tuple containing the fields in this table.
265
+ """
266
+ return self._stream.read_fields(">4s4sLLHHqqhhhhHHHHH", self._offset)
267
+
268
+ @property
269
+ def _macStyle(self):
270
+ """
271
+ The unsigned short value of the 'macStyle' field in this head table.
272
+ """
273
+ return self._fields[12]
274
+
275
+
276
+ class _NameTable(_BaseTable):
277
+ """
278
+ An OpenType font table having the tag 'name' and containing the
279
+ name-related strings for the font.
280
+ """
281
+
282
+ def __init__(self, tag, stream, offset, length):
283
+ super(_NameTable, self).__init__(tag, stream, offset, length)
284
+
285
+ @property
286
+ def family_name(self):
287
+ """
288
+ The name of the typeface family for this font, e.g. 'Arial'.
289
+ """
290
+
291
+ def find_first(dict_, keys, default=None):
292
+ for key in keys:
293
+ value = dict_.get(key)
294
+ if value is not None:
295
+ return value
296
+ return default
297
+
298
+ # keys for Unicode, Mac, and Windows family name, respectively
299
+ return find_first(self._names, ((0, 1), (1, 1), (3, 1)))
300
+
301
+ @staticmethod
302
+ def _decode_name(raw_name, platform_id, encoding_id):
303
+ """
304
+ Return the unicode name decoded from *raw_name* using the encoding
305
+ implied by the combination of *platform_id* and *encoding_id*.
306
+ """
307
+ if platform_id == 1:
308
+ # reject non-Roman Mac font names
309
+ if encoding_id != 0:
310
+ return None
311
+ return raw_name.decode("mac-roman")
312
+ elif platform_id in (0, 3):
313
+ return raw_name.decode("utf-16-be")
314
+ else:
315
+ return None
316
+
317
+ def _iter_names(self):
318
+ """Generate a key/value pair for each name in this table.
319
+
320
+ The key is a (platform_id, name_id) 2-tuple and the value is the unicode text
321
+ corresponding to that key.
322
+ """
323
+ table_format, count, strings_offset = self._table_header
324
+ table_bytes = self._table_bytes
325
+
326
+ for idx in range(count):
327
+ platform_id, name_id, name = self._read_name(table_bytes, idx, strings_offset)
328
+ if name is None:
329
+ continue
330
+ yield ((platform_id, name_id), name)
331
+
332
+ @staticmethod
333
+ def _name_header(bufr, idx):
334
+ """
335
+ The (platform_id, encoding_id, language_id, name_id, length,
336
+ name_str_offset) 6-tuple encoded in each name record C-struct.
337
+ """
338
+ name_hdr_offset = 6 + idx * 12
339
+ return unpack_from(">HHHHHH", bufr, name_hdr_offset)
340
+
341
+ @staticmethod
342
+ def _raw_name_string(bufr, strings_offset, str_offset, length):
343
+ """
344
+ Return the *length* bytes comprising the encoded string in *bufr* at
345
+ *str_offset* in the strings area beginning at *strings_offset*.
346
+ """
347
+ offset = strings_offset + str_offset
348
+ tmpl = "%ds" % length
349
+ return unpack_from(tmpl, bufr, offset)[0]
350
+
351
+ def _read_name(self, bufr, idx, strings_offset):
352
+ """Return a (platform_id, name_id, name) 3-tuple for name at `idx` in `bufr`.
353
+
354
+ The triple looks like (0, 1, 'Arial'). `strings_offset` is the for the name at
355
+ `idx` position in `bufr`. `strings_offset` is the index into `bufr` where actual
356
+ name strings begin. The returned name is a unicode string.
357
+ """
358
+ platform_id, enc_id, lang_id, name_id, length, str_offset = self._name_header(bufr, idx)
359
+ name = self._read_name_text(bufr, platform_id, enc_id, strings_offset, str_offset, length)
360
+ return platform_id, name_id, name
361
+
362
+ def _read_name_text(
363
+ self, bufr, platform_id, encoding_id, strings_offset, name_str_offset, length
364
+ ):
365
+ """
366
+ Return the unicode name string at *name_str_offset* or |None| if
367
+ decoding its format is not supported.
368
+ """
369
+ raw_name = self._raw_name_string(bufr, strings_offset, name_str_offset, length)
370
+ return self._decode_name(raw_name, platform_id, encoding_id)
371
+
372
+ @lazyproperty
373
+ def _table_bytes(self):
374
+ """
375
+ The binary contents of this name table.
376
+ """
377
+ return self._stream.read(self._offset, self._length)
378
+
379
+ @property
380
+ def _table_header(self):
381
+ """
382
+ The (table_format, name_count, strings_offset) 3-tuple contained
383
+ in the header of this table.
384
+ """
385
+ return unpack_from(">HHH", self._table_bytes)
386
+
387
+ @lazyproperty
388
+ def _names(self):
389
+ """A mapping of (platform_id, name_id) keys to string names for this font."""
390
+ return dict(self._iter_names())
391
+
392
+
393
+ def _TableFactory(tag, stream, offset, length):
394
+ """
395
+ Return an instance of |Table| appropriate to *tag*, loaded from
396
+ *font_file* with content of *length* starting at *offset*.
397
+ """
398
+ TableClass = {"head": _HeadTable, "name": _NameTable}.get(tag, _BaseTable)
399
+ return TableClass(tag, stream, offset, length)