// ---------------- helpers ------------------------------------------------

    procedure LoadSystemFonts;
    var
      stub: int64;
      i: integer;
    begin
      VerboseLog('Loading system fonts...');
      FoLocalStoreDir:=Cgepath(LocalStorePath + '/fonts/');
      _MakeSurePathExists(PChar(FoLocalStoreDir));
      ec;
      For i:=0 to MAX_SYS_FONT do begin
        _cgeNewResource(CGE_RESOURCE_FONT, @SystemFont[i], @stub);
        ec;
      end;
      _foLoadFont('modules/chentrah/fonts/chebstyle', SystemFont[0]);
      ec;
      _foLoadFont('modules/chentrah/fonts/nimbus_sans', SystemFont[1]);
      ec;
      _foLoadFont('modules/chentrah/fonts/cyrold', SystemFont[2]);
      ec;
      _foLoadFont('modules/chentrah/fonts/monospace', SystemFont[3]);
      ec;
      VerboseLogOk;
    end;
    
    
    
    const FaceName: array [boolean, boolean, boolean] of string = (
      (('plain','condensed'),
      ('italic','italic_condensed')),
      (('bold','bold_condensed'),
      ('bold_italic','bold_italic_condensed')));
      
    function CopyRectLightenGray8(const SrcImage: TImageData; SrcX, SrcY, Width, Height: LongInt;
      var DstImage: TImageData; DstX, DstY: LongInt): Boolean;
    var
      x, y: integer;
      sp, dp: PByte;
    begin
      if (srcX < 0) or (srcY < 0)
        or (srcX + Width > SrcImage.Width) or (srcY + Height > SrcImage.Height)
        or (dstX + Width > DstImage.Width) or (dstY + Height > DstImage.Height)
        or (SrcImage.Format <> ifGray8) or (DstImage.Format <> ifGray8)
        then Exit(False);
      sp:=SrcImage.Bits + srcX + srcY * SrcImage.Width;
      dp:=DstImage.Bits + dstX + dstY * DstImage.Width;
      For y:=0 to Height - 1 do begin
        For x:=0 to Width - 1 do
          (dp + x)^ := max ((dp + x)^, (sp + x)^);
        sp+= SrcImage.Width;
        dp+= DstImage.Width;
      end;
      Result:=True;
    end;
    
    procedure MaxContrastGray8 (var img: TImageData);
    var
      x: integer;
    begin
      // 16%
      For x:=0 to img.Width * img.Height - 1 do begin
        if byte((img.Bits + x)^) < FO_CONTRAST_TRESHOLD
          then byte((img.Bits + x)^):=0
          else
            if byte((img.Bits + x)^) > (255 - FO_CONTRAST_TRESHOLD)
            then byte((img.Bits + x)^):=255;
      end;
      if byte((Img.Bits + Img.Width * Img.Height - 1)^) > 0
        then For x:=0 to img.Width * img.Height - 1 do
          byte((img.Bits + x)^):=255 - byte((img.Bits + x)^);
    end;
    
    procedure PropagateBrighterG8 (var img: TImagedata; iterations: integer);
    var
      x, y, x1, y1: integer;
      i2: TImageData;
    begin
      if iterations > 1 then PropagateBrighterG8(img, iterations - 1);
      CloneImage(img, i2);
      For y:=0 to img.Height - 1 do
        For x:=0 to img.Width - 1 do
          For y1:= -1 to 1 do
            if (y + y1 >= 0) and (y + y1 < img.Height) then
              For x1:= -1 to 1 do
                if (x + x1 >= 0) and (x + x1 < img.Width) then
                  pbyte(img.bits + y * i2.width + x)^:= max (pbyte(img.bits + y * i2.width + x)^, pbyte(i2.bits + (y + y1) * i2.width + x + x1)^);
      FreeImage(i2);
    end;
    
    const weight: array[-1..1, -1..1] of single = (
     (0.35,0.50,0.35),
     (0.50,1.00,0.50),
     (0.35,0.50,0.35));

    procedure BlurredShadowG8 (var img: TImagedata; iterations: integer);
    var
      x, y, x1, y1: integer;
      i2: TImageData;
    begin
      if iterations > 1 then BlurredShadowG8(img, iterations - 1);
      CloneImage(img, i2);
      For y:=0 to img.Height - 1 do
        For x:=0 to img.Width - 1 do
          For y1:= -1 to 1 do
            if (y + y1 >= 0) and (y + y1 < img.Height) then
              For x1:= -1 to 1 do
                if (x + x1 >= 0) and (x + x1 < img.Width) then
                  pbyte(img.bits + y * i2.width + x)^:= min(255, pbyte(img.bits + y * i2.width + x)^ + trunc(pbyte(i2.bits + (y + y1) * i2.width + x + x1)^ * weight[x1,y1]));
      FreeImage(i2);
    end;
    
    function GetImgPixelG8(var img: TImageData; x, y: integer): integer;
    begin
      if (x < 0) or (y < 0) or (x >= img.Width) or (y >= img.height) then Exit(0);
      Result:=pbyte(img.bits + x + y * img.width)^;
    end;

    function CalculateKerning(var A, B: TImageData; abase, bbase: integer): integer;
    var
      AA: TImageData;
      x, y, bt: integer;
      u: boolean;
    begin
      NewImage(A.Width + 2 * FO_KERN_MAX, A.Height + 2 * FO_KERN_MAX, ifGray8, AA);
      FillChar(AA.Bits^, AA.Width*AA.Height, 0);
      CopyRect(A, 0, 0, A.Width, A.Height, AA, FO_KERN_MAX, FO_KERN_MAX);
      PropagateBrighterG8(AA, FO_KERN_MAX);
      bt:=FO_KERN_MAX + (A.height - abase) - (B.height - bbase);
      Result:=0;
      u:=False;
      Repeat
        For y:=0 to AA.Height - 1 do
          For x:= AA.Width - 1 downto 0 do
            if (GetImgPixelG8(AA, x, y) >= FO_CHARTHRESOLD)
            and (GetImgPixelG8(B, x - AA.Width - Result + 1, y + bt) >= FO_CHARTHRESOLD)
            then begin
              u:=True;
              Break;
            end;
        if u then Break;
        Dec(Result);
        if Result < -A.Width + FO_MIN_KERN_CHARWIDTH then begin
          //If they never touch (like, for example, a point and an apostrophe),
          // then use the full width.
          Result:=0;
          Break;
        end;
      until false;
      inc(Result, FO_KERN_MAX);
      FreeImage(AA);
      Result:=min(127, max(-127, Result));
    end;
    
    
    function IsLineEmptyG8(var img: TImageData; L: integer): boolean;
    var x: integer;
    begin
      For x:=0 to img.Width - 1 do
        if byte((img.Bits + x + img.Width * L)^) > 0
          then Exit(False);
      Result:=True;
    end;
    
    function IsColumnEmptyG8(var img: TImageData; L: integer): boolean;
    var y: integer;
    begin
      For y:=0 to img.Height - 1 do
        if byte((img.Bits + L + img.Width * y)^) > 0
          then Exit(False);
      Result:=True;
    end;
    
    function IsLineBrigther50PercentG8(var img: TImageData; L: integer): boolean;
    var x: integer;
    begin
      For x:=0 to img.Width - 1 do
        if byte((img.Bits + x + img.Width * L)^) > 127
          then Exit(True);
      Result:=false;
    end;
    
    function IsLineBrigther50PercentG8LR(var img: TImageData; L, left, right: integer): boolean;
    var x: integer;
    begin
      For x:=left to right do
        if byte((img.Bits + x + img.Width * L)^) > 127
          then Exit(True);
      Result:=false;
    end;

    
    procedure CropGlyphG8Col(var img, glyph: TImageData; var base: smallint; top, hght, bbase: integer);
    var
      xl, xr, yt, yb: integer;
      function izp(ix, iy: integer): boolean;// inline;
      begin
        Result:=byte((img.Bits + ix + img.Width * (iy + top))^) > 0;
      end;
      function eh(jy: integer): boolean;
      var x: integer;
      begin
        Result:=true;
        For x:=xl to xr do Result:=Result and not izp(x, jy);
      end;
      function ev(jx: integer): boolean;
      var y: integer;
      begin
        Result:=true;
        For y:=yt to yb do Result:=Result and not izp(jx, y);
      end;
    begin
      xl:=0;
      xr:=img.width - 1;
      yt:=0;
      yb:=hght - 1;
      While eh(yt) and (yt < yb) do inc(yt);
      While ev(xl) and (xl < xr) do inc(xl);
      While eh(yb) and (yt < yb) do dec(yb);
      While ev(xr) and (xl < xr) do dec(xr);
      base:= yb - bbase + hght;
      NewImage(max(1, xr - xl + 1), max(1, yb - yt + 1), ifGray8, glyph);
      CopyRect(img, xl, yt + top, glyph.width, glyph.height, glyph, 0, 0);
    end;

    procedure CropGlyphG8Row(var img, glyph: TImageData; var base: smallint; left, wdth, bbase: integer);
    var
      xl, xr, yt, yb: integer;
      function izp(ix, iy: integer): boolean;// inline;
      begin
        Result:=byte((img.Bits + ix + left + img.Width * (iy))^) > 0;
      end;
      function eh(jy: integer): boolean;
      var x: integer;
      begin
        Result:=true;
        For x:=xl to xr do Result:=Result and not izp(x, jy);
      end;
      function ev(jx: integer): boolean;
      var y: integer;
      begin
        Result:=true;
        For y:=yt to yb do Result:=Result and not izp(jx, y);
      end;
    begin
      xl:=0;
      xr:=wdth - 1;
      yt:=0;
      yb:=img.height - 1;
      While eh(yt) and (yt < yb) do inc(yt);
