reportlab-layout 1.0.0__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.
@@ -0,0 +1,69 @@
1
+ """Une couche de mise en page à curseur au-dessus de reportlab.
2
+
3
+ Le canvas de reportlab dessine où on lui dit, en points, depuis le coin
4
+ bas-gauche. Ses ``platypus`` templates, à l'inverse, gèrent le flux mais
5
+ reprennent la main sur la page entière. Ce paquet occupe l'espace entre les
6
+ deux : un curseur qui descend dans la page, et le canvas resté accessible pour
7
+ tout ce qui doit être placé au point près.
8
+
9
+ from reportlab_layout import PDFMaker
10
+
11
+ with PDFMaker("bulletin.pdf", top=20) as doc:
12
+ doc.draw_paragraph("Bulletin du 3e trimestre", "Heading1 Centered")
13
+ doc.add_space()
14
+ doc.draw_table([["Matière", "Note"], ["Maths", "17"]])
15
+ """
16
+
17
+ from reportlab_layout.boxes import Box
18
+ from reportlab_layout.colors import ColorLike, to_color
19
+ from reportlab_layout.cursor import Cursor
20
+ from reportlab_layout.document import PDFMaker
21
+ from reportlab_layout.frames import FrameWriter
22
+ from reportlab_layout.geometry import Margins, PageGeometry, resolve_pagesize
23
+ from reportlab_layout.images import ImageSpec, image_spec, load_image
24
+ from reportlab_layout.metrics import (
25
+ TextMetrics,
26
+ baseline_offset,
27
+ cap_height,
28
+ font_ascent,
29
+ font_descent,
30
+ font_height,
31
+ string_width,
32
+ )
33
+ from reportlab_layout.numbering import NumberedCanvas
34
+ from reportlab_layout.shapes import ShapePainter
35
+ from reportlab_layout.styles import STYLES, StyleLike, add_style, make_stylesheet, resolve_style
36
+ from reportlab_layout.text import TextPainter
37
+
38
+ __version__ = "1.0.0"
39
+
40
+ __all__ = [
41
+ "STYLES",
42
+ "Box",
43
+ "ColorLike",
44
+ "Cursor",
45
+ "FrameWriter",
46
+ "ImageSpec",
47
+ "Margins",
48
+ "NumberedCanvas",
49
+ "PDFMaker",
50
+ "PageGeometry",
51
+ "ShapePainter",
52
+ "StyleLike",
53
+ "TextMetrics",
54
+ "TextPainter",
55
+ "__version__",
56
+ "add_style",
57
+ "baseline_offset",
58
+ "cap_height",
59
+ "font_ascent",
60
+ "font_descent",
61
+ "font_height",
62
+ "image_spec",
63
+ "load_image",
64
+ "make_stylesheet",
65
+ "resolve_pagesize",
66
+ "resolve_style",
67
+ "string_width",
68
+ "to_color",
69
+ ]
@@ -0,0 +1,30 @@
1
+ """Rectangle rendu par une opération de tracé."""
2
+
3
+ from typing import NamedTuple
4
+
5
+ __all__ = ["Box"]
6
+
7
+
8
+ class Box(NamedTuple):
9
+ """Rectangle effectivement occupé par un élément dessiné, repère canvas.
10
+
11
+ Se déballe comme un quadruplet ``(x, y, width, height)``. ``x, y`` est le
12
+ coin **bas-gauche**, comme partout dans reportlab.
13
+ """
14
+
15
+ x: float
16
+ y: float
17
+ width: float
18
+ height: float
19
+
20
+ @property
21
+ def right(self) -> float:
22
+ return self.x + self.width
23
+
24
+ @property
25
+ def top(self) -> float:
26
+ return self.y + self.height
27
+
28
+ @property
29
+ def center(self) -> tuple[float, float]:
30
+ return (self.x + self.width / 2, self.y + self.height / 2)
@@ -0,0 +1,31 @@
1
+ """Conversion des couleurs vers le type attendu par reportlab."""
2
+
3
+ from typing import TypeAlias
4
+
5
+ from reportlab.lib.colors import Color, HexColor, toColor
6
+
7
+ __all__ = ["ColorLike", "to_color"]
8
+
9
+ #: Tout ce qui peut désigner une couleur dans ce paquet. ``None`` signifie
10
+ #: « conserver la couleur courante du canvas ».
11
+ ColorLike: TypeAlias = Color | str | tuple[float, ...] | list[float] | None
12
+
13
+
14
+ def to_color(value: ColorLike) -> Color | None:
15
+ """Normalise une couleur en ``reportlab.lib.colors.Color``.
16
+
17
+ Accepte un ``Color``, un nom CSS ou une chaîne ``"#rrggbb"``, un triplet ou
18
+ quadruplet de flottants dans ``[0, 1]``. ``None`` est rendu tel quel : il
19
+ signifie « ne pas toucher à la couleur courante ».
20
+ """
21
+ if value is None or isinstance(value, Color):
22
+ return value
23
+ if isinstance(value, str):
24
+ return HexColor(value) if value.startswith("#") else toColor(value)
25
+ if isinstance(value, tuple | list):
26
+ if len(value) == 3:
27
+ return Color(*value)
28
+ if len(value) == 4:
29
+ return Color(value[0], value[1], value[2], alpha=value[3])
30
+ raise ValueError(f"Un tuple de couleur doit avoir 3 ou 4 composantes, reçu {len(value)}")
31
+ raise TypeError(f"Couleur non convertible : {value!r}")
@@ -0,0 +1,58 @@
1
+ """Curseur de flux : profondeur d'écriture courante dans la page."""
2
+
3
+ __all__ = ["Cursor"]
4
+
5
+
6
+ class Cursor:
7
+ """Suit la profondeur d'écriture, mesurée depuis le haut de la page.
8
+
9
+ La profondeur croît vers le bas, contrairement à l'ordonnée du canvas
10
+ reportlab. La conversion est du ressort de
11
+ :class:`~reportlab_layout.geometry.PageGeometry`.
12
+ """
13
+
14
+ def __init__(self, top: float, bottom_depth: float) -> None:
15
+ self._top = top
16
+ self._bottom_depth = bottom_depth
17
+ self._depth = top
18
+
19
+ def __repr__(self) -> str:
20
+ return f"Cursor(depth={self._depth:.1f}, remaining={self.remaining:.1f})"
21
+
22
+ @property
23
+ def depth(self) -> float:
24
+ """Profondeur courante, en points."""
25
+ return self._depth
26
+
27
+ @depth.setter
28
+ def depth(self, value: float) -> None:
29
+ self._depth = float(value)
30
+
31
+ @property
32
+ def top(self) -> float:
33
+ """Profondeur de départ, c'est-à-dire la marge haute."""
34
+ return self._top
35
+
36
+ @property
37
+ def bottom_depth(self) -> float:
38
+ """Profondeur de la limite basse de la zone de contenu."""
39
+ return self._bottom_depth
40
+
41
+ @property
42
+ def remaining(self) -> float:
43
+ """Hauteur restante avant la marge basse. Négative en cas de débordement."""
44
+ return self._bottom_depth - self._depth
45
+
46
+ def fits(self, height: float) -> bool:
47
+ """Vrai si un élément de hauteur ``height`` tient encore sur la page."""
48
+ return height <= self.remaining
49
+
50
+ def reset(self) -> float:
51
+ """Ramène le curseur en haut de la zone de contenu."""
52
+ self._depth = self._top
53
+ return self._depth
54
+
55
+ def advance(self, height: float) -> float:
56
+ """Descend le curseur de ``height`` points et rend la nouvelle profondeur."""
57
+ self._depth += height
58
+ return self._depth