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

Hello fellow monks. I am trying to build a custom PCL downloadable soft font. This is a series of PCL commands which describe how to render each ASCII character in a font set, which can be transferred to modern LaserJet compatible printers and augment their built-in font capabilities. The PCL involved is very tightly packed, to the point where several font characteristics are combined into 2 bytes. For example, the "posture" (upright, italic, etc) and the "structure" (solid, outline, shadow, etc) are combined across 2 bytes. Here is a short diagram:
| Style MSB | Style LSB | |15 9 | 4 1 0| ----------------------------------------- | reserved |Structure|App wid|Pos| So, reading from right to left, bits 0 and 1 = Posture (poss values 0-3) bits 2-4 = Appearance Width (0-7) bits 5-9 = Structure (0-31) bits 10-15 = Reserved (locked to value of 0)
Well, I understand binary math and what I *want* the final 16 bit thingy to look like. I'm still fuzzzy on how to use pack() to achieve this. If, for example:
posture=2 (10) app_width=5 (101) structure=6 (00110) then the bits should look like: 000000 00110 101 10 or, without the spaces: 0000000011010110
I've tried
my $style_word=pack(b2 b3 b5 b6),$pos,$aw,$stru,0;
but that doesn't work. Should I be using b or B as a template code? Should I be using regular integer values like 2, 5 and 6 or should I be packing those into some other bitstring first? Worse yet, I'm at a loss on how to debug this thing and see an understandable representation of what's really getting put into $sw. I'd love to see a series of 0's and 1's which represent the individual bits of this 16 bit word. Then I could be sure what I'm putting into the structure before writing it to disk. Any help would be greatly appreciated.

Shelob

When there is no wind, row.

Replies are listed 'Best First'.
Re: using pack() to build a 16 bit word
by Roy Johnson (Monsignor) on May 11, 2004 at 21:12 UTC
    To pack using bitstrings, you have to have bitstrings (strings of 1s and 0s), not decimal numbers. You can get bitstrings from sprintf:
    my ($pos, $aw, $stru) = (2, 5, 6); my $bits = sprintf('%06b%05b%03b%02b', 0, $stru, $aw, $pos); print "Bits=<$bits>\n"; my $bstr=pack('b16', $bits); printf "<$bstr> is %d bytes\n", length($bstr); print "\nUnpack: ", unpack('B*', $bstr), "\n";
    If you use format strings like 'b6 b5 b3 b2', each unit will be padded out to full byte width, rather than being all jammed together.

    The PerlMonk tr/// Advocate
      Note that whitespace in bitstrings is irrelevant, so if it makes your debugging easier to do, you can always insert some spaces in that sprintf line.
      -- @/=map{[/./g]}qw/.h_nJ Xapou cets krht ele_ r_ra/; map{y/X_/\n /;print}map{pop@$_}@/for@/
Re: using pack() to build a 16 bit word
by Ven'Tatsu (Deacon) on May 11, 2004 at 19:07 UTC
    Use bitshifts and bitwise or to get all the values into one number then pack them at once.
    pack('n', $stru << 5 | $aw << 2 | $pos)
    You can also throw a bitwise and in there to make sure values can't overflow their bit fields
    pack('n', ($stru & 31) << 5 | ($aw & 7) << (2 & 3) | $pos)
    I'm guessing it's big-endian, if not use v in pack instead of n
Re: using pack() to build a 16 bit word
by jmcnamara (Monsignor) on May 11, 2004 at 20:21 UTC

    Here is one way to do it:
    #!/usr/bin/perl -wl use strict; my $posture = 2; my $app_width = 5; my $structure = 6; my $style_word = $posture; $style_word |= $app_width << 2; $style_word |= $structure << 5; $style_word = pack 'n', $style_word; print unpack "B*", $style_word; __END__ Prints: 0000000011010110
    You can also use the %b format in sprintf to debug the unpacked integer (perl5.6 and later):
    my $style_word2 = $posture | $app_width << 2 | $structure << 5; printf "%016b\n", $style_word2;

    --
    John.