#http://stackoverflow.com/questions/5747739/python-render-non-anti-aliased-font-to-internal-image
#Author: Pavel Brychta
from PIL import Image
from PIL import ImageFont, ImageDraw
import sys
import ast

def character(fontname, fontsize, bitsperpixel, char):
  """ Vykresli znak do vnitrniho povrchu a nasledne ho prevede do pole elementu intenzit,
      ktere ma vzdy 4 bity/bod (intenzita 0-15).
	  Vraci [rozmer obrazku v ose X, rozmer obrazku v ose Y, Prvni radek obrazku, ktery nese obrazovou informaci (offset od 0), Posledni radek obrazku, ktery nese obrazovou informaci, [pole elementu]
  """
  result = []

  usr_font = ImageFont.truetype(fontname, fontsize)
  imagexsize, imageysize = usr_font.getsize(char)
  image = Image.new("RGB", (imagexsize, imageysize), "Black")
  #image = Image.new("RGB", (320, 320), "Black")
  # print(imagexsize, imageysize)
  d_usr = ImageDraw.Draw(image)
  if bitsperpixel > 1:
    d_usr.fontmode = "L" # Zapneme antialiasovani
  else:
    d_usr.fontmode = "1" # Vypneme antialiasovani (pro fonty o hloubce 1bit/pixel)
  #imagexsize, imageysize = d_usr.textsize(char, usr_font)
  d_usr.text((0, 0), char, "White", font=usr_font)
  #print "Imageinfosize:", ord(char), imagexsize, imageysize
  #image.save('test_' + str(ord(char)) + '.png', 'PNG')
  pix = image.load()
  firstimageline = 1000
  lastimageline = -1
  for x in range(imagexsize):
    for y in range(imageysize):
      R, G, B = pix[x, y]
      if R < 0x08:
        outpix = 0
      elif R < 0x18:
        outpix = 1
      elif R < 0x28:
        outpix = 2
      elif R < 0x38:
        outpix = 3
      elif R < 0x48:
        outpix = 4
      elif R < 0x58:
        outpix = 5
      elif R < 0x68:
        outpix = 6
      elif R < 0x78:
        outpix = 7
      elif R < 0x88:
        outpix = 8
      elif R < 0x98:
        outpix = 9
      elif R < 0xa8:
        outpix = 10
      elif R < 0xb8:
        outpix = 11
      elif R < 0xc8:
        outpix = 12
      elif R < 0xd8:
        outpix = 13
      elif R < 0xe8:
        outpix = 14
      else:
        outpix = 15
      result.append(outpix),
      if outpix != 0:
        if firstimageline > y:
            firstimageline = y
        if lastimageline < y:
            lastimageline = y
  if firstimageline == 1000:
    firstimageline = imageysize >> 1
    lastimageline = firstimageline
  return imagexsize, imageysize, firstimageline, lastimageline, result

def encodechar(chardata, bitsperpixel, firstline, height):
  """ Prevede zadanou bitovou mapu s rozsahem 0-15 do binarniho tvaru
      s orezem na viditelnou cast za pomoci firstline a height parametru
  """
  result = []
  w, h, rawdata = chardata
  # print(rawdata)
  # print(chardata)
  # print(firstline, w, len(rawdata))
  # print(firstline, h, height, len(rawdata))

  index = 0
  for x in range(w):
    posBit = 0
    outdata = 0
    for y in range(h):
      if (y >= firstline) and (y <= (height + firstline)):
        data = rawdata[index] >> 3
        mask = data << posBit
        outdata |= mask
        posBit += 1
        if posBit > 7:
            result.append(outdata)
            posBit = 0
            outdata = 0
      index += 1
    if posBit != 0:
      result.append(outdata)
  # optimalizace dat (odstraneni poslednich bytu, ktere jsou nulove)
  while len(result) > 1 and result[-1] == 0:
    del result[-1]
  return result

def buildfontname(fontname, ptsize, charfrom, charto, bitsperpixel, reduceheight, codepage):
  """ Build ID of the font
  """
  result = "Font"
  result += fontname.split(".")[0] + "_"
  result += "{0}pt_".format(ptsize)
  result += "{0:02X}_".format(charfrom)
  result += "{0:02X}_".format(charto)
  return result

