1

I am working on a Go library that reads a .dvi file and produces a .png file. I have already the packages that find+parse .tfm and .pk files working: I have the pk2bm, tfm2pl and dvitype equivalent executables producing a bit-to-bit compliant output with the ones from tex-live.

now, I would like to be able to replicate the functionality of dvipng in Go (and I'd like to keep out of the sources of dvipng to keep my packages licensed under BSD-3.)

from first principles, I think I managed to correctly extract the bitmap data from the .pk file and display each glyph, but I am not sure whether:

  • I have correctly scaled them
  • I have a correctly scaled bounding box
  • I have a correctly scaled horizontal advance value.

here is what I get, trying to display the string VA (outside the dvi "machinery", just getting the V and A glyphs, taking their bounding boxes, drawing the bitmap of each glyph, and then moving the "dot" with the glyph-advance (I haven't included kerning just yet)):

enter image description here

obviously, my handling of the width of each glyph is subpar.

Let's say I have the following tfm package:

package tfm

type fileBody struct { header header glyphs []glyphInfo width []fixed.Int12_20 height []fixed.Int12_20 depth []fixed.Int12_20 italic []fixed.Int12_20 ligKern []ligKernCmd kern []fixed.Int12_20 exten []extensible param []fixed.Int12_20 }

type header struct { chksum uint32 designSize fixed.Int12_20 codingScheme string fontID string sevenBitSafe bool face byte

    extra []fixed.Int12_20

}

type glyphInfo struct { raw [4]uint8 }

and the following pk font package:

package pkf

// Font is a Packed Font. type Font struct { hdr CmdPre glyphs []Glyph }

type CmdPre struct { Version uint8 Msg string Design uint32 Checksum uint32 Hppp uint32 Vppp uint32 }

// Glyph represents a glyph contained in a PK font file. type Glyph struct { flag uint8 code uint32 // character code wtfm uint32 // TFM width dx uint32 // horizontal escapement dy uint32 // vertical escapement width uint32 // width in pixels of the minimum bounding box height uint32 // height in pixels of the minimum bounding box hoff int32 // horizontal offset from the upper left pixel voff int32 // vertical offset from the upper left pixel

    bitmap []byte // decoded bitmap data

}

how should I scale all of these, when starting with "pk/ljfour/public/cm/dpi600/cmr10.pk" and "tfm/public/cm/cmr10.tfm" as inputs, and trying to draw VAp into a ((0,0) , (500, 250)) PNG image? (with a 'dot' at (0, 100))

(and I'd like to scale this to a 10pt + 600DPI image.)

Marijn
  • 37,699
sbinet
  • 41
  • 3
  • Have you looked at the output of dvitype? Does it help? – ShreevatsaR Sep 11 '21 at 20:10
  • It's been a while so I've forgotten, but see if something here helps: https://tex.stackexchange.com/questions/360137/how-to-generate-a-bitmap-using-tex-with-bitmap-fonts – ShreevatsaR Sep 11 '21 at 20:22
  • @ShreevatsaR yeah, I managed to do something along these lines (using "my" version of dvitype (called dvi-dump)). it only works for the "natural" DPI of the pk font as I don't have the tooling to rescale the pk font for the user's input. – sbinet Sep 13 '21 at 17:40
  • see the output: hello.png and xcolor.png – sbinet Sep 13 '21 at 17:46
  • Ah cool... Instead of rescaling the pk font, the ultimate ideal AFAICT would be to run Metafont at the desired resolution: this may be impractical as it may result in too much computation or disk space (one set of pk fonts for each desired resolution, like 600pk, 500pk, …), but then again that may be ok. – ShreevatsaR Sep 13 '21 at 19:33
  • yes, ultimately, having a Go-based solution for Metafont would be great. but I think I'll direct my meager development workforce towards type1 support and climb up the ladder of deprecated fonts :) – sbinet Sep 14 '21 at 07:32

1 Answers1

2

ok... progress. it's not completely satisfying, but -- unless somebody has more insight -- I'll try to make do with this.

I've converted lmroman10-regular.otf to pk:

$> ttf2tfm lmroman10-regular.otf -q -T T1-WGL4.enc \
  -v lmroman10-regular.vpl                         \
  lmroman10-regular.tfm  >> ./ttfonts.map
$> ttf2pk lmroman10-regular 600

to be sure I was comparing apples to apples.

then, after trial and error, and looking at what the advance+glyph-bounds of A in the .otf file should look like, I got:

i := tfmFile.indexOf('A') // retrieve the glyph index for 'A'
w := tfmFile.width[i] + tfmFile.italic[i] // take into account italic correction
advance := int26_6_From_Int12_20(w) // Go fonts work with 26.6 fixed point ints
advance *= pkFont.UnitsPerEm() // 1000 for most (all?) PK fonts
advance /= 1<<6                // some more 26.6 fiddling?

advance = rescale(pkFont.scale * advance, pkFont.UnitsPerEm())

where:

// rescale returns x divided by unitsPerEm, rounded to the nearest fixed.Int26_6
// value (1/64th of a pixel).
func rescale(x fixed.Int26_6, unitsPerEm int32) fixed.Int26_6 {
        if x >= 0 {
                x += fixed.Int26_6(unitsPerEm) / 2
        } else {
                x -= fixed.Int26_6(unitsPerEm) / 2
        }
        return x / fixed.Int26_6(unitsPerEm)
}

and pkFont.scale is:

opts.Size = tfmFile.DesignSize().AsFloat64()
opts.DPI = 600.0
pkFont.scale = fixed.Int26_6(0.5 + (opts.Size * opts.DPI * 64 / 72))

in the end, I get, for the lmroman10-regular.otf (with the bounding boxes drawn around each glyph): enter image description here

and for the lmroman10-regular.600pk file produced following the instructions above:

enter image description here

there are some differences. but I'll take this as a win.

sbinet
  • 41
  • 3