Автоматическое создание серии файлов SVG и PDF из SVG шаблона на примере Inkscape

Материал из СисадминВики (SysadminWiki.ru)
Перейти к: навигация, поиск

Описание задачи

В этой статье мы рассмотрим пример создания персонизированных визиток из единого шаблона. Имея подготовленный SVG шаблон визитки мы используя скрипты на выходе получим набор визиток в отдельных файлах в форматах SVG и PDF.

SVG - это XML файл с описанием всех объектов и их свойств, а значит может быть открыт и изменён как обычный текстовый файл. Написав парсер текстового файла, мы сможем сделать любые изменения в нём, в том числе поменять один фрагмент текста на другой.

SVG поддерживается многими векторными редакторами. Для бесплатного редактора Inkscape SVG - это родной формат, и команда этого проекта стремится полностью соответствовать публичной его спецификации.

Алгоритм таков:

  1. Создаём SVG шаблон визитки (например в Inkscape)
  2. Создаём базу данных с контактными данными в формате CSV
  3. Создаём скрипт-парсер для копирования, заполнения и экспорта в PDF
  4. Создаём командный скрипт в операционной системе для запуска скрипта-парсера в нужном окружении ОС.

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

Создаём SVG шаблон визитки в Inkscape

Visitka-template.png

При проектировании визитки нужно учесть, что вставляемые значения могут быть разной длины. Также выравнивание текста должно быть задано с учётом этого. Переменные вместо которых будем вставлять текст лучше сделать заметными в общем дизайне и унифицированными по виду. Например обозначить заглавными латинскими буквами и заключить в скобки. Также они не должны повторять текст в шаблоне. В целом стоит иметь в виду что подстановка реальных данных в эти переменные будет осуществляться простым поиском и заменой одной подстроки на другую.

Пример шаблона визитки приведён справа ( скачать SVG исходник).

QR-код (содержащий URL сайта или другую контактную информацию) можно сгенерировать с помощью встроенного расширения, специально для этого созданного:

  • Расширение → Отрисовка → Штрих-код → код QR...

Создаём базу данных с контактными данными в формате CSV

В приведённом выше шаблоне используется 4 переменных, соответственно CSV файл должен содержать строки вида:

Иванов Иван Иванович;НАЧАЛЬНИК ТЕХНИЧЕСКОГО ОТДЕЛА;+7 961-111-2222;ivanov@domain.ru
Петров Пётр Петрович;ГЛАВНЫЙ СПЕЦИАЛИСТ;+7 961-333-4444;petrov@domain.ru

Разделитель полей можно будет поменять с ";" на любой другой в скрипте осуществляющем подстановку данных (см. ниже). Файл CSV может быть экспортирован из имеющейся таблицы (например LibreOffice Calc или Microsoft Excel).

Создаём скрипт-парсер для копирования, заполнения и экспорта в PDF

Скрипт-парсер можно написать на любом удобном языке. Главное, чтобы в системе был установлен необходимый интерпретатор или компилятор. Вместе с Inkscape поставляется также интерпритатор Python. Синтаксис Python 2.x и 3.x отличаются. Для 2-й версии есть проблемы с кириллицей, поскольку по умолчанию используется ASCII кодировка. Начиная с версии Inkscape 1.0 beta используется 3-я версия Питона, полноценно поддерживающая UTF-8.

Ниже приведены скрипты для формирования SVG и PDF файлов из одного шаблонного. Разница между вариантами для Windows и для Linux только в формате записи путей.

Скрипт на Python 3 для Linux

svg2pdf.py
# This is Python 3 script for Inkscape in Linux
# coding=utf-8

# Notes befor run.
# Linux: 
#    - use / as directory delimiter
#
# More information: https://sysadminwiki.ru/wiki/Inkscape

import csv
import os
import shutil
import subprocess

#=== Определение переменных пользователем ===
# Рабочий каталог
workDir = '/home/user/images/'
# SVG шаблон
fnSVG = "Бейджик.svg"
# CSV файл с данными
fnCSV = "Участники.csv"
# Разделитель полей
csvDelimiter = ";"
# Скобки переменных, по умолчанию: { }
varStart = '{'
varEnd = '}'
# Номер поля, которое будет использоваться для имени файла (начинается с 0)
fieldN = 2

