views:

229

answers:

1

Hi.

I'm new with unicode, so I hope anybody can help me. I want write unicode to a PDF, now I need the width and height of the unicode text for text formatting. For AnsiString I have this functions in PDF class:

function PDFClass.TextWidth(Text: AnsiString): Single;
var
  i: integer;
  ch: AnsiChar;
  tmpWidth: Single;
  chv: Integer;
begin
  Result := 0;
  for I := 1 to Length(Text) do
  begin
    ch := Text[i];
    chv := CurrentFontObj.GetCharWidth(Text, I);
    tmpWidth := chv * CurrentFontObj.Size / 1000;
    if FHorizontalScaling <> 100 then
      tmpWidth := tmpWidth * FHorizontalScaling / 100;
    if tmpWidth > 0 then
      tmpWidth := tmpWidth + FCharSpace
    else
      tmpWidth := 0;
    if (ch = ' ') and (FWordSpace > 0) and (i <> Length(Text)) then
      tmpWidth := tmpWidth + FWordSpace;
    Result := Result + tmpWidth;
  end;
  Result := (Result / DocScale);
end;


function PDFClass.TextHeight(Text: AnsiString): Real;
begin
  Result := CurrentFontObj.Size * CurrentFontObj.Ascent / 1000;
end;

Can anybody help me with this function for unicode text? I use this component in my C++Builder 2009 with UnicodeString.

CurrentFontObj is from class PDFFontObj

  PDFFontObj = class(TObject)
  private
    Name: AnsiString;
    Size: Single;
    ArrIndex: Integer;
    Saved: boolean;
    OldName: AnsiString;
    Ascent: Integer;
    FActive: boolean;
    IsUsed: boolean;
    UniLen: Integer;
    FontLen: Integer;
    IsUnicode: boolean;
    IsVertical: boolean;
    OrdinalName: AnsiString;
    IsStandard: boolean;
    FontStyle: TFontStyles;
    FontCharset: TFontCharset;
    IsMonospaced: boolean;
    OutTextM: OUTLINETEXTMETRIC;
    ABCArray: array[0..255] of ABC;
    Symbols: array of CDescript;
    UnicodeTable: array of IndexedChar;
    SymbolTable: array[32..255] of boolean;
    function GetCharWidth(AText: AnsiString; APos: integer): integer;
    function GetCodeByID(ID: Word): Word;
    procedure CopyFontFetures(InFnt: PDFFontObj);
    procedure GetFontFeatures;
    procedure ParseFontName;
    procedure ClearTables;
  end;

function PDFFontObj.GetCharWidth(AText: AnsiString; APos: integer): integer;
var
  ChCode: Byte;
begin
  ChCode := Ord(AText[APos]);
  if not IsMonospaced then
    Result := ABCArray[ChCode].abcA + Integer(ABCArray[ChCode].abcB) + ABCArray[ChCode].abcC
  else
    Result := ABCArray[0].abcA + Integer(ABCArray[0].abcB) + ABCArray[0].abcC;
end;
A: 

In a comment you write

I would like to write 3 texts in a row, each text in a different color. For this I need the pixel width of each text. For AnsiString it's:

int width = PDF->CurrentPage->TextWidth(ansi_text);

but I need the width of a Unicode text, like:

int width = PDF->CurrentPage->TextWidthUnicode(unicode_text_with_chinese_signs);

Short answer:
If you have code that does the right thing for Ansi strings, then it should work properly with Unicode without changes.

Longer answer:
The PDFClass.TextWidth() method does loop through all the characters in the string, calls a function of the current font object to get the width of the single character, applies scaling and adds extra space between characters and words. This is more or less what you can read in chapter 9 of the PDF 1.7 specification.

The code you posted is incomplete, but I suspect that it is not working correctly for Ansi strings already. There are Ansi encodings that can use multiple characters to encode one visible character glyph, the CJK multibyte encodings. Looping over single bytes in the Ansi string and summing up the returned widths is not going to work correctly there. The code may somehow account for this, but your snippet doesn't show it.

The code may also have problems with different single byte Ansi encodings, but it's hard to tell from the snippets you posted. If you take an Ansi program that uses non-ASCII characters (like 'ä' or 'á') and run it on for example a Russian Windows version, would the created PDF files contain cyrillic glyphs? If not, then the code does not put the necessary encoding information into the PDF. If so then it's not going to work for Unicode strings either. Consult the PDF spec for more information about encoding issues.

Note also that the code may not work for all users, even when it compiles:

  • You will need a font that contains all necessary glyphs (Chinese in your example). If it doesn't, then a fallback glyph will be displayed instead. If the system does not have exactly the same font, then a similar but different font may be used instead, which may have different widths for some or all of the glyphs, in which case the calculated width will be wrong. For that reason it is best to embed the TrueType font (better even only the used glyphs of the font) in the PDF document. The TTF font may or may not be embeddable, and you may not have the right to embed it and distribute the document.

  • Some languages will place glyphs from right to left, or even stack them vertically.

  • Some languages will use other code points to separate words, the code in the question recognizes only spaces as word delimiters. This will again cause the calculation to produce different results if FWordSpace does not equal 0.

mghie