Length measurements, positioning utilities, and helper functions for working with PowerPoint's coordinate system, measurements, and common programming patterns.
Convert between different measurement units and work with PowerPoint's coordinate system.
def Inches(inches: float) -> Length:
"""
Create Length from inches measurement.
Parameters:
- inches: Length in inches
Returns:
Length object representing the measurement
"""
def Cm(cm: float) -> Length:
"""
Create Length from centimeters measurement.
Parameters:
- cm: Length in centimeters
Returns:
Length object
"""
def Mm(mm: float) -> Length:
"""
Create Length from millimeters measurement.
Parameters:
- mm: Length in millimeters
Returns:
Length object
"""
def Pt(points: float) -> Length:
"""
Create Length from points measurement.
Parameters:
- points: Length in points (1/72 inch)
Returns:
Length object
"""
def Emu(emu: int) -> Length:
"""
Create Length from English Metric Units.
Parameters:
- emu: Length in EMU (1/914400 inch)
Returns:
Length object
"""
def Centipoints(centipoints: int) -> Length:
"""
Create Length from centipoints measurement.
Parameters:
- centipoints: Length in centipoints (1/7200 inch)
Returns:
Length object
"""Work with length measurements and perform unit conversions.
class Length:
"""Length measurement with unit conversion capabilities."""
@property
def inches(self) -> float:
"""Length in inches."""
@property
def cm(self) -> float:
"""Length in centimeters."""
@property
def mm(self) -> float:
"""Length in millimeters."""
@property
def pt(self) -> float:
"""Length in points."""
@property
def emu(self) -> int:
"""Length in English Metric Units (EMU)."""
@property
def centipoints(self) -> int:
"""Length in centipoints."""
def __add__(self, other: 'Length') -> 'Length':
"""Add two lengths together."""
def __sub__(self, other: 'Length') -> 'Length':
"""Subtract one length from another."""
def __mul__(self, factor: float) -> 'Length':
"""Multiply length by numeric factor."""
def __truediv__(self, divisor: float) -> 'Length':
"""Divide length by numeric divisor."""
def __eq__(self, other: 'Length') -> bool:
"""Test length equality."""
def __lt__(self, other: 'Length') -> bool:
"""Test if length is less than another."""
def __le__(self, other: 'Length') -> bool:
"""Test if length is less than or equal to another."""
def __gt__(self, other: 'Length') -> bool:
"""Test if length is greater than another."""
def __ge__(self, other: 'Length') -> bool:
"""Test if length is greater than or equal to another."""
def __str__(self) -> str:
"""String representation of length."""
def __repr__(self) -> str:
"""Developer representation of length."""Helper decorators and utilities for efficient property management and common patterns.
def lazyproperty(func):
"""
Decorator for lazy property evaluation.
Property is computed once on first access and cached for subsequent calls.
Useful for expensive computations or object creation.
Parameters:
- func: Property getter function
Returns:
Property descriptor with lazy evaluation
"""# Common measurement relationships
# 1 inch = 72 points
# 1 inch = 914,400 EMU
# 1 inch = 2.54 cm
# 1 cm = 10 mm
# 1 point = 12,700 EMU
# 1 centipoint = 127 EMU
# Standard slide dimensions
SLIDE_WIDTH_STANDARD = Inches(10) # Standard 4:3 slide width
SLIDE_HEIGHT_STANDARD = Inches(7.5) # Standard 4:3 slide height
SLIDE_WIDTH_WIDESCREEN = Inches(13.33) # Widescreen 16:9 slide width
SLIDE_HEIGHT_WIDESCREEN = Inches(7.5) # Widescreen 16:9 slide heightUsage examples:
from pptx import Presentation
from pptx.util import Inches, Cm, Pt, Mm, lazyproperty
from pptx.enum.shapes import MSO_AUTO_SHAPE_TYPE
prs = Presentation()
slide = prs.slides.add_slide(prs.slide_layouts[6])
# Using different measurement units
rect_inches = slide.shapes.add_shape(
MSO_AUTO_SHAPE_TYPE.RECTANGLE,
Inches(1), # 1 inch from left
Inches(1), # 1 inch from top
Inches(2), # 2 inches wide
Inches(1) # 1 inch tall
)
rect_cm = slide.shapes.add_shape(
MSO_AUTO_SHAPE_TYPE.RECTANGLE,
Cm(8), # 8 cm from left
Cm(3), # 3 cm from top
Cm(4), # 4 cm wide
Cm(2) # 2 cm tall
)
rect_points = slide.shapes.add_shape(
MSO_AUTO_SHAPE_TYPE.RECTANGLE,
Pt(144), # 144 points from left (2 inches)
Pt(216), # 216 points from top (3 inches)
Pt(72), # 72 points wide (1 inch)
Pt(36) # 36 points tall (0.5 inches)
)
# Length arithmetic
width = Inches(2)
height = Inches(1)
total_area = width * height # Length objects support arithmetic
# Position calculations
margin = Inches(0.5)
content_width = Inches(8)
shape_width = Inches(3)
# Center shape horizontally
left_position = margin + (content_width - shape_width) / 2
centered_shape = slide.shapes.add_shape(
MSO_AUTO_SHAPE_TYPE.OVAL,
left_position,
Inches(5),
shape_width,
Inches(1)
)
# Unit conversions
length_inches = Inches(2)
print(f"2 inches = {length_inches.cm:.2f} cm")
print(f"2 inches = {length_inches.mm:.1f} mm")
print(f"2 inches = {length_inches.pt:.0f} points")
print(f"2 inches = {length_inches.emu:,} EMU")
# Working with slide dimensions
slide_width = prs.slide_width # In EMU
slide_height = prs.slide_height # In EMU
# Convert to more usable units
width_inches = Length(slide_width).inches
height_inches = Length(slide_height).inches
print(f"Slide size: {width_inches:.1f}\" x {height_inches:.1f}\"")
# Create grid layout using measurements
def create_grid_layout(slide, rows, cols, margin=Inches(0.5)):
"""Create grid of shapes with consistent spacing."""
slide_width_length = Length(slide.part.presentation_part.presentation.slide_width)
slide_height_length = Length(slide.part.presentation_part.presentation.slide_height)
usable_width = slide_width_length - (margin * 2)
usable_height = slide_height_length - (margin * 2)
cell_width = usable_width / cols
cell_height = usable_height / rows
shapes = []
for row in range(rows):
for col in range(cols):
left = margin + (cell_width * col)
top = margin + (cell_height * row)
shape = slide.shapes.add_shape(
MSO_AUTO_SHAPE_TYPE.RECTANGLE,
left, top,
cell_width * 0.8, # 80% of cell width
cell_height * 0.8 # 80% of cell height
)
shape.text = f"Cell {row+1},{col+1}"
shapes.append(shape)
return shapes
# Create 2x3 grid
grid_slide = prs.slides.add_slide(prs.slide_layouts[6])
grid_shapes = create_grid_layout(grid_slide, 2, 3)
prs.save('utilities-example.pptx')from pptx.util import Inches, Cm, Pt
# Length comparison and validation
def validate_dimensions(width, height, max_width=Inches(10), max_height=Inches(7.5)):
"""Validate shape dimensions against slide constraints."""
if width > max_width:
raise ValueError(f"Width {width.inches:.2f}\" exceeds maximum {max_width.inches:.2f}\"")
if height > max_height:
raise ValueError(f"Height {height.inches:.2f}\" exceeds maximum {max_height.inches:.2f}\"")
return True
# Proportional scaling
def scale_to_fit(original_width, original_height, max_width, max_height):
"""Scale dimensions proportionally to fit within constraints."""
width_ratio = max_width / original_width
height_ratio = max_height / original_height
scale_factor = min(width_ratio.emu / original_width.emu,
height_ratio.emu / original_height.emu)
return (original_width * scale_factor, original_height * scale_factor)
# Common spacing calculations
def calculate_spacing(total_width, num_items, item_width):
"""Calculate spacing between items for even distribution."""
remaining_space = total_width - (item_width * num_items)
if num_items > 1:
return remaining_space / (num_items - 1)
else:
return remaining_space / 2 # Center single item
# Position helpers
class PositionHelper:
"""Helper class for common positioning operations."""
def __init__(self, slide_width, slide_height, margin=Inches(0.5)):
self.slide_width = slide_width
self.slide_height = slide_height
self.margin = margin
self.content_width = slide_width - (margin * 2)
self.content_height = slide_height - (margin * 2)
def center_horizontal(self, shape_width):
"""Calculate left position to center shape horizontally."""
return self.margin + (self.content_width - shape_width) / 2
def center_vertical(self, shape_height):
"""Calculate top position to center shape vertically."""
return self.margin + (self.content_height - shape_height) / 2
def align_right(self, shape_width):
"""Calculate left position to right-align shape."""
return self.slide_width - self.margin - shape_width
def align_bottom(self, shape_height):
"""Calculate top position to bottom-align shape."""
return self.slide_height - self.margin - shape_height
# Using positioning helper
helper = PositionHelper(Inches(10), Inches(7.5))
# Center a shape
shape_width = Inches(4)
shape_height = Inches(2)
centered_left = helper.center_horizontal(shape_width)
centered_top = helper.center_vertical(shape_height)
centered_shape = slide.shapes.add_shape(
MSO_AUTO_SHAPE_TYPE.RECTANGLE,
centered_left, centered_top, shape_width, shape_height
)class ExpensiveShapeAnalyzer:
"""Example class using lazy properties for expensive computations."""
def __init__(self, slide):
self.slide = slide
@lazyproperty
def total_text_length(self):
"""Compute total text length across all shapes (cached after first call)."""
total = 0
for shape in self.slide.shapes:
if hasattr(shape, 'text'):
total += len(shape.text or '')
return total
@lazyproperty
def shape_area_stats(self):
"""Compute shape area statistics (cached after first call)."""
areas = []
for shape in self.slide.shapes:
area = (Length(shape.width).inches * Length(shape.height).inches)
areas.append(area)
if areas:
return {
'total': sum(areas),
'average': sum(areas) / len(areas),
'max': max(areas),
'min': min(areas)
}
return {'total': 0, 'average': 0, 'max': 0, 'min': 0}
# Usage
analyzer = ExpensiveShapeAnalyzer(slide)
print(f"Total text length: {analyzer.total_text_length}") # Computed on first access
print(f"Total text length: {analyzer.total_text_length}") # Retrieved from cache# Use consistent units throughout your application
STANDARD_MARGIN = Inches(0.5)
STANDARD_SPACING = Inches(0.25)
TITLE_HEIGHT = Inches(1)
CONTENT_FONT_SIZE = Pt(12)
HEADER_FONT_SIZE = Pt(16)
# Create reusable dimension constants
class StandardDimensions:
"""Standard dimensions for consistent layouts."""
SLIDE_WIDTH = Inches(10)
SLIDE_HEIGHT = Inches(7.5)
MARGIN = Inches(0.5)
TITLE_HEIGHT = Inches(1)
FOOTER_HEIGHT = Inches(0.5)
# Chart dimensions
CHART_WIDTH = Inches(6)
CHART_HEIGHT = Inches(4)
# Text box dimensions
TEXTBOX_WIDTH = Inches(4)
TEXTBOX_HEIGHT = Inches(2)
@classmethod
def content_area(cls):
"""Calculate usable content area."""
width = cls.SLIDE_WIDTH - (cls.MARGIN * 2)
height = cls.SLIDE_HEIGHT - (cls.MARGIN * 2) - cls.TITLE_HEIGHT - cls.FOOTER_HEIGHT
return width, height
# Use in layout calculations
content_width, content_height = StandardDimensions.content_area()
chart_left = StandardDimensions.MARGIN + (content_width - StandardDimensions.CHART_WIDTH) / 2