Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
376 changes: 309 additions & 67 deletions clean_slides/chart_engine/builder.py

Large diffs are not rendered by default.

8 changes: 4 additions & 4 deletions clean_slides/chart_engine/overlay_bar.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@

from collections.abc import Mapping, Sequence
from pathlib import Path
from typing import Any, Callable, Union, cast
from typing import Callable, Union, cast

from pptx.dml.color import RGBColor
from pptx.util import Pt
Expand Down Expand Up @@ -51,8 +51,8 @@
OverlaySpec = Mapping[str, object]
MetaSpec = Mapping[str, object]

AddLineAnnotationFn = Callable[[Any, dict[str, object]], None]
AddShapeAnnotationFn = Callable[[Any, dict[str, object]], None]
AddLineAnnotationFn = Callable[[object, dict[str, object]], None]
AddShapeAnnotationFn = Callable[[object, dict[str, object]], None]
LoadTemplateFn = Callable[[Path, str], object]


Expand Down Expand Up @@ -218,7 +218,7 @@ def _plot_layout(value: object) -> dict[str, float]:


def add_bar_overlays(
slide: Any,
slide: object,
chart_box: tuple[int, int, int, int],
meta: MetaSpec,
) -> None:
Expand Down
48 changes: 45 additions & 3 deletions clean_slides/chart_engine/overlay_bar_legend.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@

from collections.abc import Mapping, Sequence
from pathlib import Path
from typing import Any, Callable, Union, cast
from typing import Callable, Protocol, Union, cast

from pptx.dml.color import RGBColor
from pptx.enum.shapes import MSO_SHAPE
Expand All @@ -23,6 +23,46 @@
TemplateMap = Mapping[str, object]
LegendGeometry = Mapping[str, float]


class _ShapeForeColorLike(Protocol):
theme_color: object
rgb: object


class _ShapeFillLike(Protocol):
fore_color: _ShapeForeColorLike

def solid(self) -> None: ...


class _ShapeLineFillLike(Protocol):
def background(self) -> None: ...


class _ShapeLineLike(Protocol):
fill: _ShapeLineFillLike


class _ShapeLike(Protocol):
fill: _ShapeFillLike
line: _ShapeLineLike


class _SlideShapesLike(Protocol):
def add_shape(
self,
shape_type: object,
left: object,
top: object,
width: object,
height: object,
) -> _ShapeLike: ...


class _SlideLike(Protocol):
shapes: _SlideShapesLike


AddTextLabelFn = Callable[..., object]
ResolveTemplateFn = Callable[[PathOrNone, str, object], object]

Expand Down Expand Up @@ -54,7 +94,7 @@ def _bool(value: object, default: bool) -> bool:


