OCR for your old scanner/printer’s E-mails

Yesterday I explained how to make a scrap computer do OCR on your scanner/printer’s scanned PDFs in case you have a SMB file share (a Windows file share) where the printer will write to.

I also promised I would make the E-Mail feature of the printer send E-mails with the PDFs in that E-mail being OCR scanned.

I had earlier explained how you can make your old scanner/printer support modern SMTP servers that have TLS, by introducing a scrap computer running Postfix to forward the E-mails for you. This article depends on that, of course. As we will let the scrap computer now do the OCR part. If you have not yet done that, first do it before continuing here.

I looked at Xavier Merten‘s CockooMX, and decided to massacre it until it would do what I want it to do. Namely call ocrmypdf on the application/pdf attachments and then add the resulting PDF/A (which will have OCR text) to the E-mail.

First install some extra software: apt-get install libmime-tools-perl . It will provide you with MIME::Tools, we will use MIME::Parser and MIME::Entity.

Create a Perl script called /usr/local/bin/ocrpdf.pl (chmod 755 it) that looks like this (which is Xavier’s CockooMX massacred and reduced to what I need – Sorry Xavier. Maybe we could try to make CockooMX have a plugin like infrastructure? But writing what looks suspicious to a database ain’t what I’m aiming for here):


# Copyright note

use Digest::MD5;
use File::Path qw(make_path remove_tree);
use File::Temp;
use MIME::Parser;
use Sys::Syslog;
use strict;
use warnings;

use constant EX_TEMPFAIL => 75; # Mail sent to the deferred queue (retry)
use constant EX_UNAVAILABLE => 69; # Mail bounced to the sender (undeliverable)

my $syslogProgram	= "ocrpdf";
my $sendmailPath	= "/usr/sbin/sendmail";
my $syslogFacility	= "mail";
my $outputDir		= "/var/ocrpdf";
my $ocrmypdf		= "/usr/bin/ocrmypdf";

