Delphi和带有unicode文本的TextWidth / TextHeight

时间:2010-04-14 09:05:06

标签: delphi pdf unicode

我是unicode的新手,所以我希望有人可以帮助我。我想将unicode写入PDF,现在我需要unicode文本的宽度和高度来进行文本格式化。对于AnsiString,我在PDF类中有这个功能:

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;

任何人都可以帮我使用unicode文本的这个功能吗?我在带有UnicodeString的C ++ Builder 2009中使用了这个组件。

CurrentFontObj来自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;

1 个答案:

答案 0 :(得分:0)

在你写的评论中

  

我想连续写3个文本,每个文本用不同的颜色。为此,我需要每个文本的像素宽度。对于AnsiString,它是:

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

但我需要Unicode文本的宽度,例如:

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

简答:
如果你的代码对Ansi字符串做了正确的事情,那么它应该可以正常使用Unicode而无需更改。

更长的回答:
PDFClass.TextWidth()方法循环遍历字符串中的所有字符,调用当前字体对象的函数以获取单个字符的宽度,应用缩放并在字符和单词之间添加额外的空格。这或多或少是您在PDF 1.7 specification的第9章中可以阅读的内容。

您发布的代码不完整,但我怀疑它已经无法正常使用Ansi字符串。有Ansi编码可以使用多个字符来编码一个可见字符字形,即CJK多字节编码。循环Ansi字符串中的单个字节并总结返回的宽度在那里无法正常工作。代码可能会以某种方式解释此问题,但您的代码段不会显示它。

代码也可能存在不同的单字节Ansi编码问题,但很难从您发布的代码片段中分辨出来。如果您使用非ASCII字符(例如'ä'或'á')的Ansi程序并以例如俄语Windows版本运行它,创建的PDF文件是否包含西里尔字形?如果没有,则代码不会将必要的编码信息放入PDF中。如果是这样,那么它也无法用于Unicode字符串。有关编码问题的更多信息,请参阅PDF规范。

另请注意,即使编译代码,代码也可能不适用于所有用户:

  • 您需要一个包含所有必需字形的字体(在您的示例中为中文)。如果没有,则会显示后备字形。如果系统不具有完全相同的字体,则可以使用类似但不同的字体,其可以对于一些或所有字形具有不同的宽度,在这种情况下,计算的宽度将是错误的。因此,最好在PDF文档中嵌入TrueType字体(甚至只使用字体中使用的字形)。 TTF字体可能是也可能不是可嵌入的,您可能没有权利嵌入它并分发文档。

  • 有些语言会从右到左放置字形,甚至可以垂直堆叠。

  • 有些语言会使用其他代码点来分隔单词,问题中的代码只将空格识别为单词分隔符。如果FWordSpace不等于0,这将再次导致计算产生不同的结果。