//addlog(' --- yt %0', [yt]); _cgeheartbeat ;
      While ev(xl) and (xl < xr) do inc(xl);
//addlog(' --- xl %0', [xl]); _cgeheartbeat ;
      While eh(yb) and (yt < yb) do dec(yb);
//addlog(' --- yb %0', [yb]); _cgeheartbeat;
      While ev(xr) and (xl < xr) do dec(xr);
//addlog(' --- xr %0', [xr]); _cgeheartbeat ;
      base:= yb - bbase + img.height;
      NewImage(max(1, xr - xl + 1), max(1, yb - yt + 1), ifGray8, glyph);
      CopyRect(img, xl + left, yt, glyph.width, glyph.height, glyph, 0, 0);
    end;


    function TCgeFont.FigureFace (size: integer; var bold, italic, condensed: boolean): integer; //return size index
    var
      b, i, c: boolean;
    begin
      b:=Face[sIndex[size], bold, italic, condensed].Subs[1];
      i:=Face[sIndex[size], bold, italic, condensed].Subs[2];
      c:=Face[sIndex[size], bold, italic, condensed].Subs[3];
      bold:=b;
      italic:=i;
      condensed:=c;
      Result:=sIndex[size];
    end;

    function TCgeFont.GetBase(A: WideChar; size: integer; bold, italic, condensed: boolean): integer;
    var
      si: integer;
    begin
      si:=FigureFace(size, bold, italic, condensed);
      Result:=_GetBase(@A, si, bold, italic, condensed);
    end;
    
    function TCgeFont._GetBase(A: PWideChar; si: integer; bold, italic, condensed: boolean): integer;
    var
      b, i, c: boolean;
    begin
      if cIndex[A^] < 0
        then Result:=_GetBase('?', si, bold, italic, condensed)
        else Result:=Face[si, bold, italic, condensed].Char[cIndex[A^], gpBase];
    end;


    function TCgeFont.GetKerning(A, B: WideChar; size: integer; bold, italic, condensed: boolean): integer;
    var
      si: integer;
    begin
      if (cIndex[A] < 0) or (cIndex[B] < 0) then Exit(0);
      si:=FigureFace(size, bold, italic, condensed);
      Result:=_GetKerning(@A, @B, si, bold, italic, condensed);
    end;
    
    function TCgeFont._GetKerning(A, B: PWideChar; si: integer; bold, italic, condensed: boolean): integer;
    begin
      if (cIndex[A^] < 0) or (cIndex[B^] < 0) then Exit(0);
      Result:=PByte(Face[si, bold, italic, condensed].Kerning.Bits + cIndex[A^] * Length(cindex) + cIndex[B^])^ - 128;
    end;

    function TCgeFont.GetGlyph(A: WideChar; size: integer; bold, italic, condensed: boolean): PImageData;
    var
      si: integer;
    begin
      si:=FigureFace(size, bold, italic, condensed);
      Result:=_GetGlyph(@A, si, bold, italic, condensed);
    end;


    function TCgeFont._GetGlyph(A: PWideChar; si: integer; bold, italic, condensed: boolean): PImageData;
    begin
      if cIndex[A^] < 0
        then Result:=@Face[si, bold, italic, condensed].cGlyph[cIndex['?']]
        else Result:=@Face[si, bold, italic, condensed].cGlyph[cIndex[A^]];
    end;
    
    function TCgeFont._GetGlyphData(A: PWideChar; si: integer; bold, italic, condensed: boolean): PGlyphData;
    begin
      if cIndex[A^] < 0
        then Result:=@Face[si, bold, italic, condensed].char[cIndex['?']]
        else Result:=@Face[si, bold, italic, condensed].char[cIndex[A^]];
    end;

    function TCgeFont.GetTexture(size: integer; bold, italic, condensed: boolean; sizex, sizey: PInteger; shadow: pglUint): glUint;
    var
      b, i, c: boolean;
      s: integer;
    begin
      s:=sIndex[size];
      b:=Face[s, bold, italic, condensed].Subs[1];
      i:=Face[s, bold, italic, condensed].Subs[2];
      c:=Face[s, bold, italic, condensed].Subs[3];
      Result:=Face[s, b, i, c].Texture;
      sizex^:=Face[s, b, i, c].TextureWidth;
      sizey^:=Face[s, b, i, c].TextureHeight;
      shadow^:=Face[s, b, i, c].ShadowTexture;
    end;

    Constructor TCgeFont.Create;
    begin
      inherited;
    end;

    Destructor TCgeFont.Destroy;
    var
      a, e: integer;
      b,c,d: boolean;
    begin
      For a:=0 to High(cSize) do
        For b:=False to True do
          For c:=False to True do
            For d:=False to True do
              if not Face[a,b,c,d].Subs[0]
                then with Face[a,b,c,d] do begin
                  For e:=0 to High(cGlyph) do FreeImage(cGlyph[e]);//just in case;
                  SetLength(cGlyph, 0);
                  FreeImage(Kerning);
                  if not GoingDownHard() then begin
                    glDeleteTextures(Texture, 1);
                    glDeleteTextures(ShadowTexture, 1);
                  end;
                end;
      inherited;
    end;
    
    procedure TCgeFont.Load(fn: string);
    var
      ini: TIniFile;
      img, imT, imS: TImageData;
      Charset: TAOW;
      ininame, filename, gfname, kfname, tfname, sfname, dfname: string;
      initime, otime: TDateTime;
      n, j, x, a, v, y, ymax, L, cL, cR: integer;
      b, i, c, b2, i2, c2: boolean;
      fsn: string;
      zero0top, zero0bottom, zero0base, zero1top, zero1base,
      zero0left, zero0right, maxw,
      spacing, ultop, ulbase: integer;
      w: WideChar;
      ws: WideString;
      NeedToInstall: boolean;
      bp: pbyte;
      _cx, _cy: array of integer;
      _rowHeight: integer;
      f: TFileStream;
    begin
      ininame:=ChangeFileExt(fn, '.ini');
      fn:=CgePath(ininame);
      Name:=fn;
      Try
        VerboseLog('Loading font "%0":', [fn]);
        initime:=FileDateToDateTime(FileAge(fn));
        ini:=TIniFile.Create(fn);
        filename:=ini.ReadString('font','filename','');
        Name:=ini.ReadString('font','name', fn);

   //Reading the sizes set
        SetLength(cSize, ini.ReadInteger('sizes', 'num', -1));
        for j:=0 to High(cSize) do
          cSize[j]:=ini.ReadInteger('sizes', IntToStr(j), -1);
        sZMin:=cSize[0];
        sZMax:=cSize[High(cSize)];
        For n:=0 to MAX_CHARACTER_SIZE do begin
          x:=max(n, szMin);
          For j:= High(cSize) downto 0 do
            if x >= cSize[j] then begin
              x:=cSize[j];
              sIndex[n]:=x;
            end;
        end;

   //Reading the charset
        initime:=max(initime, FileDateToDateTime(FileAge(ExtractFilePath(fn) + filename + '_charset.txt')));
        Charset:=LoadUnicodeText(ExtractFilePath(fn) + filename + '_charset.txt');
        While Length(Trim(Charset.Last)) = 0 do begin
          Charset.Length:=Charset.Length - 1;
        end;
        if Charset.Length = 1 then begin
          //a row
          ws:=Charset[0];
          Charset.Length:=0;
          For j:=1 to Length(ws) do if ws[j] > ' ' then Charset.Add(Copy(ws, j, 1));
        end
        else begin
          //a column
          While Length(Charset.Last) <> 1 do
            Charset.Length:=Charset.Length - 1;
        end;
