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-программу на любом языке!

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 03:51 am
Powered by Dreamwidth Studios