http://www.perlmonks.org?node_id=1200175

oha has asked for the wisdom of the Perl Monks concerning the following question:

Fellow monks,

I have to build a very small (128x32) black/white (no grayscale) image, which should contains small text (between 8 to 15 pixel height, the lower the better) that might have some unicode letters in it.

I need to be able to display clearly letters like Å, Æ and Ø (both uppercase and lowercase), but support for other languages like arabic, chinese and so on is welcome (I expect I will have to dynamically increase the font size in those cases)

So far I've tried GD, but I found no way to make it work with unicode letters, so I moved to something else.

Imager is far better, I make it work with unifont.tff, but either the fact that is a true-type font or that is not designed for be used so small, or because the lack of antialiasing, the results are not very easy to read, and too big anyways.

do you have suggestions about which font to use with Imager? or if I should change aproach?

thanks in advance

Replies are listed 'Best First'.
Re: Tiny font
by vr (Curate) on Sep 27, 2017 at 14:05 UTC
    use strict; use warnings; use utf8; use Imager; my $im = Imager -> new( xsize => 400, ysize => 200 ) -> box( filled => 1, color => 'white' ) -> to_paletted( make_colors => 'mono' ); my $text = 'JORDBÆR PÅ FLØDE (jordbær på fløde)'; my $y = 50; $im-> string( x => 50, y => $y += 25, string => $text, font => $_, color => 'black', size => 16 ) for map { Imager::Font-> new( file => $_, type => 'ft2' ) or die Imager-> errstr } ( 'unifont-10.0.06.ttf', 'ucs-fonts/6x9.bdf', 'ucs-fonts/6x12.bdf', 'ucs-fonts/8x13.bdf', ); $im-> write( file => 'test.png' );

    Wikipedia says, the original Unifont was bitmapped font 16 px high. Looks like vectorized to TTF with pixels literally becoming squares (united to rectangles where possible). So rendering, without AA, to any size other than 16, 32, 48, ... will not likely be pretty, especially to smaller sizes (try changing size, above).

    You can experiment with other TTF fonts and check if all required (now and in the future ;-) ) glyphs render w/o AA without ugly artifacts at chosen height. It's not totally impossible.

    Maybe better go with bitmapped fonts. Note: these are monospaced, + different sizes differ in Unicode coverage. 'Size' parameter appears to be ignored by Imager. + Complex glyphs are also not very beautiful at small sizes.

      Thanks you so much vr, Unifont requires a too big font size, i was hoping to stay between 8 to 12 pixels I found PixelOperator8.ttf as a very good compromise, so let me share my findings in case someone else need it:
      use strict; use warnings; use utf8; use Imager; use Data::Dumper; my $img = Imager->new(xsize=>128, ysize=>8); my $small = Imager::Font->new(file => 'PixelOperator/PixelOperator8.tt +f') or die; $small->align(string => "Færevåg Øst", size => 8, x => 0, y => 0, valign => 'top', image => $img); for (my $y=0; $y<8; $y++) { for (my $x=0; $x<128; $x++) { my $col = $img->getpixel(x=>$x, y=>$y); print [$col->rgba()]->[0] ? "O" : " "; } print "\n"; }
      OOOOO OOO OOO O + O O O O O + O O OOO OOO O OO OOO O O OOO OOOO O OO OOO + OOOO OOO O O O O O O O O O O O O O O O + O O OOOOOOOO OO OOOOO O O OOOO O O OO O OOO + O O O O O O O O O O OOOO O O O + O O OOO OOO O OOO O OOOO O O OOO OOOO + OOO OOO
      It is a very limited subset of unicode, but good enough for my case and i can fall back to unifont with bigger size conditionally. HTH

        Thanks for sharing your results, oha. Author of "PixelOperator8.ttf" has clearly put some effort into his font, so that at 8 px height text looks as good as it possibly ever can, probably. (See some comparisons below.) I just hope these 8 px are not modern display pixels, but something larger.

        Your link is to outdated (and named differently) version, which produces "somewhat" inferior result. Here is better link.

        Below is "screenshot" (produced in a way similar to what you did:

        say $im-> getscanline( y => $_, type => 'index' ) =~ tr/\0\1/O /r for 0 .. $im-> getheight - 1;

        , for "paletted" image 80x60, similar to what I did :) ), for these test subjects:

        '10714/8bitOperatorPlus8-Regular.ttf', # Abomination from wrong link 'pixel_operator/PixelOperator8.ttf', # Correct version 'ucs-fonts/6x12.bdf', # 'Fixed', from my answer above + 'uw-ttyp0-1.3/genbdf/t0-12-uni.bdf', # http://people.mpi-inf.mpg.de/ +~uwe/misc/uw-ttyp0/ 'gohufont-2.1/gohufont-uni-11.pcf', # http://font.gohu.org/

