) -> 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
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,
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.'
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')
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:
- 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()
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.'