Length of a string in a specific font

Viewed 67

I am trying to calculate the length of a string in a a font. The goal is to create texts like:

DD  Qui n'a pu l'obtenir ne le méritait pas.
LC  Ne le méritait pas! Moi?    
DD                           Vous!  
LC                                 Ton impudence    
    Téméraire vieillard, aura sa récompense

In a fixed font (as above), this is easy. For proportional fonts, I can make an acceptable approximation of the previous line length with:

use Tk;
use Tk::Font;

my $mw = MainWindow->new();
my $font = $mw->fontCreate( 'TimesNew Roman' );

sub timesspace {
    (my $text)=@_;
    my $retval= $font->measure( $text );
    return int($retval);
}

(a reasonable estimation, because the Tk fonts are not exactly the same as the output-processor's font).

This however, requires a graphical environment (because I create a Tk window), even though al the scripts are CLI only. This leads to:

couldn't connect to display "localhost:11.0" at /usr/local/lib64/perl5/Tk/MainWindow.pm line 53.
MainWindow->new() at /usr/local/bin/xml3roff line 10.

Is there a reliable and simple way of finding the length of a string in Times/Helvetica/... without the need of such an environment?

2 Answers

Looks like Font::AFM might be what you're after, but it relies on Adobe Font Metrics (AFM) files which may not be installed on your system.

Alternatively, you could try to approximate it like this answer.

Based on Roughly approximate the width of a string of text in Python? as referred to by @Joshua, I created my own estimation of the font sizes.

The Adobe Font Metrics are indeed not generally available. As a reference, I used the groff fonts that should be available on most Linux systems. There are some issues with the glyph names and the representations. My input texts contain é, ü etc. and the font file contains 'e, :u etcetera. So, I use groff to generate a table that is parsable for me:

#!/bin/bash
tmpfont=tmpfont

font=$(basename $1)
if [ ! -f "/usr/share/groff/current/font/devps/$font" ] ; then
    echo "No suchfont $font"
    exit
fi

cp "/usr/share/groff/current/font/devps/$font" $tmpfont
sed -i '1,/charset/d' $tmpfont
sed -i 's/,.*//' $tmpfont
sed -i 's/^\(..\)\t/\\(\1  /'  $tmpfont
sed -i 's/^\(...\)\t/\\[\1]  /'  $tmpfont
sed -i '/^\(.....*\)\t/d'  $tmpfont
sed -i '/"/d'  $tmpfont
sed -i 'a .br'  $tmpfont
groff $tmpfont > $tmpfont.ps

ps2ascii $tmpfont.ps  | sed 's/ //g;s/^\(.\)/\1 /'>$font.measure

And to determine the width of the string I use:

#!/usr/bin/perl
use strict;


my %fontmeasure;

if (open ( my $FONT,'<',"TI.measure")){
    while (<$FONT>){
        chomp;
        (my $char,my $width)=split '    ';
        $fontmeasure{$char}=$width;
    }
    close $FONT;
}
else {
    die "Can't open font measure";
}
$fontmeasure{' '}=250;

my @str;
while (<>){
    undef @str;
    @str=split (//);
    my $total=0;
    for (@str){
        if (defined($fontmeasure{$_})){
            $total=$total+$fontmeasure{$_};
        }
        else {
            $total=$total+250;
        }
    }
    print "$total\n";
}

For those interested: the text given in the question will produce: enter image description here

Related