#=== Вычисляемые переменные (изменять не надо) ===
# Каталоги для выгрузки сформированных файлов
dirSVG = workDir + "SVG/"
dirPDF = workDir + "PDF/"
# Полные имена файлов (абсолютные пути)
fullfnSVG = workDir + fnSVG
fullfnCSV = workDir + fnCSV

#=== ОСНОВНОЙ КОД ===
#-----------------------------------------------------------------------------
print("Start to work with CSV file: " + fullfnCSV)

if not os.path.isdir(dirSVG):
    os.mkdir(dirSVG)
if not os.path.isdir(dirPDF):
    os.mkdir(dirPDF)

with open(fullfnCSV, newline='', encoding='utf-8') as fCSV:
    reader = csv.DictReader(fCSV, delimiter=csvDelimiter)
    headers = reader.fieldnames
    for row in reader:
        print('Work on: ' + row[headers[fieldN]])
        destSVG = dirSVG + row[headers[fieldN]] + '.svg'
        shutil.copyfile(fullfnSVG, destSVG)
        
        with open (destSVG, 'r', encoding='utf-8') as f:
            data = f.read()

        for var in headers:
            data = data.replace(varStart + var + varEnd, row[var])

        with open (destSVG, 'w', encoding='utf-8') as f:
          f.write(data)        
        
        destPDF = dirPDF + row[headers[fieldN]] + '.pdf'
        print('SVG:', destSVG)
        print('PDF:', destPDF)
        os.system('inkscape ' + '--export-filename=\"' + destPDF + '\" --export-type=pdf -T \"' + destSVG + '\"')


Скрипт на Python 3 для Windows

svg2pdf.py
# This is Python 3 script for Inkscape in Windows
# coding=utf-8

# Notes befor run.
#    - set command line to utf-8 encoding: chcp 65001 
#    - use \ as directory delimiter
#
# More information: https://sysadminwiki.ru/wiki/Inkscape

import csv
import os
import shutil
import subprocess

#=== Определение переменных пользователем ===
# Рабочий каталог
workDir = 'c:\\Temp\\Python\\'
# SVG шаблон
fnSVG = "Бейджик.svg"
# CSV файл с данными
fnCSV = "Участники.csv"
# Разделитель полей
csvDelimiter = ";"
# Скобки переменных, по умолчанию: { }
varStart = '{'
varEnd = '}'
# Номер поля, которое будет использоваться для имени файла (начинается с 0)
fieldN = 2

#=== Вычисляемые переменные (изменять не надо) ===
# Каталоги для выгрузки сформированных файлов
dirSVG = workDir + "SVG\\"
dirPDF = workDir + "PDF\\"
# Полные имена файлов (абсолютные пути)
fullfnSVG = workDir + fnSVG
fullfnCSV = workDir + fnCSV

#=== ОСНОВНОЙ КОД ===
#-----------------------------------------------------------------------------
print("Start to work with CSV file: " + fullfnCSV)

if not os.path.isdir(dirSVG):
    os.mkdir(dirSVG)
if not os.path.isdir(dirPDF):
    os.mkdir(dirPDF)

with open(fullfnCSV, newline='', encoding='utf-8') as fCSV:
    reader = csv.DictReader(fCSV, delimiter=csvDelimiter)
    headers = reader.fieldnames
    for row in reader:
        print('Work on: ' + row[headers[fieldN]])
        destSVG = dirSVG + row[headers[fieldN]] + '.svg'
        shutil.copyfile(fullfnSVG, destSVG)
        
        with open (destSVG, 'r', encoding='utf-8') as f:
            data = f.read()

        for var in headers:
            data = data.replace(varStart + var + varEnd, row[var])

        with open (destSVG, 'w', encoding='utf-8') as f:
          f.write(data)        
        
        destPDF = dirPDF + row[headers[fieldN]] + '.pdf'
        print('SVG:', destSVG)
        print('PDF:', destPDF)
        os.system('inkscape ' + destSVG + ' --export-filename=' + destPDF + ' --export-type=pdf -T')        

