* More fixes from Graeme, hopefully UTF16 works now fully

git-svn-id: trunk@33453 -
This commit is contained in:
michael 2016-04-08 18:16:46 +00:00
parent f8e9b33f99
commit 897547b115
4 changed files with 122 additions and 614 deletions

View File

@ -31,26 +31,6 @@ type
// Tables recognized in this unit.
TTTFTableType = (ttUnknown,ttHead,tthhea,ttmaxp,tthmtx,ttcmap,ttname,ttOS2,ttpost);
TPDFFontDefinition = Record
FontType : String;
FontName : String;
Ascender : Integer;
Descender : Integer;
CapHeight : Integer;
Flags : Integer;
BBox : Array[0..3] of Integer;
ItalicAngle : Integer;
StemV : Integer;
MissingWidth : integer;
FontUp : Integer;
FontUt : Integer;
Encoding : String;
FontFile : String;
Diffs : String;
CharWidths : String;
OriginalSize : integer;
end;
TSmallintArray = Packed Array of Int16;
TWordArray = Packed Array of UInt16;
@ -253,10 +233,11 @@ Type
FWidths: TLongHorMetrics; // hmtx data
// Needed to create PDF font def.
FOriginalSize : Cardinal;
MissingWidth: Integer;
FMissingWidth: Integer;
FNameEntries: TNameEntries;
{ This only applies to TFixedVersionRec values. }
function FixMinorVersion(const AMinor: word): word;
function GetMissingWidth: integer;
Protected
// Stream reading functions.
Function IsNativeData : Boolean; virtual;
@ -273,23 +254,10 @@ Type
procedure ParseOS2(AStream : TStream); virtual;
procedure ParsePost(AStream : TStream); virtual;
// Make differences for postscript fonts
procedure PrepareEncoding(Const AEnCoding : String);
procedure PrepareEncoding(Const AEncoding : String);
function MakeDifferences: String; virtual;
// Utility function to convert FShort to natural units
Function ToNatural(AUnit: Smallint) : Smallint;
// Some utility functions to create the PDF font definition
Procedure MakePDFFontDefinitionFile(Const FontFile,Section,AEncoding: string); virtual;
Function Flags : Integer;
Function Bold: Boolean;
Function StemV: SmallInt;
Function Embeddable : Boolean;
Function Ascender: SmallInt;
Function Descender: SmallInt;
{ Also know as the linegap. "Leading" is the gap between two lines. }
Function Leading: SmallInt;
Function CapHeight: SmallInt;
{ Returns the glyph advance width, based on the AIndex (glyph index) value. The result is in font units. }
function GetAdvanceWidth(AIndex: word): word;
public
Chars: TWordArray;
CharWidth: array[0..255] of SmallInt;
@ -304,17 +272,32 @@ Type
// Load a TTF file from file or stream.
Procedure LoadFromFile(const AFileName : String);
Procedure LoadFromStream(AStream: TStream); virtual;
// Checks if Embedded is allowed, and also prepares CharWidths array
// Checks if Embedded is allowed, and also prepares CharWidths array. NOTE: this is possibly not needed any more.
procedure PrepareFontDefinition(const Encoding:string; Embed: Boolean);
// Fill record with PDF Font definition data.
Procedure FillPDFFontDefinition(Out ADef: TPDFFontDefinition; Const AFontFile,AEncoding : String);
// Write Font Definition data to a file named FontFile.
procedure MakePDFFontDef(const FontFile: string; const Encoding: string; Embed: Boolean);
// The following are only valid after the file was succesfully read.
// Font file header info.
Function Flags : Integer;
Function Bold: Boolean;
Function StemV: SmallInt;
Function Embeddable : Boolean;
Function Ascender: SmallInt;
Function Descender: SmallInt;
{ Also know as the linegap. "Leading" is the gap between two lines. }
Function Leading: SmallInt;
Function CapHeight: SmallInt;
{ Returns the glyph advance width, based on the AIndex (glyph index) value. The result is in font units. }
function GetAdvanceWidth(AIndex: word): word;
function ItalicAngle: LongWord;
{ max glyph bounding box values - as space separated values }
function BBox: string;
property MissingWidth: Integer read GetMissingWidth;
{ original font file size }
property OriginalSize: Cardinal read FOriginalSize;
property Filename: string read FFilename;
Property Directory : TTableDirectory Read FTableDir;
Property Tables : TTableDirectoryEntries Read FTables;
// The various tables as present in the font file.
Property Head : THead Read FHead;
Property HHead : THHead Read FHHead;
property CmapH : TCMapHeader Read FCmapH;
@ -338,12 +321,9 @@ type
// Convert string to known table type
Function GetTableType(Const AName : String) : TTTFTableType;
// Utility functions for text encoding conversions
function ConvertUTF8ToUTF16(Dest: PWideChar; DestWideCharCount: SizeUInt; Src: PChar; SrcCharCount: SizeUInt;
Options: TConvertOptions; out ActualWideCharCount: SizeUInt): TConvertResult;
function UTF8ToUTF16(const P: PChar; ByteCnt: SizeUInt): UnicodeString;
function UTF8ToUTF16(const S: AnsiString): UnicodeString;
function StrToUTF16Hex(const AValue: UnicodeString; AIncludeBOM: boolean = True): AnsiString;
{ To overcome the annoying compiler hint: "Local variable does not seem to be initialized" }
procedure FillMem(Dest: pointer; Size: longint; Data: Byte );
Const
@ -373,8 +353,6 @@ Const
implementation
uses
inifiles;
resourcestring
rsFontEmbeddingNotAllowed = 'Font licence does not allow embedding';
@ -386,222 +364,6 @@ begin
Result:=Pred(Result);
end;
{------------------------------------------------------------------------------
Name: ConvertUTF8ToUTF16
Params: Dest - Pointer to destination string
DestWideCharCount - Wide char count allocated in destination string
Src - Pointer to source string
SrcCharCount - Char count allocated in source string
Options - Conversion options, if none is set, both
invalid and unfinished source chars are skipped
toInvalidCharError - Stop on invalid source char and report
error
toInvalidCharToSymbol - Replace invalid source chars with '?'
toUnfinishedCharError - Stop on unfinished source char and
report error
toUnfinishedCharToSymbol - Replace unfinished source char with '?'
ActualWideCharCount - Actual wide char count converted from source
string to destination string
Returns:
trNoError - The string was successfully converted without
any error
trNullSrc - Pointer to source string is nil
trNullDest - Pointer to destination string is nil
trDestExhausted - Destination buffer size is not big enough to hold
converted string
trInvalidChar - Invalid source char has occured
trUnfinishedChar - Unfinished source char has occured
Converts the specified UTF-8 encoded string to UTF-16 encoded (system endian)
------------------------------------------------------------------------------}
function ConvertUTF8ToUTF16(Dest: PWideChar; DestWideCharCount: SizeUInt;
Src: PChar; SrcCharCount: SizeUInt; Options: TConvertOptions;
out ActualWideCharCount: SizeUInt): TConvertResult;
var
DestI, SrcI: SizeUInt;
B1, B2, B3, B4: Byte;
W: Word;
C: Cardinal;
function UnfinishedCharError: Boolean;
begin
if toUnfinishedCharToSymbol in Options then
begin
Dest[DestI] := System.WideChar('?');
Inc(DestI);
Result := False;
end
else
if toUnfinishedCharError in Options then
begin
ConvertUTF8ToUTF16 := trUnfinishedChar;
Result := True;
end
else Result := False;
end;
function InvalidCharError(Count: SizeUInt): Boolean; inline;
begin
if not (toInvalidCharError in Options) then
begin
if toInvalidCharToSymbol in Options then
begin
Dest[DestI] := System.WideChar('?');
Inc(DestI);
end;
Dec(SrcI, Count);
// skip trailing UTF-8 char bytes
while (Count > 0) do
begin
if (Byte(Src[SrcI]) and %11000000) <> %10000000 then Break;
Inc(SrcI);
Dec(Count);
end;
Result := False;
end
else
if toInvalidCharError in Options then
begin
ConvertUTF8ToUTF16 := trUnfinishedChar;
Result := True;
end;
end;
begin
ActualWideCharCount := 0;
if not Assigned(Src) then
begin
Result := trNullSrc;
Exit;
end;
if not Assigned(Dest) then
begin
Result := trNullDest;
Exit;
end;
SrcI := 0;
DestI := 0;
while (DestI < DestWideCharCount) and (SrcI < SrcCharCount) do
begin
B1 := Byte(Src[SrcI]);
Inc(SrcI);
if B1 < 128 then // single byte UTF-8 char
begin
Dest[DestI] := System.WideChar(B1);
Inc(DestI);
end
else
begin
if SrcI >= SrcCharCount then
if UnfinishedCharError then Exit(trInvalidChar)
else Break;
B2 := Byte(Src[SrcI]);
Inc(SrcI);
if (B1 and %11100000) = %11000000 then // double byte UTF-8 char
begin
if (B2 and %11000000) = %10000000 then
begin
Dest[DestI] := System.WideChar(((B1 and %00011111) shl 6) or (B2 and %00111111));
Inc(DestI);
end
else // invalid character, assume single byte UTF-8 char
if InvalidCharError(1) then Exit(trInvalidChar);
end
else
begin
if SrcI >= SrcCharCount then
if UnfinishedCharError then Exit(trInvalidChar)
else Break;
B3 := Byte(Src[SrcI]);
Inc(SrcI);
if (B1 and %11110000) = %11100000 then // triple byte UTF-8 char
begin
if ((B2 and %11000000) = %10000000) and ((B3 and %11000000) = %10000000) then
begin
W := ((B1 and %00011111) shl 12) or ((B2 and %00111111) shl 6) or (B3 and %00111111);
if (W < $D800) or (W > $DFFF) then // to single wide char UTF-16 char
begin
Dest[DestI] := System.WideChar(W);
Inc(DestI);
end
else // invalid UTF-16 character, assume double byte UTF-8 char
if InvalidCharError(2) then Exit(trInvalidChar);
end
else // invalid character, assume double byte UTF-8 char
if InvalidCharError(2) then Exit(trInvalidChar);
end
else
begin
if SrcI >= SrcCharCount then
if UnfinishedCharError then Exit(trInvalidChar)
else Break;
B4 := Byte(Src[SrcI]);
Inc(SrcI);
if ((B1 and %11111000) = %11110000) and ((B2 and %11000000) = %10000000)
and ((B3 and %11000000) = %10000000) and ((B4 and %11000000) = %10000000) then
begin // 4 byte UTF-8 char
C := ((B1 and %00011111) shl 18) or ((B2 and %00111111) shl 12)
or ((B3 and %00111111) shl 6) or (B4 and %00111111);
// to double wide char UTF-16 char
Dest[DestI] := System.WideChar($D800 or ((C - $10000) shr 10));
Inc(DestI);
if DestI >= DestWideCharCount then Break;
Dest[DestI] := System.WideChar($DC00 or ((C - $10000) and %0000001111111111));
Inc(DestI);
end
else // invalid character, assume triple byte UTF-8 char
if InvalidCharError(3) then Exit(trInvalidChar);
end;
end;
end;
end;
if DestI >= DestWideCharCount then
begin
DestI := DestWideCharCount - 1;
Result := trDestExhausted;
end
else
Result := trNoError;
Dest[DestI] := #0;
ActualWideCharCount := DestI + 1;
end;
function UTF8ToUTF16(const P: PChar; ByteCnt: SizeUInt): UnicodeString;
var
L: SizeUInt;
begin
if ByteCnt=0 then
exit('');
SetLength(Result, ByteCnt);
// wide chars of UTF-16 <= bytes of UTF-8 string
if ConvertUTF8ToUTF16(PWideChar(Result), Length(Result) + 1, P, ByteCnt,
[toInvalidCharToSymbol], L) = trNoError
then SetLength(Result, L - 1)
else Result := '';
end;
function UTF8ToUTF16(const S: AnsiString): UnicodeString;
begin
Result:=UTF8ToUTF16(PChar(S),length(S));
end;
function StrToUTF16Hex(const AValue: UnicodeString; AIncludeBOM: boolean = True): AnsiString;
var
pc: ^Word;
@ -618,6 +380,11 @@ begin
end;
end;
procedure FillMem(Dest: pointer; Size: longint; Data: Byte );
begin
FillChar(Dest^, Size, Data);
end;
function TTFFileInfo.ReadULong(AStream: TStream): Longword;inline;
begin
Result:=0;
@ -755,7 +522,7 @@ begin
While (UE>=0) and ((FSubtables[UE].PlatformID<>3) or (FSubtables[UE].EncodingID<> 1)) do
Dec(UE);
if (UE=-1) then
Raise ETTF.Create('No Format 4 map (unicode) table found');
Raise ETTF.Create('No Format 4 map (unicode) table found <'+FFileName + ' - ' + PostScriptName+'>');
TT:=TableStartPos+FSubtables[UE].Offset;
AStream.Position:=TT;
FUnicodeMap.Format:= ReadUShort(AStream); // 2 bytes - Format of subtable
@ -839,12 +606,8 @@ begin
StringOffset:=ReadUShort(AStream); // 2 bytes
E := FNameEntries;
SetLength(E,Count);
FillMem(@N, SizeOf(TNameRecord), 0);
// Read Descriptors
{$IFDEF VER3}
N := Default(TNameRecord);
{$ELSE}
FillChar(N,SizeOf(TNameRecord),0);
{$ENDIF}
for I:=0 to Count-1 do
begin
AStream.ReadBuffer(N,SizeOf(TNameRecord));
@ -1027,7 +790,7 @@ begin
ttcmap: ParseCmap(AStream);
ttname: ParseName(AStream);
ttos2 : ParseOS2(AStream);
ttPost: ParsePost(AStream); // lecture table "Post"
ttPost: ParsePost(AStream);
end;
end;
end;
@ -1041,52 +804,17 @@ begin
raise ETTF.Create(rsFontEmbeddingNotAllowed);
PrepareEncoding(Encoding);
// MissingWidth:=ToNatural(Widths[Chars[CharCodes^[32]]].AdvanceWidth); // Char(32) - Space character
MissingWidth:=Widths[Chars[CharCodes^[32]]].AdvanceWidth; // Char(32) - Space character
FMissingWidth := Widths[Chars[CharCodes^[32]]].AdvanceWidth; // Char(32) - Space character
for I:=0 to 255 do
begin
if (Widths[Chars[CharCodes^[i]]].AdvanceWidth> 0) and (CharNames^[i]<> '.notdef') then
CharWidth[I]:= ToNatural(Widths[Chars[CharCodes^[I]]].AdvanceWidth)
else
CharWidth[I]:= MissingWidth;
CharWidth[I]:= FMissingWidth;
end;
end;
procedure TTFFileInfo.FillPDFFontDefinition(out ADef: TPDFFontDefinition; const AFontFile, AEncoding: String);
Var
I : Integer;
S : String;
begin
ADef.FontType:='TrueType'; // DON'T LOCALIZE
ADef.FontName:=PostScriptName;
ADef.Ascender:=Ascender;
ADef.Descender:=Descender;
ADef.CapHeight:=Capheight;
ADef.Flags:=Flags;
For I:=0 to 3 do
ADef.BBox[i]:=ToNatural(FHead.BBox[I]);
ADef.ItalicAngle:=FPostScript.ItalicAngle;
ADef.StemV:=StemV;
ADef.MissingWidth:=MissingWidth;
ADef.FontUp:=ToNatural(FPostScript.UnderlinePosition);
ADef.FontUt:=ToNatural(FPostScript.UnderlineThickness);
ADef.Encoding:=AEncoding;
ADef.OriginalSize:=FOriginalSize;
ADef.FontFile:=ChangeFileExt(AFontFile,'.z');
if (Lowercase(AEncoding)<>'cp1252') then
ADef.Diffs:=MakeDifferences;
S:='';
for I:=32 to 255 do
begin
if I>32 then
S:=S+' ';
S:=S+IntToStr(CharWidth[I]);
end;
ADef.CharWidths:=S;
end;
procedure TTFFileInfo.PrepareEncoding(const AEnCoding: String);
procedure TTFFileInfo.PrepareEncoding(const AEncoding: String);
var
TE : TTTFEncoding;
V : PTTFEncodingValues;
@ -1098,49 +826,6 @@ begin
GetEncodingTables(Te,CharBase,V);
end;
procedure TTFFileInfo.MakePDFFontDefinitionFile(const FontFile, Section, AEncoding: string);
var
Ini : TMemIniFile;
S: String;
I : Integer;
Def : TPDFFontDefinition;
begin
FillPDFFontDefinition(Def,FontFile,AEncoding);
Ini:=TMemIniFile.Create(FontFile);
With Ini Do
try
WriteString(Section,'FontType',Def.FontType);
WriteString(Section,'FontName',Def.FontName);
WriteInteger(Section,'Ascent',Def.Ascender);
WriteInteger(Section,'Descent',Def.Descender);
WriteInteger(Section,'CapHeight',Def.CapHeight);
WriteInteger(Section,'Flags',Def.Flags);
S:='';
for i:=0 to 3 do
begin
if I>0 then
S:=S+' ';
S:=S+IntToStr(Def.BBox[I]);
end;
WriteString(Section,'FontBBox',S);
WriteInteger(Section,'ItalicAngle',Def.ItalicAngle);
WriteInteger(Section,'StemV',Def.StemV);
WriteInteger(Section,'MissingWidth',Def.MissingWidth);
WriteInteger(Section,'FontUp',Def.FontUp);
WriteInteger(Section,'FontUt',Def.FontUt);
WriteString(Section,'Encoding',Def.Encoding);
WriteString(Section,'FontFile',Def.FontFile);
WriteInteger(Section,'OriginalSize',Def.OriginalSize);
WriteString(Section,'Diffs',Def.Diffs);
WriteString(Section,'CharWidth',Def.CharWidths);
UpdateFile;
finally
Ini.Free;
end;
end;
function TTFFileInfo.MakeDifferences: String;
var
i,l: Integer;
@ -1212,6 +897,24 @@ begin
Result := Widths[AIndex].AdvanceWidth;
end;
function TTFFileInfo.ItalicAngle: LongWord;
begin
Result := FPostScript.ItalicAngle;
end;
function TTFFileInfo.BBox: string;
var
i: integer;
begin
Result := '';
for i := 0 to 3 do
begin
if i > 0 then
Result := Result + ' ';
Result := Result + IntToStr(ToNatural(FHead.BBox[I]));
end;
end;
destructor TTFFileInfo.Destroy;
begin
SetLength(FNameEntries, 0);
@ -1228,6 +931,15 @@ begin
Result := round(d*10000);
end;
function TTFFileInfo.GetMissingWidth: integer;
begin
if FMissingWidth = 0 then
begin
FMissingWidth := Widths[Chars[CharCodes^[32]]].AdvanceWidth; // Char(32) - Space character
end;
Result := FMissingWidth;
end;
function TTFFileInfo.IsNativeData: Boolean;
begin
Result:=False;
@ -1250,12 +962,4 @@ begin
Result := Result+64;
end;
procedure TTFFileInfo.MakePDFFontDef(const FontFile: string; const Encoding:string; Embed: Boolean);
begin
PrepareFontDefinition(Encoding, Embed);
MakePDFFontDefinitionFile(FontFile,PostScriptName,Encoding);
end;
end.

