athena-python-pptx 0.1.75__tar.gz → 0.1.76__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (83) hide show
  1. {athena_python_pptx-0.1.75 → athena_python_pptx-0.1.76}/CHANGELOG.md +17 -0
  2. {athena_python_pptx-0.1.75 → athena_python_pptx-0.1.76}/PKG-INFO +1 -1
  3. {athena_python_pptx-0.1.75 → athena_python_pptx-0.1.76}/pptx/__init__.py +1 -1
  4. {athena_python_pptx-0.1.75 → athena_python_pptx-0.1.76}/pptx/commands.py +7 -0
  5. {athena_python_pptx-0.1.75 → athena_python_pptx-0.1.76}/pptx/shapes/__init__.py +71 -7
  6. {athena_python_pptx-0.1.75 → athena_python_pptx-0.1.76}/pyproject.toml +1 -1
  7. {athena_python_pptx-0.1.75 → athena_python_pptx-0.1.76}/.gitignore +0 -0
  8. {athena_python_pptx-0.1.75 → athena_python_pptx-0.1.76}/API_PARITY_REPORT.md +0 -0
  9. {athena_python_pptx-0.1.75 → athena_python_pptx-0.1.76}/CLAUDE.md +0 -0
  10. {athena_python_pptx-0.1.75 → athena_python_pptx-0.1.76}/DEV-GUIDE.md +0 -0
  11. {athena_python_pptx-0.1.75 → athena_python_pptx-0.1.76}/PARITY_QUESTIONS.md +0 -0
  12. {athena_python_pptx-0.1.75 → athena_python_pptx-0.1.76}/PUBLISHING.md +0 -0
  13. {athena_python_pptx-0.1.75 → athena_python_pptx-0.1.76}/README.md +0 -0
  14. {athena_python_pptx-0.1.75 → athena_python_pptx-0.1.76}/docs/API_PARITY_EXCEPTIONS.md +0 -0
  15. {athena_python_pptx-0.1.75 → athena_python_pptx-0.1.76}/docs/athena-api.json +0 -0
  16. {athena_python_pptx-0.1.75 → athena_python_pptx-0.1.76}/docs/athena-api.md +0 -0
  17. {athena_python_pptx-0.1.75 → athena_python_pptx-0.1.76}/pptx/_ptc.py +0 -0
  18. {athena_python_pptx-0.1.75 → athena_python_pptx-0.1.76}/pptx/action.py +0 -0
  19. {athena_python_pptx-0.1.75 → athena_python_pptx-0.1.76}/pptx/batching.py +0 -0
  20. {athena_python_pptx-0.1.75 → athena_python_pptx-0.1.76}/pptx/chart/__init__.py +0 -0
  21. {athena_python_pptx-0.1.75 → athena_python_pptx-0.1.76}/pptx/chart/axis.py +0 -0
  22. {athena_python_pptx-0.1.75 → athena_python_pptx-0.1.76}/pptx/chart/category.py +0 -0
  23. {athena_python_pptx-0.1.75 → athena_python_pptx-0.1.76}/pptx/chart/chart.py +0 -0
  24. {athena_python_pptx-0.1.75 → athena_python_pptx-0.1.76}/pptx/chart/data.py +0 -0
  25. {athena_python_pptx-0.1.75 → athena_python_pptx-0.1.76}/pptx/chart/datalabel.py +0 -0
  26. {athena_python_pptx-0.1.75 → athena_python_pptx-0.1.76}/pptx/chart/legend.py +0 -0
  27. {athena_python_pptx-0.1.75 → athena_python_pptx-0.1.76}/pptx/chart/marker.py +0 -0
  28. {athena_python_pptx-0.1.75 → athena_python_pptx-0.1.76}/pptx/chart/plot.py +0 -0
  29. {athena_python_pptx-0.1.75 → athena_python_pptx-0.1.76}/pptx/chart/point.py +0 -0
  30. {athena_python_pptx-0.1.75 → athena_python_pptx-0.1.76}/pptx/chart/series.py +0 -0
  31. {athena_python_pptx-0.1.75 → athena_python_pptx-0.1.76}/pptx/chart/xlsx.py +0 -0
  32. {athena_python_pptx-0.1.75 → athena_python_pptx-0.1.76}/pptx/client.py +0 -0
  33. {athena_python_pptx-0.1.75 → athena_python_pptx-0.1.76}/pptx/decorators.py +0 -0
  34. {athena_python_pptx-0.1.75 → athena_python_pptx-0.1.76}/pptx/dml/__init__.py +0 -0
  35. {athena_python_pptx-0.1.75 → athena_python_pptx-0.1.76}/pptx/dml/chtfmt.py +0 -0
  36. {athena_python_pptx-0.1.75 → athena_python_pptx-0.1.76}/pptx/dml/color.py +0 -0
  37. {athena_python_pptx-0.1.75 → athena_python_pptx-0.1.76}/pptx/dml/effect.py +0 -0
  38. {athena_python_pptx-0.1.75 → athena_python_pptx-0.1.76}/pptx/dml/fill.py +0 -0
  39. {athena_python_pptx-0.1.75 → athena_python_pptx-0.1.76}/pptx/dml/line.py +0 -0
  40. {athena_python_pptx-0.1.75 → athena_python_pptx-0.1.76}/pptx/docgen.py +0 -0
  41. {athena_python_pptx-0.1.75 → athena_python_pptx-0.1.76}/pptx/enum/__init__.py +0 -0
  42. {athena_python_pptx-0.1.75 → athena_python_pptx-0.1.76}/pptx/enum/action.py +0 -0
  43. {athena_python_pptx-0.1.75 → athena_python_pptx-0.1.76}/pptx/enum/chart.py +0 -0
  44. {athena_python_pptx-0.1.75 → athena_python_pptx-0.1.76}/pptx/enum/dml.py +0 -0
  45. {athena_python_pptx-0.1.75 → athena_python_pptx-0.1.76}/pptx/enum/lang.py +0 -0
  46. {athena_python_pptx-0.1.75 → athena_python_pptx-0.1.76}/pptx/enum/shapes.py +0 -0
  47. {athena_python_pptx-0.1.75 → athena_python_pptx-0.1.76}/pptx/enum/text.py +0 -0
  48. {athena_python_pptx-0.1.75 → athena_python_pptx-0.1.76}/pptx/errors.py +0 -0
  49. {athena_python_pptx-0.1.75 → athena_python_pptx-0.1.76}/pptx/exc.py +0 -0
  50. {athena_python_pptx-0.1.75 → athena_python_pptx-0.1.76}/pptx/media.py +0 -0
  51. {athena_python_pptx-0.1.75 → athena_python_pptx-0.1.76}/pptx/package.py +0 -0
  52. {athena_python_pptx-0.1.75 → athena_python_pptx-0.1.76}/pptx/parts/__init__.py +0 -0
  53. {athena_python_pptx-0.1.75 → athena_python_pptx-0.1.76}/pptx/parts/_base.py +0 -0
  54. {athena_python_pptx-0.1.75 → athena_python_pptx-0.1.76}/pptx/parts/chart.py +0 -0
  55. {athena_python_pptx-0.1.75 → athena_python_pptx-0.1.76}/pptx/parts/coreprops.py +0 -0
  56. {athena_python_pptx-0.1.75 → athena_python_pptx-0.1.76}/pptx/parts/embeddedpackage.py +0 -0
  57. {athena_python_pptx-0.1.75 → athena_python_pptx-0.1.76}/pptx/parts/image.py +0 -0
  58. {athena_python_pptx-0.1.75 → athena_python_pptx-0.1.76}/pptx/parts/media.py +0 -0
  59. {athena_python_pptx-0.1.75 → athena_python_pptx-0.1.76}/pptx/parts/presentation.py +0 -0
  60. {athena_python_pptx-0.1.75 → athena_python_pptx-0.1.76}/pptx/parts/slide.py +0 -0
  61. {athena_python_pptx-0.1.75 → athena_python_pptx-0.1.76}/pptx/presentation.py +0 -0
  62. {athena_python_pptx-0.1.75 → athena_python_pptx-0.1.76}/pptx/shapes/autoshape.py +0 -0
  63. {athena_python_pptx-0.1.75 → athena_python_pptx-0.1.76}/pptx/shapes/base.py +0 -0
  64. {athena_python_pptx-0.1.75 → athena_python_pptx-0.1.76}/pptx/shapes/connector.py +0 -0
  65. {athena_python_pptx-0.1.75 → athena_python_pptx-0.1.76}/pptx/shapes/freeform.py +0 -0
  66. {athena_python_pptx-0.1.75 → athena_python_pptx-0.1.76}/pptx/shapes/graphfrm.py +0 -0
  67. {athena_python_pptx-0.1.75 → athena_python_pptx-0.1.76}/pptx/shapes/group.py +0 -0
  68. {athena_python_pptx-0.1.75 → athena_python_pptx-0.1.76}/pptx/shapes/picture.py +0 -0
  69. {athena_python_pptx-0.1.75 → athena_python_pptx-0.1.76}/pptx/shapes/placeholder.py +0 -0
  70. {athena_python_pptx-0.1.75 → athena_python_pptx-0.1.76}/pptx/shapes/shapetree.py +0 -0
  71. {athena_python_pptx-0.1.75 → athena_python_pptx-0.1.76}/pptx/shared.py +0 -0
  72. {athena_python_pptx-0.1.75 → athena_python_pptx-0.1.76}/pptx/slide.py +0 -0
  73. {athena_python_pptx-0.1.75 → athena_python_pptx-0.1.76}/pptx/slides.py +0 -0
  74. {athena_python_pptx-0.1.75 → athena_python_pptx-0.1.76}/pptx/spec.py +0 -0
  75. {athena_python_pptx-0.1.75 → athena_python_pptx-0.1.76}/pptx/table.py +0 -0
  76. {athena_python_pptx-0.1.75 → athena_python_pptx-0.1.76}/pptx/text/__init__.py +0 -0
  77. {athena_python_pptx-0.1.75 → athena_python_pptx-0.1.76}/pptx/text/fonts.py +0 -0
  78. {athena_python_pptx-0.1.75 → athena_python_pptx-0.1.76}/pptx/text/layout.py +0 -0
  79. {athena_python_pptx-0.1.75 → athena_python_pptx-0.1.76}/pptx/text/text.py +0 -0
  80. {athena_python_pptx-0.1.75 → athena_python_pptx-0.1.76}/pptx/types.py +0 -0
  81. {athena_python_pptx-0.1.75 → athena_python_pptx-0.1.76}/pptx/typing.py +0 -0
  82. {athena_python_pptx-0.1.75 → athena_python_pptx-0.1.76}/pptx/units.py +0 -0
  83. {athena_python_pptx-0.1.75 → athena_python_pptx-0.1.76}/pptx/util.py +0 -0