Re: Tiny font
by thanos1983 (Parson) on Sep 27, 2017 at 10:46 UTC

    Hello oha,

    I do not have much experience with Imager::Draw but I have found this "relevant question" Converting special characters with ImageMagick.

    Sample of your code that we could experiment would help a lot to reduce time and effort.

    Looking forward to your update, BR.

    Seeking for Perl wisdom...on the process of learning...not there...yet!
Re: Tiny font -- pixmap
by Discipulus (Abbot) on Sep 28, 2017 at 14:28 UTC
    Hello oha,

    I present not a solution but maybe a workaround or better a last resort: I used it some times ago writing small little files, each one consisting of only one char, using Gimp2 and exporting them as pixmaps: obviously they are not text but images but you can play with little or very little font size and use Gimp2's antialiasing.

    Below an example of a pixmap 32x32 (for sake of readability here) with the text:

    Å, Æ Ø

    using Sans with 10 pixel and antialiasing on. The image is grayscale. Gimp2's export -> pixmap gives you someting usable (the example is a ~3Kb image).

    my $pixmap = <<'EOFFONT'; /* XPM */ static char * Icon_xpm[] = { "32 32 68 1", " g None", ". g #FFFFFF", "+ g #6A6A6A", "@ g #0A0A0A", "# g #191919", "$ g #B4B4B4", "% g #1B1B1B", "& g #C9C9C9", "* g #000000", "= g #CECECE", "- g #989898", "; g #797979", "> g #313131", ", g #7A7A7A", "' g #323232", ") g #BABABA", "! g #F4F4F4", "~ g #DFDFDF", "{ g #F5F5F5", "] g #CBCBCB", "^ g #222222", "/ g #FEFEFE", "( g #939393", "_ g #5D5D5D", ": g #949494", "< g #656565", "[ g #858585", "} g #FDFDFD", "| g #232323", "1 g #F2F2F2", "2 g #0C0C0C", "3 g #ACACAC", "4 g #393939", "5 g #ADADAD", "6 g #525252", "7 g #B8B8B8", "8 g #F8F8F8", "9 g #F7F7F7", "0 g #6D6D6D", "a g #161616", "b g #1E1E1E", "c g #9D9D9D", "d g #535353", "e g #787878", "f g #444444", "g g #E3E3E3", "h g #E2E2E2", "i g #181818", "j g #1F1F1F", "k g #D5D5D5", "l g #838383", "m g #929292", "n g #090909", "o g #F9F9F9", "p g #A1A1A1", "q g #A0A0A0", "r g #303030", "s g #999999", "t g #D3D3D3", "u g #202020", "v g #E4E4E4", "w g #424242", "x g #FCFCFC", "y g #545454", "z g #9C9C9C", "A g #1D1D1D", "B g #151515", "C g #6C6C6C", "................................", "................................", "................................", "................................", "................................", "......+@+.......................", "......#$%.......................", "......&*=..........-******......", "......;>,..........')*..........", ".....!%~%{........]^/*..........", ".....(_._:........<[.*****......", "....}^***|}......12***..........", "....34...45......-6..*..........", "....45...54.*....'7..*****......", "............*...................", "................................", "................................", "................8...............", "...........90abcd/..............", "...........efghi(...............", "...........jk.lm'...............", "...........nopq9@...............", "...........rsl.tu...............", "...........(#vgw,...............", "..........xyzABC9...............", "................................", "................................", "................................", "................................", "................................", "................................", "................................"}; EOFFONT

    Please note that gimp2 normally use a full path to the file like:

    static char * /path/to/small_fonts2_xpm[] = {

    as second line of the file.

    L*

    There are no rules, there are no thumbs..
    Reinvent the wheel, then learn The Wheel; may be one day you reinvent one of THE WHEELS.
Re: Tiny font
by Your Mother (Archbishop) on Sep 28, 2017 at 18:41 UTC

    Your requirements sound broken to me. You will never be able to reasonably display Chinese at 8 pixels, for example. If it's a display space issue, it would be better to double or triple typeface sizes and provide some kind of display scrolling mechanism.

    If I were doing this, I would search for "best unicode font" and experiment with what is available. A good TrueType font will contain hinting which attempts to make display more legible by adjusting weights and corners and such at different sizes. That means a complete rerender for any size change though and it is dependent on the hinting being any good. There are a lot of wonderful open source fonts that were not produced by typographers or were converted from one format to another so they have shortcomings like bad, missing kerning pairs or lousy automatic hinting.

Re: Tiny font
by ForgotPasswordAgain (Priest) on Sep 28, 2017 at 18:00 UTC
    Not sure if this will be useful, but I encountered this Font Maker when playing with an Arduino TFT display. It relates to this Perl script, which might provide some inspiration. (The script generates a C array for the data of the font, starting from a font image.) Good luck!