font.go 3.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145
  1. // Copyright ©2021 The star-tex Authors. All rights reserved.
  2. // Use of this source code is governed by a BSD-style
  3. // license that can be found in the LICENSE-STAR-TEX file.
  4. package pkf
  5. import (
  6. "fmt"
  7. "io"
  8. "modernc.org/knuth/font/fixed"
  9. "modernc.org/knuth/internal/iobuf"
  10. )
  11. // Units are an integral number of abstract, scalable "font units". The em
  12. // square is typically 1000 or 2048 "font units". This would map to a certain
  13. // number (e.g. 30 pixels) of physical pixels, depending on things like the
  14. // display resolution (DPI) and font size (e.g. a 12 point font).
  15. type Units int32
  16. // Font is a Packed Font.
  17. type Font struct {
  18. hdr CmdPre
  19. glyphs []Glyph
  20. }
  21. // Parse parses a Packed Font file.
  22. func Parse(r io.Reader) (*Font, error) {
  23. raw, err := io.ReadAll(r)
  24. if err != nil {
  25. return nil, err
  26. }
  27. rr := iobuf.NewReader(raw)
  28. if opCode(rr.PeekU8()) != opPre {
  29. return nil, fmt.Errorf("pkf: invalid PK header")
  30. }
  31. var fnt Font
  32. fnt.hdr.read(rr)
  33. specials:
  34. for {
  35. op := opCode(rr.PeekU8())
  36. if op < opXXX1 || op == opPost {
  37. break specials
  38. }
  39. switch op {
  40. case opXXX1, opXXX2, opXXX3, opXXX4:
  41. op.cmd().read(rr)
  42. case opYYY:
  43. op.cmd().read(rr)
  44. case opNOP:
  45. op.cmd().read(rr)
  46. case 247, 248, 249, 250, 251, 252, 253, 254, 255:
  47. return nil, fmt.Errorf("pkf: unexpected PK flagbyte 0x%x (%d)", op, op)
  48. }
  49. }
  50. loop:
  51. for {
  52. op := opCode(rr.PeekU8())
  53. switch op {
  54. case opPost:
  55. break loop
  56. case opNOP:
  57. op.cmd().read(rr)
  58. case opXXX1, opXXX2, opXXX3, opXXX4:
  59. op.cmd().read(rr)
  60. case opYYY:
  61. op.cmd().read(rr)
  62. default:
  63. switch {
  64. case op < opXXX1:
  65. glyph, err := readGlyph(rr)
  66. if err != nil {
  67. return nil, err
  68. }
  69. fnt.glyphs = append(fnt.glyphs, glyph)
  70. default:
  71. return nil, fmt.Errorf("pkf: invalid opcode 0x%x (%d)", op, op)
  72. }
  73. }
  74. }
  75. return &fnt, nil
  76. }
  77. // UnitsPerEm returns the number of units per em for that font.
  78. func (fnt *Font) UnitsPerEm() Units {
  79. // FIXME(sbinet): extract or infer from TFM.body.param ?
  80. return 1000
  81. }
  82. // DesignSize returns the TFM/PK font's design size.
  83. func (fnt *Font) DesignSize() fixed.Int12_20 {
  84. return fixed.Int12_20(fnt.hdr.Design)
  85. }
  86. // Checksum returns the PK font checksum of that font.
  87. // Checksum should be equal to the TFM checksum.
  88. func (fnt *Font) Checksum() uint32 {
  89. return fnt.hdr.Checksum
  90. }
  91. // NumGlyphs returns the number of glyphs in this font.
  92. func (fnt *Font) NumGlyphs() int {
  93. return len(fnt.glyphs)
  94. }
  95. // GlyphAt returns the i-th glyph from the PK font.
  96. func (fnt *Font) GlyphAt(i int) *Glyph {
  97. if i < 0 || len(fnt.glyphs) <= i {
  98. return nil
  99. }
  100. return &fnt.glyphs[i]
  101. }
  102. // Glyph returns the glyph corresponding to the provided rune r,
  103. // or nil if it is not present in the PK font.
  104. func (fnt *Font) Glyph(r rune) *Glyph {
  105. g, ok := fnt.glyph(r)
  106. if !ok {
  107. return nil
  108. }
  109. return g
  110. }
  111. func (fnt *Font) index(r rune) int {
  112. for i := range fnt.glyphs {
  113. if fnt.glyphs[i].code == uint32(r) {
  114. return i
  115. }
  116. }
  117. return -1
  118. }
  119. func (fnt *Font) glyph(r rune) (*Glyph, bool) {
  120. for i := range fnt.glyphs {
  121. g := &fnt.glyphs[i]
  122. if g.code == uint32(r) {
  123. return g, true
  124. }
  125. }
  126. return nil, false
  127. }