name.py 4.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151
  1. import struct
  2. from lxml.etree import Element
  3. from transform.bytes import generateOffsets, outputTableBytes
  4. class LangTagRecord:
  5. """
  6. Class representing a LangTagRecord in a name table.
  7. Only used for bytes compilation.
  8. """
  9. def __init__(self, length, offset):
  10. self.length = length
  11. self.offset = offset
  12. def toBytes(self):
  13. return struct.pack(">HH", self.length, self.offset)
  14. class nameRecord:
  15. """
  16. Class representing a record in a name table.
  17. """
  18. def __init__(self, nameID, platformID, platEncID, languageID, text):
  19. self.nameID = nameID
  20. self.platformID = platformID
  21. self.platEncID = platEncID
  22. self.languageID = languageID
  23. self.text = text
  24. def toTTX(self):
  25. languageID = ""
  26. if self.platformID == 1: # Mac
  27. languageID = str(self.languageID)
  28. elif self.platformID == 3: # Win
  29. languageID = hex(self.languageID)
  30. elif self.platformID == 0: # Unicode
  31. languageID = str(self.languageID)
  32. record = Element("namerecord", { "nameID" : str(self.nameID)
  33. , "platformID" : str(self.platformID)
  34. , "platEncID" : str(self.platEncID)
  35. , "langID" : str(languageID)
  36. })
  37. record.text = self.text
  38. return record
  39. class name:
  40. """
  41. Class representing a name table.
  42. """
  43. def __init__(self, fontFormat, m):
  44. self.format = 0 # the format that is being worked with.
  45. self.nameRecords = []
  46. # data compilation
  47. # -------------------------------------------------------------------
  48. nameRecords = m['metadata']['nameRecords'][fontFormat]
  49. macLangID = m['encoding']['macLangID']
  50. msftLangID = m['encoding']['msftLangID']
  51. for id, value in nameRecords.items():
  52. self.nameRecords.append(nameRecord(int(id), 0, 4, 0x0, value)) # unicode
  53. self.nameRecords.append(nameRecord(int(id), 1, 0, macLangID, value)) # macintosh
  54. self.nameRecords.append(nameRecord(int(id), 3, 1, msftLangID, value)) # microsoft
  55. # if the Microsoft Language ID is not American English (0x409),
  56. # make an additional entry with the PostScript name for American English.
  57. #
  58. # (this makes Microsoft's font validator happy)
  59. if msftLangID != int('0x0409', 16):
  60. self.nameRecords.append(nameRecord(6, 3, 1, 0x0409, nameRecords["6"]))
  61. def toTTX(self):
  62. name = Element("name")
  63. for r in self.nameRecords:
  64. name.append(r.toTTX())
  65. return name
  66. def toBytes(self):
  67. # This follows the structure of naming table format 0.
  68. # (there's a difference)
  69. metadata = []
  70. texts = []
  71. for nr in self.nameRecords:
  72. # plat 0 (Uni), platEncID any - UTF-16 encoding
  73. # plat 1 (Mac), assume UTF-16.
  74. # plat 3 (Msft), platEncID 1 - UTF-16BE encoding
  75. # basically just assuming UTF-16BE.
  76. texts.append(nr.text.encode('utf_16_be'))
  77. metadata.append(nr)
  78. stringOffset = 6 + (16*len(self.nameRecords)) + 2
  79. offsets = generateOffsets(texts, 16, stringOffset, usingClasses=False)
  80. stringData = offsets["bytes"]
  81. nameRecords = b''
  82. for num, nr in enumerate(self.nameRecords):
  83. nameRecords += struct.pack(">HHHHHH"
  84. , nr.platformID # UInt16
  85. , nr.platEncID # UInt16
  86. , nr.languageID # UInt16
  87. , nr.nameID # UInt16
  88. , len(texts[num]) # UInt16
  89. , offsets["offsetInts"][num] # Offset16 (UInt16)
  90. )
  91. beginning = struct.pack(">HHH"
  92. , self.format # UInt16
  93. , len(self.nameRecords) # UInt16
  94. , stringOffset # Offset16 (UInt16)
  95. )
  96. table = beginning + nameRecords + stringData
  97. return outputTableBytes(table)