FLOSS Manuals

 English |  Español |  Français |  Italiano |  Português |  Русский |  Shqip

Python Scripting with Scribus

colorchart_generic.py

We had a request from the German nonprofit organization, freieFarbe e.V., to create a script that would take a color file, a plain text list of color names, and the values for making Lab colors, and use that to make color charts with appropriate labels. freieFarbe means "free colors", and their objective is to spread the use of freely available color sets, especially in the field of commercial printing. You can find them at www.freieFarbe.de.

The first task was to have a look at these files, and they go something like this in the CIE-HLC.clf file:

HLC 000 60 00    60    0    0
HLC 000 70 00    70    0    0
HLC 000 80 00    80    0    0
HLC 000 90 00    90    0    0
HLC 010 20 10    20    9,9    1,7
HLC 010 20 20    20    19,7    3,5
HLC 010 20 30    20    29,5    5,2
HLC 010 30 10    30    9,9    1,7
HLC 010 30 20    30    19,7    3,5
HLC 010 30 30    30    29,5    5,2
HLC 010 30 40    30    39,4    7
HLC 010 30 50    30    49,2    8,7

Each line begins with a color name, which may have some spaces in it, and then separated by tabs are 3 values, the L, a, and b which are used to create the color. Notice that the values use a comma for a decimal separator. Fortunately, the most recent development versions of Scribus are able to create colors using floating point Lab values.

#!/usr/bin/env python
# -*- coding: utf-8 -*-
# colorchart_generic.py

"""
# ABOUT
# This script is designed to create color swatches
# according to certain specifications, on A4 paper, with swatches
# measuring 22x12mm.
# Color patches will be placed in a 7x7 array, colors created to
# match the L, a, and b values for each color. A label is placed under
# each color patch with its name, and the L, a, and b values.
# Each line of the clf file source should contain a color name, then its
# L, a, and b values separated by tabs. If the Lab data uses commas
# for the decimal separator, they will be converted to decimal points.
# A dialog asks for a heading for the pages.

"""

import scribus
import re, os, sys, datetime

def SelectAllText(textframe):
    texlen = scribus.getTextLength(textframe)
    scribus.selectText(0,texlen,textframe)
    return

colors = []
L = []
a = []
b = []
index = 0

ypos = -5 # this was used as a correction factor for placement
scribus.messageBox("About",'This script will take the Lab colour data \nfrom a clf colour file, create those colours\nin Scribus, and in addition create a \ndocument displaying each of these colours.')
colorfile = scribus.fileDialog('Select a color file', 'CLF files(*.clf)')
headingparts = os.path.split(colorfile)
heading = headingparts[-1]
heading = heading[:-4]
header = scribus.valueDialog("Title",'Enter a title for your color chart',heading)
for line in open(colorfile, 'rU').xreadlines():
    content = line.split('\t')
    fields = len(content)
    if (fields == 4):
        colors.append(content[0])
        Lval = content[fields - 3]
        Lval = re.sub(r',','.',Lval)
        Lval = float(Lval)
        L.append(Lval)
        aval = content[fields - 2]
        aval = re.sub(r',','.',aval)
        aval = float(aval)
        a.append(aval)
        bval = content[fields - 1]
        bval = re.sub(r',','.',bval)
        bval = float(bval)
        b.append(bval)
size = len(colors)
docsize = int(size/49)
plural = 's'
if ((size % 49) > 0):
    docsize = docsize + 1
if docsize == 1:
    plural = ''
if size > 500:
    response = scribus.messageBox('Query', 'You are about to create '+str(size) + ' colors,\n and a document with '+str(docsize)+ ' page' + plural + '.\nThis may take several minutes.\nDo you want to do this?', icon=scribus.ICON_WARNING, button1=scribus.BUTTON_YES, button2=scribus.BUTTON_NO)
else:
    response = scribus.messageBox('Query', 'You are about to create '+str(size) + ' colors,\n and a document with '+str(docsize)+ ' page' + plural + '.\nDo you want to do this?', icon=scribus.ICON_NONE, button1=scribus.BUTTON_YES, button2=scribus.BUTTON_NO)
if response != 16384:
    sys.exit(-1)