//addlog('== %0 characters', [Charset.Length - 2]);
        if (Charset[0] <> '0') or (Charset[1] <> '0')
          then Die(
            'Шрифт "%0": первые два символа в файле, задающем набор символов, должны быть нулями!',
            'Font "%0" : the first two characters in the charset definition file must be zeroes!', [Name]);
        For j:=0 to Charset.High do begin
          if Charset[j] = '' then Charset[j]:=#0;
          cIndex[Charset[j][1]]:=j;
        end;
        SetLength(Face, Length(cSize));
          FillChar(Face[0], Length(cSize) * SizeOf(Face[0]), 0);
    //Building the faces substitution list
        For n:=0 to High(cSize) do begin
        // finding substitutes;
          For b:=False to True do
            For i:=False to True do
              For c:=False to True do begin
                fsn:=Trim(ini.ReadString ('faces_' + IntToStr(cSize[n]), facename[b, i, c] ,'<not found>'));
                if fsn <> '*' then begin
                  For b2:=False to True do
                    For i2:=False to True do
                      For c2:=False to True do
                        if fsn = facename[b2, i2, c2]
                          then begin
                            Face[n, b, i, c].Subs[0]:=True;
                            Face[n, b, i, c].Subs[1]:=b2;
                            Face[n, b, i, c].Subs[2]:=i2;
                            Face[n, b, i, c].Subs[3]:=c2;
                          end;
                  if not Face[n, b, i, c].Subs[0]
                    then Die(
                      'Начертание %0/%1: недопустимая подстановка "%2".'
                     ,'Face %0/%1: invalid substitute "%2".'
                     ,[cSize[n], facename[b, i, c], fsn])
                end
                else begin
                  Face[n, b, i, c].Subs[1]:=b;
                  Face[n, b, i, c].Subs[2]:=i;
                  Face[n, b, i, c].Subs[3]:=c;
                end;
              end;
        end;

   //Loading the font..
        For n:=0 to High(cSize) do begin
          For b:=False to True do
            For i:=False to True do
              For c:=False to True do
                With Face[n, b, i, c] do
                  if not Subs[0] then Try
          //Loading the face
                    SetLength(cGlyph, Charset.Length);
                    SetLength(char, Charset.Length);

                    gfname:=ExtractFilePath(fn) + filename + '_' + IntToStr(cSize[n] div 10) + IntToStr(cSize[n] mod 10) + '_' + facename[b,i,c];
                    otime:=max(initime, FileDateToDateTime(FileAge(gfname + '.png')));
                    kfname:= CgePath(FoLocalStoreDir + ExtractFileName(gfname) + '_kerning.png');
                    dfname:= CgePath(FoLocalStoreDir + ExtractFileName(gfname) + '_chardata');
                    tfname:= CgePath(FoLocalStoreDir + ExtractFileName(gfname) + '_texture.png');
                    sfname:= CgePath(FoLocalStoreDir + ExtractFileName(gfname) + '_shadow.png');
                    gfname+= '.png';

                    if   not (FileExists(kfname) and (FileDateToDateTime(FileAge(kfname)) > otime))
                      or not (FileExists(dfname) and (FileDateToDateTime(FileAge(dfname)) > otime))
                      or not (FileExists(tfname) and (FileDateToDateTime(FileAge(tfname)) > otime))
                      or not (FileExists(sfname) and (FileDateToDateTime(FileAge(sfname)) > otime))
                    then begin
                   //Reinstall the font
                      Console.SetDefaultBg(dbInstalling);
                      AddLog(RuEn(
                        'Настройка "%0" (%2.%1) ...',
                        'Setting up "%0" (%2.%1) ...'),
                        [Name ,facename[b, i, c], cSize[n]]);
                      _CgeHeartBeat;

                      // 38%
                      LoadImageFromFile(gfname, img);


                      // 31%
                      if img.Format <> ifGray8 then ConvertImage(img, ifGray8);
                      MaxContrastGray8(img);
                   //finding the initial zeroes
                      if img.width > img.height then begin
                        //a row
                        zero0left:= -1;
                        for j:=0 to img.Width - 1 do
                          if not IsColumnEmptyG8(img, j) then begin
                            zero0left:=j;
                            Break;
                          end;
                        if zero0left < 0 then Die('Bitmap calibration failure.');
                        zero0right:=-1;
                        for j:=zero0left + 2 to img.Width - 1 do
                          if IsColumnEmptyG8(img, j) then begin
                            zero0right:=j;
                            Break;
                          end;
                        if zero0right < 0 then Die('Bitmap calibration failure.');
                        zero0base:=-1;
                        for j:=img.height - 1 downto 2 do
                          if IsLineBrigther50PercentG8LR(img, j, zero0left, zero0right) then begin
                            zero0base:= j;
                            Break;
                          end;
                        if zero0base < 0 then Die('Bitmap calibration failure.');
                        maxw:=cSize[n] * 3; //maximum allowed character width.
                        spacing:=((zero0right - zero0left) div 2);
                          //space between characters at least as wide as a half of the the zero glyph.
                        L:=zero0left;
                        For a:=0 to charset.High do
                          Try
                          //find the boundaries
                            while (L < img.width - 1) and IsColumnEmptyG8(img, L) do inc(L);
                            cL:=L + 1;
                            cR:=cL;
                            while (cR - cL < Spacing) do begin
                              if cR >= img.width - 1 then Break;
                              if not IsColumnEmptyG8(img, cR) then cL:=cR;
                              inc(cR);
                            end;
