sbix.py 4.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141
  1. import struct
  2. import sys
  3. from lxml.etree import Element
  4. from data import Tag, BFlags
  5. from transform.bytes import generateOffsets, outputTableBytes
  6. class sbixBitmap:
  7. """
  8. Class representing a single bitmap within a strike within an sbix table.
  9. """
  10. def __init__(self, glyph, ppem):
  11. self.name = glyph.name()
  12. self.originOffsetX = 0 # hard-coded for now
  13. self.originOffsetY = 0 # hard-coded for now
  14. self.graphicType = Tag("png ") # hard-coded for now
  15. # image data (if any)
  16. # sbix does have bitmap entries with no bitmap data, for non-pr
  17. if glyph.imgDict:
  18. self.img = glyph.imgDict["png-" + str(ppem)]
  19. else:
  20. self.img = None
  21. def toTTX(self):
  22. if not self.img:
  23. return Element("glyph", {"name": self.name })
  24. else:
  25. sbixBitmap = Element("glyph", {"name": self.name
  26. ,"graphicType": str(self.graphicType)
  27. ,"originOffsetX": str(self.originOffsetX)
  28. ,"originOffsetY": str(self.originOffsetY)
  29. })
  30. hexdata = Element("hexdata")
  31. hexdata.text = self.img.getHexDump()
  32. sbixBitmap.append(hexdata)
  33. return sbixBitmap
  34. def toBytes(self):
  35. metadata = struct.pack( ">hh4s"
  36. , self.originOffsetX # Int16
  37. , self.originOffsetY # Int16
  38. , self.graphicType.toBytes() # Tag (4 bytes/UInt32)
  39. )
  40. if self.img is None:
  41. return metadata
  42. else:
  43. return metadata + self.img.getBytes()
  44. # TODO: figure out if you need to make some sort of big-endian version of this.
  45. class sbixStrike:
  46. """
  47. Class representing a single strike within an sbix table.
  48. - https://docs.microsoft.com/en-gb/typography/opentype/spec/sbix#strikes
  49. - https://developer.apple.com/fonts/TrueType-Reference-Manual/RM06/Chap6sbix.html
  50. """
  51. def __init__(self, ppem, glyphs):
  52. self.ppem = ppem
  53. self.ppi = 72 # hard-coded for now
  54. self.bitmaps = []
  55. # number of glyphs are determined from maxp table.
  56. for g in glyphs:
  57. self.bitmaps.append( sbixBitmap(g, ppem) )
  58. def toTTX(self):
  59. strike = Element("strike")
  60. strike.append(Element("ppem", {"value": str(self.ppem) }))
  61. strike.append(Element("resolution", {"value": str(self.ppi) }))
  62. for bitmap in self.bitmaps:
  63. strike.append(bitmap.toTTX())
  64. return strike
  65. def toBytes(self):
  66. strikeMetadata = struct.pack ( ">HH"
  67. , self.ppem # UInt16
  68. , self.ppi # UInt16
  69. )
  70. bitmapBytes = generateOffsets(self.bitmaps, 32, 4) # long offsets (UInt32)
  71. # TODO: there's meant to be an extra offset in glyphDataOffsets. it's unclear what that is.
  72. return strikeMetadata + bitmapBytes["offsetBytes"] + bitmapBytes["bytes"]
  73. class sbix:
  74. """
  75. Class representing an sbix table.
  76. """
  77. def __init__(self, glyphs):
  78. self.version = 1 # hard-coded
  79. self.flags = BFlags("10000000 00000000") # hard-coded
  80. self.strikes = []
  81. # iterate over each strike.
  82. for imageFormat, image in glyphs["img"][0].imgDict.items():
  83. if imageFormat.split('-')[0] == "png":
  84. self.strikes.append( sbixStrike(image.strike, glyphs["img_empty"]) )
  85. def toTTX(self):
  86. sbix = Element("sbix")
  87. sbix.append(Element("version", {"value": str(self.version) })) # hard-coded
  88. sbix.append(Element("flags", {"value": self.flags.toTTXStr() })) # hard-coded
  89. for strike in self.strikes:
  90. sbix.append(strike.toTTX())
  91. return sbix
  92. def toBytes(self):
  93. header = struct.pack( ">H2sI"
  94. , self.version # UInt16
  95. , self.flags.toBytes() # 2 bytes/UInt16
  96. , len(self.strikes) # UInt32
  97. )
  98. strikeBytes = generateOffsets(self.strikes, 32, 8) # long offsets (UInt32)
  99. return outputTableBytes(header + strikeBytes["offsetBytes"] + strikeBytes["bytes"])