Шрифты.Чтение/запись отдельных символов
Случается, что для реализации какой-либо идеи необходимы несколько нестандартных символов. Найти подходящий шрифт не проблема. Существует множество бесплатных шрифтов с самыми разными начертаниями. Не проблема и само подключение и использование этого шрифта в своей программе. Но всегда ли оправдано распространение шрифта с программой, если необходимы только несколько символов из него. И можно ли как-то обойтись без этого не слишком усложняя себе жизнь ? Оказывается можно и сейчас мы посмотрим как это сделать.

Во-первых установим шрифт стандартными средствами Windows и определимся с символами, которые нам нужны. Для тестового примера опустим этот этап и используем уже существующий в системе шрифт Times New Roman. Предположим нужно сохранить описание символа <A>.

Объект GDI+ TGPGraphicsPath содержит метод GetPathData, которые позволяет для любого пути получить перечень линий и кривых Безье, из которых он сформирован. Вот эти данные мы и сохраним в файл:

pascal
var p : TGPGraphicsPath;
    f : TGPFontFamily;
    D : TPathData;
    C : integer;
    FS: TFileStream;
begin
   p := TGPGraphicsPath.Create;
   D := TPathData.Create;
   f := TGPFontFamily.Create('Times New Roman');
   FS := TFileStream.Create('test.dat', fmCreate);
   try

      p.AddString('A', 1, f, FontStyleBold, 32, MakePoint(0.0, 0.0), nil);
      p.GetPathData(D);

      C := D.Count;
      FS.Write(C, sizeOF(C));
      FS.Write(D.Points^, sizeOF(TGPPointF)*C);
      FS.Write(D.Types^, sizeOF(byte)*C);

   finally
      FS.free;
      f.free;
      D.free;
      p.free;
   end;

Здесь мы сохраняем координаты точек как TGPPointF. Размер этой структуры 8 байт и если символов будет много, получится достаточно большой файл. В принципе можно несколько усложнить код и использовать для хранения каждой координаты один байт. Для этого достаточно разделить значение координаты на 5 при записи и соответствнно умножить при чтении. При размере шрифта 32 пункта координаты в этом случае как раз помещаются в диапазон [0 .. 255]. Незначительная потеря точности при округлении практически не сказывается на виде символов, а размера 32 пункта достаточно, чтобы без искажений мастабировать символы как минимум до размера 128 пунктов.

Попробуем теперь считать символ. Процедура чтения такая-же простая как и запись:

pascal
procedure ReadPathData(F:TFileStream; P:TPathData);
var C:integer;
begin
   F.Read(C, sizeOF(C));
   P.Count := C;

   if assigned(P.Points) then FreeMem(P.Points);
   if assigned(P.Types) then freemem(P.Types);

   getmem(P.Points, SizeOf(TGPPointF) * C);
   Getmem(P.Types, C);

   F.Read(P.Points^, sizeOF(TGPPointF)*C);
   F.Read(P.Types^, sizeOF(byte)*C);
end;

Построить по этим данным путь также не сложно.

pascal
procedure MakePathFromPathData(D:TPathData; P:TGPGraphicsPath);
type
   paPoints = ^ taPoints;
   taPoints = array[word] of TGPPointF;
   paTypes = ^ taTypes;
   taTypes = array[word] of byte;

var LP, P1 :TGPPointF;
    T1 :byte;
    C : integer;
begin
   if D.count < 1 then exit;

   LP := paPoints(D.Points)[0];
   C := 1;
   while C < D.Count do begin
      T1 := paTypes(D.Types)[C];
      P1 := paPoints(D.Points)[C];
      case PathPointType(T1 and $07) of
         PathPointTypeStart  : P.StartFigure;
         PathPointTypeLine   : P.AddLine(LP, P1);
         PathPointTypeBezier :
           begin
              P.AddBezier(LP, P1,
                              paPoints(D.Points)[C+1],
                              paPoints(D.Points)[C+2]);
              inc(C, 2);
              T1 := paTypes(D.Types)[C];
              P1 := paPoints(D.Points)[C];
           end;
      end;
      if (T1 and $80) <> 0
      then P.CloseFigure;

      LP := P1;
      inc(C);
   end;
end;

Теперь остается только масштабировать путь до нужного нам размера, переместить его в нужную позицию и отрисовать:

pascal
         //...
         D := TPathData.Create;
         ReadPathData(F, D);
         P := TGPGraphicsPath.Create;
         m := TGPMatrix.Create;
         try
            MakePathFromPathData(D, P);
            m.Scale(2, 2);
            m.Translate(x, 0, MatrixOrderAppend);
            P.Transform(m);

            g.FillPath(brush, P);
            P.GetBounds(B);
            x := B.X + B.Width;
         //...

В архиве полный код тестового примера. Для чтения используется подготовленный файл с тремя, следующими подряд символами:

 

21 марта 2010

Downloads
Тестовое приложение с кодом, приведенным в статье (test_fonts.zip) (5.3 Кб, просмотров: 1510 )

No comments
Вы можете оставить комментарий или задать вопрос
Ваше имя:

Текст сообщения:


Copyright © 2009-2014 by