def add_bar_legend(
slide: Any,
slide: object,
*,
overlay: Mapping[str, object],
chart_box: tuple[int, int, int, int],
Expand All @@ -78,6 +118,8 @@ def add_bar_legend(
templates: TemplateMap,
) -> None:
"""Render legend labels and optional color markers."""
slide_like = cast(_SlideLike, slide)

legend_layout = _optional_str(overlay.get("legend_layout"))
legend_align = normalize_alignment(_optional_str(overlay.get("legend_alignment")))
legend_show_markers = _bool(overlay.get("legend_show_markers", True), True)
Expand Down Expand Up @@ -162,7 +204,7 @@ def add_bar_legend(
if legend_show_markers and series_color:
marker_x = plot_left + plot_width * (marker_left_ratio + marker_step_ratio * idx)
marker_y = legend_y + marker_y_offset
shape = slide.shapes.add_shape(
shape = slide_like.shapes.add_shape(
MSO_SHAPE.RECTANGLE,
int(marker_x),
int(marker_y),
Expand Down
4 changes: 2 additions & 2 deletions clean_slides/chart_engine/overlay_bar_segments.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@

from collections.abc import Mapping, Sequence
from pathlib import Path
from typing import Any, Callable, Union, cast
from typing import Callable, Union, cast

from pptx.dml.color import RGBColor

Expand Down Expand Up @@ -108,7 +108,7 @@ def _coerce_fill_color(fill_value: object, series_color: StrOrNone) -> ColorValu


def add_bar_segment_labels(
slide: Any,
slide: object,
*,
overlay: OverlaySpec,
categories: Sequence[object],
Expand Down
6 changes: 3 additions & 3 deletions clean_slides/chart_engine/overlay_bar_totals_categories.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@

from collections.abc import Mapping, Sequence
from pathlib import Path
from typing import Any, Callable, Union, cast
from typing import Callable, Union, cast

from pptx.dml.color import RGBColor
from pptx.enum.text import PP_ALIGN
Expand Down Expand Up @@ -66,7 +66,7 @@ def _bar_center(geometry: Geometry, idx: int) -> FloatOrNone:


def add_bar_total_labels(
slide: Any,
slide: object,
*,
overlay: Mapping[str, object],
totals: Sequence[FloatOrNone],
Expand Down Expand Up @@ -176,7 +176,7 @@ def add_bar_total_labels(


def add_bar_category_labels(
slide: Any,
slide: object,
*,
overlay: Mapping[str, object],
categories: Sequence[object],
Expand Down
4 changes: 2 additions & 2 deletions clean_slides/chart_engine/overlay_waterfall.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
from __future__ import annotations

from collections.abc import Mapping
from typing import Any, Union, cast
from typing import Union, cast

from pptx.dml.color import RGBColor
from pptx.util import Emu, Pt
Expand Down Expand Up @@ -110,7 +110,7 @@ def _index_set(value: object) -> set[int]:


def add_waterfall_overlays(
slide: Any,
slide: object,
chart_box: tuple[int, int, int, int],
meta: Mapping[str, object],
slide_size: SlideSize = None,
Expand Down
45 changes: 42 additions & 3 deletions clean_slides/chart_engine/overlay_waterfall_connectors.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
from __future__ import annotations

from collections.abc import Mapping, Sequence
from typing import Any, Union, cast
from typing import Protocol, Union, cast

from pptx.dml.color import RGBColor
from pptx.enum.shapes import MSO_SHAPE
Expand All @@ -17,6 +17,44 @@
Geometry = Mapping[str, object]


class _ShapeForeColorLike(Protocol):
rgb: object


class _ShapeFillLike(Protocol):
fore_color: _ShapeForeColorLike

def solid(self) -> None: ...


class _ShapeLineFillLike(Protocol):
def background(self) -> None: ...


class _ShapeLineLike(Protocol):
fill: _ShapeLineFillLike


class _ShapeLike(Protocol):
fill: _ShapeFillLike
line: _ShapeLineLike


class _SlideShapesLike(Protocol):
def add_shape(
self,
shape_type: object,
left: object,
top: object,
width: object,
height: object,
) -> _ShapeLike: ...


class _SlideLike(Protocol):
shapes: _SlideShapesLike


def _geometry_series(geometry: Geometry, key: str) -> list[float]:
raw_values = geometry.get(key)
if not isinstance(raw_values, list):
Expand All @@ -40,7 +78,7 @@ def _connector_value(values: Sequence[FloatOrNone], idx: int) -> FloatOrNone:


def render_waterfall_connectors(
slide: Any,
slide: object,
categories: Sequence[object],
connector_values: Sequence[FloatOrNone],
geometry: Geometry,
Expand All @@ -60,11 +98,12 @@ def render_waterfall_connectors(
connector_color: ColorValue,
) -> None:
"""Render connector segments between adjacent waterfall categories."""
slide_like = cast(_SlideLike, slide)

def add_dash_segment(x: float, y: float, width: float, height: float) -> None:
if width <= 0 or height <= 0:
return
shape = slide.shapes.add_shape(
shape = slide_like.shapes.add_shape(
MSO_SHAPE.RECTANGLE,
round(x),
round(y),
Expand Down
Loading