idconvert/backend/export/factories.py

277 lines
8.9 KiB
Python

from __future__ import annotations
from typing import Any, Dict, List, Optional
from export.models import (
IDExportCharacterStyle,
IDExportDocument,
IDExportImageFrame,
IDExportPage,
IDExportPageNumber,
IDExportParagraph,
IDExportParagraphStyle,
IDExportRun,
IDExportShape,
IDExportTable,
IDExportTableCell,
IDExportTableRow,
IDExportTextFrame,
)
class IDExportDocumentFactory:
"""Builds IDExportDocument from a validated idconvert_export.json dict."""
@staticmethod
def from_dict(data: dict) -> IDExportDocument:
doc_info = data.get('document', {})
styles_data = data.get('styles', {})
paragraph_styles = [
IDExportParagraphStyleFactory.from_dict(s)
for s in styles_data.get('paragraph', [])
]
character_styles = [
IDExportCharacterStyleFactory.from_dict(s)
for s in styles_data.get('character', [])
]
pages = [
IDExportPageFactory.from_dict(p)
for p in data.get('pages', [])
]
# Find first page_number item across all pages for footer use
page_number_style: Optional[IDExportPageNumber] = None
for page in pages:
for item in page.items:
if isinstance(item, IDExportPageNumber):
page_number_style = item
break
if page_number_style:
break
return IDExportDocument(
name=doc_info.get('name', 'Document'),
page_width_pt=float(doc_info.get('page_width_pt', 595.28)),
page_height_pt=float(doc_info.get('page_height_pt', 841.89)),
page_count=int(doc_info.get('page_count', len(pages))),
facing_pages=bool(doc_info.get('facing_pages', False)),
paragraph_styles=paragraph_styles,
character_styles=character_styles,
colors=data.get('colors', {}),
fonts_used=data.get('fonts_used', []),
pages=pages,
page_number_style=page_number_style,
)
class IDExportParagraphStyleFactory:
@staticmethod
def from_dict(data: dict) -> IDExportParagraphStyle:
return IDExportParagraphStyle(
name=data.get('name', ''),
font=data.get('font'),
size_pt=_optional_float(data.get('size_pt')),
leading_pt=_optional_float(data.get('leading_pt')),
space_before_pt=float(data.get('space_before_pt', 0)),
space_after_pt=float(data.get('space_after_pt', 0)),
alignment=data.get('alignment', 'left'),
color_hex=data.get('color_hex'),
bold=bool(data.get('bold', False)),
italic=bool(data.get('italic', False)),
)
class IDExportCharacterStyleFactory:
@staticmethod
def from_dict(data: dict) -> IDExportCharacterStyle:
return IDExportCharacterStyle(
name=data.get('name', ''),
font=data.get('font'),
bold=bool(data.get('bold', False)),
italic=bool(data.get('italic', False)),
color_hex=data.get('color_hex'),
)
class IDExportPageFactory:
@staticmethod
def from_dict(data: dict) -> IDExportPage:
items = [
IDExportItemFactory.from_dict(item)
for item in data.get('items', [])
if item
]
items = [i for i in items if i is not None]
return IDExportPage(
page_number=data.get('page_number', 1),
width_pt=float(data.get('width_pt', 595.28)),
height_pt=float(data.get('height_pt', 841.89)),
items=items,
)
class IDExportItemFactory:
@staticmethod
def from_dict(data: dict):
item_type = data.get('type', '')
if item_type == 'text_frame':
return IDExportTextFrameFactory.from_dict(data)
if item_type == 'image_frame':
return IDExportImageFrameFactory.from_dict(data)
if item_type == 'table':
return IDExportTableFactory.from_dict(data)
if item_type == 'page_number':
return IDExportPageNumberFactory.from_dict(data)
if item_type == 'shape':
return IDExportShapeFactory.from_dict(data)
# frame_group, column_break, and unknown types not converted
return None
class IDExportShapeFactory:
@staticmethod
def from_dict(data: dict) -> IDExportShape:
return IDExportShape(
id=str(data.get('id', '')),
x_pt=float(data.get('x_pt', 0)),
y_pt=float(data.get('y_pt', 0)),
width_pt=float(data.get('width_pt', 0)),
height_pt=float(data.get('height_pt', 0)),
shape_type=data.get('shape_type', 'rect'),
fill_hex=data.get('fill_hex'),
stroke_hex=data.get('stroke_hex'),
stroke_weight_pt=float(data.get('stroke_weight_pt', 0)),
)
class IDExportTextFrameFactory:
@staticmethod
def from_dict(data: dict) -> IDExportTextFrame:
paragraphs = [
IDExportParagraphFactory.from_dict(p)
for p in data.get('paragraphs', [])
]
return IDExportTextFrame(
id=str(data.get('id', '')),
thread_id=str(data.get('thread_id', '')),
thread_position=int(data.get('thread_position', 1)),
x_pt=float(data.get('x_pt', 0)),
y_pt=float(data.get('y_pt', 0)),
width_pt=float(data.get('width_pt', 0)),
height_pt=float(data.get('height_pt', 0)),
column_count=int(data.get('column_count', 1)),
paragraphs=paragraphs,
)
class IDExportParagraphFactory:
@staticmethod
def from_dict(data: dict) -> IDExportParagraph:
runs = [
IDExportRunFactory.from_dict(r)
for r in data.get('runs', [])
]
return IDExportParagraph(
style=data.get('style', ''),
text=data.get('text', ''),
runs=runs,
)
class IDExportRunFactory:
@staticmethod
def from_dict(data: dict) -> IDExportRun:
return IDExportRun(
text=data.get('text', ''),
style=data.get('style'),
bold=bool(data.get('bold', False)),
italic=bool(data.get('italic', False)),
color_hex=data.get('color_hex'),
font=data.get('font'),
size_pt=_optional_float(data.get('size_pt')),
)
class IDExportImageFrameFactory:
@staticmethod
def from_dict(data: dict) -> IDExportImageFrame:
return IDExportImageFrame(
id=str(data.get('id', '')),
x_pt=float(data.get('x_pt', 0)),
y_pt=float(data.get('y_pt', 0)),
width_pt=float(data.get('width_pt', 0)),
height_pt=float(data.get('height_pt', 0)),
image_name=data.get('image_name', 'image'),
image_data_b64=data.get('image_data_b64'),
fit_mode=data.get('fit_mode', 'fill_proportionally'),
)
class IDExportTableFactory:
@staticmethod
def from_dict(data: dict) -> IDExportTable:
rows = [
IDExportTableRowFactory.from_dict(r)
for r in data.get('rows', [])
]
return IDExportTable(
id=str(data.get('id', '')),
x_pt=float(data.get('x_pt', 0)),
y_pt=float(data.get('y_pt', 0)),
width_pt=float(data.get('width_pt', 0)),
column_widths_pt=[float(w) for w in data.get('column_widths_pt', [])],
rows=rows,
)
class IDExportTableRowFactory:
@staticmethod
def from_dict(data: dict) -> IDExportTableRow:
cells = [
IDExportTableCellFactory.from_dict(c)
for c in data.get('cells', [])
]
return IDExportTableRow(
is_header=bool(data.get('is_header', False)),
cells=cells,
)
class IDExportTableCellFactory:
@staticmethod
def from_dict(data: dict) -> IDExportTableCell:
return IDExportTableCell(
text=data.get('text', ''),
style=data.get('style'),
col_span=int(data.get('col_span', 1)),
row_span=int(data.get('row_span', 1)),
background_hex=data.get('background_hex'),
text_color_hex=data.get('text_color_hex'),
)
class IDExportPageNumberFactory:
@staticmethod
def from_dict(data: dict) -> IDExportPageNumber:
return IDExportPageNumber(
id=str(data.get('id', '')),
x_pt=float(data.get('x_pt', 0)),
y_pt=float(data.get('y_pt', 0)),
font=data.get('font'),
size_pt=float(data.get('size_pt', 9)),
color_hex=data.get('color_hex', '000000') or '000000',
alignment=data.get('alignment', 'center'),
)
def _optional_float(value) -> Optional[float]:
if value is None:
return None
try:
return float(value)
except (TypeError, ValueError):
return None