|
@@ -3,9 +3,9 @@
|
|
Copyright (c) 2014 by Michael Van Canneyt
|
|
Copyright (c) 2014 by Michael Van Canneyt
|
|
|
|
|
|
This unit generates PDF files, without dependencies on GUI libraries.
|
|
This unit generates PDF files, without dependencies on GUI libraries.
|
|
- (Based on original ideas from the fpGUI pdf generator by Jean-Marc Levecque
|
|
|
|
|
|
+ (Based on original ideas from the fpGUI pdf generator by Jean-Marc Levecque
|
|
<[email protected]>)
|
|
<[email protected]>)
|
|
-
|
|
|
|
|
|
+
|
|
See the file COPYING.FPC, included in this distribution,
|
|
See the file COPYING.FPC, included in this distribution,
|
|
for details about the copyright.
|
|
for details about the copyright.
|
|
|
|
|
|
@@ -26,7 +26,8 @@ uses
|
|
StrUtils,
|
|
StrUtils,
|
|
contnrs,
|
|
contnrs,
|
|
fpImage,
|
|
fpImage,
|
|
- zstream;
|
|
|
|
|
|
+ zstream,
|
|
|
|
+ fpparsettf;
|
|
|
|
|
|
Const
|
|
Const
|
|
clBlack = $000000;
|
|
clBlack = $000000;
|
|
@@ -165,7 +166,14 @@ type
|
|
end;
|
|
end;
|
|
|
|
|
|
|
|
|
|
- TPDFString = class(TPDFDocumentObject)
|
|
|
|
|
|
+ TPDFAbstractString = class(TPDFDocumentObject)
|
|
|
|
+ protected
|
|
|
|
+ // These symbols must be preceded by a backslash: "(", ")", "\"
|
|
|
|
+ function InsertEscape(const AValue: string): string;
|
|
|
|
+ end;
|
|
|
|
+
|
|
|
|
+
|
|
|
|
+ TPDFString = class(TPDFAbstractString)
|
|
private
|
|
private
|
|
FValue: string;
|
|
FValue: string;
|
|
protected
|
|
protected
|
|
@@ -175,12 +183,12 @@ type
|
|
end;
|
|
end;
|
|
|
|
|
|
|
|
|
|
- TPDFUTF8String = class(TPDFDocumentObject)
|
|
|
|
|
|
+ TPDFUTF8String = class(TPDFAbstractString)
|
|
private
|
|
private
|
|
FValue: UTF8String;
|
|
FValue: UTF8String;
|
|
FFontIndex: integer;
|
|
FFontIndex: integer;
|
|
{ Remap each character to the equivalant dictionary character code }
|
|
{ Remap each character to the equivalant dictionary character code }
|
|
- function RemapedText: String;
|
|
|
|
|
|
+ function RemapedText: AnsiString;
|
|
protected
|
|
protected
|
|
procedure Write(const AStream: TStream); override;
|
|
procedure Write(const AStream: TStream); override;
|
|
public
|
|
public
|
|
@@ -558,6 +566,37 @@ type
|
|
end;
|
|
end;
|
|
|
|
|
|
|
|
|
|
|
|
+ // forward declarations
|
|
|
|
+ TTextMapping = class;
|
|
|
|
+
|
|
|
|
+
|
|
|
|
+ TTextMappingList = class(TObject)
|
|
|
|
+ private
|
|
|
|
+ FList: TFPObjectList;
|
|
|
|
+ function GetCount: Integer;
|
|
|
|
+ protected
|
|
|
|
+ function GetItem(AIndex: Integer): TTextMapping; reintroduce;
|
|
|
|
+ procedure SetItem(AIndex: Integer; AValue: TTextMapping); reintroduce;
|
|
|
|
+ public
|
|
|
|
+ constructor Create;
|
|
|
|
+ destructor Destroy; override;
|
|
|
|
+ function Add(AObject: TTextMapping): Integer; overload;
|
|
|
|
+ function Add(const ACharID, AGlyphID: uint16): Integer; overload;
|
|
|
|
+ property Count: Integer read GetCount;
|
|
|
|
+ property Items[Index: Integer]: TTextMapping read GetItem write SetItem; default;
|
|
|
|
+ end;
|
|
|
|
+
|
|
|
|
+
|
|
|
|
+ TTextMapping = class(TObject)
|
|
|
|
+ private
|
|
|
|
+ FCharID: uint16;
|
|
|
|
+ FGlyphID: uint16;
|
|
|
|
+ public
|
|
|
|
+ class function NewTextMap(const ACharID, AGlyphID: uint16): TTextMapping;
|
|
|
|
+ property CharID: uint16 read FCharID write FCharID;
|
|
|
|
+ property GlyphID: uint16 read FGlyphID write FGlyphID;
|
|
|
|
+ end;
|
|
|
|
+
|
|
TTextDictionary = class(TObject)
|
|
TTextDictionary = class(TObject)
|
|
private
|
|
private
|
|
FChar: UnicodeChar;
|
|
FChar: UnicodeChar;
|
|
@@ -571,17 +610,34 @@ type
|
|
private
|
|
private
|
|
FColor: TARGBColor;
|
|
FColor: TARGBColor;
|
|
FName: String;
|
|
FName: String;
|
|
- FFontFile: string;
|
|
|
|
- { stores lookup code for each letter of text used and associated 2-byte Unicode code point }
|
|
|
|
- FDictionary: TObjectList;
|
|
|
|
|
|
+ FFontFilename: String;
|
|
|
|
+ FTrueTypeFile: TTFFileInfo;
|
|
|
|
+ { stores mapping of Char IDs to font Glyph IDs }
|
|
|
|
+ FTextMappingList: TTextMappingList;
|
|
|
|
+ procedure PrepareTextMapping;
|
|
|
|
+ procedure SetFontFilename(AValue: string);
|
|
public
|
|
public
|
|
- constructor Create(ACollection: TCollection); override;
|
|
|
|
destructor Destroy; override;
|
|
destructor Destroy; override;
|
|
- function FindIndexOf(const AValue: UnicodeChar): integer;
|
|
|
|
- Property FontFile: string read FFontFile write FFontFile;
|
|
|
|
- Property Name : String Read FName Write FName;
|
|
|
|
- Property Color : TARGBColor Read FColor Write FColor;
|
|
|
|
- property Dictionary: TObjectList read FDictionary;
|
|
|
|
|
|
+ { Returns a string where each character is replaced with a glyph index value instead. }
|
|
|
|
+ function GetGlyphIndices(const AText: UnicodeString): AnsiString;
|
|
|
|
+ procedure AddTextToMappingList(const AText: UnicodeString);
|
|
|
|
+ Property FontFile: string read FFontFilename write SetFontFilename;
|
|
|
|
+ Property Name: String Read FName Write FName;
|
|
|
|
+ Property Color: TARGBColor Read FColor Write FColor;
|
|
|
|
+ property TextMapping: TTextMappingList read FTextMappingList;
|
|
|
|
+ end;
|
|
|
|
+
|
|
|
|
+
|
|
|
|
+ TPDFTrueTypeCharWidths = class(TPDFDocumentObject)
|
|
|
|
+ private
|
|
|
|
+ FEmbeddedFontNum: integer;
|
|
|
|
+ FFontIndex: integer;
|
|
|
|
+ protected
|
|
|
|
+ procedure Write(const AStream: TStream);override;
|
|
|
|
+ public
|
|
|
|
+ constructor Create(const ADocument: TPDFDocument; const AEmbeddedFontNum: integer); overload;
|
|
|
|
+ property EmbeddedFontNum: integer read FEmbeddedFontNum;
|
|
|
|
+ property FontIndex: integer read FFontIndex write FFontIndex;
|
|
end;
|
|
end;
|
|
|
|
|
|
|
|
|
|
@@ -671,7 +727,6 @@ type
|
|
end;
|
|
end;
|
|
|
|
|
|
|
|
|
|
-
|
|
|
|
TPDFLineStyleDef = Class(TCollectionItem)
|
|
TPDFLineStyleDef = Class(TCollectionItem)
|
|
private
|
|
private
|
|
FColor: TARGBColor;
|
|
FColor: TARGBColor;
|
|
@@ -744,11 +799,13 @@ type
|
|
function LoadFont(AFont: TPDFFont; Out FontDef : TFontDef): string;
|
|
function LoadFont(AFont: TPDFFont; Out FontDef : TFontDef): string;
|
|
procedure CreateStdFont(EmbeddedFontName: string; EmbeddedFontNum: integer);virtual;
|
|
procedure CreateStdFont(EmbeddedFontName: string; EmbeddedFontNum: integer);virtual;
|
|
procedure CreateTTFFont(const EmbeddedFontNum: integer; FontDef : TFontDef);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 CreateTp1Font(const EmbeddedFontNum: integer);virtual;
|
|
procedure CreateTp1Font(const EmbeddedFontNum: integer);virtual;
|
|
procedure CreateFontDescriptor(const EmbeddedFontNum: integer; FontDef : TFontDef);virtual;
|
|
procedure CreateFontDescriptor(const EmbeddedFontNum: integer; FontDef : TFontDef);virtual;
|
|
procedure CreateToUnicode(const EmbeddedFontNum: integer; FontDef : TFontDef);virtual;
|
|
procedure CreateToUnicode(const EmbeddedFontNum: integer; FontDef : TFontDef);virtual;
|
|
procedure CreateFontWidth(FontDef : TFontDef);virtual;
|
|
procedure CreateFontWidth(FontDef : TFontDef);virtual;
|
|
- procedure CreateFontFileEntry(const EmbeddedFontNum: integer;FontDef : TFontDef);virtual;
|
|
|
|
|
|
+ procedure CreateFontFileEntry(const EmbeddedFontNum: integer; FontDef: TFontDef);virtual;
|
|
procedure CreateImageEntry(ImgWidth, ImgHeight, NumImg: integer);virtual;
|
|
procedure CreateImageEntry(ImgWidth, ImgHeight, NumImg: integer);virtual;
|
|
procedure CreatePageStream(APage : TPDFPage; PageNum: integer);
|
|
procedure CreatePageStream(APage : TPDFPage; PageNum: integer);
|
|
Function CreateGlobalXRef: TPDFXRef;
|
|
Function CreateGlobalXRef: TPDFXRef;
|
|
@@ -805,8 +862,9 @@ type
|
|
|
|
|
|
|
|
|
|
const
|
|
const
|
|
- CRLF =#13#10;
|
|
|
|
- PDF_VERSION ='%PDF-1.3';
|
|
|
|
|
|
+ CRLF = #13#10;
|
|
|
|
+ PDF_VERSION = '%PDF-1.3';
|
|
|
|
+ PDF_BINARY_BLOB = '%'#$C3#$A4#$C3#$BC#$C3#$B6#$C3#$9F;
|
|
PDF_FILE_END = '%%EOF';
|
|
PDF_FILE_END = '%%EOF';
|
|
PDF_MAX_GEN_NUM = 65535;
|
|
PDF_MAX_GEN_NUM = 65535;
|
|
PDF_UNICODE_HEADER = 'FEFF001B%s001B';
|
|
PDF_UNICODE_HEADER = 'FEFF001B%s001B';
|
|
@@ -858,8 +916,6 @@ function InchesToPDF(Inches: single): TPDFFloat;
|
|
|
|
|
|
implementation
|
|
implementation
|
|
|
|
|
|
-uses
|
|
|
|
- fpparsettf;
|
|
|
|
|
|
|
|
Resourcestring
|
|
Resourcestring
|
|
rsErrReportFontFileMissing = 'Font File "%s" does not exist.';
|
|
rsErrReportFontFileMissing = 'Font File "%s" does not exist.';
|
|
@@ -867,6 +923,12 @@ Resourcestring
|
|
SerrInvalidSectionPage = 'Error: Invalid section page index.';
|
|
SerrInvalidSectionPage = 'Error: Invalid section page index.';
|
|
SErrNoGlobalDict = 'Error: no global XRef named "%s".';
|
|
SErrNoGlobalDict = 'Error: no global XRef named "%s".';
|
|
SErrInvalidPageIndex = 'Invalid page index: %d';
|
|
SErrInvalidPageIndex = 'Invalid page index: %d';
|
|
|
|
+ SErrNoFontIndex = 'No FontIndex was set - please use SetFont() first.';
|
|
|
|
+
|
|
|
|
+type
|
|
|
|
+ // to get access to protected methods
|
|
|
|
+ TTTFFriendClass = class(TTFFileInfo)
|
|
|
|
+ end;
|
|
|
|
|
|
Const
|
|
Const
|
|
// TODO: we should improve this to take into account the line width
|
|
// TODO: we should improve this to take into account the line width
|
|
@@ -888,22 +950,6 @@ const
|
|
// cm = ((pixels * 25.4) / dpi) / 10
|
|
// cm = ((pixels * 25.4) / dpi) / 10
|
|
|
|
|
|
|
|
|
|
-// These symbols must be preceded by a slash: %, (, ), <, >, [, ], {, }, / and #
|
|
|
|
-function InsertEscape(const AValue: string): string;
|
|
|
|
-var
|
|
|
|
- S: string;
|
|
|
|
-begin
|
|
|
|
- Result:='';
|
|
|
|
- S:=AValue;
|
|
|
|
- if Pos('\', S) > 0 then
|
|
|
|
- S:=AnsiReplaceStr(S, '\', '\\');
|
|
|
|
- if Pos('(', S) > 0 then
|
|
|
|
- S:=AnsiReplaceStr(S, '(', '\(');
|
|
|
|
- if Pos(')', S) > 0 then
|
|
|
|
- S:=AnsiReplaceStr(S, ')', '\)');
|
|
|
|
- Result:=S;
|
|
|
|
-end;
|
|
|
|
-
|
|
|
|
function DateToPdfDate(const ADate: TDateTime): string;
|
|
function DateToPdfDate(const ADate: TDateTime): string;
|
|
begin
|
|
begin
|
|
Result:=FormatDateTime('"D:"yyyymmddhhnnss', ADate);
|
|
Result:=FormatDateTime('"D:"yyyymmddhhnnss', ADate);
|
|
@@ -917,11 +963,6 @@ begin
|
|
Result:=StringOfChar('0',Padlen)+Result;
|
|
Result:=StringOfChar('0',Padlen)+Result;
|
|
end;
|
|
end;
|
|
|
|
|
|
-function IntToStrZeros(const AValue, ADigits: integer): string;
|
|
|
|
-begin
|
|
|
|
- result := SysUtils.Format('%.*d', [ADigits, AValue]) ;
|
|
|
|
-end;
|
|
|
|
-
|
|
|
|
procedure CompressStream(AFrom: TStream; ATo: TStream; ACompressLevel: TCompressionLevel = clDefault; ASkipHeader: boolean = False);
|
|
procedure CompressStream(AFrom: TStream; ATo: TStream; ACompressLevel: TCompressionLevel = clDefault; ASkipHeader: boolean = False);
|
|
var
|
|
var
|
|
c: TCompressionStream;
|
|
c: TCompressionStream;
|
|
@@ -1066,35 +1107,146 @@ begin
|
|
Result.y := (APoint.y - _21) / _11;
|
|
Result.y := (APoint.y - _21) / _11;
|
|
end;
|
|
end;
|
|
|
|
|
|
|
|
+{ TTextMappingList }
|
|
|
|
+
|
|
|
|
+function TTextMappingList.GetCount: Integer;
|
|
|
|
+begin
|
|
|
|
+ Result := FList.Count;
|
|
|
|
+end;
|
|
|
|
+
|
|
|
|
+function TTextMappingList.GetItem(AIndex: Integer): TTextMapping;
|
|
|
|
+begin
|
|
|
|
+ Result := TTextMapping(FList.Items[AIndex]);
|
|
|
|
+end;
|
|
|
|
+
|
|
|
|
+procedure TTextMappingList.SetItem(AIndex: Integer; AValue: TTextMapping);
|
|
|
|
+begin
|
|
|
|
+ FList.Items[AIndex] := AValue;
|
|
|
|
+end;
|
|
|
|
+
|
|
|
|
+constructor TTextMappingList.Create;
|
|
|
|
+begin
|
|
|
|
+ FList := TFPObjectList.Create;
|
|
|
|
+end;
|
|
|
|
+
|
|
|
|
+destructor TTextMappingList.Destroy;
|
|
|
|
+begin
|
|
|
|
+ FList.Free;
|
|
|
|
+ inherited Destroy;
|
|
|
|
+end;
|
|
|
|
+
|
|
|
|
+function TTextMappingList.Add(AObject: TTextMapping): Integer;
|
|
|
|
+var
|
|
|
|
+ i: integer;
|
|
|
|
+begin
|
|
|
|
+ Result := -1;
|
|
|
|
+ for i := 0 to FList.Count-1 do
|
|
|
|
+ begin
|
|
|
|
+ if TTextMapping(FList.Items[i]).CharID = AObject.CharID then
|
|
|
|
+ Exit; // mapping already exists
|
|
|
|
+ end;
|
|
|
|
+ Result := FList.Add(AObject);
|
|
|
|
+end;
|
|
|
|
+
|
|
|
|
+function TTextMappingList.Add(const ACharID, AGlyphID: uint16): Integer;
|
|
|
|
+var
|
|
|
|
+ o: TTextMapping;
|
|
|
|
+begin
|
|
|
|
+ o := TTextMapping.Create;
|
|
|
|
+ o.CharID := ACharID;
|
|
|
|
+ o.GlyphID := AGlyphID;
|
|
|
|
+ Result := Add(o);
|
|
|
|
+ if Result = -1 then
|
|
|
|
+ o.Free;
|
|
|
|
+end;
|
|
|
|
+
|
|
|
|
+{ TTextMapping }
|
|
|
|
+
|
|
|
|
+class function TTextMapping.NewTextMap(const ACharID, AGlyphID: uint16): TTextMapping;
|
|
|
|
+begin
|
|
|
|
+ Result := TTextMapping.Create;
|
|
|
|
+ Result.CharID := ACharID;
|
|
|
|
+ Result.GlyphID := AGlyphID;
|
|
|
|
+end;
|
|
|
|
+
|
|
{ TPDFFont }
|
|
{ TPDFFont }
|
|
|
|
|
|
-constructor TPDFFont.Create(ACollection: TCollection);
|
|
|
|
|
|
+procedure TPDFFont.PrepareTextMapping;
|
|
begin
|
|
begin
|
|
- inherited Create(ACollection);
|
|
|
|
- FDictionary := TObjectList.create;
|
|
|
|
|
|
+ if FFontFilename <> '' then
|
|
|
|
+ begin
|
|
|
|
+ // only create objects when needed
|
|
|
|
+ FTextMappingList := TTextMappingList.Create;
|
|
|
|
+ FTrueTypeFile := TTFFileInfo.Create;
|
|
|
|
+ FTrueTypeFile.LoadFromFile(FFontFilename);
|
|
|
|
+ end;
|
|
|
|
+end;
|
|
|
|
+
|
|
|
|
+procedure TPDFFont.SetFontFilename(AValue: string);
|
|
|
|
+begin
|
|
|
|
+ if FFontFilename = AValue then
|
|
|
|
+ Exit;
|
|
|
|
+ FFontFilename := AValue;
|
|
|
|
+ PrepareTextMapping;
|
|
end;
|
|
end;
|
|
|
|
|
|
destructor TPDFFont.Destroy;
|
|
destructor TPDFFont.Destroy;
|
|
begin
|
|
begin
|
|
- FDictionary.Free;
|
|
|
|
|
|
+ FTextMappingList.Free;
|
|
|
|
+ FTrueTypeFile.Free;
|
|
inherited Destroy;
|
|
inherited Destroy;
|
|
end;
|
|
end;
|
|
|
|
|
|
-function TPDFFont.FindIndexOf(const AValue: UnicodeChar): integer;
|
|
|
|
|
|
+function TPDFFont.GetGlyphIndices(const AText: UnicodeString): AnsiString;
|
|
var
|
|
var
|
|
i: integer;
|
|
i: integer;
|
|
|
|
+ c: word;
|
|
begin
|
|
begin
|
|
- result := -1; // default to not found
|
|
|
|
- for i := 0 to FDictionary.Count-1 do
|
|
|
|
|
|
+ Result := '';
|
|
|
|
+ for i := 1 to Length(AText) do
|
|
begin
|
|
begin
|
|
- if TTextDictionary(FDictionary[i]).Char = AValue then
|
|
|
|
- begin
|
|
|
|
- result := i;
|
|
|
|
- exit;
|
|
|
|
- end;
|
|
|
|
|
|
+ c := Word(AText[i]);
|
|
|
|
+ Result := Result + IntToHex(FTrueTypeFile.GetGlyphIndex(c), 4);
|
|
|
|
+ end;
|
|
|
|
+end;
|
|
|
|
+
|
|
|
|
+procedure TPDFFont.AddTextToMappingList(const AText: UnicodeString);
|
|
|
|
+var
|
|
|
|
+ i: integer;
|
|
|
|
+ c: uint16; // Unicode codepoint
|
|
|
|
+begin
|
|
|
|
+ if AText = '' then
|
|
|
|
+ Exit;
|
|
|
|
+ for i := 1 to Length(AText) do
|
|
|
|
+ begin
|
|
|
|
+ c := uint16(AText[i]);
|
|
|
|
+ FTextMappingList.Add(c, FTrueTypeFile.GetGlyphIndex(c));
|
|
end;
|
|
end;
|
|
end;
|
|
end;
|
|
|
|
|
|
|
|
+{ TPDFTrueTypeCharWidths }
|
|
|
|
+
|
|
|
|
+procedure TPDFTrueTypeCharWidths.Write(const AStream: TStream);
|
|
|
|
+var
|
|
|
|
+ i: integer;
|
|
|
|
+ s: string;
|
|
|
|
+ lst: TTextMappingList;
|
|
|
|
+ lFont: TTFFileInfo;
|
|
|
|
+begin
|
|
|
|
+ s := '';
|
|
|
|
+ lst := Document.Fonts[EmbeddedFontNum].TextMapping;
|
|
|
|
+ lFont := Document.Fonts[EmbeddedFontNum].FTrueTypeFile;
|
|
|
|
+ for i := 0 to lst.Count-1 do
|
|
|
|
+ s := s + Format(' %d [%d]', [ lst[i].GlyphID, TTTFFriendClass(lFont).ToNatural(lFont.Widths[lst[i].GlyphID].AdvanceWidth)]);
|
|
|
|
+ WriteString(s, AStream);
|
|
|
|
+end;
|
|
|
|
+
|
|
|
|
+constructor TPDFTrueTypeCharWidths.Create(const ADocument: TPDFDocument; const AEmbeddedFontNum: integer);
|
|
|
|
+begin
|
|
|
|
+ inherited Create(ADocument);
|
|
|
|
+ FEmbeddedFontNum := AEmbeddedFontNum;
|
|
|
|
+end;
|
|
|
|
+
|
|
{ TPDFMoveTo }
|
|
{ TPDFMoveTo }
|
|
|
|
|
|
class function TPDFMoveTo.Command(APos: TPDFCoord): String;
|
|
class function TPDFMoveTo.Command(APos: TPDFCoord): String;
|
|
@@ -1384,27 +1536,12 @@ end;
|
|
|
|
|
|
procedure TPDFPage.AddTextToLookupLists(AText: UTF8String);
|
|
procedure TPDFPage.AddTextToLookupLists(AText: UTF8String);
|
|
var
|
|
var
|
|
- i: integer;
|
|
|
|
- c: UnicodeChar;
|
|
|
|
str: UnicodeString;
|
|
str: UnicodeString;
|
|
- idx: integer;
|
|
|
|
- dict: TTextDictionary;
|
|
|
|
begin
|
|
begin
|
|
if AText = '' then
|
|
if AText = '' then
|
|
Exit;
|
|
Exit;
|
|
str := UTF8ToUTF16(AText);
|
|
str := UTF8ToUTF16(AText);
|
|
- for i := 1 to Length(str) do
|
|
|
|
- begin
|
|
|
|
- c := str[i];
|
|
|
|
- idx := Document.Fonts[FFontIndex].FindIndexOf(c);
|
|
|
|
- if idx = -1 then
|
|
|
|
- begin
|
|
|
|
- dict := TTextDictionary.Create;
|
|
|
|
- dict.Char := c;
|
|
|
|
- dict.CodePoint := AnsiString(IntToHex(Word(c), 4));;
|
|
|
|
- Document.Fonts[FFontIndex].Dictionary.Add(dict);
|
|
|
|
- end;
|
|
|
|
- end;
|
|
|
|
|
|
+ Document.Fonts[FFontIndex].AddTextToMappingList(str);
|
|
end;
|
|
end;
|
|
|
|
|
|
procedure TPDFPage.DoUnitConversion(var APoint: TPDFCoord);
|
|
procedure TPDFPage.DoUnitConversion(var APoint: TPDFCoord);
|
|
@@ -1456,8 +1593,8 @@ begin
|
|
|
|
|
|
FMatrix._00 := 1;
|
|
FMatrix._00 := 1;
|
|
FMatrix._20 := 0;
|
|
FMatrix._20 := 0;
|
|
- FMatrix._11 := -1; // flip coordinates
|
|
|
|
- FMatrix._21 := GetPaperHeight;
|
|
|
|
|
|
+ FMatrix._11 := -1; // flip coordinates
|
|
|
|
+ AdjustMatrix; // sets FMatrix._21 value
|
|
end;
|
|
end;
|
|
|
|
|
|
destructor TPDFPage.Destroy;
|
|
destructor TPDFPage.Destroy;
|
|
@@ -1522,14 +1659,18 @@ begin
|
|
end;
|
|
end;
|
|
|
|
|
|
procedure TPDFPage.WriteUTF8Text(X, Y: TPDFFloat; AText: UTF8String);
|
|
procedure TPDFPage.WriteUTF8Text(X, Y: TPDFFloat; AText: UTF8String);
|
|
-Var
|
|
|
|
- T : TPDFUTF8Text;
|
|
|
|
|
|
+var
|
|
|
|
+ T: TPDFUTF8Text;
|
|
|
|
+ p: TPDFCoord;
|
|
begin
|
|
begin
|
|
if FFontIndex = -1 then
|
|
if FFontIndex = -1 then
|
|
- raise EPDF.Create('No FontIndex was set - please use SetFont() first.');
|
|
|
|
- T := Document.CreateUTF8Text(X,Y,AText,FFontIndex);
|
|
|
|
- AddObject(T);
|
|
|
|
|
|
+ raise EPDF.Create(SErrNoFontIndex);
|
|
|
|
+ p.X := X;
|
|
|
|
+ p.Y := Y;
|
|
|
|
+ DoUnitConversion(p);
|
|
AddTextToLookupLists(AText);
|
|
AddTextToLookupLists(AText);
|
|
|
|
+ T := Document.CreateUTF8Text(p.X, p.Y, AText, FFontIndex);
|
|
|
|
+ AddObject(T);
|
|
end;
|
|
end;
|
|
|
|
|
|
procedure TPDFPage.DrawLine(X1, Y1, X2, Y2, ALineWidth: TPDFFloat);
|
|
procedure TPDFPage.DrawLine(X1, Y1, X2, Y2, ALineWidth: TPDFFloat);
|
|
@@ -1557,7 +1698,7 @@ var
|
|
S: TPDFLineStyleDef;
|
|
S: TPDFLineStyleDef;
|
|
begin
|
|
begin
|
|
S := Document.LineStyles[AStyle];
|
|
S := Document.LineStyles[AStyle];
|
|
- SetColor(S.Color, False);
|
|
|
|
|
|
+ SetColor(S.Color, True);
|
|
SetPenStyle(S.PenStyle);
|
|
SetPenStyle(S.PenStyle);
|
|
DrawLine(X1, Y1, X2, Y2, S.LineWidth);
|
|
DrawLine(X1, Y1, X2, Y2, S.LineWidth);
|
|
end;
|
|
end;
|
|
@@ -1754,7 +1895,7 @@ var
|
|
P : Integer;
|
|
P : Integer;
|
|
|
|
|
|
begin
|
|
begin
|
|
- P:=Pos('-', AValue);
|
|
|
|
|
|
+ P:=RPos('-', AValue);
|
|
if (P>0) then
|
|
if (P>0) then
|
|
FontName:=Copy(AValue,1,P-1)
|
|
FontName:=Copy(AValue,1,P-1)
|
|
else
|
|
else
|
|
@@ -1917,9 +2058,15 @@ begin
|
|
Inc(ADocument.FObjectCount);
|
|
Inc(ADocument.FObjectCount);
|
|
end;
|
|
end;
|
|
|
|
|
|
|
|
+{ We opted to use the Str() function instead of FormatFloat(), because it is
|
|
|
|
+ considerably faster. This also works around the problem of locale specific
|
|
|
|
+ DecimalSeparator causing float formatting problems in the generated PDF. }
|
|
class function TPDFObject.FloatStr(F: TPDFFloat): String;
|
|
class function TPDFObject.FloatStr(F: TPDFFloat): String;
|
|
begin
|
|
begin
|
|
- Str(F:5:2,Result);
|
|
|
|
|
|
+ if ((Round(F*100) mod 100)=0) then
|
|
|
|
+ Str(F:4:0,Result)
|
|
|
|
+ else
|
|
|
|
+ Str(F:4:2,Result);
|
|
end;
|
|
end;
|
|
|
|
|
|
procedure TPDFObject.Write(const AStream: TStream);
|
|
procedure TPDFObject.Write(const AStream: TStream);
|
|
@@ -2002,10 +2149,29 @@ begin
|
|
Result := s;
|
|
Result := s;
|
|
end;
|
|
end;
|
|
|
|
|
|
|
|
+{ TPDFAbstractString }
|
|
|
|
+
|
|
|
|
+function TPDFAbstractString.InsertEscape(const AValue: string): string;
|
|
|
|
+var
|
|
|
|
+ S: string;
|
|
|
|
+begin
|
|
|
|
+ Result:='';
|
|
|
|
+ S:=AValue;
|
|
|
|
+ if Pos('\', S) > 0 then
|
|
|
|
+ S:=AnsiReplaceStr(S, '\', '\\');
|
|
|
|
+ if Pos('(', S) > 0 then
|
|
|
|
+ S:=AnsiReplaceStr(S, '(', '\(');
|
|
|
|
+ if Pos(')', S) > 0 then
|
|
|
|
+ S:=AnsiReplaceStr(S, ')', '\)');
|
|
|
|
+ Result:=S;
|
|
|
|
+end;
|
|
|
|
+
|
|
|
|
+{ TPDFString }
|
|
|
|
+
|
|
procedure TPDFString.Write(const AStream: TStream);
|
|
procedure TPDFString.Write(const AStream: TStream);
|
|
var
|
|
var
|
|
s: AnsiString;
|
|
s: AnsiString;
|
|
- cs: AnsiString;
|
|
|
|
|
|
+// cs: AnsiString;
|
|
begin
|
|
begin
|
|
s := Utf8ToAnsi(FValue);
|
|
s := Utf8ToAnsi(FValue);
|
|
if poCompressText in Document.Options then
|
|
if poCompressText in Document.Options then
|
|
@@ -2021,29 +2187,19 @@ end;
|
|
constructor TPDFString.Create(Const ADocument : TPDFDocument; const AValue: string);
|
|
constructor TPDFString.Create(Const ADocument : TPDFDocument; const AValue: string);
|
|
begin
|
|
begin
|
|
inherited Create(ADocument);
|
|
inherited Create(ADocument);
|
|
- FValue:=AValue;
|
|
|
|
|
|
+ FValue := AValue;
|
|
if (Pos('(', FValue) > 0) or (Pos(')', FValue) > 0) or (Pos('\', FValue) > 0) then
|
|
if (Pos('(', FValue) > 0) or (Pos(')', FValue) > 0) or (Pos('\', FValue) > 0) then
|
|
- FValue:=InsertEscape(FValue);
|
|
|
|
|
|
+ FValue := InsertEscape(FValue);
|
|
end;
|
|
end;
|
|
|
|
|
|
{ TPDFUTF8String }
|
|
{ TPDFUTF8String }
|
|
|
|
|
|
-function TPDFUTF8String.RemapedText: String;
|
|
|
|
|
|
+function TPDFUTF8String.RemapedText: AnsiString;
|
|
var
|
|
var
|
|
s: UnicodeString;
|
|
s: UnicodeString;
|
|
- i: integer;
|
|
|
|
- idx: integer; // character code
|
|
|
|
- c: UnicodeChar;
|
|
|
|
begin
|
|
begin
|
|
- Result := '';
|
|
|
|
s := UTF8ToUTF16(FValue);
|
|
s := UTF8ToUTF16(FValue);
|
|
- // lookup the alphabet code for each character
|
|
|
|
- for i := 1 to Length(s) do
|
|
|
|
- begin
|
|
|
|
- c := s[i];
|
|
|
|
- idx := Document.Fonts[FontIndex].FindIndexOf(c);
|
|
|
|
- Result := Result + IntToStrZeros(idx+1,2); // PDF expects 1-based numbering
|
|
|
|
- end;
|
|
|
|
|
|
+ Result := Document.Fonts[FontIndex].GetGlyphIndices(s);
|
|
end;
|
|
end;
|
|
|
|
|
|
procedure TPDFUTF8String.Write(const AStream: TStream);
|
|
procedure TPDFUTF8String.Write(const AStream: TStream);
|
|
@@ -2051,7 +2207,7 @@ begin
|
|
if poCompressText in Document.Options then
|
|
if poCompressText in Document.Options then
|
|
begin
|
|
begin
|
|
// do nothing yet
|
|
// do nothing yet
|
|
- WriteString('('+UTF8ToUTF16(FValue)+')', AStream);
|
|
|
|
|
|
+ WriteString('<'+RemapedText+'>', AStream)
|
|
end
|
|
end
|
|
else
|
|
else
|
|
WriteString('<'+RemapedText+'>', AStream);
|
|
WriteString('<'+RemapedText+'>', AStream);
|
|
@@ -2063,7 +2219,7 @@ begin
|
|
FValue := AValue;
|
|
FValue := AValue;
|
|
FFontIndex := AFontIndex;
|
|
FFontIndex := AFontIndex;
|
|
if (Pos('(', FValue) > 0) or (Pos(')', FValue) > 0) or (Pos('\', FValue) > 0) then
|
|
if (Pos('(', FValue) > 0) or (Pos(')', FValue) > 0) or (Pos('\', FValue) > 0) then
|
|
- FValue:=InsertEscape(FValue);
|
|
|
|
|
|
+ FValue := InsertEscape(FValue);
|
|
end;
|
|
end;
|
|
|
|
|
|
{ TPDFArray }
|
|
{ TPDFArray }
|
|
@@ -2482,9 +2638,9 @@ end;
|
|
|
|
|
|
procedure TPDFDictionary.WriteDictionary(const AObject: integer; const AStream: TStream);
|
|
procedure TPDFDictionary.WriteDictionary(const AObject: integer; const AStream: TStream);
|
|
var
|
|
var
|
|
- ISize,i, NumImg, NumFnt: integer;
|
|
|
|
|
|
+ ISize,i, NumImg, NumFnt, BufSize: integer;
|
|
Value: string;
|
|
Value: string;
|
|
- M : TMemoryStream;
|
|
|
|
|
|
+ M, Buf : TMemoryStream;
|
|
E : TPDFDictionaryItem;
|
|
E : TPDFDictionaryItem;
|
|
D : TPDFDictionary;
|
|
D : TPDFDictionary;
|
|
begin
|
|
begin
|
|
@@ -2524,13 +2680,21 @@ begin
|
|
Value:=E.FKey.Name;
|
|
Value:=E.FKey.Name;
|
|
NumFnt:=StrToInt(Copy(Value, Succ(Pos(' ', Value)), Length(Value) - Pos(' ', Value)));
|
|
NumFnt:=StrToInt(Copy(Value, Succ(Pos(' ', Value)), Length(Value) - Pos(' ', Value)));
|
|
m.LoadFromFile(Document.FontFiles[NumFnt]);
|
|
m.LoadFromFile(Document.FontFiles[NumFnt]);
|
|
- // write fontfile stream length in xobject dictionary
|
|
|
|
- D:=Document.GlobalXRefs[AObject].Dict;
|
|
|
|
- D.AddInteger('Length',M.Size);
|
|
|
|
- LastElement.Write(AStream);
|
|
|
|
- WriteString('>>', AStream);
|
|
|
|
- // write fontfile stream in xobject dictionary
|
|
|
|
- TPDFEmbeddedFont.WriteEmbeddedFont(Document, M, AStream);
|
|
|
|
|
|
+ Buf := TMemoryStream.Create;
|
|
|
|
+ try
|
|
|
|
+ // write fontfile stream (could be compressed or not) to a temporary buffer so we can get the size
|
|
|
|
+ BufSize := TPDFEmbeddedFont.WriteEmbeddedFont(Document, M, Buf);
|
|
|
|
+ Buf.Position := 0;
|
|
|
|
+ // write fontfile stream length in xobject dictionary
|
|
|
|
+ D := Document.GlobalXRefs[AObject].Dict;
|
|
|
|
+ D.AddInteger('Length', BufSize);
|
|
|
|
+ LastElement.Write(AStream);
|
|
|
|
+ WriteString('>>', AStream);
|
|
|
|
+ // write fontfile buffer stream in xobject dictionary
|
|
|
|
+ Buf.SaveToStream(AStream);
|
|
|
|
+ finally
|
|
|
|
+ Buf.Free;
|
|
|
|
+ end;
|
|
finally
|
|
finally
|
|
M.Free;
|
|
M.Free;
|
|
end;
|
|
end;
|
|
@@ -2637,31 +2801,28 @@ end;
|
|
|
|
|
|
procedure TPDFToUnicode.Write(const AStream: TStream);
|
|
procedure TPDFToUnicode.Write(const AStream: TStream);
|
|
var
|
|
var
|
|
|
|
+ lst: TTextMappingList;
|
|
i: integer;
|
|
i: integer;
|
|
- lFontIndex: integer;
|
|
|
|
- lFont: TPDFFont;
|
|
|
|
begin
|
|
begin
|
|
- WriteString('/CIDInit/ProcSet findresource begin'+CRLF, AStream);
|
|
|
|
|
|
+ lst := Document.Fonts[EmbeddedFontNum].TextMapping;
|
|
|
|
+
|
|
|
|
+ WriteString('/CIDInit /ProcSet findresource begin'+CRLF, AStream);
|
|
WriteString('12 dict begin'+CRLF, AStream);
|
|
WriteString('12 dict begin'+CRLF, AStream);
|
|
WriteString('begincmap'+CRLF, AStream);
|
|
WriteString('begincmap'+CRLF, AStream);
|
|
- WriteString('/CIDSystemInfo <<'+CRLF, AStream);
|
|
|
|
- WriteString('/Registry (Adobe)'+CRLF, AStream);
|
|
|
|
- WriteString('/Ordering (UCS)'+CRLF, AStream);
|
|
|
|
|
|
+ WriteString('/CIDSystemInfo'+CRLF, AStream);
|
|
|
|
+ WriteString('<</Registry (Adobe)'+CRLF, AStream);
|
|
|
|
+ WriteString('/Ordering (Identity)'+CRLF, AStream);
|
|
WriteString('/Supplement 0'+CRLF, AStream);
|
|
WriteString('/Supplement 0'+CRLF, AStream);
|
|
WriteString('>> def'+CRLF, AStream);
|
|
WriteString('>> def'+CRLF, AStream);
|
|
- WriteString('/CMapName/Adobe-Identity-UCS def'+CRLF, AStream);
|
|
|
|
|
|
+ WriteString(Format('/CMapName /%s def', [Document.Fonts[EmbeddedFontNum].FTrueTypeFile.PostScriptName])+CRLF, AStream);
|
|
WriteString('/CMapType 2 def'+CRLF, AStream);
|
|
WriteString('/CMapType 2 def'+CRLF, AStream);
|
|
WriteString('1 begincodespacerange'+CRLF, AStream);
|
|
WriteString('1 begincodespacerange'+CRLF, AStream);
|
|
- WriteString('<00> <FF>'+CRLF, AStream);
|
|
|
|
|
|
+ WriteString('<0000> <FFFF>'+CRLF, AStream);
|
|
WriteString('endcodespacerange'+CRLF, AStream);
|
|
WriteString('endcodespacerange'+CRLF, AStream);
|
|
-
|
|
|
|
- lFont := Document.Fonts[EmbeddedFontNum];
|
|
|
|
- WriteString(Format('%d beginbfchar'+CRLF, [lFont.Dictionary.Count]), AStream);
|
|
|
|
- // write mapping for each character in dictionary
|
|
|
|
- for i := 0 to lFont.Dictionary.Count-1 do
|
|
|
|
- WriteString(Format('<%s> <%s>'+CRLF, [IntToStrZeros(i+1, 2), TTextDictionary(lFont.Dictionary[i]).CodePoint]), AStream); // PDF expects 1-based numbering
|
|
|
|
|
|
+ WriteString(Format('%d beginbfchar', [lst.Count])+CRLF, AStream);
|
|
|
|
+ for i := 0 to lst.Count-1 do
|
|
|
|
+ WriteString(Format('<%s> <%s>', [IntToHex(lst[i].GlyphID, 4), IntToHex(lst[i].CharID, 4)])+CRLF, AStream);
|
|
WriteString('endbfchar'+CRLF, AStream);
|
|
WriteString('endbfchar'+CRLF, AStream);
|
|
-
|
|
|
|
WriteString('endcmap'+CRLF, AStream);
|
|
WriteString('endcmap'+CRLF, AStream);
|
|
WriteString('CMapName currentdict /CMap defineresource pop'+CRLF, AStream);
|
|
WriteString('CMapName currentdict /CMap defineresource pop'+CRLF, AStream);
|
|
WriteString('end'+CRLF, AStream);
|
|
WriteString('end'+CRLF, AStream);
|
|
@@ -2675,6 +2836,7 @@ begin
|
|
FFontDef := AFontDef;
|
|
FFontDef := AFontDef;
|
|
end;
|
|
end;
|
|
|
|
|
|
|
|
+{ TPDFDocument }
|
|
|
|
|
|
procedure TPDFDocument.SetInfos(AValue: TPDFInfos);
|
|
procedure TPDFDocument.SetInfos(AValue: TPDFInfos);
|
|
begin
|
|
begin
|
|
@@ -2722,7 +2884,7 @@ begin
|
|
p:=GetX(i).Dict.Elements[0].Value;
|
|
p:=GetX(i).Dict.Elements[0].Value;
|
|
if (p is TPDFName) and (TPDFName(p).Name=AValue) then
|
|
if (p is TPDFName) and (TPDFName(p).Name=AValue) then
|
|
Result:=i;
|
|
Result:=i;
|
|
- Inc(I);
|
|
|
|
|
|
+ Inc(I);
|
|
end;
|
|
end;
|
|
end;
|
|
end;
|
|
|
|
|
|
@@ -2944,24 +3106,19 @@ begin
|
|
end;
|
|
end;
|
|
|
|
|
|
procedure TPDFDocument.CreateStdFont(EmbeddedFontName: string; EmbeddedFontNum: integer);
|
|
procedure TPDFDocument.CreateStdFont(EmbeddedFontName: string; EmbeddedFontNum: integer);
|
|
-
|
|
|
|
var
|
|
var
|
|
FDict: TPDFDictionary;
|
|
FDict: TPDFDictionary;
|
|
N: TPDFName;
|
|
N: TPDFName;
|
|
-
|
|
|
|
begin
|
|
begin
|
|
- if Pos('Italic', EmbeddedFontName) > 0 then
|
|
|
|
- EmbeddedFontName:=Copy(EmbeddedFontName, 1, Pred(Pos('Italic', EmbeddedFontName)))+'Oblique';
|
|
|
|
- // AnsiReplaceText(EmbeddedFontName,'Italic','Oblique');
|
|
|
|
// add xref entry
|
|
// add xref entry
|
|
- FDict:=CreateGlobalXRef.Dict;
|
|
|
|
|
|
+ FDict := CreateGlobalXRef.Dict;
|
|
FDict.AddName('Type', 'Font');
|
|
FDict.AddName('Type', 'Font');
|
|
FDict.AddName('Subtype', 'Type1');
|
|
FDict.AddName('Subtype', 'Type1');
|
|
FDict.AddName('Encoding', 'WinAnsiEncoding');
|
|
FDict.AddName('Encoding', 'WinAnsiEncoding');
|
|
FDict.AddInteger('FirstChar', 32);
|
|
FDict.AddInteger('FirstChar', 32);
|
|
FDict.AddInteger('LastChar', 255);
|
|
FDict.AddInteger('LastChar', 255);
|
|
FDict.AddName('BaseFont', EmbeddedFontName);
|
|
FDict.AddName('BaseFont', EmbeddedFontName);
|
|
- N:=CreateName('F'+IntToStr(EmbeddedFontNum));
|
|
|
|
|
|
+ N := CreateName('F'+IntToStr(EmbeddedFontNum));
|
|
FDict.AddElement('Name',N);
|
|
FDict.AddElement('Name',N);
|
|
AddFontNameToPages(N.Name,GLobalXRefCount-1);
|
|
AddFontNameToPages(N.Name,GLobalXRefCount-1);
|
|
// add font reference to all page dictionary
|
|
// add font reference to all page dictionary
|
|
@@ -3005,8 +3162,10 @@ begin
|
|
FontDef.FFontBBox := s;
|
|
FontDef.FFontBBox := s;
|
|
FontDef.FItalicAngle := IntToStr(lFontDef.ItalicAngle);
|
|
FontDef.FItalicAngle := IntToStr(lFontDef.ItalicAngle);
|
|
FontDef.FStemV := IntToStr(lFontDef.StemV);
|
|
FontDef.FStemV := IntToStr(lFontDef.StemV);
|
|
- {$NOTE The 1000 value is a work-around until I can figure out the character spacing problem. }
|
|
|
|
- FontDef.FMissingWidth := '1000'; //IntToStr(lFontDef.MissingWidth);
|
|
|
|
|
|
+
|
|
|
|
+ {$NOTE The 700 value is a work-around until I can figure out the character spacing problem. }
|
|
|
|
+ FontDef.FMissingWidth := '700'; //IntToStr(lFontDef.MissingWidth);
|
|
|
|
+
|
|
FontDef.FEncoding := lFontDef.Encoding;
|
|
FontDef.FEncoding := lFontDef.Encoding;
|
|
FontDef.FFile := lFName;
|
|
FontDef.FFile := lFName;
|
|
FontDef.FOriginalSize := IntToStr(lFontDef.OriginalSize);
|
|
FontDef.FOriginalSize := IntToStr(lFontDef.OriginalSize);
|
|
@@ -3023,50 +3182,79 @@ procedure TPDFDocument.CreateTTFFont(const EmbeddedFontNum: integer; FontDef : T
|
|
var
|
|
var
|
|
FDict: TPDFDictionary;
|
|
FDict: TPDFDictionary;
|
|
N: TPDFName;
|
|
N: TPDFName;
|
|
-
|
|
|
|
|
|
+ Arr: TPDFArray;
|
|
begin
|
|
begin
|
|
// add xref entry
|
|
// add xref entry
|
|
- FDict:=CreateGlobalXRef.Dict;
|
|
|
|
|
|
+ FDict := CreateGlobalXRef.Dict;
|
|
FDict.AddName('Type', 'Font');
|
|
FDict.AddName('Type', 'Font');
|
|
- FDict.AddName('Subtype', FontDef.FType);
|
|
|
|
- FDict.AddName('Encoding', 'WinAnsiEncoding');
|
|
|
|
- FDict.AddInteger('FirstChar', 32);
|
|
|
|
- FDict.AddInteger('LastChar', 255);
|
|
|
|
|
|
+ FDict.AddName('Subtype', 'Type0');
|
|
FDict.AddName('BaseFont', FontDef.FName);
|
|
FDict.AddName('BaseFont', FontDef.FName);
|
|
|
|
+ FDict.AddName('Encoding', 'Identity-H');
|
|
// add name element to font dictionary
|
|
// add name element to font dictionary
|
|
N:=CreateName('F'+IntToStr(EmbeddedFontNum));
|
|
N:=CreateName('F'+IntToStr(EmbeddedFontNum));
|
|
FDict.AddElement('Name',N);
|
|
FDict.AddElement('Name',N);
|
|
Self.AddFontNameToPages(N.Name,GlobalXRefCount-1);
|
|
Self.AddFontNameToPages(N.Name,GlobalXRefCount-1);
|
|
- CreateFontDescriptor(EmbeddedFontNum,FontDef);
|
|
|
|
- // add fontdescriptor reference to font dictionary
|
|
|
|
- FDict.AddReference('FontDescriptor',GlobalXRefCount-2);
|
|
|
|
- CreateFontWidth(FontDef);
|
|
|
|
- // add fontwidth reference to font dictionary
|
|
|
|
- FDict.AddReference('Widths',GlobalXRefCount-1);
|
|
|
|
|
|
+ CreateTTFDescendantFont(EmbeddedFontNum, FontDef);
|
|
|
|
+ Arr := CreateArray;
|
|
|
|
+ FDict.AddElement('DescendantFonts', Arr);
|
|
|
|
+ Arr.AddItem(TPDFReference.Create(self, GlobalXRefCount-4));
|
|
CreateToUnicode(EmbeddedFontNum, FontDef);
|
|
CreateToUnicode(EmbeddedFontNum, FontDef);
|
|
FDict.AddReference('ToUnicode', GlobalXRefCount-1);
|
|
FDict.AddReference('ToUnicode', GlobalXRefCount-1);
|
|
FontFiles.Add(FontDef.FFile);
|
|
FontFiles.Add(FontDef.FFile);
|
|
end;
|
|
end;
|
|
|
|
|
|
|
|
+procedure TPDFDocument.CreateTTFDescendantFont(const EmbeddedFontNum: integer; FontDef: TFontDef);
|
|
|
|
+var
|
|
|
|
+ FDict: TPDFDictionary;
|
|
|
|
+ N: TPDFName;
|
|
|
|
+ Arr: TPDFArray;
|
|
|
|
+begin
|
|
|
|
+ // add xref entry
|
|
|
|
+ FDict := CreateGlobalXRef.Dict;
|
|
|
|
+ FDict.AddName('Type', 'Font');
|
|
|
|
+ FDict.AddName('Subtype', 'CIDFontType2');
|
|
|
|
+ FDict.AddName('BaseFont', FontDef.FName);
|
|
|
|
+
|
|
|
|
+ CreateTTFCIDSystemInfo(EmbeddedFontNum, FontDef);
|
|
|
|
+ FDict.AddReference('CIDSystemInfo', GlobalXRefCount-1);
|
|
|
|
+
|
|
|
|
+ // add fontdescriptor reference to font dictionary
|
|
|
|
+ CreateFontDescriptor(EmbeddedFontNum,FontDef);
|
|
|
|
+ FDict.AddReference('FontDescriptor',GlobalXRefCount-2);
|
|
|
|
+
|
|
|
|
+ Arr := CreateArray;
|
|
|
|
+ FDict.AddElement('W',Arr);
|
|
|
|
+ Arr.AddItem(TPDFTrueTypeCharWidths.Create(self, EmbeddedFontNum));
|
|
|
|
+end;
|
|
|
|
+
|
|
|
|
+procedure TPDFDocument.CreateTTFCIDSystemInfo(const EmbeddedFontNum: integer; FontDef: TFontDef);
|
|
|
|
+var
|
|
|
|
+ FDict: TPDFDictionary;
|
|
|
|
+begin
|
|
|
|
+ FDict := CreateGlobalXRef.Dict;
|
|
|
|
+ FDict.AddString('Registry', 'Adobe');
|
|
|
|
+ FDict.AddString('Ordering', 'Identity');
|
|
|
|
+ FDict.AddInteger('Supplement', 0);
|
|
|
|
+end;
|
|
|
|
+
|
|
procedure TPDFDocument.CreateTp1Font(const EmbeddedFontNum: integer);
|
|
procedure TPDFDocument.CreateTp1Font(const EmbeddedFontNum: integer);
|
|
begin
|
|
begin
|
|
Assert(EmbeddedFontNum<>-1);
|
|
Assert(EmbeddedFontNum<>-1);
|
|
end;
|
|
end;
|
|
|
|
|
|
procedure TPDFDocument.CreateFontDescriptor(const EmbeddedFontNum: integer; FontDef : TFontDef);
|
|
procedure TPDFDocument.CreateFontDescriptor(const EmbeddedFontNum: integer; FontDef : TFontDef);
|
|
-
|
|
|
|
var
|
|
var
|
|
Arr: TPDFArray;
|
|
Arr: TPDFArray;
|
|
FDict: TPDFDictionary;
|
|
FDict: TPDFDictionary;
|
|
-
|
|
|
|
begin
|
|
begin
|
|
FDict:=CreateGlobalXRef.Dict;
|
|
FDict:=CreateGlobalXRef.Dict;
|
|
FDict.AddName('Type', 'FontDescriptor');
|
|
FDict.AddName('Type', 'FontDescriptor');
|
|
FDict.AddName('FontName', FontDef.FName);
|
|
FDict.AddName('FontName', FontDef.FName);
|
|
|
|
+ FDict.AddName('FontFamily', Fonts[EmbeddedFontNum].FTrueTypeFile.FamilyName);
|
|
FDict.AddInteger('Ascent', StrToInt(FontDef.FAscent));
|
|
FDict.AddInteger('Ascent', StrToInt(FontDef.FAscent));
|
|
FDict.AddInteger('Descent', StrToInt(FontDef.FDescent));
|
|
FDict.AddInteger('Descent', StrToInt(FontDef.FDescent));
|
|
FDict.AddInteger('CapHeight', StrToInt(FontDef.FCapHeight));
|
|
FDict.AddInteger('CapHeight', StrToInt(FontDef.FCapHeight));
|
|
- FDict.AddInteger('Flags', StrToInt(FontDef.FFlags));
|
|
|
|
|
|
+ FDict.AddInteger('Flags', 32);
|
|
Arr:=CreateArray;
|
|
Arr:=CreateArray;
|
|
FDict.AddElement('FontBBox',Arr);
|
|
FDict.AddElement('FontBBox',Arr);
|
|
Arr.AddIntArray(FontDef.FFontBBox);
|
|
Arr.AddIntArray(FontDef.FFontBBox);
|
|
@@ -3099,14 +3287,13 @@ begin
|
|
Arr.AddIntArray(FontDef.FCharWidth);
|
|
Arr.AddIntArray(FontDef.FCharWidth);
|
|
end;
|
|
end;
|
|
|
|
|
|
-procedure TPDFDocument.CreateFontFileEntry(const EmbeddedFontNum: integer;FontDef : TFontDef);
|
|
|
|
-
|
|
|
|
|
|
+procedure TPDFDocument.CreateFontFileEntry(const EmbeddedFontNum: integer; FontDef: TFontDef);
|
|
var
|
|
var
|
|
FDict: TPDFDictionary;
|
|
FDict: TPDFDictionary;
|
|
-
|
|
|
|
begin
|
|
begin
|
|
FDict:=CreateGlobalXRef.Dict;
|
|
FDict:=CreateGlobalXRef.Dict;
|
|
- FDict.AddName('Filter','FlateDecode');
|
|
|
|
|
|
+ if poCompressFonts in Options then
|
|
|
|
+ FDict.AddName('Filter','FlateDecode');
|
|
FDict.AddInteger('Length1 '+IntToStr(EmbeddedFontNum),StrToInt(FontDef.FOriginalSize));
|
|
FDict.AddInteger('Length1 '+IntToStr(EmbeddedFontNum),StrToInt(FontDef.FOriginalSize));
|
|
end;
|
|
end;
|
|
|
|
|
|
@@ -3152,8 +3339,8 @@ var
|
|
begin
|
|
begin
|
|
Contents:=CreateGlobalXRef;
|
|
Contents:=CreateGlobalXRef;
|
|
Contents.FStream:=CreateStream(False);
|
|
Contents.FStream:=CreateStream(False);
|
|
- Result:=GLobalXRefCount-1;
|
|
|
|
- GLobalXrefs[GLobalXRefCount-2].Dict.AddReference('Contents',Result);
|
|
|
|
|
|
+ Result:=GlobalXRefCount-1;
|
|
|
|
+ GlobalXrefs[GlobalXRefCount-2].Dict.AddReference('Contents',Result);
|
|
end;
|
|
end;
|
|
|
|
|
|
procedure TPDFDocument.CreatePageStream(APage : TPDFPage; PageNum: integer);
|
|
procedure TPDFDocument.CreatePageStream(APage : TPDFPage; PageNum: integer);
|
|
@@ -3387,17 +3574,16 @@ begin
|
|
NumFont:=0;
|
|
NumFont:=0;
|
|
for i:=0 to Fonts.Count-1 do
|
|
for i:=0 to Fonts.Count-1 do
|
|
begin
|
|
begin
|
|
- FontName:=ExtractBaseFontName(Fonts[i].Name);
|
|
|
|
- P:=Pos('-', FontName);
|
|
|
|
- if P>0 then
|
|
|
|
- FtName:=LowerCase(Copy(FontName,1,P-1))
|
|
|
|
- else
|
|
|
|
- FtName:=LowerCase(FontName);
|
|
|
|
- if (FtName='courier') or (FtName='helvetica') or (FtName='times') then
|
|
|
|
- begin
|
|
|
|
- FontName[1]:=UpCase(FontName[1]);
|
|
|
|
- CreateStdFont(FontName,NumFont);
|
|
|
|
- end
|
|
|
|
|
|
+ FontName := Fonts[i].Name;
|
|
|
|
+ { Acrobat Reader expects us to be case sensitive. Other PDF viewers are case-insensitive. }
|
|
|
|
+ if (FontName='Courier') or (FontName='Courier-Bold') or (FontName='Courier-Oblique') or (FontName='Courier-BoldOblique')
|
|
|
|
+ or (FontName='Helvetica') or (FontName='Helvetica-Bold') or (FontName='Helvetica-Oblique') or (FontName='Helvetica-BoldOblique')
|
|
|
|
+ or (FontName='Times-Roman') or (FontName='Times-Bold') or (FontName='Times-Italic') or (FontName='Times-BoldItalic')
|
|
|
|
+ or (FontName='Symbol')
|
|
|
|
+ or (FontName='Zapf Dingbats') then
|
|
|
|
+ begin
|
|
|
|
+ CreateStdFont(FontName, NumFont);
|
|
|
|
+ end
|
|
else if (LoadFont(Fonts[i], FontDef)='TrueType') then
|
|
else if (LoadFont(Fonts[i], FontDef)='TrueType') then
|
|
CreateTtfFont(NumFont,FontDef)
|
|
CreateTtfFont(NumFont,FontDef)
|
|
else
|
|
else
|
|
@@ -3428,6 +3614,7 @@ begin
|
|
(Trailer.ValueByName('Size') as TPDFInteger).Value:=GlobalXRefCount;
|
|
(Trailer.ValueByName('Size') as TPDFInteger).Value:=GlobalXRefCount;
|
|
AStream.Position:=0;
|
|
AStream.Position:=0;
|
|
TPDFObject.WriteString(PDF_VERSION+CRLF, AStream);
|
|
TPDFObject.WriteString(PDF_VERSION+CRLF, AStream);
|
|
|
|
+ TPDFObject.WriteString(PDF_BINARY_BLOB+CRLF, AStream); // write some binary data as recommended in PDF Spec section 3.4.1 (File Header)
|
|
// write numbered indirect objects
|
|
// write numbered indirect objects
|
|
for i:=1 to GlobalXRefCount-1 do
|
|
for i:=1 to GlobalXRefCount-1 do
|
|
begin
|
|
begin
|
|
@@ -3537,22 +3724,48 @@ begin
|
|
end;
|
|
end;
|
|
|
|
|
|
function TPDFDocument.AddFont(AName: String; AColor : TARGBColor = clBlack): Integer;
|
|
function TPDFDocument.AddFont(AName: String; AColor : TARGBColor = clBlack): Integer;
|
|
-Var
|
|
|
|
- F : TPDFFont;
|
|
|
|
-
|
|
|
|
|
|
+var
|
|
|
|
+ F: TPDFFont;
|
|
|
|
+ i: integer;
|
|
begin
|
|
begin
|
|
- F:=Fonts.AddFontDef;
|
|
|
|
- F.Name:=AName;
|
|
|
|
- F.Color:=AColor;
|
|
|
|
- Result:=Fonts.Count-1;
|
|
|
|
|
|
+ { reuse existing font definition if it exists }
|
|
|
|
+ for i := 0 to Fonts.Count-1 do
|
|
|
|
+ begin
|
|
|
|
+ if Fonts[i].Name = AName then
|
|
|
|
+ begin
|
|
|
|
+ Result := i;
|
|
|
|
+ Exit;
|
|
|
|
+ end;
|
|
|
|
+ end;
|
|
|
|
+ F := Fonts.AddFontDef;
|
|
|
|
+ F.Name := AName;
|
|
|
|
+ F.Color := AColor;
|
|
|
|
+ Result := Fonts.Count-1;
|
|
end;
|
|
end;
|
|
|
|
|
|
function TPDFDocument.AddFont(AFontFile: String; AName: String; AColor: TARGBColor): Integer;
|
|
function TPDFDocument.AddFont(AFontFile: String; AName: String; AColor: TARGBColor): Integer;
|
|
-Var
|
|
|
|
- F : TPDFFont;
|
|
|
|
|
|
+var
|
|
|
|
+ F: TPDFFont;
|
|
|
|
+ i: integer;
|
|
|
|
+ lFName: string;
|
|
begin
|
|
begin
|
|
- F:=Fonts.AddFontDef;
|
|
|
|
- F.FontFile := AFontFile;
|
|
|
|
|
|
+ { reuse existing font definition if it exists }
|
|
|
|
+ for i := 0 to Fonts.Count-1 do
|
|
|
|
+ begin
|
|
|
|
+ if Fonts[i].Name = AName then
|
|
|
|
+ begin
|
|
|
|
+ Result := i;
|
|
|
|
+ Exit;
|
|
|
|
+ end;
|
|
|
|
+ end;
|
|
|
|
+ F := Fonts.AddFontDef;
|
|
|
|
+ if ExtractFilePath(AFontFile) <> '' then
|
|
|
|
+ // assume AFontFile is the full path to the TTF file
|
|
|
|
+ lFName := AFontFile
|
|
|
|
+ else
|
|
|
|
+ // assume it's just the TTF filename
|
|
|
|
+ lFName := IncludeTrailingPathDelimiter(FontDirectory)+AFontFile;
|
|
|
|
+ F.FontFile := lFName;
|
|
F.Name := AName;
|
|
F.Name := AName;
|
|
F.Color := AColor;
|
|
F.Color := AColor;
|
|
Result := Fonts.Count-1;
|
|
Result := Fonts.Count-1;
|
|
@@ -3572,5 +3785,6 @@ begin
|
|
Result:=FLineStyleDefs.Count-1;
|
|
Result:=FLineStyleDefs.Count-1;
|
|
end;
|
|
end;
|
|
|
|
|
|
|
|
+
|
|
end.
|
|
end.
|
|
|
|
|