os2.py 9.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235
  1. import struct
  2. from lxml.etree import Element
  3. from data import BFlags
  4. from tables.common.os2Extra import PANOSE
  5. from transform.bytes import outputTableBytes
  6. class OS2:
  7. """
  8. Class representing an OS/2 table.
  9. """
  10. def __init__(self, m, glyphs):
  11. # PREPARE SOME OF THE DATA
  12. # --------------------------
  13. metrics = m['metrics']
  14. singleCodepoints = []
  15. twoByte = []
  16. # the only bit in ulUnicodeRange that's *really* necessary to set.
  17. supplementaryPlane = False
  18. for g in glyphs['all']:
  19. if g.codepoints.seq[0] >= int('0x10000', 16) and g.codepoints.seq[0] <= int('0x10ffff', 16):
  20. supplementaryPlane = True
  21. if len(g.codepoints.seq) == 1:
  22. singleCodepoints.append(g.codepoints.seq[0])
  23. if g.codepoints.seq[0] < int('ffff', 16):
  24. twoByte.append(g.codepoints.seq[0])
  25. usFirstCharIndex = min(twoByte)
  26. usLastCharIndex = max(twoByte)
  27. # STORE DATA
  28. # --------------------------
  29. self.version = 5 # hard-coded, the current (also the latest) version for this table generation is 5.
  30. self.xAvgCharWidth = metrics['xMax']
  31. self.usWeightClass = 500 # hard-coded for now. This is ideal for emoji.
  32. self.usWidthClass = 5 # hard-coded for now. This is ideal for emoji.
  33. self.fsType = BFlags('00000000 00000000') # hard-coded. must agree with head.macStyle
  34. self.ySubscriptXSize = metrics['OS2ySubscriptXSize']
  35. self.ySubscriptYSize = metrics['OS2ySubscriptYSize']
  36. self.ySubscriptXOffset = metrics['OS2ySubscriptXOffset']
  37. self.ySubscriptYOffset = metrics['OS2ySubscriptYOffset']
  38. self.ySuperscriptXSize = metrics['OS2ySuperscriptXSize']
  39. self.ySuperscriptYSize = metrics['OS2ySuperscriptYSize']
  40. self.ySuperscriptXOffset = metrics['OS2ySuperscriptXOffset']
  41. self.ySuperscriptYOffset = metrics['OS2ySuperscriptYOffset']
  42. self.yStrikeoutSize = metrics['OS2yStrikeoutSize']
  43. self.yStrikeoutPosition = metrics['OS2yStrikeoutPosition']
  44. self.sFamilyClass = 5 # hard-coded for now. This is ideal for emoji.
  45. self.panose = PANOSE(2, 0, 6, 9, 0, 0, 0, 0, 0, 0) # hardcoded for now. This is ideal for emoji.
  46. self.ulUnicodeRange1 = BFlags('00000000 00000000 00000000 00000000')
  47. self.ulUnicodeRange2 = BFlags('00000000 00000000 00000000 00000000')
  48. self.ulUnicodeRange2.set(57-32, int(supplementaryPlane)) # set bit 57 based on whether there are codepoints in SPUA or not.
  49. self.ulUnicodeRange3 = BFlags('00000000 00000000 00000000 00000000')
  50. self.ulUnicodeRange4 = BFlags('00000000 00000000 00000000 00000000')
  51. self.achVendID = m['metadata']['OS2VendorID']
  52. self.fsSelection = BFlags('00000000 00000000') # hard-coded
  53. self.usFirstCharIndex = usFirstCharIndex
  54. self.usLastCharIndex = usLastCharIndex
  55. self.sTypoAscender = metrics['yMax']
  56. self.sTypoDescender = metrics['yMin'] # this should be this way based on validators and best practices.
  57. self.sTypoLineGap = 0 # hard-coded based on best practices.
  58. self.usWinAscent = metrics['yMax']
  59. self.usWinDescent = (- metrics['yMin']) # this should be the way it is (-yMin): https://docs.microsoft.com/en-us/typography/opentype/spec/os2#uswindescent
  60. self.ulCodePageRange1 = BFlags('00000000 00000000 00000000 00000000')
  61. self.ulCodePageRange2 = BFlags('00000000 00000000 00000000 00000000')
  62. self.sxHeight = 0 # leaving it hard-coded at 0 for now.
  63. self.sCapHeight = metrics['yMax']
  64. self.usDefaultChar = 0
  65. self.usBreakChar = 0x20
  66. self.usMaxContent = 1
  67. self.usLowerOpticalPointSize = 0
  68. self.usUpperOpticalPointSize = 0
  69. def toTTX(self):
  70. os2 = Element("OS_2")
  71. os2.append(Element("version", {'value': str(self.version) }))
  72. os2.append(Element("xAvgCharWidth", {'value': str(self.xAvgCharWidth) }))
  73. os2.append(Element("usWeightClass", {'value': str(self.usWeightClass) }))
  74. os2.append(Element("usWidthClass", {'value': str(self.usWidthClass) }))
  75. os2.append(Element("fsType", {'value': self.fsType.toTTXStr() }))
  76. os2.append(Element("ySubscriptXSize", {'value': str(self.ySubscriptXSize) }))
  77. os2.append(Element("ySubscriptYSize", {'value': str(self.ySubscriptYSize) }))
  78. os2.append(Element("ySubscriptXOffset", {'value': str(self.ySubscriptXOffset) }))
  79. os2.append(Element("ySubscriptYOffset", {'value': str(self.ySubscriptYOffset) }))
  80. os2.append(Element("ySuperscriptXSize", {'value': str(self.ySuperscriptXSize) }))
  81. os2.append(Element("ySuperscriptYSize", {'value': str(self.ySuperscriptYSize) }))
  82. os2.append(Element("ySuperscriptXOffset", {'value': str(self.ySuperscriptXOffset) }))
  83. os2.append(Element("ySuperscriptYOffset", {'value': str(self.ySuperscriptYOffset) }))
  84. os2.append(Element("yStrikeoutSize", {'value': str(self.yStrikeoutSize) }))
  85. os2.append(Element("yStrikeoutPosition", {'value': str(self.yStrikeoutPosition) }))
  86. os2.append(Element("sFamilyClass", {'value': str(self.sFamilyClass) }))
  87. os2.append(self.panose.toTTX())
  88. os2.append(Element("ulUnicodeRange1", {'value': self.ulUnicodeRange1.toTTXStr() }))
  89. os2.append(Element("ulUnicodeRange2", {'value': self.ulUnicodeRange2.toTTXStr() }))
  90. os2.append(Element("ulUnicodeRange3", {'value': self.ulUnicodeRange3.toTTXStr() }))
  91. os2.append(Element("ulUnicodeRange4", {'value': self.ulUnicodeRange4.toTTXStr() }))
  92. os2.append(Element("achVendID", {'value': str(self.achVendID) }))
  93. os2.append(Element("fsSelection", {'value': self.fsSelection.toTTXStr() }))
  94. # TTX actually cannibalises these two, but forc is going to input them anyway.
  95. os2.append(Element("usFirstCharIndex", {'value': hex(self.usFirstCharIndex) }))
  96. os2.append(Element("usLastCharIndex", {'value': hex(self.usLastCharIndex) }))
  97. os2.append(Element("sTypoAscender", {'value': str(self.sTypoAscender) }))
  98. os2.append(Element("sTypoDescender", {'value': str(self.sTypoDescender) }))
  99. os2.append(Element("sTypoLineGap", {'value': str(self.sTypoLineGap) }))
  100. os2.append(Element("usWinAscent", {'value': str(self.usWinAscent) }))
  101. os2.append(Element("usWinDescent", {'value': str(self.usWinDescent) }))
  102. os2.append(Element("ulCodePageRange1", {'value': self.ulCodePageRange1.toTTXStr() }))
  103. os2.append(Element("ulCodePageRange2", {'value': self.ulCodePageRange2.toTTXStr() }))
  104. os2.append(Element("sxHeight", {'value': str(self.sxHeight) }))
  105. os2.append(Element("sCapHeight", {'value': str(self.sCapHeight) }))
  106. os2.append(Element("usDefaultChar", {'value': str(self.usDefaultChar) }))
  107. os2.append(Element("usBreakChar", {'value': str(hex(self.usBreakChar)) }))
  108. os2.append(Element("usMaxContext", {'value': str(self.usMaxContent) }))
  109. os2.append(Element("usLowerOpticalPointSize", {'value': str(self.usLowerOpticalPointSize) }))
  110. os2.append(Element("usUpperOpticalPointSize", {'value': str(self.usUpperOpticalPointSize) }))
  111. return os2
  112. def toBytes(self):
  113. os2 = struct.pack( ">hhHH2shhhhhhhhhhh10s4s4s4s4s4s2sHHhhhHH4s4shhHHHHH"
  114. , self.version # UInt16
  115. , self.xAvgCharWidth # Int16
  116. , self.usWeightClass # UInt16
  117. , self.usWidthClass # UInt16
  118. , self.fsType.toBytes() # 2 bytes/UInt16
  119. , self.ySubscriptXSize # Int16
  120. , self.ySubscriptYSize # Int16
  121. , self.ySubscriptXOffset # Int16
  122. , self.ySubscriptYOffset # Int16
  123. , self.ySuperscriptXSize # Int16
  124. , self.ySuperscriptYSize # Int16
  125. , self.ySuperscriptXOffset # Int16
  126. , self.ySuperscriptYOffset # Int16
  127. , self.yStrikeoutSize # Int16
  128. , self.yStrikeoutPosition # Int16
  129. , self.sFamilyClass # Int16
  130. , self.panose.toBytes() # 10 byte/10 UInt16s.
  131. , self.ulUnicodeRange1.toBytes() # 4 bytes/UInt32
  132. , self.ulUnicodeRange2.toBytes() # 4 bytes/UInt32
  133. , self.ulUnicodeRange3.toBytes() # 4 bytes/UInt32
  134. , self.ulUnicodeRange4.toBytes() # 4 bytes/UInt32
  135. , self.achVendID.toBytes() # Tag (4 bytes/UInt32)
  136. , self.fsSelection.toBytes() # 2 bytes/UInt16
  137. , self.usFirstCharIndex # UInt16
  138. , self.usLastCharIndex # UInt16
  139. , self.sTypoAscender # Int16
  140. , self.sTypoDescender # Int16
  141. , self.sTypoLineGap # Int16
  142. , self.usWinAscent # UInt16
  143. , self.usWinDescent # UInt16
  144. , self.ulCodePageRange1.toBytes() # 4 bytes/UInt32
  145. , self.ulCodePageRange2.toBytes() # 4 bytes/UInt32
  146. , self.sxHeight # Int16
  147. , self.sCapHeight # Int16
  148. , self.usDefaultChar # UInt16
  149. , self.usBreakChar # UInt16
  150. , self.usMaxContent # UInt16
  151. , self.usLowerOpticalPointSize # UInt16
  152. , self.usUpperOpticalPointSize # UInt16
  153. )
  154. return outputTableBytes(os2)