Jekyll2019-10-13T00:52:38+02:00https://bornageek.com/feed.xmlphidelux.github.ioSimple bash script to prepare images for the web2015-10-25T15:10:00+01:002015-10-25T15:10:00+01:00https://bornageek.com/general/linux/2015/10/25/simple-bash-script-to-prepare-image-for-the-web<p>A few days ago i reactivated my blog and decided to switch from <a href="https://www.wordpress.org" title="The wordpress project page">Wordpress</a> to <a href="https://www.jekyllrb.com" title="The Jekyll project page">Jekyll</a>. After I had solved most problems regarding the migration, I had to think about my new publishing process and the major issue I had to face was the preparation of my images. Wordpress scaled my images and reduced the image quality for me, now I have to do this on my own.</p>
<p>To do this, I have written a small script that checks the width and height of a given image and scales it down and reduces its quality. This script internally uses two commands of the <a href="https://www.imagemagick.org">ImageMagick</a> software suite, the <em>identify</em> and <em>convert</em> command. While <em>identify</em> allows me to collect all important information about a given image, <em>convert</em> enables me to convert the image to another image format, scale the image and reduce the image quality.</p>
<!--more-->
<figure class="highlight"><pre><code class="language-bash" data-lang="bash"><span class="nv">$ </span>identify images/cheat-cube.png
images/cheat-cube.png PNG 1024x724 1024x724+0+0 8-bit sRGB 403KB 0.000u 0:00.000</code></pre></figure>
<p>The above example of the <em>identify</em> command returns the type of the image, it’s dimensions, the used color scheme and it’s size. This could now be used to convert the image to a more storage safing format, scale the image down to the needed size and reduce the quality to reduce the download time.</p>
<p>As I always convert my images to *.jpg files, scale them to the same with and limit them to the same high, I wrote a script that will do the job for me:</p>
<figure class="highlight"><pre><code class="language-bash" data-lang="bash"><span class="c">#!/bin/bash</span>
<span class="c"># Define the usage text.</span>
<span class="nv">USAGE</span><span class="o">=</span><span class="si">$(</span> <span class="nb">cat</span> <span class="o"><<</span><span class="no">EOF</span><span class="sh">
Usage: `basename </span><span class="nv">$0</span><span class="sh">` [-hvd] [-W <max_width>] [-H <max_height>] [-c <target-format>] [-o <output-directory>] [-q <quality></span><span class="se">\n</span><span class="sh">
-h|--help:
</span><span class="se">\t</span><span class="sh">Displays this help.
-v|--version
</span><span class="se">\t</span><span class="sh">Displays the current version of this script.
-W|--max-width
</span><span class="se">\t</span><span class="sh">Maximum image width.
-H|--max-height
</span><span class="se">\t</span><span class="sh">Maximum image height.
-c|--convert
</span><span class="se">\t</span><span class="sh">Format the image gets converted to.
-o|--output-directory
</span><span class="se">\t</span><span class="sh">Directory to save the new files to.
-q|--quality
</span><span class="se">\t</span><span class="sh">Quality of the newly generated image (value between 0 and 100).
</span><span class="no">EOF
</span><span class="si">)</span>
<span class="c"># Declare all used variables.</span>
<span class="nv">MAX_WIDTH</span><span class="o">=</span><span class="s2">""</span>
<span class="nv">MAX_HEIGHT</span><span class="o">=</span><span class="s2">""</span>
<span class="nv">CONVERT</span><span class="o">=</span><span class="s2">""</span>
<span class="nv">QUALITY</span><span class="o">=</span><span class="s2">""</span>
<span class="nv">OUTPUT_DIR</span><span class="o">=</span><span class="s2">""</span>
<span class="c"># Functions for messages.</span>
<span class="k">function </span>notice<span class="o">()</span> <span class="o">{</span>
<span class="nb">echo</span> <span class="nt">-e</span> <span class="s2">"</span><span class="nv">$*</span><span class="s2">"</span> <span class="o">></span>&2
<span class="o">}</span>
<span class="k">function </span>info<span class="o">()</span> <span class="o">{</span>
<span class="nb">echo</span> <span class="nt">-e</span> <span class="s2">"</span><span class="nv">$*</span><span class="s2">"</span> <span class="o">></span>&2
<span class="nb">exit </span>0
<span class="o">}</span>
<span class="k">function </span>error<span class="o">()</span> <span class="o">{</span>
<span class="nb">echo</span> <span class="nt">-e</span> <span class="s2">"</span><span class="nv">$*</span><span class="s2">"</span> <span class="o">></span>&2
<span class="nb">exit </span>1
<span class="o">}</span>
<span class="c"># Check if a given program is installed.</span>
haveProg<span class="o">()</span> <span class="o">{</span>
which <span class="nv">$1</span> &> /dev/null
<span class="o">}</span>
<span class="c"># Join array to string.</span>
<span class="nb">join</span><span class="o">()</span> <span class="o">{</span>
<span class="nb">local </span><span class="nv">IFS</span><span class="o">=</span><span class="s2">"</span><span class="nv">$1</span><span class="s2">"</span>
<span class="nb">shift
echo</span> <span class="s2">"</span><span class="nv">$*</span><span class="s2">"</span>
<span class="o">}</span>
<span class="c"># Declare a list of packages needed in order to use this script.</span>
<span class="nv">packages</span><span class="o">=(</span><span class="s1">'convert'</span> <span class="s1">'identify'</span><span class="o">)</span>
<span class="c"># Generate a list of missing packages.</span>
<span class="nv">missing</span><span class="o">=()</span>
<span class="nv">cnt</span><span class="o">=</span><span class="k">${#</span><span class="nv">packages</span><span class="p">[@]</span><span class="k">}</span>
<span class="k">for</span> <span class="o">((</span><span class="nv">i</span><span class="o">=</span>0<span class="p">;</span>i<cnt<span class="p">;</span>i++<span class="o">))</span><span class="p">;</span> <span class="k">do
if</span> <span class="o">!</span> haveProg <span class="s2">"</span><span class="k">${</span><span class="nv">packages</span><span class="p">[i]</span><span class="k">}</span><span class="s2">"</span><span class="p">;</span> <span class="k">then
</span>missing+<span class="o">=(</span><span class="s2">"</span><span class="k">${</span><span class="nv">packages</span><span class="p">[i]</span><span class="k">}</span><span class="s2">"</span><span class="o">)</span>
<span class="k">fi
done</span>
<span class="c"># Dump an error message for missing packages.</span>
<span class="k">if</span> <span class="o">[[</span> <span class="k">${#</span><span class="nv">missing</span><span class="p">[@]</span><span class="k">}</span> <span class="nt">-ne</span> 0 <span class="o">]]</span> <span class="p">;</span> <span class="k">then
</span><span class="nv">missingStr</span><span class="o">=</span><span class="sb">`</span><span class="nb">join</span> <span class="s1">' '</span> <span class="k">${</span><span class="nv">missing</span><span class="p">[@]</span><span class="k">}</span><span class="sb">`</span>
error <span class="s2">"The following packages are missing: </span><span class="nv">$missingStr</span><span class="s2">"</span>
<span class="k">fi</span>
<span class="c"># Initialize the positional argument list ...</span>
<span class="nv">args</span><span class="o">=()</span>
<span class="c"># ... and fetch command line options.</span>
<span class="k">while</span> <span class="o">[[</span> <span class="nt">-n</span> <span class="s2">"</span><span class="k">${</span><span class="nv">1</span><span class="p">+xxx</span><span class="k">}</span><span class="s2">"</span> <span class="o">]]</span><span class="p">;</span> <span class="k">do
case</span> <span class="s2">"</span><span class="nv">$1</span><span class="s2">"</span> <span class="k">in</span>
<span class="nt">--help</span><span class="p">|</span><span class="nt">-h</span><span class="p">)</span>
info <span class="s2">"</span><span class="nv">$USAGE</span><span class="s2">"</span>
<span class="p">;;</span>
<span class="nt">--version</span><span class="p">|</span><span class="nt">-v</span><span class="p">)</span>
info <span class="s2">"</span><span class="sb">`</span><span class="nb">basename</span> <span class="nv">$0</span><span class="sb">`</span><span class="s2"> version 1.0"</span>
<span class="p">;;</span>
<span class="nt">--max-width</span><span class="p">|</span><span class="nt">-W</span><span class="p">)</span>
<span class="nb">shift
</span><span class="nv">MAX_WIDTH</span><span class="o">=</span><span class="nv">$1</span>
<span class="nv">re</span><span class="o">=</span><span class="s1">'^[0-9]+$'</span>
<span class="k">if</span> <span class="o">!</span> <span class="o">[[</span> <span class="nv">$MAX_WIDTH</span> <span class="o">=</span>~ <span class="nv">$re</span> <span class="o">]]</span> <span class="p">;</span> <span class="k">then
</span>error <span class="s2">"Given max width is not a number."</span>
<span class="k">fi
</span><span class="nb">shift</span>
<span class="p">;;</span>
<span class="nt">--max-height</span><span class="p">|</span><span class="nt">-H</span><span class="p">)</span>
<span class="nb">shift
</span><span class="nv">MAX_HEIGHT</span><span class="o">=</span><span class="nv">$1</span>
<span class="nv">re</span><span class="o">=</span><span class="s1">'^[0-9]+$'</span>
<span class="k">if</span> <span class="o">!</span> <span class="o">[[</span> <span class="nv">$MAX_HEIGHT</span> <span class="o">=</span>~ <span class="nv">$re</span> <span class="o">]]</span> <span class="p">;</span> <span class="k">then
</span>error <span class="s2">"Given max height is not a number."</span>
<span class="k">fi
</span><span class="nb">shift</span>
<span class="p">;;</span>
<span class="nt">--convert</span><span class="p">|</span><span class="nt">-c</span><span class="p">)</span>
<span class="nb">shift
</span><span class="nv">CONVERT</span><span class="o">=</span><span class="nv">$1</span>
<span class="k">if</span> <span class="o">!</span> <span class="o">[[</span> <span class="s2">"</span><span class="nv">$CONVERT</span><span class="s2">"</span> <span class="o">]]</span> <span class="p">;</span> <span class="k">then
</span>error <span class="s2">"Missing target format to convert to."</span>
<span class="k">fi
</span><span class="nv">CONVERT</span><span class="o">=</span><span class="si">$(</span><span class="nb">tr</span> <span class="s1">'[:upper:]'</span> <span class="s1">'[:lower:]'</span> <span class="o"><<<</span> <span class="nv">$CONVERT</span><span class="si">)</span>
<span class="nb">shift</span>
<span class="p">;;</span>
<span class="nt">--output-directory</span><span class="p">|</span><span class="nt">-o</span><span class="p">)</span>
<span class="nb">shift
</span><span class="nv">OUTPUT_DIR</span><span class="o">=</span><span class="k">${</span><span class="nv">1</span><span class="p">%/</span><span class="k">}</span>
<span class="k">if</span> <span class="o">[</span> <span class="nt">-z</span> <span class="s2">"</span><span class="nv">$OUTPUT_DIR</span><span class="s2">"</span> <span class="o">]</span> <span class="o">||</span> <span class="o">[</span> <span class="o">!</span> <span class="nt">-d</span> <span class="s2">"</span><span class="nv">$OUTPUT_DIR</span><span class="s2">"</span> <span class="o">]</span> <span class="p">;</span> <span class="k">then
</span>error <span class="s2">"Missing or not existing output directory."</span>
<span class="k">fi
</span><span class="nb">shift</span>
<span class="p">;;</span>
<span class="nt">--quality</span><span class="p">|</span><span class="nt">-q</span><span class="p">)</span>
<span class="nb">shift
</span><span class="nv">QUALITY</span><span class="o">=</span><span class="nv">$1</span>
<span class="nv">re</span><span class="o">=</span><span class="s1">'^(100|[1-9][0-9]|[0-9])$'</span>
<span class="k">if</span> <span class="o">!</span> <span class="o">[[</span> <span class="nv">$QUALITY</span> <span class="o">=</span>~ <span class="nv">$re</span> <span class="o">]]</span> <span class="p">;</span> <span class="k">then
</span>error <span class="s2">"Given quality is not valid."</span>
<span class="k">fi
</span><span class="nb">shift</span>
<span class="p">;;</span>
-<span class="k">*</span><span class="p">)</span>
error <span class="s2">"unrecognized option: </span><span class="nv">$1</span><span class="se">\n\n</span><span class="nv">$USAGE</span><span class="s2">"</span>
<span class="p">;;</span>
<span class="k">*</span><span class="p">)</span>
args+<span class="o">=(</span><span class="s2">"</span><span class="nv">$1</span><span class="s2">"</span><span class="o">)</span>
<span class="nb">shift</span>
<span class="p">;;</span>
<span class="k">esac</span>
<span class="k">done
</span><span class="nv">cnt</span><span class="o">=</span><span class="k">${#</span><span class="nv">args</span><span class="p">[@]</span><span class="k">}</span>
<span class="k">for</span> <span class="o">((</span><span class="nv">i</span><span class="o">=</span>0<span class="p">;</span>i<cnt<span class="p">;</span>i++<span class="o">))</span><span class="p">;</span> <span class="k">do
</span><span class="nv">file</span><span class="o">=</span><span class="s2">"</span><span class="k">${</span><span class="nv">args</span><span class="p">[i]</span><span class="k">}</span><span class="s2">"</span>
<span class="c"># Extract the image dimensions from the info blob ...</span>
<span class="nv">identity</span><span class="o">=</span><span class="sb">`</span>identify <span class="nv">$file</span> | <span class="nb">awk</span> <span class="s1">'{print $3}'</span><span class="sb">`</span>
<span class="c"># ... and split dimensions to width and height.</span>
<span class="nv">dimensions</span><span class="o">=(</span><span class="k">${</span><span class="nv">identity</span><span class="p">//x/ </span><span class="k">}</span><span class="o">)</span>
<span class="nv">width</span><span class="o">=</span><span class="k">${</span><span class="nv">dimensions</span><span class="p">[0]</span><span class="k">}</span>
<span class="nv">height</span><span class="o">=</span><span class="k">${</span><span class="nv">dimensions</span><span class="p">[1]</span><span class="k">}</span>
<span class="c"># Extract the path, filename, basename and extension.</span>
<span class="nv">filename</span><span class="o">=</span><span class="si">$(</span><span class="nb">basename</span> <span class="s2">"</span><span class="nv">$file</span><span class="s2">"</span><span class="si">)</span>
<span class="nv">extension</span><span class="o">=</span><span class="s2">"</span><span class="k">${</span><span class="nv">filename</span><span class="p">##*.</span><span class="k">}</span><span class="s2">"</span>
<span class="nv">filename</span><span class="o">=</span><span class="s2">"</span><span class="k">${</span><span class="nv">filename</span><span class="p">%.*</span><span class="k">}</span><span class="s2">"</span>
<span class="nv">path</span><span class="o">=</span><span class="si">$(</span><span class="nb">dirname</span> <span class="s2">"</span><span class="nv">$file</span><span class="s2">"</span><span class="si">)</span>
<span class="c"># Set target directory.</span>
<span class="nv">outDir</span><span class="o">=</span><span class="s2">"</span><span class="nv">$path</span><span class="s2">"</span>
<span class="k">if</span> <span class="o">[</span> <span class="o">!</span> <span class="nt">-z</span> <span class="s2">"</span><span class="nv">$OUTPUT_DIR</span><span class="s2">"</span> <span class="o">]</span><span class="p">;</span> <span class="k">then
</span><span class="nv">outDir</span><span class="o">=</span><span class="s2">"</span><span class="nv">$OUTPUT_DIR</span><span class="s2">"</span>
<span class="k">fi</span>
<span class="c"># Identify the current file.</span>
<span class="nb">printf</span> <span class="s1">'\n-> %s (%s x % s) %s\n'</span> <span class="s2">"</span><span class="nv">$file</span><span class="s2">"</span> <span class="s2">"</span><span class="nv">$width</span><span class="s2">"</span> <span class="s2">"</span><span class="nv">$height</span><span class="s2">"</span> <span class="s2">"</span><span class="nv">$extension</span><span class="s2">"</span>
<span class="c"># Generate the new filename.</span>
<span class="nv">newFile</span><span class="o">=</span><span class="si">$(</span><span class="nb">printf</span> <span class="s1">'%s/%s.%s'</span> <span class="s2">"</span><span class="nv">$outDir</span><span class="s2">"</span> <span class="s2">"</span><span class="nv">$filename</span><span class="s2">"</span> <span class="s2">"</span><span class="nv">$CONVERT</span><span class="s2">"</span><span class="si">)</span>
<span class="c"># Convert the file if needed.</span>
<span class="k">if</span> <span class="o">[</span> <span class="o">!</span> <span class="nt">-z</span> <span class="s2">"</span><span class="nv">$CONVERT</span><span class="s2">"</span> <span class="o">]</span> <span class="o">&&</span> <span class="o">[</span> <span class="s2">"</span><span class="nv">$extension</span><span class="s2">"</span> <span class="o">!=</span> <span class="s2">"</span><span class="nv">$CONVERT</span><span class="s2">"</span> <span class="o">]</span><span class="p">;</span> <span class="k">then
</span><span class="nb">printf</span> <span class="s1">'Converting %s to %s\n'</span> <span class="s2">"</span><span class="nv">$file</span><span class="s2">"</span> <span class="s2">"</span><span class="nv">$newFile</span><span class="s2">"</span>
convert <span class="s2">"</span><span class="nv">$file</span><span class="s2">"</span> <span class="s2">"</span><span class="nv">$newFile</span><span class="s2">"</span>
<span class="k">fi
if</span> <span class="o">[</span> <span class="o">!</span> <span class="nt">-f</span> <span class="s2">"</span><span class="nv">$newFile</span><span class="s2">"</span> <span class="o">]</span><span class="p">;</span> <span class="k">then
</span><span class="nb">cp</span> <span class="s2">"</span><span class="nv">$file</span><span class="s2">"</span> <span class="s2">"</span><span class="nv">$newFile</span><span class="s2">"</span>
<span class="k">fi</span>
<span class="c"># Scale down the image in width if needed.</span>
<span class="k">if</span> <span class="o">[</span> <span class="o">!</span> <span class="nt">-z</span> <span class="s2">"</span><span class="nv">$MAX_WIDTH</span><span class="s2">"</span> <span class="o">]</span> <span class="o">&&</span> <span class="o">[</span> <span class="s2">"</span><span class="k">${</span><span class="nv">width</span><span class="k">:-</span><span class="nv">0</span><span class="k">}</span><span class="s2">"</span> <span class="nt">-ge</span> <span class="s2">"</span><span class="k">${</span><span class="nv">MAX_WIDTH</span><span class="k">}</span><span class="s2">"</span> <span class="o">]</span><span class="p">;</span> <span class="k">then
</span><span class="nb">printf</span> <span class="s1">'Scaling image down from a width of %s pixels to %s pixels.\n'</span> <span class="s2">"</span><span class="nv">$width</span><span class="s2">"</span> <span class="s2">"</span><span class="nv">$MAX_WIDTH</span><span class="s2">"</span>
convert <span class="s2">"</span><span class="nv">$newFile</span><span class="s2">"</span> <span class="nt">-resize</span> <span class="s2">"</span><span class="nv">$MAX_WIDTH</span><span class="s2">"</span> <span class="s2">"</span><span class="nv">$newFile</span><span class="s2">"</span>
<span class="c"># Extract the image dimensions from the info blob ...</span>
<span class="nv">identity</span><span class="o">=</span><span class="sb">`</span>identify <span class="nv">$newFile</span> | <span class="nb">awk</span> <span class="s1">'{print $3}'</span><span class="sb">`</span>
<span class="c"># ... and split dimensions to width and height.</span>
<span class="nv">dimensions</span><span class="o">=(</span><span class="k">${</span><span class="nv">identity</span><span class="p">//x/ </span><span class="k">}</span><span class="o">)</span>
<span class="nv">width</span><span class="o">=</span><span class="k">${</span><span class="nv">dimensions</span><span class="p">[0]</span><span class="k">}</span>
<span class="nv">height</span><span class="o">=</span><span class="k">${</span><span class="nv">dimensions</span><span class="p">[1]</span><span class="k">}</span>
<span class="k">fi</span>
<span class="c"># Scale down the image in height if needed.</span>
<span class="k">if</span> <span class="o">[</span> <span class="o">!</span> <span class="nt">-z</span> <span class="s2">"</span><span class="nv">$MAX_HEIGHT</span><span class="s2">"</span> <span class="o">]</span> <span class="o">&&</span> <span class="o">[</span> <span class="s2">"</span><span class="k">${</span><span class="nv">height</span><span class="k">:-</span><span class="nv">0</span><span class="k">}</span><span class="s2">"</span> <span class="nt">-ge</span> <span class="s2">"</span><span class="k">${</span><span class="nv">MAX_HEIGHT</span><span class="k">}</span><span class="s2">"</span> <span class="o">]</span><span class="p">;</span> <span class="k">then
</span><span class="nb">printf</span> <span class="s1">'Scaling image down from a height of %s pixels to %s pixels.\n'</span> <span class="s2">"</span><span class="nv">$height</span><span class="s2">"</span> <span class="s2">"</span><span class="nv">$MAX_HEIGHT</span><span class="s2">"</span>
convert <span class="s2">"</span><span class="nv">$newFile</span><span class="s2">"</span> <span class="nt">-resize</span> <span class="s2">"x</span><span class="nv">$MAX_HEIGHT</span><span class="s2">"</span> <span class="s2">"</span><span class="nv">$newFile</span><span class="s2">"</span>
<span class="k">fi</span>
<span class="c"># Reduce the quality of the image if needed.</span>
<span class="k">if</span> <span class="o">[</span> <span class="o">!</span> <span class="nt">-z</span> <span class="s2">"</span><span class="nv">$QUALITY</span><span class="s2">"</span> <span class="o">]</span><span class="p">;</span> <span class="k">then
</span><span class="nb">printf</span> <span class="s1">'Reducing the image quality to %s percent\n'</span> <span class="s2">"</span><span class="nv">$QUALITY</span><span class="s2">"</span>
convert <span class="s2">"</span><span class="nv">$newFile</span><span class="s2">"</span> <span class="nt">-quality</span> <span class="s2">"</span><span class="nv">$QUALITY</span><span class="s2">"</span> <span class="s2">"</span><span class="nv">$newFile</span><span class="s2">"</span>
<span class="k">fi
done</span></code></pre></figure>
<p>The resulting image fits nicely in my page and is about a factor ten smaller than the original.</p>
<figure class="highlight"><pre><code class="language-bash" data-lang="bash"><span class="nv">$ </span>identify images/cheat-cube.jpg
images/cheat-cube.jpg JPEG 565x400 565x400+0+0 8-bit sRGB 40.9KB 0.000u 0:00.000</code></pre></figure>
<p>Hope, you find this uselful. Stay tuned.</p>Andreas Schickedanzinfo@bornageek.comA few days ago i reactivated my blog and decided to switch from Wordpress to Jekyll. After I had solved most problems regarding the migration, I had to think about my new publishing process and the major issue I had to face was the preparation of my images. Wordpress scaled my images and reduced the image quality for me, now I have to do this on my own. To do this, I have written a small script that checks the width and height of a given image and scales it down and reduces its quality. This script internally uses two commands of the ImageMagick software suite, the identify and convert command. While identify allows me to collect all important information about a given image, convert enables me to convert the image to another image format, scale the image and reduce the image quality.Load .Xresources when using GDM in Arch Linux2015-10-16T15:38:00+02:002015-10-16T15:38:00+02:00https://bornageek.com/general/linux/2015/10/16/loading-xresources-when-using-gdm-in-arch-linux<p>A few days ago I faced some issues with lightdm after upgrading my system. A quick fix was to switch to GDM. However, after switching to GDM my <em>.Xresources</em> wasn’t load at boot. A quick look into <em>/etc/gdm</em> and I found the <em>Xsession</em> configuration file, which included the following code:</p>
<figure class="highlight"><pre><code class="language-bash" data-lang="bash"><span class="k">if</span> <span class="o">[</span> <span class="nt">-f</span> <span class="s2">"</span><span class="nv">$userresources</span><span class="s2">"</span> <span class="o">]</span><span class="p">;</span> <span class="k">then
</span>xrdb <span class="nt">-nocpp</span> <span class="nt">-merge</span> <span class="s2">"</span><span class="nv">$userresources</span><span class="s2">"</span>
<span class="k">fi</span></code></pre></figure>
<blockquote>
<p><strong>xrdb Manual:</strong> -nocpp This option indicates that xrdb should not run the input file through a preprocessor before loading it into properties.</p>
</blockquote>
<p>This means that my <em>.Xresources</em> configuration was loaded, but the includes were not executed because no preprocessor was used. So the GDM maintainers hoped to improve performance by using the <em>-nocpp</em> flag. So to fix this issue I just had to remove the flag and my <em>XTerm</em> styles were loaded.</p>
<p>Hope this will help you. Until next time: Stay geeky!</p>Andreas Schickedanzinfo@bornageek.comA few days ago I faced some issues with lightdm after upgrading my system. A quick fix was to switch to GDM. However, after switching to GDM my .Xresources wasn’t load at boot. A quick look into /etc/gdm and I found the Xsession configuration file, which included the following code: if [ -f "$userresources" ]; then xrdb -nocpp -merge "$userresources" fi xrdb Manual: -nocpp This option indicates that xrdb should not run the input file through a preprocessor before loading it into properties. This means that my .Xresources configuration was loaded, but the includes were not executed because no preprocessor was used. So the GDM maintainers hoped to improve performance by using the -nocpp flag. So to fix this issue I just had to remove the flag and my XTerm styles were loaded. Hope this will help you. Until next time: Stay geeky!Using Font Awesome in QML applications2015-10-01T14:08:03+02:002015-10-01T14:08:03+02:00https://bornageek.com/general/development/linux/2015/10/01/using-font-awesome-in-qml-applications<p>Today I was wondering if I could use Font Awesome with Qt’s QML to add some nice looking icons to my buttons. A quick google search brought up an article “<a href="https://kdeblog.mageprojects.com/2012/11/20/using-fonts-awesome-in-qml/">Using Fonts Awesome in QML</a>” by <em>markg85</em> at <a href="https://kdeblog.mageprojects.com">Mark’s KDE Blog</a>. Mark came up with a nice solution that uses a javascript dictionary to store the icon names with their corresponding unicode character:</p>
<figure class="highlight"><pre><code class="language-javascript" data-lang="javascript"><span class="kd">var</span> <span class="nx">Icon</span> <span class="o">=</span> <span class="p">{</span>
<span class="na">Glass</span> <span class="p">:</span> <span class="dl">"</span><span class="se">\</span><span class="s2">uf000</span><span class="dl">"</span><span class="p">,</span>
<span class="na">Music</span> <span class="p">:</span> <span class="dl">"</span><span class="se">\</span><span class="s2">uf001</span><span class="dl">"</span><span class="p">,</span>
<span class="p">...</span>
<span class="p">};</span></code></pre></figure>
<p>However, this has two problems.</p>
<ol>
<li>The names in the javascript file do not match the definitions on <a href="https://fontawesome.com/icons">fontawesome.github.io</a>.</li>
<li>The defintions are outdated and do neither include the latest icons nor alias names.</li>
</ol>
<blockquote>
<p><strong>Funny Fact:</strong> It’s actually <a href="https://fontawesome.com">fortawesome.github.io</a> not <a href="https://fontawesome.com">fontawesome.github.io</a>.</p>
</blockquote>
<!--more-->
<p>To fix both issues, I wrote a small python script, which will visit <a href="https://fontawesome.com/icons">https://fontawesome.com/icons</a>, extract all icon names and the corresponding icon pages (for example <a href="https://fontawesome.com/icons/cogs">https://fontawesome.com/icons/cogs</a>) and load the unicode associated with this icon name from the icon page:</p>
<figure class="highlight"><pre><code class="language-python" data-lang="python"><span class="c1">#!/usr/bin/python3
</span><span class="kn">import</span> <span class="nn">re</span>
<span class="kn">import</span> <span class="nn">urllib.request</span>
<span class="kn">import</span> <span class="nn">http.server</span>
<span class="k">def</span> <span class="nf">fetchSite</span><span class="p">(</span><span class="n">url</span><span class="p">):</span>
<span class="n">req</span> <span class="o">=</span> <span class="n">urllib</span><span class="o">.</span><span class="n">request</span><span class="o">.</span><span class="n">Request</span><span class="p">(</span><span class="n">url</span><span class="p">)</span>
<span class="k">try</span><span class="p">:</span>
<span class="k">with</span> <span class="n">urllib</span><span class="o">.</span><span class="n">request</span><span class="o">.</span><span class="n">urlopen</span><span class="p">(</span><span class="n">req</span><span class="p">)</span> <span class="k">as</span> <span class="n">response</span><span class="p">:</span>
<span class="n">html</span> <span class="o">=</span> <span class="n">response</span><span class="o">.</span><span class="n">read</span><span class="p">()</span>
<span class="k">except</span> <span class="n">urllib</span><span class="o">.</span><span class="n">error</span><span class="o">.</span><span class="n">HTTPError</span> <span class="k">as</span> <span class="n">e</span><span class="p">:</span>
<span class="n">errorMsg</span> <span class="o">=</span> <span class="n">http</span><span class="o">.</span><span class="n">server</span><span class="o">.</span><span class="n">BaseHTTPRequestHandler</span><span class="o">.</span><span class="n">responses</span><span class="p">[</span><span class="n">e</span><span class="o">.</span><span class="n">code</span><span class="p">][</span><span class="mi">0</span><span class="p">]</span>
<span class="k">print</span><span class="p">(</span><span class="s">"Cannot retrieve URL: {} : {}"</span><span class="o">.</span><span class="nb">format</span><span class="p">(</span><span class="nb">str</span><span class="p">(</span><span class="n">e</span><span class="o">.</span><span class="n">code</span><span class="p">),</span> <span class="n">errorMsg</span><span class="p">))</span>
<span class="k">except</span> <span class="n">urllib</span><span class="o">.</span><span class="n">error</span><span class="o">.</span><span class="n">URLError</span> <span class="k">as</span> <span class="n">e</span><span class="p">:</span>
<span class="k">print</span><span class="p">(</span><span class="s">"Cannot retrieve URL: {}"</span><span class="o">.</span><span class="nb">format</span><span class="p">(</span><span class="n">e</span><span class="o">.</span><span class="n">reason</span><span class="p">))</span>
<span class="k">except</span><span class="p">:</span>
<span class="k">print</span><span class="p">(</span><span class="s">"Cannot retrieve URL: unknown error"</span><span class="p">)</span>
<span class="k">return</span> <span class="n">html</span>
<span class="k">def</span> <span class="nf">extractIconLinks</span><span class="p">(</span><span class="n">html</span><span class="p">):</span>
<span class="n">links</span> <span class="o">=</span> <span class="p">{}</span>
<span class="n">pattern</span> <span class="o">=</span> <span class="s">r'<div class="fa-hover col-md-3 col-sm-4"><a href="../icon/(.*?)"><i class="fa (.*?)"></i> (.*?)</a></div>'</span>
<span class="k">for</span> <span class="n">match</span> <span class="ow">in</span> <span class="n">re</span><span class="o">.</span><span class="n">finditer</span><span class="p">(</span><span class="n">pattern</span><span class="p">,</span> <span class="nb">str</span><span class="p">(</span><span class="n">html</span><span class="p">)):</span>
<span class="n">iconName</span> <span class="o">=</span> <span class="n">match</span><span class="o">.</span><span class="n">group</span><span class="p">(</span><span class="mi">2</span><span class="p">)</span><span class="o">.</span><span class="n">partition</span><span class="p">(</span><span class="s">' '</span><span class="p">)[</span><span class="mi">0</span><span class="p">]</span>
<span class="n">iconLink</span> <span class="o">=</span> <span class="n">match</span><span class="o">.</span><span class="n">group</span><span class="p">(</span><span class="mi">1</span><span class="p">)</span>
<span class="k">if</span> <span class="n">iconName</span><span class="p">[:</span><span class="mi">3</span><span class="p">]</span> <span class="o">==</span> <span class="s">"fa-"</span><span class="p">:</span>
<span class="n">links</span><span class="p">[</span><span class="n">iconName</span><span class="p">]</span> <span class="o">=</span> <span class="s">"{}{}"</span><span class="o">.</span><span class="nb">format</span><span class="p">(</span><span class="s">"https://fontawesome.com/icons/"</span><span class="p">,</span> <span class="n">iconLink</span><span class="p">)</span>
<span class="k">return</span> <span class="n">links</span>
<span class="k">def</span> <span class="nf">extractUnicode</span><span class="p">(</span><span class="n">html</span><span class="p">):</span>
<span class="n">match</span> <span class="o">=</span> <span class="n">re</span><span class="o">.</span><span class="n">search</span><span class="p">(</span><span class="s">r'Unicode: <span class="upper">(.*?)</span>'</span><span class="p">,</span> <span class="nb">str</span><span class="p">(</span><span class="n">html</span><span class="p">))</span>
<span class="k">if</span> <span class="n">match</span><span class="p">:</span>
<span class="k">return</span> <span class="s">"</span><span class="se">\\</span><span class="s">u{}"</span><span class="o">.</span><span class="nb">format</span><span class="p">(</span><span class="n">match</span><span class="o">.</span><span class="n">group</span><span class="p">(</span><span class="mi">1</span><span class="p">))</span>
<span class="k">return</span> <span class="bp">None</span>
<span class="k">def</span> <span class="nf">extractIconData</span><span class="p">(</span><span class="n">html</span><span class="p">):</span>
<span class="n">icons</span> <span class="o">=</span> <span class="p">[]</span>
<span class="c1"># Extract the icon urls from the given site, ...
</span> <span class="n">links</span> <span class="o">=</span> <span class="n">extractIconLinks</span><span class="p">(</span><span class="n">html</span><span class="p">)</span>
<span class="c1"># ... fetch for each icon the unicode char.
</span> <span class="k">for</span> <span class="n">key</span><span class="p">,</span> <span class="n">value</span> <span class="ow">in</span> <span class="n">links</span><span class="o">.</span><span class="n">items</span><span class="p">():</span>
<span class="k">try</span><span class="p">:</span>
<span class="n">iconHtml</span> <span class="o">=</span> <span class="n">fetchSite</span><span class="p">(</span><span class="n">value</span><span class="p">)</span>
<span class="n">iconUnicode</span> <span class="o">=</span> <span class="n">extractUnicode</span><span class="p">(</span><span class="n">iconHtml</span><span class="p">)</span>
<span class="n">icons</span><span class="o">.</span><span class="n">append</span><span class="p">({</span><span class="s">'name'</span><span class="p">:</span> <span class="n">key</span><span class="p">,</span> <span class="s">'link'</span><span class="p">:</span> <span class="n">value</span><span class="p">,</span> <span class="s">'unicode'</span><span class="p">:</span> <span class="n">iconUnicode</span><span class="p">})</span>
<span class="k">except</span><span class="p">:</span>
<span class="k">print</span><span class="p">(</span><span class="s">"Failed to fetch icon {} from {}"</span><span class="o">.</span><span class="nb">format</span><span class="p">(</span><span class="n">key</span><span class="p">,</span> <span class="n">value</span><span class="p">))</span>
<span class="k">return</span> <span class="n">icons</span>
<span class="k">def</span> <span class="nf">main</span><span class="p">():</span>
<span class="c1"># Prepare the url of the main site ...
</span> <span class="n">fawesomeUrl</span> <span class="o">=</span> <span class="s">"https://fontawesome.com/icons/"</span>
<span class="c1"># ... fetch its' content ...
</span> <span class="n">fawesomeHtml</span> <span class="o">=</span> <span class="n">fetchSite</span><span class="p">(</span><span class="n">fawesomeUrl</span><span class="p">)</span>
<span class="c1"># ... and extract the icon data.
</span> <span class="n">icons</span> <span class="o">=</span> <span class="n">extractIconData</span><span class="p">(</span><span class="n">fawesomeHtml</span><span class="p">)</span>
<span class="c1"># Write the result to fontawesome.js.
</span> <span class="k">with</span> <span class="nb">open</span><span class="p">(</span><span class="s">'fontawesome.js'</span><span class="p">,</span> <span class="s">'w'</span><span class="p">)</span> <span class="k">as</span> <span class="n">fp</span><span class="p">:</span>
<span class="n">line</span> <span class="o">=</span> <span class="s">'</span><span class="se">\t</span><span class="s">'</span>
<span class="n">fp</span><span class="o">.</span><span class="n">write</span><span class="p">(</span><span class="s">"var Icon = {</span><span class="se">\n</span><span class="s">"</span><span class="p">)</span>
<span class="k">for</span> <span class="n">icon</span> <span class="ow">in</span> <span class="n">icons</span><span class="p">:</span>
<span class="n">escapedName</span> <span class="o">=</span> <span class="n">icon</span><span class="p">[</span><span class="s">'name'</span><span class="p">]</span><span class="o">.</span><span class="n">replace</span><span class="p">(</span><span class="s">'-'</span><span class="p">,</span> <span class="s">'_'</span><span class="p">)</span>
<span class="n">iconEntry</span> <span class="o">=</span> <span class="s">'{}: "{}", '</span><span class="o">.</span><span class="nb">format</span><span class="p">(</span><span class="n">escapedName</span><span class="p">,</span> <span class="n">icon</span><span class="p">[</span><span class="s">'unicode'</span><span class="p">])</span>
<span class="k">if</span> <span class="nb">len</span><span class="p">(</span><span class="s">'{}{}'</span><span class="o">.</span><span class="nb">format</span><span class="p">(</span><span class="n">line</span><span class="p">,</span> <span class="n">iconEntry</span><span class="p">))</span> <span class="o"><=</span> <span class="mi">80</span><span class="p">:</span>
<span class="n">line</span> <span class="o">=</span> <span class="s">'{}{}'</span><span class="o">.</span><span class="nb">format</span><span class="p">(</span><span class="n">line</span><span class="p">,</span> <span class="n">iconEntry</span><span class="p">)</span>
<span class="k">else</span><span class="p">:</span>
<span class="n">fp</span><span class="o">.</span><span class="n">write</span><span class="p">(</span><span class="s">'{}</span><span class="se">\n</span><span class="s">'</span><span class="o">.</span><span class="nb">format</span><span class="p">(</span><span class="n">line</span><span class="p">))</span>
<span class="n">line</span> <span class="o">=</span> <span class="s">'</span><span class="se">\t</span><span class="s">{}'</span><span class="o">.</span><span class="nb">format</span><span class="p">(</span><span class="n">iconEntry</span><span class="p">)</span>
<span class="n">fp</span><span class="o">.</span><span class="n">write</span><span class="p">(</span><span class="s">'{}</span><span class="se">\n</span><span class="s">}};'</span><span class="o">.</span><span class="nb">format</span><span class="p">(</span><span class="n">line</span><span class="p">))</span>
<span class="k">if</span> <span class="n">__name__</span> <span class="o">==</span> <span class="s">"__main__"</span><span class="p">:</span>
<span class="n">main</span><span class="p">()</span></code></pre></figure>
<p>This will generate a file in the current working directory called <em>fontawesome.js</em> with the following content (shorted):</p>
<figure class="highlight"><pre><code class="language-javascript" data-lang="javascript"><span class="kd">var</span> <span class="nx">Icon</span> <span class="o">=</span> <span class="p">{</span>
<span class="na">fa_copyright</span><span class="p">:</span> <span class="dl">"</span><span class="se">\</span><span class="s2">uf1f9</span><span class="dl">"</span><span class="p">,</span> <span class="na">fa_connectdevelop</span><span class="p">:</span> <span class="dl">"</span><span class="se">\</span><span class="s2">uf20e</span><span class="dl">"</span><span class="p">,</span> <span class="na">fa_archive</span><span class="p">:</span> <span class="dl">"</span><span class="se">\</span><span class="s2">uf187</span><span class="dl">"</span><span class="p">,</span>
<span class="na">fa_taxi</span><span class="p">:</span> <span class="dl">"</span><span class="se">\</span><span class="s2">uf1ba</span><span class="dl">"</span><span class="p">,</span> <span class="na">fa_file_sound_o</span><span class="p">:</span> <span class="dl">"</span><span class="se">\</span><span class="s2">uf1c7</span><span class="dl">"</span><span class="p">,</span>
<span class="p">...</span>
<span class="na">fa_link</span><span class="p">:</span> <span class="dl">"</span><span class="se">\</span><span class="s2">uf0c1</span><span class="dl">"</span><span class="p">,</span>
<span class="p">};</span></code></pre></figure>
<p>The resulting javascript file could be loaded and accessed within the <em>*.qml</em> file as Mark described in his Blog Post:</p>
<figure class="highlight"><pre><code class="language-javascript" data-lang="javascript"><span class="k">import</span> <span class="nx">QtQuick</span> <span class="mf">2.5</span>
<span class="k">import</span> <span class="dl">"</span><span class="s2">fontawesome.js</span><span class="dl">"</span> <span class="k">as</span> <span class="nx">FontAwesome</span>
<span class="nx">ApplicationWindow</span> <span class="p">{</span>
<span class="nl">visible</span><span class="p">:</span> <span class="kc">true</span>
<span class="nx">width</span><span class="p">:</span> <span class="mi">300</span>
<span class="nx">height</span><span class="p">:</span> <span class="mi">300</span>
<span class="nx">title</span><span class="p">:</span> <span class="nx">qsTr</span><span class="p">(</span><span class="dl">"</span><span class="s2">Font Awesome - Test</span><span class="dl">"</span><span class="p">)</span>
<span class="nx">FontLoader</span> <span class="p">{</span>
<span class="nl">source</span><span class="p">:</span> <span class="dl">"</span><span class="s2">fontawesome-webfont.ttf</span><span class="dl">"</span>
<span class="p">}</span>
<span class="nx">Rectangle</span> <span class="p">{</span>
<span class="nl">id</span><span class="p">:</span> <span class="nx">main</span>
<span class="nx">anchors</span><span class="p">.</span><span class="nx">fill</span><span class="p">:</span> <span class="nx">parent</span>
<span class="nx">Text</span> <span class="p">{</span>
<span class="nx">anchors</span><span class="p">.</span><span class="nx">horizontalCenter</span><span class="p">:</span> <span class="nx">main</span><span class="p">.</span><span class="nx">horizontalCenter</span>
<span class="nx">anchors</span><span class="p">.</span><span class="nx">verticalCenter</span><span class="p">:</span> <span class="nx">main</span><span class="p">.</span><span class="nx">verticalCenter</span>
<span class="nx">font</span><span class="p">.</span><span class="nx">pointSize</span><span class="p">:</span> <span class="mi">100</span>
<span class="nx">font</span><span class="p">.</span><span class="nx">family</span><span class="p">:</span> <span class="dl">"</span><span class="s2">FontAwesome</span><span class="dl">"</span>
<span class="nx">text</span><span class="p">:</span> <span class="nx">FontAwesome</span><span class="p">.</span><span class="nx">Icon</span><span class="p">.</span><span class="nx">fa_optin_monster</span>
<span class="p">}</span>
<span class="p">}</span>
<span class="p">}</span></code></pre></figure>
<p>The above QML code should produce the following output:</p>
<p><a href="https://bornageek.com/wp-content/uploads/2015/10/font_awesome_window.jpg"><img src="https://bornageek.com/wp-content/uploads/2015/10/font_awesome_window.jpg" alt="Font Awesome Test Application" /></a></p>
<p>That’s it. Enjoy and until next time: Keep coding!</p>Andreas Schickedanzinfo@bornageek.comToday I was wondering if I could use Font Awesome with Qt’s QML to add some nice looking icons to my buttons. A quick google search brought up an article “Using Fonts Awesome in QML” by markg85 at Mark’s KDE Blog. Mark came up with a nice solution that uses a javascript dictionary to store the icon names with their corresponding unicode character: var Icon = { Glass : "\uf000", Music : "\uf001", ... }; However, this has two problems. The names in the javascript file do not match the definitions on fontawesome.github.io. The defintions are outdated and do neither include the latest icons nor alias names. Funny Fact: It’s actually fortawesome.github.io not fontawesome.github.io.Cubify - The Inkscape Plugin for creating Cheat Cubes2014-09-21T13:10:36+02:002014-09-21T13:10:36+02:00https://bornageek.com/general/development/linux/software/2014/09/21/cubify-the-inkscape-plugin-for-creating-cheat-cubes<p><img src="https://bornageek.com/images/cheat-cube.jpg" alt="Raspberry Pi Cheat Cube" /></p>
<p>I love cheat sheets. A good cheat sheet decorates your desk and presents its content in a simple and understandable way. And a perfect cheat sheet presents its content also in an entertaining way and is called Cheat Cube. Cheat Cubes are awesome. It decorates your desk and serves as a toy, while waiting for a download to end, a game to load or a compiler to finish its process. And as a pleasant side effect, you learn some new stuff, you can benefit from in your daily life.</p>
<p>However, I was looking for a weekend project and decided to write my first <a href="https://www.inkscape.org" title="Inkscape Project Homepage">Inkscape</a> Plugin that simplifies the creation of Cheat Cubes and I ended up with <a href="https://github.com/Bornageek/Cubify" title="Cubify Project Homepage">Cubify</a>.</p>
<!--more-->
<p>In order to install this plugin you should first download the sources from GitHub:</p>
<figure class="highlight"><pre><code class="language-sh" data-lang="sh"><span class="nv">$ </span>git clone https://github.com/Bornageek/Cubify</code></pre></figure>
<p>Then you can move the downloaded files to the extentions folder of your Inkscape installation:</p>
<figure class="highlight"><pre><code class="language-sh" data-lang="sh"><span class="nv">$ </span><span class="nb">sudo cp</span> ./Cubify/<span class="k">*</span> /usr/share/inkscape/extentions/</code></pre></figure>
<p><img src="https://bornageek.com/images/Cubify.jpg" alt="The Cubify Wizzard" /></p>
<p>If you now start Inkscape, you should see a new entry in the <em>Extentions -> Python</em> submenu, named Cubify. Open a new document and start the Cubify extention wizzard located under <em>Extentions -> Python -> Cubify ….</em> Here you can change different options modifying the look and feel as well as the content of your cheat cube.</p>
<p>The first entries of this dialog can be used to change the color scheme of your cheat cube. So you can change the main text color, the text and background color of the footer (eg. title) of a cube side and the text and background color of the hint box above this footer. Furthermore you can set the cheat cube logo and the background color of the logo side.</p>
<p>Below this color scheme preferences, you will find a tabbed pane with five tabs, which each holds the preferences of one side of the cube. Here you can set the title of the footer, the hint text above and the title image as well as fifteen text lines for the cube side. Within this text lines you can use a very small subset of bbcode to format this lines. You can use [b][/b] for bold and [i][/i] for italic text, as well as [color=000000][/color] to change the text color. The color is specified using the color’s hex code, but without a leading hashtag (<em>#</em>).</p>
<p>When you entered all important data, click Apply and close the wizzard. You will see the scheme of your custom Cheat Cube. If the skeleton does not fit the document layout, visit <em>Files -> Document Properties …</em> and choose <em>Resize page to content …</em> at the bottom of the dialog. Select Resize page to drawing or selection and close the dialog. Now the skeleton of your Cheat Cube should exactly fit the document.</p>
<blockquote>
<p><strong>Caution:</strong> Currently you can only use *.svg images for the logos.</p>
</blockquote>
<p>Hope, you like this plugin and start creating your own Cheat Cubes. Looking forward to see some of your creations. Just share them with me in this post or share it with me on twitter.</p>
<p>Until next time, stay geeky.</p>Andreas Schickedanzinfo@bornageek.comI love cheat sheets. A good cheat sheet decorates your desk and presents its content in a simple and understandable way. And a perfect cheat sheet presents its content also in an entertaining way and is called Cheat Cube. Cheat Cubes are awesome. It decorates your desk and serves as a toy, while waiting for a download to end, a game to load or a compiler to finish its process. And as a pleasant side effect, you learn some new stuff, you can benefit from in your daily life. However, I was looking for a weekend project and decided to write my first Inkscape Plugin that simplifies the creation of Cheat Cubes and I ended up with Cubify.Home automation - RaspberryPi, FHEM and Arch Linux2014-08-17T15:13:20+02:002014-08-17T15:13:20+02:00https://bornageek.com/general/linux/hardware/2014/08/17/home-automation-raspberrypi-fhem-and-arch-linux<p>Once upon a time, there was a computer science student blogging about all that stuff that comes to his mind … Yeah, it has been about a year since my last blog post and I apologize for that. However, I would like to share some experiences with you that I gathered during one of my projects, namely home automation using the Raspberry Pi.</p>
<p>In this post I will show you how you can setup the GPL’d perl server for house automation, <a href="https://fhem.de" title="fhem">fhem</a>, on your Raspberry Pi or an other single board computer, like the Beaglebone (Black), Bananapi or Gooseberry, running the one and only Arch Linux distribution. Finally I will give an example of how you can use <em>fhem</em> to control <a href="https://www.homematic.com/" title="HomeMatic">HomeMatic</a> or other home automation components. So lets get started with the Arch Linux setup.</p>
<!--more-->
<h3 id="installing-arch-linux">Installing Arch Linux</h3>
<p><img src="https://bornageek.com/images/IMG_0014.jpg" alt="Homematic-Adapter mit RaspberryPi" /></p>
<p>To get started, we download the <a href="https://archlinuxarm.org/os/ArchLinuxARM-rpi-latest.zip" title="Latest Arch Raspberry Pi Image">latest release of Arch Linux</a> for the Raspberry Pi, which is available on the <a href="https://archlinuxarm.org/platforms/armv6/raspberry-pi" title="ArchLinuxARM Project Page">ArchLinuxARM Project page</a>. As the download has finished, extract the zip file to your hard drive, giving you the arch image. This image could then be written to an SD or micro SD card using the <em>dd</em> command:</p>
<figure class="highlight"><pre><code class="language-bash" data-lang="bash"><span class="nb">dd </span><span class="nv">bs</span><span class="o">=</span>1M <span class="k">if</span><span class="o">=</span>/path/to/archlinux-hf-2013-07-22.img <span class="nv">of</span><span class="o">=</span>/dev/sdX</code></pre></figure>
<p>Replace sdX in the command above with the location of the SD or micro SD card.</p>
<blockquote>
<p>Please ensure that you just use <em>/dev/sdX</em> and not <em>/dev/sdXi</em>. So if your micro SD card has one partition <em>/dev/sdb1</em>, you should just enter <em>/dev/sdb</em>.</p>
</blockquote>
<p>When the <em>dd</em> command has terminated, eject the card from your desktop PC, insert it into the Raspberry Pi and power it by plugging in the micro usb cable. You should now be able to ssh onto your Raspberry Pi using username ‘<em>root</em>’ with a password ‘<em>root</em>’.</p>
<blockquote>
<p>The steps above are the only ones dedicated to the Raspberry Pi. The following steps can also be done on other boards. However, if you would like to use the Beaglebone or Beaglebone Black, you could use my Script to install Arch Linux. Just follow the Instractions in my post “<a href="https://bornageek.com/804/flashing-arch-linux-to-the-beaglebone-beaglebone-black-or-beaglebone-black-emmc/" title="Flashing Arch Linux to the Beaglebone, Beaglebone Black or Beaglebone Black eMMC">Flashing Arch Linux to the Beaglebone, Beaglebone Black or Beaglebone Black eMMC</a>”.</p>
</blockquote>
<p>The first thing you should do is to set a new password for the root user:</p>
<figure class="highlight"><pre><code class="language-bash" data-lang="bash">passwd</code></pre></figure>
<p>Then you should create a new user for <em>fhem</em></p>
<figure class="highlight"><pre><code class="language-bash" data-lang="bash">useradd <span class="nt">-m</span> <span class="nt">-g</span> <span class="nb">users</span> <span class="nt">-G</span> audio,lp,optical,storage,video,wheel,games, power, <span class="nt">-s</span> /bin/bash fhem</code></pre></figure>
<p>and set the password for this user:</p>
<figure class="highlight"><pre><code class="language-bash" data-lang="bash">passwd fhem</code></pre></figure>
<blockquote>
<p>You could choose all usergroups you want, like <em>audio</em>, <em>storage</em>, etc., but you have to add the ‘<em>wheel</em>’ user group. Otherwise you will not be able to setup <em>sudo</em> for this user.</p>
</blockquote>
<p>You should now be able to connect to your Raspberry Pi using the username ‘<em>fhem</em> and the password you entered for this user. However, this user is not able to install, delete or update anything on this system. That is why we are going to setup the <em>sudo</em> command:</p>
<figure class="highlight"><pre><code class="language-bash" data-lang="bash">pacman <span class="nt">-S</span> <span class="nb">sudo</span></code></pre></figure>
<p>In order to enable sudo for the new user, you have to uncomment the following line in <em>/etc/sudoers</em>:</p>
<figure class="highlight"><pre><code class="language-bash" data-lang="bash">%wheel <span class="nv">ALL</span><span class="o">=(</span>ALL<span class="o">)</span> ALL</code></pre></figure>
<p>The <em>fhem</em> user should now have root privileges. To test this you could check that sudo works for this regular user by logging back</p>
<figure class="highlight"><pre><code class="language-bash" data-lang="bash">su - fhem</code></pre></figure>
<p>and trying to run a system update:</p>
<figure class="highlight"><pre><code class="language-bash" data-lang="bash"><span class="nb">sudo </span>pacman <span class="nt">-Syu</span></code></pre></figure>
<blockquote>
<p>In some cases it can happen that <em>pacman</em> terminates with a SSL certificate error. This could be caused by a wrong date/time configuration. In this case you have to set the current timezone using:</p>
</blockquote>
<figure class="highlight"><pre><code class="language-bash" data-lang="bash">timedatectl set-timezone Zone/SubZone </code></pre></figure>
<blockquote>
<p>The available timezones could be listed using:</p>
</blockquote>
<figure class="highlight"><pre><code class="language-bash" data-lang="bash">timedatectl list-timezones</code></pre></figure>
<blockquote>
<p>Then you should set the current time using</p>
</blockquote>
<figure class="highlight"><pre><code class="language-bash" data-lang="bash">timedatectl set-time <span class="s2">"2013-12-04 16:11:00"</span></code></pre></figure>
<blockquote>
<p>because otherwise the Network Time Protocol Deamon (<em>ntpd</em>) would not be able to syncronize date and time with a server.</p>
<p>Finally you could manually syncronize your clock with the network to the correct time by running:</p>
</blockquote>
<figure class="highlight"><pre><code class="language-bash" data-lang="bash">ntpd <span class="nt">-qg</span></code></pre></figure>
<blockquote>
<p>The date/time synchronization is also important, because <em>fhem</em> requires a synchronized clock to be able to execute commands at the right time.</p>
</blockquote>
<h3 id="installing-fhem">Installing fhem</h3>
<p>fhem is a perl server for house automation, thus you have to install some required perl packages:</p>
<figure class="highlight"><pre><code class="language-bash" data-lang="bash"><span class="nb">sudo </span>pacman <span class="nt">-S</span> perl-device-serialport perl-io-socket-ssl perl-libwww</code></pre></figure>
<p>You are now ready to install fhem. Therefore download the latest version of fhem from the <a href="https://fhem.de" title="The FHEM project homepage">project’s website</a> to your desktop PC and copy the compressed tarball to the Raspberry Pi:</p>
<figure class="highlight"><pre><code class="language-bash" data-lang="bash">scp fhem-5.5.tar.gz fhem@192.168.1.77:~/fhem-5.5.tar.gz</code></pre></figure>
<p>Extract the file</p>
<figure class="highlight"><pre><code class="language-bash" data-lang="bash"><span class="nb">tar </span>xvfz fhem-5.5.tar.gz</code></pre></figure>
<p>and switch into the extracted directory. Now execute the Makefile provided with fhem to install fhem to <em>/opt/fhem</em>:</p>
<figure class="highlight"><pre><code class="language-python" data-lang="python"><span class="n">make</span> <span class="n">install</span></code></pre></figure>
<p>That’s it! You successfully installed fhem.</p>
<blockquote>
<p>Instead of installing fhem manually by downloading it and running <em>make install</em>, you could also install it from the <em>AUR Repositories</em>. Therefore you have to install the yaourt package manager and the <em>binutils</em>, which are available in the base development utils (<em>base-devel</em>), to install fhem directly from the repositories:</p>
</blockquote>
<figure class="highlight"><pre><code class="language-bash" data-lang="bash"><span class="nb">sudo </span>pacman <span class="nt">-S</span> yaourt base-devel</code></pre></figure>
<blockquote>
<p>However, there seems to be a problem with the <em>fhem</em> package provided in the <em>AUR Repositories</em>, so I installed it manually.</p>
</blockquote>
<p>You should now be able to start the perl server by running the fhem.pl script:</p>
<figure class="highlight"><pre><code class="language-bash" data-lang="bash">perl /opt/fhem/fhem.pl /opt/fhem/fhem.cfg</code></pre></figure>
<p>It is not that smart to execute this command every time you reboot your Raspberry Pi. Thus, I created a service entry for <em>systemd</em>. Just save the following configuration file as <em>/etc/systemd/system/fhem.service</em>:</p>
<figure class="highlight"><pre><code class="language-ini" data-lang="ini"><span class="nn">[Unit]</span>
<span class="py">Description</span><span class="p">=</span><span class="s">FHEM Perl Server</span>
<span class="py">After</span><span class="p">=</span><span class="s">syslog.target network.target</span>
<span class="nn">[Service]</span>
<span class="py">User</span><span class="p">=</span><span class="s">fhem</span>
<span class="py">Type</span><span class="p">=</span><span class="s">forking</span>
<span class="py">WorkingDirectory</span><span class="p">=</span><span class="s">/opt/fhem</span>
<span class="py">ExecStart</span><span class="p">=</span><span class="s">/opt/fhem/fhem.pl /opt/fhem/fhem.cfg</span>
<span class="py">Restart</span><span class="p">=</span><span class="s">always</span>
<span class="nn">[Install]</span>
<span class="py">WantedBy</span><span class="p">=</span><span class="s">multi-user.target</span></code></pre></figure>
<p>The fhem service could now be enabled and started:</p>
<figure class="highlight"><pre><code class="language-bash" data-lang="bash"><span class="nb">sudo </span>systemctl <span class="nb">enable </span>fhem
<span class="nb">sudo </span>systemctl start fhem</code></pre></figure>
<p>This will also ensure that the service automatically restarts if the Raspberry Pi is rebooted.</p>
<p>Finally you have to change the owner of the fhem installation folder, because otherwise our <em>fhem</em> user will not be able to change the fhem configuration file:</p>
<figure class="highlight"><pre><code class="language-bash" data-lang="bash"><span class="nb">sudo chown</span> <span class="nt">-R</span> fhem /opt/fhem</code></pre></figure>
<h3 id="basic-setup-of-fhem">Basic Setup of fhem</h3>
<p><img src="https://bornageek.com/images/IMG_0006.jpg" alt="Homematic Components" /></p>
<p>Now that you have installed fhem, you could customize it by changing some configurations within the <em>fhem.cfg</em> configuration file. You could change the file directly from the fhem web interface. Therefore go to [<rasppi-pi>:8083](192.168.1.1:8083 "The local Fhem server") and choose *Edit files* -> *fhem.cfg* from the menu at the left side. Here you could update your configuration file and save your changes by clicking *Save fhem.cfg*.</rasppi-pi></p>
<p>At first you should set a password for the fhem web and mobile interface, so nobody could access the system and change something without granted permission. Therefore switch to your desktop PC and generate a password using the following command:</p>
<figure class="highlight"><pre><code class="language-bash" data-lang="bash"><span class="nb">echo</span> <span class="nt">-n</span> <username>:
<password> | <span class="nb">base64</span></code></pre></figure>
<p>Replace the username before the colon (:) and the password after with your own. This will give you a base64 string like this one:</p>
<figure class="highlight"><pre><code class="language-bash" data-lang="bash"><span class="nv">dXNlcm5hbWU6cGFzc3dvcmQ</span><span class="o">=</span></code></pre></figure>
<p>This string could now be used to set the password for the fhem interfaces. Just add a <em>basicAuth</em> attribute for the interface that should be protected by your username and password combination. The attribute definition should look like this:</p>
<figure class="highlight"><pre><code class="language-bash" data-lang="bash">attr <interface> basicAuth <span class="nv">dXNlcm5hbWU6cGFzc3dvcmQ</span><span class="o">=</span></code></pre></figure>
<p>The interface part of your <em>fhem.cfg</em> file should now look like this:</p>
<figure class="highlight"><pre><code class="language-bash" data-lang="bash">define WEB FHEMWEB 8083 global
attr WEB basicAuth <span class="nv">dXNlcm5hbWU6cGFzc3dvcmQ</span><span class="o">=</span>
define WEBphone FHEMWEB 8084 global
attr WEBphone stylesheetPrefix smallscreen
attr WEBphone basicAuth <span class="nv">dXNlcm5hbWU6cGFzc3dvcmQ</span><span class="o">=</span>
define WEBtablet FHEMWEB 8085 global
attr WEBtablet stylesheetPrefix touchpad
attr WEBtablet basicAuth <span class="nv">dXNlcm5hbWU6cGFzc3dvcmQ</span><span class="o">=</span></code></pre></figure>
<p>The changes to the configuration file take effect as soon as you restart fhem. Therefore click in the input prompt at the top of the fhem web interface and enter:</p>
<figure class="highlight"><pre><code class="language-bash" data-lang="bash">shutdown restart</code></pre></figure>
<p>After a few seconds the server is reachable again and will ask for a username and password.</p>
<h3 id="connecting-the-hmlan-adapter">Connecting the HMLan adapter</h3>
<p><img src="https://bornageek.com/images/IMG_0012.jpg" alt="HMLan adapter setup" /></p>
<p>The HomeMatic LAN adapter handels the communication between fhem and the various HomeMatic components. To be able to use it with fhem, you have to disable the AES encryption of the LAN communication, since fhem does not yet support encrypted communication. Then you can register the adapter with fhem by defining it within the fhem configuration file.</p>
<p>In order to disable the AES encryption of the LAN adapter, you have to download the <em>Configuration LAN adapter Usersoftware</em> from the <a href="https://www.eq-3.de/software.html" title="eq-3 Homepage">eq-3 Homepage</a>. The provided <em>.zip file contains two Windows executables (</em>.exe files). Just install the one named <em>Setup_HMCFG.exe</em>.</p>
<blockquote>
<p>Even if you disable the AES encryption between the HomeMatic LAN adapter and your Raspberry Pi, the communication between the LAN adapter and the different HomeMatic components is still encrypted.</p>
</blockquote>
<blockquote>
<p>If you are one of the clever guys running a Linux desktop, no problem. The HomeMatic software tools work great with <a href="https://winehq.org/" title="The wine project homepage">wine</a>.</p>
</blockquote>
<blockquote>
<p>When the installation has finished open the <em>Homematic LAN-Interface Configurator</em>, select your LAN interface and click <em>Change IP Settings</em>. At the end of the dialog you will find an entry <em>AES Encrypt LAN Communication</em>, which you have to uncheck to be able to use this adapter with fhem.</p>
</blockquote>
<p>If your LAN adapter is not displayed in the list, it might be that the <em>Homematic LAN-Interface Configurator</em> has a problem with other open ethernet ports. To tackle this issue, close all your ethernet connections, except the one that connects you to the network of your LAN adapter.</p>
<blockquote>
<p>Establishing a connection between the adapter and the Raspberry Pi that runs the fhem server is now quite easy. All you have to do is to define a name for the adapter along with its IP address within the fhem configuration file, as well as a unique ID for the LAN adapter:</p>
</blockquote>
<figure class="highlight"><pre><code class="language-bash" data-lang="bash">define HMLAN1 HMLAN 192.168.1.77:1000
attr HMLAN1 hmId 123ABC</code></pre></figure>
<blockquote>
<p>HMLAN1 is in this case the name of the LAN adapter, 192.168.1.77 the IP of the adapter, 1000 the port and 123ABC the HomeMatic ID. The here used HomeMatic ID has to be a unique six digit hexadecimal code, which you could choose by your own.</p>
</blockquote>
<blockquote>
<p>Some people recommend to use the HomeMatic ID provided by the HomeMatic tools. My experience is that this is not needed. However, if you would like to use this hmid, you have to start the <em>Configure HomeMatic Components</em> tool, that ships with the <em>.zip file you already downloaded. It will ask you for the product code and the AES key, which you find at the back side of your LAN adapter. Once the entry has been confirmed, you will see a message in the lower left corner of the window, telling you that you are connected to *JEQ07…</em>. When the connection is established, you will find a new hidden file named <em>ids</em> in *C:\Program Files (x86)\BidCoS Service* that contains an entry *BidCOS-Adress = * followed by your HomeMatic ID. In order to ensure that this approach works, you have to turn on AES encryption again, because otherwise the adapter will refuse the connection.</p>
</blockquote>
<p>Now your LAN adapter is setup for communication with fhem. To approve your setup, just click at <em>Everything</em> in the left menu of fhem and search for the entry <em>HMLAN1</em>. If the state of the adapter is <em>opened</em>, everything is fine. If the state is <em>disconnected</em>, ensure that AES encryption is disabled and no other software is connected to the adapter.</p>
<h3 id="pairing-devices">Pairing devices</h3>
<p>Now your LAN adapter is setup for communication with fhem and you could start pairing devices with fhem. I started with setting up a remote control switch, a three state position sensor and a thermostat. In order to pair a device with fhem, you just enter</p>
<figure class="highlight"><pre><code class="language-bash" data-lang="bash"><span class="nb">set </span>HMLAN1 hmPairForSec 60</code></pre></figure>
<p>in the fhem terminal at the top of the fhem page. The HomeMatic LAN adapter is now in ready-to-receive state for 60 seconds. You now have to press the hardware pairing button of the device you would like to connect to fhem.</p>
<p><img src="https://bornageek.com/images/IMG_0008.jpg" alt="Homematic Devices" /></p>
<p>Homematic Devices After a few seconds the device should show up in a new device list called <em>CUL_HM</em> that should be reachable from the navigation on the left site. This list should hold two entries, one for the device itself and one for the log file of this device. So for example as I connected the remote control switch, I got an entry <em>CUL_HM_HM_LC_SW1_PL2_20747A</em> for the device and an entry <em>FileLog_CUL_HM_HM_LC_SW1_PL2_20747A</em> for the corresponding log file. If you now open your fhem configuration file <em>fhem.cfg</em> you should also see two new definitions like the follwoing:</p>
<figure class="highlight"><pre><code class="language-bash" data-lang="bash"><span class="c"># Define the first switch ...</span>
define CUL_HM_HM_LC_SW1_PL2_20747A CUL_HM 20747A
attr CUL_HM_HM_LC_SW1_PL2_20747A .devInfo 010100
attr CUL_HM_HM_LC_SW1_PL2_20747A .stc 10
attr CUL_HM_HM_LC_SW1_PL2_20747A expert 2_full
attr CUL_HM_HM_LC_SW1_PL2_20747A firmware 1.9
attr CUL_HM_HM_LC_SW1_PL2_20747A model HM-LC-SW1-PL2
attr CUL_HM_HM_LC_SW1_PL2_20747A peerIDs
attr CUL_HM_HM_LC_SW1_PL2_20747A room CUL_HM
attr CUL_HM_HM_LC_SW1_PL2_20747A serialNr KEQ0036609
attr CUL_HM_HM_LC_SW1_PL2_20747A subType switch
attr CUL_HM_HM_LC_SW1_PL2_20747A webCmd toggle:on:off:statusRequest
<span class="c"># ... and the switch log file.</span>
define FileLog_CUL_HM_HM_LC_SW1_PL2_20747A FileLog ./log/Lamp1-%Y.log Lamp1
attr FileLog_CUL_HM_HM_LC_SW1_PL2_20747A logtype text
attr FileLog_CUL_HM_HM_LC_SW1_PL2_20747A room CUL_HM</code></pre></figure>
<p>There are now two things you might want to change - the name of the device and the room name (currently <em>CUL_HM</em>). To change the name of the device you just have to enter the following command in the fhem console:</p>
<figure class="highlight"><pre><code class="language-bash" data-lang="bash">rename <old-name> <new-name></code></pre></figure>
<p>So for example as I renamed my remote control switch, I had to enter the following:</p>
<figure class="highlight"><pre><code class="language-bash" data-lang="bash">rename CUL_HM_HM_LC_SW1_PL2_20747A Lamp1</code></pre></figure>
<p>To change the name of the room open your <em>fhem.cfg</em> and search for the room definition for your device</p>
<figure class="highlight"><pre><code class="language-bash" data-lang="bash">attr CUL_HM_HM_LC_SW1_PL2_20747A room CUL_HM</code></pre></figure>
<p>and change the name to your prefered one.</p>
<blockquote>
<p>You might wonder that the entry for the log file of your device disappeared after you defined the room for your device, but it is still there. You just cannot see it, because the entry remains in the old room <em>CUL_HM</em>. The reason for this is that you could specify different rooms for the logs and the devices. To change this, you must also change the name of the log files room.</p>
</blockquote>
<figure class="highlight"><pre><code class="language-bash" data-lang="bash">attr FileLog_CUL_HM_HM_LC_SW1_PL2_20747A room <new-room-name></code></pre></figure>
<p>You should also take a look at some other attributes of your devices that you paired with fhem.</p>
<p>The <em>webCmd</em> attribute that you find in the extract from my <em>fhem.cfg</em> above, controls the different buttons that are displayed in the web frontend. In the case of my remote control switch there are four buttons <em>on</em>, <em>off</em>, <em>toggle</em> and <em>statusRequest</em>. To remove one of this buttons just remove the corresponding entry. To add a button that for example turns on the switch for 5 seconds, you could add an entry <em>on-for-timer 5</em>:</p>
<figure class="highlight"><pre><code class="language-bash" data-lang="bash">attr Lamp1 webCmd toggle:on:off:statusRequest:on-for-timer 5</code></pre></figure>
<p>You could also rename this buttons using the attribute <em>eventMap</em>:</p>
<figure class="highlight"><pre><code class="language-bash" data-lang="bash">attr Lamp1 eventMap on:An off:Aus</code></pre></figure>
<p>If you use the <em>eventMap</em> command to rename actions in the web interface, you do not need to declare their usage using the <em>webCmd</em> command. If you do so this can even create conflicts and the renaming is ignored.</p>
<h3 id="the-next-step">The next step</h3>
<p>You now have setup a Raspberry Pi (or another single board computer) with Arch Linux, installed the fhem home automation server and paired a first HomeMatic component. So you have made the first step towards automating your home. But this is just the beginning. You can do so much using fhem:</p>
<ul>
<li>Use other home automation systems</li>
<li>Control actors like a radiator thermostat on sensor events</li>
<li>Monitor the power consumption</li>
<li>Plot sensor events</li>
<li>Send mails on sensor events</li>
<li>Execute bash scripts on sensor events</li>
</ul>
<p>You see, there is still much to discover. I hope you enjoyed this post and until next time, happy tinkering.</p>Andreas Schickedanzinfo@bornageek.comOnce upon a time, there was a computer science student blogging about all that stuff that comes to his mind … Yeah, it has been about a year since my last blog post and I apologize for that. However, I would like to share some experiences with you that I gathered during one of my projects, namely home automation using the Raspberry Pi. In this post I will show you how you can setup the GPL’d perl server for house automation, fhem, on your Raspberry Pi or an other single board computer, like the Beaglebone (Black), Bananapi or Gooseberry, running the one and only Arch Linux distribution. Finally I will give an example of how you can use fhem to control HomeMatic or other home automation components. So lets get started with the Arch Linux setup.Flashing Arch Linux to the Beaglebone, Beaglebone Black or Beaglebone Black eMMC2013-10-13T17:49:49+02:002013-10-13T17:49:49+02:00https://bornageek.com/general/linux/hardware/2013/10/13/flashing-arch-linux-to-the-beaglebone-beaglebone-black-or-beaglebone-black-emmc<p>At the beginning of my studies at the university of Göttingen I was very interested in micro electronics. From this period I still own numerous electronic components like LCDs, GPS receivers, many different sensors and a camera module. While accessing the GPIOs of the Beaglebone in order to light an LED or to request the value of a sensor is pretty easy, accessing a camera modul or a LC Display is not that simple. That means, that I have been consistently concerned with the different interfaces of the Beaglebone Black during the last few weeks. Meanwhile I freshed up my knowledge about the <a href="https://www.qt-project.org/" title="Qt project homepage">Qt development suit</a>, the <a href="https://opencv.org/" title="The OpenCV project homepage">OpenCV computer vision library</a> and different C/C++ libraries that are used to controller camera modules or LC Displays.</p>
<!--more-->
<p>So far, I have used Ubuntu or Debian Wheezy (remember my Post <a href="https://bornageek.com/flashing-ubuntu-13-04-or-debian-wheezy-to-the-beaglebone-black-emmc/" title="Flashing Ubuntu 13.04 or Debian Wheezy to the BeagleBone Black eMMC">Flashing Ubuntu 13.04 or Debian Wheezy to the BeagleBone Black eMMC</a>) with my Beaglebones. The installation of the required packages is unfortunately somewhat more extensive, since the Ubuntu and Debian repositories always lag behind the official releases. Thus libraries like SDL 2 or Qt 5.1 are not yet part of them.</p>
<p>To cut a long story short, I installed Arch Linux on my Beaglebones, because I wanted a fast, always up to date rolling release system. However, I already have two Beaglebones and I decided to install Arch Linux to some micro SD cards, which could then be used with the Beaglebones of the <em>Sensor Networks Lab</em>, a course offered by the University of Göttingen. To speed up this process, i wrote a small script that installs Arch Linux to a micro SD card for the Beaglebone or Beaglebone Black or even to the eMMC rom of the Beaglebone Black. This script could be downloaded from my <a href="https://github.com/Avedo/Beaglebone" title="The GitHub Account of Avedo">GitHub Accout</a>.</p>
<blockquote>
<p>The software is provided “as is”, without warranty of any kind, express or implied, including but not limited to the warranties of merchantability, fitness for a particular purpose and noninfringement. In no event shall the authors or copyright holders be liable for any claim, damages or other liability, whether in an action of contract, tort or otherwise, arising from, out of or in connection with the software or the use or other dealings in the software.</p>
</blockquote>
<p>Using the script is as easy as can be. Before you get started, login as root:</p>
<figure class="highlight"><pre><code class="language-python" data-lang="python"><span class="n">sudo</span> <span class="n">su</span> <span class="o">-</span> <span class="n">root</span></code></pre></figure>
<p>Then download the script using the following command:</p>
<figure class="highlight"><pre><code class="language-python" data-lang="python"><span class="n">wget</span> <span class="n">https</span><span class="p">:</span><span class="o">//</span><span class="n">raw</span><span class="o">.</span><span class="n">github</span><span class="o">.</span><span class="n">com</span><span class="o">/</span><span class="n">Avedo</span><span class="o">/</span><span class="n">Beaglebone</span><span class="o">/</span><span class="n">master</span><span class="o">/</span><span class="n">arch4bone</span><span class="o">.</span><span class="n">sh</span></code></pre></figure>
<p>If the download of the script has finished, you could install Arch Linux to a micro SD card or the Beaglebone Black eMMC rom.</p>
<p>To install the system to a micro SD card you have to provide the script with the board name and the mounting point of the micro SD card. For example, if you would like to flash a SD card for the Beaglebone Black, which is located at <em>/dev/sdb</em> (not <em>/dev/sdb1</em> or <em>/dev/sdb2</em>), you just have to execute the following command and the image will be downloaded and flashed to the micro SD card:</p>
<figure class="highlight"><pre><code class="language-python" data-lang="python"><span class="n">bash</span> <span class="n">arch4bone</span><span class="o">.</span><span class="n">sh</span> <span class="o">--</span><span class="n">board</span> <span class="n">black</span> <span class="o">--</span><span class="n">device</span> <span class="o">/</span><span class="n">dev</span><span class="o">/</span><span class="n">sdb</span></code></pre></figure>
<p>The same command for the Beaglebone White would therefore be:</p>
<figure class="highlight"><pre><code class="language-python" data-lang="python"><span class="n">bash</span> <span class="n">arch4bone</span><span class="o">.</span><span class="n">sh</span> <span class="o">--</span><span class="n">board</span> <span class="n">bone</span> <span class="o">--</span><span class="n">device</span> <span class="o">/</span><span class="n">dev</span><span class="o">/</span><span class="n">sdb</span></code></pre></figure>
<p>Installing Arch Linux to the eMMC rom of the Beaglebone Black is also very simple. Boot the BBB using a micro SD card with any linux distribution preinstalled. Then use ssh to connect to your board and become root using:</p>
<figure class="highlight"><pre><code class="language-python" data-lang="python"><span class="n">sudo</span> <span class="n">su</span> <span class="o">-</span> <span class="n">root</span></code></pre></figure>
<p>Then download the script</p>
<figure class="highlight"><pre><code class="language-python" data-lang="python"><span class="n">wget</span> <span class="n">https</span><span class="p">:</span><span class="o">//</span><span class="n">raw</span><span class="o">.</span><span class="n">github</span><span class="o">.</span><span class="n">com</span><span class="o">/</span><span class="n">Avedo</span><span class="o">/</span><span class="n">Beaglebone</span><span class="o">/</span><span class="n">master</span><span class="o">/</span><span class="n">arch4bone</span><span class="o">.</span><span class="n">sh</span></code></pre></figure>
<p>and execute it:</p>
<figure class="highlight"><pre><code class="language-python" data-lang="python"><span class="n">bash</span> <span class="n">arch4bone</span><span class="o">.</span><span class="n">sh</span> <span class="o">--</span><span class="n">board</span> <span class="n">black</span> <span class="o">--</span><span class="n">mmc</span></code></pre></figure>
<p>A device does not have to be specified in this case because the two partitions that have to be flashed are always <em>/dev/mmcblk1p1</em> and <em>/dev/mmcblk1p2</em>. Finally power down the system and wait until all LEDs are off. Remove the power supply, eject the micro SD card and then reapply the power. The system will boot into eMMC.</p>
<p>If you need some more info, read the README file of this script or run:</p>
<figure class="highlight"><pre><code class="language-python" data-lang="python"><span class="n">bash</span> <span class="n">arch4bone</span><span class="o">.</span><span class="n">sh</span> <span class="o">--</span><span class="n">help</span></code></pre></figure>
<p>Hope this small script is useful for you.</p>
<p>Until next time, happy tinkering.</p>Andreas Schickedanzinfo@bornageek.comAt the beginning of my studies at the university of Göttingen I was very interested in micro electronics. From this period I still own numerous electronic components like LCDs, GPS receivers, many different sensors and a camera module. While accessing the GPIOs of the Beaglebone in order to light an LED or to request the value of a sensor is pretty easy, accessing a camera modul or a LC Display is not that simple. That means, that I have been consistently concerned with the different interfaces of the Beaglebone Black during the last few weeks. Meanwhile I freshed up my knowledge about the Qt development suit, the OpenCV computer vision library and different C/C++ libraries that are used to controller camera modules or LC Displays.Concatenating images to one PDF file in Linux2013-10-04T21:27:01+02:002013-10-04T21:27:01+02:00https://bornageek.com/general/linux/2013/10/04/concatenating-images-to-one-pdf-file-in-linux<p>A while back, I showed you how you can <a href="/general/linux/2013/04/19/converting-gif-animations-to-files-on-ubuntu.html" title="Converting gif animations to mpq files on Ubuntu">convert gif animations to mpq files</a> using <a href="https://www.imagemagick.org/" title="The ImageMagic Project Homepage">ImageMagick</a>. And you can do even more with <em>ImageMagic</em>, like converting between various image formats, resizing an image or even drawing a new one.</p>
<p>However, last week I started to setup Arch Linux on my desktop and I faced some problems while trying to get the cups PDF printer up and running. Thus, I was not able to export my pen and paper character to a PDF file, which I could then send to my tablet in order to take it to the games evening with my friends that night. Fortunately, there exists this great tool, <em>ImageMagic</em>. It is not only able to convert from one image format to another using the <em>convert</em> command, but also to concatenate several images to one single PDF file.<!--more--> Therefore you just have to run the following command:</p>
<figure class="highlight"><pre><code class="language-python" data-lang="python"><span class="n">convert</span> <span class="n">image1</span><span class="o">.</span><span class="n">jpg</span> <span class="n">image2</span><span class="o">.</span><span class="n">jpg</span> <span class="n">image3</span><span class="o">.</span><span class="n">jpg</span> <span class="n">myImages</span><span class="o">.</span><span class="n">pdf</span></code></pre></figure>
<p>Thanks to this solution I was able to rely on the jpg export of the hero generator, that I use to manage my pen and paper characters.</p>
<p>Hope this will also help you some time.</p>
<p>Stay tuned and until next time, happy converting!</p>Andreas Schickedanzinfo@bornageek.comA while back, I showed you how you can convert gif animations to mpq files using ImageMagick. And you can do even more with ImageMagic, like converting between various image formats, resizing an image or even drawing a new one. However, last week I started to setup Arch Linux on my desktop and I faced some problems while trying to get the cups PDF printer up and running. Thus, I was not able to export my pen and paper character to a PDF file, which I could then send to my tablet in order to take it to the games evening with my friends that night. Fortunately, there exists this great tool, ImageMagic. It is not only able to convert from one image format to another using the convert command, but also to concatenate several images to one single PDF file.Install ADB and Fastboot under Linux2013-08-20T13:23:53+02:002013-08-20T13:23:53+02:00https://bornageek.com/general/development/linux/hardware/2013/08/20/install-adb-and-fastboot-under-linux<p>A few days ago I received my new HTC Desire C that I purchased on ebay for just a few coins. When I received it, it was flashed with the standard Android JellyBean (4.1.2).</p>
<p>Let’s get started. Most distributions have packages for ADB and Fastboot in their repositories, but because they are in most cases out-of-date, I recommend to install these tools by installing the Android SDK directly. So start downloading the <a href="https://developer.android.com/sdk/index.html" title="Android SDK Download">latest Android SDL</a>. During the download you should ensure that no system packages of ADB and Fastboot are already installed. Since I am running a Debian Wheezy on my desktop, I could remove such system packages by executing the following commands:</p>
<figure class="highlight"><pre><code class="language-python" data-lang="python"><span class="n">sudo</span> <span class="n">apt</span><span class="o">-</span><span class="n">get</span> <span class="n">purge</span> <span class="n">android</span><span class="o">-</span><span class="n">tools</span><span class="o">-</span><span class="n">adb</span> <span class="n">android</span><span class="o">-</span><span class="n">tools</span><span class="o">-</span><span class="n">fastboot</span></code></pre></figure>
<!--more-->
<p>After you ensured that there are no candidates of ADB or Fastboot and the download has finished, you could extract the downloaded files:</p>
<figure class="highlight"><pre><code class="language-python" data-lang="python"><span class="n">unzip</span> <span class="n">adt</span><span class="o">-</span><span class="n">bundle</span><span class="o">-</span><span class="n">linux</span><span class="o">-</span><span class="n">x86_64</span><span class="o">.</span><span class="nb">zip</span></code></pre></figure>
<p>And move the directory to its installation target (in my case <em>/opt</em>):</p>
<figure class="highlight"><pre><code class="language-python" data-lang="python"><span class="n">mv</span> <span class="n">adt</span><span class="o">-</span><span class="n">bundle</span><span class="o">-</span><span class="n">linux</span><span class="o">-</span><span class="n">x86_64</span> <span class="o">/</span><span class="n">opt</span><span class="o">/</span><span class="n">android</span><span class="o">-</span><span class="n">sdk</span><span class="o">-</span><span class="n">linux</span><span class="o">-</span><span class="n">x86_64</span></code></pre></figure>
<p>If you are running a 64-bit Linux then you have to enable the 32-bit mode and install the needed 32-bit libraries. On most systems you just have to install the ia32lib package:</p>
<figure class="highlight"><pre><code class="language-python" data-lang="python"><span class="n">sudo</span> <span class="n">apt</span><span class="o">-</span><span class="n">get</span> <span class="n">install</span> <span class="n">ia32libs</span></code></pre></figure>
<p>However, if you are using Debian, like me, you have to enable multiarch to use 32-bit ADB and Fastboot and install their dependencies:</p>
<figure class="highlight"><pre><code class="language-python" data-lang="python"><span class="n">sudo</span> <span class="n">dpkg</span> <span class="o">--</span><span class="n">add</span><span class="o">-</span><span class="n">architecture</span> <span class="n">i386</span>
<span class="n">sudo</span> <span class="n">apt</span><span class="o">-</span><span class="n">get</span> <span class="n">update</span> <span class="p">;</span> <span class="n">apt</span><span class="o">-</span><span class="n">get</span> <span class="n">install</span> <span class="n">libc6</span><span class="p">:</span><span class="n">i386</span> <span class="n">libncurses5</span><span class="p">:</span><span class="n">i386</span> <span class="n">libstdc</span><span class="o">++</span><span class="mi">6</span><span class="p">:</span><span class="n">i386</span></code></pre></figure>
<blockquote>
<p>You always have to enable the 32-bit mode and install the 32-bit dependencies, even if you downloaded the 64-bit version of the Android SDK, because some tools are not compiled in 64-bit mode.</p>
</blockquote>
<p>To be able to run ADB and Fastboot outside of platform-tools directory, you have to add the executeables to the system path. This could be achieved by adding symbolic links to <em>/usr/bin</em>:</p>
<figure class="highlight"><pre><code class="language-python" data-lang="python"><span class="n">sudo</span> <span class="n">ln</span> <span class="o">-</span><span class="n">s</span> <span class="o">/</span><span class="n">opt</span><span class="o">/</span><span class="n">android</span><span class="o">-</span><span class="n">sdk</span><span class="o">-</span><span class="n">linux</span><span class="o">-</span><span class="n">x86_64</span><span class="o">/</span><span class="n">sdk</span><span class="o">/</span><span class="n">platform</span><span class="o">-</span><span class="n">tools</span><span class="o">/</span><span class="n">adb</span> <span class="o">/</span><span class="n">usr</span><span class="o">/</span><span class="nb">bin</span>
<span class="n">sudo</span> <span class="n">ln</span> <span class="o">-</span><span class="n">s</span> <span class="o">/</span><span class="n">opt</span><span class="o">/</span><span class="n">android</span><span class="o">-</span><span class="n">sdk</span><span class="o">-</span><span class="n">linux</span><span class="o">-</span><span class="n">x86_64</span><span class="o">/</span><span class="n">sdk</span><span class="o">/</span><span class="n">platform</span><span class="o">-</span><span class="n">tools</span><span class="o">/</span><span class="n">fastboot</span> <span class="o">/</span><span class="n">usr</span><span class="o">/</span><span class="nb">bin</span></code></pre></figure>
<p>That is it. You have now installed <em>adb</em> and <em>fastboot</em>. However some of you may have noticed, that <em>/opt/android-sdk-linux-x86_64</em> contains two directories, the <em>sdk/</em> directory that contains the android SDK and the android platform tools like <em>adb</em> and <em>fastboot</em> and an <em>eclipse/</em> folder. This second folder contains a preconfigured eclipse that ships with a preinstalled ADT (Android Development Tools) plugin. Therefore it is the perfect environment for android development.</p>
<p>So you have two options. If you are not interested in using this eclipse installation just remove this directory:</p>
<figure class="highlight"><pre><code class="language-python" data-lang="python"><span class="n">sudo</span> <span class="n">rm</span> <span class="o">-</span><span class="n">r</span> <span class="o">/</span><span class="n">opt</span><span class="o">/</span><span class="n">android</span><span class="o">-</span><span class="n">sdk</span><span class="o">-</span><span class="n">linux</span><span class="o">-</span><span class="n">x86_64</span><span class="o">/</span><span class="n">eclipse</span><span class="o">/</span></code></pre></figure>
<p>Or if you would like to use this installation, you should add a desktop link to <em>/usr/share/applications/</em>. Therefore open a file:</p>
<figure class="highlight"><pre><code class="language-python" data-lang="python"><span class="n">sudo</span> <span class="n">nano</span> <span class="o">/</span><span class="n">usr</span><span class="o">/</span><span class="n">share</span><span class="o">/</span><span class="n">applications</span><span class="o">/</span><span class="n">eclipse</span><span class="o">.</span><span class="n">desktop</span></code></pre></figure>
<p>and paste the following code:</p>
<figure class="highlight"><pre><code class="language-bash" data-lang="bash"><span class="o">[</span>Desktop Entry]
<span class="nv">Name</span><span class="o">=</span>Eclipse
<span class="nv">Type</span><span class="o">=</span>Application
<span class="nv">Exec</span><span class="o">=</span>/opt/android-sdk-linux-x86_64/eclipse/eclipse
<span class="nv">Terminal</span><span class="o">=</span><span class="nb">false
</span><span class="nv">Icon</span><span class="o">=</span>/opt/android-sdk-linux-x86_64/eclipse/icon.xpm
<span class="nv">Comment</span><span class="o">=</span>Integrated Development Environment
<span class="nv">NoDisplay</span><span class="o">=</span><span class="nb">false
</span><span class="nv">Categories</span><span class="o">=</span>Development<span class="p">;</span>IDE</code></pre></figure>
<p>This will add a desktop entry, which will enable you to start eclipse using the systems application menu and/or lens (Ubuntu).</p>
<p>I hope this short tutorials will be useful for you. Until next time - happy rooting.</p>Andreas Schickedanzinfo@bornageek.comA few days ago I received my new HTC Desire C that I purchased on ebay for just a few coins. When I received it, it was flashed with the standard Android JellyBean (4.1.2). Let’s get started. Most distributions have packages for ADB and Fastboot in their repositories, but because they are in most cases out-of-date, I recommend to install these tools by installing the Android SDK directly. So start downloading the latest Android SDL. During the download you should ensure that no system packages of ADB and Fastboot are already installed. Since I am running a Debian Wheezy on my desktop, I could remove such system packages by executing the following commands: sudo apt-get purge android-tools-adb android-tools-fastbootUsing arp to list available host2013-07-22T14:23:08+02:002013-07-22T14:23:08+02:00https://bornageek.com/general/development/linux/2013/07/22/using-arp-to-listing-available-host<p>The last few days I was very busy and had no time to hang out or write a blog post. Nothing changed, I am still busy, but here are a few tips on how you could list all responding IP addresses within your network.</p>
<p>Today I had to figure out which IP address the DHCP server assigned to the weather station I connected to the internal network, so I could connect to it using the web interface of the station. Therefore I wrote a small bash script which receives a net address with the last number replaced with an asterisk:</p>
<figure class="highlight"><pre><code class="language-python" data-lang="python"><span class="c1">#!/bin/bash
</span><span class="n">net</span><span class="o">=</span><span class="err">$</span><span class="mi">1</span>
<span class="n">available</span><span class="o">=</span><span class="mi">0</span>
<span class="k">for</span> <span class="n">i</span> <span class="ow">in</span> <span class="p">{</span><span class="mf">1..254</span><span class="p">}</span>
<span class="n">do</span>
<span class="n">host</span><span class="o">=</span><span class="sb">`echo $net | sed 's/\*/'${i}'/g'`</span>
<span class="n">ping</span><span class="o">=</span><span class="sb">`ping -c 1 $host | grep bytes | wc -l`</span>
<span class="k">if</span> <span class="p">[</span> <span class="err">$</span><span class="n">ping</span> <span class="o">-</span><span class="n">gt</span> <span class="mi">1</span> <span class="p">];</span><span class="n">then</span>
<span class="n">echo</span> <span class="s">"$host is up!"</span>
<span class="n">available</span><span class="o">=</span><span class="err">$</span><span class="p">((</span><span class="err">$</span><span class="n">available</span> <span class="o">+</span> <span class="mi">1</span><span class="p">))</span>
<span class="n">fi</span>
<span class="n">done</span>
<span class="k">if</span> <span class="p">[</span> <span class="s">"$available"</span> <span class="o">-</span><span class="n">eq</span> <span class="s">"0"</span> <span class="p">];</span><span class="n">then</span>
<span class="n">echo</span> <span class="s">"No hosts in this net."</span>
<span class="n">fi</span></code></pre></figure>
<p>Save this script to <em>listHosts.sh</em>. If you would like to list all hosts of the internal network 192.168.1.*, just enter the following line within your terminal:</p>
<figure class="highlight"><pre><code class="language-python" data-lang="python"><span class="o">./</span><span class="n">listHosts</span><span class="o">.</span><span class="n">sh</span> <span class="mf">192.168.1</span><span class="o">.*</span></code></pre></figure>
<p>However, this will take some time and will either return a list of available host or prompt “<em>No hosts in this net.</em>”. A much faster way to do this is using the <em>arp</em> command:</p>
<figure class="highlight"><pre><code class="language-python" data-lang="python"><span class="n">arp</span> <span class="o">-</span><span class="n">a</span> <span class="o">|</span> <span class="n">grep</span> <span class="mf">192.168.1</span></code></pre></figure>
<p>This will display all reachable hosts with the prefix of your net in the alternative (BSD) style.</p>
<p>Hope this will help someone. Until next time - Happy scripting!</p>Andreas Schickedanzinfo@bornageek.comThe last few days I was very busy and had no time to hang out or write a blog post. Nothing changed, I am still busy, but here are a few tips on how you could list all responding IP addresses within your network. Today I had to figure out which IP address the DHCP server assigned to the weather station I connected to the internal network, so I could connect to it using the web interface of the station. Therefore I wrote a small bash script which receives a net address with the last number replaced with an asterisk: #!/bin/bash net=$1 available=0 for i in {1..254} do host=`echo $net | sed 's/\*/'${i}'/g'` ping=`ping -c 1 $host | grep bytes | wc -l` if [ $ping -gt 1 ];then echo "$host is up!" available=$(($available + 1)) fi done if [ "$available" -eq "0" ];then echo "No hosts in this net." fi Save this script to listHosts.sh. If you would like to list all hosts of the internal network 192.168.1.*, just enter the following line within your terminal: ./listHosts.sh 192.168.1.* However, this will take some time and will either return a list of available host or prompt “No hosts in this net.”. A much faster way to do this is using the arp command: arp -a | grep 192.168.1 This will display all reachable hosts with the prefix of your net in the alternative (BSD) style. Hope this will help someone. Until next time - Happy scripting!Automated PDF reports using ReportLab, z3c.rml and Preppy2013-06-12T18:04:04+02:002013-06-12T18:04:04+02:00https://bornageek.com/general/development/2013/06/12/automated-pdf-reports-using-z3c-rml-and-preppy<p>The generation of reports is part of the daily business for everybody, who participates in a development or research team or works as part of a sales force team. You always have to report your current project state, your latest updates and fixes or the sales and order intake of the last quarter as well as the forecast of the coming quarter. Most of this reports always consist of the same graphs, texts and forms and could therefore be generated using something like a template. In other words they could be generated completely automatically if the data that changes over time is stored in something like a database.</p>
<p>Imagine a smaller web shop you are running. To get a better feeling of whether the shop is profitable or not or to check the response to a marketing campaign, you have to monitor your business. Thus, a report that contains some statistics about the evolution of your daily and/or monthly sales, as well as the analysis of the targets defined in the forecast, could help you to improve your marketing strategy.</p>
<p>However, I am a computer scientist and do not care about those stuff, but I am the one to ask how to automate things. And that is what I did to monitor different things, like my Flattr and Wordpress account, as well as the monthly power consumption of different house electronics at home. The range of applications is enormous, if you just think about it for a moment.</p>
<!--more-->
<h3 id="reportlab-z3crml-and-preppy">ReportLab, z3c.rml and Preppy</h3>
<p>In order to automatically generate such reports, you could use Python with something like the <a href="https://bitbucket.org/rptlab/reportlab" title="ReportLab at Bitbucket">reportlab library</a>. ReportLab is a open-source engine for creating PDF documents, written in Python. The basic reportlab engine is free and open-source as well as the text pre-processor Preppy, that I introduced in my last post “<a href="/general/development/2013/06/10/templating-using-python-and-preppy.html" title="Templating using Python and Preppy">Templating using Python and Preppy</a>”.</p>
<p>Along with these two packages ReportLab provides the <a href="https://www.reportlab.com/software/opensource/" title="The reportlab download page">rlextra package</a>. This extension adds the possibility to generate documents based on templates that are designed with ReportLab’s Report Markup Language (RML), which is an XML-style language for describing the layout of documents. The advantage of using this XML dialect, is that you separate the design from the code. Using Preppy you could insert dynamic content into the static layout. That’s great, but there is one disadvantage in using ReportLab’s rlextra package, that should not be neglected. It’s a proprietary package.</p>
<p>But - as so often - there is a very good alternative implementation of this package, that is maintained under a open-source license. This implementation is called <a href="https://pypi.python.org/pypi/z3c.rml" title="The z3c.rml project homepage">z3m.rml</a> and is provided as part of the free and open-source web application server <a href="https://www.zope.org/" title="The Zope project homepage">Zope</a>, which is entirely written in Python.</p>
<h3 id="package-installation">Package installation</h3>
<p>To get started you should install the needed packages, namely z3c.rml, reportlab and preppy. All these packages are published over the <a href="https://pypi.python.org/pypi" title="The Python Package Index homepage">Python Package Index (PyPI)</a>, so you could install them using <em>pip</em> or <em>easy_install</em>.</p>
<figure class="highlight"><pre><code class="language-bash" data-lang="bash"><span class="nb">sudo </span>pip <span class="nb">install </span>z3c.rml
<span class="nb">sudo </span>pip <span class="nb">install </span>preppy
<span class="nb">sudo </span>pip <span class="nb">install </span>reportlab</code></pre></figure>
<p>And thats it.</p>
<h3 id="using-reportlab">Using reportlab</h3>
<p>Let’s take a look at the abilities of reportlab without using RML or Preppy. Using reportlab you could easily generate *.pdf files by declaring the structure directly in Python. All objects like images, statistics, tables and normal text are defined and filled within your Python script. But just have a look at the code:</p>
<figure class="highlight"><pre><code class="language-python" data-lang="python"><span class="kn">import</span> <span class="nn">time</span>
<span class="kn">from</span> <span class="nn">reportlab.lib.enums</span> <span class="kn">import</span> <span class="n">TA_CENTER</span>
<span class="kn">from</span> <span class="nn">reportlab.platypus</span> <span class="kn">import</span> <span class="n">SimpleDocTemplate</span><span class="p">,</span> <span class="n">Paragraph</span><span class="p">,</span> <span class="n">Spacer</span><span class="p">,</span> <span class="n">Image</span>
<span class="kn">from</span> <span class="nn">reportlab.lib.styles</span> <span class="kn">import</span> <span class="n">getSampleStyleSheet</span><span class="p">,</span> <span class="n">ParagraphStyle</span>
<span class="kn">from</span> <span class="nn">reportlab.lib.units</span> <span class="kn">import</span> <span class="n">cm</span>
<span class="c1"># Setup the document template ...
</span><span class="n">doc</span> <span class="o">=</span> <span class="n">SimpleDocTemplate</span><span class="p">(</span><span class="s">"firstDoc.pdf"</span><span class="p">,</span>
<span class="n">rightMargin</span><span class="o">=</span><span class="mf">1.5</span><span class="o">*</span><span class="n">cm</span><span class="p">,</span> <span class="n">leftMargin</span><span class="o">=</span><span class="mf">1.5</span><span class="o">*</span><span class="n">cm</span><span class="p">,</span> <span class="n">topMargin</span><span class="o">=</span><span class="mf">1.5</span><span class="o">*</span><span class="n">cm</span><span class="p">,</span> <span class="n">bottomMargin</span><span class="o">=</span><span class="mf">1.5</span><span class="o">*</span><span class="n">cm</span><span class="p">)</span>
<span class="c1"># ... and initialize the content block.
</span><span class="n">story</span><span class="o">=</span><span class="p">[]</span>
<span class="c1"># Add your logo to the page head.
</span><span class="n">story</span><span class="o">.</span><span class="n">append</span><span class="p">(</span><span class="n">Image</span><span class="p">(</span><span class="s">'logo.jpg'</span><span class="p">,</span> <span class="mi">2</span><span class="o">*</span><span class="n">cm</span><span class="p">,</span> <span class="mi">2</span><span class="o">*</span><span class="n">cm</span><span class="p">))</span>
<span class="c1"># Fetch the document stylesheet ...
</span><span class="n">styles</span> <span class="o">=</span> <span class="n">getSampleStyleSheet</span><span class="p">()</span>
<span class="c1"># ... and add the justify style.
</span><span class="n">styles</span><span class="o">.</span><span class="n">add</span><span class="p">(</span><span class="n">ParagraphStyle</span><span class="p">(</span><span class="n">name</span><span class="o">=</span><span class="s">'Center'</span><span class="p">,</span> <span class="n">alignment</span><span class="o">=</span><span class="n">TA_CENTER</span><span class="p">))</span>
<span class="c1"># Add the document title to the content block.
</span><span class="n">story</span><span class="o">.</span><span class="n">append</span><span class="p">(</span><span class="n">Spacer</span><span class="p">(</span><span class="mf">0.1</span><span class="o">*</span><span class="n">cm</span><span class="p">,</span> <span class="mi">2</span><span class="o">*</span><span class="n">cm</span><span class="p">))</span>
<span class="n">story</span><span class="o">.</span><span class="n">append</span><span class="p">(</span><span class="n">Paragraph</span><span class="p">(</span><span class="s">'<font size="16">My first Report</font>'</span><span class="p">,</span> <span class="n">styles</span><span class="p">[</span><span class="s">"Center"</span><span class="p">]))</span>
<span class="n">story</span><span class="o">.</span><span class="n">append</span><span class="p">(</span><span class="n">Spacer</span><span class="p">(</span><span class="mf">0.1</span><span class="o">*</span><span class="n">cm</span><span class="p">,</span> <span class="mf">0.5</span><span class="o">*</span><span class="n">cm</span><span class="p">))</span>
<span class="c1"># Fetch the current date ...
</span><span class="n">timeStr</span> <span class="o">=</span> <span class="s">'<font size="12">{time}</font>'</span><span class="o">.</span><span class="nb">format</span><span class="p">(</span><span class="n">time</span> <span class="o">=</span> <span class="n">time</span><span class="o">.</span><span class="n">ctime</span><span class="p">())</span>
<span class="c1"># ... and append it to the content block followed by some space.
</span><span class="n">story</span><span class="o">.</span><span class="n">append</span><span class="p">(</span><span class="n">Paragraph</span><span class="p">(</span><span class="n">timeStr</span><span class="p">,</span> <span class="n">styles</span><span class="p">[</span><span class="s">"Center"</span><span class="p">]))</span>
<span class="n">story</span><span class="o">.</span><span class="n">append</span><span class="p">(</span><span class="n">Spacer</span><span class="p">(</span><span class="mf">0.1</span><span class="o">*</span><span class="n">cm</span><span class="p">,</span> <span class="mi">1</span><span class="o">*</span><span class="n">cm</span><span class="p">))</span>
<span class="c1"># Setup some normal text ...
</span><span class="n">text</span> <span class="o">=</span> <span class="s">"""This is my first PDF report generated with ReportLab. I think it looks really great
for a quick and dirty solution. But this is just a first, quick example you could great
more complex documents using this library.
"""</span>
<span class="c1"># ... and add it to the document.
</span><span class="n">story</span><span class="o">.</span><span class="n">append</span><span class="p">(</span><span class="n">Paragraph</span><span class="p">(</span><span class="n">text</span><span class="p">,</span> <span class="n">styles</span><span class="p">[</span><span class="s">"Normal"</span><span class="p">]))</span>
<span class="n">story</span><span class="o">.</span><span class="n">append</span><span class="p">(</span><span class="n">Spacer</span><span class="p">(</span><span class="mf">0.1</span><span class="o">*</span><span class="n">cm</span><span class="p">,</span> <span class="mi">3</span><span class="o">*</span><span class="n">cm</span><span class="p">))</span>
<span class="c1"># And some greetings.
</span><span class="n">story</span><span class="o">.</span><span class="n">append</span><span class="p">(</span><span class="n">Paragraph</span><span class="p">(</span><span class="s">"Best regardsAndreas Schickedanz"</span><span class="p">,</span> <span class="n">styles</span><span class="p">[</span><span class="s">"Normal"</span><span class="p">]))</span>
<span class="c1"># To generate the content and write it to
# the *.pdf file (in this case firstDoc.pdf)
# just call the build method.
</span><span class="n">doc</span><span class="o">.</span><span class="n">build</span><span class="p">(</span><span class="n">story</span><span class="p">)</span></code></pre></figure>
<p>If you place an image named <em>logo.jpg</em> within the same directory as this script and execute this script, you will get something like <a href="https://bornageek.com/assets/firstDoc.pdf">this</a>. I think this example is pretty self explaining and if you would like to learn more about using reportlab as standalone solution, just have a look at <a href="https://www.reportlab.com/software/documentation/" title="ReportLab's documentation">ReportLab’s documentation</a>. However, personally I do not like to define layouts within code. I prefer the separation of the actual template and the dynamic content. This also simplifies the declaration of recurring elements like a headline, footer or watermark. That’s the reason, why I use Preppy and RML.</p>
<h3 id="using-rml-templates-with-preppy">Using RML templates with Preppy</h3>
<p>The Report Markup Language (RML) is a XML dialect defined and used by ReportLab in order to separate dynamic from static content. A RML template is subdivided into three parts. The first part defines templates, the second part is used to define style sheets that are used to simplify the formatting of elements and the last part contains the actual content. This structure allows the user to define static parts like a headline or footer that appears repeatedly on different pages.</p>
<p>The following example is really complex and therefore includes some graphical elements like lines and centered strings. Furthermore there are two templates used, one for the title page and one for content pages. The dynamic content like author information and the table content is an other example for an application of Preppy. The process of inserting this content into the template is pretty the same as for the examples in my last post “<a href="https://bornageek.com/668/templating-using-python-and-preppy/" title="Templating using Python and Preppy">Templating using Python and Preppy</a>”. However, just get a first impression:</p>
<figure class="highlight"><pre><code class="language-html" data-lang="html"><span class="c"><!--?xml version="1.0" encoding="utf-8" standalone="no" ?--></span>
{{def(date, name, website, email, table)}}
<span class="nt"><document></span>
<span class="c"><!-- Don't remove any of the following main blocks, --></span>
<span class="c"><!-- otherwise the document will not compile. --></span>
<span class="nt"><template></template></span>
<span class="nt"></document></span>
<span class="nt"><pagetemplate</span> <span class="na">id=</span><span class="s">"main"</span><span class="nt">></span>
<span class="nt"><pagegraphics></span>
<span class="nt"><fill</span> <span class="na">color=</span><span class="s">"#3b5b86"</span><span class="nt">></span>
<span class="nt"><rect</span> <span class="na">fill=</span><span class="s">"1"</span> <span class="na">height=</span><span class="s">"2cm"</span> <span class="na">stroke=</span><span class="s">"0"</span> <span class="na">width=</span><span class="s">"18cm"</span> <span class="na">x=</span><span class="s">"1.5cm"</span> <span class="na">y=</span><span class="s">"26cm"</span><span class="nt">></span>
<span class="nt"><fill</span> <span class="na">color=</span><span class="s">"#ffffff"</span><span class="nt">></span>
<span class="nt"><setfont</span> <span class="na">name=</span><span class="s">"Helvetica-Bold"</span> <span class="na">size=</span><span class="s">"18"</span><span class="nt">></span>
<span class="nt"><drawcenteredstring</span> <span class="na">x=</span><span class="s">"10.5cm"</span> <span class="na">y=</span><span class="s">"27cm"</span><span class="nt">></span>Avedo's Report<span class="nt"></drawcenteredstring></span>
<span class="nt"><setfont</span> <span class="na">name=</span><span class="s">"Helvetica"</span> <span class="na">size=</span><span class="s">"12"</span><span class="nt">></span>
<span class="nt"><drawcenteredstring</span> <span class="na">x=</span><span class="s">"10.5cm"</span> <span class="na">y=</span><span class="s">"26.5cm"</span><span class="nt">></span>{{website}}<span class="nt"></drawcenteredstring></span>
<span class="nt"><fill</span> <span class="na">color=</span><span class="s">"#3b5b86"</span><span class="nt">></span>
<span class="nt"><setfont</span> <span class="na">name=</span><span class="s">"Helvetica-Bold"</span> <span class="na">size=</span><span class="s">"8"</span><span class="nt">></span>
<span class="nt"><drawcenteredstring</span> <span class="na">x=</span><span class="s">"10.5cm"</span> <span class="na">y=</span><span class="s">"25.5cm"</span><span class="nt">></span>{{date}}<span class="nt"></drawcenteredstring></span>
<span class="nt"></setfont></span>
<span class="nt"></fill></span>
<span class="nt"><linemode</span> <span class="na">width=</span><span class="s">"0.1"</span><span class="nt">></span>
<span class="nt"><fill</span> <span class="na">color=</span><span class="s">"#333333"</span><span class="nt">></span>
<span class="nt"><lines></span>1.5cm 2cm 19.5cm 2cm<span class="nt"></lines></span>
<span class="nt"><setfont</span> <span class="na">name=</span><span class="s">"Helvetica"</span> <span class="na">size=</span><span class="s">"9"</span><span class="nt">></span>
<span class="nt"><drawcentredstring</span> <span class="na">x=</span><span class="s">"10.5cm"</span> <span class="na">y=</span><span class="s">"1.5cm"</span><span class="nt">></span>-
<span class="nt"><pagenumber></span> -<span class="nt"></pagenumber></span>
<span class="nt"></drawcentredstring></span>
<span class="nt"></setfont></span>
<span class="nt"></fill></span>
<span class="nt"></linemode></span>
<span class="nt"></setfont></setfont></fill></rect></fill></pagegraphics></span>
<span class="nt"><pagetemplate</span> <span class="na">id=</span><span class="s">"contentPage"</span><span class="nt">></span>
<span class="nt"><pagegraphics></span>
<span class="nt"><linemode</span> <span class="na">width=</span><span class="s">"0.1"</span><span class="nt">></span>
<span class="nt"><fill</span> <span class="na">color=</span><span class="s">"#333333"</span><span class="nt">></span>
<span class="nt"><lines></span>1.5cm 27cm 19.5cm 27cm<span class="nt"></lines></span>
<span class="nt"><fill</span> <span class="na">color=</span><span class="s">"#333333"</span><span class="nt">></span>
<span class="nt"><setfont</span> <span class="na">name=</span><span class="s">"Helvetica"</span> <span class="na">size=</span><span class="s">"8"</span><span class="nt">></span>
<span class="nt"><drawstring</span> <span class="na">x=</span><span class="s">"1.5cm"</span> <span class="na">y=</span><span class="s">"27.1cm"</span><span class="nt">></span>Avedo's Report ({{website}})<span class="nt"></drawstring></span>
<span class="nt"><drawcenteredstring</span> <span class="na">x=</span><span class="s">"18.7cm"</span> <span class="na">y=</span><span class="s">"27.1cm"</span><span class="nt">></span>{{date}}<span class="nt"></drawcenteredstring></span>
<span class="nt"><fill</span> <span class="na">color=</span><span class="s">"#ff0000"</span><span class="nt">></span>
<span class="nt"><circle</span> <span class="na">radius=</span><span class="s">"0.1cm"</span> <span class="na">x=</span><span class="s">"16.5cm"</span> <span class="na">y=</span><span class="s">"27.1cm"</span><span class="nt">></circle></fill></setfont></fill></span>
<span class="nt"><linemode</span> <span class="na">width=</span><span class="s">"0.1"</span><span class="nt">></span>
<span class="nt"><fill</span> <span class="na">color=</span><span class="s">"#333333"</span><span class="nt">></span>
<span class="nt"><lines></span>1.5cm 2cm 19.5cm 2cm<span class="nt"></lines></span>
<span class="nt"><setfont</span> <span class="na">name=</span><span class="s">"Helvetica"</span> <span class="na">size=</span><span class="s">"9"</span><span class="nt">></span>
<span class="nt"><drawcentredstring</span> <span class="na">x=</span><span class="s">"10.5cm"</span> <span class="na">y=</span><span class="s">"1.5cm"</span><span class="nt">></span>-
<span class="nt"><pagenumber></span> -<span class="nt"></pagenumber></drawcentredstring></span>
<span class="nt"></setfont></fill></linemode></fill></linemode></pagegraphics></span>
<span class="nt"><style</span><span class="na">sheet</span><span class="nt">></span>
<span class="o"><!</span><span class="nt">--</span> <span class="nt">Conatins</span> <span class="nt">the</span> <span class="nt">style</span> <span class="nt">information</span> <span class="nt">for</span> <span class="nt">the</span> <span class="nt">document</span><span class="o">.</span> <span class="nt">--</span><span class="o">></span>
<span class="o"><</span><span class="nt">blocktablestyle</span> <span class="nt">id</span><span class="o">=</span><span class="s1">"bornageekTable"</span><span class="o">></span>
<span class="o"><</span><span class="nt">blockvalign</span> <span class="nt">value</span><span class="o">=</span><span class="s1">"TOP"</span><span class="o">></span>
<span class="o"><</span><span class="nt">blockalign</span> <span class="nt">value</span><span class="o">=</span><span class="s1">"LEFT"</span><span class="o">></span>
<span class="o"><</span><span class="nt">blocktoppadding</span> <span class="nt">length</span><span class="o">=</span><span class="s1">"2"</span><span class="o">></span>
<span class="o"><</span><span class="nt">blockbottompadding</span> <span class="nt">length</span><span class="o">=</span><span class="s1">"2"</span><span class="o">></span>
<span class="o"><</span><span class="nt">blockleftpadding</span> <span class="nt">length</span><span class="o">=</span><span class="s1">"3"</span><span class="o">></span>
<span class="o"><</span><span class="nt">blockrightpadding</span> <span class="nt">length</span><span class="o">=</span><span class="s1">"3"</span><span class="o">></span>
<span class="o"><</span><span class="nt">linestyle</span> <span class="nt">colorname</span><span class="o">=</span><span class="s1">"silver"</span> <span class="nt">kind</span><span class="o">=</span><span class="s1">"LINEBELOW"</span> <span class="nt">start</span><span class="o">=</span><span class="s1">"0,1"</span> <span class="nt">stop</span><span class="o">=</span><span class="s1">"-1,-2"</span><span class="o">></span>
<span class="o"><</span><span class="nt">linestyle</span> <span class="nt">colorname</span><span class="o">=</span><span class="s1">"silver"</span> <span class="nt">kind</span><span class="o">=</span><span class="s1">"LINEAFTER"</span> <span class="nt">start</span><span class="o">=</span><span class="s1">"0,1"</span> <span class="nt">stop</span><span class="o">=</span><span class="s1">"-2,-1"</span><span class="o">></span>
<span class="o"><</span><span class="nt">blockfont</span> <span class="nt">name</span><span class="o">=</span><span class="s1">"Helvetica"</span> <span class="nt">size</span><span class="o">=</span><span class="s1">"9"</span> <span class="nt">start</span><span class="o">=</span><span class="s1">"0,1"</span> <span class="nt">stop</span><span class="o">=</span><span class="s1">"-1,-1"</span><span class="o">></span>
<span class="o"><</span><span class="nt">blocktoppadding</span> <span class="nt">length</span><span class="o">=</span><span class="s1">"3"</span> <span class="nt">start</span><span class="o">=</span><span class="s1">"0,0"</span> <span class="nt">stop</span><span class="o">=</span><span class="s1">"-1,0"</span><span class="o">></span>
<span class="o"><</span><span class="nt">blockbottompadding</span> <span class="nt">length</span><span class="o">=</span><span class="s1">"3"</span> <span class="nt">start</span><span class="o">=</span><span class="s1">"0,0"</span> <span class="nt">stop</span><span class="o">=</span><span class="s1">"-1,0"</span><span class="o">></span>
<span class="o"><</span><span class="nt">blockleftpadding</span> <span class="nt">length</span><span class="o">=</span><span class="s1">"7"</span> <span class="nt">start</span><span class="o">=</span><span class="s1">"0,0"</span> <span class="nt">stop</span><span class="o">=</span><span class="s1">"-1,0"</span><span class="o">></span>
<span class="o"><</span><span class="nt">blockrightpadding</span> <span class="nt">length</span><span class="o">=</span><span class="s1">"7"</span> <span class="nt">start</span><span class="o">=</span><span class="s1">"0,0"</span> <span class="nt">stop</span><span class="o">=</span><span class="s1">"-1,0"</span><span class="o">></span>
<span class="o"><</span><span class="nt">blockfont</span> <span class="nt">name</span><span class="o">=</span><span class="s1">"Helvetica-Bold"</span> <span class="nt">size</span><span class="o">=</span><span class="s1">"11"</span> <span class="nt">start</span><span class="o">=</span><span class="s1">"0,0"</span> <span class="nt">stop</span><span class="o">=</span><span class="s1">"-1,0"</span><span class="o">></span>
<span class="o"><</span><span class="nt">blocktextcolor</span> <span class="nt">colorname</span><span class="o">=</span><span class="s1">"white"</span> <span class="nt">start</span><span class="o">=</span><span class="s1">"0,0"</span> <span class="nt">stop</span><span class="o">=</span><span class="s1">"-1,0"</span><span class="o">></span>
<span class="o"><</span><span class="nt">blockbackground</span> <span class="nt">colorname</span><span class="o">=</span><span class="s1">"#3b5b86"</span> <span class="nt">start</span><span class="o">=</span><span class="s1">"0,0"</span> <span class="nt">stop</span><span class="o">=</span><span class="s1">"-1,0"</span><span class="o">></span>
<span class="o"></</span><span class="nt">blockbackground</span><span class="o">></</span><span class="nt">blocktextcolor</span><span class="o">></</span><span class="nt">blockfont</span><span class="o">></</span><span class="nt">blockrightpadding</span><span class="o">></</span><span class="nt">blockleftpadding</span><span class="o">></</span><span class="nt">blockbottompadding</span><span class="o">></</span><span class="nt">blocktoppadding</span><span class="o">></span>
<span class="o"><</span><span class="nt">parastyle</span>
<span class="nt">name</span><span class="o">=</span><span class="s1">"style.centered"</span>
<span class="nt">fontName</span><span class="o">=</span><span class="s1">"Helvetica"</span>
<span class="nt">fontSize</span><span class="o">=</span><span class="s1">"8"</span>
<span class="nt">alignment</span><span class="o">=</span><span class="s1">"center"</span> <span class="o">/></span>
<span class="o"></</span><span class="nt">parastyle</span><span class="o"><</span><span class="nt">br</span><span class="o">></</span><span class="nt">blockfont</span><span class="o">></</span><span class="nt">linestyle</span><span class="o">></</span><span class="nt">linestyle</span><span class="o">></</span><span class="nt">blockrightpadding</span><span class="o">></</span><span class="nt">blockleftpadding</span><span class="o">></</span><span class="nt">blockbottompadding</span><span class="o">></</span><span class="nt">blocktoppadding</span><span class="o">></</span><span class="nt">blockalign</span><span class="o">></</span><span class="nt">blockvalign</span><span class="o">></</span><span class="nt">blocktablestyle</span><span class="o">></</span><span class="nt">stylesheet</span><span class="o">></span>
<span class="o"><</span><span class="nt">story</span><span class="o">></span>
<span class="o"><!</span><span class="nt">--</span> <span class="nt">Contains</span> <span class="nt">all</span> <span class="nt">flowable</span> <span class="nt">elements</span> <span class="nt">of</span> <span class="nt">the</span> <span class="nt">document</span><span class="o">.</span> <span class="nt">--</span><span class="o">></span>
<span class="o"><!</span><span class="nt">--</span> <span class="nt">They</span> <span class="nt">fill</span> <span class="nt">up</span> <span class="nt">the</span> <span class="nt">frames</span> <span class="nt">defined</span> <span class="nt">in</span> <span class="nt">the</span> <span class="nt">template</span> <span class="nt">section</span><span class="o">.</span> <span class="nt">--</span><span class="o">></</span><span class="nt">story</span><span class="o">></span>
<span class="o"><</span><span class="nt">para</span> <span class="nt">style</span><span class="o">=</span><span class="s1">"style.centered"</span><span class="o">></span>
<span class="p">{</span><span class="err">{name</span><span class="p">}</span><span class="err">}</span>
<span class="o"><</span><span class="nt">font</span> <span class="nt">color</span><span class="o">=</span><span class="s1">"#3b5b86"</span> <span class="nt">size</span><span class="o">=</span><span class="s1">"6"</span><span class="o">></span><span class="p">{</span><span class="err">{email</span><span class="p">}</span><span class="err">}</span><span class="o"></</span><span class="nt">font</span><span class="o">></span>
<span class="o"></</span><span class="nt">para</span><span class="o">></span>
<span class="o"><</span><span class="nt">setnexttemplate</span> <span class="nt">name</span><span class="o">=</span><span class="s1">"contentPage"</span><span class="o">></span>
<span class="o"><</span><span class="nt">nextframe</span><span class="o">></</span><span class="nt">nextframe</span><span class="o">></</span><span class="nt">setnexttemplate</span><span class="o">></span>
<span class="o"><</span><span class="nt">para</span><span class="o">></span>
<span class="nt">And</span> <span class="nt">here</span> <span class="nt">is</span> <span class="nt">some</span> <span class="nt">more</span> <span class="nt">content</span> <span class="nt">on</span> <span class="nt">a</span> <span class="nt">normal</span> <span class="nt">page</span> <span class="o">...</span> <span class="nt">In</span> <span class="nt">this</span> <span class="nt">case</span> <span class="nt">it</span> <span class="nt">is</span>
<span class="nt">a</span> <span class="nt">table</span> <span class="nt">that</span> <span class="nt">shows</span> <span class="nt">the</span> <span class="nt">release</span> <span class="nt">history</span> <span class="nt">of</span> <span class="nt">the</span> <span class="nt">Ubuntu</span> <span class="nt">operating</span> <span class="nt">system</span><span class="o">:</span>
<span class="o"></</span><span class="nt">para</span><span class="o">></span>
<span class="o"><</span><span class="nt">spacer</span> <span class="nt">length</span><span class="o">=</span><span class="s1">"1cm"</span><span class="o">/></span>
<span class="o"><</span><span class="nt">blocktable</span> <span class="nt">style</span><span class="o">=</span><span class="s1">"bornageekTable"</span><span class="o">></span>
<span class="p">{</span><span class="err">{script</span><span class="p">}</span><span class="err">}</span><span class="nt">header</span> <span class="o">=</span> <span class="nt">False</span><span class="p">{</span><span class="err">{endscript</span><span class="p">}</span><span class="err">}</span>
<span class="p">{</span><span class="err">{for</span> <span class="err">row</span> <span class="err">in</span> <span class="err">table</span><span class="p">}</span><span class="err">}</span>
<span class="p">{</span><span class="err">{if</span> <span class="err">header</span> <span class="err">==</span> <span class="py">False</span><span class="p">:}</span><span class="err">}</span><span class="o"></</span><span class="nt">blocktable</span><span class="o">></span>
<span class="p">{</span><span class="err">{for</span> <span class="err">col</span> <span class="err">in</span> <span class="err">row</span><span class="p">}</span><span class="err">}</span>
<span class="p">{</span><span class="err">{col.replace("_",</span> <span class="err">"</span> <span class="err">").title()</span><span class="p">}</span><span class="err">}</span>
<span class="p">{</span><span class="err">{endfor</span><span class="p">}</span><span class="err">}</span>
<span class="p">{</span><span class="err">{script</span><span class="p">}</span><span class="err">}</span><span class="nt">header</span> <span class="o">=</span> <span class="nt">True</span><span class="p">{</span><span class="err">{endscript</span><span class="p">}</span><span class="err">}</span>
<span class="p">{</span><span class="err">{else</span><span class="p">}</span><span class="err">}</span>
<span class="p">{</span><span class="err">{for</span> <span class="err">col</span> <span class="err">in</span> <span class="err">row</span><span class="p">}</span><span class="err">}</span>
<span class="p">{</span><span class="err">{col</span><span class="p">}</span><span class="err">}</span>
<span class="p">{</span><span class="err">{endfor</span><span class="p">}</span><span class="err">}</span>
<span class="p">{</span><span class="err">{endif</span><span class="p">}</span><span class="err">}</span>
<span class="p">{</span><span class="err">{endfor</span><span class="p">}</span><span class="err">}</span>
<span class="o"></</span><span class="nt">pagetemplate</span><span class="o">></</span><span class="nt">pagetemplate</span><span class="o">></span></code></pre></figure>
<p>I think it is easy to understand how RML works. The only thing you have to understand is the organisation of the document. It starts with the normal XML header (<code class="highlighter-rouge"><table class="rouge-table"><tbody><tr><td class="rouge-gutter gl"><pre class="lineno">1
</pre></td><td class="rouge-code"><pre><xml ...="">
</pre></td></tr></tbody></table></code>) followed by the doctype (in case of RML documents ``). The next step is to specify the root node of the document, the <code class="highlighter-rouge"><table class="rouge-table"><tbody><tr><td class="rouge-gutter gl"><pre class="lineno">1
</pre></td><td class="rouge-code"><pre><document></document>
</pre></td></tr></tbody></table></code> block, which contains three other main blocks, namely the <code class="highlighter-rouge"><table class="rouge-table"><tbody><tr><td class="rouge-gutter gl"><pre class="lineno">1
</pre></td><td class="rouge-code"><pre><template></template>,
</pre></td></tr></tbody></table></code> the <code class="highlighter-rouge"><table class="rouge-table"><tbody><tr><td class="rouge-gutter gl"><pre class="lineno">1
</pre></td><td class="rouge-code"><pre><stylesheet></stylesheet>
</pre></td></tr></tbody></table></code> and the <code class="highlighter-rouge"><table class="rouge-table"><tbody><tr><td class="rouge-gutter gl"><pre class="lineno">1
</pre></td><td class="rouge-code"><pre><story></story>
</pre></td></tr></tbody></table></code> block.</p>
<p>The last three blocks are the container for your static page content, your used style sheets and dynamic and/or flowable content.</p>
<p>You start with defining all templates for your sites within the <code class="highlighter-rouge"><table class="rouge-table"><tbody><tr><td class="rouge-gutter gl"><pre class="lineno">1
</pre></td><td class="rouge-code"><pre><template></template>
</pre></td></tr></tbody></table></code> block. Each of this templates is declared using the <code class="highlighter-rouge"><table class="rouge-table"><tbody><tr><td class="rouge-gutter gl"><pre class="lineno">1
</pre></td><td class="rouge-code"><pre><pagetemplate></pagetemplate>
</pre></td></tr></tbody></table></code> element, which holds all content that should be displayed of all sites using this template. So here you would place your headlines and your footers.</p>
<p>Within the next block, the <code class="highlighter-rouge"><table class="rouge-table"><tbody><tr><td class="rouge-gutter gl"><pre class="lineno">1
</pre></td><td class="rouge-code"><pre><stylesheet></stylesheet>
</pre></td></tr></tbody></table></code> block, you could define styles, that could be used with the corresponding elements within the <code class="highlighter-rouge"><table class="rouge-table"><tbody><tr><td class="rouge-gutter gl"><pre class="lineno">1
</pre></td><td class="rouge-code"><pre><template></template>
</pre></td></tr></tbody></table></code> and <code class="highlighter-rouge"><table class="rouge-table"><tbody><tr><td class="rouge-gutter gl"><pre class="lineno">1
</pre></td><td class="rouge-code"><pre><story></story>
</pre></td></tr></tbody></table></code> block. It is a bit like creating Cascading Style Sheets for your HTML pages.</p>
<p>The last block, the <code class="highlighter-rouge"><table class="rouge-table"><tbody><tr><td class="rouge-gutter gl"><pre class="lineno">1
</pre></td><td class="rouge-code"><pre><story></story>
</pre></td></tr></tbody></table></code> block, contains the actual content, like plain text, tables, illustrations, diagrams or program code. The content placed within this block is automatically inserted into the first template specified within the <code class="highlighter-rouge"><table class="rouge-table"><tbody><tr><td class="rouge-gutter gl"><pre class="lineno">1
</pre></td><td class="rouge-code"><pre><template></template>
</pre></td></tr></tbody></table></code> block. So in the example above the template with the ID “<code class="highlighter-rouge"><table class="rouge-table"><tbody><tr><td class="rouge-gutter gl"><pre class="lineno">1
</pre></td><td class="rouge-code"><pre>main
</pre></td></tr></tbody></table></code>” is used. In order to switch to an other template you could just embed <code class="highlighter-rouge"><table class="rouge-table"><tbody><tr><td class="rouge-gutter gl"><pre class="lineno">1
</pre></td><td class="rouge-code"><pre><setnexttemplate ...=""></setnexttemplate>
</pre></td></tr></tbody></table></code> with the name attribute set to the corresponding template ID and the new template will be used on the next page. In the example above I forced a page break, so the following content is directly embedded within the specified template.</p>
<p>However, that’s all about the basic structure of RML documents and since I just like to show you the abilities of <em>reportlab</em>, <em>z3c.rml</em> and <em>preppy</em>, I do not explain the above document any longer and head over to the Python code:</p>
<figure class="highlight"><pre><code class="language-python" data-lang="python"><span class="c1">#!/usr/bin/python
</span><span class="kn">from</span> <span class="nn">z3c.rml</span> <span class="kn">import</span> <span class="n">rml2pdf</span>
<span class="kn">import</span> <span class="nn">datetime</span>
<span class="kn">import</span> <span class="nn">preppy</span>
<span class="kn">import</span> <span class="nn">csv</span>
<span class="kn">import</span> <span class="nn">sys</span>
<span class="k">def</span> <span class="nf">fetchTable</span><span class="p">():</span>
<span class="c1"># Initialize the result array ...
</span> <span class="n">data</span> <span class="o">=</span> <span class="p">[]</span>
<span class="c1"># ... and parse the content.
</span> <span class="k">with</span> <span class="nb">open</span><span class="p">(</span><span class="s">'data.csv'</span><span class="p">,</span> <span class="s">'r'</span><span class="p">)</span> <span class="k">as</span> <span class="n">csvFile</span><span class="p">:</span>
<span class="k">for</span> <span class="n">row</span> <span class="ow">in</span> <span class="n">csv</span><span class="o">.</span><span class="n">reader</span><span class="p">(</span><span class="n">csvFile</span><span class="p">,</span> <span class="n">delimiter</span><span class="o">=</span><span class="s">','</span><span class="p">):</span>
<span class="n">rowData</span> <span class="o">=</span> <span class="p">[]</span>
<span class="k">for</span> <span class="n">key</span><span class="p">,</span> <span class="n">col</span> <span class="ow">in</span> <span class="nb">enumerate</span><span class="p">(</span><span class="n">row</span><span class="p">):</span>
<span class="n">rowData</span><span class="o">.</span><span class="n">append</span><span class="p">(</span><span class="n">col</span><span class="p">)</span>
<span class="n">data</span><span class="o">.</span><span class="n">append</span><span class="p">(</span><span class="n">rowData</span><span class="p">)</span>
<span class="k">return</span> <span class="n">data</span>
<span class="k">def</span> <span class="nf">main</span><span class="p">(</span><span class="n">argv</span><span class="p">):</span>
<span class="c1"># Load the rml template into the preprocessor, ...
</span> <span class="n">template</span> <span class="o">=</span> <span class="n">preppy</span><span class="o">.</span><span class="n">getModule</span><span class="p">(</span><span class="s">'testDoc.prep'</span><span class="p">)</span>
<span class="c1"># ... fetch the table data ...
</span> <span class="n">table</span> <span class="o">=</span> <span class="n">fetchTable</span><span class="p">()</span>
<span class="c1"># ... and do the preprocessing.
</span> <span class="n">rmlText</span> <span class="o">=</span> <span class="n">template</span><span class="o">.</span><span class="n">get</span><span class="p">(</span>
<span class="n">datetime</span><span class="o">.</span><span class="n">datetime</span><span class="o">.</span><span class="n">now</span><span class="p">()</span><span class="o">.</span><span class="n">strftime</span><span class="p">(</span><span class="s">"</span><span class="si">%</span><span class="s">Y-</span><span class="si">%</span><span class="s">m-</span><span class="si">%</span><span class="s">d"</span><span class="p">),</span> <span class="s">'Andreas Schickedanz'</span><span class="p">,</span>
<span class="s">'www.bornageek.com'</span><span class="p">,</span> <span class="s">'info@bornageek.com'</span><span class="p">,</span> <span class="n">table</span><span class="p">)</span>
<span class="c1"># Finally generate the *.pdf output ...
</span> <span class="n">pdf</span> <span class="o">=</span> <span class="n">rml2pdf</span><span class="o">.</span><span class="n">parseString</span><span class="p">(</span><span class="n">rmlText</span><span class="p">)</span>
<span class="c1"># ... and save it.
</span> <span class="k">with</span> <span class="nb">open</span><span class="p">(</span><span class="s">'rmlReport.pdf'</span><span class="p">,</span> <span class="s">'w'</span><span class="p">)</span> <span class="k">as</span> <span class="n">pdfFile</span><span class="p">:</span>
<span class="n">pdfFile</span><span class="o">.</span><span class="n">write</span><span class="p">(</span><span class="n">pdf</span><span class="o">.</span><span class="n">read</span><span class="p">())</span>
<span class="k">if</span> <span class="n">__name__</span> <span class="o">==</span> <span class="s">'__main__'</span><span class="p">:</span>
<span class="n">main</span><span class="p">(</span><span class="n">sys</span><span class="o">.</span><span class="n">argv</span><span class="p">[</span><span class="mi">1</span><span class="p">:])</span></code></pre></figure>
<p><img src="https://bornageek.com/images/report.jpg" alt="An advanced example using ReportLab and RML" /></p>
<p>The interesting stuff regarding this script you find within the main function. First of all a new Python module is created from the RML document using Preppy, which is then fed with the dynamic data, like the author information and the table data. The resulting RML document contains all this information and could now be parsed using <em>z3c.rml</em>. This could be achieved by calling the <em>parseString()</em> method of the <em>rml2pdf</em> class. The result of this call is already the content of the PDF file, which just have to be written to the file itself. That’s all, you are done. The PDF file should look like <a href="https://bornageek.com/assets/rmlReport.pdf">this</a>.</p>
<p>The *.csv file, I used in the above example could be downloaded <a href="https://bornageek.com/assets/data.csv">here</a>.</p>
<h3 id="conclusion">Conclusion</h3>
<p>I think that the combination of ReportLab, the Report Markup Language (RML) and the Preppy text pre-processor is a very powerful solution for automatically generate *.pdf files. As I already mentioned, the applications for such automated reports are unlimited. You could create personalized form letters, documentations or business reports in just a couple of minutes.</p>
<p>The <em>z3c.rml</em> package as alternative to the proprietary <em>rlextra</em> package of ReportLab has some limits, but it packs enough features to keep up with the competition. It enables the user to separate the template from the dynamic content, so designers and developers could work in parallel.</p>
<p>I hope you enjoyed this short introduction. I would be happy to hear of your experience and to see your results. In one of my next posts I will show you how to use these libraries to generate reports feeded with data from a sensor network connected with my Beaglebone Black.</p>
<p>Stay tuned and until next time - happy coding!</p>Andreas Schickedanzinfo@bornageek.comThe generation of reports is part of the daily business for everybody, who participates in a development or research team or works as part of a sales force team. You always have to report your current project state, your latest updates and fixes or the sales and order intake of the last quarter as well as the forecast of the coming quarter. Most of this reports always consist of the same graphs, texts and forms and could therefore be generated using something like a template. In other words they could be generated completely automatically if the data that changes over time is stored in something like a database. Imagine a smaller web shop you are running. To get a better feeling of whether the shop is profitable or not or to check the response to a marketing campaign, you have to monitor your business. Thus, a report that contains some statistics about the evolution of your daily and/or monthly sales, as well as the analysis of the targets defined in the forecast, could help you to improve your marketing strategy. However, I am a computer scientist and do not care about those stuff, but I am the one to ask how to automate things. And that is what I did to monitor different things, like my Flattr and Wordpress account, as well as the monthly power consumption of different house electronics at home. The range of applications is enormous, if you just think about it for a moment.