Как нарисовать Ангстрем (Å), используя AggPas? - PullRequest
1 голос
/ 09 марта 2011

Суммирование:

Не удалось найти способ вывода знака ангстрема с помощью библиотеки AggPas.

===============================================

Функция рисования текста в библиотеке AggPas принимает параметр PAnsiChar. Мне интересно, как я могу использовать PAnsiChar, чтобы указывать на текст, содержащий ангстрем (Å)?

  SetThreadLocale($0409);                                       // No difference.

  ShowMessage(Chr(0197));                                       // Correct
  ShowMessage(AnsiString(Chr(0197)));                           // Wrong - question mark
  ShowMessage(PAnsiChar(Chr(0197)));                            // AV
  ShowMessage(UTF8String(Chr(0197)));                           // Correct
  ShowMessage(UTF8Encode(Chr(0197)));                           // Correct
  ShowMessage(RawByteString(Chr(0197)));                        // Wrong - question mark

  ShowMessage(AnsiString(UTF8String(Chr(0197))));               // Wrong - question mark
  ShowMessage(AnsiString(UTF8Encode(Chr(0197))));               // Correct

  ShowMessage(RawByteString(UTF8String(Chr(0197))));            // Wrong - question mark
  ShowMessage(RawByteString(UTF8Encode(Chr(0197))));            // Correct

  ShowMessage(PAnsiChar(AnsiString(UTF8Encode(Chr(0197)))));    // Wrong - strange character
  ShowMessage(PAnsiChar(RawByteString(UTF8Encode(Chr(0197))))); // Wrong - strange character

