figquilt 0.1.6__tar.gz → 0.1.7__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {figquilt-0.1.6 → figquilt-0.1.7}/PKG-INFO +1 -1
- {figquilt-0.1.6 → figquilt-0.1.7}/pyproject.toml +1 -1
- {figquilt-0.1.6 → figquilt-0.1.7}/src/figquilt/compose_pdf.py +2 -2
- {figquilt-0.1.6 → figquilt-0.1.7}/src/figquilt/compose_svg.py +2 -2
- {figquilt-0.1.6 → figquilt-0.1.7}/src/figquilt/grid.py +1 -0
- {figquilt-0.1.6 → figquilt-0.1.7}/src/figquilt/layout.py +18 -0
- {figquilt-0.1.6 → figquilt-0.1.7}/src/figquilt/units.py +44 -5
- {figquilt-0.1.6 → figquilt-0.1.7}/README.md +0 -0
- {figquilt-0.1.6 → figquilt-0.1.7}/src/figquilt/__init__.py +0 -0
- {figquilt-0.1.6 → figquilt-0.1.7}/src/figquilt/cli.py +0 -0
- {figquilt-0.1.6 → figquilt-0.1.7}/src/figquilt/errors.py +0 -0
- {figquilt-0.1.6 → figquilt-0.1.7}/src/figquilt/images.py +0 -0
- {figquilt-0.1.6 → figquilt-0.1.7}/src/figquilt/parser.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.3
|
|
2
2
|
Name: figquilt
|
|
3
|
-
Version: 0.1.
|
|
3
|
+
Version: 0.1.7
|
|
4
4
|
Summary: figquilt is a small, language-agnostic CLI tool that composes multiple figures (PDF/SVG/PNG) into a single publication-ready figure, based on a simple layout file (YAML/JSON). The key function is creating a PDF by composing multiple PDFs and adding subfigure labels and minimal annotations.
|
|
5
5
|
Author: YY Ahn
|
|
6
6
|
Author-email: YY Ahn <yongyeol@gmail.com>
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
[project]
|
|
2
2
|
name = "figquilt"
|
|
3
|
-
version = "0.1.
|
|
3
|
+
version = "0.1.7"
|
|
4
4
|
description = "figquilt is a small, language-agnostic CLI tool that composes multiple figures (PDF/SVG/PNG) into a single publication-ready figure, based on a simple layout file (YAML/JSON). The key function is creating a PDF by composing multiple PDFs and adding subfigure labels and minimal annotations."
|
|
5
5
|
readme = "README.md"
|
|
6
6
|
authors = [
|
|
@@ -100,11 +100,11 @@ class PDFComposer:
|
|
|
100
100
|
# No height specified: use source aspect ratio
|
|
101
101
|
h = w * src_aspect
|
|
102
102
|
|
|
103
|
-
# Calculate content rect using fit mode
|
|
103
|
+
# Calculate content rect using fit mode and alignment
|
|
104
104
|
from .units import calculate_fit
|
|
105
105
|
|
|
106
106
|
content_w, content_h, offset_x, offset_y = calculate_fit(
|
|
107
|
-
src_aspect, w, h, panel.fit
|
|
107
|
+
src_aspect, w, h, panel.fit, panel.align
|
|
108
108
|
)
|
|
109
109
|
rect = fitz.Rect(
|
|
110
110
|
x + offset_x,
|
|
@@ -71,11 +71,11 @@ class SVGComposer:
|
|
|
71
71
|
else:
|
|
72
72
|
h = w * src_aspect
|
|
73
73
|
|
|
74
|
-
# Calculate content dimensions using fit mode
|
|
74
|
+
# Calculate content dimensions using fit mode and alignment
|
|
75
75
|
from .units import calculate_fit
|
|
76
76
|
|
|
77
77
|
content_w, content_h, offset_x, offset_y = calculate_fit(
|
|
78
|
-
src_aspect, w, h, panel.fit
|
|
78
|
+
src_aspect, w, h, panel.fit, panel.align
|
|
79
79
|
)
|
|
80
80
|
|
|
81
81
|
# Group for the panel
|
|
@@ -4,6 +4,17 @@ from pathlib import Path
|
|
|
4
4
|
from pydantic import BaseModel, Field, field_validator, model_validator
|
|
5
5
|
|
|
6
6
|
FitMode = Literal["contain", "cover"]
|
|
7
|
+
Alignment = Literal[
|
|
8
|
+
"center",
|
|
9
|
+
"top",
|
|
10
|
+
"bottom",
|
|
11
|
+
"left",
|
|
12
|
+
"right",
|
|
13
|
+
"top-left",
|
|
14
|
+
"top-right",
|
|
15
|
+
"bottom-left",
|
|
16
|
+
"bottom-right",
|
|
17
|
+
]
|
|
7
18
|
|
|
8
19
|
|
|
9
20
|
class LabelStyle(BaseModel):
|
|
@@ -38,6 +49,10 @@ class Panel(BaseModel):
|
|
|
38
49
|
"contain",
|
|
39
50
|
description="How to fit the figure: contain (preserve aspect) or cover (fill and clip)",
|
|
40
51
|
)
|
|
52
|
+
align: Alignment = Field(
|
|
53
|
+
"center",
|
|
54
|
+
description="How to align content within cell when using contain fit mode",
|
|
55
|
+
)
|
|
41
56
|
label: Optional[str] = Field(
|
|
42
57
|
None, description="Override the auto-generated label text"
|
|
43
58
|
)
|
|
@@ -76,6 +91,9 @@ class LayoutNode(BaseModel):
|
|
|
76
91
|
None, description="Path to source file (PDF, SVG, or PNG)"
|
|
77
92
|
)
|
|
78
93
|
fit: FitMode = Field("contain", description="How to fit the figure in its cell")
|
|
94
|
+
align: Alignment = Field(
|
|
95
|
+
"center", description="How to align content within cell when using contain fit"
|
|
96
|
+
)
|
|
79
97
|
label: Optional[str] = Field(
|
|
80
98
|
None, description="Override the auto-generated label text"
|
|
81
99
|
)
|
|
@@ -21,16 +21,22 @@ def to_pt(value: float, units: str) -> float:
|
|
|
21
21
|
|
|
22
22
|
|
|
23
23
|
def calculate_fit(
|
|
24
|
-
src_aspect: float,
|
|
24
|
+
src_aspect: float,
|
|
25
|
+
cell_w: float,
|
|
26
|
+
cell_h: float,
|
|
27
|
+
fit_mode: str,
|
|
28
|
+
align: str = "center",
|
|
25
29
|
) -> tuple[float, float, float, float]:
|
|
26
30
|
"""
|
|
27
|
-
Calculate content dimensions and offset based on fit mode.
|
|
31
|
+
Calculate content dimensions and offset based on fit mode and alignment.
|
|
28
32
|
|
|
29
33
|
Args:
|
|
30
34
|
src_aspect: Source aspect ratio (height / width)
|
|
31
35
|
cell_w: Cell width in points
|
|
32
36
|
cell_h: Cell height in points
|
|
33
37
|
fit_mode: "contain" or "cover"
|
|
38
|
+
align: Alignment within cell (center, top, bottom, left, right,
|
|
39
|
+
top-left, top-right, bottom-left, bottom-right)
|
|
34
40
|
|
|
35
41
|
Returns:
|
|
36
42
|
Tuple of (content_w, content_h, offset_x, offset_y)
|
|
@@ -58,8 +64,41 @@ def calculate_fit(
|
|
|
58
64
|
content_w = cell_w
|
|
59
65
|
content_h = cell_w * src_aspect
|
|
60
66
|
|
|
61
|
-
#
|
|
62
|
-
|
|
63
|
-
|
|
67
|
+
# Calculate offsets based on alignment
|
|
68
|
+
space_x = cell_w - content_w
|
|
69
|
+
space_y = cell_h - content_h
|
|
70
|
+
|
|
71
|
+
# Parse alignment
|
|
72
|
+
if align == "center":
|
|
73
|
+
offset_x = space_x / 2
|
|
74
|
+
offset_y = space_y / 2
|
|
75
|
+
elif align == "top":
|
|
76
|
+
offset_x = space_x / 2
|
|
77
|
+
offset_y = 0
|
|
78
|
+
elif align == "bottom":
|
|
79
|
+
offset_x = space_x / 2
|
|
80
|
+
offset_y = space_y
|
|
81
|
+
elif align == "left":
|
|
82
|
+
offset_x = 0
|
|
83
|
+
offset_y = space_y / 2
|
|
84
|
+
elif align == "right":
|
|
85
|
+
offset_x = space_x
|
|
86
|
+
offset_y = space_y / 2
|
|
87
|
+
elif align == "top-left":
|
|
88
|
+
offset_x = 0
|
|
89
|
+
offset_y = 0
|
|
90
|
+
elif align == "top-right":
|
|
91
|
+
offset_x = space_x
|
|
92
|
+
offset_y = 0
|
|
93
|
+
elif align == "bottom-left":
|
|
94
|
+
offset_x = 0
|
|
95
|
+
offset_y = space_y
|
|
96
|
+
elif align == "bottom-right":
|
|
97
|
+
offset_x = space_x
|
|
98
|
+
offset_y = space_y
|
|
99
|
+
else:
|
|
100
|
+
# Unknown alignment, default to center
|
|
101
|
+
offset_x = space_x / 2
|
|
102
|
+
offset_y = space_y / 2
|
|
64
103
|
|
|
65
104
|
return content_w, content_h, offset_x, offset_y
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|