//For w:='!' to #$ffff do if cIndex[w] =a then begin AddLog('. %0  (%1)',[w, ord(w)]); break end; _cgeHeartBeat;
                            if cR > img.width - 1 then cR:=img.width - 1;
//addlog('>  %0', [L]); _cgeHeartBeat;
                            CropGlyphG8Row(
                              img, cGlyph[a], char[a][gpBase], L, cR - L, zero0base);
//addlog('<'); _cgeHeartBeat;
                            L:=cR + 1;
      //AddLog('== "%0" [%4/%5] is %1x%2, %3', [w, cGlyph[n, cIndex[w], b, i, c].Width, cGlyph[n, cIndex[w], b, i, c].Height, cBase[n, cIndex[w], b, i, c], cSize[n], facename[b,i,c]]);
      //SaveImageToFile(CgePath('modules/cge/fonts/shit/' + IntToStr(ord(w))+'_'+IntToStr(cSize[n])+facename[b,i,c]+'.png'),cGlyph[n, cIndex[w], b, i, c]);
                          Except
                            For w:='!' to #$ffff do
                              if cIndex[w] =a then Die(
                              'Крах при загрузке буквы "%0" (код %1).'
                             ,'Crashed loading character "%0" (code %1).'
                             ,[w, ord(w)]);
                            Die(
                              'Крах при загрузке не-буквы №%0.'
                             ,'Crashed loading not-character #%0.',[a]);
                          End;
 //==============================================
                      end
                      else begin
                        //a column
                        zero0top:=-1;
                        For j:=0 to img.Height - 1 do
                          if not IsLineEmptyG8(img, j) then begin
                            zero0top:=j;
                            Break;
                          end;
                        if zero0top < 0 then Die('Bitmap calibration failure.');
                        zero0bottom:=-1;
                        For j:=zero0top to img.Height - 1 do
                          if IsLineEmptyG8(img, j) then begin
                            zero0bottom:=j - 1;
                            Break;
                          end;
                        if zero0bottom <= zero0top then Die('Bitmap calibration failure.');
                        zero0base:=-1;
                        for j:=zero0bottom downto zero0top do
                          if IsLineBrigther50PercentG8(img, j) then begin
                            zero0base:= j;
                            Break;
                          end;
                        if zero0base < 0 then Die('Bitmap calibration failure.');
                        zero1top:=-1;
                        For j:=zero0bottom + 1 to img.Height - 1 do
                          if not IsLineEmptyG8(img, j) then begin
                            zero1top:=j;
                            Break;
                          end;
                        if zero1top - zero0bottom < FO_MIN_INTERLINE_SPACING
                          then Die('Межстрочный интервал недопустимо мал.','Line spacing is too dense.',[]);
                        spacing:= zero1top - zero0top;
                        ultop:=zero0top - (zero1top - zero0bottom) div 2;
                        ulbase:=zero0base - ultop;
      //addlog('== %0/%1 spacing=%2  ultop=%3  ulbase=%4  zero0top=%5  zero1top=%6', [cSize[n], facename[b,i,c],spacing, ultop,ulbase,zero0top,zero1top]);
                    //loading and initializing glyphs
                        For a:=0 to charset.High do
                          Try
                          //load the glyph
                            CropGlyphG8Col(
                              img, cGlyph[a], char[a][gpBase], ultop + spacing * a, spacing, ulbase);
      //AddLog('== "%0" [%4/%5] is %1x%2, %3', [w, cGlyph[n, cIndex[w], b, i, c].Width, cGlyph[n, cIndex[w], b, i, c].Height, cBase[n, cIndex[w], b, i, c], cSize[n], facename[b,i,c]]);
      //SaveImageToFile(CgePath('modules/cge/fonts/shit/' + IntToStr(ord(w))+'_'+IntToStr(cSize[n])+facename[b,i,c]+'.png'),cGlyph[n, cIndex[w], b, i, c]);
                          Except
                            For w:='!' to #$ffff do
                              if cIndex[w] =a then Die(
                              'Крах при загрузке буквы "%0" (код %1).'
                             ,'Crashed loading character "%0" (code %1).'
                             ,[w, ord(w)]);
                            Die(
                              'Крах при загрузке не-буквы №%0.'
                             ,'Crashed loading not-character #%0.',[a]);
                          End;
                      end;
                      FreeImage(img);

                      //Calculating the kerning
                      NewImage(Charset.Length, Charset.Length, ifGray8, Kerning);
                      bp:=Kerning.Bits;
                      FillChar(bp^, Charset.Length * Charset.Length, 128);
                      for a:=0 to Charset.High do
                        for v:=0 to Charset.High do
                          pbyte(bp + a * Charset.Length + v)^:= 128 + CalculateKerning(
                            cGlyph[a], cGlyph[v], char[a, gpBase],  char[v, gpBase]);
                      SaveImageToFile(kfname, Kerning);

                      //Setting up the texture mapping
                      SetLength(char, Charset.Length);
                      FillChar(char[0], Sizeof(char[0]), 0);
                      x:=2; y:=2; ymax:=2;
                      for a:=0 to Charset.High do begin
                        char[a][gpW]:=cGlyph[a].Width;
                        char[a][gpH]:=cGlyph[a].Height;
                        if a > 0 then
                          if x + char[a-1][gpW] + 3 + char[a][gpW] >= FO_TEXTURE_WIDTH
                          then begin
                            y:=ymax + 4;
                            x:=2;
                          end
                          else inc(x, char[a-1][gpW] + 3);
                        ymax:=max(ymax, y + char[a][gpH] - 1);
                        char[a][gpX]:=x;
                        char[a][gpY]:=y;
                      end;
                      ymax:= GetHigherPowerOf2(ymax + 2);
                      ec;
                      if ymax > FO_MAX_TEXTURE_HEIGHT then Die(
                        'Символы не лезут в текстуру (максимальный размер %0х%1)',
                        'Character glyphs do not fit into the max res texture (%0x%1)',
                        [FO_TEXTURE_WIDTH, FO_MAX_TEXTURE_HEIGHT]);