def createfont(fontname, ptsize, charfrom, charto, bitsperpixel, reduceheight, codepage):
  """ Write Font file
  """
  fontminline = 65535
  fontmaxline = 0
  fontrawdata = []
  fontheight = 0
  fontvaliddata = []
  fontoffsets = []
  fontsizes = []
  actualoffset = 0
  fontmonospacewidth = 0

  for c in range(charfrom, charto + 1):
    encchar = chr(c)
    fchar = character(fontname, ptsize, bitsperpixel, encchar)
    w, h, ymin, ymax, dummy = fchar
    if w > fontmonospacewidth:
      fontmonospacewidth = w # znak je zatim nejsirsi - zapamatujeme si jeho sirku pro monospace vystup
    fontrawdata.append((w, h, dummy))
    if ymin < fontminline:
      fontminline = ymin # znak zacina drive nez predchozi (ve vyssim bode), zapamatujeme si pocatek
    if ymax > fontmaxline:
      fontmaxline = ymax # znak konci pozdeji nez predchozi (v nizsim bode), zapamatujeme si konec
  if reduceheight:
    fontheight = fontmaxline - fontminline + 1 # vypocitame si vysku skutecneho pismena
  else:
    fontminline = 0
    fontheight = h

  for c in fontrawdata:
    fontoffsets.append(actualoffset)
    fontchar = encodechar(c, bitsperpixel, fontminline, fontheight)
    fontvaliddata.append(fontchar)
    fontsizes.append(len(fontchar))
    actualoffset += len(fontchar)

  OutFile = open(buildfontname(fontname, ptsize, charfrom, charto, 1, reduceheight, codepage) + ".c", 'w')
  OutFile.write("/* Font resource created by fontconv.py (c) 2020 by Pavel Brychta (1 bit per pixel)*/\n")
  OutFile.write("const uint8_t " + buildfontname(fontname, ptsize, charfrom, charto, 1, reduceheight, codepage) + "[] PROGMEM =\n{\n")
  OutFile.write("\t{0}, /* width for monospace output */\n".format(fontmonospacewidth))
  OutFile.write("\t{0}, /* font height */\n".format(fontheight))
  OutFile.write("\t{0}, /* first char ID */\n".format(charfrom))
  OutFile.write("\t{0}, /* number of characters */\n".format(charto - charfrom + 1))
  OutFile.write("\t/* index table: offset MSB, offset LSB, length, width */\n")
  ptr = 0
  for o in fontoffsets:
    f = fontrawdata[ptr]
    OutFile.write("\t0x{0:02X},".format((o >> 8) & 0xff)) # MSB
    OutFile.write("0x{0:02X},".format(o & 0xff)) # LSB
    OutFile.write("0x{0:02X},".format(fontsizes[ptr])) # length
    OutFile.write("0x{0:02X},".format(f[0])) # width
    OutFile.write("\n")
    ptr += 1
  OutFile.write("\t/* glyph raw */")
  count = 0
  for c in fontvaliddata:
    for u in c:
      if 0 == count:
        OutFile.write("\n\t")
      OutFile.write("0x{0:02X}".format(u))
      count += 1
      if 20 == count:
        count = 0
      OutFile.write(",")
  OutFile.write("\n};\n")
  OutFile.close()

def main(argv):
  if len(argv) != 8:
    sys.stderr.write("Invalid number of parameters.")
    return 1
  else:
    fontname = argv[1] # jmeno fontu
    fontsize = int(argv[2]) # velikost fontu (vyska v bodech)
    charfrom = ast.literal_eval(argv[3]) # kod prvniho znaku, ktery bude ve vyslednem fontu ulozeny
    charto = ast.literal_eval(argv[4]) # kod posledniho znaku, ktery bude ve vyslednem fontu ulozeny
    bitsperpixel = int(argv[5]) # pocet bitu/pixel. Platne hodnoty jsou 1, 2, 4
    reduceheight = int(argv[6]) # priznak, zda do vysledneho fontu neukladat "mrtve" oblasti (spocita skutecnou vysku nejvyssiho znaku a na tuto vysku prepocita vsechny ostatni znaky)
    codepage = argv[7] # pouzita kodova stranka pro narodni znaky (diakritiku)
    createfont(fontname, fontsize, charfrom, charto, bitsperpixel, reduceheight, codepage)
    return 0

if __name__ == "__main__":
    sys.exit(main(sys.argv))