#start = datetime.datetime.now().replace(microsecond=0)
scribus.progressReset()
scribus.progressTotal(docsize)
currpage = 1
if scribus.newDocument(scribus.PAPER_A4, (0,0,0,0),scribus.PORTRAIT, 1, scribus.UNIT_POINTS, scribus.NOFACINGPAGES, scribus.FIRSTPAGERIGHT,1):
    scribus.setUnit(scribus.UNIT_MILLIMETERS)
    scribus.setRedraw(False)
    scribus.createCharStyle("titlechar", "DejaVu Sans Condensed Bold", 18)
    scribus.createParagraphStyle("titlestyle",1,0,0,0,0,0,0,0,0,0,0,"titlechar")
    scribus.createCharStyle("keychar", "DejaVu Sans Condensed Bold", 7.0,'','Black',1.0,'',0,0,0,0,0,0,0,0,0,1,1,-50)
    scribus.createParagraphStyle("keystyle",0,7.0,1,0,0,0,0,0,0,0,0,"keychar")
    scribus.createCharStyle("keyLabchar", "DejaVu Sans Book", 6.0)
    scribus.createParagraphStyle("keyLabstyle",0,7.0,1,0,0,0,0,0,0,0,0,"keyLabchar")
    scribus.createCharStyle("footerchar", "DejaVu Sans Book", 7)
    scribus.createParagraphStyle("footerstyle",0,12.0,2,0,0,0,0,0,0,0,0,"footerchar")
    scribus.createMasterPage('attribution')
    scribus.editMasterPage('attribution')
    footer = scribus.createText(97,285.3,99,5.2)
    scribus.setText('Scribus script created by Gregory Pittman & freieFarbe.de 2017', footer)
    scribus.setStyle("footerstyle", footer)
    leftfooter = scribus.createText(15.5, 281, 80, 9.5)
    scribus.setText(heading + '\nNo. of colours: ' + str(size) + ' ' + u'\u00b7' + ' Page \x1e of \x17', leftfooter)
    SelectAllText(leftfooter)
    scribus.setStyle("footerstyle", leftfooter)
    scribus.setTextAlignment(scribus.ALIGN_LEFT, leftfooter)
    scribus.closeMasterPage()
    scribus.gotoPage(1)

    titleframe = scribus.createText(30, 15, 90, 15)
    scribus.setText(heading, titleframe)
    scribus.setStyle("titlestyle", titleframe)
    key = scribus.createText(15.5, 43.5, 8, 18)
    scribus.setText("Name\n\nL\na\nb", key)
    SelectAllText(key)
    scribus.setStyle('keystyle', key)
    textlen = scribus.getTextLength(key)
    scribus.selectText(textlen-6, 6, key)
    scribus.setStyle('keyLabstyle', key)

    while (index < len(colors)):
        if ypos < 230:
            ypos = ypos + 35
        else:
            ypos = 30
            scribus.newPage(-1)
            currpage = currpage + 1
        for xpos in range(25, 180, 25):
           scribus.defineColorLab(colors[index], float(L[index]), float(a[index]), float(b[index]))
           newrectangle = scribus.createRect(xpos,ypos,22,12)
           scribus.setLineColor("None",newrectangle)
           scribus.setFillColor(colors[index], newrectangle)
           newtext = scribus.createText(xpos, ypos + 13.5, 22, 15)
           if (len(colors[index])<18):
               spacer = '\n'
           else:
               spacer = ''
           data = str(L[index]) + '\n' + str(a[index]) + '\n' + str(b[index])
           scribus.setText(colors[index]+spacer+'\n'+ data, newtext)
           SelectAllText(newtext)
           scribus.setStyle('keystyle',newtext)
           textlen = scribus.getTextLength(newtext)
           datalen = len(data)
           overflow = scribus.textOverflows(newtext)
           if overflow == 1:
               scribus.sizeObject(22,20, newtext)
           scribus.selectText(textlen - datalen, datalen, newtext)
           scribus.setStyle('keyLabstyle', newtext)
           index = index + 1
           if index >= len(colors):
               break
        scribus.progressSet(currpage)
pages = scribus.pageCount()
for p in range(1, pages + 1):
    scribus.applyMasterPage('attribution',p)
scribus.progressReset()
scribus.deselectAll()
#finish = datetime.datetime.now().replace(microsecond=0)
#interval = finish - start
#scribus.messageBox("Interval",'This took ' + str(interval) + ' to finish',button1=scribus.BUTTON_OK)
scribus.setRedraw(True)
scribus.redrawAll()

At the top, in addition to some packages we've seen before, we also import re and datetime. More on these later.

