In Novem­ber we received an invi­ta­tion from Medialab-Prado to work on a cam­paign for the Libre Graph­ics Meet­ing 2013. Focus was to cre­ate a design that involves the dif­fer­ent voices of peo­ple and com­mu­ni­ties con­nected to libre graph­ics and reflects at the same time ques­tions and prej­u­dices around the topic.

The result is the com­bi­na­tion of a pub­lic READ/WRITE inter­face and a poster engine using the GNU/Linux com­man­d­line as inter­face to a diver­sity of libre (graph­ics) soft­ware. In the fol­low­ing you find a rudi­men­tary expla­na­tion, for bet­ter under­stand­ing the code is simplified.

Pre­pare Input files for Processing

Pub­lic input is man­aged via an online inter­face and before run­ning the gen­er­a­tor remote files are dumped to a local machine. Files are stored as plain textfiles (with spe­cial char­ac­ters con­verted to html enti­ties). and an unique id (md5string of the question’s con­tent), which helps to match related files.

ls ${VOICE%%_Q.txt}_A-*.txt lists all answers for the ques­tion pro­vided with VOICE=i/free/voice/9e30071ce9e4e7baa962b862b9b45be1_Q.txt argu­ment. To under­stand ${VOICE%%_Q.txt} you should know some­thing about bash sub­sti­tu­tion.

  for ANSWER in `ls ${VOICE%%_Q.txt}_A-*.txt`
      SPEAKSRC=`ls i/free/svg/src/speak-*.svg | shuf -n 1`
ANSWERRECODE=$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 con­verted to uni­code (despite some spe­cial char­ac­ters (sed "s/\&/\&/g" | sed "s/\§/§/g" | sed "s/\"/\"/g)).

To inte­grate the input text into a graphic (a speech bub­ble), I sub­sti­tute a place­holder text (QWERTZY) in a pre­pared tem­plate svg. To sim­plify the pro­ce­dure I ensure that the string is on a sin­gle line (sed "s/QWERTZY/\nQWERTZY \n/") by adding a new­line (\n) before and after the place­holder string.

   cat $SPEAKSRC | sed "s/QWERTZY/\nQWERTZY \n/"   > ${SPEAKMADE}.tmp


font-weight:normal;font-family" /></flowRegion><flowPara
id="flowPara3877">QWERTZY</flowPara></flowRoot>  </g>


font-weight:normal;font-family" /></flowRegion><flowPara
</flowPara></flowRoot>  </g>

Then I start writ­ing a new svg by extract­ing every­thing above the place­holder string. I use a sed one­liner (sed -n '/QWERTZY/,$p'), which is nor­mally intended to print the sec­tion from a reg­u­lar expres­sion to the end of a file. But because I actu­ally want to print the sec­tion of the file from the begin­ning to the reg­u­lar expres­sion, I use tac, which prints files in reverse (tac = echo cat | rev), extract the sec­tion, 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 pre­pared text with

   cat $ANSWERRECODE                               >> $SPEAKMADE

where the place­holder string would have been, write the rest of the svg (now the orig­i­nal sed one­liner) 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 bound­ing box I cal­cu­late the font­size accord­ing to the num­ber of char­ac­ters (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 con­vert the text to out­lines and export an pdf with inkscape so I won’t get font prob­lems in Pro­cess­ing. Because Pro­cess­ing can’t import pdf files I con­vert it back to svg again with pdf2svg.

   inkscape --export-area-page \
            --export-text-to-path  \
            --export-pdf=${SPEAKMADE%%.*}.pdf \
SPEAKMADE4P5=${SPEAKMADE%%.*}4p5.svg pdf2svg ${SPEAKMADE%%.*}.pdf $SPEAKMADE4P5

To iden­tify the speech bub­bles later on, I change their col­ors (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'                          $SPEAKMADE4P5
ls $TMPDIR/*.svg | grep $SPEAKMADE4P5 >> $SVGLIST.tmp

Write lists for Processing

Files that will be imported into Pro­cess­ing are ref­er­enced in plaint text lists. Addi­tion­ally to the speech bub­bles there are illus­tra­tions 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.tmp

Illus­tra­tions are divided into two cat­e­gories. Simple/small graph­ics that may be added next to the text path and more com­plex graph­ics that need more space. Illus­tra­tions are mostly derived from a graphic made by LGRU.

Run Pro­cess­ing

By now all input files are pre­pared and the pro­cess­ing sketch can be exe­cuted using the head­less workaround.

  # EXPORT DISPLAY FOR PROCESSING HEADLESS ##############################
  export DISPLAY=localhost:1.0 ##########################################
APPDIR=$(dirname "$0") LIBDIR=$APPDIR/src/$SKETCHNAME/application.linux/lib SKETCH=$LIBDIR/$SKETCHNAME.jar
CORE=$LIBDIR/core.jar PDF=$LIBDIR/pdf.jar GMRTV=$LIBDIR/geomerative.jar BTK=$LIBDIR/batikfont.jar TXT=$LIBDIR/itext.jar
java -Djava.library.path="$APPDIR" \ -cp "$LIBS" \ $SKETCHNAME

Mod­ify PDF writ­ten by Processing

By now we have an pdf file exported by pro­cess­ing, which will be used as basis for all fur­ther trans­for­ma­tions. To do so, the pdf file is con­verted to a plain text svg file that can eas­ily be mod­i­fied 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 cer­tain amount (if [ $((RANDOM%10)) -ge 4 ]) of paths gen­er­ated by Pro­cess­ing (marked by red color) is selected while the remain­ing paths are stored in tem­po­rary file. This is done by chang­ing the des­ti­na­tion for std­out (STORE=${PDF%%.*}_l_tmp.svg or STORE=$TMPDIR/text.lines). The red fill color is replaced ran­domly 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 with­out blue/green color (= lgm ele­ments) and after­wards the remain­ing 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 (there­fore on very top) we select all paths with green color (= the speech bub­bles). Blue is con­verted 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 dif­fer­ent accord­ing to their scal­ing) are con­verted 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
# -------------------------------------------------------------------------- #

Final­ize Poster

  VOICEID=`basename $VOICE | cut -d "_" -f 1 | cut -c 1-8`
  PDFNAME=LGM-2013_${VOICEID}_`date +%s`.pdf
PDFMETA=$TMPDIR/pdfmeta.txt ; I=$TMPDIR/info.txt SUBJECT=`echo $VOICE | sed ':a;N;$!ba;s/\n/ /g'`
pdftk $INFOPDF \ background $PDF \ output $TMPDIR/${PDFNAME}
pdftk $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%%.*}.jpg
# -------------------------------------------------------------------------- #
exit 0;

gs -o ${KOMBI%%.*}.jpg -sDEVICE=jpeg -r144 -dUseCIEColor ${KOMBI} helps to get a proper con­ver­sion of cmyk to rgb