//addlogcomment(' --%0x%1-- ',[FO_TEXTURE_WIDTH,ymax]);

                      //generating the texture image
                      NewImage(FO_TEXTURE_WIDTH, ymax, ifGray8, imT);
                      FillChar(imT.Bits^, FO_TEXTURE_WIDTH * ymax, 0);
                      for a:=0 to Charset.High do
                        CopyRect(cGlyph[a], 0, 0, char[a][gpW], char[a][gpH], imT, char[a][gpX], char[a][gpY]);
                      SaveImageToFile(tfname, imT);
                      
                      //creating the shadow:
                      CloneImage(imT, imS);
                      BlurredShadowG8 (imS, 1);
                      SaveImageToFile(sfname, imS);
                      
                      
                      //saving the texture mapping
                      f:=TFileStream.Create(dfname, fmCreate);
                      f.Write(char[0], Length(char) * SizeOf(char[0]));
                      f.Free;
                      
                      for a:=0 to Charset.High do FreeImage(cGlyph[a]);
                      FreeImage(imS);
                      FreeImage(imT);
                      
                      AddLogOk;
                      _CgeHeartBeat;
                    end
                    else begin
                      //Just load the font
                      SetLength(char, Charset.Length);
                      f:=TFileStream.Create(dfname, fmOpenRead);
                      f.Read(char[0], Length(char) * SizeOf(char[0]));
                      f.Free;
                      LoadImageFromFile(kfname, Kerning);
                      if Kerning.Format <> ifGray8 then Die('Неверный формат файла, "%0"','Wrong file format, "%0"',[kfname])
                    end;
                  Except
                     Die(
                      'Крах при загрузке начертания %0/%1.'
                     ,'Crashed loading face %0/%1.'
                     ,[cSize[n], facename[b, i, c]]);
                  End;
        end;
      Except
        Die('Крах при загрузке шрифта "%0". Возможно, нарушена файловая структура.',
            'Crashed loading font "%0". Possibly the file structure is corrupt.', [Name]);
      End;
      Charset.Free;
      ini.Free;
    end;

