[Q] Python script written to generate spine marks

Previous topic - Next topic

jirib

Hi,
what do you think about this python script to create cover page from a PDF with spine marks ?

---%>---
from reportlab.pdfgen import canvas
from reportlab.lib.units import mm, inch
from reportlab.lib.colors import black
from pypdf import PdfReader, PdfWriter, PageObject
from io import BytesIO
from pathlib import Path
import sys

pt = inch / 72.0
CROP_OFFSET = 6 * pt
CROP_LINE_WIDTH = 0.2 * pt
CROP_LEN = 5 * mm
A5_WIDTH = 148.5 * mm
HEIGHT = 210 * mm
BLEED = 5 * mm

def draw_marks(c, total_width, total_height, spine_width):
    c.setStrokeColor(black)
    c.setLineWidth(CROP_LINE_WIDTH)

    xL = BLEED
    xR = total_width - BLEED
    xCL = xL - CROP_OFFSET
    xCR = xR + CROP_OFFSET
    yB = BLEED
    yT = total_height - BLEED
    yCB = yB - CROP_OFFSET
    yCT = yT + CROP_OFFSET

    c.line(xL, yCT, xL, yCT + CROP_LEN)
    c.line(xR, yCT, xR, yCT + CROP_LEN)
    c.line(xCL - CROP_LEN, yT, xCL, yT)
    c.line(xCR, yT, xCR + CROP_LEN, yT)
    c.line(xL, yCB, xL, yCB - CROP_LEN)
    c.line(xR, yCB, xR, yCB - CROP_LEN)
    c.line(xCL - CROP_LEN, yB, xCL, yB)
    c.line(xCR, yB, xCR + CROP_LEN, yB)

    xS1 = BLEED + A5_WIDTH
    xS2 = xS1 + spine_width
    for x in [xS1, xS2]:
        c.line(x, yCB, x, yCB - CROP_LEN)
        c.line(x, yCT, x, yCT + CROP_LEN)

if len(sys.argv) < 2:
    print("Usage: python generate_cover.py input.pdf")
    sys.exit(1)

input_path = Path(sys.argv[1])
reader = PdfReader(str(input_path))
total_pages = len(reader.pages)
if total_pages < 4:
    raise ValueError("❌ PDF must have at least 4 pages.")

cover_pairs = [
    (reader.pages[total_pages - 1], reader.pages[0]),
    (reader.pages[1], reader.pages[total_pages - 2])
]
cover_indices = {0, 1, total_pages - 2, total_pages - 1}

content_page_count = total_pages - 4
spine_width = round(content_page_count * 0.083, 3) * mm
TOTAL_WIDTH = 2 * A5_WIDTH + spine_width + 2 * BLEED
TOTAL_HEIGHT = HEIGHT + 2 * BLEED

# ✅ Output paths
base = input_path.with_suffix('')
cover_out = base.with_name(base.name + "_cover_final.pdf")
content_out = base.with_name(base.name + "_content.pdf")

# === Generate 2-page cover PDF
cover_writer = PdfWriter()

for i, (left, right) in enumerate(cover_pairs):
    merged = PageObject.create_blank_page(None, TOTAL_WIDTH, TOTAL_HEIGHT)

    if i == 0:
        packet = BytesIO()
        c = canvas.Canvas(packet, pagesize=(TOTAL_WIDTH, TOTAL_HEIGHT))
        draw_marks(c, TOTAL_WIDTH, TOTAL_HEIGHT, spine_width)
        c.showPage()
        c.save()
        packet.seek(0)
        marks = PdfReader(packet).pages[0]
        merged.merge_page(marks)

    merged.merge_translated_page(left, tx=BLEED, ty=BLEED)
    merged.merge_translated_page(right, tx=BLEED + A5_WIDTH + spine_width, ty=BLEED)
    cover_writer.add_page(merged)

with open(cover_out, "wb") as f:
    cover_writer.write(f)

# === Generate content-only PDF
content_writer = PdfWriter()
for i in range(total_pages):
    if i not in cover_indices:
        content_writer.add_page(reader.pages[i])

with open(content_out, "wb") as f:
    content_writer.write(f)

# === Summary
print("✅ Cover PDF:   ", cover_out.name)
print("✅ Content PDF: ", content_out.name)
print("📏 Spine width: ", round(spine_width / mm, 3), "mm")
---%<---


a.l.e

I can't say much about the calculation, sorry.

But if the calculations are validate by people who know more about spine marks than me, it might also be interesting to have a version that runs inside of Scribus and adds the marks on a dedicated layer.

jirib

I even did not know how crop marks or spine marks should look like :/ I could not find an exact description. Maybe in the scribus source (for crop marks) ???

Nermander

Do I understand it right, this script takes a PDF and make a cover of the first and last page?

So, with a 12 page PDF, pages 1 and 12 end up in a cover PDF (landscape, 2-up, with a spine between the pages) and the pages 2-11 end up in a "content only" PDF?

