trying to fix in built templates
This commit is contained in:
parent
795c0c531c
commit
9ed8976885
@ -4,7 +4,8 @@ Template operations mixin for pyPhotoAlbum
|
||||
|
||||
from PyQt6.QtWidgets import (
|
||||
QInputDialog, QDialog, QVBoxLayout, QLabel, QComboBox,
|
||||
QRadioButton, QButtonGroup, QPushButton, QHBoxLayout
|
||||
QRadioButton, QButtonGroup, QPushButton, QHBoxLayout,
|
||||
QDoubleSpinBox
|
||||
)
|
||||
from pyPhotoAlbum.decorators import ribbon_action, undoable_operation
|
||||
|
||||
@ -186,6 +187,22 @@ class TemplateOperationsMixin:
|
||||
|
||||
layout.addSpacing(10)
|
||||
|
||||
# Margin/Spacing percentage
|
||||
layout.addWidget(QLabel("Margin/Spacing:"))
|
||||
margin_layout = QHBoxLayout()
|
||||
margin_spinbox = QDoubleSpinBox()
|
||||
margin_spinbox.setRange(0.0, 10.0)
|
||||
margin_spinbox.setValue(2.5)
|
||||
margin_spinbox.setSuffix("%")
|
||||
margin_spinbox.setDecimals(1)
|
||||
margin_spinbox.setSingleStep(0.5)
|
||||
margin_spinbox.setToolTip("Percentage of page size to use for margins and spacing")
|
||||
margin_layout.addWidget(margin_spinbox)
|
||||
margin_layout.addStretch()
|
||||
layout.addLayout(margin_layout)
|
||||
|
||||
layout.addSpacing(10)
|
||||
|
||||
# Scaling selection
|
||||
layout.addWidget(QLabel("Scaling:"))
|
||||
scale_group = QButtonGroup(dialog)
|
||||
@ -228,6 +245,7 @@ class TemplateOperationsMixin:
|
||||
template_name = template_combo.currentText()
|
||||
mode_id = mode_group.checkedId()
|
||||
scale_id = scale_group.checkedId()
|
||||
margin_percent = margin_spinbox.value()
|
||||
|
||||
mode = "replace" if mode_id == 0 else "reflow"
|
||||
scale_mode = ["proportional", "stretch", "center"][scale_id]
|
||||
@ -241,7 +259,8 @@ class TemplateOperationsMixin:
|
||||
template,
|
||||
current_page,
|
||||
mode=mode,
|
||||
scale_mode=scale_mode
|
||||
scale_mode=scale_mode,
|
||||
margin_percent=margin_percent
|
||||
)
|
||||
|
||||
# Update display
|
||||
|
||||
@ -204,16 +204,18 @@ class TemplateManager:
|
||||
elements: List[BaseLayoutElement],
|
||||
from_size: Tuple[float, float],
|
||||
to_size: Tuple[float, float],
|
||||
scale_mode: str = "proportional"
|
||||
scale_mode: str = "proportional",
|
||||
margin_percent: float = 0.0
|
||||
) -> List[BaseLayoutElement]:
|
||||
"""
|
||||
Scale template elements to fit target page size.
|
||||
Scale template elements to fit target page size with adjustable margins.
|
||||
|
||||
Args:
|
||||
elements: List of elements to scale
|
||||
from_size: Original template size (width, height) in mm
|
||||
to_size: Target page size (width, height) in mm
|
||||
scale_mode: "proportional", "stretch", or "center"
|
||||
margin_percent: Percentage of page size to use for margins (0-10%)
|
||||
|
||||
Returns:
|
||||
List of scaled elements
|
||||
@ -221,26 +223,37 @@ class TemplateManager:
|
||||
from_width, from_height = from_size
|
||||
to_width, to_height = to_size
|
||||
|
||||
if scale_mode == "center":
|
||||
# No scaling, just center elements
|
||||
offset_x = (to_width - from_width) / 2
|
||||
offset_y = (to_height - from_height) / 2
|
||||
scale_x = 1.0
|
||||
scale_y = 1.0
|
||||
# Calculate target margins from percentage
|
||||
margin_x = to_width * (margin_percent / 100.0)
|
||||
margin_y = to_height * (margin_percent / 100.0)
|
||||
|
||||
# Available content area after margins
|
||||
content_width = to_width - (2 * margin_x)
|
||||
content_height = to_height - (2 * margin_y)
|
||||
|
||||
# Calculate scale factors based on mode
|
||||
if scale_mode == "stretch":
|
||||
# Stretch to fill content area independently in each dimension
|
||||
scale_x = content_width / from_width
|
||||
scale_y = content_height / from_height
|
||||
offset_x = margin_x
|
||||
offset_y = margin_y
|
||||
elif scale_mode == "proportional":
|
||||
# Maintain aspect ratio
|
||||
scale = min(to_width / from_width, to_height / from_height)
|
||||
# Maintain aspect ratio - scale uniformly to fit content area
|
||||
scale = min(content_width / from_width, content_height / from_height)
|
||||
scale_x = scale
|
||||
scale_y = scale
|
||||
# Center the scaled content
|
||||
offset_x = (to_width - from_width * scale) / 2
|
||||
offset_y = (to_height - from_height * scale) / 2
|
||||
else: # "stretch"
|
||||
# Stretch to fit
|
||||
scale_x = to_width / from_width
|
||||
scale_y = to_height / from_height
|
||||
offset_x = 0
|
||||
offset_y = 0
|
||||
# Center the scaled content within the page
|
||||
scaled_width = from_width * scale
|
||||
scaled_height = from_height * scale
|
||||
offset_x = (to_width - scaled_width) / 2
|
||||
offset_y = (to_height - scaled_height) / 2
|
||||
else: # "center"
|
||||
# No scaling, just center on page
|
||||
scale_x = 1.0
|
||||
scale_y = 1.0
|
||||
offset_x = (to_width - from_width) / 2
|
||||
offset_y = (to_height - from_height) / 2
|
||||
|
||||
scaled_elements = []
|
||||
for element in elements:
|
||||
@ -283,10 +296,11 @@ class TemplateManager:
|
||||
template: Template,
|
||||
page: Page,
|
||||
mode: str = "replace",
|
||||
scale_mode: str = "proportional"
|
||||
scale_mode: str = "proportional",
|
||||
margin_percent: float = 2.5
|
||||
):
|
||||
"""
|
||||
Apply template to an existing page.
|
||||
Apply template to an existing page with adjustable margins.
|
||||
|
||||
Args:
|
||||
template: Template to apply
|
||||
@ -294,6 +308,7 @@ class TemplateManager:
|
||||
mode: "replace" to clear page and add placeholders,
|
||||
"reflow" to keep existing content and reposition
|
||||
scale_mode: "proportional", "stretch", or "center"
|
||||
margin_percent: Percentage of page size to use for margins (0-10%)
|
||||
"""
|
||||
if mode == "replace":
|
||||
# Clear existing elements
|
||||
@ -304,7 +319,8 @@ class TemplateManager:
|
||||
template.elements,
|
||||
template.page_size_mm,
|
||||
page.layout.size,
|
||||
scale_mode
|
||||
scale_mode,
|
||||
margin_percent
|
||||
)
|
||||
|
||||
# Add scaled elements to page
|
||||
@ -321,7 +337,8 @@ class TemplateManager:
|
||||
template.elements,
|
||||
template.page_size_mm,
|
||||
page.layout.size,
|
||||
scale_mode
|
||||
scale_mode,
|
||||
margin_percent
|
||||
)
|
||||
|
||||
template_placeholders = [e for e in scaled_elements if isinstance(e, PlaceholderData)]
|
||||
|
||||
@ -1,20 +1,20 @@
|
||||
{
|
||||
"name": "Grid_2x2",
|
||||
"description": "Simple 2x2 grid layout with equal-sized image placeholders",
|
||||
"description": "Simple 2x2 grid layout with equal-sized image placeholders (square page, margins applied at use time)",
|
||||
"page_size_mm": [
|
||||
210,
|
||||
297
|
||||
210
|
||||
],
|
||||
"elements": [
|
||||
{
|
||||
"type": "placeholder",
|
||||
"position": [
|
||||
5,
|
||||
5
|
||||
0,
|
||||
0
|
||||
],
|
||||
"size": [
|
||||
100,
|
||||
143.5
|
||||
105,
|
||||
105
|
||||
],
|
||||
"rotation": 0,
|
||||
"z_index": 0,
|
||||
@ -25,11 +25,11 @@
|
||||
"type": "placeholder",
|
||||
"position": [
|
||||
105,
|
||||
5
|
||||
0
|
||||
],
|
||||
"size": [
|
||||
100,
|
||||
143.5
|
||||
105,
|
||||
105
|
||||
],
|
||||
"rotation": 0,
|
||||
"z_index": 0,
|
||||
@ -39,12 +39,12 @@
|
||||
{
|
||||
"type": "placeholder",
|
||||
"position": [
|
||||
5,
|
||||
148.5
|
||||
0,
|
||||
105
|
||||
],
|
||||
"size": [
|
||||
100,
|
||||
143.5
|
||||
105,
|
||||
105
|
||||
],
|
||||
"rotation": 0,
|
||||
"z_index": 0,
|
||||
@ -55,11 +55,11 @@
|
||||
"type": "placeholder",
|
||||
"position": [
|
||||
105,
|
||||
148.5
|
||||
105
|
||||
],
|
||||
"size": [
|
||||
100,
|
||||
143.5
|
||||
105,
|
||||
105
|
||||
],
|
||||
"rotation": 0,
|
||||
"z_index": 0,
|
||||
|
||||
@ -1,20 +1,20 @@
|
||||
{
|
||||
"name": "Single_Large",
|
||||
"description": "Single large image placeholder with title text",
|
||||
"description": "Single large image placeholder with title text (square page, margins applied at use time)",
|
||||
"page_size_mm": [
|
||||
210,
|
||||
297
|
||||
210
|
||||
],
|
||||
"elements": [
|
||||
{
|
||||
"type": "textbox",
|
||||
"position": [
|
||||
10,
|
||||
10
|
||||
0,
|
||||
0
|
||||
],
|
||||
"size": [
|
||||
190,
|
||||
30
|
||||
210,
|
||||
25
|
||||
],
|
||||
"rotation": 0,
|
||||
"z_index": 1,
|
||||
@ -33,12 +33,12 @@
|
||||
{
|
||||
"type": "placeholder",
|
||||
"position": [
|
||||
10,
|
||||
50
|
||||
0,
|
||||
25
|
||||
],
|
||||
"size": [
|
||||
190,
|
||||
230
|
||||
210,
|
||||
185
|
||||
],
|
||||
"rotation": 0,
|
||||
"z_index": 0,
|
||||
|
||||
@ -530,3 +530,163 @@ class TestTemplateManager:
|
||||
assert scaled[0].text_content == "Test"
|
||||
assert scaled[0].font_settings == font_settings
|
||||
assert scaled[0].alignment == text.alignment
|
||||
|
||||
def test_grid_2x2_stretch_to_square_page(self):
|
||||
"""Test Grid_2x2 template applied to square page with stretch mode"""
|
||||
manager = TemplateManager()
|
||||
|
||||
# Create a 2x2 grid template at 210x210mm (margin-less, fills entire space)
|
||||
template = Template(name="Grid_2x2", page_size_mm=(210, 210))
|
||||
# 4 cells: each 105 x 105mm (half of 210mm)
|
||||
template.add_element(PlaceholderData(x=0, y=0, width=105, height=105))
|
||||
template.add_element(PlaceholderData(x=105, y=0, width=105, height=105))
|
||||
template.add_element(PlaceholderData(x=0, y=105, width=105, height=105))
|
||||
template.add_element(PlaceholderData(x=105, y=105, width=105, height=105))
|
||||
|
||||
# Apply to same size page with stretch mode and 2.5% margin
|
||||
layout = PageLayout(width=210, height=210)
|
||||
page = Page(layout=layout, page_number=1)
|
||||
|
||||
manager.apply_template_to_page(
|
||||
template, page,
|
||||
mode="replace",
|
||||
scale_mode="stretch",
|
||||
margin_percent=2.5
|
||||
)
|
||||
|
||||
# With 2.5% margin on 210mm page: margin = 5.25mm, content area = 199.5mm
|
||||
# Template is 210mm, so scale = 199.5 / 210 = 0.95
|
||||
# Each element should scale by 0.95 and be offset by margin
|
||||
assert len(page.layout.elements) == 4
|
||||
|
||||
# Check first element (top-left)
|
||||
elem = page.layout.elements[0]
|
||||
scale = 199.5 / 210.0 # 0.95
|
||||
expected_x = 0 * scale + 5.25 # 0 + 5.25 = 5.25
|
||||
expected_y = 0 * scale + 5.25 # 0 + 5.25 = 5.25
|
||||
expected_width = 105 * scale # 99.75
|
||||
expected_height = 105 * scale # 99.75
|
||||
|
||||
assert abs(elem.position[0] - expected_x) < 0.1
|
||||
assert abs(elem.position[1] - expected_y) < 0.1
|
||||
assert abs(elem.size[0] - expected_width) < 0.1
|
||||
assert abs(elem.size[1] - expected_height) < 0.1
|
||||
|
||||
def test_grid_2x2_stretch_to_a4_page(self):
|
||||
"""Test Grid_2x2 template applied to A4 page with stretch mode"""
|
||||
manager = TemplateManager()
|
||||
|
||||
# Create Grid_2x2 template (210x210mm, margin-less)
|
||||
template = Template(name="Grid_2x2", page_size_mm=(210, 210))
|
||||
template.add_element(PlaceholderData(x=0, y=0, width=105, height=105))
|
||||
template.add_element(PlaceholderData(x=105, y=0, width=105, height=105))
|
||||
template.add_element(PlaceholderData(x=0, y=105, width=105, height=105))
|
||||
template.add_element(PlaceholderData(x=105, y=105, width=105, height=105))
|
||||
|
||||
# Apply to A4 page (210x297mm) with stretch mode and 2.5% margin
|
||||
layout = PageLayout(width=210, height=297)
|
||||
page = Page(layout=layout, page_number=1)
|
||||
|
||||
manager.apply_template_to_page(
|
||||
template, page,
|
||||
mode="replace",
|
||||
scale_mode="stretch",
|
||||
margin_percent=2.5
|
||||
)
|
||||
|
||||
# With 2.5% margin: x_margin = 5.25mm, y_margin = 7.425mm
|
||||
# Content area: 199.5 x 282.15mm
|
||||
# Scale: x = 199.5/210 = 0.95, y = 282.15/210 = 1.3436
|
||||
assert len(page.layout.elements) == 4
|
||||
|
||||
# First element should stretch
|
||||
elem = page.layout.elements[0]
|
||||
scale_x = 199.5 / 210.0
|
||||
scale_y = 282.15 / 210.0
|
||||
|
||||
expected_x = 0 * scale_x + 5.25 # 5.25
|
||||
expected_y = 0 * scale_y + 7.425 # 7.425
|
||||
expected_width = 105 * scale_x # 99.75
|
||||
expected_height = 105 * scale_y # 141.075
|
||||
|
||||
assert abs(elem.position[0] - expected_x) < 0.1
|
||||
assert abs(elem.position[1] - expected_y) < 0.1
|
||||
assert abs(elem.size[0] - expected_width) < 0.1
|
||||
assert abs(elem.size[1] - expected_height) < 0.1
|
||||
|
||||
def test_grid_2x2_with_different_margins(self):
|
||||
"""Test Grid_2x2 template with different margin percentages"""
|
||||
manager = TemplateManager()
|
||||
|
||||
template = Template(name="Grid_2x2", page_size_mm=(210, 210))
|
||||
template.add_element(PlaceholderData(x=0, y=0, width=105, height=105))
|
||||
|
||||
# Test with 0% margin
|
||||
layout = PageLayout(width=210, height=210)
|
||||
page = Page(layout=layout, page_number=1)
|
||||
|
||||
manager.apply_template_to_page(
|
||||
template, page,
|
||||
mode="replace",
|
||||
scale_mode="stretch",
|
||||
margin_percent=0.0
|
||||
)
|
||||
|
||||
# With 0% margin, template fills entire page (scale = 1.0, offset = 0)
|
||||
elem = page.layout.elements[0]
|
||||
assert abs(elem.position[0] - 0.0) < 0.1
|
||||
assert abs(elem.position[1] - 0.0) < 0.1
|
||||
assert abs(elem.size[0] - 105.0) < 0.1
|
||||
|
||||
# Test with 5% margin
|
||||
layout2 = PageLayout(width=210, height=210)
|
||||
page2 = Page(layout=layout2, page_number=1)
|
||||
|
||||
manager.apply_template_to_page(
|
||||
template, page2,
|
||||
mode="replace",
|
||||
scale_mode="stretch",
|
||||
margin_percent=5.0
|
||||
)
|
||||
|
||||
# With 5% margin: margin = 10.5mm, content = 189mm, scale = 189/210 = 0.9
|
||||
elem2 = page2.layout.elements[0]
|
||||
assert abs(elem2.position[0] - 10.5) < 0.1
|
||||
assert abs(elem2.position[1] - 10.5) < 0.1
|
||||
assert abs(elem2.size[0] - (105 * 0.9)) < 0.1
|
||||
|
||||
def test_grid_2x2_proportional_mode(self):
|
||||
"""Test Grid_2x2 template with proportional scaling"""
|
||||
manager = TemplateManager()
|
||||
|
||||
template = Template(name="Grid_2x2", page_size_mm=(210, 210))
|
||||
template.add_element(PlaceholderData(x=0, y=0, width=105, height=105))
|
||||
|
||||
# Apply to rectangular page with proportional mode
|
||||
layout = PageLayout(width=210, height=297)
|
||||
page = Page(layout=layout, page_number=1)
|
||||
|
||||
manager.apply_template_to_page(
|
||||
template, page,
|
||||
mode="replace",
|
||||
scale_mode="proportional",
|
||||
margin_percent=2.5
|
||||
)
|
||||
|
||||
# With proportional mode on 210x297 page:
|
||||
# Content area: 199.5 x 282.15mm
|
||||
# Template: 210 x 210mm
|
||||
# Scale = min(199.5/210, 282.15/210) = 0.95 (uniform)
|
||||
# Content is centered on page
|
||||
|
||||
elem = page.layout.elements[0]
|
||||
scale = 199.5 / 210.0
|
||||
|
||||
# Should be scaled uniformly
|
||||
expected_width = 105 * scale # 99.75
|
||||
expected_height = 105 * scale # 99.75
|
||||
|
||||
assert abs(elem.size[0] - expected_width) < 0.1
|
||||
assert abs(elem.size[1] - expected_height) < 0.1
|
||||
# Width should equal height (uniform scaling)
|
||||
assert abs(elem.size[0] - elem.size[1]) < 0.1
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user