// -------------------------------------------------------------------------

function _foLoadFont (FileName: PAnsiChar; name: integer): boolean; cdecl;
begin
 eb
  TCgeFont(ResPtr[CGE_RESOURCE_FONT][name]).Load(FileName);
  Result:=True;
 ee
end;

function _foGetSysFont (index: integer): integer; cdecl;
begin
 eb
  if (index < 0) or (index > MAX_SYS_FONT)
    then Die('Недопустимый индекс системного шрифта (%0)','Invalid system font index (%0)', [index]);
  Result:=SystemFont[index];
 ee
end;

function _foGetFontname (index: integer): PAnsiChar; cdecl;
begin
 eb
  Result:=PAnsiChar(TCgeFont(ResPtr[CGE_RESOURCE_FONT][index]).Name);
 ee
end;

function _foGetFontTexture (font, size: integer; bold, italic, condensed: boolean; sizex, sizey: PInteger; shadow: pgluint): glUint; cdecl;
begin
 eb
  Result:=TCgeFont(ResPtr[CGE_RESOURCE_FONT][font]).GetTexture(size, bold, italic, condensed, sizex, sizey, shadow);
 ee
end;

//Returns the *actual* character size (culled to min..max supported by the font)
function _foCalcWord
  (chars: PWideChar; len, font, size: integer; bold, italic, condensed: boolean; word: PiCharArray;
   texture, shadow: pglUint): integer; cdecl;
