classes.c21_page

  1import drawBot
  2
  3from lib import layout
  4from classes import KBox
  5
  6
  7class KPage(KBox):
  8    def __init__(
  9        self,
 10        size: layout.PageSize = "A5",
 11        margin: tuple = 5,
 12        aside: int = 0,
 13        spread: bool = False,
 14        render: bool = True,
 15    ):
 16        """
 17        Extends `classes.c20_box.KBox` to form a page object.
 18
 19        Args:
 20            size: Page size. See `lib.layout.PageSize`.
 21            margin: Margin in mm.
 22            aside: Width of pagination column.
 23            spread: Divide at spine for 2-page PDF view.
 24            render: Draw immediately. Otherwise, call `render()` manually.
 25        """
 26        super().__init__(size)
 27
 28        if render:
 29            self.render()
 30
 31        self.aside: int = aside
 32        """Width of aside/pagination column."""
 33        self.margin: tuple[int] = layout.mirror(margin) if self.isLeftSide else margin
 34        """Margin for the page (mirrored for left side)."""
 35        self._isSpread: bool = spread
 36        """Used to draw a spine for 2-page PDF view."""
 37
 38        self._divide()
 39        self._draftBoxes()
 40
 41    def _draftBoxes(self) -> None:
 42        """Set up the frame and body `classes.c20_box.KBox` objects."""
 43        self.frame = KBox(layout.shrink(frame=self.coords, margin=self.margin))
 44        self.body = KBox(
 45            layout.shrinkWidth(
 46                frame=self.frame.coords,
 47                amount=self.aside,
 48                origin="right" if self.isRightSide else "left",
 49                mode="mm",
 50            )
 51        )
 52
 53    @property
 54    def isLeftSide(self) -> bool:
 55        """True if the page is on the left side."""
 56        return layout.pageIsEven()
 57
 58    @property
 59    def isRightSide(self) -> bool:
 60        """True if the page is on the right side."""
 61        return not self.isLeftSide
 62
 63    @property
 64    def pageNumber(self) -> int:
 65        """The current page number."""
 66        return drawBot.pageCount()
 67
 68    def fillBackground(self, color: list[int]) -> None:
 69        """
 70        Fill the background of the page with a color.
 71
 72        Args:
 73            color: RGB color to fill with.
 74
 75        Example:
 76        ```
 77        page.fillBackground((0.5, 0.5, 0.5))
 78        ```
 79        """
 80        with drawBot.savedState():
 81            drawBot.fill(*color)
 82            drawBot.rect(*self.coords)
 83
 84        self._divide()
 85
 86    def paginate(self) -> None:
 87        """Draw the page number with contextual alignment. Padded to 2 digits."""
 88        drawBot.textBox(
 89            str(drawBot.pageCount()).rjust(2, "0"),
 90            self.frame.coords,
 91            align="left" if self.isRightSide else "right",
 92        )
 93
 94    def addHeader(self, title: str = None, gap: int = 5) -> None:
 95        """
 96        Add a header `classes.c20_box.KBox` to the page.
 97
 98        Args:
 99            title (optional): Text to write in the header.
100            gap: Space between header and body.
101        """
102        self.header = KBox(layout.draftBar(self.body.coords))
103        self.body.coords = layout.draftBody(
104            self.body.coords, header=self.header.coords, gap=gap
105        )
106        if title:
107            drawBot.textBox(title, self.header.coords)
108
109    def addFooter(self, title: str = None, gap: int = 5) -> None:
110        """
111        Add a footer `classes.c20_box.KBox` to the page.
112
113        Args:
114            title (optional): Text to write in the footer.
115            gap: Space between footer and body.
116        """
117        self.footer = KBox(layout.draftBar(self.body.coords, position="bottom"))
118        self.body.coords = layout.draftBody(
119            self.body.coords, footer=self.footer.coords, gap=gap
120        )
121        if title:
122            drawBot.textBox(title, self.footer.coords)
123
124    def addBleed(self, bleed: int = 7, preview: bool = True) -> None:
125        """
126        Add crop marks and optional bleed preview.
127
128        Args:
129            bleed: Bleed size.
130            preview: Fill bleed area with black for preview.
131        """
132        if preview:
133            self.fillBackground((0,))
134        self.shrink(bleed)
135        self.addCropMarks()
136        if preview:
137            self.fillBackground((1,))
138
139        self._draftBoxes()
140
141    def debug(self, enabled: bool = True) -> None:
142        """
143        Debug all child `classes.c20_box.KBox` instances using `lib.layout.xray`.
144
145        Args:
146            enabled: Toggle debug mode.
147        """
148        if not enabled:
149            return
150        coordsList = [self.frame.coords, self.body.coords]
151        if hasattr(self, "header"):
152            coordsList.append(self.header.coords)
153        if hasattr(self, "footer"):
154            coordsList.append(self.footer.coords)
155        layout.xray(coordsList)
156
157    def _divide(self) -> None:
158        """Draw a spine divider for 2-page PDF view."""
159        if not self._isSpread or not self.isLeftSide:
160            return
161
162        with drawBot.savedState():
163            drawBot.fill(None)
164            drawBot.stroke(0, 0.25)
165            drawBot.strokeWidth(3)
166            drawBot.line((self.right, self.bottom), (self.right, self.top))
167
168    def render(self) -> None:
169        """Render the page to the canvas via `drawBot`."""
170        drawBot.newPage(*self.dimensions)
class KPage(classes.c20_box.KBox):
  8class KPage(KBox):
  9    def __init__(
 10        self,
 11        size: layout.PageSize = "A5",
 12        margin: tuple = 5,
 13        aside: int = 0,
 14        spread: bool = False,
 15        render: bool = True,
 16    ):
 17        """
 18        Extends `classes.c20_box.KBox` to form a page object.
 19
 20        Args:
 21            size: Page size. See `lib.layout.PageSize`.
 22            margin: Margin in mm.
 23            aside: Width of pagination column.
 24            spread: Divide at spine for 2-page PDF view.
 25            render: Draw immediately. Otherwise, call `render()` manually.
 26        """
 27        super().__init__(size)
 28
 29        if render:
 30            self.render()
 31
 32        self.aside: int = aside
 33        """Width of aside/pagination column."""
 34        self.margin: tuple[int] = layout.mirror(margin) if self.isLeftSide else margin
 35        """Margin for the page (mirrored for left side)."""
 36        self._isSpread: bool = spread
 37        """Used to draw a spine for 2-page PDF view."""
 38
 39        self._divide()
 40        self._draftBoxes()
 41
 42    def _draftBoxes(self) -> None:
 43        """Set up the frame and body `classes.c20_box.KBox` objects."""
 44        self.frame = KBox(layout.shrink(frame=self.coords, margin=self.margin))
 45        self.body = KBox(
 46            layout.shrinkWidth(
 47                frame=self.frame.coords,
 48                amount=self.aside,
 49                origin="right" if self.isRightSide else "left",
 50                mode="mm",
 51            )
 52        )
 53
 54    @property
 55    def isLeftSide(self) -> bool:
 56        """True if the page is on the left side."""
 57        return layout.pageIsEven()
 58
 59    @property
 60    def isRightSide(self) -> bool:
 61        """True if the page is on the right side."""
 62        return not self.isLeftSide
 63
 64    @property
 65    def pageNumber(self) -> int:
 66        """The current page number."""
 67        return drawBot.pageCount()
 68
 69    def fillBackground(self, color: list[int]) -> None:
 70        """
 71        Fill the background of the page with a color.
 72
 73        Args:
 74            color: RGB color to fill with.
 75
 76        Example:
 77        ```
 78        page.fillBackground((0.5, 0.5, 0.5))
 79        ```
 80        """
 81        with drawBot.savedState():
 82            drawBot.fill(*color)
 83            drawBot.rect(*self.coords)
 84
 85        self._divide()
 86
 87    def paginate(self) -> None:
 88        """Draw the page number with contextual alignment. Padded to 2 digits."""
 89        drawBot.textBox(
 90            str(drawBot.pageCount()).rjust(2, "0"),
 91            self.frame.coords,
 92            align="left" if self.isRightSide else "right",
 93        )
 94
 95    def addHeader(self, title: str = None, gap: int = 5) -> None:
 96        """
 97        Add a header `classes.c20_box.KBox` to the page.
 98
 99        Args:
100            title (optional): Text to write in the header.
101            gap: Space between header and body.
102        """
103        self.header = KBox(layout.draftBar(self.body.coords))
104        self.body.coords = layout.draftBody(
105            self.body.coords, header=self.header.coords, gap=gap
106        )
107        if title:
108            drawBot.textBox(title, self.header.coords)
109
110    def addFooter(self, title: str = None, gap: int = 5) -> None:
111        """
112        Add a footer `classes.c20_box.KBox` to the page.
113
114        Args:
115            title (optional): Text to write in the footer.
116            gap: Space between footer and body.
117        """
118        self.footer = KBox(layout.draftBar(self.body.coords, position="bottom"))
119        self.body.coords = layout.draftBody(
120            self.body.coords, footer=self.footer.coords, gap=gap
121        )
122        if title:
123            drawBot.textBox(title, self.footer.coords)
124
125    def addBleed(self, bleed: int = 7, preview: bool = True) -> None:
126        """
127        Add crop marks and optional bleed preview.
128
129        Args:
130            bleed: Bleed size.
131            preview: Fill bleed area with black for preview.
132        """
133        if preview:
134            self.fillBackground((0,))
135        self.shrink(bleed)
136        self.addCropMarks()
137        if preview:
138            self.fillBackground((1,))
139
140        self._draftBoxes()
141
142    def debug(self, enabled: bool = True) -> None:
143        """
144        Debug all child `classes.c20_box.KBox` instances using `lib.layout.xray`.
145
146        Args:
147            enabled: Toggle debug mode.
148        """
149        if not enabled:
150            return
151        coordsList = [self.frame.coords, self.body.coords]
152        if hasattr(self, "header"):
153            coordsList.append(self.header.coords)
154        if hasattr(self, "footer"):
155            coordsList.append(self.footer.coords)
156        layout.xray(coordsList)
157
158    def _divide(self) -> None:
159        """Draw a spine divider for 2-page PDF view."""
160        if not self._isSpread or not self.isLeftSide:
161            return
162
163        with drawBot.savedState():
164            drawBot.fill(None)
165            drawBot.stroke(0, 0.25)
166            drawBot.strokeWidth(3)
167            drawBot.line((self.right, self.bottom), (self.right, self.top))
168
169    def render(self) -> None:
170        """Render the page to the canvas via `drawBot`."""
171        drawBot.newPage(*self.dimensions)

