Create a Calendar Using Scripting in Photoshop
Создание календаря, используя скрипт
Создание графического календаря вручную, нелегкая задача. Именно поэтому лучше найти способ автоматизировать процесс. В этом уроке будет показано, как в Фотошопе сгенерировать календарь на весь год, используя JavaScript. Если вы знаетесь в программировании - это замечательно, но если нет — не волнуйтесь, описание довольно прозрачное (единственно, очень внимательно записывайте команды, в некоторых местах регистр также имеет значение):
(внизу каждого шага я буду предоставлять, сделанный по этому уроку, свой вариант кода — участок для данного шага — который с грехом пополам получился. В скобках мои примечания.)
________________________________
Шаг 1
Согласно Adobe, скрипт - это ряд команд, которые задают Фотошопу выполнить одно или более действий. Список свойств и методов, поддерживаемых Фотошопом CS4, с примерами, можно посмотреть здесь - это даст общее представление относительно того, как выглядят и за что отвечают команды используемые в скрипте (при желании, после прочтения темы, это руководство может помочь решить свои задумки по автоматизации).
________________________________
Шаг 2
Давайте приступим. Главная идея состоит в том, чтобы создать текстовые слои на каждый месяц, содержащие даты. Поэтому главным образом мы будем работать вокруг генерации текста. Запустите ExtendScript Toolkit (идет в комплекте с Фш) и создайте новый файл JavaScript. Он будет содержать все команды, которые мы собираемся задать для Фотошопа. Если вы не хотите использовать ExtendScript Toolkit, то можете работать в любом текстовом редакторе.
________________________________
Шаг 3
Сначала мы определяем переменные значения, которые будут использованы при создании документа и систему цветов календаря. Я задал размеры 1280 на 800рх, при разрешении в 72 pixels/inch, с именем "PhotoshopScriptCalendar" и выбрал 2010-й как год календаря, который мы создаем:
Рассмотрим цвета, которые мы задали и к чему они будут впоследствии применены. "NormalColor" — для названий месяцев, будних чисел и дней. Цвет столбика воскресных дней будет зависеть от строчки "highlightColor", а "backColor" — это соответственно фоновый цвет календаря:
//параметры документа width = 1280; height = 800; resolution = 72; docName = "PhotoshopScriptCalendar"; year = 2010; //Cистема цветов normalColor = new SolidColor(); normalColor.rgb.hexValue = "FFFFFF"; highlightColor = new SolidColor(); highlightColor.rgb.hexValue = "A4B1D1"; backColor = new SolidColor(); backColor.rgb.hexValue = "1B4392";
Шаг 4
Поскольку мы главным образом собираемся работать с текстом, то следует определить для него некоторые переменные, которые мы собираемся неоднократно использовать в заголовках месяцев (название месяцев и дней можно писать кириллицей):
Чтобы получить цвет колонки с воскресеньем отличным от остальных дней, нам нужно сделать его отдельным текстовым слоем. Поэтому мы зададим в коде два заголовка: "monthHeader" - от понедельника до субботы (пробелы будут учитываться как расстояние), и "sundayHeader" - воскресение. Каждая из этих двух переменных заканчивается двумя "\r".Они поддерживают перенос текста на новую строку, как нажатие на клавишу ENTER. Затем определим переменную сдвига первого числа года на нужный день - "firstIdent". Так как первое января было в пятницу, то мы должны учесть предыдущие ячейки дней и сделать необходимое число пробелов, которое будет зависеть от используемого шрифта и его размера, что проверяется опытным путем нескольких проб. Наконец, запишите все названия месяцев в порядке их следования:
//Определение переменных для текстовых слоев monthHeader = "Пн Вт Ср Чт Пт Сб\r\r"; sundayHeader = "Вс\r\r"; firstIndent = " "; months = new Array("Январь", "Февраль", "Март", "Апрель", "Май", "Июнь", "Июль", "Август", "Сентябрь", "Октябрь", "Ноябрь", "Декабрь");
Шаг 5
Теперь, когда у нас есть все необходимые объекты, мы можем начать создавать .psd документ:
Как вы видите, код довольно понятен и удобочитаемый. Здесь задана команда создания нового документа с посыланием на значения ширины, высоты, разрешения и названия, которые были внесены ранее в 3-м шаге (посылание в скобках), также указывается цветовое пространство. По умолчанию все новые документы создаются в цветовом пространстве RGB, поэтому можно было опустить этот параметр, но если вы хотите изначально создать другое пространство, например CMYK, то так и записываете в отведенном месте "NewDocumentMode. CMYK". То же и с другими цветовыми моделями, просто записываете их название после точки: LAB, GRAYSCALE, BITMAP и т.д.
Далее мы записывает команду выделения всего документа (думаю понятно, что этой команде соответствует строка "doc.selection.selectAll()"), команду заливки и снятия выделения:
//Создание нового документа doc = app.documents.add(width, height, resolution, docName, NewDocumentMode.RGB); doc.selection.selectAll(); doc.selection.fill(backColor); doc.selection.deselect();
________________________________
Шаг 6
Затем мы прописываем добавление начального фона из стороннего изображения. Градиентный фон, что виден на окончательном результате, по сути является отдельным изображением. Для этого мы собираемся использовать функцию "openDialog ()" - она запускает диалоговое окно File > Open, что позволит выбрать изображение с жесткого диска:
Ниже прописываем следующую строчку. Мы собираемся использовать только первое выделенное изображение (вы ведь знаете, что в фотошопе можно разом открыть несколько пикчей — а в данном случае нужно всего одно, но ведь можно случайно выбрать и два), поэтому для множества "file" вписываем 0. Но также мы должны поставить проверку на то, было ли вообще выбрано какое-либо изображение "if":
Затем изображение загружается и его окно становится активным, что мы прописываем "app.activeDocument":
Изменяем размеры изображения к нашим параметрам. Снова прописываем выделение всего документа, потом копируем выделенную область и закрываем документ сохранения изменений (полагаю, что смысл прописанных внутри операций виден и понятен фотошоперам невооруженным глазом — ведь что такое "selectAll", "fill" и прочие названия знакомо всем):
Наконец, запишем команду вставки (ясно, что после закрытия, активным автоматически становится предыдущий документ), что приведет к помещению скопированного изображения на слой выше фонового. Заодно зададим слою название, например "BackgroundImage".
Заметьте, что если при выборе изображения нажать Cancel, то все действия этого шага, после первой строчки, не выполняются (потому что мы прописали команды только при варианте наличия выбора изображения, помните "if". Тобишь, если не нужна заморочка со вставкой картинки, ведь ее можно и потом добавить, то можно вообще не делать этот шаг):
//выбор фонового изображения file = app.openDialog(); if(file[0]){ app.load(file[0]); backFile = app.activeDocument; backFile.resizeImage(width, height); backFile.selection.selectAll(); backFile.selection.copy(); backFile.close(SaveOptions.DONOTSAVECHANGES); doc.paste(); doc.layers[0].name = "Фоновое изображение"; }
________________________________
Шаг 7
Хорошо, приступим к самой генерации календаря (пишем на строчку ниже). Данный этап может показаться немного сложным, но мы будем продвигаться вперед шаг за шагом, и, надеюсь он немного прояснится.
Необходимо сделать набор действий, участвующих в создании каждого месяца (основу повторения действий). Используем петлю назначения "for", в которой переменная "curr" используется, чтобы обозначить текущий месяц, с которым мы работаем. Начальное значение для "curr" устанавливаем на 0, а конечное на 12 - это значит, что набор написанных позже действий будет прокручиваться 12 раз, в количестве месяцев года. Это-то нам и нужно (тем, кто хоть краем мозга знакомился с программирование, логика построения строчки должна быть понятной):
Сначала мы должны определить две переменные, которые собираемся использовать, чтобы расположить наши месяцы на холсте в виде сетки. Ими будут X и Y - координаты осей горизонтали и вертикали. При данных размерах холста разумно разместить на один ряд 4 месяца, таким образом для параметра смещения X, отвечающего за горизонталь, мы собираемся использовать операцию "%". В результате деления переменной "curr" на 4, получаем три ряда. Для оси Y мы используем функцию javascript - "Math.floor ()" (тут я не совсем въехал в смысл описания, но цифра та же)
For the Y offset we use the "Math.floor()" javascript function that returns the largest value, smaller than the division result of "curr" to 4. Thus for the months from the same row, the Y offset is the same
Гораздо удобнее, если создаваемые текстовые слои, для всех месяцев, будут находится в отдельных одноименных папках. Посему запишем создание таковых папок, имена которые будут получать по очереди из уже готового списка, что мы определили ранее:
for(curr=0; curr<12; curr++){ x = curr % 4; y= Math.floor(curr / 4); group = doc.layerSets.add(); group.name = months[curr]; }
________________________________
Шаг 8
Далее мы создаем новый текстовый слой внутри группы и определяем его имя соответствующее названию группы текущего месяца:
Теперь мы должны установить текстовые атрибуты, такие как цвет, размер шрифта и выключку. Мы установим тип ввода нашего текста "PARAGRAPHTEXT" (способ с ограничивающей рамкой) и зададим ему определенные размеры. Атрибут для "contents" в переменной "monthName" обозначает фактический текст, что будет виден в слое - нам же нужно название определенного месяца, поэтому мы поставим и ставим "months[curr]" (напомню любителям, что в программировании часто идут ссылки на определенные, ранее прописанные строки, с их свойствами и значениями… короче атрибутами, чтобы ими воспользоваться. Это как в математике, когда определенный повторяющийся участок формулы мы приравниваем к иксу и далее, для упрощения писанины, используем уже Икс, зная при этом истинное его значение. Так вот, атрибуты для months мы прописали в начале кода, а заданная переменная curr, с каждым витком подставит следующее значение от months):
Также давайте развернем текст на 90° против часовой стрелки и разместим в определенном месте на слое (здесь мы собираемся использовать наши переменные смещений "x" и "y"):
Имейте в виду, что расположение сделано относительно верхнего левого угла, но так как мы произвели вращение на 90° против часовой стрелки - это теперь стало нижним левым углом. Если вы задали для своего документа иные размеры, чем в уроке, то вам придется соответственно изменить константы, которые я использовал у себя:
for(curr=0; curr<12; curr++){ x = curr % 4; y= Math.floor(curr / 4); group = doc.layerSets.add(); group.name = months[curr]; monthName = group.artLayers.add(); monthName.kind = LayerKind.TEXT; monthName.name = months[curr]; monthName.textItem.color = normalColor; monthName.textItem.size = 27; monthName.textItem.kind = TextType.PARAGRAPHTEXT; monthName.textItem.justification = Justification.RIGHT; monthName.textItem.height = 35; monthName.textItem.width = 145; monthName.textItem.contents = months[curr]; monthName.rotate(-90); monthName.textItem.position = new Array(55 + 300 * x, (202 + (210 * y))); }
________________________________
Шаг 9
На этом этапе мы собираемся сделать текстовый слой, который впоследствии будет содержать все даты текущего месяца, кроме воскресений. Сейчас мы сделаем так, чтобы слой был добавлен в ранее созданную группу, а также определим имя слою, выключку, цвет шрифта, размер и позицию расположения. Содержание слоя мы добавим чуть позже и когда мы дойдем до того пункта я объясню почему:
Та же самая процедура и для воскресной колонки, только цвет другой — "highlightColor":
days = group.artLayers.add(); days.kind = LayerKind.TEXT; days.name = "Days"; days.textItem.Justification = Justification.CENTER; days.textItem.color = normalColor; days.textItem.size = 19; days.textItem.position = new Array(85 + (300 * x), 70 + (210 * y)); sundays = group.artLayers.add() sundays.kind = LayerKind.TEXT; sundays.name = "Sundays"; sundays.textItem.Justification = Justification.CENTER; sundays.textItem.color = highlightColor; sundays.textItem.size = 19; sundays.textItem.position = new Array(280 + (300 * x), 70 + (210 * y)); }
________________________________
Шаг 10
Теперь мы должны создать две переменные, которые будут вмещать генерируемый текст: переменная "text" будет содержать будние дни, "textSun" — даты воскресенья. Начинаем с добавления заголовков столбиков и отступа первого числа на нужный день недели для первого месяца. Создание новой даты делается через функцию javascript "Date()" — функции года нашего календаря с месяцами и получения их позиции по неделях (я так понял берет данные из "даты и времени" на компьютере). Помните, нумерация всегда начинается от 0, так например, если первым в столбике месяца будет понедельник, то "n" = 0, если это будет вторник, то "n" будет 1 и так далее. Тогда мы должны добавить заявку, которую мы определили вначале к переменной "text", столько раз, сколько необходимо. Например, если первой в месяце будет среда, то мы добавим заявку два раза:
text = monthHeader; textSun = sundayHeader; startDate = new Date(year, curr, 1); n = startDate.getDay(); for(i=0; i<n-1; i++) text += firstIndent; }
________________________________
Шаг 11
Настало время для генерации всех чисел месяца. Для этого мы должны знать, сколько дней в каждом конкретном месяце, и нам нужны числа в формате "leading zeros" — таким образом мы должны вернутся и определить две вспомогательных функции: "daysInMonth" и "makeDay". Вернитесь к началу кода и добавьте эти функции.
Функция "daysInMonth" — возвращает нужное количество дней для данного месяца, который мы делаем (определяет их для каждого месяца согласно данным календаря на компьютере).
Функция "makeDay" — возвращает данное ей число в определенном формате (добавляет нули к числам меньшим 10 — в коде это видно из условия, что если d меньше 10, то перед ним нужно добавить цифру 0) и задает расстояние между цифрами чисел дней (в пробелах между кавычками строчки return d + " " — его следуем задавать с учетом пробелов между названиями дней недели).
Так, например, если мы вызовем функцию "daysInMonth" со значением для year = 2010 (год мы указали в параметрах документа) и month = 0 (что будет означать Январь), то мы получим 31 сгенерированный день в заданном месяце. Если мы вызовем функцию "makeDay", например, с d = 3, то это возвратит нам текст "03" (добавит ноль впереди), но если d = 13, то число "13" таким и останется:
//Определение вспомогательных функций function daysInMonth(month, year){ return 32 - new Date(month, year, 32).getDate(); } function makeDay(d){ if(d < 10) d = "0" + d; return d + " "; }
Спуститесь назад, книзу кода. Мы начнем со значения "d = 1", оно будет увеличиваться, пока не достигнет числа количества дней в месяце. Если "i" равняется шести — это означает, что настало воскресенье, таким образом мы должны добавить дату к воскресному слою. Не забудьте записать "\r", для сноса последующего текста на новую строчку. Иначе в эту же строчку будут добавляться следующие будние числа.
Теперь мы добавляем новую строчку, только если текущий день суббота (i=5). В конце мы должны увеличить обе "i" и "d". При значении "i", когда оно достигает "7", если последним днем было добавлено воскресенье, оно должно вернутся к первому столбику, а значит мы должны указать это, прировняв его к "0" (в этом блоке я мог что-то и неправильно перевести):
Теперь у нас есть все даты в наших текстовых переменных и мы можем добавить их к нашим слоям. Причина задержки этого шага заключается в том, что поочередное добавление дат к слою занимает время работы Фотошопа, поэтому лучше добавить все сразу, вместо того, чтобы добавлять каждый день отдельно:
d = 1; while(d <= daysInMonth(curr, year)){ if(i == 6) textSun += makeDay(d) + "\r"; else{ text += makeDay(d); if(i == 5) text += "\r"; } i++; d++; if(i == 7) i = 0; } days.textItem.contents = text; sundays.textItem.contents = textSun; }
________________________________
Шаг 12
Итак, код генерации всех чисел месяца готов, а сейчас мы пропишем добавление слоя с годом и создания небольшой линии в основании документа. Для слоя с годом — процедура та же, что мы использовали прежде: создание слоя, переименование, указание размета с цветом и местоположения на холсте:
Для линии у основания код немного иной. Сначала мы должны определить область с координатами X и Y для всех четырех углов, затем сделать выделение этой области, а потом залить ее и снять выделение:
// Создание текстового слоя с годом yearLayer = doc.artLayers.add(); yearLayer.kind = LayerKind.TEXT; yearLayer.name = year; yearLayer.textItem.contents = year; yearLayer.textItem.size = 72; yearLayer.textItem.color = highlightColor; yearLayer.textItem.position = new Array(1050, 730); //Создание нижней линии line = doc.artLayers.add(); line.name = "Line"; region = Array(Array(80, 729), Array(1000, 729), Array(1000, 730), Array(80, 730)); doc.selection.select(region); doc.selection.fill(normalColor) doc.selection.deselect();
________________________________
Шаг 13
Код скрипта готов! Единственная вещь, которую осталось сделать, так это воспользоваться им. Если вы пользовались программой ExtendScript Toolkit, то из выпадающего меню выберите Adobe Photoshop, если при этом фотошоп не запущен, кликните на изображение маленькой цепи, а затем на кнопку запуска. Если Вы использовали любого рода текстовый редактор, вам остается сохранить файл с расширением ".js" или ".jsx", а потом запустить его в фотошопе через меню: File > Scripts > Browse… и выбрать этот сохраненный файл:
Здесь торжественно представляю для скачивания получившийся скрипт и сам код, сделанные по уроку. Прочитав урок, вы поймете, как и что можно в нем изменить или какие из его частей использовать в других целях. Проверял на CS3 ex, но не факт что у всех пойдет script23_calendar.rar ( 2,85 KB )
//Скрипт для генерации календаря на целый год. //Сделан Manoylov-ом AC на основе урока //http://psd.tutsplus.com/tutorials/designing-tutorials/create-a-calendar-using-scripting-in-photoshop/ //Определение вспомогательных функций function daysInMonth(month, year){ return 32 - new Date(month, year, 32).getDate(); } function makeDay(d){ if(d < 10) d = "0" + d; return d + " "; } //параметры документа width = 1280; height = 800; resolution = 72; docName = "PhotoshopScriptCalendar"; year = 2010; //Cистема цветов normalColor = new SolidColor(); normalColor.rgb.hexValue = "FFFFFF"; highlightColor = new SolidColor(); highlightColor.rgb.hexValue = "A4B1D1"; backColor = new SolidColor(); backColor.rgb.hexValue = "1B4392"; //Определение переменных для текстовых слоев monthHeader = "Пн Вт Ср Чт Пт Сб\r\r"; //пробелы между заголовками будут учтены sundayHeader = "Вс\r\r"; //две эрочки - это как два нажатия Enter, а значит числа дней будут начинатся ниже на две строчки. firstIndent = " "; // начальный пробел для подгонки первого числа первого месяца на позицию своего дня (узнается опытным путем, так как зависит от размера шрифта и его свойств) months = new Array("Январь", "Февраль", "Март", "Апрель", "Май", "Июнь", "Июль", "Август", "Сентябрь", "Октябрь", "Ноябрь", "Декабрь"); //Создание нового документа doc = app.documents.add(width, height, resolution, docName, NewDocumentMode.RGB); doc.selection.selectAll(); doc.selection.fill(backColor); doc.selection.deselect(); //выбор фонового изображения file = app.openDialog(); if(file[0]){ app.load(file[0]); backFile = app.activeDocument; backFile.resizeImage(width, height); backFile.selection.selectAll(); backFile.selection.copy(); backFile.close(SaveOptions.DONOTSAVECHANGES); doc.paste(); doc.layers[0].name = "Фоновое изображение"; } for(curr=0; curr<12; curr++){ x = curr % 4; y= Math.floor(curr / 4); group = doc.layerSets.add(); group.name = months[curr]; monthName = group.artLayers.add(); monthName.kind = LayerKind.TEXT; monthName.name = months[curr]; monthName.textItem.color = normalColor; monthName.textItem.size = 27; monthName.textItem.kind = TextType.PARAGRAPHTEXT; monthName.textItem.justification = Justification.RIGHT; monthName.textItem.height = 35; monthName.textItem.width = 145; monthName.textItem.contents = months[curr]; monthName.rotate(-90); monthName.textItem.position = new Array(55 + 300 * x, (202 + (210 * y))); days = group.artLayers.add(); days.kind = LayerKind.TEXT; days.name = "Days"; days.textItem.Justification = Justification.CENTER; days.textItem.color = normalColor; days.textItem.size = 19; days.textItem.position = new Array(85 + (300 * x), 70 + (210 * y)); sundays = group.artLayers.add() sundays.kind = LayerKind.TEXT; sundays.name = "Sundays"; sundays.textItem.Justification = Justification.CENTER; sundays.textItem.color = highlightColor; sundays.textItem.size = 19; sundays.textItem.position = new Array(280 + (300 * x), 70 + (210 * y)); text = monthHeader; textSun = sundayHeader; startDate = new Date(year, curr, 1); n = startDate.getDay(); for(i=0; i<n-1; i++) text += firstIndent; d = 1; while(d <= daysInMonth(curr, year)){ if(i == 6) textSun += makeDay(d) + "\r"; else{ text += makeDay(d); if(i == 5) text += "\r"; } i++; d++; if(i == 7) i = 0; } days.textItem.contents = text; sundays.textItem.contents = textSun; } // Создание текстового слоя с годом yearLayer = doc.artLayers.add(); yearLayer.kind = LayerKind.TEXT; yearLayer.name = year; yearLayer.textItem.contents = year; yearLayer.textItem.size = 72; yearLayer.textItem.color = highlightColor; yearLayer.textItem.position = new Array(1050, 730); //Создание нижней линии line = doc.artLayers.add(); line.name = "Line"; region = Array(Array(80, 729), Array(1000, 729), Array(1000, 730), Array(80, 730)); doc.selection.select(region); doc.selection.fill(normalColor) doc.selection.deselect();
А уже тут скрипт с вырезанным шестым шагом (для пользования, в данном случае, по сути ненужный) и совмещенным генерирование всех дней в одном слое, без отдельного воскресного столбика — чисто для примера изменений script5.rar ( 2,42 KB )
Если будете менять скрипт, переделывая его, то для удобства проверки выхода уменьшите максимальное значение для [curr] с 12 на 1 (чтобы не все месяцы прокручивало, а только один), а уже после того, как достигнете желаемого, вернете назад.
Автор: Alexandru Pitea
Перевел: Manoylov AC (demiart)