Pick a font able to properly render a string composed of Unicode characters with Perl

In the case of automated watermarking with randomly picked fonts within a Perl script, it is quite annoying to stumble on fonts missing many non-basic unicode characters (accents, etc). In French, you’ll likely miss the ê or ü or even é or à. In Polish, while the ł is often provided, you’ll like miss ź.

The Perl module Font::FreeType is quite convenient in this regard. The sample code here will try to find a font, within the @fonts list, able to render the $string.  It will pick the fonts randomly, one by one, and check every character of the string against the characters provided by the font. It will stop to pick the first one that actually can fully render the string:

use Font::FreeType;
use utf8; # must occur before any string definition!
use strict;

my @image_tags = "~ł ajàüd&é)=ù\$;«~źmn";
my @fonts = ("/usr/share/fonts/truetype/ttf-bitstream-vera/Vera.ttf", "/usr/share/fonts/truetype/zeppelin.ttf", "/usr/share/fonts/truetype/Barrio-Regular.ttf");
my %fonts_characters;
my $watermark_font;

# we want a random font: but we also want a font that can print every character
# (not obvious with utf8)
# loop until we find a suitable one (all chars are valid, so the chars counter reached 0) or,
# worse case scenario, until we checked them all (means more suitable fonts should be added)
my $chars_to_check = length("#".@image_tags[0]);
my $fonts_to_check = scalar(@fonts);
my %fonts_checked;
while ($chars_to_check > 0 and $fonts_to_check > 0) {

 # pick a random font
 srand();
 $watermark_font = $fonts[rand @fonts];
 
 # if this font was already probed, pick another one
 next if $fonts_checked{$watermark_font};
 $fonts_checked{$watermark_font} = 1; 

 # always reset the chars counter each time we try a font
 $chars_to_check = length("#".@image_tags[0]);
 
 print "Selected font $watermark_font (to check: $fonts_to_check)\n";
 
 # if not yet already, build list of available chars with this font
 unless ($fonts_characters{$watermark_font}) {
 Font::FreeType->new->face($watermark_font)->foreach_char(
 sub {
 my $char_chr = chr($_->char_code);
 my $char_code = $_->char_code;
 $fonts_characters{$watermark_font}{$char_chr} = $char_code;
 });
 print "Slurped $watermark_font chars\n";
 }
 
 # then check if every available character of the watermark exists in this font
 for (split //, "#".@image_tags[0]) {
 print "Check $_\n";
 # breaks out if missing char
 last unless $fonts_characters{$watermark_font}{$_};
 # otherwise decrement counter of chars to check: if we reach 0, they are all valid
 # and we should get out of the font picking loop 
 $chars_to_check--;
 print "Chars still to check $chars_to_check\n";
 }
 
 # we also record there is one less font to check
 $fonts_to_check--;
 
}


print "FONT PICKED $watermark_font\n";

This code is actually included in my post-image-to-tumblr.pl script (hence the variables name).

Obviously, if no font is suitable, it’ll take the last one tested. It won’t go as far as comparing which one is the most suitable, since in the context of this script, if no fonts can fully render a tag, the only sensible course is to add more (unicode capable) fonts to the fonts/ directory.

Advertisements

Add proportional label/watermark to images with ImageMagick

There is nothing exceptional here, there are many documented ways to achieve this.

Still, follows the most efficient way I found to do so on any images within a directory, after testing quite a few: I wanted to be able to add a small tag on top, size being propertional to the image (that excluded many solutions based on -pointsize), on static images as well as animated ones.

#!/bin/bash
rm -f *marked*

for pic in *.jpg *.png *.gif; do
   ext="${pic##*.}"
   # identify is messed up with gif (give width per frame), use convert
   # instead
   width=$(convert $pic -print "%w" /dev/null)
   height=$(expr 5 \* $width \/ 100)

   # first build a watermark so we can easily manipulate it is size afterwards
   convert -size ${width}x${height} -gravity NorthEast -stroke "#454545" -fill "#c8c8c8" -background transparent -strokewidth 1 -font LiberationSerif-Bold.ttf label:"$pic" ${pic//.$ext}-mark.png
 
   # then add it
   convert $pic -coalesce -gravity NorthEast null: ${pic//.$ext}-mark.png -layers composite -layers optimize ${pic//.$ext}-marked.$ext

   rm ${pic//.$ext}-mark.png
done

Within a perl script, it would be something like (beware, most variables were set earlier in the script):

    # build watermark (for now minimal checks, assume files are regular) 
    my ($watermark_h, $watermark) = tempfile(SUFFIX => ".png", 
					     UNLINK => $unlink_temp);
    binmode($watermark_h);
    my $watermark_width = $$image_info{"ImageWidth"};
    my $watermark_height = $watermark_proportion * $watermark_width / 100;
    system($convert,
	   "-size", $watermark_width."x".$watermark_height,
	   "-gravity", "NorthEast",
	   "-stroke", "#454545",
	   "-fill", "#c8c8c8",
	   "-background", "transparent",
	   "-strokewidth", "1",
	   "-font", $watermark_font,
	   "label:#".@image_tags[0],
	   $watermark);

    # add watermark
    system($convert,
	   $altered_image,
	   "-coalesce",
	   "-gravity", "NorthEast",
	   "null:",
	   $watermark,
	   "-layers", "composite",
	   "-layers", "optimize",
	   $altered_image);    
}

Yeah, so that does mean that my script post-image-to-tumblr.pl managing a tumblr posts-queue locally with #Tags was updated to add these #Tags as watermark/label. Note that it won’t alter the original file, only the one posted.

New options can be added in ~/.tumblrrc:

# no watermark/label
no_watermark 

# specific font
watermark_font=/path/to/font

Note that it requires ImageMagick to be installed.

Add alphanumeric prefixes to files inside a directory that serves as queue

In case, as in my previous article about post-image-to-tumblr.pl example, you use a directory as queue, you may want an easy way to rename files.

For instance, if you have files like shot001.png, shot003.png, shot012.png, whenever you want to insert a file at a specific position in the queue, you are forced to rename it to something like shotXXX.png; you may even have to rename other files.

So this qrename.pl script add a prefix like CCC5—$file in front until it reaches WWW… using 7 differents characters only, so it’s really easy to insert files anywhere. If it reaches WWW, then it’ll use the form WWWNNN5—, NNN being a three digits counter. You can set how many digits you want with the option —max-queue-digits so you virtually can manage queue with as many files as you want (however unpractical that could actually be). It works on the current directory, only on regular files, and actually does not do anything unless you set the option –please-do in order to avoid any accidental mess.