A layout primitive with coordinates, dimensions and spatial utilities.

KPage( size: None | str | tuple[int, int] = 'A5', margin: tuple = 5, aside: int = 0, spread: bool = False, render: bool = True)
 9    def __init__(
10        self,
11        size: layout.PageSize = "A5",
12        margin: tuple = 5,
13        aside: int = 0,
14        spread: bool = False,
15        render: bool = True,
16    ):
17        """
18        Extends `classes.c20_box.KBox` to form a page object.
19
20        Args:
21            size: Page size. See `lib.layout.PageSize`.
22            margin: Margin in mm.
23            aside: Width of pagination column.
24            spread: Divide at spine for 2-page PDF view.
25            render: Draw immediately. Otherwise, call `render()` manually.
26        """
27        super().__init__(size)
28
29        if render:
30            self.render()
31
32        self.aside: int = aside
33        """Width of aside/pagination column."""
34        self.margin: tuple[int] = layout.mirror(margin) if self.isLeftSide else margin
35        """Margin for the page (mirrored for left side)."""
36        self._isSpread: bool = spread
37        """Used to draw a spine for 2-page PDF view."""
38
39        self._divide()
40        self._draftBoxes()

Extends classes.c20_box.KBox to form a page object.

Arguments:
  • size: Page size. See lib.layout.PageSize.
  • margin: Margin in mm.
  • aside: Width of pagination column.
  • spread: Divide at spine for 2-page PDF view.
  • render: Draw immediately. Otherwise, call render() manually.
