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 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 mm_to_pt, to_pt
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 + mm_to_pt(style.offset_x_mm)
159
- raw_y = rect.y0 + mm_to_pt(style.offset_y_mm)
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 mm_to_pt, to_pt
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 + mm_to_pt(style.offset_x_mm)
183
- y = offset_y + mm_to_pt(style.offset_y_mm)
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
@@ -58,6 +58,7 @@ def _resolve_node(
58
58
  width=width,
59
59
  height=height,
60
60
  fit=node.fit,
61
+ align=node.align,
61
62
  label=node.label,
62
63
  label_style=node.label_style,
63
64
  )
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
- offset_x_mm: float = Field(
17
- 2.0, description="Horizontal offset from panel edge (mm)"
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, cell_w: float, cell_h: float, fit_mode: str
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
- # Center in cell
67
- offset_x = (cell_w - content_w) / 2
68
- offset_y = (cell_h - content_h) / 2
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.5
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,,
@@ -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,,