begin
 eb
  Result:=TCgeFont(ResPtr[CGE_RESOURCE_FONT][font]).CalcWord(
     chars, len, size, bold, italic, condensed, word, texture, shadow);
 ee
end;


function _foGetSUpportedSize (font, size: integer): integer; cdecl;
begin
 eb
  Result:=TCgeFont(ResPtr[CGE_RESOURCE_FONT][font]).GetSUpportedSize(size);
 ee
end;


function TCGEFont.GetSupportedSize(i: integer): integer;
begin
  Result:=cSize[sIndex[max(0, min(i, MAX_CHARACTER_SIZE))]];
end;

function TCGEFont.CalcWord
  (chars: PWideChar; len, size: integer; bold, italic, condensed: boolean; word: PiCharArray;
   texture, shadow: pglUint): integer;
var
  si, i, p, tzx, tzy: integer;
  gd: PGlyphData;
begin
  size:=max(0, min(size, MAX_CHARACTER_SIZE));
  texture^:=GetTexture(size, bold, italic, condensed, @tzx, @tzy, shadow);
  si:=FigureFace(size, bold, italic, condensed);
  Result:=cSize[si];
  word^[0].x1:=0;
  For i:=0 to len - 1 do
    with word^[i] do begin
      if i > 0 then x1:=word^[i-1].x2 + _GetKerning(chars + (i-1), chars + i, si, bold, italic, condensed);
      gd:= _GetGlyphData(chars + i, si,  bold, italic, condensed);
      x2:= x1 + gd^[gpW];
      y2:= - gd^[gpBase];
      y1:= y2 + gd^[gpH];
      s1:= gd^[gpx] / tzx;
      t1:= gd^[gpy] / tzy;
      s2:= s1 + gd^[gpW] / tzx;
      t2:= t1 + gd^[gpH] / tzy;
    end;
end;


