home · contact · privacy
Some code restructuring. master
authorPlom Heller <plom@plomlompom.com>
Fri, 10 Apr 2026 07:32:04 +0000 (09:32 +0200)
committerPlom Heller <plom@plomlompom.com>
Fri, 10 Apr 2026 07:32:04 +0000 (09:32 +0200)
bookmaker.py

index 92e7a38f7a15b472a237276d4822860d53993c24..e43d7549da5adb9a6ffa2d5323683472f8582220 100755 (executable)
@@ -167,7 +167,7 @@ class Page:
             ) -> None:
         'Draw horizontal or vertical lines.'
         min_x, min_y, max_x, max_y = self._pypdf.mediabox
             ) -> None:
         'Draw horizontal or vertical lines.'
         min_x, min_y, max_x, max_y = self._pypdf.mediabox
-        full_lines = []
+        full_lines: list[tuple[float, float, float, float]] = []
         for is_horizontal, position in lines:
             if is_horizontal:
                 x0, x1 = min_x, max_x
         for is_horizontal, position in lines:
             if is_horizontal:
                 x0, x1 = min_x, max_x
@@ -176,7 +176,7 @@ class Page:
                 y0, y1 = min_y, max_y
                 x0 = x1 = position
             full_lines += [(x0, y0, x1, y1)]
                 y0, y1 = min_y, max_y
                 x0 = x1 = position
             full_lines += [(x0, y0, x1, y1)]
-        self.draw_lines(full_lines, rgb_color, line_width)
+        self.draw_lines(tuple(full_lines), rgb_color, line_width)
 
     def draw_borders(
             self,
 
     def draw_borders(
             self,
@@ -190,6 +190,88 @@ class Page:
                 rgb_color,
                 line_width)
 
                 rgb_color,
                 line_width)
 
+    def nup4_transforms(
+            self,
+            nup4_geometry: 'Nup4Geometry',
+            nup4_i: int
+            ) -> None:
+        'Perform inner, outer page transforms to position in nup4_geometry.'
+        # Apply crop instructions adequate to position in nup4 geometry.
+        self.translate(
+                tx=((-self.crop.left) if nup4_i in {0, 2}
+                    else  # in {1, 3}
+                    A4_WIDTH / self.crop.zoom - (A4_WIDTH - self.crop.right)),
+                ty=A4_HEIGHT / self.crop.zoom - (A4_HEIGHT - self.crop.top))
+        self.scale(self.crop.zoom * nup4_geometry.shrink_for_spine)
+        if nup4_i in {2, 3}:
+            self.translate(
+                ty=-2 * nup4_geometry.margin / nup4_geometry.shrink_for_margin)
+        # Shrink and position into nup4_geometry as per its position nup4_i.
+        self.translate(ty=(1 - nup4_geometry.shrink_for_spine) * A4_HEIGHT)
+        if nup4_i in {0, 1}:
+            y_section = A4_HEIGHT
+            self.set_box(bottom=A4_HEIGHT/2, top=A4_HEIGHT)
+        else:  # nup4_in in {2, 3}
+            y_section = 0
+            self.set_box(bottom=0, top=A4_HEIGHT/2)
+        x_section: float
+        if nup4_i in {0, 2}:
+            x_section = 0
+            self.set_box(left=0, right=A4_WIDTH/2)
+        else:  # nup4_in in {1, 3}
+            self.translate(tx=(1 - nup4_geometry.shrink_for_spine) * A4_WIDTH)
+            x_section = A4_WIDTH
+            self.set_box(left=A4_WIDTH/2, right=A4_WIDTH)
+        self.translate(tx=x_section, ty=y_section)
+        self.scale(QUARTER_SCALE_FACTOR)
+
+    def ornate_nup4(
+            self,
+            is_front_page: bool,
+            nup4_geometry: 'Nup4Geometry',
+            analyzing: bool
+            ) -> None:
+        'Apply nup4 line guides.'
+
+        def draw_book_binding_cut_guide(
+                x_spine_limit: float,
+                direction: int
+                ) -> None:
+            directed_half_width = 0.5 * CUT_WIDTH * direction
+            outer_start_x = x_spine_limit - directed_half_width
+            inner_start_x = x_spine_limit + directed_half_width
+            middle_point_y = A4_HEIGHT/2 + MIDDLE_POINT_DEPTH * direction
+            end_point_y = A4_HEIGHT/2 + CUT_DEPTH * direction
+            self.draw_lines(((inner_start_x, A4_HEIGHT/2,
+                              x_spine_limit, end_point_y),
+                             (x_spine_limit, end_point_y,
+                              x_spine_limit, middle_point_y),
+                             (x_spine_limit, middle_point_y,
+                              outer_start_x, A4_HEIGHT/2)),
+                            line_width=0.2)
+
+        if analyzing:
+            self.draw_borders((0.5, 1.0, 1.0))
+        self.draw_orth_lines(((True, self.box['top']/2),
+                              (False, self.box['right']/2)),
+                             line_width=0.1)
+        printable_offset_x = nup4_geometry.margin
+        printable_offset_y = nup4_geometry.margin * A4_HEIGHT / A4_WIDTH
+        self.scale(nup4_geometry.shrink_for_margin)
+        self.translate(tx=printable_offset_x, ty=printable_offset_y)
+        if not (analyzing or is_front_page):
+            return
+        x_left_spine_limit = A4_WIDTH/2 * nup4_geometry.shrink_for_spine
+        x_right_spine_limit = A4_WIDTH - x_left_spine_limit
+        if analyzing:
+            self.draw_orth_lines(((False, x_left_spine_limit,),
+                                  (False, x_right_spine_limit)),
+                                 line_width=0.1,
+                                 rgb_color=(1.0, 0.5, 1.0))
+        if is_front_page:
+            draw_book_binding_cut_guide(x_left_spine_limit, 1)
+            draw_book_binding_cut_guide(x_right_spine_limit, -1)
+
 
 class PageCrop:
     'Per-page crop instructions as sizes in point and cm, and A4-zoom factor.'
 
 class PageCrop:
     'Per-page crop instructions as sizes in point and cm, and A4-zoom factor.'