@@ -2,6 +2,23 @@
2
2
 
3
3
  All notable changes to `athena-python-pptx` are documented in this file.
4
4
 
5
+ ## 0.1.76
6
+
7
+ **Table cell paragraph alignment now round-trips to OOXML (baseline-parity Gap A2).**
8
+
9
+ `cell.text_frame.paragraphs[0].alignment = PP_ALIGN.X` previously stored
10
+ the value locally for python-pptx parity but never reached the server.
11
+ The setter now translates `PP_PARAGRAPH_ALIGNMENT` enum members (and the
12
+ python-pptx lowercase string aliases) to the OOXML short form
13
+ (`l` / `ctr` / `r` / `just` / `dist` / `thaiDist` / `justLow`) and emits
14
+ a `SetTableCell` command carrying `paragraphAlignment`. The applier
15
+ writes it to every paragraph in the cell's text body; the export worker
16
+ emits a `SetTableCellText` intent whose paragraph alignment lands on
17
+ `<a:pPr algn="…">` in the exported PPTX.
18
+
19
+ Closes ~52 baseline-parity XML diffs against the 33 fixtures in
20
+ `pptx-studio/tests/baseline-parity/`.
21
+
5
22
  ## 0.1.75
6
23
 
7
24
  **Critical bug fix — invisible text from `font.size = Pt(X)`.**
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: athena-python-pptx
3
- Version: 0.1.75
3
+ Version: 0.1.76
4
4
  Summary: Drop-in replacement for python-pptx that connects to PPTX Studio for real-time collaboration
