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