python-pptx2 2.13.0__tar.gz

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 (611) hide show
  1. python_pptx2-2.13.0/HISTORY.rst +2776 -0
  2. python_pptx2-2.13.0/LICENSE +22 -0
  3. python_pptx2-2.13.0/MANIFEST.in +6 -0
  4. python_pptx2-2.13.0/PKG-INFO +351 -0
  5. python_pptx2-2.13.0/README.rst +306 -0
  6. python_pptx2-2.13.0/features/act-action.feature +88 -0
  7. python_pptx2-2.13.0/features/act-hyperlink.feature +53 -0
  8. python_pptx2-2.13.0/features/cht-axis-props.feature +187 -0
  9. python_pptx2-2.13.0/features/cht-axistitle-props.feature +44 -0
  10. python_pptx2-2.13.0/features/cht-category-access.feature +72 -0
  11. python_pptx2-2.13.0/features/cht-category-props.feature +24 -0
  12. python_pptx2-2.13.0/features/cht-chart.feature +108 -0
  13. python_pptx2-2.13.0/features/cht-charttitle-props.feature +44 -0
  14. python_pptx2-2.13.0/features/cht-data-props.feature +107 -0
  15. python_pptx2-2.13.0/features/cht-datalabels.feature +208 -0
  16. python_pptx2-2.13.0/features/cht-gridlines-props.feature +11 -0
  17. python_pptx2-2.13.0/features/cht-legend-access.feature +31 -0
  18. python_pptx2-2.13.0/features/cht-legend-props.feature +82 -0
  19. python_pptx2-2.13.0/features/cht-marker-props.feature +59 -0
  20. python_pptx2-2.13.0/features/cht-plot-props.feature +121 -0
  21. python_pptx2-2.13.0/features/cht-point-access.feature +37 -0
  22. python_pptx2-2.13.0/features/cht-point-props.feature +21 -0
  23. python_pptx2-2.13.0/features/cht-replace-data.feature +40 -0
  24. python_pptx2-2.13.0/features/cht-series.feature +144 -0
  25. python_pptx2-2.13.0/features/cht-ticklabels-props.feature +26 -0
  26. python_pptx2-2.13.0/features/dml-color.feature +22 -0
  27. python_pptx2-2.13.0/features/dml-effect.feature +29 -0
  28. python_pptx2-2.13.0/features/dml-fill.feature +106 -0
  29. python_pptx2-2.13.0/features/dml-line.feature +62 -0
  30. python_pptx2-2.13.0/features/environment.py +24 -0
  31. python_pptx2-2.13.0/features/ph-inherit-props.feature +33 -0
  32. python_pptx2-2.13.0/features/ph-insert-shape.feature +33 -0
  33. python_pptx2-2.13.0/features/ph-phfmt-props.feature +25 -0
  34. python_pptx2-2.13.0/features/ph-placeholder-props.feature +53 -0
  35. python_pptx2-2.13.0/features/prs-coreprops.feature +20 -0
  36. python_pptx2-2.13.0/features/prs-default-template.feature +9 -0
  37. python_pptx2-2.13.0/features/prs-open-save.feature +39 -0
  38. python_pptx2-2.13.0/features/prs-presentation-props.feature +38 -0
  39. python_pptx2-2.13.0/features/shp-autoshape.feature +26 -0
  40. python_pptx2-2.13.0/features/shp-connector.feature +83 -0
  41. python_pptx2-2.13.0/features/shp-freeform.feature +45 -0
  42. python_pptx2-2.13.0/features/shp-graphicframe.feature +50 -0
  43. python_pptx2-2.13.0/features/shp-groupshape.feature +46 -0
  44. python_pptx2-2.13.0/features/shp-movie-props.feature +24 -0
  45. python_pptx2-2.13.0/features/shp-picture.feature +73 -0
  46. python_pptx2-2.13.0/features/shp-placeholder.feature +16 -0
  47. python_pptx2-2.13.0/features/shp-shapes.feature +384 -0
  48. python_pptx2-2.13.0/features/shp-shared.feature +211 -0
  49. python_pptx2-2.13.0/features/sld-background.feature +15 -0
  50. python_pptx2-2.13.0/features/sld-slide.feature +142 -0
  51. python_pptx2-2.13.0/features/sld-slides.feature +78 -0
  52. python_pptx2-2.13.0/features/steps/action.py +105 -0
  53. python_pptx2-2.13.0/features/steps/axis.py +303 -0
  54. python_pptx2-2.13.0/features/steps/background.py +27 -0
  55. python_pptx2-2.13.0/features/steps/category.py +157 -0
  56. python_pptx2-2.13.0/features/steps/chart.py +380 -0
  57. python_pptx2-2.13.0/features/steps/chartdata.py +210 -0
  58. python_pptx2-2.13.0/features/steps/color.py +70 -0
  59. python_pptx2-2.13.0/features/steps/coreprops.py +73 -0
  60. python_pptx2-2.13.0/features/steps/datalabel.py +195 -0
  61. python_pptx2-2.13.0/features/steps/effect.py +35 -0
  62. python_pptx2-2.13.0/features/steps/fill.py +156 -0
  63. python_pptx2-2.13.0/features/steps/font.py +150 -0
  64. python_pptx2-2.13.0/features/steps/font_color.py +94 -0
  65. python_pptx2-2.13.0/features/steps/helpers.py +51 -0
  66. python_pptx2-2.13.0/features/steps/legend.py +90 -0
  67. python_pptx2-2.13.0/features/steps/line.py +96 -0
  68. python_pptx2-2.13.0/features/steps/picture.py +77 -0
  69. python_pptx2-2.13.0/features/steps/placeholder.py +312 -0
  70. python_pptx2-2.13.0/features/steps/plot.py +135 -0
  71. python_pptx2-2.13.0/features/steps/presentation.py +222 -0
  72. python_pptx2-2.13.0/features/steps/series.py +345 -0
  73. python_pptx2-2.13.0/features/steps/shape.py +666 -0
  74. python_pptx2-2.13.0/features/steps/shapes.py +342 -0
  75. python_pptx2-2.13.0/features/steps/slide.py +206 -0
  76. python_pptx2-2.13.0/features/steps/slides.py +142 -0
  77. python_pptx2-2.13.0/features/steps/table.py +305 -0
  78. python_pptx2-2.13.0/features/steps/test_files/72-dpi.tiff +0 -0
  79. python_pptx2-2.13.0/features/steps/test_files/CVS_LOGO.WMF +0 -0
  80. python_pptx2-2.13.0/features/steps/test_files/act-props.pptm +0 -0
  81. python_pptx2-2.13.0/features/steps/test_files/calibriz.ttf +0 -0
  82. python_pptx2-2.13.0/features/steps/test_files/cht-axis-props.pptx +0 -0
  83. python_pptx2-2.13.0/features/steps/test_files/cht-category-access.pptx +0 -0
  84. python_pptx2-2.13.0/features/steps/test_files/cht-chart-props.pptx +0 -0
  85. python_pptx2-2.13.0/features/steps/test_files/cht-chart-type.pptx +0 -0
  86. python_pptx2-2.13.0/features/steps/test_files/cht-charts.pptx +0 -0
  87. python_pptx2-2.13.0/features/steps/test_files/cht-datalabels.pptx +0 -0
  88. python_pptx2-2.13.0/features/steps/test_files/cht-gridlines-props.pptx +0 -0
  89. python_pptx2-2.13.0/features/steps/test_files/cht-legend-props.pptx +0 -0
  90. python_pptx2-2.13.0/features/steps/test_files/cht-legend.pptx +0 -0
  91. python_pptx2-2.13.0/features/steps/test_files/cht-marker-props.pptx +0 -0
  92. python_pptx2-2.13.0/features/steps/test_files/cht-plot-props.pptx +0 -0
  93. python_pptx2-2.13.0/features/steps/test_files/cht-point-access.pptx +0 -0
  94. python_pptx2-2.13.0/features/steps/test_files/cht-point-props.pptx +0 -0
  95. python_pptx2-2.13.0/features/steps/test_files/cht-replace-data.pptx +0 -0
  96. python_pptx2-2.13.0/features/steps/test_files/cht-series.pptx +0 -0
  97. python_pptx2-2.13.0/features/steps/test_files/cht-ticklabels-props.pptx +0 -0
  98. python_pptx2-2.13.0/features/steps/test_files/dml-effect.pptx +0 -0
  99. python_pptx2-2.13.0/features/steps/test_files/dml-fill.pptx +0 -0
  100. python_pptx2-2.13.0/features/steps/test_files/dml-line.pptx +0 -0
  101. python_pptx2-2.13.0/features/steps/test_files/ext-rels.pptx +0 -0
  102. python_pptx2-2.13.0/features/steps/test_files/extracted-pptx/[Content_Types].xml +27 -0
  103. python_pptx2-2.13.0/features/steps/test_files/extracted-pptx/_rels/.rels +7 -0
  104. python_pptx2-2.13.0/features/steps/test_files/extracted-pptx/docProps/app.xml +42 -0
  105. python_pptx2-2.13.0/features/steps/test_files/extracted-pptx/docProps/core.xml +13 -0
  106. python_pptx2-2.13.0/features/steps/test_files/extracted-pptx/docProps/thumbnail.jpeg +0 -0
  107. python_pptx2-2.13.0/features/steps/test_files/extracted-pptx/ppt/_rels/presentation.xml.rels +10 -0
  108. python_pptx2-2.13.0/features/steps/test_files/extracted-pptx/ppt/presProps.xml +11 -0
  109. python_pptx2-2.13.0/features/steps/test_files/extracted-pptx/ppt/presentation.xml +106 -0
  110. python_pptx2-2.13.0/features/steps/test_files/extracted-pptx/ppt/printerSettings/printerSettings1.bin +0 -0
  111. python_pptx2-2.13.0/features/steps/test_files/extracted-pptx/ppt/slideLayouts/_rels/slideLayout1.xml.rels +4 -0
  112. python_pptx2-2.13.0/features/steps/test_files/extracted-pptx/ppt/slideLayouts/_rels/slideLayout10.xml.rels +4 -0
  113. python_pptx2-2.13.0/features/steps/test_files/extracted-pptx/ppt/slideLayouts/_rels/slideLayout11.xml.rels +4 -0
  114. python_pptx2-2.13.0/features/steps/test_files/extracted-pptx/ppt/slideLayouts/_rels/slideLayout2.xml.rels +4 -0
  115. python_pptx2-2.13.0/features/steps/test_files/extracted-pptx/ppt/slideLayouts/_rels/slideLayout3.xml.rels +4 -0
  116. python_pptx2-2.13.0/features/steps/test_files/extracted-pptx/ppt/slideLayouts/_rels/slideLayout4.xml.rels +4 -0
  117. python_pptx2-2.13.0/features/steps/test_files/extracted-pptx/ppt/slideLayouts/_rels/slideLayout5.xml.rels +4 -0
  118. python_pptx2-2.13.0/features/steps/test_files/extracted-pptx/ppt/slideLayouts/_rels/slideLayout6.xml.rels +4 -0
  119. python_pptx2-2.13.0/features/steps/test_files/extracted-pptx/ppt/slideLayouts/_rels/slideLayout7.xml.rels +4 -0
  120. python_pptx2-2.13.0/features/steps/test_files/extracted-pptx/ppt/slideLayouts/_rels/slideLayout8.xml.rels +4 -0
  121. python_pptx2-2.13.0/features/steps/test_files/extracted-pptx/ppt/slideLayouts/_rels/slideLayout9.xml.rels +4 -0
  122. python_pptx2-2.13.0/features/steps/test_files/extracted-pptx/ppt/slideLayouts/slideLayout1.xml +237 -0
  123. python_pptx2-2.13.0/features/steps/test_files/extracted-pptx/ppt/slideLayouts/slideLayout10.xml +165 -0
  124. python_pptx2-2.13.0/features/steps/test_files/extracted-pptx/ppt/slideLayouts/slideLayout11.xml +175 -0
  125. python_pptx2-2.13.0/features/steps/test_files/extracted-pptx/ppt/slideLayouts/slideLayout2.xml +165 -0
  126. python_pptx2-2.13.0/features/steps/test_files/extracted-pptx/ppt/slideLayouts/slideLayout3.xml +241 -0
  127. python_pptx2-2.13.0/features/steps/test_files/extracted-pptx/ppt/slideLayouts/slideLayout4.xml +283 -0
  128. python_pptx2-2.13.0/features/steps/test_files/extracted-pptx/ppt/slideLayouts/slideLayout5.xml +417 -0
  129. python_pptx2-2.13.0/features/steps/test_files/extracted-pptx/ppt/slideLayouts/slideLayout6.xml +113 -0
  130. python_pptx2-2.13.0/features/steps/test_files/extracted-pptx/ppt/slideLayouts/slideLayout7.xml +90 -0
  131. python_pptx2-2.13.0/features/steps/test_files/extracted-pptx/ppt/slideLayouts/slideLayout8.xml +272 -0
  132. python_pptx2-2.13.0/features/steps/test_files/extracted-pptx/ppt/slideLayouts/slideLayout9.xml +248 -0
  133. python_pptx2-2.13.0/features/steps/test_files/extracted-pptx/ppt/slideMasters/_rels/slideMaster1.xml.rels +15 -0
  134. python_pptx2-2.13.0/features/steps/test_files/extracted-pptx/ppt/slideMasters/slideMaster1.xml +505 -0
  135. python_pptx2-2.13.0/features/steps/test_files/extracted-pptx/ppt/slides/_rels/slide1.xml.rels +4 -0
  136. python_pptx2-2.13.0/features/steps/test_files/extracted-pptx/ppt/slides/slide1.xml +74 -0
  137. python_pptx2-2.13.0/features/steps/test_files/extracted-pptx/ppt/tableStyles.xml +2 -0
  138. python_pptx2-2.13.0/features/steps/test_files/extracted-pptx/ppt/theme/theme1.xml +281 -0
  139. python_pptx2-2.13.0/features/steps/test_files/extracted-pptx/ppt/viewProps.xml +32 -0
  140. python_pptx2-2.13.0/features/steps/test_files/font-color.pptx +0 -0
  141. python_pptx2-2.13.0/features/steps/test_files/just-two-mice.mp4 +0 -0
  142. python_pptx2-2.13.0/features/steps/test_files/just-two-mice.png +0 -0
  143. python_pptx2-2.13.0/features/steps/test_files/lyt-shapes.pptx +0 -0
  144. python_pptx2-2.13.0/features/steps/test_files/minimal.pptx +0 -0
  145. python_pptx2-2.13.0/features/steps/test_files/monty-truth.png +0 -0
  146. python_pptx2-2.13.0/features/steps/test_files/mst-placeholders.pptx +0 -0
  147. python_pptx2-2.13.0/features/steps/test_files/mst-shapes.pptx +0 -0
  148. python_pptx2-2.13.0/features/steps/test_files/mst-slide-layouts.pptx +0 -0
  149. python_pptx2-2.13.0/features/steps/test_files/ph-inherit-props.pptx +0 -0
  150. python_pptx2-2.13.0/features/steps/test_files/ph-populated-placeholders.pptx +0 -0
  151. python_pptx2-2.13.0/features/steps/test_files/ph-unpopulated-placeholders.pptx +0 -0
  152. python_pptx2-2.13.0/features/steps/test_files/pic.emf +0 -0
  153. python_pptx2-2.13.0/features/steps/test_files/prs-add-slide.pptx +0 -0
  154. python_pptx2-2.13.0/features/steps/test_files/prs-notes.pptx +0 -0
  155. python_pptx2-2.13.0/features/steps/test_files/prs-properties.pptx +0 -0
  156. python_pptx2-2.13.0/features/steps/test_files/prs-slide-masters.pptx +0 -0
  157. python_pptx2-2.13.0/features/steps/test_files/python-icon.jpeg +0 -0
  158. python_pptx2-2.13.0/features/steps/test_files/python-powered.png +0 -0
  159. python_pptx2-2.13.0/features/steps/test_files/python.bmp +0 -0
  160. python_pptx2-2.13.0/features/steps/test_files/shp-access-chart.pptx +0 -0
  161. python_pptx2-2.13.0/features/steps/test_files/shp-access-ole-object.pptx +0 -0
  162. python_pptx2-2.13.0/features/steps/test_files/shp-autoshape-adjustments.pptx +0 -0
  163. python_pptx2-2.13.0/features/steps/test_files/shp-autoshape-props.pptx +0 -0
  164. python_pptx2-2.13.0/features/steps/test_files/shp-common-props.pptx +0 -0
  165. python_pptx2-2.13.0/features/steps/test_files/shp-connector-props.pptx +0 -0
  166. python_pptx2-2.13.0/features/steps/test_files/shp-embedded-docx.docx +0 -0
  167. python_pptx2-2.13.0/features/steps/test_files/shp-embedded-pptx.pptx +0 -0
  168. python_pptx2-2.13.0/features/steps/test_files/shp-embedded-xlsx.xlsx +0 -0
  169. python_pptx2-2.13.0/features/steps/test_files/shp-freeform.pptx +0 -0
  170. python_pptx2-2.13.0/features/steps/test_files/shp-groupshape.pptx +0 -0
  171. python_pptx2-2.13.0/features/steps/test_files/shp-movie-props.pptx +0 -0
  172. python_pptx2-2.13.0/features/steps/test_files/shp-picture.pptx +0 -0
  173. python_pptx2-2.13.0/features/steps/test_files/shp-pos-and-size.pptx +0 -0
  174. python_pptx2-2.13.0/features/steps/test_files/shp-shapes.pptx +0 -0
  175. python_pptx2-2.13.0/features/steps/test_files/sld-background.pptx +0 -0
  176. python_pptx2-2.13.0/features/steps/test_files/sld-blank.pptx +0 -0
  177. python_pptx2-2.13.0/features/steps/test_files/sld-notes.pptx +0 -0
  178. python_pptx2-2.13.0/features/steps/test_files/sld-slide.pptx +0 -0
  179. python_pptx2-2.13.0/features/steps/test_files/sld-slides.pptx +0 -0
  180. python_pptx2-2.13.0/features/steps/test_files/sonic.gif +0 -0
  181. python_pptx2-2.13.0/features/steps/test_files/tbl-cell.pptx +0 -0
  182. python_pptx2-2.13.0/features/steps/test_files/test-image-jpg-mime.pptx +0 -0
  183. python_pptx2-2.13.0/features/steps/test_files/test.pptx +0 -0
  184. python_pptx2-2.13.0/features/steps/test_files/txt-fit-text.pptx +0 -0
  185. python_pptx2-2.13.0/features/steps/test_files/txt-font-props.pptx +0 -0
  186. python_pptx2-2.13.0/features/steps/test_files/txt-font-typeface.pptx +0 -0
  187. python_pptx2-2.13.0/features/steps/test_files/txt-paragraph-spacing.pptx +0 -0
  188. python_pptx2-2.13.0/features/steps/test_files/txt-text-frame.pptx +0 -0
  189. python_pptx2-2.13.0/features/steps/test_files/txt-text.pptx +0 -0
  190. python_pptx2-2.13.0/features/steps/text.py +248 -0
  191. python_pptx2-2.13.0/features/steps/text_frame.py +129 -0
  192. python_pptx2-2.13.0/features/tbl-cell.feature +113 -0
  193. python_pptx2-2.13.0/features/tbl-column.feature +10 -0
  194. python_pptx2-2.13.0/features/tbl-table.feature +60 -0
  195. python_pptx2-2.13.0/features/txt-fit-text.feature +11 -0
  196. python_pptx2-2.13.0/features/txt-font-color.feature +55 -0
  197. python_pptx2-2.13.0/features/txt-font-props.feature +149 -0
  198. python_pptx2-2.13.0/features/txt-paragraph.feature +95 -0
  199. python_pptx2-2.13.0/features/txt-text.feature +23 -0
  200. python_pptx2-2.13.0/features/txt-textframe.feature +75 -0
  201. python_pptx2-2.13.0/pyproject.toml +178 -0
  202. python_pptx2-2.13.0/setup.cfg +4 -0
  203. python_pptx2-2.13.0/src/pptx2/__init__.py +152 -0
  204. python_pptx2-2.13.0/src/pptx2/_color.py +75 -0
  205. python_pptx2-2.13.0/src/pptx2/_slide_importer.py +597 -0
  206. python_pptx2-2.13.0/src/pptx2/_svg.py +155 -0
  207. python_pptx2-2.13.0/src/pptx2/_template_applier.py +292 -0
  208. python_pptx2-2.13.0/src/pptx2/_textstyle.py +187 -0
  209. python_pptx2-2.13.0/src/pptx2/accessibility.py +365 -0
  210. python_pptx2-2.13.0/src/pptx2/action.py +270 -0
  211. python_pptx2-2.13.0/src/pptx2/animation.py +2237 -0
  212. python_pptx2-2.13.0/src/pptx2/api.py +49 -0
  213. python_pptx2-2.13.0/src/pptx2/audit.py +258 -0
  214. python_pptx2-2.13.0/src/pptx2/chart/__init__.py +0 -0
  215. python_pptx2-2.13.0/src/pptx2/chart/analytics.py +381 -0
  216. python_pptx2-2.13.0/src/pptx2/chart/axis.py +543 -0
  217. python_pptx2-2.13.0/src/pptx2/chart/category.py +200 -0
  218. python_pptx2-2.13.0/src/pptx2/chart/chart.py +670 -0
  219. python_pptx2-2.13.0/src/pptx2/chart/data.py +864 -0
  220. python_pptx2-2.13.0/src/pptx2/chart/datalabel.py +406 -0
  221. python_pptx2-2.13.0/src/pptx2/chart/legend.py +86 -0
  222. python_pptx2-2.13.0/src/pptx2/chart/marker.py +70 -0
  223. python_pptx2-2.13.0/src/pptx2/chart/palettes.py +129 -0
  224. python_pptx2-2.13.0/src/pptx2/chart/plot.py +462 -0
  225. python_pptx2-2.13.0/src/pptx2/chart/point.py +101 -0
  226. python_pptx2-2.13.0/src/pptx2/chart/quick_layouts.py +325 -0
  227. python_pptx2-2.13.0/src/pptx2/chart/series.py +334 -0
  228. python_pptx2-2.13.0/src/pptx2/chart/xlsx.py +272 -0
  229. python_pptx2-2.13.0/src/pptx2/chart/xmlwriter.py +1845 -0
  230. python_pptx2-2.13.0/src/pptx2/compose/__init__.py +28 -0
  231. python_pptx2-2.13.0/src/pptx2/compose/from_spec.py +1094 -0
  232. python_pptx2-2.13.0/src/pptx2/design/__init__.py +8 -0
  233. python_pptx2-2.13.0/src/pptx2/design/components.py +607 -0
  234. python_pptx2-2.13.0/src/pptx2/design/figures.py +389 -0
  235. python_pptx2-2.13.0/src/pptx2/design/layout.py +370 -0
  236. python_pptx2-2.13.0/src/pptx2/design/recipes.py +1967 -0
  237. python_pptx2-2.13.0/src/pptx2/design/style.py +209 -0
  238. python_pptx2-2.13.0/src/pptx2/design/tokens.py +915 -0
  239. python_pptx2-2.13.0/src/pptx2/diagrams.py +754 -0
  240. python_pptx2-2.13.0/src/pptx2/dml/__init__.py +0 -0
  241. python_pptx2-2.13.0/src/pptx2/dml/chtfmt.py +40 -0
  242. python_pptx2-2.13.0/src/pptx2/dml/color.py +496 -0
  243. python_pptx2-2.13.0/src/pptx2/dml/effect.py +909 -0
  244. python_pptx2-2.13.0/src/pptx2/dml/fill.py +691 -0
  245. python_pptx2-2.13.0/src/pptx2/dml/line.py +287 -0
  246. python_pptx2-2.13.0/src/pptx2/dml/picture.py +212 -0
  247. python_pptx2-2.13.0/src/pptx2/dml/three_d.py +381 -0
  248. python_pptx2-2.13.0/src/pptx2/enum/__init__.py +0 -0
  249. python_pptx2-2.13.0/src/pptx2/enum/action.py +71 -0
  250. python_pptx2-2.13.0/src/pptx2/enum/animation.py +31 -0
  251. python_pptx2-2.13.0/src/pptx2/enum/base.py +218 -0
  252. python_pptx2-2.13.0/src/pptx2/enum/chart.py +574 -0
  253. python_pptx2-2.13.0/src/pptx2/enum/dml.py +740 -0
  254. python_pptx2-2.13.0/src/pptx2/enum/lang.py +685 -0
  255. python_pptx2-2.13.0/src/pptx2/enum/presentation.py +133 -0
  256. python_pptx2-2.13.0/src/pptx2/enum/shapes.py +1029 -0
  257. python_pptx2-2.13.0/src/pptx2/enum/text.py +230 -0
  258. python_pptx2-2.13.0/src/pptx2/exc.py +42 -0
  259. python_pptx2-2.13.0/src/pptx2/formats.py +139 -0
  260. python_pptx2-2.13.0/src/pptx2/geometry.py +420 -0
  261. python_pptx2-2.13.0/src/pptx2/inherit.py +109 -0
  262. python_pptx2-2.13.0/src/pptx2/lint.py +2256 -0
  263. python_pptx2-2.13.0/src/pptx2/math.py +177 -0
  264. python_pptx2-2.13.0/src/pptx2/media.py +197 -0
  265. python_pptx2-2.13.0/src/pptx2/opc/__init__.py +0 -0
  266. python_pptx2-2.13.0/src/pptx2/opc/constants.py +332 -0
  267. python_pptx2-2.13.0/src/pptx2/opc/oxml.py +188 -0
  268. python_pptx2-2.13.0/src/pptx2/opc/package.py +762 -0
  269. python_pptx2-2.13.0/src/pptx2/opc/packuri.py +109 -0
  270. python_pptx2-2.13.0/src/pptx2/opc/serialized.py +296 -0
  271. python_pptx2-2.13.0/src/pptx2/opc/shared.py +20 -0
  272. python_pptx2-2.13.0/src/pptx2/opc/spec.py +45 -0
  273. python_pptx2-2.13.0/src/pptx2/oxml/__init__.py +555 -0
  274. python_pptx2-2.13.0/src/pptx2/oxml/action.py +53 -0
  275. python_pptx2-2.13.0/src/pptx2/oxml/chart/__init__.py +0 -0
  276. python_pptx2-2.13.0/src/pptx2/oxml/chart/axis.py +337 -0
  277. python_pptx2-2.13.0/src/pptx2/oxml/chart/chart.py +481 -0
  278. python_pptx2-2.13.0/src/pptx2/oxml/chart/datalabel.py +253 -0
  279. python_pptx2-2.13.0/src/pptx2/oxml/chart/legend.py +72 -0
  280. python_pptx2-2.13.0/src/pptx2/oxml/chart/marker.py +61 -0
  281. python_pptx2-2.13.0/src/pptx2/oxml/chart/plot.py +365 -0
  282. python_pptx2-2.13.0/src/pptx2/oxml/chart/series.py +425 -0
  283. python_pptx2-2.13.0/src/pptx2/oxml/chart/shared.py +220 -0
  284. python_pptx2-2.13.0/src/pptx2/oxml/coreprops.py +288 -0
  285. python_pptx2-2.13.0/src/pptx2/oxml/dml/__init__.py +0 -0
  286. python_pptx2-2.13.0/src/pptx2/oxml/dml/color.py +135 -0
  287. python_pptx2-2.13.0/src/pptx2/oxml/dml/effect.py +213 -0
  288. python_pptx2-2.13.0/src/pptx2/oxml/dml/fill.py +316 -0
  289. python_pptx2-2.13.0/src/pptx2/oxml/dml/line.py +12 -0
  290. python_pptx2-2.13.0/src/pptx2/oxml/dml/three_d.py +110 -0
  291. python_pptx2-2.13.0/src/pptx2/oxml/ns.py +135 -0
  292. python_pptx2-2.13.0/src/pptx2/oxml/presentation.py +313 -0
  293. python_pptx2-2.13.0/src/pptx2/oxml/shapes/__init__.py +19 -0
  294. python_pptx2-2.13.0/src/pptx2/oxml/shapes/autoshape.py +467 -0
  295. python_pptx2-2.13.0/src/pptx2/oxml/shapes/connector.py +107 -0
  296. python_pptx2-2.13.0/src/pptx2/oxml/shapes/graphfrm.py +347 -0
  297. python_pptx2-2.13.0/src/pptx2/oxml/shapes/groupshape.py +329 -0
  298. python_pptx2-2.13.0/src/pptx2/oxml/shapes/picture.py +270 -0
  299. python_pptx2-2.13.0/src/pptx2/oxml/shapes/shared.py +577 -0
  300. python_pptx2-2.13.0/src/pptx2/oxml/simpletypes.py +1027 -0
  301. python_pptx2-2.13.0/src/pptx2/oxml/slide.py +563 -0
  302. python_pptx2-2.13.0/src/pptx2/oxml/table.py +650 -0
  303. python_pptx2-2.13.0/src/pptx2/oxml/text.py +815 -0
  304. python_pptx2-2.13.0/src/pptx2/oxml/theme.py +36 -0
  305. python_pptx2-2.13.0/src/pptx2/oxml/xmlchemy.py +717 -0
  306. python_pptx2-2.13.0/src/pptx2/package.py +222 -0
  307. python_pptx2-2.13.0/src/pptx2/parts/__init__.py +0 -0
  308. python_pptx2-2.13.0/src/pptx2/parts/chart.py +95 -0
  309. python_pptx2-2.13.0/src/pptx2/parts/coreprops.py +167 -0
  310. python_pptx2-2.13.0/src/pptx2/parts/diagram.py +37 -0
  311. python_pptx2-2.13.0/src/pptx2/parts/embeddedpackage.py +93 -0
  312. python_pptx2-2.13.0/src/pptx2/parts/image.py +275 -0
  313. python_pptx2-2.13.0/src/pptx2/parts/media.py +37 -0
  314. python_pptx2-2.13.0/src/pptx2/parts/presentation.py +136 -0
  315. python_pptx2-2.13.0/src/pptx2/parts/slide.py +371 -0
  316. python_pptx2-2.13.0/src/pptx2/presentation.py +408 -0
  317. python_pptx2-2.13.0/src/pptx2/py.typed +0 -0
  318. python_pptx2-2.13.0/src/pptx2/render.py +586 -0
  319. python_pptx2-2.13.0/src/pptx2/section.py +272 -0
  320. python_pptx2-2.13.0/src/pptx2/shapes/__init__.py +26 -0
  321. python_pptx2-2.13.0/src/pptx2/shapes/autoshape.py +442 -0
  322. python_pptx2-2.13.0/src/pptx2/shapes/base.py +1078 -0
  323. python_pptx2-2.13.0/src/pptx2/shapes/connector.py +297 -0
  324. python_pptx2-2.13.0/src/pptx2/shapes/freeform.py +337 -0
  325. python_pptx2-2.13.0/src/pptx2/shapes/graphfrm.py +316 -0
  326. python_pptx2-2.13.0/src/pptx2/shapes/group.py +264 -0
  327. python_pptx2-2.13.0/src/pptx2/shapes/picture.py +422 -0
  328. python_pptx2-2.13.0/src/pptx2/shapes/placeholder.py +468 -0
  329. python_pptx2-2.13.0/src/pptx2/shapes/shapetree.py +2027 -0
  330. python_pptx2-2.13.0/src/pptx2/shared.py +82 -0
  331. python_pptx2-2.13.0/src/pptx2/skill/SKILL.md +450 -0
  332. python_pptx2-2.13.0/src/pptx2/skill/__init__.py +78 -0
  333. python_pptx2-2.13.0/src/pptx2/skill/__main__.py +64 -0
  334. python_pptx2-2.13.0/src/pptx2/skill/references/animations.md +189 -0
  335. python_pptx2-2.13.0/src/pptx2/skill/references/basics.md +421 -0
  336. python_pptx2-2.13.0/src/pptx2/skill/references/charts.md +254 -0
  337. python_pptx2-2.13.0/src/pptx2/skill/references/compose.md +234 -0
  338. python_pptx2-2.13.0/src/pptx2/skill/references/design.md +366 -0
  339. python_pptx2-2.13.0/src/pptx2/skill/references/effects.md +249 -0
  340. python_pptx2-2.13.0/src/pptx2/skill/references/end-to-end-deck.md +231 -0
  341. python_pptx2-2.13.0/src/pptx2/skill/references/geometry-and-arrows.md +334 -0
  342. python_pptx2-2.13.0/src/pptx2/skill/references/lint.md +275 -0
  343. python_pptx2-2.13.0/src/pptx2/skill/references/math.md +86 -0
  344. python_pptx2-2.13.0/src/pptx2/skill/references/picture-effects.md +129 -0
  345. python_pptx2-2.13.0/src/pptx2/skill/references/render.md +151 -0
  346. python_pptx2-2.13.0/src/pptx2/skill/references/smart-art.md +75 -0
  347. python_pptx2-2.13.0/src/pptx2/skill/references/space-aware-authoring.md +249 -0
  348. python_pptx2-2.13.0/src/pptx2/skill/references/tables.md +244 -0
  349. python_pptx2-2.13.0/src/pptx2/skill/references/theme.md +127 -0
  350. python_pptx2-2.13.0/src/pptx2/skill/references/three-d.md +109 -0
  351. python_pptx2-2.13.0/src/pptx2/skill/references/transitions.md +100 -0
  352. python_pptx2-2.13.0/src/pptx2/slide.py +1244 -0
  353. python_pptx2-2.13.0/src/pptx2/smart_art.py +220 -0
  354. python_pptx2-2.13.0/src/pptx2/spec.py +633 -0
  355. python_pptx2-2.13.0/src/pptx2/table.py +1181 -0
  356. python_pptx2-2.13.0/src/pptx2/table_styles.py +184 -0
  357. python_pptx2-2.13.0/src/pptx2/templates/default.pptx +0 -0
  358. python_pptx2-2.13.0/src/pptx2/templates/docx-icon.emf +0 -0
  359. python_pptx2-2.13.0/src/pptx2/templates/generic-icon.emf +0 -0
  360. python_pptx2-2.13.0/src/pptx2/templates/notes.xml +23 -0
  361. python_pptx2-2.13.0/src/pptx2/templates/notesMaster.xml +352 -0
  362. python_pptx2-2.13.0/src/pptx2/templates/pptx-icon.emf +0 -0
  363. python_pptx2-2.13.0/src/pptx2/templates/theme.xml +321 -0
  364. python_pptx2-2.13.0/src/pptx2/templates/xlsx-icon.emf +0 -0
  365. python_pptx2-2.13.0/src/pptx2/text/__init__.py +0 -0
  366. python_pptx2-2.13.0/src/pptx2/text/fonts.py +482 -0
  367. python_pptx2-2.13.0/src/pptx2/text/layout.py +374 -0
  368. python_pptx2-2.13.0/src/pptx2/text/text.py +1272 -0
  369. python_pptx2-2.13.0/src/pptx2/theme.py +721 -0
  370. python_pptx2-2.13.0/src/pptx2/types.py +36 -0
  371. python_pptx2-2.13.0/src/pptx2/util.py +263 -0
  372. python_pptx2-2.13.0/src/python_pptx2.egg-info/PKG-INFO +351 -0
  373. python_pptx2-2.13.0/src/python_pptx2.egg-info/SOURCES.txt +609 -0
  374. python_pptx2-2.13.0/src/python_pptx2.egg-info/dependency_links.txt +1 -0
  375. python_pptx2-2.13.0/src/python_pptx2.egg-info/entry_points.txt +3 -0
  376. python_pptx2-2.13.0/src/python_pptx2.egg-info/requires.txt +8 -0
  377. python_pptx2-2.13.0/src/python_pptx2.egg-info/top_level.txt +1 -0
  378. python_pptx2-2.13.0/tests/__init__.py +0 -0
  379. python_pptx2-2.13.0/tests/chart/__init__.py +0 -0
  380. python_pptx2-2.13.0/tests/chart/test_analytics.py +352 -0
  381. python_pptx2-2.13.0/tests/chart/test_axis.py +1268 -0
  382. python_pptx2-2.13.0/tests/chart/test_category.py +249 -0
  383. python_pptx2-2.13.0/tests/chart/test_chart.py +941 -0
  384. python_pptx2-2.13.0/tests/chart/test_chart_toggles.py +128 -0
  385. python_pptx2-2.13.0/tests/chart/test_collision_strategy.py +128 -0
  386. python_pptx2-2.13.0/tests/chart/test_data.py +893 -0
  387. python_pptx2-2.13.0/tests/chart/test_datalabel.py +669 -0
  388. python_pptx2-2.13.0/tests/chart/test_legend.py +187 -0
  389. python_pptx2-2.13.0/tests/chart/test_marker.py +134 -0
  390. python_pptx2-2.13.0/tests/chart/test_palettes.py +60 -0
  391. python_pptx2-2.13.0/tests/chart/test_plot.py +598 -0
  392. python_pptx2-2.13.0/tests/chart/test_point.py +221 -0
  393. python_pptx2-2.13.0/tests/chart/test_quick_layouts.py +202 -0
  394. python_pptx2-2.13.0/tests/chart/test_series.py +551 -0
  395. python_pptx2-2.13.0/tests/chart/test_xlsx.py +425 -0
  396. python_pptx2-2.13.0/tests/chart/test_xmlwriter.py +783 -0
  397. python_pptx2-2.13.0/tests/design/__init__.py +0 -0
  398. python_pptx2-2.13.0/tests/design/test_components.py +331 -0
  399. python_pptx2-2.13.0/tests/design/test_figures.py +225 -0
  400. python_pptx2-2.13.0/tests/design/test_layout.py +259 -0
  401. python_pptx2-2.13.0/tests/design/test_recipes.py +686 -0
  402. python_pptx2-2.13.0/tests/design/test_style.py +94 -0
  403. python_pptx2-2.13.0/tests/design/test_tokens.py +296 -0
  404. python_pptx2-2.13.0/tests/dml/__init__.py +0 -0
  405. python_pptx2-2.13.0/tests/dml/test_chtfmt.py +87 -0
  406. python_pptx2-2.13.0/tests/dml/test_color.py +389 -0
  407. python_pptx2-2.13.0/tests/dml/test_effect.py +553 -0
  408. python_pptx2-2.13.0/tests/dml/test_fill.py +980 -0
  409. python_pptx2-2.13.0/tests/dml/test_line.py +337 -0
  410. python_pptx2-2.13.0/tests/dml/test_three_d.py +327 -0
  411. python_pptx2-2.13.0/tests/enum/__init__.py +0 -0
  412. python_pptx2-2.13.0/tests/enum/test_base.py +107 -0
  413. python_pptx2-2.13.0/tests/enum/test_shapes.py +46 -0
  414. python_pptx2-2.13.0/tests/integration/__init__.py +1 -0
  415. python_pptx2-2.13.0/tests/integration/round_trip.py +90 -0
  416. python_pptx2-2.13.0/tests/integration/test_apply_template.py +175 -0
  417. python_pptx2-2.13.0/tests/integration/test_compose_package.py +44 -0
  418. python_pptx2-2.13.0/tests/integration/test_import_slide.py +473 -0
  419. python_pptx2-2.13.0/tests/integration/test_round_trip.py +244 -0
  420. python_pptx2-2.13.0/tests/opc/__init__.py +0 -0
  421. python_pptx2-2.13.0/tests/opc/test_oxml.py +211 -0
  422. python_pptx2-2.13.0/tests/opc/test_package.py +1037 -0
  423. python_pptx2-2.13.0/tests/opc/test_packuri.py +94 -0
  424. python_pptx2-2.13.0/tests/opc/test_serialized.py +410 -0
  425. python_pptx2-2.13.0/tests/oxml/__init__.py +0 -0
  426. python_pptx2-2.13.0/tests/oxml/shapes/__init__.py +0 -0
  427. python_pptx2-2.13.0/tests/oxml/shapes/test_autoshape.py +266 -0
  428. python_pptx2-2.13.0/tests/oxml/shapes/test_graphfrm.py +80 -0
  429. python_pptx2-2.13.0/tests/oxml/shapes/test_groupshape.py +297 -0
  430. python_pptx2-2.13.0/tests/oxml/shapes/test_picture.py +107 -0
  431. python_pptx2-2.13.0/tests/oxml/test___init__.py +105 -0
  432. python_pptx2-2.13.0/tests/oxml/test_dml.py +152 -0
  433. python_pptx2-2.13.0/tests/oxml/test_ns.py +94 -0
  434. python_pptx2-2.13.0/tests/oxml/test_presentation.py +63 -0
  435. python_pptx2-2.13.0/tests/oxml/test_simpletypes.py +283 -0
  436. python_pptx2-2.13.0/tests/oxml/test_slide.py +23 -0
  437. python_pptx2-2.13.0/tests/oxml/test_table.py +255 -0
  438. python_pptx2-2.13.0/tests/oxml/test_theme.py +23 -0
  439. python_pptx2-2.13.0/tests/oxml/test_xmlchemy.py +604 -0
  440. python_pptx2-2.13.0/tests/oxml/unitdata/__init__.py +0 -0
  441. python_pptx2-2.13.0/tests/oxml/unitdata/dml.py +102 -0
  442. python_pptx2-2.13.0/tests/oxml/unitdata/shape.py +215 -0
  443. python_pptx2-2.13.0/tests/oxml/unitdata/text.py +85 -0
  444. python_pptx2-2.13.0/tests/parts/__init__.py +0 -0
  445. python_pptx2-2.13.0/tests/parts/test_chart.py +152 -0
  446. python_pptx2-2.13.0/tests/parts/test_coreprops.py +198 -0
  447. python_pptx2-2.13.0/tests/parts/test_embeddedpackage.py +74 -0
  448. python_pptx2-2.13.0/tests/parts/test_image.py +246 -0
  449. python_pptx2-2.13.0/tests/parts/test_media.py +33 -0
  450. python_pptx2-2.13.0/tests/parts/test_presentation.py +238 -0
  451. python_pptx2-2.13.0/tests/parts/test_slide.py +589 -0
  452. python_pptx2-2.13.0/tests/schema/__init__.py +1 -0
  453. python_pptx2-2.13.0/tests/schema/oxml_schema_validator.py +452 -0
  454. python_pptx2-2.13.0/tests/schema/test_schema_validity.py +821 -0
  455. python_pptx2-2.13.0/tests/shapes/__init__.py +0 -0
  456. python_pptx2-2.13.0/tests/shapes/test_anchor.py +297 -0
  457. python_pptx2-2.13.0/tests/shapes/test_animate.py +71 -0
  458. python_pptx2-2.13.0/tests/shapes/test_autoshape.py +606 -0
  459. python_pptx2-2.13.0/tests/shapes/test_base.py +643 -0
  460. python_pptx2-2.13.0/tests/shapes/test_connector.py +427 -0
  461. python_pptx2-2.13.0/tests/shapes/test_freeform.py +565 -0
  462. python_pptx2-2.13.0/tests/shapes/test_graphfrm.py +175 -0
  463. python_pptx2-2.13.0/tests/shapes/test_group.py +235 -0
  464. python_pptx2-2.13.0/tests/shapes/test_picture.py +329 -0
  465. python_pptx2-2.13.0/tests/shapes/test_placeholder.py +637 -0
  466. python_pptx2-2.13.0/tests/shapes/test_shapetree.py +2359 -0
  467. python_pptx2-2.13.0/tests/test_accessibility.py +279 -0
  468. python_pptx2-2.13.0/tests/test_action.py +373 -0
  469. python_pptx2-2.13.0/tests/test_animation.py +758 -0
  470. python_pptx2-2.13.0/tests/test_api.py +87 -0
  471. python_pptx2-2.13.0/tests/test_audit.py +105 -0
  472. python_pptx2-2.13.0/tests/test_compose_from_spec.py +855 -0
  473. python_pptx2-2.13.0/tests/test_diagrams.py +186 -0
  474. python_pptx2-2.13.0/tests/test_files/calibriz.ttf +0 -0
  475. python_pptx2-2.13.0/tests/test_files/cdw-logo.eps +420 -0
  476. python_pptx2-2.13.0/tests/test_files/dummy.mp4 +1 -0
  477. python_pptx2-2.13.0/tests/test_files/expanded_pptx/[Content_Types].xml +27 -0
  478. python_pptx2-2.13.0/tests/test_files/expanded_pptx/_rels/.rels +7 -0
  479. python_pptx2-2.13.0/tests/test_files/expanded_pptx/docProps/app.xml +41 -0
  480. python_pptx2-2.13.0/tests/test_files/expanded_pptx/docProps/core.xml +13 -0
  481. python_pptx2-2.13.0/tests/test_files/expanded_pptx/docProps/thumbnail.jpeg +0 -0
  482. python_pptx2-2.13.0/tests/test_files/expanded_pptx/ppt/_rels/presentation.xml.rels +10 -0
  483. python_pptx2-2.13.0/tests/test_files/expanded_pptx/ppt/presProps.xml +4 -0
  484. python_pptx2-2.13.0/tests/test_files/expanded_pptx/ppt/presentation.xml +109 -0
  485. python_pptx2-2.13.0/tests/test_files/expanded_pptx/ppt/printerSettings/printerSettings1.bin +0 -0
  486. python_pptx2-2.13.0/tests/test_files/expanded_pptx/ppt/slideLayouts/_rels/slideLayout1.xml.rels +4 -0
  487. python_pptx2-2.13.0/tests/test_files/expanded_pptx/ppt/slideLayouts/_rels/slideLayout10.xml.rels +4 -0
  488. python_pptx2-2.13.0/tests/test_files/expanded_pptx/ppt/slideLayouts/_rels/slideLayout11.xml.rels +4 -0
  489. python_pptx2-2.13.0/tests/test_files/expanded_pptx/ppt/slideLayouts/_rels/slideLayout2.xml.rels +4 -0
  490. python_pptx2-2.13.0/tests/test_files/expanded_pptx/ppt/slideLayouts/_rels/slideLayout3.xml.rels +4 -0
  491. python_pptx2-2.13.0/tests/test_files/expanded_pptx/ppt/slideLayouts/_rels/slideLayout4.xml.rels +4 -0
  492. python_pptx2-2.13.0/tests/test_files/expanded_pptx/ppt/slideLayouts/_rels/slideLayout5.xml.rels +4 -0
  493. python_pptx2-2.13.0/tests/test_files/expanded_pptx/ppt/slideLayouts/_rels/slideLayout6.xml.rels +4 -0
  494. python_pptx2-2.13.0/tests/test_files/expanded_pptx/ppt/slideLayouts/_rels/slideLayout7.xml.rels +4 -0
  495. python_pptx2-2.13.0/tests/test_files/expanded_pptx/ppt/slideLayouts/_rels/slideLayout8.xml.rels +4 -0
  496. python_pptx2-2.13.0/tests/test_files/expanded_pptx/ppt/slideLayouts/_rels/slideLayout9.xml.rels +4 -0
  497. python_pptx2-2.13.0/tests/test_files/expanded_pptx/ppt/slideLayouts/slideLayout1.xml +242 -0
  498. python_pptx2-2.13.0/tests/test_files/expanded_pptx/ppt/slideLayouts/slideLayout10.xml +165 -0
  499. python_pptx2-2.13.0/tests/test_files/expanded_pptx/ppt/slideLayouts/slideLayout11.xml +175 -0
  500. python_pptx2-2.13.0/tests/test_files/expanded_pptx/ppt/slideLayouts/slideLayout2.xml +170 -0
  501. python_pptx2-2.13.0/tests/test_files/expanded_pptx/ppt/slideLayouts/slideLayout3.xml +246 -0
  502. python_pptx2-2.13.0/tests/test_files/expanded_pptx/ppt/slideLayouts/slideLayout4.xml +288 -0
  503. python_pptx2-2.13.0/tests/test_files/expanded_pptx/ppt/slideLayouts/slideLayout5.xml +422 -0
  504. python_pptx2-2.13.0/tests/test_files/expanded_pptx/ppt/slideLayouts/slideLayout6.xml +118 -0
  505. python_pptx2-2.13.0/tests/test_files/expanded_pptx/ppt/slideLayouts/slideLayout7.xml +95 -0
  506. python_pptx2-2.13.0/tests/test_files/expanded_pptx/ppt/slideLayouts/slideLayout8.xml +277 -0
  507. python_pptx2-2.13.0/tests/test_files/expanded_pptx/ppt/slideLayouts/slideLayout9.xml +253 -0
  508. python_pptx2-2.13.0/tests/test_files/expanded_pptx/ppt/slideMasters/_rels/slideMaster1.xml.rels +15 -0
  509. python_pptx2-2.13.0/tests/test_files/expanded_pptx/ppt/slideMasters/slideMaster1.xml +507 -0
  510. python_pptx2-2.13.0/tests/test_files/expanded_pptx/ppt/slides/_rels/slide1.xml.rels +4 -0
  511. python_pptx2-2.13.0/tests/test_files/expanded_pptx/ppt/slides/slide1.xml +76 -0
  512. python_pptx2-2.13.0/tests/test_files/expanded_pptx/ppt/tableStyles.xml +2 -0
  513. python_pptx2-2.13.0/tests/test_files/expanded_pptx/ppt/theme/theme1.xml +281 -0
  514. python_pptx2-2.13.0/tests/test_files/expanded_pptx/ppt/viewProps.xml +34 -0
  515. python_pptx2-2.13.0/tests/test_files/minimal.pptx +0 -0
  516. python_pptx2-2.13.0/tests/test_files/minimal_slide.xml +15 -0
  517. python_pptx2-2.13.0/tests/test_files/missing_rels_item.pptx +0 -0
  518. python_pptx2-2.13.0/tests/test_files/monty-truth.png +0 -0
  519. python_pptx2-2.13.0/tests/test_files/no-core-props.pptx +0 -0
  520. python_pptx2-2.13.0/tests/test_files/no-slides.pptx +0 -0
  521. python_pptx2-2.13.0/tests/test_files/presentation.xml +109 -0
  522. python_pptx2-2.13.0/tests/test_files/python-icon.jpeg +0 -0
  523. python_pptx2-2.13.0/tests/test_files/python-powered.png +0 -0
  524. python_pptx2-2.13.0/tests/test_files/python.bmp +0 -0
  525. python_pptx2-2.13.0/tests/test_files/slideLayout1.xml +265 -0
  526. python_pptx2-2.13.0/tests/test_files/smart_art_org_chart.pptx +0 -0
  527. python_pptx2-2.13.0/tests/test_files/snippets/2x2-area-date.txt +166 -0
  528. python_pptx2-2.13.0/tests/test_files/snippets/2x2-area-float.txt +167 -0
  529. python_pptx2-2.13.0/tests/test_files/snippets/2x2-area-stacked-100.txt +165 -0
  530. python_pptx2-2.13.0/tests/test_files/snippets/2x2-area-stacked.txt +165 -0
  531. python_pptx2-2.13.0/tests/test_files/snippets/2x2-area.txt +165 -0
  532. python_pptx2-2.13.0/tests/test_files/snippets/2x2-bar-clustered-date.txt +145 -0
  533. python_pptx2-2.13.0/tests/test_files/snippets/2x2-bar-clustered-float.txt +145 -0
  534. python_pptx2-2.13.0/tests/test_files/snippets/2x2-bar-clustered.txt +143 -0
  535. python_pptx2-2.13.0/tests/test_files/snippets/2x2-bar-stacked-100.txt +144 -0
  536. python_pptx2-2.13.0/tests/test_files/snippets/2x2-bar-stacked.txt +144 -0
  537. python_pptx2-2.13.0/tests/test_files/snippets/2x2-column-clustered.txt +143 -0
  538. python_pptx2-2.13.0/tests/test_files/snippets/2x2-column-stacked-100.txt +144 -0
  539. python_pptx2-2.13.0/tests/test_files/snippets/2x2-column-stacked.txt +144 -0
  540. python_pptx2-2.13.0/tests/test_files/snippets/2x2-line-date.txt +162 -0
  541. python_pptx2-2.13.0/tests/test_files/snippets/2x2-line-float.txt +162 -0
  542. python_pptx2-2.13.0/tests/test_files/snippets/2x2-line-markers-stacked-100.txt +154 -0
  543. python_pptx2-2.13.0/tests/test_files/snippets/2x2-line-markers-stacked.txt +154 -0
  544. python_pptx2-2.13.0/tests/test_files/snippets/2x2-line-markers.txt +154 -0
  545. python_pptx2-2.13.0/tests/test_files/snippets/2x2-line-stacked-100.txt +160 -0
  546. python_pptx2-2.13.0/tests/test_files/snippets/2x2-line-stacked.txt +160 -0
  547. python_pptx2-2.13.0/tests/test_files/snippets/2x2-line.txt +160 -0
  548. python_pptx2-2.13.0/tests/test_files/snippets/2x3-bubble-3d.txt +215 -0
  549. python_pptx2-2.13.0/tests/test_files/snippets/2x3-bubble.txt +215 -0
  550. python_pptx2-2.13.0/tests/test_files/snippets/2x3-xy-lines-no-markers.txt +172 -0
  551. python_pptx2-2.13.0/tests/test_files/snippets/2x3-xy-lines.txt +166 -0
  552. python_pptx2-2.13.0/tests/test_files/snippets/2x3-xy-smooth-no-markers.txt +172 -0
  553. python_pptx2-2.13.0/tests/test_files/snippets/2x3-xy-smooth.txt +166 -0
  554. python_pptx2-2.13.0/tests/test_files/snippets/2x3-xy.txt +166 -0
  555. python_pptx2-2.13.0/tests/test_files/snippets/2x5-radar.txt +203 -0
  556. python_pptx2-2.13.0/tests/test_files/snippets/3x1-pie-exploded.txt +73 -0
  557. python_pptx2-2.13.0/tests/test_files/snippets/3x1-pie.txt +72 -0
  558. python_pptx2-2.13.0/tests/test_files/snippets/3x2-doughnut-exploded.txt +145 -0
  559. python_pptx2-2.13.0/tests/test_files/snippets/3x2-doughnut.txt +143 -0
  560. python_pptx2-2.13.0/tests/test_files/snippets/4x2-multi-cat-bar.txt +181 -0
  561. python_pptx2-2.13.0/tests/test_files/snippets/adjust-ser-count.txt +157 -0
  562. python_pptx2-2.13.0/tests/test_files/snippets/cat-labels.txt +146 -0
  563. python_pptx2-2.13.0/tests/test_files/snippets/content-types-xml.txt +9 -0
  564. python_pptx2-2.13.0/tests/test_files/snippets/default-notes.txt +22 -0
  565. python_pptx2-2.13.0/tests/test_files/snippets/default-notesMaster.txt +347 -0
  566. python_pptx2-2.13.0/tests/test_files/snippets/default-theme.txt +317 -0
  567. python_pptx2-2.13.0/tests/test_files/snippets/freeform.txt +45 -0
  568. python_pptx2-2.13.0/tests/test_files/snippets/package-rels-xml.txt +7 -0
  569. python_pptx2-2.13.0/tests/test_files/snippets/placeholders.txt +54 -0
  570. python_pptx2-2.13.0/tests/test_files/snippets/presentation-rels-xml.txt +6 -0
  571. python_pptx2-2.13.0/tests/test_files/snippets/relationships.txt +2 -0
  572. python_pptx2-2.13.0/tests/test_files/snippets/rels-load-from-xml.txt +6 -0
  573. python_pptx2-2.13.0/tests/test_files/snippets/rewrite-ser.txt +488 -0
  574. python_pptx2-2.13.0/tests/test_files/snippets/timing.txt +101 -0
  575. python_pptx2-2.13.0/tests/test_files/test.pptx +0 -0
  576. python_pptx2-2.13.0/tests/test_files/test_slides.pptx +0 -0
  577. python_pptx2-2.13.0/tests/test_formats.py +78 -0
  578. python_pptx2-2.13.0/tests/test_geometry.py +219 -0
  579. python_pptx2-2.13.0/tests/test_inherit.py +110 -0
  580. python_pptx2-2.13.0/tests/test_lint.py +2261 -0
  581. python_pptx2-2.13.0/tests/test_lint_on_save.py +121 -0
  582. python_pptx2-2.13.0/tests/test_math.py +151 -0
  583. python_pptx2-2.13.0/tests/test_media.py +134 -0
  584. python_pptx2-2.13.0/tests/test_new_features.py +345 -0
  585. python_pptx2-2.13.0/tests/test_package.py +377 -0
  586. python_pptx2-2.13.0/tests/test_powerpoint_strict_validation.py +257 -0
  587. python_pptx2-2.13.0/tests/test_presentation.py +311 -0
  588. python_pptx2-2.13.0/tests/test_render.py +411 -0
  589. python_pptx2-2.13.0/tests/test_render_slides_alias.py +34 -0
  590. python_pptx2-2.13.0/tests/test_section.py +287 -0
  591. python_pptx2-2.13.0/tests/test_shape_helpers.py +277 -0
  592. python_pptx2-2.13.0/tests/test_shared.py +83 -0
  593. python_pptx2-2.13.0/tests/test_skill.py +49 -0
  594. python_pptx2-2.13.0/tests/test_slide.py +1376 -0
  595. python_pptx2-2.13.0/tests/test_smart_art.py +145 -0
  596. python_pptx2-2.13.0/tests/test_svg_picture.py +180 -0
  597. python_pptx2-2.13.0/tests/test_table.py +1328 -0
  598. python_pptx2-2.13.0/tests/test_table_extras.py +321 -0
  599. python_pptx2-2.13.0/tests/test_theme.py +219 -0
  600. python_pptx2-2.13.0/tests/test_transition_morph.py +217 -0
  601. python_pptx2-2.13.0/tests/test_util.py +133 -0
  602. python_pptx2-2.13.0/tests/text/__init__.py +0 -0
  603. python_pptx2-2.13.0/tests/text/test_fonts.py +807 -0
  604. python_pptx2-2.13.0/tests/text/test_layout.py +293 -0
  605. python_pptx2-2.13.0/tests/text/test_text.py +1774 -0
  606. python_pptx2-2.13.0/tests/unitdata.py +120 -0
  607. python_pptx2-2.13.0/tests/unitutil/__init__.py +13 -0
  608. python_pptx2-2.13.0/tests/unitutil/cxml.py +283 -0
  609. python_pptx2-2.13.0/tests/unitutil/file.py +57 -0
  610. python_pptx2-2.13.0/tests/unitutil/mock.py +130 -0
  611. python_pptx2-2.13.0/tox.ini +9 -0
