glyphProc.py 8.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280
  1. import pathlib
  2. import log
  3. from glyph import simpleHex, Glyph, Img
  4. # glyphProc.py
  5. # -----------------------------
  6. #
  7. # processing and compiling glyphs before font creation.
  8. def compileImageGlyphs(dir, m, delim, nusc, afsc, imageFormats):
  9. ## get a rough list of everything
  10. imgCollection = dict()
  11. if 'svg' in imageFormats:
  12. if not (dir / 'svg').exists():
  13. raise Exception(f"You don't have an 'svg' folder in your input!")
  14. if not list((dir / 'svg').glob("*.svg")):
  15. raise Exception(f"There are no svg images in your SVG folder!.")
  16. imgCollection['svg'] = dict()
  17. for path in list((dir / 'svg').glob("*.svg")):
  18. imgCollection['svg'][path.stem] = Img("svg", 0, m, path.absolute(), afsc)
  19. if 'png' in imageFormats:
  20. if not list(dir.glob("png*")):
  21. raise Exception(f"There are no PNG folders in your input folder.")
  22. for pngFolder in list(dir.glob("png*")):
  23. if not pngFolder.name[0] == '.' and not pngFolder.suffix: # if it's not a hidden file and if it's not a file.
  24. try:
  25. formatName, strike = pngFolder.name.split('-', 2)
  26. strikeSize = int(strike)
  27. except ValueError as e:
  28. raise Exception(f"One of your PNG folders ('{pngFolder.name}') isn't named properly. Make sure it's 'png-<strike size>'.")
  29. if not list(pngFolder.glob("*.png")):
  30. raise Exception(f"There are no PNG images in '{pngFolder}'.")
  31. imgCollection[pngFolder.name] = dict()
  32. for path in list(pngFolder.glob("*.png")):
  33. imgCollection[pngFolder.name][path.stem] = Img("png", strikeSize, m, path.absolute())
  34. ## check size
  35. firstFolderName = list(imgCollection.keys())[0]
  36. firstFolder = imgCollection[firstFolderName]
  37. if len(imgCollection) > 1:
  38. for key, folder in list(imgCollection.items())[1:]:
  39. if not len(folder) == len(firstFolder):
  40. raise Exception(f"The amount of glyphs in your input folders aren't the same. '{key}' has {str(len(folder))}. '{firstFolderName}' has {len(firstFolder)}. The amount of images in every folder should be the same.")
  41. ## convert them into glyphs
  42. ## (at the same time checking if all the codepoint names are the same)
  43. imgGlyphs = []
  44. for c, file in firstFolder.items():
  45. imgDict = dict()
  46. for folderName, folder in imgCollection.items():
  47. if not c in folder:
  48. raise Exception(f"There's a mismatch in your files. I tried to find an image for the codepoint '{c}' in '{folderName}', but I couldn't find one. You have to make sure you have the exact same sets of filenames in each of your input folders.")
  49. else:
  50. imgDict[folderName] = folder[c]
  51. try:
  52. imgGlyphs.append(Glyph(c, imgDict=imgDict, delim=delim))
  53. except ValueError as e:
  54. raise Exception(f"There was a problem when trying to create a glyph object for {c}. → {e}")
  55. return imgGlyphs
  56. def compileAliasGlyphs(glyphs, aliases, delim):
  57. # basic check!
  58. for target, destination in aliases.items():
  59. try:
  60. aliasGlyph = Glyph(target, alias=destination, delim=delim)
  61. except ValueError as e:
  62. raise Exception(f"Some part of an alias glyph isn't named correctly. → {e}")
  63. # is the target NOT a real destination
  64. targetMatches = False
  65. for g in glyphs:
  66. if aliasGlyph == g:
  67. targetMatches = True
  68. if targetMatches:
  69. raise Exception(f"The codepoint sequence for the alias glyph ('{target}') is represented in your image glyphs. It has to be something different.")
  70. # is the destination is a real destination
  71. destinationMatches = False
  72. for g in glyphs:
  73. if aliasGlyph.alias == g.codepoints:
  74. destinationMatches = True
  75. if not destinationMatches:
  76. raise Exception(f"The destination ('{destination}') of the alias glyph '{target}' is not represented in your image glyphs.")
  77. glyphs.append(aliasGlyph)
  78. return glyphs
  79. def addServiceGlyphs(glyphs, no_vs16):
  80. """
  81. adds service glyphs to the list of glyphs based on various requirements.
  82. """
  83. newGlyphs = []
  84. vs16Presence = False
  85. zwjPresence = False
  86. for g in glyphs:
  87. # presence
  88. if g.codepoints.vs16 and no_vs16 is False: vs16Presence = True
  89. if 0x200d in g.codepoints.seq: zwjPresence = True
  90. # add particular service glyphs.
  91. glyphs.append(Glyph(["20"], userInput=False)) # breaking space
  92. glyphs.append(Glyph(["a0"], userInput=False)) # non-breaking space
  93. if vs16Presence: glyphs.append(Glyph(["fe0f"], userInput=False))
  94. if zwjPresence: glyphs.append(Glyph(["200d"], userInput=False))
  95. return glyphs
  96. def glyphDuplicateTest(glyphs):
  97. """
  98. Checks whether there are any duplicates in codepoints in a list of glyphs.
  99. """
  100. for id1, g1 in enumerate(glyphs):
  101. for id2, g2 in enumerate(glyphs):
  102. if g1 == g2:
  103. if id1 != id2:
  104. raise Exception(f"One of your glyphs (image paths - {g1.img}) when processed, becomes {g1}. This matches another glyph that you have - {g2}. Make sure that your codepoint sequences aren't duplicates when stripped of VS16s (fe0f).")
  105. def areGlyphLigaturesSafe(glyphs):
  106. singleGlyphCodepoints = []
  107. ligatures = []
  108. for g in glyphs:
  109. if len(g.codepoints) > 1:
  110. ligatures.append(g)
  111. else:
  112. singleGlyphCodepoints.append(g.codepoints.seq[0])
  113. for g in ligatures:
  114. for codepoint in g.codepoints.seq:
  115. if codepoint not in singleGlyphCodepoints:
  116. raise Exception(f"One of your ligatures ({g.codepoints}) has an individual codepoint (apart from fe0f and 200d) that is not represented as a glyph itself ({simpleHex(codepoint)}). All components of all ligatures (apart from fe0f and 200d) must be represented as glyphs.")
  117. def mixAndSortGlyphs(glyphs):
  118. glyphStruct = {"all": [], "img_empty": [], "img": [], "empty": []}
  119. # sort glyphs.
  120. #
  121. # (using the glyphs' internal sorting mechanism, which sorts by
  122. # codepoint sequence length, then the value of the first codepoint.)
  123. #
  124. # THIS IS INCREDIBLY CRUCIAL AND CANNOT BE SKIPPED.
  125. #
  126. # CHECK OUT THE CODEPOINTSEQ CLASS TO UNDERSTAND WHY.
  127. glyphs.sort()
  128. for g in glyphs:
  129. glyphStruct["all"].append(g)
  130. if g.glyphType is not "alias":
  131. glyphStruct["img_empty"].append(g)
  132. if g.glyphType is "img":
  133. glyphStruct["img"].append(g)
  134. if g.glyphType is "empty":
  135. glyphStruct["empty"].append(g)
  136. return glyphStruct
  137. def getGlyphs(inputPath, m, aliases, delim, imageFormats, flags):
  138. """
  139. Runs inputs through all of the necessary processes and checks to create a glyphs structure.
  140. """
  141. # compile image glyphs
  142. log.out(f'- Getting + validating image glyphs... (this can take a while)', 90)
  143. imgGlyphs = compileImageGlyphs(inputPath, m, delim, flags["nusc"], flags["afsc"], imageFormats)
  144. # compile alias glyphs
  145. if aliases:
  146. log.out(f'- Getting + validating alias glyphs...', 90)
  147. glyphs = compileAliasGlyphs(imgGlyphs, aliases, delim)
  148. else:
  149. glyphs = imgGlyphs
  150. # process service glyphs
  151. log.out(f'- Adding service codepoints...', 90)
  152. glyphs = addServiceGlyphs(glyphs, flags["no_vs16"])
  153. # check for duplicate codepoints without VS16
  154. if not flags["no_vs16"]:
  155. log.out(f'- Checking if there are any duplicate glyphs...', 90)
  156. glyphDuplicateTest(glyphs)
  157. # validating (or stripping) ligatures
  158. if flags["no_lig"]:
  159. log.out(f'- [--no-lig] Stripping any ligatures...', 90)
  160. singleGlyphs = []
  161. for g in glyphs:
  162. if len(g.codepoints) == 1:
  163. singleGlyphs.append(g)
  164. glyphs = singleGlyphs
  165. else:
  166. log.out(f'- Validating ligatures...', 90)
  167. areGlyphLigaturesSafe(glyphs)
  168. log.out(f'- Mixing and sorting glyphs...', 90)
  169. return mixAndSortGlyphs(glyphs)