In November we received an invitation from Medialab-Prado to work on a campaign for the Libre Graphics Meeting 2013. Focus was to create a design that involves the different voices of people and communities connected to libre graphics and reflects at the same time questions and prejudices around the topic.
The result is the combination of a public READ/WRITE interface and a poster engine using the GNU/Linux commandline as interface to a diversity of libre (graphics) software. In the following you find a rudimentary explanation, for better understanding the code is simplified.
Prepare Input files for Processing
Public input is managed via an online interface and before running the generator remote files are dumped to a local machine. Files are stored as plain textfiles (with special characters converted to html entities). and an unique id (md5string of the question’s content), which helps to match related files.
- 9e30071ce9e4e7baa962b862b9b45be1_Q.txt
- 9e30071ce9e4e7baa962b862b9b45be1_A-1358100223487.txt
- 9e30071ce9e4e7baa962b862b9b45be1_A-1358100410236.txt
- 9e30071ce9e4e7baa962b862b9b45be1_A-1358114098744.txt
- 9e30071ce9e4e7baa962b862b9b45be1_A-1358114133404.txt
- 9e30071ce9e4e7baa962b862b9b45be1_A-1359593547107.txt
- 9e30071ce9e4e7baa962b862b9b45be1_A-1361110213865.txt
- 9e30071ce9e4e7baa962b862b9b45be1_A-1361278633669.txt
- 9e30071ce9e4e7baa962b862b9b45be1_A-1361824250587.txt
- 9e30071ce9e4e7baa962b862b9b45be1_A-1363810211405.txt
- 9e30071ce9e4e7baa962b862b9b45be1_A-1364986499679.txt
ls ${VOICE%%_Q.txt}_A-*.txt
lists all answers for the question provided with VOICE=i/free/voice/9e30071ce9e4e7baa962b862b9b45be1_Q.txt
argument. To understand ${VOICE%%_Q.txt}
you should know something about bash substitution.
for ANSWER in `ls ${VOICE%%_Q.txt}_A-*.txt` do SPEAKSRC=`ls i/free/svg/src/speak-*.svg | shuf -n 1` SPEAKMADE=$TMPDIR/speak-$RANDOM.svgANSWERRECODE=$TMPDIR/answer.recoded cat $ANSWER | recode HTML..utf-8 | \ sed "s/\&/\&/g" | \ sed "s/\§/§/g" | \ sed "s/\"/\"/g" > $ANSWERRECODE
Because I want to use the text in an inkscape svg file the text needs to be converted to unicode (despite some special characters (sed "s/\&/\&/g" | sed "s/\§/§/g" | sed "s/\"/\"/g
)).
To integrate the input text into a graphic (a speech bubble), I substitute a placeholder text (QWERTZY
) in a prepared template svg. To simplify the procedure I ensure that the string is on a single line (sed "s/QWERTZY/\nQWERTZY \n/"
) by adding a newline (\n
) before and after the placeholder string.
# REPLACE PATTERN WITH PATTTERN + NEWLINE cat $SPEAKSRC | sed "s/QWERTZY/\nQWERTZY \n/" > ${SPEAKMADE}.tmp
->
...
style="font-size:8px;font-style:normal;font-variant:normal;
font-weight:normal;font-family" /></flowRegion><flowPara
id="flowPara3877">QWERTZY</flowPara></flowRoot> </g>
...
becomes
...
style="font-size:8px;font-style:normal;font-variant:normal;
font-weight:normal;font-family" /></flowRegion><flowPara
id="flowPara3877">
QWERTZY
</flowPara></flowRoot> </g>
...
Then I start writing a new svg by extracting everything above the placeholder string. I use a sed oneliner (sed -n '/QWERTZY/,$p'
), which is normally intended to print the section from a regular expression to the end of a file. But because I actually want to print the section of the file from the beginning to the regular expression, I use tac, which prints files in reverse (tac = echo cat | rev
), extract the section, remove the line with the regex (grep -v
) and reverse again. Yes, this is a hack.
tac ${SPEAKMADE}.tmp | \ sed -n '/QWERTZY/,$p' | grep -v QWERTZY | tac > $SPEAKMADE
Then I insert the prepared text with
cat $ANSWERRECODE >> $SPEAKMADE
where the placeholder string would have been, write the rest of the svg (now the original sed oneliner) and remove all line breaks (sed -i ':a;N;$!ba;s/\n//g'
) in the last step.
cat ${SPEAKMADE}.tmp | sed -n '/QWERTZY/,$p' | \ grep -v QWERTZY >> $SPEAKMADE sed -i ':a;N;$!ba;s/\n//g' $SPEAKMADE
To make sure the text will fit into the bounding box I calculate the fontsize according to the number of characters (cat $ANSWER | recode HTML..utf-8 | wc -c
).
NUMCHAR=`cat $ANSWER | recode HTML..utf-8 | wc -c` FONTSIZE=`expr 450 / $NUMCHAR + 8`sed -i "s/font-size:8px;/font-size:${FONTSIZE}px;/g" \ $SPEAKMADE
I convert the text to outlines and export an pdf with inkscape
so I won’t get font problems in Processing. Because Processing can’t import pdf files I convert it back to svg again with pdf2svg
.
inkscape --export-area-page \ --export-text-to-path \ --export-pdf=${SPEAKMADE%%.*}.pdf \ $SPEAKMADESPEAKMADE4P5=${SPEAKMADE%%.*}4p5.svg pdf2svg ${SPEAKMADE%%.*}.pdf $SPEAKMADE4P5
To identify the speech bubbles later on, I change their colors (sed -i "s/rgb(100%,100%,100%);/#0000ff;/g"
). Finally I add the file name to a list (ls $TMPDIR/*.svg | grep $SPEAKMADE4P5
) that will be used to import the shapes into Processing.
sed -i "s/rgb(100%,100%,100%);/#0000ff;/g" $SPEAKMADE4P5 sed -i "s/rgb(0%,0%,0%);/#00ff00;/g" $SPEAKMADE4P5 sed -i 's/: /:/g' $SPEAKMADE4P5 sed -i 's/; /;/g' $SPEAKMADE4P5ls $TMPDIR/*.svg | grep $SPEAKMADE4P5 >> $SVGLIST.tmpdone
Write lists for Processing
Files that will be imported into Processing are referenced in plaint text lists. Additionally to the speech bubbles there are illustrations stored as svg files.
LETTERNUM=`echo $VOICE | wc -c` VOICENUM=`cat $SVGLIST.tmp | wc -l` ls $SVGDIR/*.svg | \ grep -v simple | \ shuf -n `expr $LETTERNUM \/ 2 - $VOICENUM` >> $SVGLIST.tmp ls $SVGDIR/*.svg | grep simple | shuf -n 20 >> $SVGLIST.tmptac $SVGLIST.tmp > $SVGLIST
Illustrations are divided into two categories. Simple/small graphics that may be added next to the text path and more complex graphics that need more space. Illustrations are mostly derived from a graphic made by LGRU.
Run Processing
By now all input files are prepared and the processing sketch can be executed using the headless workaround.
# EXPORT DISPLAY FOR PROCESSING HEADLESS ############################## export DISPLAY=localhost:1.0 ##########################################SKETCHNAME=fontplate_1_00APPDIR=$(dirname "$0") LIBDIR=$APPDIR/src/$SKETCHNAME/application.linux/lib SKETCH=$LIBDIR/$SKETCHNAME.jarCORE=$LIBDIR/core.jar PDF=$LIBDIR/pdf.jar GMRTV=$LIBDIR/geomerative.jar BTK=$LIBDIR/batikfont.jar TXT=$LIBDIR/itext.jarLIBS=$SKETCH:$CORE:$PDF:$GMRTV:$BTK:$TXTjava -Djava.library.path="$APPDIR" \ -cp "$LIBS" \ $SKETCHNAME
Modify PDF written by Processing
By now we have an pdf file exported by processing, which will be used as basis for all further transformations. To do so, the pdf file is converted to a plain text svg file that can easily be modified with GNU Power Tools.
# -------------------------------------------------------------------------- # PDF=lgm.pdf ; pdf2svg $PDF ${PDF%%.*}_tmp.svg# SELECT AND WRITE SVG HEADER ---------------------------------------------- # # -------------------------------------------------------------------------- # cat ${PDF%%.*}_tmp.svg | \ sed '/path style/,$d' > ${PDF%%.*}_l_tmp.svg# PATHS WITH RED FILL (WRITTEN BY PROCESSING) ----------------------------- # # -------------------------------------------------------------------------- # for LINE in `cat ${PDF%%.*}_tmp.svg | grep "path style" | \ grep "fill: *rgb(100%,0%,0%)" | sed 's/ /XXYYZZ/g' | shuf` do if [ $((RANDOM%10)) -ge 4 ]; then STORE=${PDF%%.*}_l_tmp.svg else STORE=$TMPDIR/text.lines fi FILL=`echo "rgb(0%,0%,0%)-rgb(100%,100%,100%)" | \ sed 's/-/\\n/g' | shuf -n 1`echo $LINE | sed 's/XXYYZZ/ /g' | \ sed "s/fill:[^;]*;/fill:$FILL;/" >> $STORE done
A certain amount (if [ $((RANDOM%10)) -ge 4 ]
) of paths generated by Processing (marked by red color) is selected while the remaining paths are stored in temporary file. This is done by changing the destination for stdout (STORE=${PDF%%.*}_l_tmp.svg
or STORE=$TMPDIR/text.lines
). The red fill color is replaced randomly either by black or white (FILL=`echo "rgb(0%,0%,0%)-rgb(100%,100%,100%)" | sed 's/-/\\n/g' | shuf -n 1
).
On top we add all path without blue/green color (= lgm elements) and afterwards the remaining red paths (cat $TMPDIR/text.lines
).
# PATHS WITHOUT RED FILL AND BLUE/GREEN COLOR ------------------------------ # # -------------------------------------------------------------------------- # cat ${PDF%%.*}_tmp.svg | grep "path style" | \ grep -v "fill: *rgb(100%,0%,0%)" | \ grep -v "rgb(0%,0%,100%)" | \ grep -v "rgb(0%,100%,0%)" >> ${PDF%%.*}_l_tmp.svg
# PATHS WITH RED FILL (THE REST) ------------------------------------------- # # -------------------------------------------------------------------------- # cat $TMPDIR/text.lines | \ sed 's/fill:rgb(0%,0%,0%)/fill:rgb(100%,100%,100%)/g' \ >> ${PDF%%.*}_l_tmp.svg
At last (therefore on very top) we select all paths with green color (= the speech bubbles). Blue is converted to white (sed ‘s/rgb(0%,100%,0%)/rgb(100%,100%,100%)/g’), green to black (sed 's/rgb(0%,0%,100%)/rgb(0%,0%,0%)/g'
) and all stroke widths (which are different according to their scaling) are converted to one value (sed -i 's/stroke-width:[^;]*;/stroke-width:0\.3;/'
).
# PATHS WITH GREEN COLOR --------------------------------------------------- # # -------------------------------------------------------------------------- # cat ${PDF%%.*}_tmp.svg | grep "path style" | \ grep "rgb(0%,100%,0%)" | \ sed 's/rgb(0%,100%,0%)/rgb(0%,0%,0%)/g' >> ${PDF%%.*}_l_tmp.svg
# SVG CLOSE --------------------------------------------------------------- # # -------------------------------------------------------------------------- # tac ${PDF%%.*}_tmp.svg | head -200 | \ sed '/path style/,$d' | tac >> ${PDF%%.*}_l_tmp.svg # -------------------------------------------------------------------------- # sed -i 's/stroke-width:[^;]*;/stroke-width:0\.3;/' ${PDF%%.*}_l_tmp.svg sed -i 's/rgb(0%,0%,100%)/rgb(100%,100%,100%)/g' ${PDF%%.*}_l_tmp.svg inkscape --export-pdf=lgm_unified+layered.pdf ${PDF%%.*}_l_tmp.svg
# -------------------------------------------------------------------------- # rm *_tmp.svg $PDF # -------------------------------------------------------------------------- #
Finalize Poster
PDF=lgm_unified+layered.pdf VOICEID=`basename $VOICE | cut -d "_" -f 1 | cut -c 1-8` PDFNAME=LGM-2013_${VOICEID}_`date +%s`.pdf INFOPDF=i/free/svg/130319_SUBLINE-12_Print4Madrid.pdfPDFMETA=$TMPDIR/pdfmeta.txt ; I=$TMPDIR/info.txt SUBJECT=`echo $VOICE | sed ':a;N;$!ba;s/\n/ /g'`pdftk $INFOPDF \ background $PDF \ output $TMPDIR/${PDFNAME}KOMBI=$PDFNAMEpdftk $TMPDIR/$KOMBI update_info $PDFMETA \ output $OUTPUTDIR/$KOMBI KOMBI=$OUTPUTDIR/$KOMBI gs -o ${KOMBI%%.*}.jpg -sDEVICE=jpeg -r144 -dUseCIEColor ${KOMBI} convert -resize x498 \ -border 1x1 \ -bordercolor black \ ${KOMBI%%.*}.jpg ${KOMBI%%.*}.gif rm ${KOMBI%%.*}.jpgrm $PDF $TMPDIR/$INFOBLOCK* $SVGLIST.tmp# -------------------------------------------------------------------------- #exit 0;
gs -o ${KOMBI%%.*}.jpg -sDEVICE=jpeg -r144 -dUseCIEColor ${KOMBI}
helps to get a proper conversion of cmyk to rgb