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.
- python_pptx2-2.13.0/HISTORY.rst +2776 -0
- python_pptx2-2.13.0/LICENSE +22 -0
- python_pptx2-2.13.0/MANIFEST.in +6 -0
- python_pptx2-2.13.0/PKG-INFO +351 -0
- python_pptx2-2.13.0/README.rst +306 -0
- python_pptx2-2.13.0/features/act-action.feature +88 -0
- python_pptx2-2.13.0/features/act-hyperlink.feature +53 -0
- python_pptx2-2.13.0/features/cht-axis-props.feature +187 -0
- python_pptx2-2.13.0/features/cht-axistitle-props.feature +44 -0
- python_pptx2-2.13.0/features/cht-category-access.feature +72 -0
- python_pptx2-2.13.0/features/cht-category-props.feature +24 -0
- python_pptx2-2.13.0/features/cht-chart.feature +108 -0
- python_pptx2-2.13.0/features/cht-charttitle-props.feature +44 -0
- python_pptx2-2.13.0/features/cht-data-props.feature +107 -0
- python_pptx2-2.13.0/features/cht-datalabels.feature +208 -0
- python_pptx2-2.13.0/features/cht-gridlines-props.feature +11 -0
- python_pptx2-2.13.0/features/cht-legend-access.feature +31 -0
- python_pptx2-2.13.0/features/cht-legend-props.feature +82 -0
- python_pptx2-2.13.0/features/cht-marker-props.feature +59 -0
- python_pptx2-2.13.0/features/cht-plot-props.feature +121 -0
- python_pptx2-2.13.0/features/cht-point-access.feature +37 -0
- python_pptx2-2.13.0/features/cht-point-props.feature +21 -0
- python_pptx2-2.13.0/features/cht-replace-data.feature +40 -0
- python_pptx2-2.13.0/features/cht-series.feature +144 -0
- python_pptx2-2.13.0/features/cht-ticklabels-props.feature +26 -0
- python_pptx2-2.13.0/features/dml-color.feature +22 -0
- python_pptx2-2.13.0/features/dml-effect.feature +29 -0
- python_pptx2-2.13.0/features/dml-fill.feature +106 -0
- python_pptx2-2.13.0/features/dml-line.feature +62 -0
- python_pptx2-2.13.0/features/environment.py +24 -0
- python_pptx2-2.13.0/features/ph-inherit-props.feature +33 -0
- python_pptx2-2.13.0/features/ph-insert-shape.feature +33 -0
- python_pptx2-2.13.0/features/ph-phfmt-props.feature +25 -0
- python_pptx2-2.13.0/features/ph-placeholder-props.feature +53 -0
- python_pptx2-2.13.0/features/prs-coreprops.feature +20 -0
- python_pptx2-2.13.0/features/prs-default-template.feature +9 -0
- python_pptx2-2.13.0/features/prs-open-save.feature +39 -0
- python_pptx2-2.13.0/features/prs-presentation-props.feature +38 -0
- python_pptx2-2.13.0/features/shp-autoshape.feature +26 -0
- python_pptx2-2.13.0/features/shp-connector.feature +83 -0
- python_pptx2-2.13.0/features/shp-freeform.feature +45 -0
- python_pptx2-2.13.0/features/shp-graphicframe.feature +50 -0
- python_pptx2-2.13.0/features/shp-groupshape.feature +46 -0
- python_pptx2-2.13.0/features/shp-movie-props.feature +24 -0
- python_pptx2-2.13.0/features/shp-picture.feature +73 -0
- python_pptx2-2.13.0/features/shp-placeholder.feature +16 -0
- python_pptx2-2.13.0/features/shp-shapes.feature +384 -0
- python_pptx2-2.13.0/features/shp-shared.feature +211 -0
- python_pptx2-2.13.0/features/sld-background.feature +15 -0
- python_pptx2-2.13.0/features/sld-slide.feature +142 -0
- python_pptx2-2.13.0/features/sld-slides.feature +78 -0
- python_pptx2-2.13.0/features/steps/action.py +105 -0
- python_pptx2-2.13.0/features/steps/axis.py +303 -0
- python_pptx2-2.13.0/features/steps/background.py +27 -0
- python_pptx2-2.13.0/features/steps/category.py +157 -0
- python_pptx2-2.13.0/features/steps/chart.py +380 -0
- python_pptx2-2.13.0/features/steps/chartdata.py +210 -0
- python_pptx2-2.13.0/features/steps/color.py +70 -0
- python_pptx2-2.13.0/features/steps/coreprops.py +73 -0
- python_pptx2-2.13.0/features/steps/datalabel.py +195 -0
- python_pptx2-2.13.0/features/steps/effect.py +35 -0
- python_pptx2-2.13.0/features/steps/fill.py +156 -0
- python_pptx2-2.13.0/features/steps/font.py +150 -0
- python_pptx2-2.13.0/features/steps/font_color.py +94 -0
- python_pptx2-2.13.0/features/steps/helpers.py +51 -0
- python_pptx2-2.13.0/features/steps/legend.py +90 -0
- python_pptx2-2.13.0/features/steps/line.py +96 -0
- python_pptx2-2.13.0/features/steps/picture.py +77 -0
- python_pptx2-2.13.0/features/steps/placeholder.py +312 -0
- python_pptx2-2.13.0/features/steps/plot.py +135 -0
- python_pptx2-2.13.0/features/steps/presentation.py +222 -0
- python_pptx2-2.13.0/features/steps/series.py +345 -0
- python_pptx2-2.13.0/features/steps/shape.py +666 -0
- python_pptx2-2.13.0/features/steps/shapes.py +342 -0
- python_pptx2-2.13.0/features/steps/slide.py +206 -0
- python_pptx2-2.13.0/features/steps/slides.py +142 -0
- python_pptx2-2.13.0/features/steps/table.py +305 -0
- python_pptx2-2.13.0/features/steps/test_files/72-dpi.tiff +0 -0
- python_pptx2-2.13.0/features/steps/test_files/CVS_LOGO.WMF +0 -0
- python_pptx2-2.13.0/features/steps/test_files/act-props.pptm +0 -0
- python_pptx2-2.13.0/features/steps/test_files/calibriz.ttf +0 -0
- python_pptx2-2.13.0/features/steps/test_files/cht-axis-props.pptx +0 -0
- python_pptx2-2.13.0/features/steps/test_files/cht-category-access.pptx +0 -0
- python_pptx2-2.13.0/features/steps/test_files/cht-chart-props.pptx +0 -0
- python_pptx2-2.13.0/features/steps/test_files/cht-chart-type.pptx +0 -0
- python_pptx2-2.13.0/features/steps/test_files/cht-charts.pptx +0 -0
- python_pptx2-2.13.0/features/steps/test_files/cht-datalabels.pptx +0 -0
- python_pptx2-2.13.0/features/steps/test_files/cht-gridlines-props.pptx +0 -0
- python_pptx2-2.13.0/features/steps/test_files/cht-legend-props.pptx +0 -0
- python_pptx2-2.13.0/features/steps/test_files/cht-legend.pptx +0 -0
- python_pptx2-2.13.0/features/steps/test_files/cht-marker-props.pptx +0 -0
- python_pptx2-2.13.0/features/steps/test_files/cht-plot-props.pptx +0 -0
- python_pptx2-2.13.0/features/steps/test_files/cht-point-access.pptx +0 -0
- python_pptx2-2.13.0/features/steps/test_files/cht-point-props.pptx +0 -0
- python_pptx2-2.13.0/features/steps/test_files/cht-replace-data.pptx +0 -0
- python_pptx2-2.13.0/features/steps/test_files/cht-series.pptx +0 -0
- python_pptx2-2.13.0/features/steps/test_files/cht-ticklabels-props.pptx +0 -0
- python_pptx2-2.13.0/features/steps/test_files/dml-effect.pptx +0 -0
- python_pptx2-2.13.0/features/steps/test_files/dml-fill.pptx +0 -0
- python_pptx2-2.13.0/features/steps/test_files/dml-line.pptx +0 -0
- python_pptx2-2.13.0/features/steps/test_files/ext-rels.pptx +0 -0
- python_pptx2-2.13.0/features/steps/test_files/extracted-pptx/[Content_Types].xml +27 -0
- python_pptx2-2.13.0/features/steps/test_files/extracted-pptx/_rels/.rels +7 -0
- python_pptx2-2.13.0/features/steps/test_files/extracted-pptx/docProps/app.xml +42 -0
- python_pptx2-2.13.0/features/steps/test_files/extracted-pptx/docProps/core.xml +13 -0
- python_pptx2-2.13.0/features/steps/test_files/extracted-pptx/docProps/thumbnail.jpeg +0 -0
- python_pptx2-2.13.0/features/steps/test_files/extracted-pptx/ppt/_rels/presentation.xml.rels +10 -0
- python_pptx2-2.13.0/features/steps/test_files/extracted-pptx/ppt/presProps.xml +11 -0
- python_pptx2-2.13.0/features/steps/test_files/extracted-pptx/ppt/presentation.xml +106 -0
- python_pptx2-2.13.0/features/steps/test_files/extracted-pptx/ppt/printerSettings/printerSettings1.bin +0 -0
- python_pptx2-2.13.0/features/steps/test_files/extracted-pptx/ppt/slideLayouts/_rels/slideLayout1.xml.rels +4 -0
- python_pptx2-2.13.0/features/steps/test_files/extracted-pptx/ppt/slideLayouts/_rels/slideLayout10.xml.rels +4 -0
- python_pptx2-2.13.0/features/steps/test_files/extracted-pptx/ppt/slideLayouts/_rels/slideLayout11.xml.rels +4 -0
- python_pptx2-2.13.0/features/steps/test_files/extracted-pptx/ppt/slideLayouts/_rels/slideLayout2.xml.rels +4 -0
- python_pptx2-2.13.0/features/steps/test_files/extracted-pptx/ppt/slideLayouts/_rels/slideLayout3.xml.rels +4 -0
- python_pptx2-2.13.0/features/steps/test_files/extracted-pptx/ppt/slideLayouts/_rels/slideLayout4.xml.rels +4 -0
- python_pptx2-2.13.0/features/steps/test_files/extracted-pptx/ppt/slideLayouts/_rels/slideLayout5.xml.rels +4 -0
- python_pptx2-2.13.0/features/steps/test_files/extracted-pptx/ppt/slideLayouts/_rels/slideLayout6.xml.rels +4 -0
- python_pptx2-2.13.0/features/steps/test_files/extracted-pptx/ppt/slideLayouts/_rels/slideLayout7.xml.rels +4 -0
- python_pptx2-2.13.0/features/steps/test_files/extracted-pptx/ppt/slideLayouts/_rels/slideLayout8.xml.rels +4 -0
- python_pptx2-2.13.0/features/steps/test_files/extracted-pptx/ppt/slideLayouts/_rels/slideLayout9.xml.rels +4 -0
- python_pptx2-2.13.0/features/steps/test_files/extracted-pptx/ppt/slideLayouts/slideLayout1.xml +237 -0
- python_pptx2-2.13.0/features/steps/test_files/extracted-pptx/ppt/slideLayouts/slideLayout10.xml +165 -0
- python_pptx2-2.13.0/features/steps/test_files/extracted-pptx/ppt/slideLayouts/slideLayout11.xml +175 -0
- python_pptx2-2.13.0/features/steps/test_files/extracted-pptx/ppt/slideLayouts/slideLayout2.xml +165 -0
- python_pptx2-2.13.0/features/steps/test_files/extracted-pptx/ppt/slideLayouts/slideLayout3.xml +241 -0
- python_pptx2-2.13.0/features/steps/test_files/extracted-pptx/ppt/slideLayouts/slideLayout4.xml +283 -0
- python_pptx2-2.13.0/features/steps/test_files/extracted-pptx/ppt/slideLayouts/slideLayout5.xml +417 -0
- python_pptx2-2.13.0/features/steps/test_files/extracted-pptx/ppt/slideLayouts/slideLayout6.xml +113 -0
- python_pptx2-2.13.0/features/steps/test_files/extracted-pptx/ppt/slideLayouts/slideLayout7.xml +90 -0
- python_pptx2-2.13.0/features/steps/test_files/extracted-pptx/ppt/slideLayouts/slideLayout8.xml +272 -0
- python_pptx2-2.13.0/features/steps/test_files/extracted-pptx/ppt/slideLayouts/slideLayout9.xml +248 -0
- python_pptx2-2.13.0/features/steps/test_files/extracted-pptx/ppt/slideMasters/_rels/slideMaster1.xml.rels +15 -0
- python_pptx2-2.13.0/features/steps/test_files/extracted-pptx/ppt/slideMasters/slideMaster1.xml +505 -0
- python_pptx2-2.13.0/features/steps/test_files/extracted-pptx/ppt/slides/_rels/slide1.xml.rels +4 -0
- python_pptx2-2.13.0/features/steps/test_files/extracted-pptx/ppt/slides/slide1.xml +74 -0
- python_pptx2-2.13.0/features/steps/test_files/extracted-pptx/ppt/tableStyles.xml +2 -0
- python_pptx2-2.13.0/features/steps/test_files/extracted-pptx/ppt/theme/theme1.xml +281 -0
- python_pptx2-2.13.0/features/steps/test_files/extracted-pptx/ppt/viewProps.xml +32 -0
- python_pptx2-2.13.0/features/steps/test_files/font-color.pptx +0 -0
- python_pptx2-2.13.0/features/steps/test_files/just-two-mice.mp4 +0 -0
- python_pptx2-2.13.0/features/steps/test_files/just-two-mice.png +0 -0
- python_pptx2-2.13.0/features/steps/test_files/lyt-shapes.pptx +0 -0
- python_pptx2-2.13.0/features/steps/test_files/minimal.pptx +0 -0
- python_pptx2-2.13.0/features/steps/test_files/monty-truth.png +0 -0
- python_pptx2-2.13.0/features/steps/test_files/mst-placeholders.pptx +0 -0
- python_pptx2-2.13.0/features/steps/test_files/mst-shapes.pptx +0 -0
- python_pptx2-2.13.0/features/steps/test_files/mst-slide-layouts.pptx +0 -0
- python_pptx2-2.13.0/features/steps/test_files/ph-inherit-props.pptx +0 -0
- python_pptx2-2.13.0/features/steps/test_files/ph-populated-placeholders.pptx +0 -0
- python_pptx2-2.13.0/features/steps/test_files/ph-unpopulated-placeholders.pptx +0 -0
- python_pptx2-2.13.0/features/steps/test_files/pic.emf +0 -0
- python_pptx2-2.13.0/features/steps/test_files/prs-add-slide.pptx +0 -0
- python_pptx2-2.13.0/features/steps/test_files/prs-notes.pptx +0 -0
- python_pptx2-2.13.0/features/steps/test_files/prs-properties.pptx +0 -0
- python_pptx2-2.13.0/features/steps/test_files/prs-slide-masters.pptx +0 -0
- python_pptx2-2.13.0/features/steps/test_files/python-icon.jpeg +0 -0
- python_pptx2-2.13.0/features/steps/test_files/python-powered.png +0 -0
- python_pptx2-2.13.0/features/steps/test_files/python.bmp +0 -0
- python_pptx2-2.13.0/features/steps/test_files/shp-access-chart.pptx +0 -0
- python_pptx2-2.13.0/features/steps/test_files/shp-access-ole-object.pptx +0 -0
- python_pptx2-2.13.0/features/steps/test_files/shp-autoshape-adjustments.pptx +0 -0
- python_pptx2-2.13.0/features/steps/test_files/shp-autoshape-props.pptx +0 -0
- python_pptx2-2.13.0/features/steps/test_files/shp-common-props.pptx +0 -0
- python_pptx2-2.13.0/features/steps/test_files/shp-connector-props.pptx +0 -0
- python_pptx2-2.13.0/features/steps/test_files/shp-embedded-docx.docx +0 -0
- python_pptx2-2.13.0/features/steps/test_files/shp-embedded-pptx.pptx +0 -0
- python_pptx2-2.13.0/features/steps/test_files/shp-embedded-xlsx.xlsx +0 -0
- python_pptx2-2.13.0/features/steps/test_files/shp-freeform.pptx +0 -0
- python_pptx2-2.13.0/features/steps/test_files/shp-groupshape.pptx +0 -0
- python_pptx2-2.13.0/features/steps/test_files/shp-movie-props.pptx +0 -0
- python_pptx2-2.13.0/features/steps/test_files/shp-picture.pptx +0 -0
- python_pptx2-2.13.0/features/steps/test_files/shp-pos-and-size.pptx +0 -0
- python_pptx2-2.13.0/features/steps/test_files/shp-shapes.pptx +0 -0
- python_pptx2-2.13.0/features/steps/test_files/sld-background.pptx +0 -0
- python_pptx2-2.13.0/features/steps/test_files/sld-blank.pptx +0 -0
- python_pptx2-2.13.0/features/steps/test_files/sld-notes.pptx +0 -0
- python_pptx2-2.13.0/features/steps/test_files/sld-slide.pptx +0 -0
- python_pptx2-2.13.0/features/steps/test_files/sld-slides.pptx +0 -0
- python_pptx2-2.13.0/features/steps/test_files/sonic.gif +0 -0
- python_pptx2-2.13.0/features/steps/test_files/tbl-cell.pptx +0 -0
- python_pptx2-2.13.0/features/steps/test_files/test-image-jpg-mime.pptx +0 -0
- python_pptx2-2.13.0/features/steps/test_files/test.pptx +0 -0
- python_pptx2-2.13.0/features/steps/test_files/txt-fit-text.pptx +0 -0
- python_pptx2-2.13.0/features/steps/test_files/txt-font-props.pptx +0 -0
- python_pptx2-2.13.0/features/steps/test_files/txt-font-typeface.pptx +0 -0
- python_pptx2-2.13.0/features/steps/test_files/txt-paragraph-spacing.pptx +0 -0
- python_pptx2-2.13.0/features/steps/test_files/txt-text-frame.pptx +0 -0
- python_pptx2-2.13.0/features/steps/test_files/txt-text.pptx +0 -0
- python_pptx2-2.13.0/features/steps/text.py +248 -0
- python_pptx2-2.13.0/features/steps/text_frame.py +129 -0
- python_pptx2-2.13.0/features/tbl-cell.feature +113 -0
- python_pptx2-2.13.0/features/tbl-column.feature +10 -0
- python_pptx2-2.13.0/features/tbl-table.feature +60 -0
- python_pptx2-2.13.0/features/txt-fit-text.feature +11 -0
- python_pptx2-2.13.0/features/txt-font-color.feature +55 -0
- python_pptx2-2.13.0/features/txt-font-props.feature +149 -0
- python_pptx2-2.13.0/features/txt-paragraph.feature +95 -0
- python_pptx2-2.13.0/features/txt-text.feature +23 -0
- python_pptx2-2.13.0/features/txt-textframe.feature +75 -0
- python_pptx2-2.13.0/pyproject.toml +178 -0
- python_pptx2-2.13.0/setup.cfg +4 -0
- python_pptx2-2.13.0/src/pptx2/__init__.py +152 -0
- python_pptx2-2.13.0/src/pptx2/_color.py +75 -0
- python_pptx2-2.13.0/src/pptx2/_slide_importer.py +597 -0
- python_pptx2-2.13.0/src/pptx2/_svg.py +155 -0
- python_pptx2-2.13.0/src/pptx2/_template_applier.py +292 -0
- python_pptx2-2.13.0/src/pptx2/_textstyle.py +187 -0
- python_pptx2-2.13.0/src/pptx2/accessibility.py +365 -0
- python_pptx2-2.13.0/src/pptx2/action.py +270 -0
- python_pptx2-2.13.0/src/pptx2/animation.py +2237 -0
- python_pptx2-2.13.0/src/pptx2/api.py +49 -0
- python_pptx2-2.13.0/src/pptx2/audit.py +258 -0
- python_pptx2-2.13.0/src/pptx2/chart/__init__.py +0 -0
- python_pptx2-2.13.0/src/pptx2/chart/analytics.py +381 -0
- python_pptx2-2.13.0/src/pptx2/chart/axis.py +543 -0
- python_pptx2-2.13.0/src/pptx2/chart/category.py +200 -0
- python_pptx2-2.13.0/src/pptx2/chart/chart.py +670 -0
- python_pptx2-2.13.0/src/pptx2/chart/data.py +864 -0
- python_pptx2-2.13.0/src/pptx2/chart/datalabel.py +406 -0
- python_pptx2-2.13.0/src/pptx2/chart/legend.py +86 -0
- python_pptx2-2.13.0/src/pptx2/chart/marker.py +70 -0
- python_pptx2-2.13.0/src/pptx2/chart/palettes.py +129 -0
- python_pptx2-2.13.0/src/pptx2/chart/plot.py +462 -0
- python_pptx2-2.13.0/src/pptx2/chart/point.py +101 -0
- python_pptx2-2.13.0/src/pptx2/chart/quick_layouts.py +325 -0
- python_pptx2-2.13.0/src/pptx2/chart/series.py +334 -0
- python_pptx2-2.13.0/src/pptx2/chart/xlsx.py +272 -0
- python_pptx2-2.13.0/src/pptx2/chart/xmlwriter.py +1845 -0
- python_pptx2-2.13.0/src/pptx2/compose/__init__.py +28 -0
- python_pptx2-2.13.0/src/pptx2/compose/from_spec.py +1094 -0
- python_pptx2-2.13.0/src/pptx2/design/__init__.py +8 -0
- python_pptx2-2.13.0/src/pptx2/design/components.py +607 -0
- python_pptx2-2.13.0/src/pptx2/design/figures.py +389 -0
- python_pptx2-2.13.0/src/pptx2/design/layout.py +370 -0
- python_pptx2-2.13.0/src/pptx2/design/recipes.py +1967 -0
- python_pptx2-2.13.0/src/pptx2/design/style.py +209 -0
- python_pptx2-2.13.0/src/pptx2/design/tokens.py +915 -0
- python_pptx2-2.13.0/src/pptx2/diagrams.py +754 -0
- python_pptx2-2.13.0/src/pptx2/dml/__init__.py +0 -0
- python_pptx2-2.13.0/src/pptx2/dml/chtfmt.py +40 -0
- python_pptx2-2.13.0/src/pptx2/dml/color.py +496 -0
- python_pptx2-2.13.0/src/pptx2/dml/effect.py +909 -0
- python_pptx2-2.13.0/src/pptx2/dml/fill.py +691 -0
- python_pptx2-2.13.0/src/pptx2/dml/line.py +287 -0
- python_pptx2-2.13.0/src/pptx2/dml/picture.py +212 -0
- python_pptx2-2.13.0/src/pptx2/dml/three_d.py +381 -0
- python_pptx2-2.13.0/src/pptx2/enum/__init__.py +0 -0
- python_pptx2-2.13.0/src/pptx2/enum/action.py +71 -0
- python_pptx2-2.13.0/src/pptx2/enum/animation.py +31 -0
- python_pptx2-2.13.0/src/pptx2/enum/base.py +218 -0
- python_pptx2-2.13.0/src/pptx2/enum/chart.py +574 -0
- python_pptx2-2.13.0/src/pptx2/enum/dml.py +740 -0
- python_pptx2-2.13.0/src/pptx2/enum/lang.py +685 -0
- python_pptx2-2.13.0/src/pptx2/enum/presentation.py +133 -0
- python_pptx2-2.13.0/src/pptx2/enum/shapes.py +1029 -0
- python_pptx2-2.13.0/src/pptx2/enum/text.py +230 -0
- python_pptx2-2.13.0/src/pptx2/exc.py +42 -0
- python_pptx2-2.13.0/src/pptx2/formats.py +139 -0
- python_pptx2-2.13.0/src/pptx2/geometry.py +420 -0
- python_pptx2-2.13.0/src/pptx2/inherit.py +109 -0
- python_pptx2-2.13.0/src/pptx2/lint.py +2256 -0
- python_pptx2-2.13.0/src/pptx2/math.py +177 -0
- python_pptx2-2.13.0/src/pptx2/media.py +197 -0
- python_pptx2-2.13.0/src/pptx2/opc/__init__.py +0 -0
- python_pptx2-2.13.0/src/pptx2/opc/constants.py +332 -0
- python_pptx2-2.13.0/src/pptx2/opc/oxml.py +188 -0
- python_pptx2-2.13.0/src/pptx2/opc/package.py +762 -0
- python_pptx2-2.13.0/src/pptx2/opc/packuri.py +109 -0
- python_pptx2-2.13.0/src/pptx2/opc/serialized.py +296 -0
- python_pptx2-2.13.0/src/pptx2/opc/shared.py +20 -0
- python_pptx2-2.13.0/src/pptx2/opc/spec.py +45 -0
- python_pptx2-2.13.0/src/pptx2/oxml/__init__.py +555 -0
- python_pptx2-2.13.0/src/pptx2/oxml/action.py +53 -0
- python_pptx2-2.13.0/src/pptx2/oxml/chart/__init__.py +0 -0
- python_pptx2-2.13.0/src/pptx2/oxml/chart/axis.py +337 -0
- python_pptx2-2.13.0/src/pptx2/oxml/chart/chart.py +481 -0
- python_pptx2-2.13.0/src/pptx2/oxml/chart/datalabel.py +253 -0
- python_pptx2-2.13.0/src/pptx2/oxml/chart/legend.py +72 -0
- python_pptx2-2.13.0/src/pptx2/oxml/chart/marker.py +61 -0
- python_pptx2-2.13.0/src/pptx2/oxml/chart/plot.py +365 -0
- python_pptx2-2.13.0/src/pptx2/oxml/chart/series.py +425 -0
- python_pptx2-2.13.0/src/pptx2/oxml/chart/shared.py +220 -0
- python_pptx2-2.13.0/src/pptx2/oxml/coreprops.py +288 -0
- python_pptx2-2.13.0/src/pptx2/oxml/dml/__init__.py +0 -0
- python_pptx2-2.13.0/src/pptx2/oxml/dml/color.py +135 -0
- python_pptx2-2.13.0/src/pptx2/oxml/dml/effect.py +213 -0
- python_pptx2-2.13.0/src/pptx2/oxml/dml/fill.py +316 -0
- python_pptx2-2.13.0/src/pptx2/oxml/dml/line.py +12 -0
- python_pptx2-2.13.0/src/pptx2/oxml/dml/three_d.py +110 -0
- python_pptx2-2.13.0/src/pptx2/oxml/ns.py +135 -0
- python_pptx2-2.13.0/src/pptx2/oxml/presentation.py +313 -0
- python_pptx2-2.13.0/src/pptx2/oxml/shapes/__init__.py +19 -0
- python_pptx2-2.13.0/src/pptx2/oxml/shapes/autoshape.py +467 -0
- python_pptx2-2.13.0/src/pptx2/oxml/shapes/connector.py +107 -0
- python_pptx2-2.13.0/src/pptx2/oxml/shapes/graphfrm.py +347 -0
- python_pptx2-2.13.0/src/pptx2/oxml/shapes/groupshape.py +329 -0
- python_pptx2-2.13.0/src/pptx2/oxml/shapes/picture.py +270 -0
- python_pptx2-2.13.0/src/pptx2/oxml/shapes/shared.py +577 -0
- python_pptx2-2.13.0/src/pptx2/oxml/simpletypes.py +1027 -0
- python_pptx2-2.13.0/src/pptx2/oxml/slide.py +563 -0
- python_pptx2-2.13.0/src/pptx2/oxml/table.py +650 -0
- python_pptx2-2.13.0/src/pptx2/oxml/text.py +815 -0
- python_pptx2-2.13.0/src/pptx2/oxml/theme.py +36 -0
- python_pptx2-2.13.0/src/pptx2/oxml/xmlchemy.py +717 -0
- python_pptx2-2.13.0/src/pptx2/package.py +222 -0
- python_pptx2-2.13.0/src/pptx2/parts/__init__.py +0 -0
- python_pptx2-2.13.0/src/pptx2/parts/chart.py +95 -0
- python_pptx2-2.13.0/src/pptx2/parts/coreprops.py +167 -0
- python_pptx2-2.13.0/src/pptx2/parts/diagram.py +37 -0
- python_pptx2-2.13.0/src/pptx2/parts/embeddedpackage.py +93 -0
- python_pptx2-2.13.0/src/pptx2/parts/image.py +275 -0
- python_pptx2-2.13.0/src/pptx2/parts/media.py +37 -0
- python_pptx2-2.13.0/src/pptx2/parts/presentation.py +136 -0
- python_pptx2-2.13.0/src/pptx2/parts/slide.py +371 -0
- python_pptx2-2.13.0/src/pptx2/presentation.py +408 -0
- python_pptx2-2.13.0/src/pptx2/py.typed +0 -0
- python_pptx2-2.13.0/src/pptx2/render.py +586 -0
- python_pptx2-2.13.0/src/pptx2/section.py +272 -0
- python_pptx2-2.13.0/src/pptx2/shapes/__init__.py +26 -0
- python_pptx2-2.13.0/src/pptx2/shapes/autoshape.py +442 -0
- python_pptx2-2.13.0/src/pptx2/shapes/base.py +1078 -0
- python_pptx2-2.13.0/src/pptx2/shapes/connector.py +297 -0
- python_pptx2-2.13.0/src/pptx2/shapes/freeform.py +337 -0
- python_pptx2-2.13.0/src/pptx2/shapes/graphfrm.py +316 -0
- python_pptx2-2.13.0/src/pptx2/shapes/group.py +264 -0
- python_pptx2-2.13.0/src/pptx2/shapes/picture.py +422 -0
- python_pptx2-2.13.0/src/pptx2/shapes/placeholder.py +468 -0
- python_pptx2-2.13.0/src/pptx2/shapes/shapetree.py +2027 -0
- python_pptx2-2.13.0/src/pptx2/shared.py +82 -0
- python_pptx2-2.13.0/src/pptx2/skill/SKILL.md +450 -0
- python_pptx2-2.13.0/src/pptx2/skill/__init__.py +78 -0
- python_pptx2-2.13.0/src/pptx2/skill/__main__.py +64 -0
- python_pptx2-2.13.0/src/pptx2/skill/references/animations.md +189 -0
- python_pptx2-2.13.0/src/pptx2/skill/references/basics.md +421 -0
- python_pptx2-2.13.0/src/pptx2/skill/references/charts.md +254 -0
- python_pptx2-2.13.0/src/pptx2/skill/references/compose.md +234 -0
- python_pptx2-2.13.0/src/pptx2/skill/references/design.md +366 -0
- python_pptx2-2.13.0/src/pptx2/skill/references/effects.md +249 -0
- python_pptx2-2.13.0/src/pptx2/skill/references/end-to-end-deck.md +231 -0
- python_pptx2-2.13.0/src/pptx2/skill/references/geometry-and-arrows.md +334 -0
- python_pptx2-2.13.0/src/pptx2/skill/references/lint.md +275 -0
- python_pptx2-2.13.0/src/pptx2/skill/references/math.md +86 -0
- python_pptx2-2.13.0/src/pptx2/skill/references/picture-effects.md +129 -0
- python_pptx2-2.13.0/src/pptx2/skill/references/render.md +151 -0
- python_pptx2-2.13.0/src/pptx2/skill/references/smart-art.md +75 -0
- python_pptx2-2.13.0/src/pptx2/skill/references/space-aware-authoring.md +249 -0
- python_pptx2-2.13.0/src/pptx2/skill/references/tables.md +244 -0
- python_pptx2-2.13.0/src/pptx2/skill/references/theme.md +127 -0
- python_pptx2-2.13.0/src/pptx2/skill/references/three-d.md +109 -0
- python_pptx2-2.13.0/src/pptx2/skill/references/transitions.md +100 -0
- python_pptx2-2.13.0/src/pptx2/slide.py +1244 -0
- python_pptx2-2.13.0/src/pptx2/smart_art.py +220 -0
- python_pptx2-2.13.0/src/pptx2/spec.py +633 -0
- python_pptx2-2.13.0/src/pptx2/table.py +1181 -0
- python_pptx2-2.13.0/src/pptx2/table_styles.py +184 -0
- python_pptx2-2.13.0/src/pptx2/templates/default.pptx +0 -0
- python_pptx2-2.13.0/src/pptx2/templates/docx-icon.emf +0 -0
- python_pptx2-2.13.0/src/pptx2/templates/generic-icon.emf +0 -0
- python_pptx2-2.13.0/src/pptx2/templates/notes.xml +23 -0
- python_pptx2-2.13.0/src/pptx2/templates/notesMaster.xml +352 -0
- python_pptx2-2.13.0/src/pptx2/templates/pptx-icon.emf +0 -0
- python_pptx2-2.13.0/src/pptx2/templates/theme.xml +321 -0
- python_pptx2-2.13.0/src/pptx2/templates/xlsx-icon.emf +0 -0
- python_pptx2-2.13.0/src/pptx2/text/__init__.py +0 -0
- python_pptx2-2.13.0/src/pptx2/text/fonts.py +482 -0
- python_pptx2-2.13.0/src/pptx2/text/layout.py +374 -0
- python_pptx2-2.13.0/src/pptx2/text/text.py +1272 -0
- python_pptx2-2.13.0/src/pptx2/theme.py +721 -0
- python_pptx2-2.13.0/src/pptx2/types.py +36 -0
- python_pptx2-2.13.0/src/pptx2/util.py +263 -0
- python_pptx2-2.13.0/src/python_pptx2.egg-info/PKG-INFO +351 -0
- python_pptx2-2.13.0/src/python_pptx2.egg-info/SOURCES.txt +609 -0
- python_pptx2-2.13.0/src/python_pptx2.egg-info/dependency_links.txt +1 -0
- python_pptx2-2.13.0/src/python_pptx2.egg-info/entry_points.txt +3 -0
- python_pptx2-2.13.0/src/python_pptx2.egg-info/requires.txt +8 -0
- python_pptx2-2.13.0/src/python_pptx2.egg-info/top_level.txt +1 -0
- python_pptx2-2.13.0/tests/__init__.py +0 -0
- python_pptx2-2.13.0/tests/chart/__init__.py +0 -0
- python_pptx2-2.13.0/tests/chart/test_analytics.py +352 -0
- python_pptx2-2.13.0/tests/chart/test_axis.py +1268 -0
- python_pptx2-2.13.0/tests/chart/test_category.py +249 -0
- python_pptx2-2.13.0/tests/chart/test_chart.py +941 -0
- python_pptx2-2.13.0/tests/chart/test_chart_toggles.py +128 -0
- python_pptx2-2.13.0/tests/chart/test_collision_strategy.py +128 -0
- python_pptx2-2.13.0/tests/chart/test_data.py +893 -0
- python_pptx2-2.13.0/tests/chart/test_datalabel.py +669 -0
- python_pptx2-2.13.0/tests/chart/test_legend.py +187 -0
- python_pptx2-2.13.0/tests/chart/test_marker.py +134 -0
- python_pptx2-2.13.0/tests/chart/test_palettes.py +60 -0
- python_pptx2-2.13.0/tests/chart/test_plot.py +598 -0
- python_pptx2-2.13.0/tests/chart/test_point.py +221 -0
- python_pptx2-2.13.0/tests/chart/test_quick_layouts.py +202 -0
- python_pptx2-2.13.0/tests/chart/test_series.py +551 -0
- python_pptx2-2.13.0/tests/chart/test_xlsx.py +425 -0
- python_pptx2-2.13.0/tests/chart/test_xmlwriter.py +783 -0
- python_pptx2-2.13.0/tests/design/__init__.py +0 -0
- python_pptx2-2.13.0/tests/design/test_components.py +331 -0
- python_pptx2-2.13.0/tests/design/test_figures.py +225 -0
- python_pptx2-2.13.0/tests/design/test_layout.py +259 -0
- python_pptx2-2.13.0/tests/design/test_recipes.py +686 -0
- python_pptx2-2.13.0/tests/design/test_style.py +94 -0
- python_pptx2-2.13.0/tests/design/test_tokens.py +296 -0
- python_pptx2-2.13.0/tests/dml/__init__.py +0 -0
- python_pptx2-2.13.0/tests/dml/test_chtfmt.py +87 -0
- python_pptx2-2.13.0/tests/dml/test_color.py +389 -0
- python_pptx2-2.13.0/tests/dml/test_effect.py +553 -0
- python_pptx2-2.13.0/tests/dml/test_fill.py +980 -0
- python_pptx2-2.13.0/tests/dml/test_line.py +337 -0
- python_pptx2-2.13.0/tests/dml/test_three_d.py +327 -0
- python_pptx2-2.13.0/tests/enum/__init__.py +0 -0
- python_pptx2-2.13.0/tests/enum/test_base.py +107 -0
- python_pptx2-2.13.0/tests/enum/test_shapes.py +46 -0
- python_pptx2-2.13.0/tests/integration/__init__.py +1 -0
- python_pptx2-2.13.0/tests/integration/round_trip.py +90 -0
- python_pptx2-2.13.0/tests/integration/test_apply_template.py +175 -0
- python_pptx2-2.13.0/tests/integration/test_compose_package.py +44 -0
- python_pptx2-2.13.0/tests/integration/test_import_slide.py +473 -0
- python_pptx2-2.13.0/tests/integration/test_round_trip.py +244 -0
- python_pptx2-2.13.0/tests/opc/__init__.py +0 -0
- python_pptx2-2.13.0/tests/opc/test_oxml.py +211 -0
- python_pptx2-2.13.0/tests/opc/test_package.py +1037 -0
- python_pptx2-2.13.0/tests/opc/test_packuri.py +94 -0
- python_pptx2-2.13.0/tests/opc/test_serialized.py +410 -0
- python_pptx2-2.13.0/tests/oxml/__init__.py +0 -0
- python_pptx2-2.13.0/tests/oxml/shapes/__init__.py +0 -0
- python_pptx2-2.13.0/tests/oxml/shapes/test_autoshape.py +266 -0
- python_pptx2-2.13.0/tests/oxml/shapes/test_graphfrm.py +80 -0
- python_pptx2-2.13.0/tests/oxml/shapes/test_groupshape.py +297 -0
- python_pptx2-2.13.0/tests/oxml/shapes/test_picture.py +107 -0
- python_pptx2-2.13.0/tests/oxml/test___init__.py +105 -0
- python_pptx2-2.13.0/tests/oxml/test_dml.py +152 -0
- python_pptx2-2.13.0/tests/oxml/test_ns.py +94 -0
- python_pptx2-2.13.0/tests/oxml/test_presentation.py +63 -0
- python_pptx2-2.13.0/tests/oxml/test_simpletypes.py +283 -0
- python_pptx2-2.13.0/tests/oxml/test_slide.py +23 -0
- python_pptx2-2.13.0/tests/oxml/test_table.py +255 -0
- python_pptx2-2.13.0/tests/oxml/test_theme.py +23 -0
- python_pptx2-2.13.0/tests/oxml/test_xmlchemy.py +604 -0
- python_pptx2-2.13.0/tests/oxml/unitdata/__init__.py +0 -0
- python_pptx2-2.13.0/tests/oxml/unitdata/dml.py +102 -0
- python_pptx2-2.13.0/tests/oxml/unitdata/shape.py +215 -0
- python_pptx2-2.13.0/tests/oxml/unitdata/text.py +85 -0
- python_pptx2-2.13.0/tests/parts/__init__.py +0 -0
- python_pptx2-2.13.0/tests/parts/test_chart.py +152 -0
- python_pptx2-2.13.0/tests/parts/test_coreprops.py +198 -0
- python_pptx2-2.13.0/tests/parts/test_embeddedpackage.py +74 -0
- python_pptx2-2.13.0/tests/parts/test_image.py +246 -0
- python_pptx2-2.13.0/tests/parts/test_media.py +33 -0
- python_pptx2-2.13.0/tests/parts/test_presentation.py +238 -0
- python_pptx2-2.13.0/tests/parts/test_slide.py +589 -0
- python_pptx2-2.13.0/tests/schema/__init__.py +1 -0
- python_pptx2-2.13.0/tests/schema/oxml_schema_validator.py +452 -0
- python_pptx2-2.13.0/tests/schema/test_schema_validity.py +821 -0
- python_pptx2-2.13.0/tests/shapes/__init__.py +0 -0
- python_pptx2-2.13.0/tests/shapes/test_anchor.py +297 -0
- python_pptx2-2.13.0/tests/shapes/test_animate.py +71 -0
- python_pptx2-2.13.0/tests/shapes/test_autoshape.py +606 -0
- python_pptx2-2.13.0/tests/shapes/test_base.py +643 -0
- python_pptx2-2.13.0/tests/shapes/test_connector.py +427 -0
- python_pptx2-2.13.0/tests/shapes/test_freeform.py +565 -0
- python_pptx2-2.13.0/tests/shapes/test_graphfrm.py +175 -0
- python_pptx2-2.13.0/tests/shapes/test_group.py +235 -0
- python_pptx2-2.13.0/tests/shapes/test_picture.py +329 -0
- python_pptx2-2.13.0/tests/shapes/test_placeholder.py +637 -0
- python_pptx2-2.13.0/tests/shapes/test_shapetree.py +2359 -0
- python_pptx2-2.13.0/tests/test_accessibility.py +279 -0
- python_pptx2-2.13.0/tests/test_action.py +373 -0
- python_pptx2-2.13.0/tests/test_animation.py +758 -0
- python_pptx2-2.13.0/tests/test_api.py +87 -0
- python_pptx2-2.13.0/tests/test_audit.py +105 -0
- python_pptx2-2.13.0/tests/test_compose_from_spec.py +855 -0
- python_pptx2-2.13.0/tests/test_diagrams.py +186 -0
- python_pptx2-2.13.0/tests/test_files/calibriz.ttf +0 -0
- python_pptx2-2.13.0/tests/test_files/cdw-logo.eps +420 -0
- python_pptx2-2.13.0/tests/test_files/dummy.mp4 +1 -0
- python_pptx2-2.13.0/tests/test_files/expanded_pptx/[Content_Types].xml +27 -0
- python_pptx2-2.13.0/tests/test_files/expanded_pptx/_rels/.rels +7 -0
- python_pptx2-2.13.0/tests/test_files/expanded_pptx/docProps/app.xml +41 -0
- python_pptx2-2.13.0/tests/test_files/expanded_pptx/docProps/core.xml +13 -0
- python_pptx2-2.13.0/tests/test_files/expanded_pptx/docProps/thumbnail.jpeg +0 -0
- python_pptx2-2.13.0/tests/test_files/expanded_pptx/ppt/_rels/presentation.xml.rels +10 -0
- python_pptx2-2.13.0/tests/test_files/expanded_pptx/ppt/presProps.xml +4 -0
- python_pptx2-2.13.0/tests/test_files/expanded_pptx/ppt/presentation.xml +109 -0
- python_pptx2-2.13.0/tests/test_files/expanded_pptx/ppt/printerSettings/printerSettings1.bin +0 -0
- python_pptx2-2.13.0/tests/test_files/expanded_pptx/ppt/slideLayouts/_rels/slideLayout1.xml.rels +4 -0
- python_pptx2-2.13.0/tests/test_files/expanded_pptx/ppt/slideLayouts/_rels/slideLayout10.xml.rels +4 -0
- python_pptx2-2.13.0/tests/test_files/expanded_pptx/ppt/slideLayouts/_rels/slideLayout11.xml.rels +4 -0
- python_pptx2-2.13.0/tests/test_files/expanded_pptx/ppt/slideLayouts/_rels/slideLayout2.xml.rels +4 -0
- python_pptx2-2.13.0/tests/test_files/expanded_pptx/ppt/slideLayouts/_rels/slideLayout3.xml.rels +4 -0
- python_pptx2-2.13.0/tests/test_files/expanded_pptx/ppt/slideLayouts/_rels/slideLayout4.xml.rels +4 -0
- python_pptx2-2.13.0/tests/test_files/expanded_pptx/ppt/slideLayouts/_rels/slideLayout5.xml.rels +4 -0
- python_pptx2-2.13.0/tests/test_files/expanded_pptx/ppt/slideLayouts/_rels/slideLayout6.xml.rels +4 -0
- python_pptx2-2.13.0/tests/test_files/expanded_pptx/ppt/slideLayouts/_rels/slideLayout7.xml.rels +4 -0
- python_pptx2-2.13.0/tests/test_files/expanded_pptx/ppt/slideLayouts/_rels/slideLayout8.xml.rels +4 -0
- python_pptx2-2.13.0/tests/test_files/expanded_pptx/ppt/slideLayouts/_rels/slideLayout9.xml.rels +4 -0
- python_pptx2-2.13.0/tests/test_files/expanded_pptx/ppt/slideLayouts/slideLayout1.xml +242 -0
- python_pptx2-2.13.0/tests/test_files/expanded_pptx/ppt/slideLayouts/slideLayout10.xml +165 -0
- python_pptx2-2.13.0/tests/test_files/expanded_pptx/ppt/slideLayouts/slideLayout11.xml +175 -0
- python_pptx2-2.13.0/tests/test_files/expanded_pptx/ppt/slideLayouts/slideLayout2.xml +170 -0
- python_pptx2-2.13.0/tests/test_files/expanded_pptx/ppt/slideLayouts/slideLayout3.xml +246 -0
- python_pptx2-2.13.0/tests/test_files/expanded_pptx/ppt/slideLayouts/slideLayout4.xml +288 -0
- python_pptx2-2.13.0/tests/test_files/expanded_pptx/ppt/slideLayouts/slideLayout5.xml +422 -0
- python_pptx2-2.13.0/tests/test_files/expanded_pptx/ppt/slideLayouts/slideLayout6.xml +118 -0
- python_pptx2-2.13.0/tests/test_files/expanded_pptx/ppt/slideLayouts/slideLayout7.xml +95 -0
- python_pptx2-2.13.0/tests/test_files/expanded_pptx/ppt/slideLayouts/slideLayout8.xml +277 -0
- python_pptx2-2.13.0/tests/test_files/expanded_pptx/ppt/slideLayouts/slideLayout9.xml +253 -0
- python_pptx2-2.13.0/tests/test_files/expanded_pptx/ppt/slideMasters/_rels/slideMaster1.xml.rels +15 -0
- python_pptx2-2.13.0/tests/test_files/expanded_pptx/ppt/slideMasters/slideMaster1.xml +507 -0
- python_pptx2-2.13.0/tests/test_files/expanded_pptx/ppt/slides/_rels/slide1.xml.rels +4 -0
- python_pptx2-2.13.0/tests/test_files/expanded_pptx/ppt/slides/slide1.xml +76 -0
- python_pptx2-2.13.0/tests/test_files/expanded_pptx/ppt/tableStyles.xml +2 -0
- python_pptx2-2.13.0/tests/test_files/expanded_pptx/ppt/theme/theme1.xml +281 -0
- python_pptx2-2.13.0/tests/test_files/expanded_pptx/ppt/viewProps.xml +34 -0
- python_pptx2-2.13.0/tests/test_files/minimal.pptx +0 -0
- python_pptx2-2.13.0/tests/test_files/minimal_slide.xml +15 -0
- python_pptx2-2.13.0/tests/test_files/missing_rels_item.pptx +0 -0
- python_pptx2-2.13.0/tests/test_files/monty-truth.png +0 -0
- python_pptx2-2.13.0/tests/test_files/no-core-props.pptx +0 -0
- python_pptx2-2.13.0/tests/test_files/no-slides.pptx +0 -0
- python_pptx2-2.13.0/tests/test_files/presentation.xml +109 -0
- python_pptx2-2.13.0/tests/test_files/python-icon.jpeg +0 -0
- python_pptx2-2.13.0/tests/test_files/python-powered.png +0 -0
- python_pptx2-2.13.0/tests/test_files/python.bmp +0 -0
- python_pptx2-2.13.0/tests/test_files/slideLayout1.xml +265 -0
- python_pptx2-2.13.0/tests/test_files/smart_art_org_chart.pptx +0 -0
- python_pptx2-2.13.0/tests/test_files/snippets/2x2-area-date.txt +166 -0
- python_pptx2-2.13.0/tests/test_files/snippets/2x2-area-float.txt +167 -0
- python_pptx2-2.13.0/tests/test_files/snippets/2x2-area-stacked-100.txt +165 -0
- python_pptx2-2.13.0/tests/test_files/snippets/2x2-area-stacked.txt +165 -0
- python_pptx2-2.13.0/tests/test_files/snippets/2x2-area.txt +165 -0
- python_pptx2-2.13.0/tests/test_files/snippets/2x2-bar-clustered-date.txt +145 -0
- python_pptx2-2.13.0/tests/test_files/snippets/2x2-bar-clustered-float.txt +145 -0
- python_pptx2-2.13.0/tests/test_files/snippets/2x2-bar-clustered.txt +143 -0
- python_pptx2-2.13.0/tests/test_files/snippets/2x2-bar-stacked-100.txt +144 -0
- python_pptx2-2.13.0/tests/test_files/snippets/2x2-bar-stacked.txt +144 -0
- python_pptx2-2.13.0/tests/test_files/snippets/2x2-column-clustered.txt +143 -0
- python_pptx2-2.13.0/tests/test_files/snippets/2x2-column-stacked-100.txt +144 -0
- python_pptx2-2.13.0/tests/test_files/snippets/2x2-column-stacked.txt +144 -0
- python_pptx2-2.13.0/tests/test_files/snippets/2x2-line-date.txt +162 -0
- python_pptx2-2.13.0/tests/test_files/snippets/2x2-line-float.txt +162 -0
- python_pptx2-2.13.0/tests/test_files/snippets/2x2-line-markers-stacked-100.txt +154 -0
- python_pptx2-2.13.0/tests/test_files/snippets/2x2-line-markers-stacked.txt +154 -0
- python_pptx2-2.13.0/tests/test_files/snippets/2x2-line-markers.txt +154 -0
- python_pptx2-2.13.0/tests/test_files/snippets/2x2-line-stacked-100.txt +160 -0
- python_pptx2-2.13.0/tests/test_files/snippets/2x2-line-stacked.txt +160 -0
- python_pptx2-2.13.0/tests/test_files/snippets/2x2-line.txt +160 -0
- python_pptx2-2.13.0/tests/test_files/snippets/2x3-bubble-3d.txt +215 -0
- python_pptx2-2.13.0/tests/test_files/snippets/2x3-bubble.txt +215 -0
- python_pptx2-2.13.0/tests/test_files/snippets/2x3-xy-lines-no-markers.txt +172 -0
- python_pptx2-2.13.0/tests/test_files/snippets/2x3-xy-lines.txt +166 -0
- python_pptx2-2.13.0/tests/test_files/snippets/2x3-xy-smooth-no-markers.txt +172 -0
- python_pptx2-2.13.0/tests/test_files/snippets/2x3-xy-smooth.txt +166 -0
- python_pptx2-2.13.0/tests/test_files/snippets/2x3-xy.txt +166 -0
- python_pptx2-2.13.0/tests/test_files/snippets/2x5-radar.txt +203 -0
- python_pptx2-2.13.0/tests/test_files/snippets/3x1-pie-exploded.txt +73 -0
- python_pptx2-2.13.0/tests/test_files/snippets/3x1-pie.txt +72 -0
- python_pptx2-2.13.0/tests/test_files/snippets/3x2-doughnut-exploded.txt +145 -0
- python_pptx2-2.13.0/tests/test_files/snippets/3x2-doughnut.txt +143 -0
- python_pptx2-2.13.0/tests/test_files/snippets/4x2-multi-cat-bar.txt +181 -0
- python_pptx2-2.13.0/tests/test_files/snippets/adjust-ser-count.txt +157 -0
- python_pptx2-2.13.0/tests/test_files/snippets/cat-labels.txt +146 -0
- python_pptx2-2.13.0/tests/test_files/snippets/content-types-xml.txt +9 -0
- python_pptx2-2.13.0/tests/test_files/snippets/default-notes.txt +22 -0
- python_pptx2-2.13.0/tests/test_files/snippets/default-notesMaster.txt +347 -0
- python_pptx2-2.13.0/tests/test_files/snippets/default-theme.txt +317 -0
- python_pptx2-2.13.0/tests/test_files/snippets/freeform.txt +45 -0
- python_pptx2-2.13.0/tests/test_files/snippets/package-rels-xml.txt +7 -0
- python_pptx2-2.13.0/tests/test_files/snippets/placeholders.txt +54 -0
- python_pptx2-2.13.0/tests/test_files/snippets/presentation-rels-xml.txt +6 -0
- python_pptx2-2.13.0/tests/test_files/snippets/relationships.txt +2 -0
- python_pptx2-2.13.0/tests/test_files/snippets/rels-load-from-xml.txt +6 -0
- python_pptx2-2.13.0/tests/test_files/snippets/rewrite-ser.txt +488 -0
- python_pptx2-2.13.0/tests/test_files/snippets/timing.txt +101 -0
- python_pptx2-2.13.0/tests/test_files/test.pptx +0 -0
- python_pptx2-2.13.0/tests/test_files/test_slides.pptx +0 -0
- python_pptx2-2.13.0/tests/test_formats.py +78 -0
- python_pptx2-2.13.0/tests/test_geometry.py +219 -0
- python_pptx2-2.13.0/tests/test_inherit.py +110 -0
- python_pptx2-2.13.0/tests/test_lint.py +2261 -0
- python_pptx2-2.13.0/tests/test_lint_on_save.py +121 -0
- python_pptx2-2.13.0/tests/test_math.py +151 -0
- python_pptx2-2.13.0/tests/test_media.py +134 -0
- python_pptx2-2.13.0/tests/test_new_features.py +345 -0
- python_pptx2-2.13.0/tests/test_package.py +377 -0
- python_pptx2-2.13.0/tests/test_powerpoint_strict_validation.py +257 -0
- python_pptx2-2.13.0/tests/test_presentation.py +311 -0
- python_pptx2-2.13.0/tests/test_render.py +411 -0
- python_pptx2-2.13.0/tests/test_render_slides_alias.py +34 -0
- python_pptx2-2.13.0/tests/test_section.py +287 -0
- python_pptx2-2.13.0/tests/test_shape_helpers.py +277 -0
- python_pptx2-2.13.0/tests/test_shared.py +83 -0
- python_pptx2-2.13.0/tests/test_skill.py +49 -0
- python_pptx2-2.13.0/tests/test_slide.py +1376 -0
- python_pptx2-2.13.0/tests/test_smart_art.py +145 -0
- python_pptx2-2.13.0/tests/test_svg_picture.py +180 -0
- python_pptx2-2.13.0/tests/test_table.py +1328 -0
- python_pptx2-2.13.0/tests/test_table_extras.py +321 -0
- python_pptx2-2.13.0/tests/test_theme.py +219 -0
- python_pptx2-2.13.0/tests/test_transition_morph.py +217 -0
- python_pptx2-2.13.0/tests/test_util.py +133 -0
- python_pptx2-2.13.0/tests/text/__init__.py +0 -0
- python_pptx2-2.13.0/tests/text/test_fonts.py +807 -0
- python_pptx2-2.13.0/tests/text/test_layout.py +293 -0
- python_pptx2-2.13.0/tests/text/test_text.py +1774 -0
- python_pptx2-2.13.0/tests/unitdata.py +120 -0
- python_pptx2-2.13.0/tests/unitutil/__init__.py +13 -0
- python_pptx2-2.13.0/tests/unitutil/cxml.py +283 -0
- python_pptx2-2.13.0/tests/unitutil/file.py +57 -0
- python_pptx2-2.13.0/tests/unitutil/mock.py +130 -0
- 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
|