aside: int

Width of aside/pagination column.

margin: tuple[int]

Margin for the page (mirrored for left side).

isLeftSide: bool
54    @property
55    def isLeftSide(self) -> bool:
56        """True if the page is on the left side."""
57        return layout.pageIsEven()

True if the page is on the left side.

isRightSide: bool
59    @property
60    def isRightSide(self) -> bool:
61        """True if the page is on the right side."""
62        return not self.isLeftSide

True if the page is on the right side.

pageNumber: int
64    @property
65    def pageNumber(self) -> int:
66        """The current page number."""
67        return drawBot.pageCount()

The current page number.

def fillBackground(self, color: list[int]) -> None:
69    def fillBackground(self, color: list[int]) -> None:
70        """
71        Fill the background of the page with a color.
72
73        Args:
74            color: RGB color to fill with.
75
76        Example:
77        ```
78        page.fillBackground((0.5, 0.5, 0.5))
79        ```
80        """
81        with drawBot.savedState():
82            drawBot.fill(*color)
83            drawBot.rect(*self.coords)
84
85        self._divide()

Fill the background of the page with a color.

Arguments:
  • color: RGB color to fill with.

Example:

page.fillBackground((0.5, 0.5, 0.5))
def paginate(self) -> None:
87    def paginate(self) -> None:
88        """Draw the page number with contextual alignment. Padded to 2 digits."""
89        drawBot.textBox(
90            str(drawBot.pageCount()).rjust(2, "0"),
91            self.frame.coords,
92            align="left" if self.isRightSide else "right",
93        )

