Skip to content

Commit fef3fc4

Browse files
committed
feat: enhance text formatting options in presentation builder
1 parent 690756e commit fef3fc4

File tree

6 files changed

+124
-49
lines changed

6 files changed

+124
-49
lines changed

examples/simple_example.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,9 @@ def main():
2121
top=(50, "pt"),
2222
width=(400, "pt"),
2323
height=(50, "pt"),
24+
size=(60, "pt"),
25+
bold=True,
26+
italic=True,
2427
)
2528
.text(
2629
"Example of using tppt library", # Subtitle

src/tppt/_pptx/converter.py

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,10 +2,13 @@
22

33
from typing import Protocol, Self, TypeVar, assert_never, overload, runtime_checkable
44

5+
from pptx.dml.color import RGBColor as PptxRGBColor
56
from pptx.util import Cm as PptxCm
67
from pptx.util import Inches as PptxInches
78
from pptx.util import Length as PptxLength
89
from pptx.util import Pt as PptxPt
10+
11+
from tppt.types._color import Color
912
from tppt.types._length import (
1013
Centimeter,
1114
Inch,
@@ -55,3 +58,7 @@ def to_pptx_length(length: Length | LiteralLength | None) -> PptxLength | None:
5558
return None
5659
case _:
5760
assert_never(length)
61+
62+
63+
def to_pptx_color(color: Color) -> PptxRGBColor:
64+
return PptxRGBColor(color.r, color.g, color.b)

src/tppt/_pptx/converters.py

Lines changed: 0 additions & 32 deletions
This file was deleted.

src/tppt/_pptx/shape/text.py

Lines changed: 43 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,10 @@
1-
from typing import Literal, Self, TypedDict
1+
from typing import Literal, NotRequired, Self, TypedDict
22

3+
from pptx.enum.text import MSO_ANCHOR, MSO_AUTO_SIZE, PP_ALIGN
34
from pptx.shapes.autoshape import Shape as PptxShape
5+
6+
from tppt._pptx.converter import to_pptx_color, to_pptx_length
7+
from tppt.types._color import Color
48
from tppt.types._length import Length, LiteralLength
59

610
from . import Shape
@@ -13,6 +17,17 @@ class TextProps(TypedDict):
1317
top: Length | LiteralLength
1418
width: Length | LiteralLength
1519
height: Length | LiteralLength
20+
size: NotRequired[Length | LiteralLength]
21+
bold: NotRequired[bool]
22+
italic: NotRequired[bool]
23+
color: NotRequired[Color]
24+
margin_bottom: NotRequired[Length | LiteralLength]
25+
margin_left: NotRequired[Length | LiteralLength]
26+
vertical_anchor: NotRequired[MSO_ANCHOR]
27+
word_wrap: NotRequired[bool]
28+
auto_size: NotRequired[MSO_AUTO_SIZE]
29+
alignment: NotRequired[PP_ALIGN]
30+
level: NotRequired[int]
1631

1732

1833
class TextData(TextProps):
@@ -28,7 +43,33 @@ class Text(Shape[PptxShape]):
2843

2944
def __init__(self, pptx_obj: PptxShape, data: TextData | None = None, /) -> None:
3045
if data:
31-
pptx_obj.text = data["text"]
46+
text_frame = pptx_obj.text_frame
47+
p = text_frame.paragraphs[0]
48+
run = p.add_run()
49+
run.text = data["text"]
50+
font = run.font
51+
if size := data.get("size"):
52+
font.size = to_pptx_length(size)
53+
if (bold := data.get("bold")) is not None:
54+
font.bold = bold
55+
if (italic := data.get("italic")) is not None:
56+
font.italic = italic
57+
if (color := data.get("color")) is not None:
58+
font.color.rgb = to_pptx_color(color)
59+
if (margin_bottom := data.get("margin_bottom")) is not None:
60+
p.space_after = to_pptx_length(margin_bottom)
61+
if (margin_left := data.get("margin_left")) is not None:
62+
p.space_before = to_pptx_length(margin_left)
63+
if (vertical_anchor := data.get("vertical_anchor")) is not None:
64+
text_frame.vertical_anchor = vertical_anchor
65+
if (word_wrap := data.get("word_wrap")) is not None:
66+
text_frame.word_wrap = word_wrap
67+
if (auto_size := data.get("auto_size")) is not None:
68+
text_frame.auto_size = auto_size
69+
if (alignment := data.get("alignment")) is not None:
70+
p.alignment = alignment
71+
if (level := data.get("level")) is not None:
72+
p.level = level
3273

3374
self._pptx = pptx_obj
3475

src/tppt/types/_color.py

Lines changed: 5 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
"""Color types for tppt."""
22

33
from pptx.dml.color import RGBColor
4+
45
from tppt.exception import ColorInvalidFormatError
56

67

@@ -31,21 +32,10 @@ def __init__(self, value: str | tuple[int, int, int]):
3132

3233
else:
3334
r, g, b = value
34-
self.value = RGBColor(r, g, b)
35-
36-
@property
37-
def is_rgb(self) -> bool:
38-
"""Check if the color is defined as RGB."""
39-
return isinstance(self.value, tuple)
40-
41-
@property
42-
def is_named(self) -> bool:
43-
"""Check if the color is defined as a named color."""
44-
return isinstance(self.value, str)
35+
self.r = r
36+
self.g = g
37+
self.b = b
4538

4639
def __repr__(self) -> str:
4740
"""Return string representation."""
48-
if self.is_rgb:
49-
r, g, b = self.value
50-
return "Color())"
51-
return f"Color({self.value})"
41+
return f"Color({self.r}, {self.g}, {self.b})"

tests/test_text.py

Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,66 @@
1+
"""Tests for text module."""
2+
3+
from pptx.enum.text import MSO_ANCHOR, MSO_AUTO_SIZE, PP_ALIGN
4+
5+
import tppt
6+
from tppt.types._color import Color
7+
8+
9+
def test_text_with_options(output) -> None:
10+
"""Test creating text with various formatting options."""
11+
# テキストとその書式設定オプション
12+
text_content = "サンプルテキスト"
13+
14+
# プレゼンテーションの作成
15+
presentation = (
16+
tppt.Presentation.builder()
17+
.slide(
18+
tppt.SlideBuilder().text(
19+
text_content,
20+
left=(100, "pt"),
21+
top=(100, "pt"),
22+
width=(400, "pt"),
23+
height=(200, "pt"),
24+
size=(24, "pt"),
25+
bold=True,
26+
italic=True,
27+
color=Color((255, 0, 0)), # 赤色
28+
margin_bottom=(10, "pt"),
29+
margin_left=(10, "pt"),
30+
vertical_anchor=MSO_ANCHOR.MIDDLE,
31+
word_wrap=True,
32+
auto_size=MSO_AUTO_SIZE.TEXT_TO_FIT_SHAPE,
33+
alignment=PP_ALIGN.CENTER,
34+
level=1,
35+
)
36+
)
37+
.build()
38+
)
39+
40+
# プレゼンテーションを保存
41+
pptx_path = output / "text_with_options.pptx"
42+
presentation.save(pptx_path)
43+
44+
45+
def test_text_default_options(output) -> None:
46+
"""Test creating text with default options."""
47+
text_content = "デフォルト設定のテキスト"
48+
49+
# デフォルト設定でテキストを作成
50+
presentation = (
51+
tppt.Presentation.builder()
52+
.slide(
53+
tppt.SlideBuilder().text(
54+
text_content,
55+
left=(100, "pt"),
56+
top=(100, "pt"),
57+
width=(300, "pt"),
58+
height=(100, "pt"),
59+
)
60+
)
61+
.build()
62+
)
63+
64+
# プレゼンテーションを保存
65+
pptx_path = output / "text_default_options.pptx"
66+
presentation.save(pptx_path)

0 commit comments

Comments
 (0)