home · contact · privacy
Apply normalization crops to -nup4, don't undo by -c but rather add to.
authorPlom Heller <plom@plomlompom.com>
Tue, 7 Apr 2026 22:16:44 +0000 (00:16 +0200)
committerPlom Heller <plom@plomlompom.com>
Tue, 7 Apr 2026 22:16:44 +0000 (00:16 +0200)
bookmaker.py

index e3c0bcada9bd0231ba6aefb4c3b4fe0ee0003200..9da85aab88b6ef066d8cb07aa042ceaa7c19fafa 100755 (executable)
@@ -136,6 +136,9 @@ class PageCrop:
         self.bottom_cm = bottom_cm
         self.right_cm = right_cm
         self.top_cm = top_cm
+        self._calc_measures()
+
+    def _calc_measures(self) -> None:
         self.left = float(self.left_cm) * POINTS_PER_CM
         self.bottom = float(self.bottom_cm) * POINTS_PER_CM
         self.right = float(self.right_cm) * POINTS_PER_CM
@@ -177,14 +180,17 @@ class PageCrop:
         "What's left of A4_WIDTH after applying height croppings."
         return A4_HEIGHT - self.bottom - self.top
 
-    def make_mirror(
-            self
-            ) -> 'PageCrop':
-        'Return PageCrop of swapped .left and .right.'
-        return PageCrop(left_cm=self.right_cm,
-                        bottom_cm=self.bottom_cm,
-                        right_cm=self.left_cm,
-                        top_cm=self.top_cm)
+    def add(
+            self,
+            to_add: Self,
+            mirror=False
+            ) -> None:
+        'Add other crop to own measures, mirror left and right if demanded.'
+        self.left_cm += to_add.right_cm if mirror else to_add.left_cm
+        self.bottom_cm += to_add.bottom_cm
+        self.right_cm += to_add.left_cm if mirror else to_add.right_cm
+        self.top_cm += to_add.top_cm
+        self._calc_measures()
 
 
 class Nup4Geometry:
@@ -480,18 +486,26 @@ def normalize_pages_to_a4(
     max_y = max(page.box['top'] for page in pages)
     zooms = (A4_WIDTH / max_x, A4_HEIGHT / max_y)
     offsets = {'tx': 0.0, 'ty': 0.0}
-    if zooms[0] < zooms[1]:
+    zoom_by_x = zooms[0] < zooms[1]
+    if zoom_by_x:
         zoom = zooms[0]
         offsets['ty'] = (A4_HEIGHT - max_y * zoom) / 2
     else:
         zoom = zooms[1]
         offsets['tx'] = (A4_WIDTH - max_x * zoom) / 2
+    zoom_offset = 0.01 + offsets['tx'] / POINTS_PER_CM
+    zoom_crop = PageCrop(
+            left_cm=0 if zoom_by_x else zoom_offset,
+            right_cm=0 if zoom_by_x else zoom_offset,
+            bottom_cm=zoom_offset if zoom_by_x else 0,
+            top_cm=zoom_offset if zoom_by_x else 0)
     for page in pages:
         page.scale(zoom)
         page.translate(**offsets)
         if (rotation := page.rotation()):
             page.rotate(360 - rotation)
         page.set_box(0, 0, A4_HEIGHT, A4_WIDTH)
+        page.crop.add(zoom_crop)
 
 
 def collect_page_croppings(
@@ -512,18 +526,16 @@ def collect_page_croppings(
             print(f'{prefix}: to pages {idx_start + 1}:{idx_after} '
                   f'applying crop: {page_crop.format_in_cm}{suffix}')
             for idx in range(idx_start, idx_after):
-                pages[idx].crop = (page_crop.make_mirror()
-                                   if (args_symmetry and idx % 2)
-                                   else page_crop)
+                pages[idx].crop.add(page_crop, args_symmetry and idx % 2)
     elif args_keep_mediabox:
         for page in pages:
-            page.crop = PageCrop(
-                    page.box['left'] / POINTS_PER_CM,
-                    page.box['bottom'] / POINTS_PER_CM,
-                    (0.01 + A4_WIDTH - page.box['right']) / POINTS_PER_CM,
-                    (0.01 + A4_HEIGHT - page.box['top']) / POINTS_PER_CM)
-            if args_symmetry and not idx % 2:
-                page.crop = page.crop.make_mirror()
+            page.crop.add(
+                    PageCrop(
+                        page.box['left'] / POINTS_PER_CM,
+                        page.box['bottom'] / POINTS_PER_CM,
+                        (0.01 + A4_WIDTH - page.box['right']) / POINTS_PER_CM,
+                        (0.01 + A4_HEIGHT - page.box['top']) / POINTS_PER_CM),
+                    args_symmetry and not idx % 2)
 
 
 def build_single_pages_output(