figquilt 0.1.5__py3-none-any.whl → 0.1.7__py3-none-any.whl
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/compose_pdf.py +5 -5
- figquilt/compose_svg.py +5 -5
- figquilt/grid.py +1 -0
- figquilt/layout.py +23 -3
- figquilt/units.py +44 -10
- {figquilt-0.1.5.dist-info → figquilt-0.1.7.dist-info}/METADATA +1 -1
- figquilt-0.1.7.dist-info/RECORD +14 -0
- figquilt-0.1.5.dist-info/RECORD +0 -14
- {figquilt-0.1.5.dist-info → figquilt-0.1.7.dist-info}/WHEEL +0 -0
- {figquilt-0.1.5.dist-info → figquilt-0.1.7.dist-info}/entry_points.txt +0 -0
figquilt/compose_pdf.py
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import fitz
|
|
2
2
|
from pathlib import Path
|
|
3
3
|
from .layout import Layout, Panel
|
|
4
|
-
from .units import
|
|
4
|
+
from .units import to_pt
|
|
5
5
|
from .errors import FigQuiltError
|
|
6
6
|
|
|
7
7
|
|
|
@@ -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,
|
|
@@ -155,8 +155,8 @@ class PDFComposer:
|
|
|
155
155
|
# PyMuPDF insert_text uses 'baseline', so (0,0) is bottom-left of text char.
|
|
156
156
|
# We need to shift Y down by approximately the font sizing to match SVG visual.
|
|
157
157
|
|
|
158
|
-
pos_x = rect.x0 +
|
|
159
|
-
raw_y = rect.y0 +
|
|
158
|
+
pos_x = rect.x0 + to_pt(style.offset_x, self.units)
|
|
159
|
+
raw_y = rect.y0 + to_pt(style.offset_y, self.units)
|
|
160
160
|
|
|
161
161
|
# Approximate baseline shift: font_size
|
|
162
162
|
# (A more precise way uses font.ascender, but for basic standard fonts, size is decent proxy for visual top->baseline)
|
figquilt/compose_svg.py
CHANGED
|
@@ -2,7 +2,7 @@ from pathlib import Path
|
|
|
2
2
|
import base64
|
|
3
3
|
from lxml import etree
|
|
4
4
|
from .layout import Layout, Panel
|
|
5
|
-
from .units import
|
|
5
|
+
from .units import to_pt
|
|
6
6
|
from .errors import FigQuiltError
|
|
7
7
|
import fitz
|
|
8
8
|
|
|
@@ -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
|
|
@@ -179,8 +179,8 @@ class SVGComposer:
|
|
|
179
179
|
text_str = text_str.upper()
|
|
180
180
|
|
|
181
181
|
# Offset relative to the content position
|
|
182
|
-
x = offset_x +
|
|
183
|
-
y = offset_y +
|
|
182
|
+
x = offset_x + to_pt(style.offset_x, self.units)
|
|
183
|
+
y = offset_y + to_pt(style.offset_y, self.units)
|
|
184
184
|
|
|
185
185
|
# Create text element
|
|
186
186
|
txt = etree.SubElement(parent, "text")
|
figquilt/grid.py
CHANGED
figquilt/layout.py
CHANGED
|
@@ -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):
|
|
@@ -13,10 +24,12 @@ class LabelStyle(BaseModel):
|
|
|
13
24
|
auto_sequence: bool = Field(True, description="Auto-generate labels A, B, C...")
|
|
14
25
|
font_family: str = Field("Helvetica", description="Font family for labels")
|
|
15
26
|
font_size_pt: float = Field(8.0, description="Font size in points")
|
|
16
|
-
|
|
17
|
-
2.0, description="Horizontal offset from panel edge (
|
|
27
|
+
offset_x: float = Field(
|
|
28
|
+
2.0, description="Horizontal offset from panel edge (in page units)"
|
|
29
|
+
)
|
|
30
|
+
offset_y: float = Field(
|
|
31
|
+
2.0, description="Vertical offset from panel edge (in page units)"
|
|
18
32
|
)
|
|
19
|
-
offset_y_mm: float = Field(2.0, description="Vertical offset from panel edge (mm)")
|
|
20
33
|
bold: bool = Field(True, description="Use bold font")
|
|
21
34
|
uppercase: bool = Field(True, description="Use uppercase letters")
|
|
22
35
|
|
|
@@ -36,6 +49,10 @@ class Panel(BaseModel):
|
|
|
36
49
|
"contain",
|
|
37
50
|
description="How to fit the figure: contain (preserve aspect) or cover (fill and clip)",
|
|
38
51
|
)
|
|
52
|
+
align: Alignment = Field(
|
|
53
|
+
"center",
|
|
54
|
+
description="How to align content within cell when using contain fit mode",
|
|
55
|
+
)
|
|
39
56
|
label: Optional[str] = Field(
|
|
40
57
|
None, description="Override the auto-generated label text"
|
|
41
58
|
)
|
|
@@ -74,6 +91,9 @@ class LayoutNode(BaseModel):
|
|
|
74
91
|
None, description="Path to source file (PDF, SVG, or PNG)"
|
|
75
92
|
)
|
|
76
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
|
+
)
|
|
77
97
|
label: Optional[str] = Field(
|
|
78
98
|
None, description="Override the auto-generated label text"
|
|
79
99
|
)
|
figquilt/units.py
CHANGED
|
@@ -3,11 +3,6 @@ def mm_to_pt(mm: float) -> float:
|
|
|
3
3
|
return mm * 72 / 25.4
|
|
4
4
|
|
|
5
5
|
|
|
6
|
-
def pt_to_mm(pt: float) -> float:
|
|
7
|
-
"""Converts points to millimeters."""
|
|
8
|
-
return pt * 25.4 / 72
|
|
9
|
-
|
|
10
|
-
|
|
11
6
|
def inches_to_pt(inches: float) -> float:
|
|
12
7
|
"""Converts inches to points (1 inch = 72 pts)."""
|
|
13
8
|
return inches * 72
|
|
@@ -26,16 +21,22 @@ def to_pt(value: float, units: str) -> float:
|
|
|
26
21
|
|
|
27
22
|
|
|
28
23
|
def calculate_fit(
|
|
29
|
-
src_aspect: float,
|
|
24
|
+
src_aspect: float,
|
|
25
|
+
cell_w: float,
|
|
26
|
+
cell_h: float,
|
|
27
|
+
fit_mode: str,
|
|
28
|
+
align: str = "center",
|
|
30
29
|
) -> tuple[float, float, float, float]:
|
|
31
30
|
"""
|
|
32
|
-
Calculate content dimensions and offset based on fit mode.
|
|
31
|
+
Calculate content dimensions and offset based on fit mode and alignment.
|
|
33
32
|
|
|
34
33
|
Args:
|
|
35
34
|
src_aspect: Source aspect ratio (height / width)
|
|
36
35
|
cell_w: Cell width in points
|
|
37
36
|
cell_h: Cell height in points
|
|
38
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)
|
|
39
40
|
|
|
40
41
|
Returns:
|
|
41
42
|
Tuple of (content_w, content_h, offset_x, offset_y)
|
|
@@ -63,8 +64,41 @@ def calculate_fit(
|
|
|
63
64
|
content_w = cell_w
|
|
64
65
|
content_h = cell_w * src_aspect
|
|
65
66
|
|
|
66
|
-
#
|
|
67
|
-
|
|
68
|
-
|
|
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
|
|
69
103
|
|
|
70
104
|
return content_w, content_h, offset_x, offset_y
|
|
@@ -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>
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
figquilt/__init__.py,sha256=91447944015cec709e8aa7655f7e9d64e1e4508e7023a57fe3746911c0fc6fed,22
|
|
2
|
+
figquilt/cli.py,sha256=04af949a9e93cc7fdc2ded5c892cf06356d44903c1c4fbfdea2a4a10429616de,7062
|
|
3
|
+
figquilt/compose_pdf.py,sha256=d593a009863abab96dfef54602407e3e73d318ce141277dcc3efaf2324a1c96b,6428
|
|
4
|
+
figquilt/compose_svg.py,sha256=690feedc5788451ed8d30d32a92680956fcb86b7bc9eae7a7ffbf6b5958c3a0c,7683
|
|
5
|
+
figquilt/errors.py,sha256=6f4001dcae85d2171f7aa7df4161926771dbe8c21068ccb70a7865298f05cf2b,298
|
|
6
|
+
figquilt/grid.py,sha256=e977fbf92e3e2a0860f598c89e2f23071d51e66d2c8c1e7d6fab34bd75925631,3084
|
|
7
|
+
figquilt/images.py,sha256=c613655fb3a0790fca98182c558c584e632a8822225220a5feb6080c7c68eb9e,815
|
|
8
|
+
figquilt/layout.py,sha256=e3dd82c1feec18d84cb21bd8bb62c5dd0fc282e45adf5fb64cba2dab87d43422,6449
|
|
9
|
+
figquilt/parser.py,sha256=9658c73e9964f2060afd01da4123aa0063c8062d382443c1e1caac79e7ebf648,4029
|
|
10
|
+
figquilt/units.py,sha256=2b38d5d7a474211e64588c075943fbcdfc29c98ab7db7dc1c727ea5946351f0e,3093
|
|
11
|
+
figquilt-0.1.7.dist-info/WHEEL,sha256=76443c98c0efcfdd1191eac5fa1d8223dba1c474dbd47676674a255e7ca48770,79
|
|
12
|
+
figquilt-0.1.7.dist-info/entry_points.txt,sha256=8f70ce07f585bed28aca569052c7f0029384ac67c5e738faeb0daeb31695bc85,48
|
|
13
|
+
figquilt-0.1.7.dist-info/METADATA,sha256=267f47988e7304d05eb89698bf9989b95971763159df4d0cf9877f1cdbb53905,6217
|
|
14
|
+
figquilt-0.1.7.dist-info/RECORD,,
|
figquilt-0.1.5.dist-info/RECORD
DELETED
|
@@ -1,14 +0,0 @@
|
|
|
1
|
-
figquilt/__init__.py,sha256=91447944015cec709e8aa7655f7e9d64e1e4508e7023a57fe3746911c0fc6fed,22
|
|
2
|
-
figquilt/cli.py,sha256=04af949a9e93cc7fdc2ded5c892cf06356d44903c1c4fbfdea2a4a10429616de,7062
|
|
3
|
-
figquilt/compose_pdf.py,sha256=d1b3003597a0b549e60671aa06c7ea2d0a84fd94590240c6e63f3dcb9f4c0d77,6399
|
|
4
|
-
figquilt/compose_svg.py,sha256=f61f1b43e252f77b54e40bd7b34dc55f729e8c954414aa74e2d8404c9a004f9a,7654
|
|
5
|
-
figquilt/errors.py,sha256=6f4001dcae85d2171f7aa7df4161926771dbe8c21068ccb70a7865298f05cf2b,298
|
|
6
|
-
figquilt/grid.py,sha256=b56aa9fabe023e9c9447d4d13c573a7ad64a60a1de38435fceb4429ecfaed00f,3050
|
|
7
|
-
figquilt/images.py,sha256=c613655fb3a0790fca98182c558c584e632a8822225220a5feb6080c7c68eb9e,815
|
|
8
|
-
figquilt/layout.py,sha256=5bd09ce23678b0e12dc21c32a0234dc143265dcd5a2c5d1512225d0b7958172b,5998
|
|
9
|
-
figquilt/parser.py,sha256=9658c73e9964f2060afd01da4123aa0063c8062d382443c1e1caac79e7ebf648,4029
|
|
10
|
-
figquilt/units.py,sha256=25098b6d7559cb78214fe1a9889fd38ce88633993d0c92819cc609510654275d,2124
|
|
11
|
-
figquilt-0.1.5.dist-info/WHEEL,sha256=76443c98c0efcfdd1191eac5fa1d8223dba1c474dbd47676674a255e7ca48770,79
|
|
12
|
-
figquilt-0.1.5.dist-info/entry_points.txt,sha256=8f70ce07f585bed28aca569052c7f0029384ac67c5e738faeb0daeb31695bc85,48
|
|
13
|
-
figquilt-0.1.5.dist-info/METADATA,sha256=fb3252c7991719d2112a9cf1e09ebd4c95f46aa4914e3892f3ab46efa08b29bb,6217
|
|
14
|
-
figquilt-0.1.5.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|