The script otherwise begins with a function SelectAllText(), which is discussed in the chapter Creating Text Styles. After this, we initialize some variables and lists and then gives a message explaining what this script is about. We then ask for a color file using a fileDialog(), and since we're only interested in these color files, we sift out those ending with .clf. I then sift out the name of the file to potentially be used as the default header for the document we will create later, but a valueDialog() allows the user to make the heading whatever is desired.

Now we're ready to read this file, line by line:

for line in open(colorfile, 'rU').xreadlines():

The part I wanted to point out is the 'rU' parameter for the file. What I found as I was writing this script was that some of the .clf files had been created and saved on a Mac computer. The importance of this is that a text file saved on a Mac does not use a newline character to end each line, and therefore Python running on UTF-8 will read the entire file as one line. For this and other reasons I put in the conditional, if (fields ==4): later to identify these files. I thought I was going to have to convert these Mac files, but then I discovered this 'rU' parameter, which covers Mac and Windows methods of indicating new lines. I left the conditional in just in case some file had some other kind of error, and I did find a file that had a missing tab.

Next comes parsing our values, and here the re package comes in to change the decimal separators to periods. At the end of parsing, we have four lists for our color names and the Lab values to create them. Some of these files were quite large, with thousands of colors, so in order to give the user some feedback about this, we make note of the number of colors and also the number of pages of document that 49 colors per page will create. Next a messageBox() displays this information, and gives the user an option to bail out and quit the script. For whatever reason, if you click an OK button, this returns the integer 16384, thus the reason for this check.

I've commented out the line beginning with #start = ... along with two others later – I'll discuss these below. The next two lines

scribus.progressReset()
scribus.progressTotal(docsize)

set up a Progress Bar in the right lower edge of the Scribus window for some feedback about the progress of the script – we're using docsize as the denominator to check progress.

We're finally ready to begin the document, A4 size, with units in millimeters. Among the preparatory work is that of creating a number of Character and Paragraph Styles to be used in the document, and then a Master Page, which will have some basic information which will be on all pages. Look at what's in the footer:

    scribus.setText(heading + '\nNo. of colours: ' + str(size) + ' ' + u'\u00b7' + ' Page \x1e of \x17', leftfooter)

We're indicating the number of colors in the document, then Unicode 00b7, which is a middle dot. But what are '\x1e' and '\x17'? These turn out to be the hexadecimal notation in Scribus for the current page and the total number of pages. Since this is a Master Page, we want the current page to change accordingly. After this we close our Master Page editing and begin the first page of the document. Only the first page will show a header and also a key which explains the Lab values we will display under each color swatch.

As I said, each page will contain a maximum of 49 color swatches, 7 rows of 7 swatches, the size and placement of which was decided in some to and fro discussions with freieFarbe. Underneath each swatch is a small text frame showing the color name, then a line each for the L, the a, and b values. Since we are going through our lists with an indexing method, it seemed efficient to create each color, then its swatch, then the swatch's key, then move to the next color. We have to know when we've filled a page so we can make a new one, and know when all the colors are created. After the first row of a page is created, we reset our Progress Bar with scribus.progressSet(currpage). When we finish the document, then we are ready to apply the Master Page to all pages.

pages = scribus.pageCount()
for p in range(1, pages + 1):
    scribus.applyMasterPage('attribution',p)

This shows a peculiarity of the range command in Python. If you set a range of (1, 10) the conditional actually uses 1 through 9, so this is why I needed to add one to the pages variable.

I did not cover the application of the styles to the labels since this is handled in Creating Text Styles.

Now let's talk about those lines commented out:

#start = datetime.datetime.now().replace(microsecond=0)
#finish = datetime.datetime.now().replace(microsecond=0)
#interval = finish - start
#scribus.messageBox("Interval",'This took ' + str(interval) + ' to finish',button1=scribus.BUTTON_OK)

When I began trying out this script on the large number of .clf files which I had, I quickly found that the larger files would run for an hour or more! This is part of what led to the warning messages, but I wanted to get some objective measure of this, and found this method using the datetime package. This sets a start time somewhere before the bulk of the work, then after the document is finished a finish time so we can calculate the interval, followed by a simple message about this. This also helped understand the huge benefit of creating these styles, since originally the script simply applied text features to each label frame as they were created. Somehow this ends up being very time-consuming. Using styles instead meant that only minutes were needed for even the largest files.

If you're going to try out this script, make sure you're using Scribus 1.5.4svn or better. There should be an official release of 1.5.4 later this year. You can download a collection of .clf files from freieFarbe's website.



There has been error in communication with Booktype server. Not sure right now where is the problem.

You should refresh this page.