#! /usr/bin/env python3 # This file is part of the Gentium package for TeX. # It is licensed under the Expat License, see doc//README for details. # author: Pavel Farar, pavel.farar@centrum.cz # # This script generates extra kerning pairs for the # combination accent + capital Greek letter. The kerning # pairs will have such a value that the combination will # look like the precomposed accented letter (with the # exception of the left side bearing). The input must be # a fontforge source (sfd file) and the output will have # the same form as kerning pairs in the afm file # # The main idea is that the fontforge source contains the # information how the accented letter is composed from the # accent and the base letter. The relevant information is # the width of the accent and the position of the accent # and the base letter (how they are moved from the position # X = 0). The kerning is computed using the following formula: # # Kern = X_letter - X_accent - WidthOfAccent # # This script is dirtier than it could be. The # reason is that it should work with different fonts, # but some fonts have problems. There is a problem # with the letter Omega in GentiumPlus which has not # the usual unicode value. In DejaVu the precomposed # letters are composed sometimes as accent + letter, # but sometimes also as letter + accent. import sys # Table with LIGKERNs for Greek capital letters, where # first (accent) + second (letter) gives third (accented # letter. Well, probably not the best term, but I created # the following table to a great extent automatically # from the LIGKERN commands for the precomposed small Greek # accented letters in the encoding LGR. Then I did uppercasing # using information from Unicode and looked if there is # some accented letter missing. Therefore I call it # LigatureTable and this file uses one line for every # capital Greek letter. I did it in such a way to avoid # random errors. LigatureTable = [] LigatureTable.append(['uni1FBF', 'Alpha', 'uni1F08']) LigatureTable.append(['uni1FBF', 'Epsilon', 'uni1F18']) LigatureTable.append(['uni1FBF', 'Eta', 'uni1F28']) LigatureTable.append(['uni1FBF', 'Iota', 'uni1F38']) LigatureTable.append(['uni1FBF', 'Omicron', 'uni1F48']) LigatureTable.append(['uni1FBF', 'Omega', 'uni1F68']) LigatureTable.append(['uni1FFE', 'Alpha', 'uni1F09']) LigatureTable.append(['uni1FFE', 'Epsilon', 'uni1F19']) LigatureTable.append(['uni1FFE', 'Eta', 'uni1F29']) LigatureTable.append(['uni1FFE', 'Iota', 'uni1F39']) LigatureTable.append(['uni1FFE', 'Omicron', 'uni1F49']) LigatureTable.append(['uni1FFE', 'Upsilon', 'uni1F59']) LigatureTable.append(['uni1FFE', 'Omega', 'uni1F69']) LigatureTable.append(['uni1FFE', 'Rho', 'uni1FEC']) LigatureTable.append(['uni1FCD', 'Alpha', 'uni1F0A']) LigatureTable.append(['uni1FCD', 'Epsilon', 'uni1F1A']) LigatureTable.append(['uni1FCD', 'Eta', 'uni1F2A']) LigatureTable.append(['uni1FCD', 'Iota', 'uni1F3A']) LigatureTable.append(['uni1FCD', 'Omicron', 'uni1F4A']) LigatureTable.append(['uni1FCD', 'Omega', 'uni1F6A']) LigatureTable.append(['uni1FDD', 'Alpha', 'uni1F0B']) LigatureTable.append(['uni1FDD', 'Epsilon', 'uni1F1B']) LigatureTable.append(['uni1FDD', 'Eta', 'uni1F2B']) LigatureTable.append(['uni1FDD', 'Iota', 'uni1F3B']) LigatureTable.append(['uni1FDD', 'Omicron', 'uni1F4B']) LigatureTable.append(['uni1FDD', 'Upsilon', 'uni1F5B']) LigatureTable.append(['uni1FDD', 'Omega', 'uni1F6B']) LigatureTable.append(['uni1FCE', 'Alpha', 'uni1F0C']) LigatureTable.append(['uni1FCE', 'Epsilon', 'uni1F1C']) LigatureTable.append(['uni1FCE', 'Eta', 'uni1F2C']) LigatureTable.append(['uni1FCE', 'Iota', 'uni1F3C']) LigatureTable.append(['uni1FCE', 'Omicron', 'uni1F4C']) LigatureTable.append(['uni1FCE', 'Omega', 'uni1F6C']) LigatureTable.append(['uni1FDE', 'Alpha', 'uni1F0D']) LigatureTable.append(['uni1FDE', 'Epsilon', 'uni1F1D']) LigatureTable.append(['uni1FDE', 'Eta', 'uni1F2D']) LigatureTable.append(['uni1FDE', 'Iota', 'uni1F3D']) LigatureTable.append(['uni1FDE', 'Omicron', 'uni1F4D']) LigatureTable.append(['uni1FDE', 'Upsilon', 'uni1F5D']) LigatureTable.append(['uni1FDE', 'Omega', 'uni1F6D']) LigatureTable.append(['uni1FCF', 'Alpha', 'uni1F0E']) LigatureTable.append(['uni1FCF', 'Eta', 'uni1F2E']) LigatureTable.append(['uni1FCF', 'Iota', 'uni1F3E']) LigatureTable.append(['uni1FCF', 'Omega', 'uni1F6E']) LigatureTable.append(['uni1FDF', 'Alpha', 'uni1F0F']) LigatureTable.append(['uni1FDF', 'Eta', 'uni1F2F']) LigatureTable.append(['uni1FDF', 'Iota', 'uni1F3F']) LigatureTable.append(['uni1FDF', 'Upsilon', 'uni1F5F']) LigatureTable.append(['uni1FDF', 'Omega', 'uni1F6F']) LigatureTable.append(['uni1FEF', 'Alpha', 'uni1FBA']) LigatureTable.append(['uni1FEF', 'Epsilon', 'uni1FC8']) LigatureTable.append(['uni1FEF', 'Eta', 'uni1FCA']) LigatureTable.append(['uni1FEF', 'Iota', 'uni1FDA']) LigatureTable.append(['uni1FEF', 'Omicron', 'uni1FF8']) LigatureTable.append(['uni1FEF', 'Upsilon', 'uni1FEA']) LigatureTable.append(['uni1FEF', 'Omega', 'uni1FFA']) LigatureTable.append(['tonos', 'Alpha', 'Alphatonos']) LigatureTable.append(['tonos', 'Epsilon', 'Epsilontonos']) LigatureTable.append(['tonos', 'Eta', 'Etatonos']) LigatureTable.append(['tonos', 'Iota', 'Iotatonos']) LigatureTable.append(['tonos', 'Omicron', 'Omicrontonos']) LigatureTable.append(['tonos', 'Upsilon', 'Upsilontonos']) LigatureTable.append(['tonos', 'Omega', 'Omegatonos']) # Set with Greek accents GreekAccents = set() # empty set for i in range(len(LigatureTable)): GreekAccents.add(LigatureTable[i][0]) # Set with Greek letters GreekLetters = set() # empty set for i in range(len(LigatureTable)): GreekLetters.add(LigatureTable[i][1]) # Set with precomposed Greek letters PrecomposedGreekLetters = set() # empty set for i in range(len(LigatureTable)): PrecomposedGreekLetters.add(LigatureTable[i][2]) def UnicodeValue(s): # The unicode value is the second number after "Encoding:" if s.find('Encoding:') != 0: sys.exit('UnicodeValue error') return s.split()[2] # The third item def WidthValue(s): if s.find('Width:') != 0: sys.exit('WidthValue error') return s.split()[1] # The second item # Dictionaries (all values are strings, not numbers!) AccentWidth = {} AccentUnicode = {} LetterUnicode = {} # Process the components (accents and base letters) f = open(sys.argv[1], 'r') farray = f.readlines() f.close() findex = -1 while True: findex = findex + 1 if findex >= len(farray): break # end of file s = farray[findex] if s.find('StartChar: ') == 0: CharacterName = s[11:-1] if CharacterName in GreekLetters: findex = findex + 1 s = farray[findex] LetterUnicode[CharacterName] = UnicodeValue(s) if CharacterName in GreekAccents: findex = findex + 1 s = farray[findex] AccentUnicode[CharacterName] = UnicodeValue(s) findex = findex + 1 s = farray[findex] AccentWidth[CharacterName] = WidthValue(s) def MoveValue(ss, lindex): sarray = ss.split() if sarray[0] != 'Refer:': sys.exit('No Refer:') x = int(sarray[8]) # the value of move # dirty hack to make Omega work in both Gentium and GentiumPlus # This script without the following hack would work well for Gentium, but not for # GentiumPlus. The problem with GentiumPlus is that the accented letters with Omega # uses as a base letter that with the unicode value 937 (which should be Omega), but # this letter has PostScript name uni03A9, not Omega. Omega has a different value. if ( sarray[2] == '937' ): # the standard unicode value for Omega = problems in GentiumPlus sarray[2] = LetterUnicode['Omega'] # is it accent or letter? # unicode is the second number (third value) if sarray[2] == AccentUnicode[LigatureTable[lindex][0]]: # accent x = -x # the move of accent is taken negatively elif sarray[2] == LetterUnicode[LigatureTable[lindex][1]]: # letter x = +x # the move of letter is taken positively else: sys.exit('Bad unicode of component') return x def KernValue(letter, s1, s2): # letter - the name of the precomposed letter # s1 - Refer to the accent (expected to be first) # s2 - Refer to the letter for i in range(len(LigatureTable)): if LigatureTable[i][2] == letter: LetterIndex = i FirstMove = MoveValue(s1, LetterIndex) SecondMove = MoveValue(s2, LetterIndex) return FirstMove + SecondMove - int(AccentWidth[LigatureTable[LetterIndex][0]]) KernTable = {} # Process the precomposed letters # The sfd file is still in "farray" findex = -1 while True: findex = findex + 1 if findex >= len(farray): break # end of file s = farray[findex] if s.find('StartChar: ') == 0: CharacterName = s[11:-1] if CharacterName in PrecomposedGreekLetters: while s.find('Refer: ') != 0: findex = findex + 1 s = farray[findex] KernTable[CharacterName] = KernValue(CharacterName, s, farray[findex + 1]) # Write the kerning table fafm = open(sys.argv[1][:-4] + '-extra.afm', 'w') for i in range(len(LigatureTable)): sourceKern = KernTable[LigatureTable[i][2]] if sourceKern != 0: # remove zero kerns fafm.write( 'KPX ' + LigatureTable[i][0] + ' ' + LigatureTable[i][1] + ' ' + str(int(round(1000.0 / 2048.0 * sourceKern))) + '\n' ) fafm.close()