5
5
  Project-URL: Homepage, https://github.com/pptx-studio/python-sdk
6
6
  Project-URL: Documentation, https://docs.pptx-studio.com/sdk/python
@@ -127,7 +127,7 @@ def flush_all() -> None:
127
127
  _active_buffers[:] = alive
128
128
 
129
129
 
130
- __version__ = "0.1.75"
130
+ __version__ = "0.1.76"
131
131
 
132
132
  __all__ = [
133
133
  # Main entry point
@@ -476,6 +476,12 @@ class SetTableCell(Command):
476
476
  col: Column index (0-based)
477
477
  text: Cell text content
478
478
  fill_color_hex: Cell background color
479
+ paragraph_alignment: Horizontal alignment of the cell's text body.
480
+ Mirrors python-pptx ``cell.text_frame.paragraphs[0].alignment``.
481
+ Accepts the OOXML short forms ``l``, ``ctr``, ``r``, ``just``,
482
+ ``dist``, ``thaiDist``. Translated from ``PP_ALIGN`` /
483
+ ``PP_PARAGRAPH_ALIGNMENT`` by the SDK before emit. Closes
484
+ baseline-parity Gap A2 (~52 diffs across table fixtures).
479
485
  """
480
486
 
481
487
  shape_id: ShapeId
@@ -486,6 +492,7 @@ class SetTableCell(Command):
486
492
  font_size_centipoints: Optional[int] = None
487
493
  bold: Optional[bool] = None
488
494
  font_color_hex: Optional[str] = None
495
+ paragraph_alignment: Optional[str] = None
489
496
 
490
497
  @property
491
498
  def command_type(self) -> str:
@@ -56,6 +56,57 @@ _ELEMENT_TYPE_TO_MSO_SHAPE_TYPE: dict[str, MSO_SHAPE_TYPE] = {
56
56
  }
57
57
 
58
58
 
59
+ # Map PP_PARAGRAPH_ALIGNMENT enum members and python-pptx string aliases
60
+ # to OOXML <a:pPr algn="..."> short forms. Accepts the IntEnum int values
61
+ # directly so callers can pass ``PP_ALIGN.CENTER`` without importing the
62
+ # enum class. ``None`` clears the attribute.
63
+ _PP_ALIGN_INT_TO_OOXML: dict[int, str] = {
64
+ 1: "l",
65
+ 2: "ctr",
66
+ 3: "r",
67
+ 4: "just",
68
+ 5: "dist",
69
+ 6: "thaiDist",
70
+ 7: "justLow",
71
+ }
72
+ _PP_ALIGN_NAME_TO_OOXML: dict[str, str] = {
73
+ "left": "l",
74
+ "center": "ctr",
75
+ "right": "r",
76
+ "justify": "just",
77
+ "distribute": "dist",
78
+ "thai_distribute": "thaiDist",
79
+ "justify_low": "justLow",
80
+ # Already-short forms pass through.
81
+ "l": "l", "ctr": "ctr", "r": "r",
82
+ "just": "just", "dist": "dist", "thaiDist": "thaiDist", "justLow": "justLow",
83
+ }
84
+
85
+
86
+ def _normalize_pp_align(value: Any) -> Optional[str]:
87
+ """Translate a PP_ALIGN value to the OOXML short form used by SetTableCell.
88
+
89
+ Accepts ``PP_PARAGRAPH_ALIGNMENT`` (or any ``int``) and the lowercased
90
+ python-pptx string aliases used by ``pptx.text.PP_ALIGN``. Returns
91
+ ``None`` for ``None`` input so the setter can clear the attribute.
92
+ """
93
+ if value is None:
94
+ return None
95
+ if isinstance(value, int) and not isinstance(value, bool):
96
+ try:
97
+ return _PP_ALIGN_INT_TO_OOXML[int(value)]
98
+ except KeyError:
99
+ raise ValueError(f"Unsupported paragraph alignment value: {value!r}")
100
+ if isinstance(value, str):
101
+ try:
102
+ return _PP_ALIGN_NAME_TO_OOXML[value]
103
+ except KeyError:
104
+ raise ValueError(f"Unsupported paragraph alignment value: {value!r}")
105
+ raise TypeError(
106
+ f"alignment must be a PP_ALIGN enum, int, or string; got {type(value).__name__}"
107
+ )
108
+
109
+
59
110
  # Map XL_CHART_TYPE → (server chart_type, grouping). Only the chart types
60
111
  # author.ts supports today are listed here; anything else raises
61
112
  # UnsupportedFeatureError. Surface area mirrors python-pptx's accepted enum
@@ -5814,15 +5865,13 @@ class _TableCellParagraph:
5814
5865
  For API parity with python-pptx, ``add_run()`` and ``clear()`` are
5815
5866
  supported but operate on the single underlying run — multiple
5816
5867
  ``add_run()`` calls return the *same* ``_TableCellRun`` object, and
5817
- only the last text/style assignment wins. ``alignment`` is accepted
5818
- for compatibility but doesn't yet round-trip to OOXML (no per-cell
5819
- paragraph-alignment command on the applier).
5868
+ only the last text/style assignment wins. ``alignment`` round-trips
5869
+ to ``<a:pPr algn="...">`` on the cell's text body via SetTableCell.
5820
5870
  """
5821
5871
 
5822
5872
  def __init__(self, cell: "TableCell"):
5823
5873
  self._cell = cell
5824
5874
  self._runs = [_TableCellRun(cell)]
5825
- self._alignment: Optional[Any] = None
5826
5875
 
5827
5876
  @property
5828
5877
  def runs(self) -> list[_TableCellRun]:
@@ -5838,12 +5887,22 @@ class _TableCellParagraph:
5838
5887
 
5839
5888
  @property
5840
5889
  def alignment(self) -> Optional[Any]:
5841
- return self._alignment
5890
+ """Return the cell's paragraph alignment as the original input value.
5891
+
5892
+ Stored on the parent cell so that text-body re-creation (which
5893
+ rebuilds the paragraph wrapper) doesn't lose the assignment.
5894
+ """
5895
+ return self._cell._paragraph_alignment_input
5842
5896
 
5843
5897
  @alignment.setter
5844
5898
  def alignment(self, value: Any) -> None:
5845
- # Accepted for python-pptx parity; not yet round-tripped to OOXML.
5846
- self._alignment = value
5899
+ # Translate PP_ALIGN enum / string to OOXML short form. Stored on
5900
+ # the cell so we can re-emit it inside SetTableCell. Keep the
5901
+ # original input around for the getter to return per python-pptx
5902
+ # semantics (callers expect the same value type back).
5903
+ self._cell._paragraph_alignment_input = value
5904
+ self._cell._paragraph_alignment = _normalize_pp_align(value)
5905
+ self._cell._emit_cell_change()
5847
5906
 
5848
5907
  def add_run(self) -> _TableCellRun:
5849
5908
  """Return the single underlying run.
@@ -5943,6 +6002,10 @@ class TableCell:
5943
6002
  self._margin_top = margin_top
5944
6003
  self._margin_bottom = margin_bottom
5945
6004
  self._vertical_anchor = vertical_anchor
6005
+ # OOXML short form (``l``/``ctr``/``r``/...) sent over the wire and
6006
+ # the caller's original input retained for the python-pptx getter.
6007
+ self._paragraph_alignment: Optional[str] = None
6008
+ self._paragraph_alignment_input: Any = None
5946
6009
 
5947
6010
  @property
5948
6011
  def text(self) -> str:
@@ -6318,6 +6381,7 @@ class TableCell:
6318
6381
  font_size_centipoints=int(self._font_size_pt * 100) if self._font_size_pt is not None else None,
6319
6382
  bold=self._bold,
6320
6383
  font_color_hex=self._font_color_hex,
6384
+ paragraph_alignment=self._paragraph_alignment,
6321
6385
  )
6322
6386
  self._table._buffer.add(cmd)
6323
6387
 
@@ -4,7 +4,7 @@ build-backend = "hatchling.build"
4
4
 
5
5
  [project]
6
6
  name = "athena-python-pptx"
7
- version = "0.1.75"
7
+ version = "0.1.76"
8
8
  description = "Drop-in replacement for python-pptx that connects to PPTX Studio for real-time collaboration"
9
9
  readme = "README.md"
10
10
  license = "MIT"