Для вашего удобства процедура DrawTextCenterAligned в следующем коде не может выводить букву ангстрема.

    unit u2DRenderEngine_aggpas;

    interface

    uses
      u2DRenderEngine, uMathVector3D,
      agg_2D,
      Graphics, IniFiles, Types;

    type
      T2DRenderEngine_aggpas = class;

      T2DRenderEngine_aggpas = class(T2DRenderEngine)

      private
        fFontBMP: TBitmap;
        fVG: Agg2D;

      protected

        function GetActualStringBoundingBox(aText: string; aFont: TFont)
          : TRect; override;

      public

        constructor Create;
        destructor Destroy; override;

        procedure AttachBMP(aBMP: TBitmap; flip_y: Boolean);

        procedure Flush; override;

        procedure DrawLine(aP, bP: TPoint3D; aPen: TPen); override;
        procedure DrawCircle(Center: TPoint3D; Radius: Extended;
          R, G, B: Integer); override;
        procedure FillCircle(Center: TPoint3D; Radius: Extended;
          R, G, B: Integer); override;
        procedure DrawPolygon(aPts: TAPoint3D; R, G, B: Integer); override;
        procedure FillPolygon(aPts: TAPoint3D; R, G, B: Integer); override;

        procedure DrawTextLeftAligned(aLeft: TPoint3D; aText: string; aFont: TFont;
          clearBackground: Boolean); override;
        procedure DrawTextCenterAligned(aCenter: TPoint3D; aText: string;
          aFont: TFont; clearBackground: Boolean); override;

      end;

    implementation

    uses
      u2DUtils_Vcl, SysUtils, Math;

    { TRenderEngine_2D_aggpas }

    constructor T2DRenderEngine_aggpas.Create;
    begin
      inherited;

      fFontBMP := TBitmap.Create;
      fFontBMP.Width := 2;
      fFontBMP.Height := 2;

      fVG.Construct;
    end;

    destructor T2DRenderEngine_aggpas.Destroy;
    begin

      inherited;
    end;

    procedure T2DRenderEngine_aggpas.AttachBMP(aBMP: TBitmap; flip_y: Boolean);
    var
      tmpBuffer: pointer;
      tmpStride: Integer;
    begin
      if aBMP.Empty then
        raise Exception.Create('AttachBMP: aBMP is Empty!');

      if aBMP.PixelFormat <> pf32bit then
        raise Exception.Create('AttachBMP: aBMP should be 32bit!');

      tmpStride := Integer(aBMP.ScanLine[1]) - Integer(aBMP.ScanLine[0]);

      if tmpStride < 0 then
        tmpBuffer := aBMP.ScanLine[aBMP.Height - 1]
      else
        tmpBuffer := aBMP.ScanLine[0];

      if flip_y then
        tmpStride := tmpStride * -1;

      fVG.attach(tmpBuffer, aBMP.Width, aBMP.Height, tmpStride);
    end;

    procedure T2DRenderEngine_aggpas.Flush;
    begin
    end;

    procedure T2DRenderEngine_aggpas.DrawLine(aP, bP: TPoint3D; aPen: TPen);
    begin
      fVG.line(aP.X, aP.Y, bP.X, bP.Y);
    end;

    procedure T2DRenderEngine_aggpas.DrawCircle(Center: TPoint3D; Radius: Extended;
      R, G, B: Integer);
    begin
      fVG.lineColor(R, G, B);
      fVG.noFill;
      fVG.ellipse(Center.X, Center.Y, Radius, Radius);
    end;

    procedure T2DRenderEngine_aggpas.FillCircle(Center: TPoint3D; Radius: Extended;
      R, G, B: Integer);
    begin
      fVG.fillColor(R, G, B);
      fVG.noLine;
      fVG.ellipse(Center.X, Center.Y, Radius, Radius);
    end;

    procedure T2DRenderEngine_aggpas.DrawPolygon(aPts: TAPoint3D; R, G, B: Integer);
    var
      Len, I: Integer;
      poly: array of double;
    begin
      Len := Length(aPts);

      SetLength(poly, Len * 2);
      for I := 0 to Len - 1 do
      begin
        poly[2 * I] := aPts[I].X;
        poly[2 * I + 1] := aPts[I].Y;
      end;

      fVG.lineColor(R, G, B);
      fVG.noFill;
      fVG.polygon(@poly[0], 4);
    end;

    procedure T2DRenderEngine_aggpas.FillPolygon(aPts: TAPoint3D; R, G, B: Integer);
    var
      Len, I: Integer;
      poly: array of double;
    begin
      Len := Length(aPts);

      SetLength(poly, Len * 2);
      for I := 0 to Len - 1 do
      begin
        poly[2 * I] := aPts[I].X;
        poly[2 * I + 1] := aPts[I].Y;
      end;

      fVG.fillColor(R, G, B);
      fVG.noLine;
      fVG.polygon(@poly[0], 4);
    end;

    procedure T2DRenderEngine_aggpas.DrawTextLeftAligned(aLeft: TPoint3D;
      aText: string; aFont: TFont; clearBackground: Boolean);
    var
      tmpRect: TRect;
      tmpRectWidth, tmpRectHeight: Integer;
      tmpPt: TPoint3D;
    begin
      tmpRect := GetActualStringBoundingBox(aText, aFont);
      tmpRectWidth := tmpRect.Right - tmpRect.Left;
      tmpRectHeight := tmpRect.Bottom - tmpRect.Top;
      tmpPt.X := aLeft.X;
      tmpPt.Y := aLeft.Y - tmpRectHeight;

      if clearBackground then
      begin
        fVG.fillColor(255, 255, 255);
        fVG.noLine;
        fVG.Rectangle(tmpPt.X, tmpPt.Y, tmpPt.X + tmpRectWidth,
          tmpPt.Y + tmpRectHeight);
      end;

      // Font & Colors
      fVG.fillColor(0, 0, 0);
      fVG.noLine;
      fVG.TextHints(True);
      if Agg2DUsesFreeType then
        fVG.Font(PAnsiChar(AnsiString(UTF8Encode(LowerCase(aFont.Name) + '.ttf'))),
          Abs(aFont.Height))
      else
        fVG.Font('Arial', 40.0);
      // Text
      fVG.Text(tmpPt.X, tmpPt.Y + tmpRectHeight, PAnsiChar(AnsiString(aText)));
    end;

    procedure T2DRenderEngine_aggpas.DrawTextCenterAligned(aCenter: TPoint3D;
      aText: string; aFont: TFont; clearBackground: Boolean);
    var
      tmpRect: TRect;
      tmpRectWidth, tmpRectHeight: Integer;
      tmpPt: TPoint3D;
    begin
      tmpRect := GetActualStringBoundingBox(aText, aFont);
      tmpRectWidth := tmpRect.Right - tmpRect.Left;
      tmpRectHeight := tmpRect.Bottom - tmpRect.Top;
      tmpPt.X := aCenter.X - tmpRectWidth / 2.0;
      tmpPt.Y := aCenter.Y - tmpRectHeight / 2.0;

      if clearBackground then
      begin
        fVG.fillColor(255, 255, 255);
        fVG.noLine;
        fVG.Rectangle(tmpPt.X, tmpPt.Y, tmpPt.X + tmpRectWidth,
          tmpPt.Y + tmpRectHeight);
      end;

      // Font & Colors
      fVG.fillColor(0, 0, 0);
      fVG.noLine;
      fVG.TextHints(True);
      if Agg2DUsesFreeType then
        fVG.Font(PAnsiChar(AnsiString(UTF8Encode(LowerCase(aFont.Name) + '.ttf'))),
          Abs(aFont.Height))
      else
        fVG.Font('Arial', 40.0);
      // Text
      fVG.Text(tmpPt.X, tmpPt.Y + tmpRectHeight, PAnsiChar(AnsiString(aText)));
    end;

    function T2DRenderEngine_aggpas.GetActualStringBoundingBox(aText: string;
      aFont: TFont): TRect;
    var
      tmpRectWidth, tmpRectHeight: Integer;
    begin
      Self.fFontBMP.Canvas.Font.Assign(aFont);
      tmpRectWidth := Self.fFontBMP.Canvas.TextWidth(aText);
      tmpRectHeight := Self.fFontBMP.Canvas.TextHeight(aText);

      // 2011-03-07 hard-coded
      tmpRectWidth := Ceil(tmpRectWidth * 1.05);
      // 2011-03-07 hard-coded
      tmpRectHeight := Ceil(tmpRectHeight * 0.70);

      FillChar(Result, SizeOf(Result), 0);
      Result.Right := tmpRectWidth;
      Result.Bottom := tmpRectHeight;
    end;

    end.