@@ -639,7 +721,7 @@ def build_nup4_output(
         writer: PdfWriter,
         pages: list[Page],
         args_print_margin: int,
         writer: PdfWriter,
         pages: list[Page],
         args_print_margin: int,
-        arg_analyze: str,
+        arg_analyze: bool,
         ) -> None:
     'Build nup4 pages from inputs.'
     print('-n: building 4-input-pages-per-output-page book')
         ) -> None:
     'Build nup4 pages from inputs.'
     print('-n: building 4-input-pages-per-output-page book')
@@ -653,14 +735,13 @@ def build_nup4_output(
     is_front_page = True
     new_page = Page.new_blank()
     for page in pages:
     is_front_page = True
     new_page = Page.new_blank()
     for page in pages:
-        nup4_inner_page_transform(page, nup4_geometry, nup4_i)
-        nup4_outer_page_transform(page, nup4_geometry, nup4_i)
+        page.nup4_transforms(nup4_geometry, nup4_i)
         new_page.merge_page(page)
         page_count += 1
         print(f'merged page number {page_count} (of {len(pages)})')
         nup4_i += 1
         if nup4_i > 3:
         new_page.merge_page(page)
         page_count += 1
         print(f'merged page number {page_count} (of {len(pages)})')
         nup4_i += 1
         if nup4_i > 3:
-            ornate_nup4(arg_analyze, is_front_page, new_page, nup4_geometry)
+            new_page.ornate_nup4(is_front_page, nup4_geometry, arg_analyze)
             new_page.add_to_writer(writer)
             nup4_i = 0
             new_page = Page.new_blank()
             new_page.add_to_writer(writer)
             nup4_i = 0
             new_page = Page.new_blank()
@@ -688,94 +769,6 @@ def resort_pages_for_nup4(
     pages[:] = new_page_order[:]
 
 
     pages[:] = new_page_order[:]
 
 
-def nup4_inner_page_transform(
-        page: Page,
-        nup4_geometry: Nup4Geometry,
-        nup4_i: int
-        ) -> None:
-    'Apply to page crop instructions adequate to position in nup4 geometry.'
-    page.translate(ty=A4_HEIGHT / page.crop.zoom - (A4_HEIGHT - page.crop.top))
-    page.translate(tx=(
-        (-page.crop.left) if nup4_i in {0, 2} else
-        A4_WIDTH / page.crop.zoom - (A4_WIDTH - page.crop.right)))  # in {1, 3}
-    page.scale(page.crop.zoom * nup4_geometry.shrink_for_spine)
-    if nup4_i in {2, 3}:
-        page.translate(
-                ty=-2 * nup4_geometry.margin / nup4_geometry.shrink_for_margin)
-
-
-def nup4_outer_page_transform(
-        page: Page,
-        nup4_geometry: Nup4Geometry,
-        nup4_i: int
-        ) -> None:
-    'Shrink and position page into nup4_geometry as per its position nup4_i.'
-    page.translate(ty=(1 - nup4_geometry.shrink_for_spine) * A4_HEIGHT)
-    if nup4_i in {0, 1}:
-        y_section = A4_HEIGHT
-        page.set_box(bottom=A4_HEIGHT/2, top=A4_HEIGHT)
-    else:  # nup4_in in {2, 3}
-        y_section = 0
-        page.set_box(bottom=0, top=A4_HEIGHT/2)
-    x_section: float
-    if nup4_i in {0, 2}:
-        x_section = 0
-        page.set_box(left=0, right=A4_WIDTH/2)
-    else:  # nup4_in in {1, 3}
-        page.translate(tx=(1 - nup4_geometry.shrink_for_spine) * A4_WIDTH)
-        x_section = A4_WIDTH
-        page.set_box(left=A4_WIDTH/2, right=A4_WIDTH)
-    page.translate(tx=x_section, ty=y_section)
-    page.scale(QUARTER_SCALE_FACTOR)
-
-
-def ornate_nup4(
-        arg_analyze: str,
-        is_front_page: bool,
-        new_page: Page,
-        nup4_geometry: Nup4Geometry,
-        ) -> None:
-    'Apply nup4 line guides onto new_page.'
-    if arg_analyze:
-        new_page.draw_borders((0.5, 1.0, 1.0))
-    new_page.draw_orth_lines(((True, new_page.box['top']/2),
-                              (False, new_page.box['right']/2)),
-                             line_width=0.1)
-    printable_offset_x = nup4_geometry.margin
-    printable_offset_y = nup4_geometry.margin * A4_HEIGHT / A4_WIDTH
-    new_page.scale(nup4_geometry.shrink_for_margin)
-    new_page.translate(tx=printable_offset_x, ty=printable_offset_y)
-    if not (arg_analyze or is_front_page):
-        return
-    x_left_spine_limit = A4_WIDTH/2 * nup4_geometry.shrink_for_spine
-    x_right_spine_limit = A4_WIDTH - x_left_spine_limit
-    if arg_analyze:
-        new_page.draw_orth_lines(((False, x_left_spine_limit,),
-                                  (False, x_right_spine_limit)),
-                                 line_width=0.1, rgb_color=(1.0, 0.5, 1.0))
-    if is_front_page:
-        draw_cut(new_page, x_left_spine_limit, 1)
-        draw_cut(new_page, x_right_spine_limit, -1)
-
-
-def draw_cut(
-        page: Page,
-        x_spine_limit: float,
-        direction: int
-        ) -> None:
-    'Into canvas draw book binding cut guide at x_spine_limit into direction.'
-    directed_half_width = 0.5 * CUT_WIDTH * direction
-    outer_start_x = x_spine_limit - directed_half_width
-    inner_start_x = x_spine_limit + directed_half_width
-    middle_point_y = A4_HEIGHT/2 + MIDDLE_POINT_DEPTH * direction
-    end_point_y = A4_HEIGHT/2 + CUT_DEPTH * direction
-    page.draw_lines(
-            ((inner_start_x, A4_HEIGHT/2, x_spine_limit, end_point_y),
-             (x_spine_limit, end_point_y, x_spine_limit, middle_point_y),
-             (x_spine_limit, middle_point_y, outer_start_x, A4_HEIGHT/2)),
-            line_width=0.2)
-
-
 def main(
         ) -> None:
     'Full program run to be wrapped into ArgFail catcher.'
 def main(
         ) -> None:
     'Full program run to be wrapped into ArgFail catcher.'