Скрипт на PHP

svg2pdf.php
<?php
//=== Определение переменных ===
// Каталог для выгрузки сформированных файлов
$outDir = "out";
// Базовое название выгружаемого файла, к которому будет добавлено уникальное окончание
$outFile = "visitka-";
// CSV файл с данными
$csv_file = "names-rus.csv";
// SVG шаблон
$sourceSVG = "visitka-template.svg";
// Перечень переменных в SVG шаблоне
$csv_fields = array(
    '{FIO}',
    '{OCCUPATION}',
    '{PHONE}',
    '{E-MAIL}'
);

//=== Основной код === 

// Копирование шаблона в новый файл и замена переменных на данные из массива
function fillTemplate($temlateFile, $destinationFile, $fields, $data) {
    // Создаём указатель на шаблонный файл 
    $fpTmpl = fopen($temlateFile, 'r');
    // Создаём указатель на новый файл
    $fpDest = fopen($destinationFile, 'w+');
    // Считываем шаблонный файл 
    $fileData = fread($fpTmpl, filesize($temlateFile));
    // Делаем замену переменных из массива $fields на реальные значения в массиве $data
    $fileData = str_replace($fields, $data, $fileData);
    // Изменённый текст сохраняем в новый файл
    fwrite($fpDest, $fileData);
    // Закрываем открытые файлы
    fclose($fpDest);
    fclose($fpTmpl);
} //function fillTemplate()

$row = 1;
// Открываем CSV файл и для каждой считанной строки запускаем
// функцию fillTemplate(), подготавливающую SVG c данными,
// затем создаём PDF файл.
if (($handle = fopen($csv_file, "r")) !== FALSE) {
    // Считываем строки из CSV файла, записывая в массив $data, 
    // разбивая поля по символу ";"
    while (($data = fgetcsv($handle, 1000, ";")) !== FALSE) {   
        $destSVG = $outDir . "/" . $outFile . $data[3] . ".svg";
        $destPDF = $outDir . "/" . $outFile . $data[3] . ".pdf";
        
        // Для отладки: вывод CSV строки и др
        $num = count($data);
        $csv_str = "";
        for ($c=0; $c < $num; $c++) {
            $csv_str = $csv_str . $data[$c] . ' | ';
        }       
echo "
CSV запись  : $num полей в строке $row
CSV строка  : $csv_str
исходн файл = $sourceSVG
конечн файл = $destSVG 
файл PDF    = $destPDF
";
        // Завершение вывода отладочной инфы. Формируем новый SVG из шаблонного        
        fillTemplate($sourceSVG, $destSVG, $csv_fields, $data);
        
        // Делаем экспорт нового SVG в PDF, преобразуя все шрифты и объекты в линии
        system("inkscape -T $destSVG -A $destPDF");

        // Делаем экспорт в PNG, указывая разрешение 300 dpi
        //system("inkscape $destSVG --export-png=$destPNG -d 300");
        $row++;
    }
    fclose($handle);
}

?>

Создаём командный скрипт в операционной системе для запуска скрипта-парсера в нужном окружении ОС

Если настройки окружения ОС позволяют запускать подготовленный скрипт командой

php5 svg2pdf.php

...то дополнительный командный скрипт не нужен. Однако, например в случае с Windows, где в командной строке по умолчанию до сих пор (Windows 7) используется досовская кодовая страница 866 потребуется следующий скрипт.

echo Меняем кодировку командной строки Windows на UTF-8
chcp 65001

echo Добавляем путь к Inkscape
set path=%path%;C:\Program Files\Inkscape

php5.exe svg2pdf.php

pause


Подстановка данных встроенными средствами Inkscape

В будущем, возможно, в Inkscape появится собственное средство для работы с базами данных и переменными. И даже уже сейчас можно значительно облегчить подобную задачу с подстановкой данных, если написать соответствующее расширение. По сути нужно будет просто переложить приведённый в этой статье PHP скрипт на язык Питон и оформить его в виде расширения. Благо такая возможность есть и хорошо документирована. Нужны только добровольцы. Надеюсь эта статья вдохновит кого-нибудь! :-)