@@ -0,0 +1,2776 @@
1
+ .. :changelog:
2
+
3
+ Release History
4
+ ---------------
5
+
6
+ This project was forked from `scanny/python-pptx`_ at version 1.0.2.
7
+ Releases prior to 1.1.0 are upstream history, preserved here verbatim and
8
+ attributed to Steve Canny and the original contributors. Releases from
9
+ 1.1.0 through 2.12.x were published as ``power-pptx`` (import
10
+ ``power_pptx``) by Daniel Halwell. Starting with 2.13.0 this line is
11
+ published as ``python-pptx2`` on PyPI and imported as ``pptx2``, so it
12
+ can sit beside both ``python-pptx`` (``pptx``) and ``power-pptx``
13
+ (``power_pptx``).
14
+
15
+ .. _`scanny/python-pptx`: https://github.com/scanny/python-pptx
16
+
17
+
18
+ 2.13.0 (2026-08-26)
19
+ +++++++++++++++++++
20
+
21
+ First release under the ``python-pptx2`` / ``pptx2`` names — a fork of
22
+ ``power-pptx`` 2.12 with native PowerPoint equations from LaTeX. The
23
+ library still does not compile math itself — ``latex2mathml`` turns
24
+ the fragment into MathML and ``mathml2omml`` turns that into Office
25
+ Math, which we wrap in the ``a14:m`` marker PowerPoint actually stores.
26
+
27
+ Added
28
+ .....
29
+
30
+ * **``slide.shapes.add_equation(...)``.** Same bbox / ``(left, top,
31
+ width, height)`` calling convention as ``add_text``, plus
32
+ ``latex=r"\frac{a}{b}"``. Optional ``size_pt`` / ``color`` / ``font``
33
+ / ``align`` / ``anchor``. The equation is editable in PowerPoint's
34
+ equation editor.
35
+ * **``paragraph.add_math(latex)``.** Inline OMML between ordinary runs,
36
+ so a sentence can contain a formula without a second shape.
37
+ * **``pptx2.math.latex_to_omml``** for callers that only need the
38
+ ``<m:oMath>`` fragment. Missing converters raise
39
+ ``MathBackendUnavailable`` with the install line.
40
+
41
+ Install the converters with ``pip install "python-pptx2[math]"``.
42
+
43
+
44
+ 2.12.0 (2026-08-24)
45
+ +++++++++++++++++++
46
+
47
+ Completes Phase 2 of the roadmap — the space-awareness phase this fork
48
+ exists for. The linter could already tell you two shapes overlap; it had
49
+ no way to be told the overlap was the point. Both remaining Phase 2
50
+ items ship here.
51
+
52
+ Added
53
+ .....
54
+
55
+ * **Declaring intentional overlaps.** ``ShapeCollision`` is the noisiest
56
+ rule, because deliberate layering looks exactly like a copy-paste bug
57
+ from a bounding box alone. Until now the only way to say "I meant
58
+ that" was ``shape.lint_group``, which is n-ary and symmetric — every
59
+ shape sharing the tag may overlap every other. Two narrower forms join
60
+ it:
61
+
62
+ * ``shape_a.allow_overlap_with(shape_b)`` licenses exactly one pair
63
+ and leaves every other overlap policed. It is written one-sided but
64
+ read symmetrically: either shape vouching for the pair is enough.
65
+ Paired with ``disallow_overlap_with()`` and an
66
+ ``overlap_allowances`` property (a ``frozenset`` of shape ids).
67
+ * ``shape.layer`` / ``shape.layer_above`` declare a stratum of the
68
+ design and, uniquely, assert a *direction*. A shape declaring
69
+ ``layer_above = "card"`` claims to be painted on top of every
70
+ overlapping shape whose ``layer`` is ``"card"``.
71
+
72
+ Both round-trip through save/open in the shape's ``cNvPr/extLst``, the
73
+ OOXML-sanctioned extension point, alongside the existing ``lint_group``
74
+ and ``lint_skip``.
75
+
76
+ * **Free-standing shapes in a ``from_spec`` spec.** A slide entry may
77
+ now carry a ``shapes`` list — ``left`` / ``top`` / ``width`` /
78
+ ``height`` (inches or ``Length``), plus ``name``, ``shape`` (an
79
+ ``MSO_SHAPE`` member name) and ``text`` — applied after the layout
80
+ runs. Deliberately minimal: geometry, type, text, intent, not a
81
+ drawing DSL.
82
+
83
+ * **Overlap intent is declarable in a spec.** Shape entries accept
84
+ ``lint_group``, ``layer``, ``layer_above`` and ``allow_overlap_with``,
85
+ so a generator can state at authoring time that an overlap is
86
+ deliberate and the built deck lints clean without a manual pass.
87
+ ``allow_overlap_with`` names other shapes by their spec ``name`` —
88
+ shape ids don't exist until the deck is built — and is resolved once
89
+ every shape on the slide exists, so forward references work. Names
90
+ must be unique within a slide and references may not cross slides,
91
+ since an allowance is keyed on a shape id and ids are only unique per
92
+ slide; both raise a ``ValueError`` locating the entry.
93
+
94
+ * **``LayerOrderViolation``** — a new ERROR-severity lint issue, and the
95
+ reason layer hints are more than a third way to silence a warning.
96
+ When a shape's ``layer_above`` declaration is contradicted by the
97
+ drawing order — it claims to be on top but comes earlier in ``spTree``
98
+ and is painted underneath — the declaration is taken as what the
99
+ author meant and the z-order is reported as the bug. Non-overlapping
100
+ pairs are inert, not wrong.
101
+
102
+ * ``LayerOrderViolation`` is **auto-fixable**: ``report.auto_fix()``
103
+ restacks the declaring shape to sit immediately after the layer it
104
+ named. Unlike the collision rules this needs no designer judgment —
105
+ the author already stated which shape belongs on top, and the fix only
106
+ makes the drawing order agree. Geometry is never touched.
107
+ ``slide.tidy()`` picks it up via a new ``fix_layer_order=True``
108
+ keyword.
109
+
110
+ Fixed
111
+ .....
112
+
113
+ * PyPI project links. ``Documentation`` pointed at a Read the Docs site
114
+ this project does not publish to; it now points at the GitHub Pages
115
+ site the README and badges already used. ``Changelog`` and ``Roadmap``
116
+ pointed at ``/blob/main/`` paths, but the default branch is ``master``
117
+ and no ``main`` branch exists, so both returned 404. All eight project
118
+ URLs now resolve.
119
+
120
+ * Deleting a shape left overlap allowances pointing at its id. Ids are
121
+ recycled — the allocator hands out ``max(existing) + 1`` — so deleting
122
+ the highest-id shape freed its id for the next shape added after a
123
+ save/reopen, and the stale allowance then matched that unrelated
124
+ newcomer and silently suppressed a real ``ShapeCollision``.
125
+ ``shape.delete()`` now purges allowances naming it, alongside the
126
+ animation-timing cleanup it already did — including every id nested
127
+ inside a deleted group, since removing a group's element removes its
128
+ members with it and an allowance may legitimately name one.
129
+
130
+ * ``shape.allow_overlap_with(...)`` accepted a shape from another slide.
131
+ Allowances are keyed on ``cNvPr/@id``, which is unique only *within* a
132
+ slide, so a borrowed id either collided with the source shape's own id
133
+ — surfacing as a bogus "cannot be given an overlap allowance with
134
+ itself" error — or silently matched an unrelated shape on this slide
135
+ and suppressed a collision that was real. Both the grant and the
136
+ revoke now reject a cross-slide target with a ``ValueError`` that says
137
+ why, matching the guard the spec-based reference path already had.
138
+
139
+ * Setting a single dimension on a placeholder that was still inheriting
140
+ its geometry silently zeroed the other one. ``left``/``top`` share an
141
+ ``<a:off>`` element and ``width``/``height`` share an ``<a:ext>``, so
142
+ writing one materialised the element with its sibling defaulted to
143
+ ``0`` — ``body.width = Inches(4)`` collapsed the height from 4.95in to
144
+ nothing and made the shape invisible, with no warning. The inherited
145
+ sibling is now copied across first, so a single-dimension assignment
146
+ means what it reads as: change this one, leave the other where the
147
+ layout put it. An explicit zero is still honoured.
148
+
149
+ * ``Slide.follow_master_background``'s docstring described assigning
150
+ |True| / |False| to it, but the property has no setter and any
151
+ assignment raises ``AttributeError``. It now documents the real
152
+ mechanism: inheritance breaks as a side-effect of giving the slide its
153
+ own ``background``.
154
+
155
+ * Writing any lint marker other than ``lint_group`` — a ``lint_skip``
156
+ set, and now a layer name or an overlap allowance — silently discarded
157
+ a ``lint_group`` stored in the pre-2.1.1 attribute format, including on
158
+ paths documented as no-ops. The legacy value is now migrated into the
159
+ canonical ``<pp:lintGroup>`` node instead of being dropped.
160
+
161
+ * ``SlideLintReport.auto_fix(dry_run=True)`` could report more layer
162
+ restacks than a real run would apply, because the one-move-per-shape
163
+ guard was only populated outside dry-run mode. A preview now lists
164
+ exactly the fixes the real run performs.
165
+
166
+ Added
167
+ .....
168
+
169
+ * ``prs.lint_on_save`` — a save-time validation hook. ``"off"`` (the
170
+ default, so existing code is unaffected and pays nothing) skips all
171
+ checks; ``"warn"`` lints every slide and logs each error-severity issue
172
+ on the ``power_pptx.presentation`` logger before writing the file;
173
+ ``"raise"`` lints before anything is written and raises
174
+ ``power_pptx.exc.LintError`` naming the offending slide indexes, so a
175
+ failing deck never reaches disk. Only error-severity issues trigger it,
176
+ matching the ``lint`` option of ``power_pptx.compose.from_spec``. Any
177
+ other value raises ``ValueError``. The setting is not persisted into the
178
+ ``.pptx`` file.
179
+
180
+ 2.11.0 (2026-08-21)
181
+ +++++++++++++++++++
182
+
183
+ An ergonomics release from dogfooding the library on a real nine-slide
184
+ deck: the shadow that would not turn off, corner radius spelled as a
185
+ fraction, column arithmetic written out by hand, table styling that
186
+ dropped back to raw python-pptx, and a text fit that quietly guessed
187
+ when the brand font was missing.
188
+
189
+ Added
190
+ .....
191
+
192
+ * Documentation site under ``site/`` — Astro + TypeScript + React with
193
+ shadcn-style UI primitives and MUI islands, deployed to GitHub Pages
194
+ via ``.github/workflows/pages.yml``. Covers the starter guide,
195
+ advanced usage, coding-agent (skill) usage, and the full API surface,
196
+ with a light-by-default theme and a persisted dark-mode toggle.
197
+ * ``README.rst`` gained a header block with PyPI / Python-version / CI /
198
+ license / docs badges and a library-information table linking the new
199
+ documentation pages.
200
+ * ``shape.shadow.clear()`` — one call that guarantees a shape renders with
201
+ no shadow. It drops every explicit shadow element *and* re-points the
202
+ shape's ``<a:effectRef>`` at the theme's empty effect slot, which is what
203
+ clearing the individual properties never did: auto shapes ship with
204
+ ``<a:effectRef idx="2"/>``, a soft drop shadow in most themes, so a
205
+ "cleared" card kept a phantom shadow. Non-shadow effects (glow, soft
206
+ edges, blur, reflection) are preserved, and a shape whose effects are
207
+ an ``<a:effectDag>`` has its shadow nodes pruned from that tree rather
208
+ than gaining a schema-invalid sibling ``<a:effectLst>``.
209
+ * ``shape.corner_radius`` on rounded-rectangle auto shapes — read/write in
210
+ length units (``card.corner_radius = Pt(6)``) instead of the raw
211
+ ``adjustments[0]`` fraction-of-the-shorter-side, so corner radius can be
212
+ specified the way it is designed. Raises rather than silently clipping
213
+ when the radius exceeds half the shorter side.
214
+ * ``BBox.columns(n, gap=...)`` / ``BBox.rows(n, gap=...)`` — the n-up
215
+ shorthand for the ``(available - (n - 1) * gap) / n`` arithmetic every
216
+ card row and stat grid would otherwise hand-roll. Widths partition the
217
+ box exactly, with no rounding drift on the last column.
218
+ * ``Grid.from_box(box, cols=..., rows=...)`` — a ``design.layout.Grid``
219
+ over an arbitrary region (a ``BBox``, ``Box``, or 4-tuple) rather than
220
+ the whole slide, so a panel can carry its own grid with no slide
221
+ reference.
222
+ * ``cell.format(...)`` and ``table.format_cells(rows=..., cols=..., ...)``
223
+ — fill plus text styling for table cells in the same keyword vocabulary
224
+ as ``shapes.add_text`` (``fill``, ``color``, ``bold``, ``size_pt``,
225
+ ``align``, ``anchor``, ``margin``), replacing loops over
226
+ ``cell.fill.fore_color.rgb``. Selections accept an int, a slice, an
227
+ iterable of indices, or ``None`` for all; the anchor and insets are
228
+ written to ``<a:tcPr>`` where PowerPoint actually reads them, and the
229
+ text styling is recorded in the cell's ``<a:lstStyle>`` text-body
230
+ defaults as well as on its runs, so styling a header row *before*
231
+ populating it survives the later ``cell.text`` assignment.
232
+ * ``power_pptx.text.fonts.find_font_file()``, ``font_is_installed()``, and
233
+ ``installed_font_families()`` — check up front whether a build
234
+ environment can measure a font, instead of discovering it from a
235
+ render.
236
+ * ``TextFrame.fit_text()`` now returns the point size it applied, warns
237
+ with the new ``power_pptx.exc.FontMetricsWarning`` when a *named* font
238
+ family isn't installed and measurement silently falls back to Pillow's
239
+ default metrics, and accepts ``strict=True`` to raise instead. The
240
+ space-aware guarantee is only as good as the metrics behind it; this
241
+ makes the degradation visible rather than silent. ``font_family`` now
242
+ defaults to ``None`` (meaning ``"Calibri"``) so an omitted argument is
243
+ told apart from an explicit ``"Calibri"`` — only the latter is a
244
+ request that a fallback breaks.
245
+
246
+ Changed
247
+ .......
248
+
249
+ * ``shadow.inherit``'s deprecation warning now names ``shadow.clear()``
250
+ and says plainly that ``inherit = False`` writes only an empty
251
+ ``<a:effectLst/>`` and leaves an inherited theme shadow rendering. The
252
+ property's XML behaviour is unchanged and stays symmetric — ``False``
253
+ then ``True`` returns the shape to where it started, which delegating
254
+ to ``clear()`` could not do, since the original ``effectRef`` index
255
+ isn't recoverable once overwritten.
256
+
257
+
258
+ 2.10.0 (2026-07-12)
259
+ +++++++++++++++++++
260
+
261
+ Maintenance release centred on a full-library OOXML-conformance
262
+ review: every emitted-XML subsystem was cross-checked against the
263
+ ISO/IEC 29500 schemas, [MS-PPTX], and Microsoft's published
264
+ references, hunting the "PowerPoint reports the file as broken /
265
+ silently repairs it" class of bug. Together with the tail of the
266
+ 2.9.0 review cycle, this fixes seventeen repair triggers plus a batch
267
+ of crashes and silent-misbehaviour bugs; every fix ships with
268
+ regression tests, and the schema harness gained new structural checks
269
+ so the classes stay fixed. No new public API.
270
+
271
+ Fixed
272
+ ~~~~~
273
+
274
+ - **Table border helpers crashed on a hex-string colour.**
275
+ ``cell.borders.all(color="1F4E79")`` (and ``outer`` / ``diagonal`` /
276
+ the ``row.borders`` / ``col.borders`` group helpers) raised
277
+ ``TypeError`` because the colour was pre-wrapped as
278
+ ``RGBColor(*color)``, which splat the six-character hex string into six
279
+ positional arguments. Hex strings, ``(r, g, b)`` tuples, and
280
+ ``RGBColor`` now all work, matching the colour convention used
281
+ everywhere else in the library.
282
+
283
+ - **Embedding a font produced an invalid presentation.xml.**
284
+ ``theme.embed_font(...)`` appended ``<p:embeddedFontLst>`` to the end of
285
+ ``presentation.xml``, but the ``CT_Presentation`` sequence requires it
286
+ *before* ``defaultTextStyle`` — an element every default template
287
+ already carries. The out-of-order element made PowerPoint report the
288
+ deck as broken. The list is now inserted in its schema-mandated
289
+ position (a proper ``embeddedFontLst`` child definition on
290
+ ``CT_Presentation`` with the correct successors).
291
+
292
+ - **Two OLE objects on one slide produced a duplicate shape id.** The
293
+ inner "show-as-icon" ``<p:pic>`` of every embedded OLE object
294
+ (``shapes.add_ole_object(...)``) was emitted with a hardcoded
295
+ ``id="0"``, so a slide carrying more than one OLE object contained two
296
+ shapes sharing that id — a non-unique shape id that makes PowerPoint
297
+ report the deck as needing repair. Each inner pic now receives its own
298
+ uniquely-allocated shape id.
299
+
300
+ - **3-D shapes were rejected by Microsoft PowerPoint when a bevel or
301
+ material was turned off via the enum.** ``shape.three_d.bevel_top.preset
302
+ = BevelPreset.NONE`` and ``shape.three_d.preset_material =
303
+ PresetMaterial.NONE`` — the natural way to say "no bevel" / "no
304
+ material" — emitted the token ``"none"``, which is absent from the
305
+ ISO-29500 ``ST_BevelPresetType`` / ``ST_PresetMaterialType``
306
+ enumerations. The file opened in python-pptx and LibreOffice but
307
+ PowerPoint reported it as broken and offered to repair it. Assigning
308
+ ``BevelPreset.NONE`` now removes the ``<a:bevelT>`` / ``<a:bevelB>``
309
+ element (the schema-valid way to express "no bevel"), and
310
+ ``PresetMaterial.NONE`` clears the ``prstMaterial`` attribute; both
311
+ read back as ``None``. Passing Python ``None`` keeps its prior meaning
312
+ of clearing only the preset while preserving any bevel dimensions. Use
313
+ ``PresetMaterial.FLAT`` for an explicit flat surface.
314
+
315
+ - **Removing the last animation left schema-invalid timing XML.**
316
+ ``entry.remove()``, ``animations.clear()``, and ``purge_orphans()``
317
+ removed the click-group ``<p:par>`` entries but left an empty
318
+ ``<p:childTnLst>`` behind — invalid per ``CT_TimeNodeList`` (at least
319
+ one time-node child is required) and a PowerPoint repair trigger.
320
+ Whichever removal takes out the last entry now prunes the whole
321
+ ``p:timing`` subtree; a timing tree still carrying content (remaining
322
+ effects, or a movie's ``p:video`` play-controls node) is untouched.
323
+
324
+ - **Sections stayed out of sync with the deck.** Two fixes to the
325
+ PowerPoint-2010 section extension: an empty section
326
+ (``prs.sections.add("Name")``) omitted its ``<p14:sldIdLst>`` child,
327
+ which MS-PPTX 2.5.17 makes required (PowerPoint writes it even when
328
+ empty); and ``slides.add_slide(...)`` on a sectioned deck left the new
329
+ slide belonging to no section. New slides now join the final section,
330
+ keeping the section list the complete partition of the deck that
331
+ PowerPoint itself always writes.
332
+
333
+ - **Data-label ``collision_strategy`` emitted out-of-order chart XML.**
334
+ ``"compact"`` (and a firing ``"auto"``) created ``<c:gapWidth>`` by
335
+ bare append, landing it *after* ``<c:axId>`` — out of sequence in
336
+ ``CT_BarChart`` and a repair trigger. The element is now inserted in
337
+ its schema position (before ``overlap``/``serLines``/``axId``).
338
+
339
+ - **Out-of-range trendline parameters produced invalid chart XML.**
340
+ ``trendlines.add("poly", order=10)`` / ``add("movingAvg", period=1)``
341
+ wrote values outside the schema ranges (``ST_Order`` 2–6,
342
+ ``ST_Period`` ≥ 2) verbatim; both now raise ``ValueError`` at the API
343
+ boundary like the library's other range-checked chart attributes.
344
+
345
+ - **``chart.apply_dark_theme()`` / ``chart.text_color`` crashed on
346
+ scatter charts.** ``CT_ScatterChart`` lacked the ``dLbls`` accessor its
347
+ sibling plot classes define, so the data-label sweep raised
348
+ ``AttributeError`` (``plot.has_data_labels`` on a scatter plot crashed
349
+ on both read and write). The accessor is now defined in its correct
350
+ ``CT_ScatterChart`` sequence position.
351
+
352
+ - **A hidden secondary value axis multiplied on re-access.**
353
+ ``axis.visible = False`` writes a bare ``<c:delete/>`` (the ``val``
354
+ attribute is dropped as the schema default), which made the
355
+ secondary-axis detection stop matching — each later
356
+ ``chart.secondary_value_axis`` access piled a fresh axis pair onto the
357
+ plot area. Detection no longer filters on delete-state.
358
+
359
+ - **Palettes skipped radar-chart strokes.** ``apply_palette`` /
360
+ ``recolour`` on ``RADAR`` / ``RADAR_MARKERS`` charts wrote only the
361
+ (invisible) shape fill; radar series now get their line stroke
362
+ painted like the other line-rendered chart types.
363
+
364
+ - **``fill.gradient_angle`` crashed on the library's own default
365
+ gradient.** The default ``<a:lin scaled="0"/>`` (and a
366
+ radial→linear ``change_to_kind`` switch) carries no ``ang``
367
+ attribute, and the read raised ``TypeError`` on ``360.0 - None``. An
368
+ ``a:lin`` without ``ang`` now reads as the effective angle ``0.0``.
369
+
370
+ - **``shape.three_d`` raised a bare ``AttributeError`` on groups and
371
+ graphic frames.** Both now raise an explanatory
372
+ ``NotImplementedError`` instead, mirroring the sibling effect facades
373
+ (a group's ``grpSpPr`` legally carries ``scene3d`` but not ``sp3d``,
374
+ so the facade cannot target it without emitting invalid XML).
375
+
376
+ - **Cross-deck ``import_slide`` / ``apply_template`` produced structurally
377
+ broken packages in several master-cloning paths.** Fixed as a group:
378
+
379
+ * A cloned slide master kept the *source's* ``p:sldLayoutIdLst`` rIds
380
+ while its new relationships put the theme on ``rId1`` — the first
381
+ "layout" entry resolved to the theme part, the rest were off by
382
+ one, and the last layout was unlisted. The id list is now rebuilt
383
+ entry-by-entry as layouts are cloned, with fresh unique ids
384
+ allocated across the whole presentation (duplicate
385
+ ``sldMasterId``/``sldLayoutId`` ids are themselves a repair
386
+ trigger).
387
+ * Cloned masters and layouts lost their own image dependencies
388
+ (template logos, backgrounds), leaving dangling ``r:embed``
389
+ references. Their dependency graphs are now copied and remapped
390
+ like the slide's.
391
+ * A layout cloned into an existing (deduped) master was related but
392
+ never registered in the master's ``p:sldLayoutIdLst``, leaving it
393
+ invisible to PowerPoint's layout picker.
394
+ * A copied notes slide's back-reference to its slide cloned the
395
+ entire slide graph a second time — an orphan slide part the notes
396
+ slide pointed at instead of the registered slide.
397
+ * The copied notes slide dropped its ``notesSlide→notesMaster``
398
+ relationship entirely (and the destination got no notes master);
399
+ it is now re-linked to the destination's own notes master, created
400
+ from the default template when absent.
401
+ * The next-slide-partname allocator counted ``p:sldIdLst`` entries
402
+ only, so ``add_slide()`` after an import could write a second,
403
+ different part under an existing partname — a duplicate zip member
404
+ name, which OPC forbids. It now scans the package's actual
405
+ partnames.
406
+
407
+ - **Latent oxml attribute types corrected against the XSD.**
408
+ ``a:reflection@kx/@ky`` are now ``ST_FixedAngle`` (exclusive ±90°,
409
+ no silent modulo-360 normalization), ``a:outerShdw@dir`` is
410
+ ``ST_PositiveFixedAngle`` (matching ``a:innerShdw``), and
411
+ ``a:lum@bright/@contrast`` are ``ST_FixedPercentage`` (±100%). Also,
412
+ resetting picture ``brightness``/``contrast`` to ``0.0`` no longer
413
+ strands a dead empty ``<a:lum/>`` on the blip.
414
+
415
+ - **Morph transitions were emitted in the wrong namespace.** MS-PPTX
416
+ defines ``morph`` in the PowerPoint-2016 namespace
417
+ (``…/powerpoint/2015/09/main``, prefix ``p159``), not the 2010
418
+ ``p14`` namespace this library used — and because every modern
419
+ PowerPoint understands ``p14``, MCE selected that branch and hit an
420
+ undefined element: the repair dialog, on the headline Morph feature.
421
+ Morph now serializes as ``<p159:morph>`` inside an ``mc:Choice
422
+ Requires="p159"``; round-tripping a PowerPoint-authored morph deck
423
+ no longer writes the p159 kind bare into ``p:sld`` (a hard
424
+ ``CT_SlideTransition`` violation) or into the ISO-pure fallback;
425
+ extension-kind fallbacks carry ``<p:fade/>`` (PowerPoint's own
426
+ downgrade) instead of rendering as a cut; and legacy decks written
427
+ with ``p14:morph`` are healed on re-save.
428
+
429
+ - **Motion paths could contain exponent-notation coordinates.**
430
+ ``%g`` formatting wrote ``1.5e-17``-style float noise into
431
+ ``animMotion`` paths (``MotionPath.spiral`` on essentially every
432
+ call); the path grammar has no exponent form, so PowerPoint dropped
433
+ the animation. Coordinates are now fixed-point with noise clamped
434
+ to ``0``.
435
+
436
+ - **Section list integrity, round two.** ``Sections.remove()`` /
437
+ ``Section.delete()`` now merge the removed section's slides into the
438
+ neighbouring section (PowerPoint's own "Remove Section" behaviour)
439
+ instead of orphaning them, and removing the only section drops the
440
+ whole extension block rather than leaving an empty
441
+ ``<p14:sectionLst/>`` (itself invalid — ``minOccurs=1``).
442
+ ``Section.add_slide()`` moves a slide between sections instead of
443
+ letting two sections reference it. A first section created with
444
+ ``start_slide_index > 0`` now gets an auto-created "Default Section"
445
+ covering the preceding slides. ``import_slide`` onto a sectioned
446
+ deck adds the imported slide to the final section. Section ids are
447
+ validated as unique brace-wrapped GUIDs. And reading
448
+ ``prs.sections`` no longer injects the PowerPoint-2010 extension
449
+ block into a section-less deck.
450
+
451
+ - **Embedded fonts survived exactly one save.** ``embed_font`` never
452
+ set ``embedTrueTypeFonts="1"`` on ``p:presentation``, so PowerPoint
453
+ considered embedding disabled and silently stripped the ``fntdata``
454
+ parts and font list the next time a user saved the deck in
455
+ PowerPoint. Also hardened the ``ThemeFonts`` recovery path for
456
+ themes missing a required font collection (it previously emitted a
457
+ ``latin``-only collection appended out of order — itself
458
+ repair-trigger XML).
459
+
460
+ - **~30 of the built-in table-style GUIDs were wrong.** Entries in
461
+ ``TABLE_STYLES`` were fabricated or mapped to a different style than
462
+ their name claimed — ``"Dark Style 1"`` selected *No Style, No
463
+ Grid*, ``"Medium Style 2"`` selected *No Style, Table Grid*, the
464
+ Medium 3 / Dark 1 / Dark 2 / Light 3 accent rows were shuffled or
465
+ invented, and ``name_for_guid`` mislabelled genuine PowerPoint
466
+ decks. A GUID outside the built-in set is schema-valid but
467
+ PowerPoint silently applies **no styling at all**. Every entry is
468
+ now verbatim from Microsoft's published list (hh273476); the
469
+ fabricated ``"Themed Style N - No Color"`` aliases are removed.
470
+
471
+ The ``tests/schema`` harness that guards this class of bug also grew
472
+ five PowerPoint-specific structural checks the XSDs cannot express —
473
+ duplicate shape ids within a slide, dangling relationship references
474
+ (``r:id`` / ``r:embed`` / ``r:link`` targets with no matching
475
+ relationship, and relationships pointing at missing parts), package
476
+ parts absent from ``[Content_Types].xml``, duplicate zip member names
477
+ (two payloads under one OPC part name), and slide parts absent from
478
+ ``p:sldIdLst`` (orphans left by a botched copy) — each a real
479
+ "PowerPoint repairs the file" trigger, each with a self-test proving
480
+ it detects its target.
481
+
482
+
483
+ 2.9.0 (2026-06-25)
484
+ ++++++++++++++++++
485
+
486
+ Minor release widening the public surface across many subsystems: the
487
+ **group-shape API** (fill, move, recursive traversal, shrink-wrap, and a
488
+ real ``ungroup()``), **run-level text effects and typography**, **shape
489
+ inner/preset shadows**, **chart plot/axis toggles plus series
490
+ trendlines, error bars, and a secondary value axis**, **table cell text
491
+ direction and the built-in table-style gallery**, **international &
492
+ multi-column text layout**, **theme dark-mode and palette-from-seed**,
493
+ a **Sections API**, **slide reorder/move and first-class notes**,
494
+ **accessibility (alt text + audit)**, and **machine-readable lint/audit
495
+ output with SARIF export and baseline diff**, plus a couple of
496
+ fail-closed ``from_spec`` ergonomics fixes. All additions are drop-in
497
+ compatible.
498
+
499
+ Added
500
+ ~~~~~
501
+
502
+ - ``group.fill`` — a |FillFormat| on the group's ``p:grpSpPr``, so a
503
+ whole group can be tinted in one call (``group.fill.solid();
504
+ group.fill.fore_color.rgb = "1F4E79"``). Member shapes that declare
505
+ their own fill paint on top, unaffected. Note the OOXML schema admits
506
+ a fill but *not* a line (``a:ln``) on a group, so there is
507
+ deliberately no ``group.line`` — emitting one would produce a file
508
+ PowerPoint reports as broken.
509
+
510
+ - ``group.move(dx, dy)`` — translate an entire group (and every shape
511
+ it contains) by an offset in one O(1) operation, instead of walking
512
+ and nudging each child.
513
+
514
+ - ``group.walk()`` — generate every descendant shape depth-first,
515
+ recursing into nested groups, so whole-tree layout, measurement, and
516
+ lint passes no longer need hand-rolled recursion.
517
+
518
+ - ``group.fit_to_children()`` — shrink-wrap the group's offset/extent to
519
+ tightly bound its members (the same recalculation that runs when a
520
+ shape is added through ``group.shapes.add_*``), keeping ``group.bbox``
521
+ accurate after member shapes are edited directly.
522
+
523
+ - ``group.ungroup()`` — dissolve a group, promoting its members to the
524
+ parent shape tree with their position and size transformed out of the
525
+ group's child coordinate space so nothing moves or resizes visually.
526
+ Z-order is preserved and the promoted shapes are returned. Raises
527
+ ``ValueError`` for a rotated or flipped group, where baking the
528
+ transform into each child is ambiguous.
529
+
530
+ - **``add_table`` / ``add_movie`` on group shape-trees.** Both lived
531
+ only on the slide shape-tree even though ``p:graphicFrame`` /
532
+ ``p:pic`` are schema-valid inside a ``p:grpSp``. They now sit on the
533
+ shared base, so ``group.shapes.add_table(...)`` /
534
+ ``group.shapes.add_movie(...)`` work — a table or video can be bundled
535
+ into a group with a caption or badge, and the group's extent
536
+ shrink-wraps to include it. Video play-controls timing is still
537
+ registered on the enclosing slide. Slide-level usage is unchanged.
538
+
539
+ - **Machine-readable lint / audit output.** ``SlideLintReport.to_dict()``
540
+ / ``.to_json()`` and ``AuditReport.to_dict()`` / ``.to_json()`` return
541
+ a self-describing payload — each issue carries its ``code``,
542
+ ``severity``, ``message``, the names of the shapes involved, and every
543
+ detector-specific field (``TextOverflow.ratio``, ``OffSlide.side``,
544
+ the ``ShapeCollision`` scoring, …) picked up automatically from the
545
+ issue dataclass. This is the counterpart to the existing human-readable
546
+ ``summary()`` / ``markdown()`` and is built for the agent loop that
547
+ generates a deck, audits it, and feeds the result back to a model (or a
548
+ CI gate) to decide what to fix, e.g. ``if audit(prs).to_dict()
549
+ ["has_errors"]: ...``.
550
+
551
+ - **Run-property typography on ``Font``.** New tri-state properties on
552
+ ``run.font`` / ``paragraph.font`` close the ``<a:rPr>`` gap that
553
+ separates "looks branded" from "looks generated":
554
+ ``all_caps`` / ``small_caps`` (the ``cap`` attribute — mutually
555
+ exclusive), ``letter_spacing`` (tracking, a ``Length`` via the ``spc``
556
+ attribute, negative tightens), ``strikethrough`` (``strike``), and
557
+ ``superscript`` / ``subscript`` (the shared ``baseline`` shift). Each
558
+ reads back ``None`` when inherited and round-trips / schema-validates.
559
+ Backed by new ``ST_TextPoint`` / ``ST_TextCapsType`` /
560
+ ``ST_TextStrikeType`` simple types wired onto
561
+ ``CT_TextCharacterProperties``.
562
+
563
+ - **"Did you mean …?" for typo'd spec keys.** ``from_spec`` /
564
+ ``from_yaml`` already fail closed on unknown keys; the error now names
565
+ the closest valid candidate (via ``difflib``) for unknown top-level
566
+ keys, unknown recipe kwargs, unknown ``transition`` names, and unknown
567
+ ``slide_size`` shorthands — e.g. ``Unknown spec keys: 'slidez' (did
568
+ you mean 'slides'?)``. Makes a spec typo recoverable in a single
569
+ follow-up, which matters most for an LLM authoring the spec.
570
+
571
+ - **Run-level text effects on ``Font``.** ``run.font.outline`` returns a
572
+ |LineFormat| over the run's text-outline stroke (``<a:ln>`` of
573
+ ``<a:rPr>``), giving glyphs a coloured outline of a chosen width.
574
+ ``run.font.shadow`` / ``run.font.glow`` return |ShadowFormat| /
575
+ |GlowFormat| over the run's ``<a:effectLst>``. All three read
576
+ non-mutatingly (no XML until assigned), preserving theme inheritance.
577
+
578
+ - **Inner- and preset-shadow effects on shapes.** ``shape.inner_shadow``
579
+ (``InnerShadowFormat`` over ``<a:innerShdw>``) and
580
+ ``shape.preset_shadow`` (``PresetShadowFormat`` over ``<a:prstShdw>``)
581
+ join ``shape.shadow`` as first-class effect proxies. ``inner_shadow``
582
+ exposes ``blur_radius`` / ``distance`` / ``direction`` / ``color``;
583
+ ``preset_shadow`` exposes ``preset`` (an ``MSO_PRESET_SHADOW`` member
584
+ or ``"shdw1".."shdw20"`` string) plus geometry and colour. New
585
+ ``MSO_PRESET_SHADOW`` enum. The required ``prst`` and a colour child
586
+ are always emitted, so output stays schema-valid.
587
+
588
+ - **Chart "go to Excel for this" toggles.** ``DoughnutPlot.hole_size``
589
+ (int 10–90), ``LinePlot.smooth`` (bool, smooths every series), and
590
+ ``ValueAxis.log_base`` (float 2–1000, or ``None`` for a linear axis).
591
+ ``smooth`` is exposed only where the series element permits it, so it
592
+ doesn't re-introduce the radar-chart ``<c:smooth>`` schema bug.
593
+
594
+ - **Table cell text direction.** ``cell.text_direction`` rotates or
595
+ stacks cell text via friendly strings (``"horizontal"``,
596
+ ``"rotate90"``, ``"rotate270"``, ``"stacked"``) over the
597
+ ``<a:tcPr vert="…">`` attribute — what matrix / rotated column
598
+ headers need — paired with the existing ``cell.vertical_anchor``
599
+ alias.
600
+
601
+ - **Chart series analytics.** ``series.trendlines.add(kind, …)`` adds
602
+ ``<c:trendline>`` curves (linear, exponential, logarithmic,
603
+ moving-average, polynomial, power) with optional equation / R²
604
+ display, polynomial order, moving-average period, and forward/backward
605
+ projection. ``series.error_bars`` offers Excel-style constructors
606
+ ``fixed()`` / ``percentage()`` / ``standard_deviation()`` /
607
+ ``standard_error()`` / ``custom(plus, minus)``. ``chart.secondary_value_axis``
608
+ (and ``series.axis_group = "secondary"``) adds a second value axis and
609
+ moves a plot onto it — new axis ids stay in the signed-int32 range so
610
+ PowerPoint never flags the file for repair. Analytics are exposed only
611
+ on series types whose schema permits them (bar/line/scatter/area/bubble,
612
+ not pie/radar).
613
+
614
+ - **Table-style gallery.** ``table.style`` applies one of PowerPoint's
615
+ built-in table styles by friendly name (``"Medium Style 2 - Accent 1"``,
616
+ ``"Table Grid"``, ``"No Style, No Grid"``, …) or by raw ``{GUID}``.
617
+ Reading returns the friendly name for known built-ins, else the GUID,
618
+ else |None|; ``table.style = None`` detaches the style. The discoverable
619
+ name→GUID mapping is exposed as ``power_pptx.table_styles.TABLE_STYLES``;
620
+ unknown names raise ``ValueError`` with a "did you mean" hint.
621
+
622
+ - **International & layout text properties.** ``paragraph.rtl`` sets
623
+ right-to-left direction (Hebrew / Arabic / Farsi). ``paragraph.start_at``
624
+ / ``paragraph.set_numbered(scheme, start_at)`` start a numbered list at
625
+ an arbitrary value. ``text_frame.column_count`` / ``text_frame.column_spacing``
626
+ lay text out in multiple columns. ``paragraph.tab_stops`` is a collection
627
+ over ``<a:tabLst>`` with ``add_tab_stop(position, alignment)``.
628
+
629
+ - **Theme dark-mode & palette-from-seed.** ``prs.theme.to_dark_mode()``
630
+ produces a dark variant of a deck's theme in place — swapping the
631
+ background/text pairs and lightening accents only as needed to keep WCAG
632
+ AA contrast. ``DesignTokens.from_seed(seed, harmony=…)`` generates a
633
+ full, deterministic palette from one seed colour (complementary /
634
+ analogous / triadic / monochromatic), and
635
+ ``DesignTokens.validate_color_blindness(…)`` flags palette pairs
636
+ confusable under deuteranopia / protanopia / tritanopia.
637
+
638
+ - **Sections API.** ``prs.sections`` exposes the named slide groupings
639
+ shown in PowerPoint's outline / slide-sorter pane (stored as the
640
+ PowerPoint-2010 ``p14:sectionLst`` presentation extension). The
641
+ ``Sections`` collection supports ``len`` / indexing / iteration,
642
+ ``.add(name, start_slide_index=None)`` and ``.remove(section)``; each
643
+ ``Section`` exposes ``.name`` (read/write), ``.id`` (GUID),
644
+ ``.slides`` / ``.slide_ids``, and ``.delete()``.
645
+
646
+ - **Slide reorder/move and first-class notes.** ``prs.slides.move(old,
647
+ new)`` relocates a single slide and ``prs.slides.reorder(order)``
648
+ applies a full permutation (indices or ``Slide`` objects) without raw
649
+ ``sldIdLst`` surgery. ``slide.notes`` is a read/write speaker-notes
650
+ accessor — reading returns ``""`` without creating a notes slide,
651
+ assigning a string writes the notes text frame (creating it on
652
+ demand). The LLM-friendly "deck + notes" path.
653
+
654
+ - **Accessibility.** ``shape.alt_text`` maps to the ``descr`` attribute
655
+ of the shape's ``<p:cNvPr>`` (the alt-text slot screen readers
656
+ announce) and ``shape.title_text`` to the companion ``title``.
657
+ ``power_pptx.accessibility.audit_accessibility(prs)`` returns a
658
+ read-only report flagging pictures/meaningful shapes missing alt text,
659
+ text below WCAG AA contrast, and slides without a title — with
660
+ ``has_errors`` / ``markdown()`` / ``to_dict()`` / ``to_json()``.
661
+
662
+ - **Lint CI extensions.** ``SlideLintReport.to_sarif()`` /
663
+ ``to_sarif_json()`` emit a SARIF v2.1.0 document (the GitHub
664
+ code-scanning interchange format) with a ``power-pptx-lint`` driver
665
+ and per-issue results (severity mapped error/warning/note);
666
+ ``lint_report_to_sarif(reports)`` aggregates a whole deck with slide
667
+ indices. ``SlideLintReport.diff(baseline)`` returns only
668
+ newly-introduced issues (matched by stable fingerprints, so a
669
+ moved-but-still-broken shape isn't flagged), and ``diff_detail()``
670
+ adds the fixed set.
671
+
672
+ Fixed
673
+ ~~~~~
674
+
675
+ - **``from_spec`` no longer silently swallows an unknown layout name.**
676
+ A typo'd / unrecognized ``"layout"`` used to fall back to the Blank
677
+ layout silently, so a misspelled ``"titel"`` produced a blank slide
678
+ that looked like the styled layout simply hadn't applied. It now
679
+ raises ``ValueError`` with the closest valid layout suggested — the
680
+ same fail-closed-on-typos contract already used for unknown spec
681
+ keys. Pass ``"layout": "blank"`` explicitly for a deliberately blank
682
+ slide.
683
+
684
+ These additions ship with unit tests, a round-trip test, and
685
+ ISO-29500 schema-validity tests for the new group-fill and run-property
686
+ XML.
687
+
688
+
689
+ 2.8.1 (2026-06-17)
690
+ ++++++++++++++++++
691
+
692
+ Patch release fixing four ISO/IEC 29500 schema-validity bugs surfaced
693
+ by an aggressive deck-generation stress suite (``examples/stress_test/``).
694
+ Each bug produced a file that opens in python-pptx and LibreOffice but
695
+ that Microsoft PowerPoint reports as broken / silently repairs — the
696
+ exact class the ``schema-validation`` CI gate exists to catch. Every
697
+ fix ships with a reproducing deck builder in
698
+ ``tests/schema/test_schema_validity.py``.
699
+
700
+ Fixed
701
+ ~~~~~
702
+
703
+ - **Charts were rejected by Microsoft PowerPoint (every chart type).**
704
+ The chart axis ids (``c:axId`` / ``c:crossAx``) hardcoded in the chart XML
705
+ writer used values above ``2**31`` (e.g. ``2226939960``). ISO-29500 types
706
+ these as ``xsd:unsignedInt`` so they pass schema validation, python-pptx and
707
+ LibreOffice both open them — but PowerPoint parses ``axId`` as a *signed*
708
+ 32-bit integer, so any value over ``2**31-1`` overflows to a negative number
709
+ and PowerPoint reports the deck as needing repair. All axis ids are now in
710
+ the valid signed-int32 range (``1 .. 2**31-1``). The ``schema-validation``
711
+ harness gained an explicit axis-id range check (the XSD cannot express it),
712
+ with a self-test that confirms it fires.
713
+
714
+ - **Slide-transition duration emitted an invalid bare ``p14:dur``.**
715
+ Setting ``slide.transition.duration`` wrote the PowerPoint-2010
716
+ extension attribute ``p14:dur`` directly on a plain
717
+ ``<p:transition>``. That attribute is only schema-valid inside the
718
+ ``<mc:AlternateContent><mc:Choice Requires="p14">`` wrapper, which
719
+ was previously created only for p14 *kind* elements (Morph, …). A
720
+ classic transition (Fade, Push, …) carrying a duration therefore
721
+ produced an invalid file. The wrapper is now applied whenever the
722
+ transition holds any p14 content — kind *or* attribute — and a
723
+ classic kind is preserved in the ``<mc:Fallback>`` for pre-2010
724
+ viewers.
725
+
726
+ - **Radar charts emitted a disallowed ``<c:smooth>`` element.**
727
+ ``CT_RadarSer`` does not admit ``smooth`` (it is a line/scatter
728
+ series element); it is no longer written for ``RADAR`` /
729
+ ``RADAR_MARKERS`` / ``RADAR_FILLED`` charts.
730
+
731
+ - **``PresetMaterial.SOFT_METAL`` emitted ``softMetal``.** The
732
+ ISO ``ST_PresetMaterialType`` enumeration spells this value
733
+ all-lowercase (``softmetal``); the XML value is corrected so 3-D
734
+ shapes using the soft-metal material validate.
735
+
736
+ - **Picture recolor ``"washout"`` dropped the required ``thresh``
737
+ attribute.** ``<a:biLevel>``'s ``thresh`` was declared as an
738
+ ``OptionalAttribute`` with ``default=0.5``, so assigning the common
739
+ value ``0.5`` made the serializer omit the (schema-required)
740
+ attribute. ``thresh`` is now a ``RequiredAttribute`` and is always
741
+ written.
742
+
743
+ Added
744
+ ~~~~~
745
+
746
+ - ``examples/stress_test/`` — a bug-surfacing deck suite plus a harness
747
+ that checks every generated deck through build, lint, round-trip,
748
+ reopen, and ISO-29500 schema validation.
749
+
750
+
751
+ 2.8.0 (2026-05-22)
752
+ ++++++++++++++++++
753
+
754
+ Minor release adding a high-level geometry / text / arrow / diagram
755
+ surface that collapses the seven-line styling rituals LLM-generated
756
+ deck code repeatedly reinvents. Motivated by the v2.7-era
757
+ recommendations captured during a real AI Engineering deck rebuild
758
+ — the friction points where the existing 1.0.2 surface forced
759
+ generated code to re-implement the same glue every time.
760
+
761
+ All additions are drop-in compatible. The 1.0.2 + post-fork
762
+ surface continues to work unchanged.
763
+
764
+ Added
765
+ ~~~~~
766
+
767
+ - ``power_pptx.BBox`` — immutable rectangular-region value object
768
+ with EMU storage. Constructors: ``from_inches``, ``from_emu``,
769
+ ``from_shape``, ``from_slide``. Transforms: ``inset(all=, x=, y=,
770
+ left=, top=, right=, bottom=)``, ``shifted``, ``resized``, ``sub``.
771
+ Splits: ``split_h([1, 1], gap=)``, ``split_v``, ``grid(cols, rows,
772
+ gap_x=, gap_y=)``. Predicates: ``contains``, ``intersects``,
773
+ ``intersection``, ``union``. Unpacks to ``(left, top, width,
774
+ height)`` so it splats into every ``add_*`` API
775
+ (``slide.shapes.add_shape(MSO_SHAPE.RECT, *bbox)``). Re-exported
776
+ from the package root. Available as ``shape.bbox`` for any shape.
777
+
778
+ - ``slide.shapes.add_text(bbox_or_lengths, text=..., font=...,
779
+ size_pt=..., bold=..., italic=..., color=..., align="center",
780
+ anchor="middle", margin_pt=..., word_wrap=True)`` — one-call
781
+ styled textbox. Hex strings, ``RGBColor``, and ``(r, g, b)``
782
+ tuples all accepted for colours; short-name strings
783
+ (``"left"``/``"center"``/``"right"``/``"justify"`` and
784
+ ``"top"``/``"middle"``/``"bottom"``) accepted for alignment and
785
+ anchor — no enum imports required. Returns the textbox shape.
786
+
787
+ - ``slide.shapes.add_arrow(start, end, head="triangle", tail=None,
788
+ color=..., weight_pt=1.5, style="solid", route="straight",
789
+ inset_pt=6.0, start_side="auto", end_side="auto")`` — connector
790
+ with a real arrowhead, auto-routed mid-edge endpoints, and a
791
+ configurable inset so the head doesn't bleed into the target
792
+ shape. Endpoints accept ``BaseShape``, ``BBox``, or ``(x, y)``.
793
+
794
+ - ``shape.fill_hex("#0B5CFF")`` and ``shape.line_hex("#0D0D0D",
795
+ weight_pt=1.25)`` — chainable hex-string shortcuts that return
796
+ ``self``. Accept ``None`` to clear, hex strings with or without
797
+ the leading ``#``, ``RGBColor`` instances, and ``(r, g, b)``
798
+ tuples.
799
+
800
+ - ``shape.set_text_preserving_format(new_text)`` — captures the
801
+ first paragraph's ``<a:pPr>`` and the first run's ``<a:rPr>``,
802
+ rebuilds the text body for ``new_text`` (one paragraph per
803
+ ``"\n"``), then re-applies those properties to every new run.
804
+ Font face / size / colour / bold / italic on the template run
805
+ are preserved verbatim.
806
+
807
+ - ``Picture.replace_with(builder, padding=0)`` — deletes the
808
+ picture and calls ``builder(slide, bbox)`` in its place, with
809
+ ``bbox`` shrunk by ``padding``. ``Picture.enclosing_container(
810
+ exclude_text=True, shrink_around=True)`` walks the slide to find
811
+ the smallest non-text-bearing shape enclosing the picture (the
812
+ "card" the picture lives inside) and trims that bbox to avoid
813
+ sibling content.
814
+
815
+ - ``slide.slide_bbox()`` returns the full slide ``BBox``.
816
+ ``slide.content_bbox(include_decorative=False)`` returns the
817
+ union of non-background shapes. ``slide.find_empty_region(
818
+ near=, min_width=, min_height=)`` returns a free region on the
819
+ slide. ``slide.tidy(fix_offslide=True, fix_overflow=True,
820
+ fix_grid_drift=False)`` is the one-call lint + safe auto-fix.
821
+
822
+ - ``power_pptx.diagrams`` module — six native-shape diagram
823
+ recipes covering ~80% of architecture-deck patterns:
824
+ ``horizontal_pipeline``, ``vertical_pipeline``, ``hub_and_spoke``,
825
+ ``cycle``, ``decision_tree``, ``comparison_columns``. Each takes
826
+ a slide, a ``BBox``, and a small content spec; returns a small
827
+ result dataclass exposing the constituent shapes. Recipes tag
828
+ their shapes with a shared ``lint_group`` so intentional
829
+ arrow-into-card overlaps don't flood ``slide.lint()`` /
830
+ ``audit()`` output.
831
+
832
+ - ``power_pptx.audit(prs, size_warn_bytes=2_000_000,
833
+ check_fonts=True)`` — whole-deck audit returning an
834
+ ``AuditReport`` with ``lint_issues`` (each tagged with the slide
835
+ index it came from), ``broken_pictures``, ``empty_slides``,
836
+ ``font_warnings`` (fonts outside a conservative
837
+ Windows/macOS/Office safe-list), and ``size_warnings``
838
+ (pictures larger than ``size_warn_bytes``). ``report.markdown()``
839
+ renders a chat-reply-ready summary. Read-only — never mutates
840
+ the deck.
841
+
842
+ - ``power_pptx.render.render_slides(prs, *, out_dir=,
843
+ slides=[0, 1, 2], name_template="slide-{:02d}.png", scale=0.5)``
844
+ — friendlier wrapper around ``render_slide_thumbnails`` with
845
+ cleaner argument names and a string-format template for output
846
+ filenames. ``scale=`` is translated to ``dpi=`` internally.
847
+
848
+ Documentation
849
+ ~~~~~~~~~~~~~
850
+
851
+ - New cheat-sheet block at the top of ``SKILL.md`` covering the
852
+ 25 most-common deck-generation operations on one screen.
853
+ - New anti-patterns block flagging the LLM-common mistakes:
854
+ comparing ``tf.paragraphs`` wrappers with ``is``, assuming
855
+ ``add_connector`` produces an arrowhead, sizing a rebuild to a
856
+ picture bbox when there's an enclosing card, etc.
857
+ - New ``references/geometry-and-arrows.md`` covering the full new
858
+ API surface with worked examples.
859
+ - README rewritten to lead with space-aware authoring, document
860
+ ``python -m power_pptx.skill install``, list the post-fork
861
+ feature set, and provide an end-to-end quick-start example.
862
+
863
+ Review-driven hardening (during PR #34)
864
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
865
+
866
+ - ``BBox.split_h`` / ``split_v`` / ``grid`` now distribute the span
867
+ with a running-remainder apportionment, so the emitted segments
868
+ sum to exactly the input span — previously each segment's width
869
+ was rounded independently and could drift by ±1 EMU per split,
870
+ which then accumulated across nested grids.
871
+ - ``BBox.intersects()`` docstring corrected to match the
872
+ implementation: touching edges do *not* count as intersection
873
+ (matching the standard "shared area" interpretation).
874
+ - ``slide.shapes.add_arrow(head=, tail=, head_size=, tail_size=)``
875
+ now validates every choice up front and raises ``ValueError`` on
876
+ typos, with case-insensitive matching for the four enum-like
877
+ string arguments. Previously an unknown name silently produced
878
+ a headless connector.
879
+ - ``slide.find_empty_region(near=...)`` validates the ``near``
880
+ argument and raises ``TypeError`` for unexpected types
881
+ (previously raised ``AttributeError`` deep inside).
882
+ - ``render_slides(name_template=)`` now validates the template
883
+ format string *before* rendering — a template without a
884
+ positional placeholder (which would silently overwrite every
885
+ PNG with the same filename) raises ``ValueError``.
886
+ - ``Picture.enclosing_container(shrink_around=True)`` rebuilt to
887
+ pick the smallest valid edge-trim that excludes each obstacle
888
+ while still containing the picture. The old heuristic could
889
+ collapse onto the obstacle (e.g. a title strip above the picture
890
+ caused the bottom edge to be pushed up, returning the top strip
891
+ instead of the picture area).
892
+ - ``audit()``'s ``empty_slides`` check now also skips slide-spanning
893
+ background rectangles (any shape ≥95% of the slide area without
894
+ live text), matching the documented contract.
895
+ - ``shape.fill_hex`` / ``shape.line_hex`` type annotations
896
+ corrected to ``str | None`` (both already supported ``None`` to
897
+ clear).
898
+ - ``hub_and_spoke`` recipe no longer creates a rectangle then
899
+ deletes it to add an oval — it builds the oval directly.
900
+
901
+ Tests
902
+ ~~~~~
903
+
904
+ - 68 new unit tests across ``tests/test_geometry.py``,
905
+ ``tests/test_shape_helpers.py``, ``tests/test_diagrams.py``,
906
+ ``tests/test_audit.py``, and ``tests/test_render_slides_alias.py``
907
+ cover the new API surface, the validation paths, and the
908
+ regression cases from the review feedback. Full 3582-test suite
909
+ passes.
910
+
911
+
912
+ 2.7.0 (2026-05-17)
913
+ ++++++++++++++++++
914
+
915
+ Minor release rolling up the playground-driven improvement batch
916
+ (``examples/playground/IMPROVEMENTS.md``). These are the fixes a
917
+ real authoring session — building five varied decks against the
918
+ recipe + ``from_spec`` paths — surfaced as friction points or
919
+ silent footguns.
920
+
921
+ Bug fixes
922
+ ~~~~~~~~~
923
+
924
+ - ``Chart.apply_palette`` now also sets ``series.format.line.color.rgb``
925
+ on the line-stroke chart family — ``LINE``, ``LINE_MARKERS``,
926
+ ``LINE_STACKED``, ``LINE_STACKED_100``, ``LINE_MARKERS_STACKED``,
927
+ ``LINE_MARKERS_STACKED_100``, ``THREE_D_LINE``,
928
+ ``XY_SCATTER_LINES`` (+ smooth and no-marker variants). Previously
929
+ only the per-series fill was written, so the call appeared to do
930
+ nothing on a line chart — the renderer fell back to the default
931
+ Office palette because the visible colour is the stroke, not the
932
+ fill. ``Chart.recolour`` got the same fix.
933
+
934
+ - ``XL_CHART_TYPE.XY_SCATTER`` now emits ``<c:scatterStyle val="marker"/>``
935
+ instead of ``val="lineMarker"`` + per-series ``<a:ln><a:noFill/>``.
936
+ The legacy combination still reads back as ``XY_SCATTER`` so older
937
+ decks round-trip, but newly-saved scatters are robust against
938
+ callers that recolour a series via ``series.format.line.color.rgb``
939
+ (which previously overwrote the ``noFill`` suppression and silently
940
+ flipped the chart to "lines with markers").
941
+
942
+ - ``TextFrame.fit_text`` measurement now uses
943
+ ``max(ink_box_height, point_size * 1.2)`` as the per-line vertical
944
+ cost, so a 72pt title in a 2-inch box no longer claims to fit when
945
+ wrapping forces a second line. Pillow's getbbox returns the *ink
946
+ box* — descenders and ascender included but not line leading — so
947
+ the old predicate under-counted wrapped-line height by ~40% and
948
+ picked a font size that overflowed.
949
+
950
+ - ``TextFrame.fit_text`` now raises ``ValueError`` when even 1pt
951
+ overflows the frame, with an actionable message. Pre-fix the
952
+ method silently returned ``None`` and the downstream ``Pt(None)``
953
+ setter crashed with a confusing ``TypeError``.
954
+
955
+ - ``slide.lint()`` ``TextOverflow`` heuristic now uses ``math.ceil``
956
+ on wrapped-line counts. A 31-char line wrapping to two real lines
957
+ used to count as 1.5 lines in the heuristic — under the
958
+ ``lines_available`` threshold — and the overflow was missed even
959
+ though every renderer drew the second line below the frame.
960
+
961
+ Added
962
+ ~~~~~
963
+
964
+ - ``Table.clear_style()`` detaches the default
965
+ "Medium Style 2 — Accent 1" table-style ID attached to every
966
+ ``slide.shapes.add_table(...)``. Necessary because the built-in
967
+ style applies its own banded-row overlay that survives
968
+ ``table.horz_banding = False`` and ``table.first_row = False``;
969
+ callers who paint every cell themselves can now stop the bleed
970
+ in one line.
971
+
972
+ - ``from_spec`` accepts a ``slide_size`` field — named shorthand
973
+ (``"16:9"``, ``"widescreen"``, ``"4:3"``, ``"standard"``,
974
+ ``"16:10"``, ``"a4"``, ``"letter"``), a ``(width, height)`` pair
975
+ in inches, or a ``{"width": …, "height": …}`` mapping. Previously
976
+ every spec-driven deck rendered at the bundled template's 4:3
977
+ default regardless of which recipes it called.
978
+
979
+ - ``from_spec`` accepts a pre-built
980
+ :class:`~power_pptx.design.tokens.DesignTokens` instance under the
981
+ ``tokens`` key. Previously the resolver rejected anything that
982
+ wasn't a ``Mapping`` with "must be a mapping"; sharing tokens
983
+ between imperative recipe calls and ``from_spec`` had to
984
+ round-trip through a (non-existent) ``.to_dict()``.
985
+
986
+ Changed
987
+ ~~~~~~~
988
+
989
+ - ``from_spec`` ``"theme"`` key — listed in ``_VALID_TOP_KEYS`` for
990
+ years — is now wired up as a friendly alias for ``"tokens"`` when
991
+ the latter is absent. Previously the validator accepted it and
992
+ ``_resolve_tokens`` silently ignored it, producing an unstyled deck
993
+ from any spec that used the documented ``theme`` shape. ``tokens``
994
+ wins when both are present.
995
+
996
+ - ``from_spec`` legacy layout aliases ``"title"`` and ``"bullets"``
997
+ are now auto-upgraded to their recipe counterparts
998
+ (``"title_recipe"`` / ``"bullets_recipe"``) when ``tokens`` is
999
+ supplied. Previously the legacy placeholder path was taken and
1000
+ the user's palette / typography was silently ignored. Specs that
1001
+ pass no ``tokens`` continue to hit the placeholder layouts
1002
+ unchanged.
1003
+
1004
+ - Recipe titles (``title_slide``, ``bullet_slide``, ``kpi_slide``,
1005
+ ``quote_slide``, ``image_hero_slide``, ``chart_slide``,
1006
+ ``table_slide``, ``code_slide``, ``timeline_slide``,
1007
+ ``comparison_slide``, ``figure_slide``) now set
1008
+ ``auto_size = TEXT_TO_FIT_SHAPE`` on the title text frame. Long
1009
+ titles that wrapped to a second line no longer spill below the
1010
+ fixed-height title region into the body content underneath. KPI
1011
+ card label / value / delta boxes got the same treatment so long
1012
+ labels like "Lines of code per slide" stop pushing the delta on
1013
+ top of the label.
1014
+
1015
+ Docs
1016
+ ~~~~
1017
+
1018
+ - ``references/design.md`` calls out that ``tokens.palette[k]``
1019
+ returns ``RGBColor`` (not a hex string); every public setter
1020
+ accepts the rich form, so ``RGBColor.from_hex(tokens.palette[k])``
1021
+ is unnecessary (and crashes).
1022
+
1023
+ - ``references/render.md`` documents three cross-renderer footguns
1024
+ worth knowing about when reviewing via
1025
+ ``Presentation.render_thumbnails`` — emoji tofu without an emoji
1026
+ font installed, LibreOffice centering un-aligned text that
1027
+ PowerPoint left-aligns, and ``wrap="none" + spAutoFit`` re-centering
1028
+ un-wrapped textboxes inside their original width.
1029
+
1030
+ - ``references/tables.md`` documents ``Table.clear_style()`` and the
1031
+ default-style-banding gotcha that motivates it.
1032
+
1033
+ - ``references/compose.md`` documents ``slide_size``, the
1034
+ ``theme``-as-``tokens``-alias behaviour, the legacy-layout
1035
+ auto-upgrade, and the ``DesignTokens`` instance acceptance.
1036
+
1037
+
1038
+ 2.6.1 (2026-05-08)
1039
+ ++++++++++++++++++
1040
+
1041
+ Patch release closing two more entries in the Issue-0 family — the
1042
+ PowerPoint-strict-validation bug class first surfaced by the v2.6.0
1043
+ doughnut ``<a:endParaRPr/>`` fix. Both bugs pass python-pptx's
1044
+ integrity check and round-trip through LibreOffice cleanly, but
1045
+ Microsoft PowerPoint's open-time validator rejects them with the
1046
+ "PowerPoint found a problem with content. Repair?" dialog and
1047
+ silently drops the offending content on accept.
1048
+
1049
+ Bug fixes
1050
+ ~~~~~~~~~
1051
+
1052
+ - Chart text-property blocks (``<c:txPr>``) and rich-title text
1053
+ (``<c:rich>``) constructed lazily by font / colour customisation no
1054
+ longer emit ``<a:p>`` paragraphs without ``<a:endParaRPr>``. The
1055
+ missing terminator triggered the "Repair?" dialog whenever a chart
1056
+ data label or legend font was customised after data labels were
1057
+ enabled (the most common pattern: ``chart.text_color = ...`` after
1058
+ ``has_data_labels = True``). The same fix applies to the dLbl
1059
+ template, the chart-title rich-text builder, and the autoshape
1060
+ text-body templates.
1061
+
1062
+ - Float-valued coordinates passed to shape constructors are now
1063
+ coerced to integer EMU at the API boundary. Users routinely write
1064
+ arithmetic like ``card_w = (Inches(N) - gutter) / 2`` (Python's
1065
+ ``/`` produces a float) and pass the result to ``add_chart``,
1066
+ ``add_shape``, ``add_textbox``, ``add_picture``, ``add_table``,
1067
+ ``add_connector``, ``add_movie``, ``add_ole_object``, or
1068
+ ``add_svg_picture``. Pre-fix, the float landed verbatim in the
1069
+ saved XML's ``<a:off>`` / ``<a:ext>`` attributes — schema-invalid
1070
+ per ``CT_Point2D`` (``xs:long``) and ``CT_PositiveSize2D``
1071
+ (``xs:nonNegativeInteger``), and rejected by PowerPoint with
1072
+ "Repair?". ``shape.left`` / ``shape.top`` / ``shape.width`` /
1073
+ ``shape.height`` setters apply the same coercion.
1074
+
1075
+ Added
1076
+ ~~~~~
1077
+
1078
+ - PowerPoint-strict regression tests in
1079
+ ``tests/test_powerpoint_strict_validation.py`` now scan saved decks
1080
+ for the three known PowerPoint-rejection patterns (``<a:p>`` with
1081
+ no terminator and no text run, ``<a:endParaRPr>`` missing
1082
+ ``lang``, and float-valued ``<a:off>`` / ``<a:ext>`` coordinates).
1083
+ Each historical Issue-0-family bug has a pinned regression
1084
+ fixture; the rule list grows as new strict-validator patterns are
1085
+ discovered in the field.
1086
+
1087
+
1088
+ 2.6.0 (2026-05-07)
1089
+ ++++++++++++++++++
1090
+
1091
+ Follows up the v2.5 review with the P0 bug fix, the P1 ergonomic
1092
+ additions identified in the user feedback document, and the
1093
+ previously-deferred P2/P3 items (lint group context manager,
1094
+ formats helpers, data-label collision strategy, the remaining
1095
+ shape-level building blocks, a constrained shape.animate() façade,
1096
+ and a deck-level PowerPoint-strict test scan).
1097
+
1098
+ Bug fixes
1099
+ ~~~~~~~~~
1100
+
1101
+ - ``area`` and ``doughnut`` chart writers no longer emit a bare
1102
+ ``<a:endParaRPr/>``. The bare form is schema-valid but Microsoft
1103
+ PowerPoint's open-time validator is stricter than the spec and
1104
+ prompted users to "Repair" any deck containing one of these chart
1105
+ types, deleting the chart contents on accept. All writers now
1106
+ emit ``<a:endParaRPr lang="en-US"/>`` and a regression test asserts
1107
+ the property holds for every chart writer.
1108
+
1109
+ New APIs
1110
+ ~~~~~~~~
1111
+
1112
+ - ``Chart.recolour(palette, by="auto")`` (US alias ``recolor``) —
1113
+ recommended single entry point for chart recolouring. ``by="auto"``
1114
+ dispatches to per-point colouring on pie / doughnut / pie-of-pie
1115
+ variants and per-series colouring otherwise. Closes the
1116
+ "apply_palette doesn't work on doughnuts" footgun.
1117
+
1118
+ - ``Chart.line_color`` (write-only) — pins axis line and gridline
1119
+ colours in one assignment, the line-side counterpart to
1120
+ ``text_color``. Skips axes that don't exist on pie / doughnut, and
1121
+ never materialises gridlines that aren't already there.
1122
+
1123
+ - ``Chart.apply_dark_theme(text=..., line=...)`` — one-call wrapper
1124
+ over ``text_color`` + ``line_color``.
1125
+
1126
+ - ``slide.shapes.add_picture(..., anchor=..., margin=..., container=...)``
1127
+ (and same kwargs on ``add_shape`` / ``add_textbox``) — anchor-aware
1128
+ positioning for branding elements. ``anchor="bottom-right"`` plus
1129
+ a ``margin`` collapses the add → measure → reposition idiom to one
1130
+ call, with ``container=`` controlling whether the anchor is
1131
+ relative to the slide or to a parent shape.
1132
+
1133
+ - ``slide.shapes.add_table(..., style="clean")`` — disables every
1134
+ inherited table-style flag at construction so custom borders /
1135
+ fills render consistently across PowerPoint and LibreOffice.
1136
+
1137
+ - ``power_pptx.add_kpi_card`` / ``power_pptx.add_progress_bar`` —
1138
+ shape-level building blocks layered beneath the slide-level
1139
+ recipes. Return small dataclasses (``KpiCard``, ``ProgressBar``)
1140
+ exposing the constituent shapes so callers can compose them into
1141
+ mixed layouts.
1142
+
1143
+ - Top-level imports — ``add_plotly_figure``, ``add_matplotlib_figure``,
1144
+ ``add_svg_figure``, ``add_html_figure``, ``FigureBackendUnavailable``,
1145
+ ``add_kpi_card``, ``add_progress_bar``, ``add_gauge``,
1146
+ ``add_status_pill``, ``add_stat_strip``, ``add_article_card``, plus
1147
+ the matching ``KpiCard`` / ``ProgressBar`` / ``Gauge`` / ``StatusPill``
1148
+ / ``StatStrip`` / ``ArticleCard`` dataclasses are importable from
1149
+ ``power_pptx`` directly (previously buried under
1150
+ ``power_pptx.design.figures`` / ``.components``).
1151
+
1152
+ - ``slide.shapes.lint_group_scope(name=None)`` — context manager
1153
+ that auto-tags every shape added inside the ``with`` block with
1154
+ the same ``lint_group``. Auto-generates a ``"design-group-N"``
1155
+ name when omitted. Lifts the existing ``shape.lint_group``
1156
+ mechanic out of "discoverable only via the docstring" into a
1157
+ first-class API. The ``ShapeCollision`` lint message now also
1158
+ appends a one-line tip recommending it when both shapes are
1159
+ untagged.
1160
+
1161
+ - ``power_pptx.formats`` — number-format string helpers that hide
1162
+ Excel's format-string syntax behind named functions: ``currency``,
1163
+ ``percent``, ``decimal``, ``thousands``, ``scientific``, ``date``.
1164
+ Use them with ``data_labels.number_format`` and
1165
+ ``cell.text_frame.text`` callsites.
1166
+
1167
+ - ``DataLabels.collision_strategy = "auto" | "shrink" | "compact"`` —
1168
+ three opinionated strategies for handling overlapping data labels
1169
+ on bar / column charts. ``"auto"`` shrinks font and drops
1170
+ ``gapWidth`` to 60 only on multi-series + ≥5-category plots;
1171
+ ``"compact"`` always applies both; ``"shrink"`` only shrinks the
1172
+ font. For real layout-aware collision avoidance, use
1173
+ ``add_plotly_figure`` — Plotly handles it natively.
1174
+
1175
+ - ``add_gauge``, ``add_status_pill``, ``add_stat_strip``,
1176
+ ``add_article_card`` — additional shape-level building blocks
1177
+ alongside ``add_kpi_card`` / ``add_progress_bar``. Each returns a
1178
+ small dataclass exposing the constituent shapes for further
1179
+ per-deck tweaks and tags the stack with ``lint_group``.
1180
+
1181
+ - ``BaseShape.animate(entry=, exit=, emphasis=, trigger=,
1182
+ delay_ms=, duration_ms=, direction=)`` — constrained-subset
1183
+ façade over the existing ``power_pptx.animation`` API. Pass
1184
+ exactly one of ``entry`` / ``exit`` / ``emphasis`` and the
1185
+ matching preset name. For animation types not covered, drop
1186
+ down to ``Entrance`` / ``Exit`` / ``Emphasis`` directly.
1187
+
1188
+ Test infrastructure
1189
+ ~~~~~~~~~~~~~~~~~~~
1190
+
1191
+ - ``tests/test_powerpoint_strict_validation.py`` — deck-level scan
1192
+ that constructs a deck with every implemented chart type, saves
1193
+ to bytes, and walks every XML part looking for stricter-than-spec
1194
+ patterns PowerPoint rejects but the OOXML schema accepts. Catches
1195
+ the ``<a:endParaRPr/>`` regression today; the scaffold is
1196
+ reusable for additional strict-validator rules we discover.
1197
+
1198
+ Behaviour changes
1199
+ ~~~~~~~~~~~~~~~~~
1200
+
1201
+ - ``slide.shapes.add_chart(BAR_*, ...)`` now defaults the category
1202
+ axis to ``reverse_order=True`` so ``["A", "B", "C"]`` renders with
1203
+ ``A`` at the top — matching natural reading order. Column charts
1204
+ retain their default left-to-right ordering, and ``BAR_OF_PIE``
1205
+ (a pie variant) is excluded. Override post-creation with
1206
+ ``chart.category_axis.reverse_order = False`` for the legacy
1207
+ ordering.
1208
+
1209
+ - ``Chart.apply_palette`` emits a ``UserWarning`` when called on a
1210
+ pie / doughnut chart and routes through ``color_by_category``,
1211
+ which is almost always what the caller meant. Use
1212
+ ``Chart.recolour`` for explicit, warning-free dispatch.
1213
+
1214
+
1215
+ 2.5.0 (2026-05-01)
1216
+ ++++++++++++++++++
1217
+
1218
+ End-to-end pass over the IMPROVEMENT_PLAN.md punch list. Smooths
1219
+ the most common authoring footguns for both human and LLM-driven
1220
+ deck generation; all changes are additive except where called out.
1221
+
1222
+ New APIs
1223
+ ~~~~~~~~
1224
+
1225
+ - ``Chart.shape`` — returns the parent ``GraphicFrame``, populated
1226
+ on first access via ``GraphicFrame.chart`` so callers don't have
1227
+ to keep the ``add_chart`` return value around. Reach for this
1228
+ instead of ``chart.element.getparent().getparent()`` when
1229
+ animating, measuring, or restyling the chart's parent shape.
1230
+
1231
+ - ``TextFrame.set_paragraph_defaults(font_name=None, size=None,
1232
+ bold=None, italic=None, color=None)`` — fills any *unset* font
1233
+ properties on every paragraph and run in the frame. Explicit per-run overrides
1234
+ (including theme-coloured runs) survive verbatim. Collapses the
1235
+ six-lines-per-paragraph branding ritual into one call.
1236
+
1237
+ - ``Slide.lint_group_overlaps(*shapes, name=None)`` — convenience
1238
+ over ``lint_group`` that auto-generates a unique-on-the-slide
1239
+ group name (``design-group-N``). Returns the chosen name so
1240
+ callers can reuse it later.
1241
+
1242
+ - ``power_pptx._color.coerce_color`` — internal helper exported for
1243
+ third-party integrations that want the same "color-like" coercion
1244
+ applied at every public boundary.
1245
+
1246
+ Improvements
1247
+ ~~~~~~~~~~~~
1248
+
1249
+ - **Color inputs are uniform across every public setter.**
1250
+ ``shape.fill.fore_color.rgb``, ``Chart.text_color``,
1251
+ ``linear_gradient``, ``gradient``, and friends now all accept hex
1252
+ strings (with or without ``#``), ``RGBColor`` instances, and
1253
+ 3-tuples of ints interchangeably. No more ``hex_rgb`` shims in
1254
+ user code. Floats / numeric strings / ``bool`` inside a 3-tuple
1255
+ are explicitly rejected so the contract stays tight.
1256
+
1257
+ - ``FillFormat.gradient(angle=...)`` now mirrors
1258
+ ``linear_gradient(angle=...)`` for symmetry. Linear-only;
1259
+ passing ``angle=`` with a non-linear ``kind`` raises
1260
+ ``ValueError``.
1261
+
1262
+ - ``apply_quick_layout`` accepts ``legend_position`` as either an
1263
+ ``XL_LEGEND_POSITION`` member, an integer enum value (preserving
1264
+ the historical numeric form), or its lowercase string name
1265
+ (``"right"``, ``"left"``, ``"top"``, ``"bottom"``, ``"corner"``).
1266
+
1267
+ - ``Presentation.set_transition(kind=...)`` no longer silently
1268
+ clobbers per-slide overrides. Slides that already have an
1269
+ explicit ``slide.transition.kind`` (including
1270
+ ``MSO_TRANSITION_TYPE.NONE`` — an explicit "no transition") are
1271
+ preserved by default; pass ``force=True`` to restore the old
1272
+ "force every slide" behaviour. ``duration``,
1273
+ ``advance_on_click``, and ``advance_after`` are unaffected.
1274
+ ``kind=None`` still wipes every slide so the existing "clear all"
1275
+ idiom keeps working.
1276
+
1277
+ - ``SlideLintReport.auto_fix()`` now handles ``TextOverflow``
1278
+ alongside ``OffSlide`` and ``OffGridDrift``. The fix flips the
1279
+ offending text frame's ``auto_size`` to
1280
+ ``MSO_AUTO_SIZE.TEXT_TO_FIT_SHAPE`` so PowerPoint shrinks the
1281
+ runs at render time; frames with an explicit auto-size are
1282
+ respected. This is the single biggest lever for "lint-or-die"
1283
+ pipelines on runtime-supplied text.
1284
+
1285
+ - Linter heuristic tuning:
1286
+
1287
+ - ``OffGridDrift`` default tolerance relaxed from 0.01" to 0.05".
1288
+ ``Inches(0.6)`` divider next to ``Inches(0.62)`` eyebrow no
1289
+ longer fires on every section header.
1290
+ - ``TextOverflow`` uses a tighter 0.45× character-width
1291
+ multiplier for short single-line strings (≤ 20 chars) — fixes
1292
+ the false positive on small badges / pills at 9pt.
1293
+ - ``ShapeCollision`` auto-suppresses the canonical layered-design
1294
+ pattern (smaller shape strictly contained in a larger shape and
1295
+ drawn on top). Equal-bbox pairs still classify as ``matched``
1296
+ so duplicate-rectangle bugs remain auditable.
1297
+
1298
+ - ``RGBColor.from_string`` now emits ``DeprecationWarning`` on call.
1299
+ ``RGBColor.from_hex`` is the supported parser (it accepts hex
1300
+ with or without a leading ``#``). Internal call sites switched
1301
+ to ``from_hex`` so the warning only fires in user code.
1302
+
1303
+ Documentation
1304
+ ~~~~~~~~~~~~~
1305
+
1306
+ - New ``docs/user/common-pitfalls.rst`` — covers colour coercion,
1307
+ transition ordering, recipe → blank layout, ``chart.shape``,
1308
+ animation experimental status, and the linter's behaviour
1309
+ changes in one place.
1310
+
1311
+ - ``power_pptx.animation`` is now flagged **experimental** in the
1312
+ module docstring, the bundled skill (``references/animations.md``),
1313
+ and ``docs/user/animation.rst``. Animation timing XML
1314
+ round-trips through the OOXML schema and renders correctly via
1315
+ LibreOffice, but does not currently play in PowerPoint slideshow
1316
+ mode (animated shapes sit at 10–15% opacity and snap to fully
1317
+ visible all at once). Slides combining entrance animations with
1318
+ a Morph transition can additionally trigger PowerPoint's
1319
+ "Repair?" dialog. Use slide *transitions* until this is fixed.
1320
+
1321
+ - Bundled Claude skill updated: SKILL.md gains a "do not generate
1322
+ animation calls" house rule, refreshed pitfalls list, and a
1323
+ pointer at the new ``references/real-world-decks.md``.
1324
+
1325
+ Infrastructure
1326
+ ~~~~~~~~~~~~~~
1327
+
1328
+ - CI gains a ``pyflakes examples/`` static-check job and a
1329
+ ``examples/real_world/build_all.py`` smoke build, so the public
1330
+ surface stays green against the ten Fortune-500-style example
1331
+ decks.
1332
+
1333
+
1334
+ 2.4.0 (2026-04-30)
1335
+ ++++++++++++++++++
1336
+
1337
+ Animation ergonomics, gradient/alpha discoverability, recipe bug fixes,
1338
+ and a bundled Claude skill. All additive; nothing removed.
1339
+
1340
+ New APIs
1341
+ ~~~~~~~~
1342
+
1343
+ - ``slide.animations.group()`` — context manager that animates every
1344
+ effect added in the block as a single visual cluster (first effect
1345
+ ``AFTER_PREVIOUS``; subsequent ones default to ``WITH_PREVIOUS``).
1346
+ Use this for sub-shapes that belong to the same card/row/panel.
1347
+ Drastically reduces the per-slide timing-tree size — and the
1348
+ perceived lag — relative to the same effects added independently.
1349
+
1350
+ - ``SlideAnimations`` is now iterable, supports ``len()``, and exposes
1351
+ ``clear()``. Iteration yields read-only ``AnimationEntry`` views
1352
+ with ``kind``, ``preset``, ``trigger``, ``shape_id``, ``shape``,
1353
+ ``duration``, ``delay``, and a ``remove()`` method. Useful for
1354
+ re-animating, copying animations between slides, and debugging why
1355
+ something didn't fire. ``purge_orphans`` is unchanged.
1356
+
1357
+ - ``fill.linear_gradient(start, end, angle=...)`` (and a multi-stop
1358
+ list form) — one-line gradient helper that wraps ``gradient()`` +
1359
+ ``gradient_stops`` for the 90% case. ``gradient_angle``'s docstring
1360
+ now spells out the OOXML convention (``0`` left→right, ``90``
1361
+ top→bottom, ``180`` right→left, ``270`` bottom→top).
1362
+
1363
+ - ``DesignTokens.with_overrides(...)`` now accepts nested-dict input
1364
+ in addition to dotted keys (``{"palette": {"primary": "#FF6600"}}``
1365
+ is equivalent to ``{"palette.primary": "#FF6600"}``). Mixed input
1366
+ is allowed.
1367
+
1368
+ Bundled Claude skill
1369
+ ~~~~~~~~~~~~~~~~~~~~
1370
+
1371
+ - The ``power-pptx`` Claude Code skill (``SKILL.md`` + reference docs)
1372
+ ships inside the package at ``power_pptx/skill/``. Install it with
1373
+ ``python -m power_pptx.skill install`` (or the ``power-pptx-skill``
1374
+ console script). Pip-installing power-pptx is now sufficient to
1375
+ make the skill available wherever the library runs.
1376
+
1377
+ Recipe bug fixes
1378
+ ~~~~~~~~~~~~~~~~
1379
+
1380
+ - ``figure_slide`` correctly routes inline SVG markup that contains a
1381
+ namespace URL (``xmlns="http://..."``) to the SVG embedder rather
1382
+ than mis-classifying it as a file path and raising
1383
+ ``FileNotFoundError``.
1384
+
1385
+ - ``code_slide``'s Pygments highlighting renders ``Token.Operator``
1386
+ and ``Token.Punctuation`` in the same colour as plain code text,
1387
+ so member-access dots (``optimiser.zero_grad``) stay legible on
1388
+ light-surface themes (previously they faded into the background).
1389
+
1390
+ ``from_spec`` ergonomics
1391
+ ~~~~~~~~~~~~~~~~~~~~~~~~
1392
+
1393
+ - Recipe layouts now reject unknown spec keys (``ValueError``) instead
1394
+ of silently dropping them. Catches typos like ``"subtitlz"`` or
1395
+ ``"millestones"`` that previously yielded a slide missing the
1396
+ intended content.
1397
+
1398
+ - ``"comparison"`` is now an unambiguous alias for the comparison
1399
+ recipe. Use ``"comparison_layout"`` to opt in to the
1400
+ placeholder-based layout from the underlying template.
1401
+
1402
+
1403
+ 2.3.0 (2026-04-29)
1404
+ ++++++++++++++++++
1405
+
1406
+ Two P0 fixes, six new recipes, a meaningful lint-noise reduction,
1407
+ and figure-embedding adapters for Plotly / Matplotlib / HTML. All
1408
+ changes are additive; no existing API was removed.
1409
+
1410
+ P0 fixes
1411
+ ~~~~~~~~
1412
+
1413
+ - ``render_slide_thumbnails`` no longer silently drops slides 2..N
1414
+ on stock LibreOffice 7+ (whose ``--convert-to png`` filter only
1415
+ emits the first slide). When the PNG path under-produces, the
1416
+ renderer transparently falls back to ``--convert-to pdf`` plus a
1417
+ per-page split via ``pdftoppm`` (Poppler) or ``pypdfium2``. New
1418
+ ``strategy="auto" | "png" | "pdf"`` and ``dpi=`` knobs.
1419
+ - ``SlideAnimations.add(kind, preset, shape, **kwargs)`` is now a
1420
+ documented entry point — the class was publicly exported but had
1421
+ no polymorphic ``add()`` method, leaving data-driven callers to
1422
+ build their own dispatcher.
1423
+
1424
+ API ergonomics
1425
+ ~~~~~~~~~~~~~~
1426
+
1427
+ - ``MotionPath.svg(slide, shape, "M 0 0 H 100 V 100", viewbox=...)``
1428
+ accepts standard SVG path syntax (M/m L/l H/h V/v C/c Q/q Z/z) and
1429
+ maps coordinates into OOXML's unit-square space. ``MotionPath.custom``
1430
+ remains the OOXML-syntax escape hatch.
1431
+ - ``kpi_slide`` delta auto-detects fraction-vs-raw: ``0.27`` →
1432
+ ``+27%``, ``14.0`` → ``+14.0`` (was silently ``+1400%``). String
1433
+ ``delta`` and the new ``delta_text`` field pass through verbatim.
1434
+ - ``quote_slide`` strips a leading hyphen / en-dash / em-dash from
1435
+ ``attribution`` so callers who already wrote ``"— Person"`` don't
1436
+ get ``"— — Person"``.
1437
+ - Every recipe docstring now lists the palette / typography /
1438
+ shadow / radii slots it consumes — no more grepping the source.
1439
+
1440
+ Six new recipes
1441
+ ~~~~~~~~~~~~~~~
1442
+
1443
+ - ``section_divider`` — full-bleed cover with an optional eyebrow
1444
+ caption and ``progress=(3, 7)`` row of progress dots.
1445
+ - ``chart_slide`` — line / line_markers / bar / column / pie /
1446
+ doughnut / area, with ``chart_palette=`` (named preset, colour
1447
+ list, or token-derived in palette priority order), plus
1448
+ ``legend=`` / ``smooth=`` / ``data_labels=`` toggles.
1449
+ - ``table_slide`` — header band + banded rows; ``widths=`` (fractions
1450
+ or absolute Lengths), per-column ``aligns=``, optional
1451
+ ``totals=`` footer row.
1452
+ - ``code_slide`` — monospace panel with optional Pygments syntax
1453
+ highlighting.
1454
+ - ``timeline_slide`` — horizontal rail with alternating-side
1455
+ date / label pairs and ``done`` marker tinting.
1456
+ - ``comparison_slide`` — matched two-column L/R rows.
1457
+ - ``figure_slide`` — embed a Plotly Figure, Matplotlib Figure, SVG
1458
+ blob, HTML snippet, or image path; dispatches by type.
1459
+
1460
+ Lint signal-to-noise
1461
+ ~~~~~~~~~~~~~~~~~~~~
1462
+
1463
+ - Implicit name-prefix grouping: shapes named ``card.bg`` /
1464
+ ``card.label`` are auto-grouped under ``card`` so the linter
1465
+ treats the pair as one logical unit without per-shape tagging.
1466
+ ``shape.lint_group = ""`` opts a shape out of the implicit group.
1467
+ - ``ShapeCollision`` ``kind="matched"`` reclassified ERROR → INFO.
1468
+ Identical bounds are almost always intentional layering (badge
1469
+ + number, button + label); the kind is preserved on the issue
1470
+ for callers who really want to filter on it.
1471
+ - ``slide.lint(disable=["ShapeCollision"], min_severity="warning")``
1472
+ filters issues at the lint level rather than after the fact.
1473
+ - ``auto_fix()`` now also clamps shape *size* when the shape is
1474
+ larger than the slide — translation alone could never converge,
1475
+ so the previous fixer was a silent no-op for oversize shapes.
1476
+ Multiple OffSlide issues on the same shape coalesce into one fix.
1477
+ - ``SlideLintReport.fingerprints()`` returns 12-char content
1478
+ digests suitable for CI baselining — re-runs after layout
1479
+ changes only surface newly-introduced issues.
1480
+
1481
+ Design system
1482
+ ~~~~~~~~~~~~~
1483
+
1484
+ - ``DesignTokens.from_preset("modern_light" | "modern_dark" |
1485
+ "corporate_navy" | "vibrant")`` ships ready-to-use token sets so
1486
+ callers don't have to invent a brand from scratch.
1487
+ - ``tokens.with_overrides({"palette.primary": "#FF6600",
1488
+ "typography.heading.size": Pt(40)})`` layers dotted-path tweaks
1489
+ onto a base set without forking it.
1490
+ - ``kpi_slide`` and ``code_slide`` now apply ``shadows.card`` and
1491
+ ``radii.md`` via a shared ``_apply_card_styling`` helper, so the
1492
+ card-style backdrops actually reflect the design system instead
1493
+ of relying on hard-coded defaults.
1494
+
1495
+ compose.from_spec
1496
+ ~~~~~~~~~~~~~~~~~
1497
+
1498
+ - Recipe dispatch: layouts ``kpi`` / ``chart`` / ``table`` / ``code`` /
1499
+ ``timeline`` / ``comparison`` / ``quote`` / ``image_hero`` /
1500
+ ``section_divider`` / ``figure`` / ``title_recipe`` /
1501
+ ``bullets_recipe`` route to the matching ``recipes`` function.
1502
+ Legacy placeholder layouts (``title``, ``bullets``, ``two_column``,
1503
+ …) keep working for branded-template flows.
1504
+ - ``tokens`` spec key accepts presets, YAML paths, inline dicts, or
1505
+ ``{"preset": name, "overrides": {...}}``.
1506
+ - ``vars`` spec key + ``{{name}}`` interpolation across every
1507
+ string in the spec, including dotted paths into nested mappings.
1508
+ Unknown names raise rather than silently rendering as the
1509
+ literal placeholder.
1510
+ - ``compose.from_yaml(path, vars={...})``: direct YAML entry point.
1511
+
1512
+ Figure embedding
1513
+ ~~~~~~~~~~~~~~~~
1514
+
1515
+ New ``power_pptx.design.figures`` module with optional-dep adapters:
1516
+
1517
+ - ``add_plotly_figure(slide, fig, ...)`` — renders via
1518
+ ``fig.to_image()`` (needs ``plotly + kaleido``); SVG when
1519
+ ``cairosvg`` is present, PNG otherwise.
1520
+ - ``add_matplotlib_figure(slide, fig, ...)`` — renders via
1521
+ ``fig.savefig()`` (needs ``matplotlib``); same SVG / PNG split.
1522
+ - ``add_svg_figure(slide, svg, ...)`` — wraps the existing
1523
+ ``add_svg_picture`` so every figure kind shares one entry shape.
1524
+ - ``add_html_figure(slide, html, ...)`` — proxy for HTML
1525
+ embedding (PowerPoint has no native HTML surface): screenshots
1526
+ the rendered DOM via headless Chromium (needs ``playwright`` +
1527
+ ``playwright install chromium``).
1528
+
1529
+ Each adapter imports its dependency lazily and raises
1530
+ ``FigureBackendUnavailable`` (subclass of ``ImportError``) naming
1531
+ the install command when the dep is missing.
1532
+
1533
+ Tooling
1534
+ ~~~~~~~
1535
+
1536
+ - New ``release-on-version-bump.yml`` workflow auto-creates a
1537
+ ``vX.Y.Z`` tag and a GitHub release when a merge to ``master``
1538
+ changes ``power_pptx.__version__`` to a value with no matching
1539
+ tag. Chains into the existing ``publish.yml`` for PyPI upload.
1540
+
1541
+
1542
+ 2.2.0 (2026-04-29)
1543
+ ++++++++++++++++++
1544
+
1545
+ Two additive lint detector changes scoped from the post-2.1.1
1546
+ follow-up — both signal-to-noise improvements on layered production
1547
+ decks. No behavior change for existing callers: the new
1548
+ classification surfaces extra fields on existing issues, and the new
1549
+ geometry mode is opt-in.
1550
+
1551
+ ShapeCollision now scores
1552
+ ~~~~~~~~~~~~~~~~~~~~~~~~~
1553
+
1554
+ ``ShapeCollision`` issues carry a ``score: float`` in ``[0.0, 1.0]``
1555
+ and a ``kind: str`` in ``{"incidental", "partial", "matched"}``,
1556
+ emitted alongside the pre-existing ``intersection_area`` /
1557
+ ``intersection_pct`` / ``groups`` fields.
1558
+
1559
+ - ``incidental`` — small shape fully inside a larger one (the
1560
+ card-on-panel pattern). Severity drops to ``INFO``.
1561
+ - ``partial`` — partial overlap, neither contains the other.
1562
+ Severity stays ``WARNING``.
1563
+ - ``matched`` — near-identical bbox (within 5% on each axis) and
1564
+ heavy overlap (>80%). Severity raised to ``ERROR`` — almost
1565
+ certainly a duplicate or copy-paste bug.
1566
+
1567
+ The score combines containment (pulls toward incidental), size ratio
1568
+ (closer to 1.0 pulls up), and overlap percentage (pulls up).
1569
+ ``lint_group`` suppression still runs *before* scoring — a tagged
1570
+ group is intentional by definition and never scores.
1571
+
1572
+ The classification is also surfaced in ``report.summary()``: each
1573
+ ``ShapeCollision`` line now carries ``[kind=…, score=…]`` so readers
1574
+ can spot the genuine bugs without re-running the detector.
1575
+
1576
+ Effect-bleed-aware geometry (opt-in)
1577
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
1578
+
1579
+ ``slide.lint(...)`` and ``lint_slide(slide, ...)`` accept a new
1580
+ ``include_effect_bleed: bool = False`` keyword argument. When
1581
+ ``True``, the ``OffSlide`` and ``ShapeCollision`` detectors widen
1582
+ each shape's bbox by its shadow's blur radius before checking
1583
+ geometry — catching the case where a panel's raw bbox sits inside the
1584
+ slide but its shadow visually bleeds past the edge.
1585
+
1586
+ Bleed-only triggers (where the raw bbox stays clean and only the
1587
+ inflated bbox crosses the boundary) are emitted as
1588
+ ``OffSlideShadow`` / ``ShapeCollisionShadow`` subclasses, so callers
1589
+ can opt out specifically via ``shape.lint_skip = {"OffSlideShadow"}``
1590
+ without silencing real geometry warnings.
1591
+
1592
+ The inflation model is the simple one in this release — each side
1593
+ extended by ``blur_radius / 2``. Directional projection
1594
+ (``distance × direction``) and other effects (glow, soft-edges,
1595
+ reflection) are TODOs for a follow-up. ``GraphicFrame.shadow ==
1596
+ None`` (added in 2.1.1) is handled gracefully — the helper falls
1597
+ back to the raw bbox.
1598
+
1599
+ Default behavior is unchanged; existing decks see no new warnings
1600
+ unless they opt in to ``include_effect_bleed=True``.
1601
+
1602
+
1603
+ 2.1.1 (2026-04-29)
1604
+ ++++++++++++++++++
1605
+
1606
+ Bug fixes and ergonomic improvements.
1607
+
1608
+ OOXML correctness
1609
+ ~~~~~~~~~~~~~~~~~
1610
+
1611
+ Both items below address the "PowerPoint found a problem with content.
1612
+ Repaired and removed it" prompt on open.
1613
+
1614
+ - ``chart.legend.position = XL_LEGEND_POSITION.RIGHT`` now writes
1615
+ ``<c:legendPos val="r"/>`` explicitly. ``CT_LegendPos.val`` is an
1616
+ ``OptionalAttribute`` whose setter strips the attribute when the
1617
+ assigned value matches the OOXML default ("r"), producing a bare
1618
+ ``<c:legendPos/>`` element that PowerPoint's strict parser rejects.
1619
+ PowerPoint then "repairs" the chart by deleting it. The bug only
1620
+ manifested for the default position — every other legend position
1621
+ wrote correctly because they didn't trip the strip-on-default branch.
1622
+
1623
+ - Fix the same "Repaired and removed it" prompt on decks that used
1624
+ ``shape.lint_group``, ``slide.lint_group(...)``, or
1625
+ ``slide.design_group(...)``. The 2.1.0 implementation stored the
1626
+ group name as a custom-namespaced *attribute* on each shape's
1627
+ ``p:cNvPr`` element. ``CT_NonVisualDrawingProps`` has no
1628
+ ``xsd:anyAttribute`` in the OOXML schema, so PowerPoint's strict
1629
+ validator (notably on macOS) flagged every tagged shape as malformed
1630
+ and stripped its non-visual properties on open.
1631
+
1632
+ Lint metadata now lives in an ``a:extLst/a:ext`` extension element
1633
+ under ``cNvPr``, the schema-sanctioned mechanism PowerPoint preserves
1634
+ verbatim. Decks saved with 2.1.0 are read transparently — the legacy
1635
+ attribute is migrated to the new layout the next time the value is
1636
+ written.
1637
+
1638
+ Ergonomic improvements
1639
+ ~~~~~~~~~~~~~~~~~~~~~~
1640
+
1641
+ - ``GraphicFrame.shadow`` now returns ``None`` (previously raised
1642
+ ``NotImplementedError``). Probing every shape on a slide for shadow
1643
+ info no longer needs a ``try/except`` wrapper; ``if shape.shadow is
1644
+ None`` is the supported "no facade available" check.
1645
+
1646
+ - ``SlideLintReport.auto_fix()`` now refreshes ``report.issues`` after
1647
+ applying fixes, so the residual punch list is just ``report.issues``
1648
+ rather than a second ``slide.lint()`` call. Skipped on
1649
+ ``dry_run=True`` (nothing changed on the slide).
1650
+
1651
+ - ``Chart.text_color = "#FFFFFF"`` (or ``RGBColor`` / ``(r, g, b)``
1652
+ tuple) now pins the colour across ``chart.font``,
1653
+ ``chart.legend.font`` (when present), ``chart.chart_title`` runs
1654
+ (when present), and every plot's ``data_labels.font`` (when
1655
+ enabled) — the most common copy-paste in dark-deck authoring.
1656
+ Write-only; read individual fonts directly.
1657
+
1658
+ - ``ShapeCollision.groups`` exposes the ``lint_group`` tag of each
1659
+ colliding shape as a ``(group_a, group_b)`` tuple. Lets callers
1660
+ triage "intentional overlap I forgot to tag" (one or both ``None``)
1661
+ vs. "genuine layout bug" (different non-``None`` tags) at a glance
1662
+ in ``report.summary()``.
1663
+
1664
+ - ``shape.lint_skip = {"MinFontSize", …}`` opts an individual shape
1665
+ out of named lint checks — the natural counterpart to ``lint_group``
1666
+ for "I know this one's fine; stop warning." Cross-shape issues
1667
+ (``ShapeCollision``, ``ZOrderAnomaly``) are only suppressed when
1668
+ *both* shapes opt out. Persists alongside ``lint_group`` in the
1669
+ ``cNvPr/extLst/ext`` extension block.
1670
+
1671
+
1672
+ 2.1.0 (2026-04-29)
1673
+ ++++++++++++++++++
1674
+
1675
+ Feature release. No breaking changes; everything from 2.0.0 continues
1676
+ to work unchanged. Headline additions span the linter, tables, charts,
1677
+ animations, and the theme.
1678
+
1679
+ Lint
1680
+ ~~~~
1681
+
1682
+ - ``shape.lint_group`` / ``slide.lint_group(name, *shapes)`` /
1683
+ ``slide.design_group(name)`` context manager — the cheapest fix for
1684
+ the noisiest part of the linter. Shapes that share a non-empty
1685
+ ``lint_group`` are allowed to overlap without producing a
1686
+ :class:`~power_pptx.lint.ShapeCollision` warning, so intentional
1687
+ layering (KPI cards, accent bars, overlaid labels) no longer drowns
1688
+ out real signal. The tag is stored as a custom-namespaced attribute
1689
+ on ``p:cNvPr`` and round-trips through power-pptx save/load.
1690
+
1691
+ - Five new lint checks:
1692
+
1693
+ * :class:`~power_pptx.lint.MinFontSize` — flags any text run below the
1694
+ legibility threshold (default 9pt).
1695
+ * :class:`~power_pptx.lint.OffGridDrift` — detects shapes whose left
1696
+ or top edge is slightly off a column/row grid that several other
1697
+ shapes hit cleanly.
1698
+ * :class:`~power_pptx.lint.LowContrast` — computes the WCAG contrast
1699
+ ratio between text and resolved-background fill, warns below 4.5:1.
1700
+ Resolves only solid RGB fills (theme colors and gradients are
1701
+ skipped silently, so the check is noise-free by construction).
1702
+ * :class:`~power_pptx.lint.ZOrderAnomaly` — finds filled shapes drawn
1703
+ above shapes they visually contain (the inner shape would be
1704
+ hidden at render time).
1705
+ * :class:`~power_pptx.lint.MasterPlaceholderCollision` — flags a
1706
+ non-placeholder shape sitting at exactly the position of a layout
1707
+ placeholder it should likely have inherited from instead.
1708
+
1709
+ - ``SlideLintReport.auto_fix()`` now also snaps ``OffGridDrift``
1710
+ offenders onto the dominant grid line — the Tier-3 auto-fix from the
1711
+ hierarchy.
1712
+
1713
+ Tables
1714
+ ~~~~~~
1715
+
1716
+ - ``row.borders`` / ``col.borders`` shorthand — apply ``left`` /
1717
+ ``right`` / ``top`` / ``bottom`` / ``outer`` borders to every cell in
1718
+ a row or column with a single call. Mirrors the existing per-cell
1719
+ ``cell.borders``.
1720
+
1721
+ - ``Table.banded_rows`` / ``Table.banded_cols`` — friendlier aliases
1722
+ for ``horz_banding`` / ``vert_banding`` that match PowerPoint's UI
1723
+ vocabulary.
1724
+
1725
+ - ``Table.fit_to_box(...)`` — for runtime-driven tables: walks every
1726
+ populated cell, computes the per-cell best-fit font size against the
1727
+ cell's own width and row height (margins respected), and applies the
1728
+ smallest of those uniformly so the grid reads as one coherent size.
1729
+
1730
+ - ``cell.text_frame.fit_text`` now measures against the cell's own
1731
+ ``width`` / ``height`` (it was measuring against the whole table
1732
+ before, giving meaningless results). ``_Cell.width`` / ``height``
1733
+ properties are exposed for the same reason.
1734
+
1735
+ Charts
1736
+ ~~~~~~
1737
+
1738
+ - ``apply_quick_layout`` accepts keyword overrides on top of named
1739
+ presets::
1740
+
1741
+ chart.apply_quick_layout("title_legend_right", title_text="Q4 ARR")
1742
+ chart.apply_quick_layout(
1743
+ "title_axes_legend_bottom",
1744
+ value_axis_title_text="Revenue (£m)",
1745
+ has_major_gridlines=False,
1746
+ )
1747
+
1748
+ - ``Chart.color_by_category(palette)`` recolors each *data point*
1749
+ instead of each series — the helper for stacked-bar / stacked-column
1750
+ charts where you want each category segment to read as a discrete
1751
+ color.
1752
+
1753
+ - ``GraphicFrame.render_to_png(...)`` renders the parent slide via
1754
+ headless LibreOffice and crops to the frame's bbox, so a chart or
1755
+ table can be exported as a standalone PNG without taking the whole
1756
+ slide. Reuses the existing :func:`~power_pptx.render.render_slide_thumbnail`
1757
+ infrastructure; requires Pillow (already a dependency) and
1758
+ ``soffice`` on PATH.
1759
+
1760
+ Animations
1761
+ ~~~~~~~~~~
1762
+
1763
+ - ``slide.animations.typewriter([s1, s2, s3], delay_between_ms=200)``
1764
+ one-line replacement for the manual ``with sequence(): for s ...``
1765
+ loop. Defaults to the ``"wipe"`` preset; any entrance preset can be
1766
+ passed.
1767
+
1768
+ - Easing curves on :meth:`SlideAnimations.add_entrance` (and friends):
1769
+ pass ``easing="ease_in"`` / ``"ease_out"`` / ``"ease_in_out"`` /
1770
+ ``"linear"`` for a preset, or ``easing=(accel, decel)`` for an
1771
+ explicit ``<p:cTn>``-level acceleration / deceleration pair.
1772
+
1773
+ - ``BaseShape.delete()`` — removes the shape *and* purges any orphan
1774
+ animation entries that targeted it. Equivalent to the manual
1775
+ ``shape._element.getparent().remove(shape._element)`` idiom but with
1776
+ the cleanup pass that the manual idiom misses.
1777
+ ``slide.animations.purge_orphans()`` is exposed publicly for callers
1778
+ that delete shapes by other means.
1779
+
1780
+ Theme
1781
+ ~~~~~
1782
+
1783
+ - ``Theme.apply(other, rebind_shape_colors=True, presentation=prs)`` —
1784
+ re-skinning a deck no longer leaves orphan literal colors. Every
1785
+ shape whose hardcoded RGB matches a slot in the *old* (pre-swap)
1786
+ palette is rewritten to point at that theme slot. Returns the
1787
+ number of color references rebound.
1788
+
1789
+ - ``Theme.embed_font(presentation, path, typeface=..., weight=...)`` —
1790
+ bundles a TTF/OTF into the deck under ``/ppt/fonts/`` and registers
1791
+ it in ``<p:embeddedFontLst>`` so the deck travels with its font and
1792
+ doesn't fall back to Calibri on the customer's machine. The font is
1793
+ embedded unobfuscated (content type ``application/x-fontdata``);
1794
+ PowerPoint 2007+ accepts this form. The fully-obfuscated form per
1795
+ ECMA-376 §15.2.13 is on the roadmap.
1796
+
1797
+ - ``Slide.color_variant`` — per-slide light / dark variant via
1798
+ ``<p:clrMapOvr>``. ``slide.color_variant = "dark"`` swaps
1799
+ ``bg`` / ``tx`` mappings without changing the deck theme;
1800
+ ``"light"`` (the default) restores master inheritance.
1801
+ ``Slide.set_clr_map_override(...)`` for arbitrary attribute remappings.
1802
+
1803
+ Bug fixes
1804
+ ~~~~~~~~~
1805
+
1806
+ - ``power_pptx.text.layout.TextFitter`` no longer raises when text
1807
+ genuinely cannot fit at any point size; the predicate now treats
1808
+ unfittable input as "doesn't fit" so :meth:`best_fit_font_size`
1809
+ returns ``None`` cleanly. Surfaces with ``cell.text_frame.fit_text``
1810
+ on tiny cells with very long text.
1811
+
1812
+ - ``tests/text/test_fonts.py`` resets the ``FontFiles`` class-level
1813
+ cache in its ``find_fixture`` so the suite is order-independent.
1814
+
1815
+ Tests
1816
+ ~~~~~
1817
+
1818
+ 3,140 passing (up from 3,087 in 2.0.0): 53 new tests across lint,
1819
+ tables, charts, animations, and theme.
1820
+
1821
+
1822
+ 2.0.0 (2026-04-29)
1823
+ ++++++++++++++++++
1824
+
1825
+ Breaking change: the importable package was renamed from ``pptx`` to
1826
+ ``power_pptx`` so that ``power-pptx`` and ``python-pptx`` can be
1827
+ installed side-by-side without colliding on the top-level ``pptx``
1828
+ module. Update imports accordingly::
1829
+
1830
+ # before (1.x)
1831
+ from pptx import Presentation
1832
+ from pptx.util import Inches
1833
+
1834
+ # after (2.0+)
1835
+ from power_pptx import Presentation
1836
+ from power_pptx.util import Inches
1837
+
1838
+ No public API behavior changed in this release; everything that used to
1839
+ live under ``pptx.*`` now lives under ``power_pptx.*`` with identical
1840
+ signatures. The PyPI distribution name (``power-pptx``) is unchanged.
1841
+
1842
+
1843
+ 1.1.0 (2026-04-28)
1844
+ ++++++++++++++++++
1845
+
1846
+ This is the inaugural release under the ``power-pptx`` distribution name
1847
+ on PyPI. It is a drop-in replacement for ``python-pptx`` 1.0.2:
1848
+ ``import power_pptx`` continues to work and existing user code is unaffected.
1849
+ It bundles every feature from Phases 1 through 10 of the fork's roadmap —
1850
+ visual effects, animations, transitions, theme reader/writer, JSON
1851
+ authoring, the layout linter, design tokens and slide recipes, chart
1852
+ palettes and quick layouts, slide thumbnails, and more.
1853
+
1854
+ The Sphinx documentation has also been rebuilt: every new module ships
1855
+ with a user-guide chapter and an API-reference page, the substitution
1856
+ table covers every public class added by the fork, and Read-the-Docs
1857
+ builds now fail on Sphinx warnings.
1858
+
1859
+ The full per-phase changelog follows; the project changes summary is
1860
+ collected near the end under "Project changes".
1861
+
1862
+ Phase 9 — design-system layer
1863
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
1864
+
1865
+ - ``power_pptx.design.tokens.DesignTokens``: source-agnostic container for
1866
+ brand tokens — ``palette`` (str → ``RGBColor``), ``typography``
1867
+ (``TypographyToken`` with ``family``/``size``/``bold``/``italic``/
1868
+ ``color``), ``radii`` and ``spacings`` (str → ``Length``), and
1869
+ ``shadows`` (``ShadowToken``). Constructors: ``from_dict``,
1870
+ ``from_yaml`` (optional ``pyyaml``), and ``from_pptx`` (extracts
1871
+ accent palette + major/minor fonts from a deck's theme).
1872
+ ``DesignTokens.merge(other)`` layers an override token set on top of
1873
+ a base.
1874
+
1875
+ - ``shape.style``: token-resolving ``ShapeStyle`` facade exposed on
1876
+ every shape. Setters fan out to the low-level proxies::
1877
+
1878
+ shape.style.fill = tokens.palette["primary"]
1879
+ shape.style.line = tokens.palette["primary"]
1880
+ shape.style.shadow = tokens.shadows["card"]
1881
+ shape.style.font = tokens.typography["body"]
1882
+ shape.style.text_color = tokens.palette["neutral"]
1883
+
1884
+ ``ShadowToken`` assignment leaves unset fields untouched so partial
1885
+ tokens are non-destructive; ``None`` clears the corresponding effect.
1886
+
1887
+ - ``power_pptx.design.recipes``: opinionated parameterized slide
1888
+ constructors. Five recipes are included — ``title_slide``,
1889
+ ``bullet_slide``, ``kpi_slide``, ``quote_slide``, and
1890
+ ``image_hero_slide`` — each taking the host ``Presentation``, the
1891
+ recipe-specific content kwargs (e.g. ``title=``, ``bullets=``,
1892
+ ``kpis=``), an optional ``DesignTokens`` for palette/typography
1893
+ resolution, and an optional ``transition=`` name. Recipes use the
1894
+ ``Blank`` layout and place every shape themselves so the rendered
1895
+ geometry doesn't depend on the host template's master. ``kpi_slide``
1896
+ honors ``palette["positive"]`` / ``palette["negative"]`` when
1897
+ tinting deltas (falling back to green/red), and applies
1898
+ ``tokens.shadows["card"]`` to each card when present.
1899
+
1900
+ - A starter pack of three example token sets — ``modern``, ``classic``,
1901
+ and ``editorial`` — lives at ``examples/starter_pack/``. Each
1902
+ module exports both a ``SPEC`` dict (suitable for serialising) and
1903
+ a ready-to-use ``TOKENS`` (a built ``DesignTokens``).
1904
+ ``examples/starter_pack/build_preview.py`` renders a comparison
1905
+ deck per set into ``examples/starter_pack/_out/`` (gitignored).
1906
+
1907
+ Phase 10 — additional motion-path presets
1908
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
1909
+
1910
+ - ``power_pptx.animation.MotionPath`` gains five new convenience constructors
1911
+ alongside the existing ``line`` / ``custom``: ``diagonal``,
1912
+ ``circle`` (closed cubic-bezier loop with a ``clockwise`` flag),
1913
+ ``arc`` (quadratic-bezier hop with a configurable ``height``
1914
+ fraction), ``zigzag`` (configurable ``segments`` / ``amplitude``),
1915
+ and ``spiral`` (configurable ``turns`` and direction). All
1916
+ normalize EMU inputs against the slide's dimensions and route
1917
+ through ``slide.animations.add_motion``, so they honor the Phase 5
1918
+ trigger model and round-trip cleanly.
1919
+
1920
+ Phase 10 — chart palette presets
1921
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
1922
+
1923
+ - ``Chart.apply_palette(palette)`` recolors every series in a chart
1924
+ from a named built-in preset or an iterable of color-likes,
1925
+ independently of ``chart_style``. Series are recolored in
1926
+ declaration order; palettes wrap when the chart has more series
1927
+ than colors.
1928
+
1929
+ - New module ``power_pptx.chart.palettes`` exposes ``CHART_PALETTES`` (six
1930
+ built-in palettes — ``modern``, ``classic``, ``editorial``,
1931
+ ``vibrant``, ``monochrome_blue``, ``monochrome_warm``),
1932
+ ``palette_names()``, and ``resolve_palette()`` for callers that want
1933
+ to share the same color set with non-chart shapes.
1934
+
1935
+ - Per-series gradient and pattern fills work out of the box through
1936
+ ``chart.series[i].format.fill`` (a regular ``FillFormat``) — locked
1937
+ in with regression tests covering the four gradient kinds and
1938
+ ``MSO_PATTERN_TYPE`` patterns.
1939
+
1940
+ Phase 6 — theme-aware color inheritance
1941
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
1942
+
1943
+ - New ``power_pptx.inherit.resolve_color(color_format, theme=...)`` returns
1944
+ the effective ``RGBColor`` for any ``ColorFormat`` (including the
1945
+ ``_LazyColorFormat`` proxy returned by ``Font.color`` /
1946
+ ``LineFormat.color``). Explicit RGB colors are returned as-is,
1947
+ scheme colors resolve through ``theme.colors[…]``, and unset colors
1948
+ return ``None`` without mutating XML. ``brightness`` is applied by
1949
+ blending the resolved RGB toward white or black, mirroring
1950
+ PowerPoint's ``lumMod`` / ``lumOff`` model.
1951
+
1952
+ Phase 6 — native SVG in ``add_picture``
1953
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
1954
+
1955
+ - New ``slide.shapes.add_svg_picture(svg_file, left, top, width=None,
1956
+ height=None, *, png_fallback=None)`` embeds both an
1957
+ ``<asvg:svgBlip>`` (Office 2016+ SVG extension) and a PNG fallback
1958
+ inside the same ``<a:blip>``. When ``png_fallback`` is omitted the
1959
+ SVG is rasterised through the optional ``cairosvg`` dependency; a
1960
+ clear ``CairoSvgUnavailable`` error guides callers to install it or
1961
+ supply their own fallback. ``image/svg+xml`` is registered as a
1962
+ first-class image content type so SVG parts round-trip cleanly.
1963
+
1964
+ Phase 7 — ``power_pptx.compose`` package
1965
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
1966
+
1967
+ - ``power_pptx.compose`` is now a real package re-exporting ``from_spec``,
1968
+ ``import_slide``, and ``apply_template`` from a single import path::
1969
+
1970
+ from power_pptx.compose import from_spec, import_slide, apply_template
1971
+
1972
+ The implementations live in private submodules
1973
+ (``power_pptx.compose.from_spec``, ``power_pptx._slide_importer``,
1974
+ ``power_pptx._template_applier``). Existing imports (``from power_pptx.compose
1975
+ import from_spec``) are unchanged.
1976
+
1977
+ Phase 10 — chart quick layouts
1978
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
1979
+
1980
+ - ``Chart.apply_quick_layout(layout)`` toggles title / legend /
1981
+ axis-title / gridline visibility in opinionated combinations. Ten
1982
+ built-in presets ship in ``power_pptx.chart.quick_layouts``
1983
+ (``title_legend_right``, ``title_legend_bottom``,
1984
+ ``title_legend_top``, ``title_legend_left``, ``title_no_legend``,
1985
+ ``no_title_no_legend``, ``title_axes_legend_right``,
1986
+ ``title_axes_legend_bottom``, ``minimal``, ``dense``); custom layouts
1987
+ can be supplied as a dict spec.
1988
+
1989
+ Phase 10 — slide-thumbnail renderer
1990
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
1991
+
1992
+ - New ``power_pptx.render`` module with
1993
+ ``render_slide_thumbnails(prs, ...)`` and
1994
+ ``render_slide_thumbnail(slide, ...)``, plus convenience methods
1995
+ ``Presentation.render_thumbnails()`` and ``Slide.render_thumbnail()``.
1996
+ Drives a headless ``soffice --headless --convert-to png`` shell-out
1997
+ to rasterise slides; supports custom binary path
1998
+ (``soffice_bin=`` or ``POWER_PPTX_SOFFICE`` env var), per-slide
1999
+ selection (``slide_indexes=``), bytes-or-paths return
2000
+ (``return_bytes=True``), custom output directory, and a configurable
2001
+ timeout. Raises ``ThumbnailRendererUnavailable`` with an install
2002
+ hint when ``soffice`` isn't on PATH and ``ThumbnailRendererError``
2003
+ on conversion failure.
2004
+
2005
+ Phase 6 — text-fit estimator on Linux / minimal runtimes
2006
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
2007
+
2008
+ - ``TextFrame.fit_text()`` now works on Linux and on runtimes without
2009
+ the requested font installed. ``FontFiles._font_directories()``
2010
+ enumerates ``/usr/share/fonts``, ``/usr/local/share/fonts``,
2011
+ ``/usr/share/fonts/truetype``, ``~/.fonts``, and
2012
+ ``~/.local/share/fonts``; unrecognised platforms now return an empty
2013
+ directory list instead of raising ``OSError``. When no matching
2014
+ system font can be located, ``_best_fit_font_size`` falls back to
2015
+ ``ImageFont.load_default(size=...)`` (Pillow ≥10.1, with a graceful
2016
+ fallback to the unsized bitmap default on older Pillow), so a call
2017
+ with no ``font_file=`` argument produces a usable estimate rather
2018
+ than a ``KeyError``. Malformed font files encountered during the
2019
+ directory scan are skipped silently.
2020
+
2021
+
2022
+ Phase 8 — 3D primitives and SmartArt text substitution
2023
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
2024
+
2025
+ - ``shape.three_d`` accessor: ``ThreeDFormat`` facade exposing
2026
+ ``bevel_top``/``bevel_bottom`` (``_BevelFormat`` with ``preset``,
2027
+ ``width``, ``height``), ``extrusion_height``, ``extrusion_color``,
2028
+ ``contour_width``, ``contour_color``, and ``preset_material``.
2029
+ Backed by ``CT_Shape3D`` and ``CT_Scene3D`` element classes in
2030
+ ``power_pptx.oxml.dml.three_d``. ``BevelPreset`` and ``PresetMaterial``
2031
+ enumerations added to ``power_pptx.enum.dml``.
2032
+
2033
+ - ``slide.smart_art``: ``SmartArtCollection`` providing indexed and
2034
+ iterable access to SmartArt graphics on a slide. Each item is a
2035
+ ``SmartArtShape`` with:
2036
+
2037
+ - ``texts`` property — ordered list of node text strings.
2038
+ - ``set_text(values, *, strict=True)`` — replaces node text in
2039
+ document order without touching layout, style, or colour parts.
2040
+
2041
+ ``DiagramDataPart`` and sibling part classes registered so SmartArt
2042
+ ``diagrams/data#.xml``, ``layout#``, ``quickStyle#``, and ``colors#``
2043
+ parts are handled as typed ``XmlPart`` subclasses.
2044
+
2045
+
2046
+ Phase 7 — slide composition
2047
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~
2048
+
2049
+ - ``Presentation.import_slide(source_slide, merge_master='dedupe'|'clone')``:
2050
+ clones a slide from any ``Presentation`` into the receiver. Copies
2051
+ the slide part and all its dependencies (images, charts, media,
2052
+ notes, SmartArt diagram parts, …). Master/layout/theme parts are
2053
+ either deduped against existing masters (``'dedupe'``) or always
2054
+ cloned fresh (``'clone'``). Slide IDs and partnames are guaranteed
2055
+ collision-free.
2056
+
2057
+ - ``Presentation.apply_template(path_or_stream)``: re-points every
2058
+ slide's layout/master/theme at masters from a ``.potx`` or ``.pptx``
2059
+ template. Slide content is preserved. Layout matching: name → type
2060
+ → first layout. Unreferenced old masters/layouts/themes are dropped
2061
+ from the saved package.
2062
+
2063
+
2064
+ Project changes
2065
+ ~~~~~~~~~~~~~~~
2066
+
2067
+ - Renamed the PyPI distribution from ``python-pptx-next`` to
2068
+ ``power-pptx``. The importable package remains ``pptx``.
2069
+ - Repository moved to ``codehalwell/power-pptx``.
2070
+ - Original ``LICENSE`` (MIT, Steve Canny, 2013) preserved verbatim;
2071
+ fork copyright added on a second line per MIT requirements.
2072
+ - Dropped the vestigial ``pyparsing`` line from ``requirements.txt``;
2073
+ it was not in ``pyproject.toml`` runtime deps and is not imported
2074
+ anywhere in ``src/pptx/``.
2075
+ - Added Python 3.13 to the supported-versions classifier list.
2076
+ - Dropped Python 3.8 (EOL October 2024). Minimum supported version is
2077
+ now 3.9, matching ``pyright``'s configured ``pythonVersion``.
2078
+
2079
+ Documentation
2080
+ ~~~~~~~~~~~~~
2081
+
2082
+ - Sphinx config rebuilt for ``power-pptx``: switched to the
2083
+ ``sphinx-rtd-theme``, removed dead upstream-specific hacks, refreshed
2084
+ the substitution table, and turned on ``fail_on_warning`` for
2085
+ Read-the-Docs builds.
2086
+ - New user-guide chapters: visual effects, animations, slide
2087
+ transitions, layout linter, JSON authoring + cross-presentation
2088
+ composition, themes, design-system layer, advanced charts (palettes
2089
+ / quick layouts / per-series fills), and slide thumbnails.
2090
+ - New API reference pages: ``power_pptx.animation``, ``power_pptx.lint``,
2091
+ ``power_pptx.compose``, ``power_pptx.theme`` (plus ``power_pptx.inherit.resolve_color``),
2092
+ ``power_pptx.design`` (tokens, style, layout, recipes), ``power_pptx.render``,
2093
+ ``power_pptx.smart_art``, plus enum pages for ``MSO_LINE_CAP_STYLE``,
2094
+ ``MSO_LINE_COMPOUND_STYLE``, ``MSO_LINE_JOIN_STYLE``,
2095
+ ``MSO_LINE_END_TYPE``, ``MSO_LINE_END_SIZE``, ``MSO_TRANSITION_TYPE``,
2096
+ and ``PP_ANIM_TRIGGER``.
2097
+ - ``ShadowFormat`` and the ``DrawingML`` reference page surface the
2098
+ full Phase 3/6 effect family (``GlowFormat``, ``SoftEdgeFormat``,
2099
+ ``BlurFormat``, ``ReflectionFormat``, ``LineEndFormat``,
2100
+ ``PictureEffects``).
2101
+
2102
+ Deprecations (scheduled for removal in 2.0)
2103
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
2104
+
2105
+ - ``ShadowFormat.inherit`` now emits a ``DeprecationWarning`` on both
2106
+ read and write. Read individual properties (``blur_radius``,
2107
+ ``distance``, ``direction``, ``color``) for ``None`` instead. The
2108
+ ``inherit`` property is scheduled for removal in 2.0.
2109
+ - ``MSO_PATTERN_TYPE.ERCENT_40`` is now an aliased member of
2110
+ ``PERCENT_40`` and emits a ``DeprecationWarning`` on access.
2111
+ - ``shapes.turbo_add_enabled`` setter remains a no-op and emits a
2112
+ ``DeprecationWarning`` (shape-id allocation is now always O(1)).
2113
+
2114
+ New API
2115
+ ~~~~~~~
2116
+
2117
+ - Radial / rectangular / shape-path gradients (Phase 6): ``FillFormat.gradient``
2118
+ now accepts a ``kind`` argument. ``fill.gradient(kind="radial")`` writes a
2119
+ ``<a:path path="circle"/>`` shading element; ``"rectangular"`` writes
2120
+ ``"rect"``; ``"shape"`` follows the bounding shape. ``fill.gradient_kind``
2121
+ reports the resolved value (``"linear"``/``"radial"``/``"rectangular"``/
2122
+ ``"shape"``/``None``). Switching kinds preserves existing gradient stops.
2123
+ ``GradientStops`` is now mutable: ``stops.append(position, color)``,
2124
+ ``stops.replace([(pos, color), ...])``, and ``del stops[i]`` (the OOXML
2125
+ 2-stop minimum is enforced). ``color`` accepts ``RGBColor``, hex strings
2126
+ (with or without leading ``#``), 3-tuples, or ``None`` (placeholder
2127
+ ``schemeClr accent1`` color).
2128
+ - ``power_pptx.design.layout`` (Phase 9): build-time geometry helpers that compute
2129
+ ``Box(left, top, width, height)`` rectangles so callers don't eyeball EMUs.
2130
+ ``Grid(slide, cols=12, rows=6, gutter=Pt(12), margin=Inches(0.5))`` allocates
2131
+ rectangles via ``grid.cell(col=, row=, col_span=, row_span=)`` or applies
2132
+ them directly with ``grid.place(shape, ...)``. ``Stack(direction="vertical"
2133
+ | "horizontal", gap=Pt(8), left=, top=, width=, height=)`` walks a running
2134
+ cursor via ``stack.next(width=, height=)`` / ``stack.place(shape, ...)``;
2135
+ ``stack.reset()`` rewinds. Pure geometry — no XML is read or mutated until
2136
+ the caller invokes a ``place()``.
2137
+ - ``MotionPath`` (Phase 5): convenience class for adding motion-path
2138
+ animations. ``MotionPath.line(slide, shape, dx, dy)`` accepts EMU
2139
+ deltas (typically built with ``Inches(...)``/``Pt(...)``) and
2140
+ normalizes them against the slide's dimensions before emitting the
2141
+ motion-path attribute, so the *absolute* travel distance is preserved
2142
+ across slide sizes. ``MotionPath.custom(slide, shape, path_str)``
2143
+ passes an OOXML motion-path expression through verbatim. Both
2144
+ delegate to ``SlideAnimations.add_motion`` and inherit the trigger /
2145
+ delay / duration model from the rest of Phase 5.
2146
+ - ``SlideAnimations.sequence()`` (Phase 5): context manager that
2147
+ groups the contained ``Entrance``/``Exit``/``Emphasis``/``MotionPath``
2148
+ effects into a single click-driven chain. Inside the block, the
2149
+ first effect (whose ``trigger`` was not explicitly supplied) fires on
2150
+ ``Trigger.ON_CLICK`` (or whatever ``start=`` is passed) and every
2151
+ subsequent effect defaults to ``Trigger.AFTER_PREVIOUS``, producing
2152
+ effects that play one after another. Explicit per-call triggers
2153
+ still override the sequence default; sequences cannot be nested.
2154
+ - ``Entrance.fade(slide, text_frame, by_paragraph=True)`` (Phase 5):
2155
+ by-paragraph entrance animations. Accepts a ``TextFrame`` or any
2156
+ shape with a ``text_frame``; emits one entrance effect per paragraph,
2157
+ each targeting ``<p:txEl>/<p:pRg st=N end=N/>`` so PowerPoint reveals
2158
+ paragraphs one at a time. The first paragraph fires on the supplied
2159
+ trigger and subsequent paragraphs on ``Trigger.AFTER_PREVIOUS``.
2160
+ Available presets: ``appear``, ``fade``, ``wipe``, ``zoom``, ``wheel``,
2161
+ ``random_bars``. The ``by_paragraph=`` keyword is also exposed on
2162
+ ``SlideAnimations.add_entrance`` for advanced use.
2163
+ - ``Theme`` writer (Phase 7): ``prs.theme`` is now read/write.
2164
+ ``theme.colors[MSO_THEME_COLOR.ACCENT_1] = RGBColor(0xFF, 0x66, 0x00)``
2165
+ rewrites the requested ``clrScheme`` slot with a fresh ``<a:srgbClr>``;
2166
+ alias slots (``BACKGROUND_1``/``BACKGROUND_2``/``TEXT_1``/``TEXT_2``)
2167
+ resolve to their canonical ``lt1``/``lt2``/``dk1``/``dk2`` target.
2168
+ ``theme.fonts.major = "Inter"`` and ``theme.fonts.minor = "Inter"``
2169
+ rewrite the ``<a:majorFont>/<a:minorFont>/<a:latin typeface=…/>``
2170
+ typeface. ``theme.apply(other_prs.theme)`` bulk-copies the palette
2171
+ and font pair. ``theme.name`` is now writable. Themes are loaded
2172
+ via a typed ``ThemePart(XmlPart)`` so writes round-trip on save.
2173
+ - ``Cell.borders`` (Phase 4): per-edge line formatting on table cells.
2174
+ ``cell.borders.left``/``.right``/``.top``/``.bottom``/``.diagonal_down``/
2175
+ ``.diagonal_up`` each return a ``LineFormat``. Convenience helpers
2176
+ ``cell.borders.all(width=, color=)``, ``cell.borders.outer(...)``, and
2177
+ ``cell.borders.none()`` apply or clear border settings across multiple
2178
+ edges in one call. Backed by the OOXML ``a:lnL/lnR/lnT/lnB/lnTlToBr/
2179
+ lnBlToTr`` children of ``a:tcPr``.
2180
+ - ``run.hyperlink.target_slide`` (Phase 4): assign a ``Slide`` to make
2181
+ a text run an internal hyperlink. Writes a relationship-based
2182
+ ``ppaction://hlinksldjump`` action; assigning ``None`` clears it. The
2183
+ symmetric getter resolves the relationship back to the target slide,
2184
+ mirroring ``Shape.click_action.target_slide``.
2185
+ - ``ColorFormat.alpha`` (Phase 3): per-color transparency. Read/write
2186
+ float in ``[0.0, 1.0]`` (``1.0`` is fully opaque, the default; ``0.0``
2187
+ is fully transparent). Maps to the ``<a:alpha>`` child of any
2188
+ ``<a:srgbClr>``/``<a:schemeClr>``/etc. Available on the lazy proxy
2189
+ returned by ``Font.color`` and ``LineFormat.color`` with the same
2190
+ non-mutating read semantics as the rest of that proxy.
2191
+ - ``LineFormat`` line-style additions (Phase 6): ``line.cap``
2192
+ (``MSO_LINE_CAP``: ``FLAT``/``ROUND``/``SQUARE``), ``line.compound``
2193
+ (``MSO_LINE_COMPOUND``: single, double, thick-thin, thin-thick,
2194
+ triple), ``line.join`` (``MSO_LINE_JOIN``: round/bevel/miter mapping
2195
+ to the ``<a:round/>``/``<a:bevel/>``/``<a:miter/>`` children), plus
2196
+ ``line.head_end`` and ``line.tail_end`` ``LineEndFormat`` proxies
2197
+ exposing ``.type`` (``MSO_LINE_END_TYPE``), ``.width`` and
2198
+ ``.length`` (``MSO_LINE_END_SIZE``). All reads are non-mutating;
2199
+ clearing the last attribute on a head/tail end drops the element so
2200
+ theme inheritance is preserved.
2201
+ - ``Slide.transition`` (Phase 4): a ``SlideTransition`` proxy exposing
2202
+ ``.kind`` (``MSO_TRANSITION_TYPE``, including PowerPoint 2010+
2203
+ ``p14`` extension transitions like ``MORPH``, ``CONVEYOR``,
2204
+ ``VORTEX``), ``.duration`` (milliseconds, via ``p14:dur`` with
2205
+ fallback mapping for the legacy ``spd`` bucket), ``.advance_on_click``
2206
+ and ``.advance_after``. Reads on a slide with no explicit
2207
+ ``<p:transition>`` return ``None`` and never mutate; ``.clear()``
2208
+ removes the element entirely.
2209
+ - ``Presentation.set_transition`` (Phase 4): deck-wide convenience that
2210
+ applies the same transition (or partial update) to every slide in a
2211
+ single call. Accepts ``kind``, ``duration``, ``advance_on_click``,
2212
+ and ``advance_after``; unspecified arguments are left untouched on
2213
+ each slide so callers can bump the duration without disturbing the
2214
+ kind. Passing ``kind=None`` removes the ``<p:transition>`` element
2215
+ on every slide.
2216
+ - ``BaseShape.blur`` and ``BaseShape.reflection`` (Phase 3): two
2217
+ additional non-mutating effect proxies. ``shape.blur`` exposes
2218
+ ``.radius`` (EMU) and ``.grow``; ``shape.reflection`` exposes
2219
+ ``.blur_radius``, ``.distance``, ``.direction``, ``.start_alpha``,
2220
+ and ``.end_alpha``. Reads on a shape with no explicit effect return
2221
+ ``None`` and never mutate the XML; the underlying ``<a:blur>`` /
2222
+ ``<a:reflection>`` element is created on first write and dropped
2223
+ again when the last explicit attribute is cleared, preserving theme
2224
+ inheritance.
2225
+ - New OOXML element classes ``CT_BlurEffect``, ``CT_InnerShadowEffect``,
2226
+ and ``CT_ReflectionEffect`` (Phase 3) registered for ``<a:blur>``,
2227
+ ``<a:innerShdw>``, and ``<a:reflection>`` so PowerPoint-authored
2228
+ effects round-trip without loss even when no high-level proxy is
2229
+ used.
2230
+
2231
+
2232
+ 1.0.2 (2024-08-07)
2233
+ ++++++++++++++++++
2234
+
2235
+ - fix: #1003 restore read-only enum members
2236
+
2237
+ 1.0.1 (2024-08-05)
2238
+ ++++++++++++++++++
2239
+
2240
+ - fix: #1000 add py.typed
2241
+
2242
+
2243
+ 1.0.0 (2024-08-03)
2244
+ ++++++++++++++++++
2245
+
2246
+ - fix: #929 raises on JPEG with image/jpg MIME-type
2247
+ - fix: #943 remove mention of a Px Length subtype
2248
+ - fix: #972 next-slide-id fails in rare cases
2249
+ - fix: #990 do not require strict timestamps for Zip
2250
+ - Add type annotations
2251
+
2252
+
2253
+ 0.6.23 (2023-11-02)
2254
+ +++++++++++++++++++
2255
+
2256
+ - fix: #912 Pillow<=9.5 constraint entails security vulnerability
2257
+
2258
+
2259
+ 0.6.22 (2023-08-28)
2260
+ +++++++++++++++++++
2261
+
2262
+ - Add #909 Add imgW, imgH params to `shapes.add_ole_object()`
2263
+ - fix: #754 _Relationships.items() raises
2264
+ - fix: #758 quote in autoshape name must be escaped
2265
+ - fix: #746 update Python 3.x support in docs
2266
+ - fix: #748 setup's `license` should be short string
2267
+ - fix: #762 AttributeError: module 'collections' has no attribute 'abc'
2268
+ (Windows Python 3.10+)
2269
+
2270
+
2271
+ 0.6.21 (2021-09-20)
2272
+ +++++++++++++++++++
2273
+
2274
+ - Fix #741 _DirPkgReader must implement .__contains__()
2275
+
2276
+
2277
+ 0.6.20 (2021-09-14)
2278
+ +++++++++++++++++++
2279
+
2280
+ - Fix #206 accommodate NULL target-references in relationships.
2281
+ - Fix #223 escape image filename that appears as literal in XML.
2282
+ - Fix #517 option to display chart categories/values in reverse order.
2283
+ - Major refactoring of ancient package loading code.
2284
+
2285
+
2286
+ 0.6.19 (2021-05-17)
2287
+ +++++++++++++++++++
2288
+
2289
+ - Add shapes.add_ole_object(), allowing arbitrary Excel or other binary file to be
2290
+ embedded as a shape on a slide. The OLE object is represented as an icon.
2291
+
2292
+
2293
+ 0.6.18 (2019-05-02)
2294
+ +++++++++++++++++++
2295
+
2296
+ - .text property getters encode line-break as a vertical-tab (VT, '\v', ASCII 11/x0B).
2297
+ This is consistent with PowerPoint's copy/paste behavior and allows like-breaks (soft
2298
+ carriage-return) to be distinguished from paragraph boundary. Previously, a line-break
2299
+ was encoded as a newline ('\n') and was not distinguishable from a paragraph boundary.
2300
+
2301
+ .text properties include Shape.text, _Cell.text, TextFrame.text, _Paragraph.text and
2302
+ _Run.text.
2303
+
2304
+ - .text property setters accept vertical-tab character and place a line-break element in
2305
+ that location. All other control characters other than horizontal-tab ('\t') and
2306
+ newline ('\n') in range \x00-\x1F are accepted and escaped with plain-text like
2307
+ "_x001B" for ESC (ASCII 27).
2308
+
2309
+ Previously a control character other than tab or newline in an assigned string would
2310
+ trigger an exception related to invalid XML character.
2311
+
2312
+
2313
+ 0.6.17 (2018-12-16)
2314
+ +++++++++++++++++++
2315
+
2316
+ - Add SlideLayouts.remove() - Delete unused slide-layout
2317
+ - Add SlideLayout.used_by_slides - Get slides based on this slide-layout
2318
+ - Add SlideLayouts.index() - Get index of slide-layout in master
2319
+ - Add SlideLayouts.get_by_name() - Get slide-layout by its str name
2320
+
2321
+
2322
+ 0.6.16 (2018-11-09)
2323
+ +++++++++++++++++++
2324
+
2325
+ - Feature #395 DataLabels.show_* properties, e.g. .show_percentage
2326
+ - Feature #453 Chart data tolerates None for labels
2327
+
2328
+
2329
+ 0.6.15 (2018-09-24)
2330
+ +++++++++++++++++++
2331
+
2332
+ - Fix #436 ValueAxis._cross_xAx fails on c:dateAxis
2333
+
2334
+
2335
+ 0.6.14 (2018-09-24)
2336
+ +++++++++++++++++++
2337
+
2338
+ - Add _Cell.merge()
2339
+ - Add _Cell.split()
2340
+ - Add _Cell.__eq__()
2341
+ - Add _Cell.is_merge_origin
2342
+ - Add _Cell.is_spanned
2343
+ - Add _Cell.span_height
2344
+ - Add _Cell.span_width
2345
+ - Add _Cell.text getter
2346
+ - Add Table.iter_cells()
2347
+ - Move power_pptx.shapes.table module to power_pptx.table
2348
+ - Add user documentation 'Working with tables'
2349
+
2350
+
2351
+ 0.6.13 (2018-09-10)
2352
+ +++++++++++++++++++
2353
+
2354
+ - Add Chart.font
2355
+ - Fix #293 Can't hide title of single-series Chart
2356
+ - Fix shape.width value is not type Emu
2357
+ - Fix add a:defRPr with c:rich (fixes some font inheritance breakage)
2358
+
2359
+
2360
+ 0.6.12 (2018-08-11)
2361
+ +++++++++++++++++++
2362
+
2363
+ - Add Picture.auto_shape_type
2364
+ - Remove Python 2.6 testing from build
2365
+ - Update dependencies to avoid vulnerable Pillow version
2366
+ - Fix #260, #301, #382, #401
2367
+ - Add _Paragraph.add_line_break()
2368
+ - Add Connector.line
2369
+
2370
+
2371
+ 0.6.11 (2018-07-25)
2372
+ +++++++++++++++++++
2373
+
2374
+ - Add gradient fill.
2375
+ - Add experimental "turbo-add" option for producing large shape-count slides.
2376
+
2377
+
2378
+ 0.6.10 (2018-06-11)
2379
+ +++++++++++++++++++
2380
+
2381
+ - Add `shape.shadow` property to autoshape, connector, picture, and group
2382
+ shape, returning a `ShadowFormat` object.
2383
+ - Add `ShadowFormat` object with read/write (boolean) `.inherit` property.
2384
+ - Fix #328 add support for 26+ series in a chart
2385
+
2386
+
2387
+ 0.6.9 (2018-05-08)
2388
+ ++++++++++++++++++
2389
+
2390
+ - Add `Picture.crop_x` setters, allowing picture cropping values to be set,
2391
+ in addition to interrogated.
2392
+ - Add `Slide.background` and `SlideMaster.background`, allowing the
2393
+ background fill to be set for an individual slide or for all slides based
2394
+ on a slide master.
2395
+ - Add option `shapes` parameter to `Shapes.add_group_shape`, allowing a group
2396
+ shape to be formed from a number of existing shapes.
2397
+ - Improve efficiency of `Shapes._next_shape_id` property to improve
2398
+ performance on high shape-count slides.
2399
+
2400
+
2401
+ 0.6.8 (2018-04-18)
2402
+ ++++++++++++++++++
2403
+
2404
+ - Add `GroupShape`, providing properties specific to a group shape, including
2405
+ its `shapes` property.
2406
+ - Add `GroupShapes`, providing access to shapes contained in a group shape.
2407
+ - Add `SlideShapes.add_group_shape()`, allowing a group shape to be added to
2408
+ a slide.
2409
+ - Add `GroupShapes.add_group_shape()`, allowing a group shape to be added to
2410
+ a group shape, enabling recursive, multi-level groups.
2411
+ - Add support for adding jump-to-named-slide behavior to shape and run
2412
+ hyperlinks.
2413
+
2414
+
2415
+ 0.6.7 (2017-10-30)
2416
+ ++++++++++++++++++
2417
+
2418
+ - Add `SlideShapes.build_freeform()`, allowing freeform shapes (such as maps)
2419
+ to be specified and added to a slide.
2420
+ - Add support for patterned fills.
2421
+ - Add `LineFormat.dash_style` to allow interrogation and setting of dashed
2422
+ line styles.
2423
+
2424
+
2425
+ 0.6.6 (2017-06-17)
2426
+ ++++++++++++++++++
2427
+
2428
+ - Add `SlideShapes.add_movie()`, allowing video media to be added to a slide.
2429
+
2430
+ - fix #190 Accommodate non-conforming part names having '00' index segment.
2431
+ - fix #273 Accommodate non-conforming part names having no index segment.
2432
+ - fix #277 ASCII/Unicode error on non-ASCII multi-level category names
2433
+ - fix #279 BaseShape.id warning appearing on placeholder access.
2434
+
2435
+
2436
+ 0.6.5 (2017-03-21)
2437
+ ++++++++++++++++++
2438
+
2439
+ - #267 compensate for non-conforming PowerPoint behavior on c:overlay element
2440
+
2441
+ - compensate for non-conforming (to spec) PowerPoint behavior related to
2442
+ c:dLbl/c:tx that results in "can't save" error when explicit data labels
2443
+ are added to bubbles on a bubble chart.
2444
+
2445
+
2446
+ 0.6.4 (2017-03-17)
2447
+ ++++++++++++++++++
2448
+
2449
+ - add Chart.chart_title and ChartTitle object
2450
+ - #263 Use Number type to test for numeric category
2451
+
2452
+
2453
+ 0.6.3 (2017-02-28)
2454
+ ++++++++++++++++++
2455
+
2456
+ - add DataLabel.font
2457
+ - add Axis.axis_title
2458
+
2459
+
2460
+ 0.6.2 (2017-01-03)
2461
+ ++++++++++++++++++
2462
+
2463
+ - add support for NotesSlide (slide notes, aka. notes page)
2464
+ - add support for arbitrary series ordering in XML
2465
+ - add Plot.categories providing access to hierarchical categories in an
2466
+ existing chart.
2467
+ - add support for date axes on category charts, including writing a dateAx
2468
+ element for the category axis when ChartData categories are date or
2469
+ datetime.
2470
+
2471
+ **BACKWARD INCOMPATIBILITIES:**
2472
+
2473
+ Some changes were made to the boilerplate XML used to create new charts. This
2474
+ was done to more closely adhere to the settings PowerPoint uses when creating
2475
+ a chart using the UI. This may result in some appearance changes in charts
2476
+ after upgrading. In particular:
2477
+
2478
+ * Chart.has_legend now defaults to True for Line charts.
2479
+ * Plot.vary_by_categories now defaults to False for Line charts.
2480
+
2481
+
2482
+ 0.6.1 (2016-10-09)
2483
+ ++++++++++++++++++
2484
+
2485
+ - add Connector shape type
2486
+
2487
+
2488
+ 0.6.0 (2016-08-18)
2489
+ ++++++++++++++++++
2490
+
2491
+ - add XY chart types
2492
+ - add Bubble chart types
2493
+ - add Radar chart types
2494
+ - add Area chart types
2495
+ - add Doughnut chart types
2496
+ - add Series.points and Point
2497
+ - add Point.data_label
2498
+ - add DataLabel.text_frame
2499
+ - add DataLabel.position
2500
+ - add Axis.major_gridlines
2501
+ - add ChartFormat with .fill and .line
2502
+ - add Axis.format (fill and line formatting)
2503
+ - add ValueAxis.crosses and .crosses_at
2504
+ - add Point.format (fill and line formatting)
2505
+ - add Slide.slide_id
2506
+ - add Slides.get() (by slide id)
2507
+ - add Font.language_id
2508
+ - support blank (None) data points in created charts
2509
+ - add Series.marker
2510
+ - add Point.marker
2511
+ - add Marker.format, .style, and .size
2512
+
2513
+
2514
+ 0.5.8 (2015-11-27)
2515
+ ++++++++++++++++++
2516
+
2517
+ - add Shape.click_action (hyperlink on shape)
2518
+ - fix: #128 Chart cat and ser names not escaped
2519
+ - fix: #153 shapes.title raises on no title shape
2520
+ - fix: #170 remove seek(0) from Image.from_file()
2521
+
2522
+
2523
+ 0.5.7 (2015-01-17)
2524
+ ++++++++++++++++++
2525
+
2526
+ - add PicturePlaceholder with .insert_picture() method
2527
+ - add TablePlaceholder with .insert_table() method
2528
+ - add ChartPlaceholder with .insert_chart() method
2529
+ - add Picture.image property, returning Image object
2530
+ - add Picture.crop_left, .crop_top, .crop_right, and .crop_bottom
2531
+ - add Shape.placeholder_format and PlaceholderFormat object
2532
+
2533
+ **BACKWARD INCOMPATIBILITIES:**
2534
+
2535
+ Shape.shape_type is now unconditionally `MSO_SHAPE_TYPE.PLACEHOLDER` for all
2536
+ placeholder shapes. Previously, some placeholder shapes reported
2537
+ `MSO_SHAPE_TYPE.AUTO_SHAPE`, `MSO_SHAPE_TYPE.CHART`,
2538
+ `MSO_SHAPE_TYPE.PICTURE`, or `MSO_SHAPE_TYPE.TABLE` for that property.
2539
+
2540
+
2541
+ 0.5.6 (2014-12-06)
2542
+ ++++++++++++++++++
2543
+
2544
+ - fix #138 - UnicodeDecodeError in setup.py on Windows 7 Python 3.4
2545
+
2546
+
2547
+ 0.5.5 (2014-11-17)
2548
+ ++++++++++++++++++
2549
+
2550
+ - feature #51 - add Python 3 support
2551
+
2552
+
2553
+ 0.5.4 (2014-11-15)
2554
+ ++++++++++++++++++
2555
+
2556
+ - feature #43 - image native size in shapes.add_picture() is now calculated
2557
+ based on DPI attribute in image file, if present, defaulting to 72 dpi.
2558
+ - feature #113 - Add Paragraph.space_before, Paragraph.space_after, and
2559
+ Paragraph.line_spacing
2560
+
2561
+
2562
+ 0.5.3 (2014-11-09)
2563
+ ++++++++++++++++++
2564
+
2565
+ - add experimental feature TextFrame.fit_text()
2566
+
2567
+
2568
+ 0.5.2 (2014-10-26)
2569
+ ++++++++++++++++++
2570
+
2571
+ - fix #127 - Shape.text_frame fails on shape having no txBody
2572
+
2573
+
2574
+ 0.5.1 (2014-09-22)
2575
+ ++++++++++++++++++
2576
+
2577
+ - feature #120 - add Shape.rotation
2578
+ - feature #97 - add Font.underline
2579
+ - issue #117 - add BMP image support
2580
+ - issue #95 - add BaseShape.name setter
2581
+ - issue #107 - all .text properties should return unicode, not str
2582
+ - feature #106 - add .text getters to Shape, TextFrame, and Paragraph
2583
+
2584
+ - Rename Shape.textframe to Shape.text_frame.
2585
+ **Shape.textframe property (by that name) is deprecated.**
2586
+
2587
+
2588
+ 0.5.0 (2014-09-13)
2589
+ ++++++++++++++++++
2590
+
2591
+ - Add support for creating and manipulating bar, column, line, and pie charts
2592
+ - Major refactoring of XML layer (oxml)
2593
+ - Rationalized graphical object shape access
2594
+ **Note backward incompatibilities below**
2595
+
2596
+ **BACKWARD INCOMPATIBILITIES:**
2597
+
2598
+ A table is no longer treated as a shape. Rather it is a graphical object
2599
+ contained in a GraphicFrame shape, as are Chart and SmartArt objects.
2600
+
2601
+ Example::
2602
+
2603
+ table = shapes.add_table(...)
2604
+
2605
+ # becomes
2606
+
2607
+ graphic_frame = shapes.add_table(...)
2608
+ table = graphic_frame.table
2609
+
2610
+ # or
2611
+
2612
+ table = shapes.add_table(...).table
2613
+
2614
+ As the enclosing shape, the id, name, shape type, position, and size are
2615
+ attributes of the enclosing GraphicFrame object.
2616
+
2617
+ The contents of a GraphicFrame shape can be identified using three available
2618
+ properties on a shape: has_table, has_chart, and has_smart_art. The enclosed
2619
+ graphical object is obtained using the properties GraphicFrame.table and
2620
+ GraphicFrame.chart. SmartArt is not yet supported. Accessing one of these
2621
+ properties on a GraphicFrame not containing the corresponding object raises
2622
+ an exception.
2623
+
2624
+
2625
+ 0.4.2 (2014-04-29)
2626
+ ++++++++++++++++++
2627
+
2628
+ - fix: issue #88 -- raises on supported image file having uppercase extension
2629
+ - fix: issue #89 -- raises on add_slide() where non-contiguous existing ids
2630
+
2631
+
2632
+ 0.4.1 (2014-04-29)
2633
+ ++++++++++++++++++
2634
+
2635
+ - Rename Presentation.slidemasters to Presentation.slide_masters.
2636
+ Presentation.slidemasters property is deprecated.
2637
+ - Rename Presentation.slidelayouts to Presentation.slide_layouts.
2638
+ Presentation.slidelayouts property is deprecated.
2639
+ - Rename SlideMaster.slidelayouts to SlideMaster.slide_layouts.
2640
+ SlideMaster.slidelayouts property is deprecated.
2641
+ - Rename SlideLayout.slidemaster to SlideLayout.slide_master.
2642
+ SlideLayout.slidemaster property is deprecated.
2643
+ - Rename Slide.slidelayout to Slide.slide_layout. Slide.slidelayout property
2644
+ is deprecated.
2645
+ - Add SlideMaster.shapes to access shapes on slide master.
2646
+ - Add SlideMaster.placeholders to access placeholder shapes on slide master.
2647
+ - Add _MasterPlaceholder class.
2648
+ - Add _LayoutPlaceholder class with position and size inheritable from master
2649
+ placeholder.
2650
+ - Add _SlidePlaceholder class with position and size inheritable from layout
2651
+ placeholder.
2652
+ - Add Table.left, top, width, and height read/write properties.
2653
+ - Add rudimentary GroupShape with left, top, width, and height properties.
2654
+ - Add rudimentary Connector with left, top, width, and height properties.
2655
+ - Add TextFrame.auto_size property.
2656
+ - Add Presentation.slide_width and .slide_height read/write properties.
2657
+ - Add LineFormat class providing access to read and change line color and
2658
+ width.
2659
+ - Add AutoShape.line
2660
+ - Add Picture.line
2661
+
2662
+ - Rationalize enumerations. **Note backward incompatibilities below**
2663
+
2664
+ **BACKWARD INCOMPATIBILITIES:**
2665
+
2666
+ The following enumerations were moved/renamed during the rationalization of
2667
+ enumerations:
2668
+
2669
+ - ``power_pptx.enum.MSO_COLOR_TYPE`` --> ``power_pptx.enum.dml.MSO_COLOR_TYPE``
2670
+ - ``power_pptx.enum.MSO_FILL`` --> ``power_pptx.enum.dml.MSO_FILL``
2671
+ - ``power_pptx.enum.MSO_THEME_COLOR`` --> ``power_pptx.enum.dml.MSO_THEME_COLOR``
2672
+ - ``power_pptx.constants.MSO.ANCHOR_*`` --> ``power_pptx.enum.text.MSO_ANCHOR.*``
2673
+ - ``power_pptx.constants.MSO_SHAPE`` --> ``power_pptx.enum.shapes.MSO_SHAPE``
2674
+ - ``power_pptx.constants.PP.ALIGN_*`` --> ``power_pptx.enum.text.PP_ALIGN.*``
2675
+ - ``power_pptx.constants.MSO.{SHAPE_TYPES}`` -->
2676
+ ``power_pptx.enum.shapes.MSO_SHAPE_TYPE.*``
2677
+
2678
+ Documentation for all enumerations is available in the Enumerations section
2679
+ of the User Guide.
2680
+
2681
+
2682
+ 0.3.2 (2014-02-07)
2683
+ ++++++++++++++++++
2684
+
2685
+ - Hotfix: issue #80 generated presentations fail to load in Keynote and other
2686
+ Apple applications
2687
+
2688
+
2689
+ 0.3.1 (2014-01-10)
2690
+ ++++++++++++++++++
2691
+
2692
+ - Hotfix: failed to load certain presentations containing images with
2693
+ uppercase extension
2694
+
2695
+
2696
+ 0.3.0 (2013-12-12)
2697
+ ++++++++++++++++++
2698
+
2699
+ - Add read/write font color property supporting RGB, theme color, and inherit
2700
+ color types
2701
+ - Add font typeface and italic support
2702
+ - Add text frame margins and word-wrap
2703
+ - Add support for external relationships, e.g. linked spreadsheet
2704
+ - Add hyperlink support for text run in shape and table cell
2705
+ - Add fill color and brightness for shape and table cell, fill can also be set
2706
+ to transparent (no fill)
2707
+ - Add read/write position and size properties to shape and picture
2708
+ - Replace PIL dependency with Pillow
2709
+ - Restructure modules to better suit size of library
2710
+
2711
+
2712
+ 0.2.6 (2013-06-22)
2713
+ ++++++++++++++++++
2714
+
2715
+ - Add read/write access to core document properties
2716
+ - Hotfix to accomodate connector shapes in _AutoShapeType
2717
+ - Hotfix to allow customXml parts to load when present
2718
+
2719
+
2720
+ 0.2.5 (2013-06-11)
2721
+ ++++++++++++++++++
2722
+
2723
+ - Add paragraph alignment property (left, right, centered, etc.)
2724
+ - Add vertical alignment within table cell (top, middle, bottom)
2725
+ - Add table cell margin properties
2726
+ - Add table boolean properties: first column (row header), first row (column
2727
+ headings), last row (for e.g. totals row), last column (for e.g. row
2728
+ totals), horizontal banding, and vertical banding.
2729
+ - Add support for auto shape adjustment values, e.g. change radius of corner
2730
+ rounding on rounded rectangle, position of callout arrow, etc.
2731
+
2732
+
2733
+ 0.2.4 (2013-05-16)
2734
+ ++++++++++++++++++
2735
+
2736
+ - Add support for auto shapes (e.g. polygons, flowchart symbols, etc.)
2737
+
2738
+
2739
+ 0.2.3 (2013-05-05)
2740
+ ++++++++++++++++++
2741
+
2742
+ - Add support for table shapes
2743
+ - Add indentation support to textbox shapes, enabling multi-level bullets on
2744
+ bullet slides.
2745
+
2746
+
2747
+ 0.2.2 (2013-03-25)
2748
+ ++++++++++++++++++
2749
+
2750
+ - Add support for opening and saving a presentation from/to a file-like
2751
+ object.
2752
+ - Refactor XML handling to use lxml objectify
2753
+
2754
+
2755
+ 0.2.1 (2013-02-25)
2756
+ ++++++++++++++++++
2757
+
2758
+ - Add support for Python 2.6
2759
+ - Add images from a stream (e.g. StringIO) in addition to a path, allowing
2760
+ images retrieved from a database or network resource to be inserted without
2761
+ saving first.
2762
+ - Expand text methods to accept unicode and UTF-8 encoded 8-bit strings.
2763
+ - Fix potential install bug triggered by importing ``__version__`` from
2764
+ package ``__init__.py`` file.
2765
+
2766
+
2767
+ 0.2.0 (2013-02-10)
2768
+ ++++++++++++++++++
2769
+
2770
+ First non-alpha release with basic capabilities:
2771
+
2772
+ - open presentation/template or use built-in default template
2773
+ - add slide
2774
+ - set placeholder text (e.g. bullet slides)
2775
+ - add picture
2776
+ - add text box