View File

@ -613,6 +613,7 @@ type
TPDFFont = CLass(TCollectionItem)
private
FColor: TARGBColor;
FIsStdFont: boolean;
FName: String;
FFontFilename: String;
FTrueTypeFile: TTFFileInfo;
@ -629,6 +630,7 @@ type
Property Name: String Read FName Write FName;
Property Color: TARGBColor Read FColor Write FColor;
property TextMapping: TTextMappingList read FTextMappingList;
property IsStdFont: boolean read FIsStdFont write FIsStdFont;
end;
@ -721,13 +723,11 @@ type
TPDFToUnicode = class(TPDFDocumentObject)
private
FFontDef: TFontDef;
FEmbeddedFontNum: integer;
protected
procedure Write(const AStream: TStream);override;
public
constructor Create(const ADocument: TPDFDocument; const AEmbeddedFontNum: integer; AFontDef : TFontDef); overload;
property FontDef: TFontDef read FFontDef;
constructor Create(const ADocument: TPDFDocument; const AEmbeddedFontNum: integer); overload;
property EmbeddedFontNum: integer read FEmbeddedFontNum;
end;
@ -801,16 +801,15 @@ type
function CreatePageEntry(Parent, PageNum: integer): integer;virtual;
function CreateOutlines: integer;virtual;
function CreateOutlineEntry(Parent, SectNo, PageNo: integer; ATitle: string): integer;virtual;
function LoadFont(AFont: TPDFFont; Out FontDef : TFontDef): string;
function LoadFont(AFont: TPDFFont): boolean;
procedure CreateStdFont(EmbeddedFontName: string; EmbeddedFontNum: integer);virtual;
procedure CreateTTFFont(const EmbeddedFontNum: integer; FontDef : TFontDef);virtual;
procedure CreateTTFDescendantFont(const EmbeddedFontNum: integer; FontDef : TFontDef);virtual;
procedure CreateTTFCIDSystemInfo(const EmbeddedFontNum: integer; FontDef: TFontDef);virtual;
procedure CreateTTFFont(const EmbeddedFontNum: integer);virtual;
procedure CreateTTFDescendantFont(const EmbeddedFontNum: integer);virtual;
procedure CreateTTFCIDSystemInfo;virtual;
procedure CreateTp1Font(const EmbeddedFontNum: integer);virtual;
procedure CreateFontDescriptor(const EmbeddedFontNum: integer; FontDef : TFontDef);virtual;
procedure CreateToUnicode(const EmbeddedFontNum: integer; FontDef : TFontDef);virtual;
procedure CreateFontWidth(FontDef : TFontDef);virtual;
procedure CreateFontFileEntry(const EmbeddedFontNum: integer; FontDef: TFontDef);virtual;
procedure CreateFontDescriptor(const EmbeddedFontNum: integer);virtual;
procedure CreateToUnicode(const EmbeddedFontNum: integer);virtual;
procedure CreateFontFileEntry(const EmbeddedFontNum: integer);virtual;
procedure CreateImageEntry(ImgWidth, ImgHeight, NumImg: integer);virtual;
procedure CreatePageStream(APage : TPDFPage; PageNum: integer);
Function CreateGlobalXRef: TPDFXRef;
@ -1028,20 +1027,19 @@ Type
var
d: TDecompressionStream;
{$IFDEF NOHEADERWORKADOUND}
I: integer;
{$ENDIF}
Count : Integer;
Buffer : TBuffer;
begin
if AFrom.Size = 0 then
begin
ATo.Size := 0;
Exit; //==>
end;
{$IFDEF VER3}
Buffer := Default(TBuffer);
{$ELSE}
FillChar(Buffer,SizeOf(TBuffer),0);
{$ENDIF};
FillMem(@Buffer, SizeOf(TBuffer), 0);
AFrom.Position := 0;
AFrom.Seek(0,soFromEnd);
@ -1212,6 +1210,7 @@ begin
FTextMappingList := TTextMappingList.Create;
FTrueTypeFile := TTFFileInfo.Create;
FTrueTypeFile.LoadFromFile(FFontFilename);
FTrueTypeFile.PrepareFontDefinition('cp1252', True);
end;
end;
@ -1236,6 +1235,8 @@ var
c: word;
begin
Result := '';
if Length(AText) = 0 then
Exit;
for i := 1 to Length(AText) do
begin
c := Word(AText[i]);
@ -1580,7 +1581,7 @@ var
begin
if AText = '' then
Exit;
str := UTF8ToUTF16(AText);
str := UTF8Decode(AText);
Document.Fonts[FFontIndex].AddTextToMappingList(str);
end;
@ -2231,7 +2232,7 @@ function TPDFUTF8String.RemapedText: AnsiString;
var
s: UnicodeString;
begin
s := UTF8ToUTF16(FValue);
s := UTF8Decode(FValue);
Result := Document.Fonts[FontIndex].GetGlyphIndices(s);
end;
@ -2862,11 +2863,10 @@ begin
WriteString('end'+CRLF, AStream);
end;
constructor TPDFToUnicode.Create(const ADocument: TPDFDocument; const AEmbeddedFontNum: integer; AFontDef: TFontDef);
constructor TPDFToUnicode.Create(const ADocument: TPDFDocument; const AEmbeddedFontNum: integer);
begin
inherited Create(ADocument);
FEmbeddedFontNum := AEmbeddedFontNum;
FFontDef := AFontDef;
end;
{ TPDFDocument }
@ -3154,18 +3154,16 @@ begin
N := CreateName('F'+IntToStr(EmbeddedFontNum));
FDict.AddElement('Name',N);
AddFontNameToPages(N.Name,GLobalXRefCount-1);
// add font reference to all page dictionary
// add font reference to global page dictionary
FontFiles.Add('');
end;
function TPDFDocument.LoadFont(AFont: TPDFFont; out FontDef: TFontDef): string;
function TPDFDocument.LoadFont(AFont: TPDFFont): boolean;
var
lFontFile: TTFFileInfo;
lFontDef: TPDFFontDefinition;
lFName: string;
i: integer;
s: string;
begin
Result := False;
if ExtractFilePath(AFont.FontFile) <> '' then
// assume AFont.FontFile is the full path to the TTF file
lFName := AFont.FontFile
@ -3175,40 +3173,14 @@ begin
if FileExists(lFName) then
begin
lFontFile := TTFFileInfo.Create;
lFontFile.LoadFromFile(lFName);
lFontFile.PrepareFontDefinition('cp1252', True);
lFontFile.FillPDFFontDefinition(lFontDef, ExtractBaseFontName(AFont.Name), 'cp1252');
FontDef.FType := lFontDef.FontType;
FontDef.FName := lFontDef.FontName;
FontDef.FAscent := IntToStr(lFontDef.Ascender);
FontDef.FDescent := IntToStr(lFontDef.Descender);
FontDef.FCapHeight := IntToStr(lFontDef.CapHeight);
FontDef.FFlags := IntToStr(lFontDef.Flags);
s := '';
for i := 0 to 3 do
begin
if i > 0 then
s := s+' ';
s := s + IntToStr(lFontDef.BBox[i]);
end;
FontDef.FFontBBox := s;
FontDef.FItalicAngle := IntToStr(lFontDef.ItalicAngle);
FontDef.FStemV := IntToStr(lFontDef.StemV);
FontDef.FMissingWidth := IntToStr(lFontDef.MissingWidth);
FontDef.FEncoding := lFontDef.Encoding;
FontDef.FFile := lFName;
FontDef.FOriginalSize := IntToStr(lFontDef.OriginalSize);
FontDef.FDiffs := lFontDef.Diffs;
FontDef.FCharWidth := lFontDef.CharWidths;
Result := lFontDef.FontType;
lFontFile.Free;
s := LowerCase(ExtractFileExt(lFName));
Result := (s = '.ttf') or (s = '.otf');
end
else
Raise EPDF.CreateFmt(rsErrReportFontFileMissing, [lFName]);
end;
procedure TPDFDocument.CreateTTFFont(const EmbeddedFontNum: integer; FontDef : TFontDef);
procedure TPDFDocument.CreateTTFFont(const EmbeddedFontNum: integer);
var
FDict: TPDFDictionary;
N: TPDFName;
@ -3218,22 +3190,22 @@ begin
FDict := CreateGlobalXRef.Dict;
FDict.AddName('Type', 'Font');
FDict.AddName('Subtype', 'Type0');
FDict.AddName('BaseFont', FontDef.FName);
FDict.AddName('BaseFont', Fonts[EmbeddedFontNum].Name);
FDict.AddName('Encoding', 'Identity-H');
// add name element to font dictionary
N:=CreateName('F'+IntToStr(EmbeddedFontNum));
FDict.AddElement('Name',N);
Self.AddFontNameToPages(N.Name,GlobalXRefCount-1);
CreateTTFDescendantFont(EmbeddedFontNum, FontDef);
AddFontNameToPages(N.Name,GlobalXRefCount-1);
CreateTTFDescendantFont(EmbeddedFontNum);
Arr := CreateArray;
FDict.AddElement('DescendantFonts', Arr);
Arr.AddItem(TPDFReference.Create(self, GlobalXRefCount-4));
CreateToUnicode(EmbeddedFontNum, FontDef);
CreateToUnicode(EmbeddedFontNum);
FDict.AddReference('ToUnicode', GlobalXRefCount-1);
FontFiles.Add(FontDef.FFile);
FontFiles.Add(Fonts[EmbeddedFontNum].FTrueTypeFile.Filename);
end;
procedure TPDFDocument.CreateTTFDescendantFont(const EmbeddedFontNum: integer; FontDef: TFontDef);
procedure TPDFDocument.CreateTTFDescendantFont(const EmbeddedFontNum: integer);
var
FDict: TPDFDictionary;
Arr: TPDFArray;
@ -3242,13 +3214,13 @@ begin
FDict := CreateGlobalXRef.Dict;
FDict.AddName('Type', 'Font');
FDict.AddName('Subtype', 'CIDFontType2');
FDict.AddName('BaseFont', FontDef.FName);
FDict.AddName('BaseFont', Fonts[EmbeddedFontNum].Name);
CreateTTFCIDSystemInfo(EmbeddedFontNum, FontDef);
CreateTTFCIDSystemInfo;
FDict.AddReference('CIDSystemInfo', GlobalXRefCount-1);
// add fontdescriptor reference to font dictionary
CreateFontDescriptor(EmbeddedFontNum,FontDef);
CreateFontDescriptor(EmbeddedFontNum);
FDict.AddReference('FontDescriptor',GlobalXRefCount-2);
Arr := CreateArray;
@ -3256,7 +3228,7 @@ begin
Arr.AddItem(TPDFTrueTypeCharWidths.Create(self, EmbeddedFontNum));
end;
procedure TPDFDocument.CreateTTFCIDSystemInfo(const EmbeddedFontNum: integer; FontDef: TFontDef);
procedure TPDFDocument.CreateTTFCIDSystemInfo;
var
FDict: TPDFDictionary;
begin
@ -3271,59 +3243,46 @@ begin
Assert(EmbeddedFontNum<>-1);
end;
procedure TPDFDocument.CreateFontDescriptor(const EmbeddedFontNum: integer; FontDef : TFontDef);
procedure TPDFDocument.CreateFontDescriptor(const EmbeddedFontNum: integer);
var
Arr: TPDFArray;
FDict: TPDFDictionary;
begin
FDict:=CreateGlobalXRef.Dict;
FDict.AddName('Type', 'FontDescriptor');
FDict.AddName('FontName', FontDef.FName);
FDict.AddName('FontName', Fonts[EmbeddedFontNum].Name);
FDict.AddName('FontFamily', Fonts[EmbeddedFontNum].FTrueTypeFile.FamilyName);
FDict.AddInteger('Ascent', StrToInt(FontDef.FAscent));
FDict.AddInteger('Descent', StrToInt(FontDef.FDescent));
FDict.AddInteger('CapHeight', StrToInt(FontDef.FCapHeight));
FDict.AddInteger('Ascent', Fonts[EmbeddedFontNum].FTrueTypeFile.Ascender);
FDict.AddInteger('Descent', Fonts[EmbeddedFontNum].FTrueTypeFile.Descender);
FDict.AddInteger('CapHeight', Fonts[EmbeddedFontNum].FTrueTypeFile.CapHeight);
FDict.AddInteger('Flags', 32);
Arr:=CreateArray;
FDict.AddElement('FontBBox',Arr);
Arr.AddIntArray(FontDef.FFontBBox);
FDict.AddInteger('ItalicAngle',StrToInt(FontDef.FItalicAngle));
FDict.AddInteger('StemV',StrToInt(FontDef.FStemV));
FDict.AddInteger('MissingWidth', StrToInt(FontDef.FMissingWidth));
CreateFontFileEntry(EmbeddedFontNum,FontDef);
Arr.AddIntArray(Fonts[EmbeddedFontNum].FTrueTypeFile.BBox);
FDict.AddInteger('ItalicAngle',Fonts[EmbeddedFontNum].FTrueTypeFile.ItalicAngle);
FDict.AddInteger('StemV', Fonts[EmbeddedFontNum].FTrueTypeFile.StemV);
FDict.AddInteger('MissingWidth', Fonts[EmbeddedFontNum].FTrueTypeFile.MissingWidth);
CreateFontFileEntry(EmbeddedFontNum);
FDict.AddReference('FontFile2',GlobalXRefCount-1);
end;
procedure TPDFDocument.CreateToUnicode(const EmbeddedFontNum: integer; FontDef: TFontDef);
procedure TPDFDocument.CreateToUnicode(const EmbeddedFontNum: integer);
var
lXRef: TPDFXRef;
begin
lXRef := CreateGlobalXRef;
lXRef.FStream := CreateStream(True);
lXRef.FStream.AddItem(TPDFToUnicode.Create(self, EmbeddedFontNum, FontDef));
lXRef.FStream.AddItem(TPDFToUnicode.Create(self, EmbeddedFontNum));
end;
procedure TPDFDocument.CreateFontWidth(FontDef : TFontDef);
var
Arr: TPDFArray;
FDict: TPDFDictionary;
begin
FDict:=CreateGlobalXRef.Dict;
Arr:=CreateArray;
FDict.AddElement('',Arr);
Arr.AddIntArray(FontDef.FCharWidth);
end;
procedure TPDFDocument.CreateFontFileEntry(const EmbeddedFontNum: integer; FontDef: TFontDef);
procedure TPDFDocument.CreateFontFileEntry(const EmbeddedFontNum: integer);
var
FDict: TPDFDictionary;
begin
FDict:=CreateGlobalXRef.Dict;
if poCompressFonts in Options then
FDict.AddName('Filter','FlateDecode');
FDict.AddInteger('Length1 '+IntToStr(EmbeddedFontNum),StrToInt(FontDef.FOriginalSize));
FDict.AddInteger('Length1 '+IntToStr(EmbeddedFontNum), Fonts[EmbeddedFontNum].FTrueTypeFile.OriginalSize);
end;
procedure TPDFDocument.CreateImageEntry(ImgWidth, ImgHeight, NumImg: integer);
@ -3595,7 +3554,6 @@ var
i: integer;
NumFont: integer;
FontName: string;
FontDef: TFontDef;
begin
// select the font type
NumFont:=0;
@ -3611,8 +3569,8 @@ begin
begin
CreateStdFont(FontName, NumFont);
end
else if (LoadFont(Fonts[i], FontDef)='TrueType') then
CreateTtfFont(NumFont,FontDef)
else if LoadFont(Fonts[i]) then
CreateTtfFont(NumFont)
else
CreateTp1Font(NumFont); // not implemented yet
Inc(NumFont);
@ -3767,6 +3725,7 @@ begin
F := Fonts.AddFontDef;
F.Name := AName;
F.Color := AColor;
F.IsStdFont := True;
Result := Fonts.Count-1;
end;
@ -3795,6 +3754,7 @@ begin
F.FontFile := lFName;
F.Name := AName;
F.Color := AColor;
F.IsStdFont := False;
Result := Fonts.Count-1;
end;