# Create our working directory
$outputDir = $outputDir . '/' . $$;
if (! -d $outputDir && !make_path("$outputDir", { mode => 0700 })) {
  syslogOutput("mkdir($outputDir) failed: $!");

# Save the mail from STDIN
if (!open(OUT, ">$outputDir/content.tmp")) {
  syslogOutput("Write to \"$outputDir/content.tmp\" failed: $!");
while() {
  print OUT $_;

# Save the sender & recipients passed by Postfix
if (!open(OUT, ">$outputDir/args.tmp")) {
  syslogOutput("Write to \"$outputDir/args.tmp\" failed: $!");
foreach my $arg (@ARGV) {
  print OUT $arg . " ";

# Extract MIME types from the message
my $parser = new MIME::Parser;
my $entity = $parser->parse_open("$outputDir/content.tmp");

# Extract sender and recipient(s)
my $headers = $entity->head;
my $from = $headers->get('From');
my $to = $headers->get('To');
my $subject = $headers->get('Subject');

syslogOutput("Processing mail from: $from ($subject)");

remove_tree($outputDir) or syslogOuput("Cannot delete \"$outputDir\": $!");

exit 0;

sub processMIMEParts
  my $entity = shift || return;
  for my $part ($entity->parts) {
    if($part->mime_type eq 'multipart/alternative' ||
       $part->mime_type eq 'multipart/related' ||
       $part->mime_type eq 'multipart/mixed' ||
       $part->mime_type eq 'multipart/signed' ||
       $part->mime_type eq 'multipart/report' ||
       $part->mime_type eq 'message/rfc822' ) {
         # Recursively process the message
     } else {
       if( $part->mime_type eq 'application/pdf' ) {
         my $type = lc  $part->mime_type;
         my $bh = $part->bodyhandle;
         syslogOutput("OCR for: \"" . $bh->{MB_Path} . "\" (" . $type . ") to \"" . $bh->{MB_Path} . ".ocr.pdf" . "\"" );
         # Perform the OCR scan, output to a new file
         system($ocrmypdf, $bh->{MB_Path}, $bh->{MB_Path} . ".ocr.pdf");
         # Add the new file as attachment
         $entity->attach(Path   => $bh->{MB_Path} . ".ocr.pdf",
                         Type   => "application/pdf",
                         Encoding => "base64");

# deliverMail - Send the mail back
sub deliverMail {
  my $entity = shift || return;

  # Write the changed entity to a temporary file
  if (! open(FH, '>', "$outputDir/outfile.tmp")) {
    syslogOutput("deliverMail: cannot write $outputDir/outfile.tmp: $!");

  # Read saved arguments
  if (! open(IN, "<$outputDir/args.tmp")) {
    syslogOutput("deliverMail: Cannot read $outputDir/args.tmp: $!");
    exit EX_TEMPFAIL;
  my $sendmailArgs = ;
  # Read mail content from temporary file of changed entity
  if (! open(IN, "<$outputDir/outfile.tmp")) {
    syslogOutput("deliverMail: Cannot read $outputDir/content.txt: $!");
  # Spawn a sendmail process
  syslogOutput("Spawn=$sendmailPath -G -i $sendmailArgs");
  if (! open(SENDMAIL, "|$sendmailPath -G -i $sendmailArgs")) {
    syslogOutput("deliverMail: Cannot spawn: $sendmailPath $sendmailArgs: $!");
    exit EX_TEMPFAIL;
  while() {
    print SENDMAIL $_;

# Send Syslog message using the defined facility
sub syslogOutput {
  my $msg = shift or return(0);
  openlog($syslogProgram, 'pid', $syslogFacility);
  syslog('info', '%s', $msg);

Now we just do what Xavier’s CockooMX documentation also tells you to do: add it to master.cf:

Create a UNIX user: adduser ocrpdf

Change the smtp service:

smtp      inet  n       -       -       -       -       smtpd
-o content_filter=ocrpdf

Create a new service

ocrpdf  unix  -       n       n       -       -       pipe
user=ocrpdf argv=/usr/local/bin/ocrpdf.pl -f ${sender} ${recipient}

OCR for your old printer/scanner

Modern printers can do OCR on your scans. But as we talked about last time, aren’t all printers or scanners modern.

We have a scrap computer that is (already) catching all E-mails on a badly configured local SMTP server, to then forward it to a well configured SMTP server that has TLS. Now we also want to do OCR on the scanned PDFs.

My printer has a so called Network Scan function that scans to a SMB file share (that’s a Windows share). The scrap computer is configured to share /var/scan using Samba as ‘share’, of course. The printer is configured to use that share. Note that you might need in smb.conf this for very old printers:

client min protocol = LANMAN1
server min protocol = LANMAN1
client lanman auth = yes
client ntlmv2 auth = no
client plaintext auth = yes
ntlm auth = yes
security = share

And of course also something like this:

path = /var/scan
writable = yes
browsable = yes
guest ok = yes
public = yes
create mask = 0777

First install software: apt-get install ocrmypdf inotify-tools screen bash

We need a script to perform OCR scan on a PDF. We’ll here use it in another script that monitors /var/scan for changes. Later in another post I’ll explain how to use it from Postfix’s master.cf on the attachments of an E-mail. Here is /usr/local/bin/fixpdf.sh:

! /bin/sh
TMP=`mktemp -d -t XXXXX`
mkdir -p $DIR/ocr
cd $DIR
TIMESTAMP=`stat -c %Y "$a"`
ocrmypdf --force-ocr "$a" "$TMP/OCR-$a"
mv -f "$TMP/OCR-$a" "$DIR/ocr/$TIMESTAMP-$a"
chmod 777 "$DIR/ocr/$TIMESTAMP-$a"
cd /tmp
rm -rf $TMP

Note that I prepend the filename with a timestamp. That’s because my printer has no way to give the scanned files a good filename that I can use for my archiving purposes. You can of course do this different.

Now we want a script that monitors /var/scan and launches that fixpdf.sh script in the background each time a file is created.

My Xerox WorkCentre 7232 uses a directory called SCANFILE.LCK/ for its own file locking. When it is finished with a SCANFILE.PDF it deletes that LCK directory.

Being bad software developers the Xerox people didn’t use a POSIX rename for SCANFILE.PDF to do an atomic write operation at the end.

It looks like this:

inotifywait -r -m  /var/scan | 
while read file_path file_event file_name; do
echo ${file_path}${file_name} event: ${file_event}
Setting up watches. Beware: since -r was given, this may take a while!
Watches established.
/var/scan/ event: OPEN,ISDIR
/var/scan/ event: ACCESS,ISDIR
/var/scan/ event: ACCESS,ISDIR
/var/scan/ event: OPEN,ISDIR
/var/scan/ event: ACCESS,ISDIR
/var/scan/ event: ACCESS,ISDIR
/var/scan/XEROXSCAN003.LCK event: CREATE,ISDIR
/var/scan/XEROXSCAN003.LCK event: OPEN,ISDIR
/var/scan/XEROXSCAN003.LCK event: ACCESS,ISDIR
/var/scan/ event: OPEN,ISDIR
/var/scan/ event: ACCESS,ISDIR
/var/scan/ event: ACCESS,ISDIR
/var/scan/ event: OPEN,ISDIR
/var/scan/ event: ACCESS,ISDIR
/var/scan/XEROXSCAN003.PDF event: CREATE
/var/scan/XEROXSCAN003.PDF event: OPEN
/var/scan/XEROXSCAN003.PDF event: MODIFY
/var/scan/XEROXSCAN003.PDF event: MODIFY
/var/scan/XEROXSCAN003.PDF event: MODIFY
/var/scan/XEROXSCAN003.PDF event: MODIFY
/var/scan/XEROXSCAN003.PDF event: ATTRIB
/var/scan/XEROXSCAN003.LCK event: OPEN,ISDIR
/var/scan/XEROXSCAN003.LCK/ event: OPEN,ISDIR
/var/scan/XEROXSCAN003.LCK event: ACCESS,ISDIR
/var/scan/XEROXSCAN003.LCK/ event: ACCESS,ISDIR
/var/scan/XEROXSCAN003.LCK event: ACCESS,ISDIR
/var/scan/XEROXSCAN003.LCK/ event: ACCESS,ISDIR
/var/scan/XEROXSCAN003.LCK/ event: DELETE_SELF
/var/scan/XEROXSCAN003.LCK event: DELETE,ISDIR

The printer deleting that SCANFILE.LCK/ directory is a good moment to start our OCR script (call it for example /usr/local/bin/monitorscan.sh):

! /bin/bash
inotifywait -r -m -e DELETE,ISDIR /var/scan |
while read file_path file_event file_name; do
if [ ${file_event} = "DELETE,ISDIR" ]; then
if [[ ${file_name} == *"LCK" ]]; then
filename=`echo ${file_name} | sed -e "s/$suffix$//"`.PDF
/usr/local/bin/fixpdf.sh $filename &

Give both scripts 755 permissions with chmod and now you just run screen /usr/local/bin/monitorscan.sh

When your printer was written by good software developers, it will do POSIX rename. That looks like this (yes, also when done over a SMB network share):

inotifywait -r -m  /var/scan | 
while read file_path file_event file_name; do
echo ${file_path}${file_name} event: ${file_event}
Setting up watches. Beware: since -r was given, this may take a while!
Watches established.
/var/scan/ event: OPEN,ISDIR
/var/scan/ event: ACCESS,ISDIR
/var/scan/ event: OPEN,ISDIR
/var/scan/ event: ACCESS,ISDIR
/var/scan/ event: OPEN,ISDIR
/var/scan/ event: ACCESS,ISDIR
/var/scan/ event: OPEN,ISDIR
/var/scan/ event: ACCESS,ISDIR
/var/scan/.tmp123.GOODBRANDSCAN-123.PDF event: CREATE
/var/scan/.tmp123.GOODBRANDSCAN-123.PDF event: OPEN
/var/scan/.tmp123.GOODBRANDSCAN-123.PDF event: MODIFY
/var/scan/.tmp123.GOODBRANDSCAN-123.PDF event: MOVED_FROM
/var/scan/GOODBRANDSCAN-123.PDF event: MOVED_TO

That means that your parameters for inotifywait could be -r -m -e MOVED_TO and in ${file_name} you’ll have that GOODBRANDSCAN-123.PDF. This is of course better than Xerox’s way with their not invented here LCK things that probably also wouldn’t be necessary with a POSIX rename call.

I will document how to do this to the E-mail feature of the printer with Postfix later.

I first need a moment in my life where I actually need this hard enough that I will start figuring out how to extract certain attachment MIME parts from an E-mail with Posix’s master.cf. I guess I will have to look into CockooMX by Xavier Mertens for that. Update: that article is available now.


Wrong SMTP config so that your old scanner/printer can send mails

This is a warning: only do this at home, instead of don’t do this at home. On your local home network that is behind a firewall and/or gateway.

Edit: Philip Paeps sent me a few suggestions to restrict things even more. I have adapted this blog item to mention all of them.

Unfortunately there are companies that make and made printers and then don’t provide firmware upgrades for it.

You end up with a scanner/printer that works perfectly fine. Except you can’t find any SMTP servers without TLS anymore. Because rightfully so has more or less every E-mail provider turned off plain text SMTP.

Now your printer cannot send the scanned documents over E-mail anymore. Firmware upgrade of your scanner/printer? What if there is none?

What to do? Run a unencrypted postfix smtpd on some scrap computer on your local network, that relays your mails with its smtp client to a TLS enabled SMTP server.

apt-get install postfix # and select Internet site

I didn’t want fake security by having authentication on the smtpd side, as there will be no encryption between printer and our local postfix. When somebody listens on your local network they would not only have the PDFs that you scanned with your scanner/printer, they will also have those authentication tokens.

I suppose you can add authentication, but then at least don’t be silly and use usernames and passwords that you use somewhere else. Note that some really old scanners/printers also can’t do SMTP with authentication.

I used these relay_restrictions for smtpd. We will place the printer’s IP address in/within mynetworks, so we will relay for it through permit_mynetworks.

smtpd_relay_restrictions = permit_mynetworks reject_unauth_destination

The mydestination should be as restricted as possible so we’ll just empty it (no local delivery at all):

mydestination =
local_recipient_maps =
local_transport = error:local delivery is disabled

You can now also remove the local delivery agent from master.cf if you want. We restrict the mynetworks of course too. The scanner/printer is with DHCP on, so add that:

mydestination =
mynetworks =

Even better is to have your scanner/printer on a fixed IP address and then use that one IP address:

mynetworks =

In fact, when the scrap computer has a fixed IP address then you can further restrict things by using inet_interfaces of course:

inet_interfaces =,

And now we configure the relayhost, we will relay to our TLS enabled SMTP(s) server:

relayhost = [smtps.server.org]:587
smtp_sasl_auth_enable = yes
smtp_sasl_password_maps = hash:/etc/postfix/sasl_passwd
smtp_sasl_security_options = noanonymous
smtp_tls_security_level = encrypt
header_size_limit = 4096000
message_size_limit = 524288000

Now make a file /etc/postfix/sasl_passwd to add the username / password. After that of course run postmap /etc/postfix/sasl_passwd

[smtps.server.org]:587 username:password

We now on the printer configure the scrap computer’s local network IP address or DHCP hostname as SMTP server.

Other security considerations that are possible is to place printer and scrap computer on a VLAN or let there be a crossed UTP cable between them. But if you are going to do those things then you also know how to do it yourself. Such things do make sense: your easily hackable LED lamps and even more easily hackable Smart TV don’t need to talk to your printer nor this scrap computer. VLAN everything!

Aankondigingspolitiek en dan daarna

Na meer dan een half jaar geshockeerd toe te kijken hoe onze Europese leiders de éne na de andere aankondigingspolitiek deden zonder wat dan ook substantiëels te doen, lijkt de politieke klasse in de Europese wijk in Brussel eindelijk te zijn wakker geschoten.

Dat Rusland tegensancties zou doen nadat wij letterlijk hun nationale reserves afnamen, was verdomd voorspelbaar en kei hard al vanaf half februari een gegeven dat zeker was.

Dat het Russisch gas uiteindelijk zou afgesloten worden was klaar en duidelijk. Hoeveel gas het was was klaar en duidelijk. Dat de godganse Europese economie dan op haar gat zou gaan, was klaar en duidelijk.

We vinden het allemaal heel erg prettig om nu een beetje met Duitsland te staan lachen. Maar heel erg binnenkort zal jouw baan, die afhankelijk is van de Duitse industrie (denk er maar eens twee keer over na), verdwijnen. Het zal gedaan zijn met lachen terwijl je gigantisch hoge energiefacturen mag betalen.

Allemaal klaar en duidelijk. Crystal helder.

Maar wat deden de jongens en meisjes in de Europese wijk te Brussel daar aan? Helemaal niets.

Aankondigingspolitiek. Populistisch zijn. Dissidenten verketteren: was je tegen sancties, dan was je voor Putin. Alsof dit enkel Putin is en niet de gehele elite in Het Kremlin. Enkelen werden zelfs afgedreigd. Ja toch wel ja. WIJ zijn aan het afglijden naar fascisme en censuur. Heel erg duidelijk.

Maar als je je economie in oorlog zet tegen Rusland, dan hoor je ook voorbereid te zijn. Dat zijn en dat waren wij niet. Totaal niet.

De populistische aankondigingspolitiek van het afgelopen jaar zal er voor zorgen dat onze Europese economie gewoon de grond ingeboord zal worden.

We hadden intelligente sancties kunnen bedenken. Maar dat was niet straf genoeg. We hadden kunnen gaan onderhandelen met Rusland. Maar dan werd je verketterd.

Of is dat niet waar? Zie dan naar Macron die probeerde en telkens door de roeptoeters verketterd werd.

Maar zie hoe Erdogan het wel doet en daarmee zijn Turkije helemaal op de kaart heeft gezet. Hoe de hele West-Europese wereld in het conflict van Oekraïne er letterlijk gewoon niet toe doet. Maar Turkije helemaal wel.

Onze Westerse leiders zijn een bende populistische Twitter-accounts maar dan zonder eigen leger. Ze zijn dus niets. Niets dat er toe doet.

Wij hebben géén strategie. Dat zijn wij helemaal verleerd. We schipperen rond op populisme en het om ter hardst roepen. Ik bedoel, tweeten.

En dat, dat werkt niet in oorlogstijden.

Belgian blue stone with white stains.

Here’s a scenario: ten years ago, you renovated your bathroom. As a shower, you chose a walk-in shower with a Belgian blue stone which is porous and is a limestone.

That’s why you have to be careful with products like Antikal. Because they will react with the lime in your limestone. In other words, you have to rinse away the excess Antikal quickly.

But you’re a computer geek and because you have a piece of software that you programmed and are already thinking about, you sometimes forget this.

Thus, a drop of Antikal remains on your blue stone.

A few hours later, that drop became a white stain. Which now really seems to be inside your stone.

Unfortunately, it really is. No matter what you try, it cannot be scrubbed off. Unless you let a professional come and polish a layer off of your stone (which costs about 800 euros, by the way).

Unfortunately, not much later the stains come back. Because the stains are deep in your stone and the reaction ‘continues’. That was 800 euros for a few weeks’ peace of mind.

Now what?

Of course you buy a whole gang of HG products such as HG blauwesteen reiniger, HG grafsteenreiniger and HG natuursteen kleurvlekkenverwijderaar.

You spray it on, let it soak in for half an hour; a little of the stains are now indeed better. But not really.

What actually worked better was the Mr. Proper Magic sponge. That sanded the top layer of that white stain away a bit.

But what worked best after that was a coat of Moeller Stone Care HMK P333 Hardsteenolie.

Your Belgian Blue stone will become a Belgian dark blue stone. But the stains (which I had first rubbed out with the Mr. Proper Magic sponge) are less visible and seem to belong to the stone’s natural life.

Then, after two full days letting the stuff get impregnated, a sticky top layer appeared. I had probably used a little too much of that stuff.

But another heavy treatment with the whole gang of HG products, such as HG blauwesteen reiniger, HG grafsteenreiniger and HG natuursteen kleurvlekkenverwijderaar (all at the same time and soak for half an hour) followed by rubbing hard with an iron sponge, and the sticky top layer is gone.

The stone is actually quite beautiful now. Dark blue.

De helaasheid der dingen

Zoals altijd zijn we als EU of Europa achtergesteld omdat we geen enkel militair antwoord hebben op de zaken die geostrategisch gaande zijn.

Voor Ukraine kunnen we weinig tot niets doen omdat we geen enkel antwoord hebben op de vraag ‘vanwaar komt het gas dan wel?’

Deze vraag is te belachelijk simpel en toch kan geen enkel EU politicus ze beantwoorden.

Nochtans was het antwoord hierop haalbaar: men had werk kunnen maken van alternatieve energiebronnen. Maar dat vonden de heren en dames EU-politici niet nodig. Overbodig. En zo verder.

M.a.w. zijn ze volstrekt incompetent. Ik schrijf ze effectief volledig af. Want ze hadden daar wel een antwoord op moeten kunnen formuleren. De idioten die er nu nog zitten kunnen dat niet. Daarom zijn het losers en daarom horen we ze te ontslaan uit hun functie. Helaas zijn het ook populisten en daarom zal hun ontslag vele jaren duren (zie Brexit).

Richting Taiwan doen de EU politici alweer hun belachelijke best om wat dan ook te betekenen. Maar iets betekenen doen ze helemaal niet. Ze doen niets dat er toe doet.

Omdat ze collectief besloten hebben geen EU-leger te hebben.

Daarom zijn ze onbelangrijk. Insignificant. Incompetent. Onbelangrijk.

We moeten

Een eigen Europees leger starten. Waarbij ieder Europees land haar eigen expertise in de groep werpt.

Afspraken maken met Rusland over de energievoorziening van Europa.

Een nieuw veiligheidspakt met Rusland maken opdat er zo weinig mogelijk conflicten in Europa zullen zijn.

Machtsprojectie doen vanuit Europa, met het Europees leger. We moeten opnieuw leren wat het is om aan geostrategie te doen. We moeten dat Europees leger durven inzetten om onze strategische doelen te behalen. We moeten niet verlegen zijn om de wereld duidelijk te maken dat wij zulke strategische doelen hebben.

Het conflict in Oekraïne beïndigen. Want het dient ons (Europeanen) en Russen niet. We zijn beiden benadeeld door dit conflict. We hebben er beiden baad bij om dit te beïndigen.

Durven praten over Europa en niet enkel over de Europese Unie.

Kosovo in de EU

De bedoeling van Finland en Zweden die lid worden van het huidige NATO mag niet zijn dat we meer NATO krijgen. Maar wel dat we een groter EU leger verkrijgen.

Alleen een goed EU leger zal in de toekomst een buurland zoals Rusland koest en kalm houden. De NATO zal dat niet doen. In tegendeel, het zal Rusland onrustig maken. Want dat speelt in NATO’s kaarten: een grootschalig conflict op het grondgebied van de EU met Rusland is in het grootste belang van de NATO om te blijven bestaan als organisatie.

M.a.w. moeten wij Europeanen heel erg veel investeren in onze eigen defensie. We moeten dit niet meer overlaten aan de VS. We moeten dit financieel én operationeel in eigen handel nemen.

We moeten bereid zijn en klaar staat om agressie van bijvoorbeeld Rusland te kunnen afslaan. Zelfs te bestraffen. Dat wil dus zeggen dat ook wij nucleaire wapens nodig hebben. In EU handen. Inzetbaar. Met een strategisch EU commando dat het middel ook effectief kan en vooral zal inzetten in geval van bepaalde scenarios.

Maar vooral moeten we investeren in diplomatie en realisme, realpolitik, met Rusland. Aan de hand van hun bezorgdheden, en die van ons.

Oekraïne was een bufferstaat. Dat werkte goed. Ik zou graag willen dat het dat zou blijven en zou zijn. Terwijl het graan produceert. Terwijl het groeit. Terwijl het eventueel over een paar decennium onderdeel van de EU wordt.

Maar zouden we niet eerst de Balkan eens eindelijk na zo’n godverdomme 20 jaar lid maken van de EU?! Kosovaren bijvoorbeeld kunnen nog steeds niet zonder reis visum naar de EU komen. Hoewel we daar de grootste NAVO-basis van West-Europa hebben (KFOR) staan. We praten hier nu over Oekraïne dat morgen lid van de EU zou kunnen worden. Terwijl de gehele Balkan-regio na haar oorlogen van de jaren negentig nog steeds, in 2022, niet geïntegreerd is.

Going around media bans

For the people who want to know how to get around the ongoing media bans:

Do a Google search and type in ‘free ssh country‘. Use as ‘country‘ the country where the media is that you want to reach. Take the first hit. Create the SSH tunnel account that is freely available.

In a console (if you don’t have SSH, you can install it with for example Git Bash, Cygwin, etc on Windows. You can also use Putty of course – make a SOCKS tunnel). Note that the username and hostname will be different (the website will tell you).

ssh -D 1337 -q -C -N user-vpnthathost.com@countryNr.thathost.com

For Firefox: Settings->Network Settings [Settings]->Manual Proxy configuration. Type next to SOCKS Host: localhost, and next to Port: 1337. Next check ‘Proxy DNS when using SOCKS v5’. You have equivalent settings in Chrome and Chromium among other browsers.

There are many options as VPN service. You can also search with Tor-browser if you think the secret services want to kill you or something. It’s not very likely, though. But if you are paranoid, then I guess sure.

Tor-browser itself might by the way also work just fine.

How to make a movie from the history of wikipedia images

Install some software

apt-get install imagej wget imagemagick bash

Get a JSON with all the versions of a image on Wikipedia

Let’s assume you want to watch how the invasion in Ukraine took place. That’s this one:


We only need the URL, so iiprop=|url. If you need the timestamp (when was the image file made) then use iiprop=|url|timestamp. A list of them you can find here.

You can use wget or in your browser just do save file as (use RAW data at the top then, for example in Firefox).

Fetch the images

#! /bin/bash

mkdir svg
cd svg
ITEMS=`cat api-result.json | jq '.query.pages."-1".imageinfo | .[] | .url'`
for a in $ITEMS
wget "$a"
sleep 1

Convert them to PNGs:

Normally they are already in the right order. So no renaming should be needed. Else you have to add to the iiprop of the query ‘timestamp’ and then with jg you extract that from the JSON to for example add it somehow to the filenames.

Or just use touch to change the file’s last modification date after wget fetched it and then here you use UNIX find to loop in the right order, and write PNG files like 0001.png, 0002.png, etc.

#! /bin/bash
cd ..
mkdir png
for a in svg/*
# You can come up with a better translation for the filename
b=`echo $a | sed s/svg//g`
convert -density 250 -size 1546x1038 $a png/$b.png

Convert PNGs to a movie

  • Start imagej
  • File > Import > Image Sequence
  • Select the png directory where the images where converted to
  • File -> Save As -> AVI -> fill in 2 frames per second -> movie.avi

Convert into fake-news, propaganda, etc

  • Use another software to insert dramatic background music
  • Upload to youtube for fame and fortune or whatever
  • Buy likes, become a hipster, convert from hipster to influencer

Nobody moves, nobody gets hurt

Maar toch. Start dat EU-leger nu maar.

Het volgende conflict, in Oekraïne, staat voor de deur. De West-Europese landen gaan zich weer volstrekt belachelijk maken, en hebben zich reeds volstrekt belachelijk gemaakt.

Het is tijd dat we stoppen met de schoothond van de VS en NAVO te zijn, en dat we als de EU een eigen leger hebben dat deel uit maakt van NAVO en dat op termijn de noodzaak van het bestaan van NAVO opheft.

Jeff Hoeyberghs liet een scheet

Het land in crisis: alle hoofdredacteuren van het land schrijven opiniestukken! Duizenden vrouwen gingen naar de rechtbank.

Meanwhile: Op 31 maart 2020 werd België in de zaak Jeanty door het Europees Mensenrechtenhof veroordeeld voor de inhumane behandeling van een gedetineerde met psychische problemen. Artikel 3 EVRM wordt geschonden geacht omdat de man in voorlopige hechtenis niet de vereiste medische zorg had verkregen (link).

Nobody cares.

Ik verwijs naar een blog-post van me uit 2008: Moral indulgence.

Improving Qt

We are a few years further. A few years in which we all tried to make a difference.

I’m incredibly proud of my achievement of QTBUG-61928. At the time I thought I could never convince the Qt development team of changing their APIs. They did and today in Qt6 it’s all very much part of the package.

I want to thank Thiago and others. But I also think it’s a team effort. It might not be because of just me. But I still feel a little bit proud of having pushed this team just enough to make the changes.

I am now at a new Qt bug report. This time it’s about int64_t. I think that QModelIndex should be completely supporting it. Again, I think a lot. And I have a lot of opinions. But I anyway filed QTBUG-99312 for this.

Laster en eerroof naar correctionele

Ik vind dat het verhuizen van welke rechtbank laster een eerroof behandelt naar de correctionele rechtbanken te laten gaan, een goede zaak is.

Ik begrijp het, dat het feit dat, assisen de rechtbank was die hierover ging, als een soort van buffer speelde om de vrijheid van meningsuiting te vrijwaren. De comma’s staan er omdat het momenten om over na te denken zijn.

Maar dat is en was eigenlijk een misbruik van een soort van overheidsincompentie (of eigenlijk rechtbankenincompetentie). Namelijk dat er bij Assisen te weinig capaciteit was om dit soort misdaden te beoordelen en wanneer (en slechts enkel wanneer) het over de schreef ging, te veroordelen.

In de praktijk betekende het dat eigenlijk bijna alles kon. Terwijl dat nu geen goed idee meer is. Niet alles kan. Je vrijheid om bv. een leugen te spreken is begrenst daar waar die vrijheid schade berokkent. En dat is zo zelfs wanneer wat jij zegt geen leugen is, en voornamelijk wanneer dat wat gezegd wordt geen maatschappelijke meerwaarde heeft.

Het is dus aan een rechter om je dan te stoppen. Wij leven hier in België onder het Belgische recht. Niet onder jouw absolute vrijheid. Want onze geringe vrijheid is afhankelijk van ons Belgische recht. M.a.w. is, hoe controversieel ook (en laat ik duidelijk zijn dat ik duidelijk weet dat het controversieel is), jouw ingebeelde recht om een ander te schaden, niet altijd gelijk aan jouw recht om het ook te uiten. Niet hier in België. Niet altijd. Bijna altijd wel, maar niet altijd.

Context doet er toe (dus, de humor kan terecht zeer ver gaan). Maar niet altijd.

Ik ben wel erg bezorgd hierover. Maar dat maakt niet dat dit niet mag of kan behandeld worden. Het slaat stevig in op de verlichtingsfilosofie. Die zegt dat bijna alles moet kunnen gezegd worden. Maar om alles te kunnen zeggen moeten we ook, zeker met de opkomst van nieuwe media, akkoord gaan dat een overvloed van leugens ook de reputatie van eerbare mensen onherstelbaar zal schaden. Dus was de verlichting als filosofie soms naïef (hoewel ze stelde dat dit normaalgesproken zichelf corrigeert).

De grond van de vrije meningsuiting mogen we toch niet vergeten. Vind ik.

RE: Dat er geen gevaar is, lozh

Ik heb gelogen. In ieder geval ben ik een belofte niet nagekomen: ik beloofde een tiental blogpost die als ondertoon zouden hebben dat er geen gevaar is.

De enige twee die daar aan zouden kunnen voldoen waren mijn blog items over dat onze aarde een rechthoekige blok is en die waar ik oproep om te stoppen met NAVO.

Want nu dat onze aarde rechthoekig is, vallen we er aan onze kant toch al niet meer af. En ook wanneer we zouden stoppen met NAVO, dan vernietigen we onze rechthoekige blok ‘de aarde’ toch al niet meer met onze eigen nucleaire wapens!

Dus, moest de mensheid deze twee blog-items volgen en geloven, dan zou ongeveer 99,98% van alle gevaar voor de mensheid verdwijnen.

Maar de toekomst brengt een nieuwe belofte: Lozh en Vranyo. Want nu gaan we eindelijk eens die sociale media aanpakken. We hebben alvast Trump overal verbannen.


Avast, Qt6 announcing new QPromise and QFuture APIs

Qt published its New_Features in Qt 6.0.

Some noteworthy items in their list:

  • QPromise allows setting values, progress and exceptions to QFuture
  • QFuture supports attaching continuations

I like to think I had my pirate-hook in it at least a little bit with QTBUG-61928.

I need to print this out and put it above my bed:

Thiago Macieira added a comment – 
You’re right
Philip Van Hoof added a comment – 
Damn, and I was worried the entire morning that I had been ranting again.
Thiago Macieira added a comment – 
oh, you were ranting. Doesn’t mean you’re wrong.

Thanks for prioritizing this Thiago.


Het verontrustende is dat het niet meer verontrustend is wanneer de President van de Verenigde Staten zegt dat hij Duitsland niet zou helpen wanneer Rusland zou aanvallen. Gelukkig is het heet worden van de koude oorlog met Rusland verder af dan ongeveer wat dan ook aan mogelijke alternatieven. Ongeacht wat bepaalde propaganda-machines ons proberen wijs te maken.

Ondertussen werken we nog steeds niet aan een eigen Europees leger. Voor stel dat het toch ooit nodig is.

Het is wel Duitslands eigen beslissing om via Northstream 2 gas uit Rusland aan te kopen en te transporteren. En het is dus niet aan de Verenigde Staten om zich daar mee te moeien. Ook niet door druk te zetten door middel van misbruik te maken van NAVO afspraken.

Het wordt nu echt tijd om de NAVO op pensioen te sturen. Dit kan zo niet meer. Twitter of niet. Clown of niet. De woorden van een President hebben gevolgen.

Zo zijn we misschien op tijd om die gaspijpleiding tegen Amerikaanse agressie te beschermen? Want het lijkt er op dat we Amerikaanse bommen gaan krijgen op dat project als we hun idiote ongevraagde en ongewenste LNG niet in ons strot doorslikken.

Versionering van je product, dit opbergen in een versie controle systeem

Het komt uiteraard van één of ander forum. Maar ik wilde dit dus toch eventjes vereeuwigen in één van mijn fameuze ontzettend belangrijke blog posts. Zodat de ganse wereld het voor goed zou kunnen naslagen.

Het is ook belangrijk voor stel dat je me onder contract neemt. Zo weet je te minste wat deze zagevent continu in je organisatie zal zeggen tegen iedereen als advies hierover.

Dat doe ik dan vooral omdat ik best wel wat ervaring heb in wat niet werkt (dat vooral) en ook in wat wel werkt. Het is me opgevallen dat er erg veel geprobeerd wordt met alles waar ik ervaring in heb en waarvan ik weet dat het niet werkt.

Maarja. Iedere project manager wil bewijzen dat hij of zij dat kan bestieren dat wat bewezen is niet te werken. Op zichzelf is dat prima. Dan factureer ik als freelancer gewoon meer en langer geld uit zijn project-budget. Maar niettemin zal ik dus altijd het volgende adviseren:

Ze zouden misschien beter semver.org hanteren voor hun versie nummers. Dat maakt het voor andere techneuten eenvoudiger om te volgen:

0.0.z wil zeggen dat het eigenlijk nog niet gereleased is, maar nog volledig in ontwikkeling ligt. Die z ophogingen zijn incrementele stappen voor de ontwikkelaars zelf.

0.y.z wil net hetzelfde zeggen. Maar de ontwikkelaars zijn begonnen met het beoefenen van versionering. Je zou ook kunnen zeggen dat iedere y ophoging betekent dat er een test is gebeurd.

1.0.0 wil zeggen dat mensen buiten het eigen ontwikkel en test-team het product in gebruik (kunnen) nemen. Dat het getest is. Dat het werkt. Dat het stabiel is.

1.0.1 wil zeggen dat er 1 enkele bugfix was op 1.0.0 en dat er enkel die bugfix in zit.

1.0.2 wil zeggen dat er 2 zulke bugfixes zijn gedaan. Nadat 1.0.1 uitgebracht was.

1.1.0 wil zeggen dat er 1 extra feature is toegevoegd aan 1.0.0.

1.1.1 wil zeggen dat er 1 extra feature is toegevoegd aan 1.0.0 en dat er in die feature een bug zat. Of dat er een oud probleem in 1.0.0 zat en dat dat hersteld is. Maar dan brengt men naast de 1.1.1 ook een 1.0.3 uit. Die 1.0.3 heeft niet die 1 extra feature van 1.1.0 maar heeft dan wel de fix die 1.1.1 heeft, gebackport voor 1.0.0 (en eigenlijk voor 1.0.2)

2.0.0 wil zeggen dat er iets gewijzigd is aan de 1.y.z reeks dat projecten die afhankelijk er van zijn kan breken. Bv. een breaking API change (of een breaking ABI). Of men heeft iets weggehaald. Er is een grote wijziging geweest.

2.0.1 wil zeggen dat er 1 bug is gefixed in 2.0.0. 2.1.0 wil zeggen 1 feature toegevoegd aan 2.0.z.

Wil je dat nu in een systeem voor versiecontrole allemaal netjes bijhouden waarbij je de wijzigingen commit per commit en branch per branch en versie per versie en auteur per auteur wil kunnen vergelijken en opvragen, voor om het even welk soort documenten, dan gebruik je gitflow.