Ответы [ 2 ]

2 голосов
/ 09 марта 2011

Если устройство принимает свой вход за PAnsiChar, вы тост.Если кодовая страница по умолчанию в вашей системе не может кодировать символ Å, просто невозможно поместить эту информацию в ANSI CHAR.И если бы такое кодирование было доступно, все ваши подпрограммы, которые теперь показывают вопросительные знаки, показывали бы правильный символ.


Чуть более длинная информация:

Unicode кодирует огромное количество символоввключая все символы на всех письменных языках, специальные символы, музыкальные ноты, пространство настолько велико, что есть кодировка для клингонских символов!Символы Ansi кодируются с использованием таблицы поиска, которая отображает значения, которые могут быть закодированы в один байт, для выбора символов Unicode.

При использовании AnsiString вы можете использовать не более 256 символов Unicode одновременно.Когда вы пытаетесь закодировать один символ Unicode в один AnsiString, вы, по сути, делаете поиск в таблице кодовых страниц, ища код, который указывает на исходный символ Unicode.Если такая кодировка недоступна, вы получите знаменитый знак вопроса!


Вот процедура, которая преобразует строку в строку UTF8 и возвращает ее как AnsiString (все UTF8 действительно допустимы - но бессмысленны - AnsiString):

function Utf8AsAnsiString(s:string):AnsiString;
var utf8s:UTF8String;
begin
  utf8s := UTF8String(s);
  SetLength(Result, Length(utf8s));
  if Length(utf8s) > 0 then
    Move(utf8s[1], Result[1], Length(utf8s));
end;

Вы можете передать результат этой функции и надеяться, что устройство сможет обрабатывать UTF8.Удачи.

1 голос
/ 09 марта 2011

Вам нужен промежуточный шаг для загрузки символа в строку, например так:

const
  ANGSTROM = Chr(0197);

procedure ShowAngstrom;
var
  message: AnsiString;
begin
  message := ANGSTROM;
  ShowMessage(PAnsiChar(message));
end;

РЕДАКТИРОВАТЬ: Вот предположение о том, что проблема может быть, если это не работает для AggPas.

Я не знаком с AggPas, но я использовал графическую библиотеку Asphyre, и ее система рисования текста, еще в дни до Unicode, требовала, чтобы вы генерировали специализированное растровое изображение, предоставив ему файл шрифта и диапазон символов, которые он может напечатать. (Не уверен, что это улучшилось с тех пор; я не использовал это некоторое время.) Любой символ вне этого диапазона не будет печататься должным образом.

Если AggPas работает аналогичным образом, возможно, что изображение шрифта, которое у вас есть, не содержит ничего для символа 197, поэтому независимо от того, насколько корректен ваш код Delphi, ему нечего сопоставлять, и вы не собирается получить правильный вывод. Посмотрим, сможешь ли ты это проверить.

Если нет ... тогда у меня нет идей, и я надеюсь, что кто-то еще здесь более знаком с этой проблемой.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...