View File

@ -190,10 +190,6 @@ type
procedure TestPostScript_minMemType1;
procedure TestPostScript_maxMemType1;
{ PDF Font Definition }
procedure TestPDFFontDefinition;
procedure TestMakePDFFontDefinition;
{ Utility functions }
procedure TestGetGlyphIndex;
procedure TestGetAdvanceWidth;
@ -353,10 +349,6 @@ type
procedure TestPostScript_maxMemType42;
procedure TestPostScript_minMemType1;
procedure TestPostScript_maxMemType1;
{ PDF Font Definition }
procedure TestPDFFontDefinition;
procedure TestMakePDFFontDefinition;
end;
implementation
@ -1124,78 +1116,6 @@ begin
AssertEquals('Failed on 1', 0, FI.PostScript.maxMemType1);
end;
procedure TTestLiberationFont.TestPDFFontDefinition;
var
Def: TPDFFontDefinition;
s: string;
begin
FI.FillPDFFontDefinition(Def, cFont1, 'cp1252');
AssertEquals('Failed on 1', 'TrueType', Def.FontType);
AssertEquals('Failed on 2', 'LiberationSans', Def.FontName);
AssertEquals('Failed on 3', 728, Def.Ascender);
AssertEquals('Failed on 4', -210, Def.Descender);
AssertEquals('Failed on 5', 688, Def.CapHeight);
AssertEquals('Failed on 6', 32, Def.Flags);
s := IntToStr(Def.BBox[0]) + ' ' + IntToStr(Def.BBox[1]) + ' ' +
IntToStr(Def.BBox[2]) + ' ' + IntToStr(Def.BBox[3]);
AssertEquals('Failed on 7', '-544 -303 1302 980', s);
AssertEquals('Failed on 8', 0, Def.ItalicAngle);
AssertEquals('Failed on 9', 70, Def.StemV);
AssertEquals('Failed on 10', 0, Def.MissingWidth);
AssertEquals('Failed on 11', -106, Def.FontUp);
AssertEquals('Failed on 12', 73, Def.FontUt);
AssertEquals('Failed on 13', 'cp1252', Def.Encoding);
AssertEquals('Failed on 14', ReplaceStr(cFont1, '.ttf', '.z'), Def.FontFile); // 'fonts/LiberationSans-Regular.z'
AssertEquals('Failed on 15', '', Def.Diffs);
{ CharWidths is only valid if we called MakePDFFontDef }
// AssertEquals('Failed on 16', '', Def.CharWidths);
AssertEquals('Failed on 17', 350200, Def.OriginalSize);
end;
procedure TTestLiberationFont.TestMakePDFFontDefinition;
const
cSection = 'LiberationSans';
var
lFile: string;
ini: TINIFile;
begin
lFile := ChangeFileExt(GetTempFileName, '.ini');
// writeln( lFile);
AssertTrue('Failed on 1', FileExists(lFile) = False);
try
FI.MakePDFFontDef(lFile, 'cp1252', True);
AssertTrue('Failed on 2', FileExists(lFile) = True);
ini := TINIFile.Create(lFile);
try
AssertEquals('Failed on 3', 'TrueType', ini.ReadString(cSection, 'FontType', ''));
AssertEquals('Failed on 4', 'LiberationSans', ini.ReadString(cSection, 'FontName', ''));
AssertEquals('Failed on 5', 728, ini.ReadInteger(cSection, 'Ascent', 0));
AssertEquals('Failed on 6', -210, ini.ReadInteger(cSection, 'Descent', 0));
AssertEquals('Failed on 7', 688, ini.ReadInteger(cSection, 'CapHeight', 0));
AssertEquals('Failed on 8', 32, ini.ReadInteger(cSection, 'Flags', 0));
AssertEquals('Failed on 9', '-544 -303 1302 980', ini.ReadString(cSection, 'FontBBox', ''));
AssertEquals('Failed on 10', 0, ini.ReadInteger(cSection, 'ItalicAngle', 0));
AssertEquals('Failed on 11', 70, ini.ReadInteger(cSection, 'StemV', 0));
AssertEquals('Failed on 12', 569, ini.ReadInteger(cSection, 'MissingWidth', 0));
AssertEquals('Failed on 13', -106, ini.ReadInteger(cSection, 'FontUp', 0));
AssertEquals('Failed on 14', 73, ini.ReadInteger(cSection, 'FontUt', 0));
AssertEquals('Failed on 15', 'cp1252', ini.ReadString(cSection, 'Encoding', ''));
AssertEquals('Failed on 16', ReplaceStr(lFile, '.ini', '.z'), ini.ReadString(cSection, 'FontFile', ''));
AssertEquals('Failed on 17', '', ini.ReadString(cSection, 'Diffs', ''));
AssertTrue('Failed on 18', ini.ReadString(cSection, 'CharWidth', '') <> '');
AssertEquals('Failed on 19', 350200, ini.ReadInteger(cSection, 'OriginalSize', 0));
finally
ini.Free;
end;
finally
DeleteFile(lFile);
end
end;
procedure TTestLiberationFont.TestGetGlyphIndex;
begin
AssertEquals('Failed on 1.1', 67, Ord('C'));
@ -1976,80 +1896,6 @@ begin
AssertEquals('Failed on 1', 0, FI.PostScript.maxMemType1);
end;
procedure TTestFreeSansFont.TestPDFFontDefinition;
var
Def: TPDFFontDefinition;
s: string;
begin
FI.FillPDFFontDefinition(Def, cFont1, 'cp1252');
AssertEquals('Failed on 1', 'TrueType', Def.FontType);
AssertEquals('Failed on 2', 'FreeSans', Def.FontName);
AssertEquals('Failed on 3', 800, Def.Ascender);
AssertEquals('Failed on 4', -200, Def.Descender);
AssertEquals('Failed on 5', 729, Def.CapHeight);
AssertEquals('Failed on 6', 32, Def.Flags);
s := IntToStr(Def.BBox[0]) + ' ' + IntToStr(Def.BBox[1]) + ' ' +
IntToStr(Def.BBox[2]) + ' ' + IntToStr(Def.BBox[3]);
AssertEquals('Failed on 7', '-1166 -638 2260 1050', s);
AssertEquals('Failed on 8', 0, Def.ItalicAngle);
AssertEquals('Failed on 9', 70, Def.StemV);
AssertEquals('Failed on 10', 0, Def.MissingWidth);
AssertEquals('Failed on 11', -176, Def.FontUp);
AssertEquals('Failed on 12', 50, Def.FontUt);
AssertEquals('Failed on 13', 'cp1252', Def.Encoding);
AssertEquals('Failed on 14', ReplaceStr(cFont1, '.ttf', '.z'), Def.FontFile); // 'fonts/LiberationSans-Regular.z'
AssertEquals('Failed on 15', '', Def.Diffs);
{ CharWidths is only valid if we called MakePDFFontDef }
// AssertEquals('Failed on 16', '', Def.CharWidths);
AssertEquals('Failed on 17', 1563256, Def.OriginalSize);
end;
procedure TTestFreeSansFont.TestMakePDFFontDefinition;
const
cSection = 'FreeSans';
var
lFile: string;
ini: TINIFile;
begin
lFile := ChangeFileExt(GetTempFileName, '.ini');
// writeln( lFile);
AssertTrue('Failed on 1', FileExists(lFile) = False);
try
FI.MakePDFFontDef(lFile, 'cp1252', True);
AssertTrue('Failed on 2', FileExists(lFile) = True);
ini := TINIFile.Create(lFile);
try
AssertEquals('Failed on 3', 'TrueType', ini.ReadString(cSection, 'FontType', ''));
AssertEquals('Failed on 4', 'FreeSans', ini.ReadString(cSection, 'FontName', ''));
AssertEquals('Failed on 5', 800, ini.ReadInteger(cSection, 'Ascent', 0));
AssertEquals('Failed on 6', -200, ini.ReadInteger(cSection, 'Descent', 0));
AssertEquals('Failed on 7', 729, ini.ReadInteger(cSection, 'CapHeight', 0));
AssertEquals('Failed on 8', 32, ini.ReadInteger(cSection, 'Flags', 0));
AssertEquals('Failed on 9', '-1166 -638 2260 1050', ini.ReadString(cSection, 'FontBBox', ''));
AssertEquals('Failed on 10', 0, ini.ReadInteger(cSection, 'ItalicAngle', 0));
AssertEquals('Failed on 11', 70, ini.ReadInteger(cSection, 'StemV', 0));
AssertEquals('Failed on 12', 250, ini.ReadInteger(cSection, 'MissingWidth', 0));
AssertEquals('Failed on 13', -176, ini.ReadInteger(cSection, 'FontUp', 0));
AssertEquals('Failed on 14', 50, ini.ReadInteger(cSection, 'FontUt', 0));
AssertEquals('Failed on 15', 'cp1252', ini.ReadString(cSection, 'Encoding', ''));
AssertEquals('Failed on 16', ReplaceStr(lFile, '.ini', '.z'), ini.ReadString(cSection, 'FontFile', ''));
AssertEquals('Failed on 17', '', ini.ReadString(cSection, 'Diffs', ''));
AssertTrue('Failed on 18', ini.ReadString(cSection, 'CharWidth', '') <> '');
AssertEquals('Failed on 19', 1563256, ini.ReadInteger(cSection, 'OriginalSize', 0));
finally
ini.Free;
end;
finally
DeleteFile(lFile);
end
end;
initialization
RegisterTest({$ifdef fptest}'fpParseTTF',{$endif}TTestEmptyParseTTF{$ifdef fptest}.Suite{$endif});

View File

@ -292,7 +292,7 @@ procedure TTestPDFObject.TestFloatStr;
Var
C : Char;
begin
AssertEquals('Failed on 1', '0.12', TMockPDFObject.FloatStr(TPDFFLoat(0.12)));
AssertEquals('Failed on 2', ' 12', TMockPDFObject.FloatStr(TPDFFLoat(12.00)));
@ -309,7 +309,7 @@ begin
AssertEquals('Failed on 9', '12.34', TMockPDFObject.FloatStr(TPDFFLoat(12.34)));
finally
FormatSettings.DecimalSeparator:=C;
end;
end;
// Set ThousandSeparator
C:=FormatSettings.ThousandSeparator;
FormatSettings.ThousandSeparator:=' ';
@ -1247,7 +1247,6 @@ end;
procedure TTestPDFImage.TestWrite;
var
o: TMockPDFImage;
ar: TPDFCoordArray;
x, y: TPDFFLoat;
begin
x := 100;
@ -1265,7 +1264,6 @@ begin
'Q'+CRLF,
S.DataString);
finally
SetLength(ar, 0);
o.Free;
end;
end;