Draw the page number with contextual alignment. Padded to 2 digits.

def addHeader(self, title: str = None, gap: int = 5) -> None:
 95    def addHeader(self, title: str = None, gap: int = 5) -> None:
 96        """
 97        Add a header `classes.c20_box.KBox` to the page.
 98
 99        Args:
100            title (optional): Text to write in the header.
101            gap: Space between header and body.
102        """
103        self.header = KBox(layout.draftBar(self.body.coords))
104        self.body.coords = layout.draftBody(
105            self.body.coords, header=self.header.coords, gap=gap
106        )
107        if title:
108            drawBot.textBox(title, self.header.coords)

Add a header classes.c20_box.KBox to the page.

Arguments:
  • title (optional): Text to write in the header.
  • gap: Space between header and body.
def addFooter(self, title: str = None, gap: int = 5) -> None:
110    def addFooter(self, title: str = None, gap: int = 5) -> None:
111        """
112        Add a footer `classes.c20_box.KBox` to the page.
113
114        Args:
115            title (optional): Text to write in the footer.
116            gap: Space between footer and body.
117        """
118        self.footer = KBox(layout.draftBar(self.body.coords, position="bottom"))
119        self.body.coords = layout.draftBody(
120            self.body.coords, footer=self.footer.coords, gap=gap
121        )
122        if title:
123            drawBot.textBox(title, self.footer.coords)

Add a footer classes.c20_box.KBox to the page.

Arguments:
  • title (optional): Text to write in the footer.
  • gap: Space between footer and body.
def addBleed(self, bleed: int = 7, preview: bool = True) -> None:
125    def addBleed(self, bleed: int = 7, preview: bool = True) -> None:
126        """
127        Add crop marks and optional bleed preview.
128
129        Args:
130            bleed: Bleed size.
131            preview: Fill bleed area with black for preview.
132        """
133        if preview:
134            self.fillBackground((0,))
135        self.shrink(bleed)
136        self.addCropMarks()
137        if preview:
138            self.fillBackground((1,))
139
140        self._draftBoxes()

Add crop marks and optional bleed preview.

Arguments:
  • bleed: Bleed size.
  • preview: Fill bleed area with black for preview.
def debug(self, enabled: bool = True) -> None:
142    def debug(self, enabled: bool = True) -> None:
143        """
144        Debug all child `classes.c20_box.KBox` instances using `lib.layout.xray`.
145
146        Args:
147            enabled: Toggle debug mode.
148        """
149        if not enabled:
150            return
151        coordsList = [self.frame.coords, self.body.coords]
152        if hasattr(self, "header"):
153            coordsList.append(self.header.coords)
154        if hasattr(self, "footer"):
155            coordsList.append(self.footer.coords)
156        layout.xray(coordsList)

Debug all child classes.c20_box.KBox instances using lib.layout.xray.

Arguments:
  • enabled: Toggle debug mode.
def render(self) -> None:
169    def render(self) -> None:
170        """Render the page to the canvas via `drawBot`."""
171        drawBot.newPage(*self.dimensions)

Render the page to the canvas via drawBot.