And the width of the spine is calculated based on the number of pages and an assumed page thickness of 0.083?

But what about the insides of the cover? Those will be blank?

jirib

#4
Quote from: Nermander on May 11, 2025, 10:39:52 AMDo I understand it right, this script takes a PDF and make a cover of the first and last page?

So, with a 12 page PDF, pages 1 and 12 end up in a cover PDF (landscape, 2-up, with a spine between the pages) and the pages 2-11 end up in a "content only" PDF?

And the width of the spine is calculated based on the number of pages and an assumed page thickness of 0.083?

But what about the insides of the cover? Those will be blank?

Not really:

...
cover_pairs = [
    (reader.pages[total_pages - 1], reader.pages[0]),
    (reader.pages[1], reader.pages[total_pages - 2])
]
...
# === Generate 2-page cover PDF
cover_writer = PdfWriter()
 
for i, (left, right) in enumerate(cover_pairs):
    merged = PageObject.create_blank_page(None, TOTAL_WIDTH, TOTAL_HEIGHT)
 
    if i == 0:
        packet = BytesIO()
        c = canvas.Canvas(packet, pagesize=(TOTAL_WIDTH, TOTAL_HEIGHT))
        draw_marks(c, TOTAL_WIDTH, TOTAL_HEIGHT, spine_width)
        c.showPage()
        c.save()
        packet.seek(0)
        marks = PdfReader(packet).pages[0]
        merged.merge_page(marks)
 
    merged.merge_translated_page(left, tx=BLEED, ty=BLEED)
    merged.merge_translated_page(right, tx=BLEED + A5_WIDTH + spine_width, ty=BLEED)
    cover_writer.add_page(merged)

with open(cover_out, "wb") as f:
    cover_writer.write(f)
...

So, it generates cover with 'cover_pairs'..., however, logically, it places crop marks and spine marks only on the first/outside cover page.

jirib

Quote from: jirib on May 11, 2025, 09:52:30 AMI even did not know how crop marks or spine marks should look like :/ I could not find an exact description. Maybe in the scribus source (for crop marks) ???

...
CROP_OFFSET = 6 * pt
CROP_LINE_WIDTH = 0.2 * pt
CROP_LEN = 5 * mm
...
BLEED = 5 * mm
...

I defined these variables based on https://printedeasy.com/help/artwork-guides/setting-up-bleed-and-crop-marks, see the picture:




jirib

Quote from: a.l.e on May 10, 2025, 02:56:25 PM(...) it might also be interesting to have a version that runs inside of Scribus and adds the marks on a dedicated layer.

I will do it when all things are clear ;)

jirib

Quote from: jirib on May 11, 2025, 11:07:27 AM...
CROP_OFFSET = 6 * pt
CROP_LINE_WIDTH = 0.2 * pt
CROP_LEN = 5 * mm
...
BLEED = 5 * mm
...


IIUC, I need to introduce "slug area": CROP_OFFSET + CROP_LEN = <slug area rounded to next unit value>, eg. 2.117 mm + 5 mm = 7.117 mm, that is, 8mm.

Nermander

Quote from: jirib on May 11, 2025, 10:57:27 AMSo, it generates cover with 'cover_pairs'..., however, logically, it places crop marks and spine marks only on the first/outside cover page.

Ok, so the first two and the last two pages end up on the cover?

jirib

Ignore this script for a while, please, it is very naive: it does not consider by design when you need a colourful cover or with an image spanning both pages, plus both the colourful "rectancle" (holding the cover of the cover) or the image must exceed cut/trim line! so, this script is maybe OK for black/white situations without a need to trim.

I will think about that and try to come with a better solution: one which needs to be done _inside_ Scribus (at least for the cover itself)...

jirib

It seems to me that the whole Scribus functionality with respect to cover is limited: we do not have spine feature; in Indesign, IIUC, they put some pages next to another, so you could have: back cover page, spine page, front cover page. We have either single page or facing-pages (two only); Indesign makes this three pages printed as "spread" - we do not have it.

Even if one would create 2 facing pages (with first page as left = outside back-cover), Scribus, IIUC, seems to have a deficiency: it can't print both pages as spread, and if you print it with bleeds having 0 size for inside, Scribus would ignore this and put crop marks on "inside" even the bleeds there is 0...

Or do I miss something?


Nermander

Quote from: jirib on May 13, 2025, 02:34:30 PMWe have either single page or facing-pages (two only);

There is nothing preventing you from creating pages of different width, the setting you are talking about is just how they are placed on the screen when working on them.

So you can create a back matter page, a narrow spine page and a front matter page. But of course they will still be individual pages, so to combine them we need something more.

AdmFubar

I've always thought of it as a separate document. Instead of separate pages, just one wide page that would include the front, spine, and back cover. Additional lengths for flaps that would wrap around the front and back cover for hardbound books.
Using Scribus 1.5.8 & 1.6.3, 1.7.0 openSUSE 15.5
Advanced hobbyist

jirib