Files
Templated-PPT-Master/runtime/ppt-master/scripts/svg_to_pptx/pptx_package/dimensions.py
T

190 lines
5.7 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
"""Slide dimensions, format detection, EMU conversion, and constants."""
from __future__ import annotations
import re
import sys
from pathlib import Path
from xml.etree import ElementTree as ET
from ..canvas_contract import (
CanvasContractError,
ProjectViewBox,
parse_project_viewbox,
read_project_viewbox,
require_consistent_project_viewboxes,
)
# Import project utility modules
sys.path.insert(0, str(Path(__file__).resolve().parent.parent))
try:
from project_utils import get_project_info
from config import CANVAS_FORMATS
except ImportError:
CANVAS_FORMATS = {
'ppt169': {'name': 'PPT 16:9', 'dimensions': '1280×720', 'viewbox': '0 0 1280 720'},
}
def get_project_info(path: str) -> dict:
return {'format': 'unknown', 'name': Path(path).name}
# EMU conversion constants
EMU_PER_INCH = 914400
EMU_PER_PIXEL = EMU_PER_INCH / 96
# XML namespaces
NAMESPACES = {
'a': 'http://schemas.openxmlformats.org/drawingml/2006/main',
'r': 'http://schemas.openxmlformats.org/officeDocument/2006/relationships',
'p': 'http://schemas.openxmlformats.org/presentationml/2006/main',
'asvg': 'http://schemas.microsoft.com/office/drawing/2016/SVG/main',
}
# Register namespaces for ElementTree output
for prefix, uri in NAMESPACES.items():
ET.register_namespace(prefix, uri)
def get_slide_dimensions(
canvas_format: str,
custom_pixels: tuple[int, int] | None = None,
) -> tuple[int, int]:
"""Get slide dimensions in EMU units.
Args:
canvas_format: Canvas format key (e.g. 'ppt169').
custom_pixels: Optional custom pixel dimensions override.
Returns:
(width_emu, height_emu) tuple.
"""
if custom_pixels:
width_px, height_px = custom_pixels
else:
if canvas_format not in CANVAS_FORMATS:
canvas_format = 'ppt169'
dimensions = CANVAS_FORMATS[canvas_format]['dimensions']
match = re.match(r'(\d+)[×x](\d+)', dimensions)
if match:
width_px = int(match.group(1))
height_px = int(match.group(2))
else:
width_px, height_px = 1280, 720
return int(width_px * EMU_PER_PIXEL), int(height_px * EMU_PER_PIXEL)
def get_pixel_dimensions(
canvas_format: str,
custom_pixels: tuple[int, int] | None = None,
) -> tuple[int, int]:
"""Get canvas pixel dimensions.
Args:
canvas_format: Canvas format key.
custom_pixels: Optional custom pixel dimensions override.
Returns:
(width_px, height_px) tuple.
"""
if custom_pixels:
return custom_pixels
if canvas_format not in CANVAS_FORMATS:
canvas_format = 'ppt169'
dimensions = CANVAS_FORMATS[canvas_format]['dimensions']
match = re.match(r'(\d+)[×x](\d+)', dimensions)
if match:
return int(match.group(1)), int(match.group(2))
return 1280, 720
def get_viewbox_dimensions(svg_path: Path) -> tuple[float, float]:
"""Extract pixel dimensions from SVG viewBox.
Args:
svg_path: Path to the SVG file.
Returns:
(width, height) in SVG pixels.
Raises:
CanvasContractError: The root canvas is missing or invalid.
"""
return read_project_viewbox(svg_path).pixel_dimensions
def detect_format_from_svg(svg_path: Path) -> str | None:
"""Detect canvas format from an SVG file's viewBox.
Args:
svg_path: Path to the SVG file.
Returns:
Canvas format key (e.g. 'ppt169'), or None if not detected.
"""
viewbox = read_project_viewbox(svg_path)
for fmt_key, fmt_info in CANVAS_FORMATS.items():
expected = parse_project_viewbox(
fmt_info['viewbox'],
context=f"registered canvas {fmt_key!r}",
)
if viewbox == expected:
return fmt_key
return None
def resolve_svg_canvas(
svg_files: list[Path],
*,
canvas_format: str | None = None,
expected_viewbox: str | None = None,
) -> tuple[ProjectViewBox, str | None]:
"""Resolve one fail-closed canvas for every public/internal SVG."""
format_viewbox: str | None = None
if canvas_format is not None:
if canvas_format not in CANVAS_FORMATS:
raise CanvasContractError(f"Unsupported canvas format: {canvas_format}")
format_viewbox = CANVAS_FORMATS[canvas_format]['viewbox']
if expected_viewbox is not None and format_viewbox is not None:
locked = parse_project_viewbox(
expected_viewbox,
context="locked canvas viewBox",
)
selected = parse_project_viewbox(
format_viewbox,
context=f"canvas format {canvas_format!r}",
)
if locked != selected:
raise CanvasContractError(
f"canvas format {canvas_format!r} ({selected.canonical}) conflicts "
f"with the locked canvas ({locked.canonical})"
)
required = expected_viewbox if expected_viewbox is not None else format_viewbox
if expected_viewbox is not None:
expected_label = "the locked canvas"
elif canvas_format is not None:
expected_label = f"canvas format {canvas_format!r}"
else:
expected_label = "the first SVG canvas"
viewbox = require_consistent_project_viewboxes(
svg_files,
expected_viewbox=required,
expected_label=expected_label,
)
detected_format = canvas_format
if detected_format is None:
for fmt_key, fmt_info in CANVAS_FORMATS.items():
registered = parse_project_viewbox(
fmt_info['viewbox'],
context=f"registered canvas {fmt_key!r}",
)
if viewbox == registered:
detected_format = fmt_key
break
return viewbox, detected_format