hardsign: (Default)
[personal profile] hardsign

Издательская программа Scribus в дополнение ко всем своим достоинствам поддерживает скрипты на языке Python.

Похоже, разработчики ориентировались прежде всего на вёрстку текста (хотя конкурировать на этой поляне с TeX’ом очень странно), но я использую её для вёрстки фотоальбомов. И в какой-то момент мне естественным образом захотелось немного автоматизировать свою работу.

Как происходит вёрстка альбома?

Для начала надо отобрать фотографии, и тут никакой автоматизации быть не может. Затем надо создать альбом и загрузить фотографии внутрь. Не знаю, какие абстракции применяет Adobe, а тут последовательность действий довольно-таки длинная: создать объект-изображение, а затем выбрать для него файл. Поскольку файлов много, кликать их быстро надоедает, и здесь мы видим первую очевидную точку автоматизации:

#!/usr/bin/env python

from scribus import *
import os

page = 5
path = "/home/hardsign/images"

gotoPage(page)
ip = 0
for f in sorted(os.listdir(os.fsencode(path))):
    loadImage(path+"/"+os.fsdecode(f),img := createImage(20+(ip%3)*80,10+(ip//3)*80,70,70))
    if (ip := 0 if ip==8 else ip+1)==0: gotoPage(page := page+1)

Дальше начинается расстановка и группировка фотографий. Естественно, это удобно делать мышью, но соблюсти при этом сколько-нибудь точные, а главное, одинаковые размеры — задача нерешаемая. Поэтому после наброса открываем свойства каждого объекта и вручную выставляем ему координаты и размер. И вот тут напрашивается вторая точка автоматизации.

Первая идея проста: давайте округлять координаты до ближайшего сантиметра! Реализация тоже проста, но она не работает: соблюсти на глаз размер даже с точностью до сантиметра практически невозможно, и поэтому сантиметры приходится добавлять вручную. Мало того, если хочется сделать несколько объектов одинакового размера, то совсем не обязательно этот размер будет выражаться целым числом.

Давайте чуть ограничим полёт фантазии: пусть объекты на странице привязываются к сетке. Вторая реализация — скрипт перебирает объекты и устанавливает им одинаковый размер. Работает превосходно, но этот размер не всегда должен быть одинаковым — некоторые фотографии могут занимать несколько модулей.

Третья инкарнация скрипта находит ближайший к углу узел сетки, перемещает угол туда, а затем растягивает объект до следующего узла. Уже намного лучше, но выясняется, что одна сетка на все случаи жизни не годится. Где-то получается квадрат 2×2 из четырёх фотографий, а где-то фотки прямоугольные и совсем мелкие, и сетка должна быть 4×5 или наоборот 5×4.

Можно, конечно, сделать стопиццот скриптов на каждую сетку, но иметь множество разных файлов, отличающихся только значениями переменных, очень неудобно. Вводить эти значения — тоже не лучший вариант, да и нет у Scribus’а средств для этого. И тут на помощь приходит unix way: а что, если записать один и тот же скрипт под кучей разных имён и доставать размер сетки прямо из имени?

Сказано — сделано. Времени сэкономлено столько, что хватило написать этот пост, и даже чуть-чуть осталось. Если вдруг кого заинтересовало — пользуйтесь на здоровье.

#!/usr/bin/env python

from scribus import *
import os, sys

# количество фотографий по горизонтали и по вертикали
(gx, gy) = [int(i) for i in os.path.basename(sys.argv[0]).replace("grid","").replace(".py","").split("x")]
# расстояние между краями фотографий в миллиметрах
(hspace, vspace) = (10, 10)
# насколько близко угол должен быть к узловой точке (лучше не менять)
distance = 30
# расстояние от левого края листа
x0 = 10 if currentPage()%2==0 else 20
y0 = 10
# размеры листа
(xsize, ysize) = (275.0, 275.0)

width = (xsize-hspace*(gx-1))/gx
height = (ysize-vspace*(gy-1))/gy
abs = lambda x: x if x>=0 else -x
for i in range(0,selectionCount()):
    cur = getSelectedObject(i)
    (x,y) = getPosition(cur)
    (w,h) = getSize(cur)
    (left, top, right, bottom) = (x0, y0, x0+width, y0+height)
    while abs(x-left)>=distance and left<xsize+100: left += width+hspace
    while abs(y-top)>=distance and top<ysize+100: top += height+vspace
    while abs(x+w-right)>=distance and right<xsize+100: right += width+hspace
    while abs(y+h-bottom)>=distance and bottom<ysize+100: bottom += height+vspace
    if left>=xsize+100 or right>=xsize+100 or top>=ysize+100 or bottom>=ysize+100: continue
    moveObjectAbs(left,top,cur)
    sizeObject(right-left,bottom-top,cur)

P. S. Опытный программист может написать Perl-программу на любом языке!

This account has disabled anonymous posting.
If you don't have an account you can create one now.
HTML doesn't work in the subject.
More info about formatting

Profile

hardsign: (Default)
hardsign

February 2026

S M T W T F S
1 2 34567
891011121314
15161718192021
22232425262728

Style Credit

Expand Cut Tags

No cut tags
Page generated Feb. 4th, 2026 05:23 am
Powered by Dreamwidth Studios