{******************************************} { } { FastReport v4.0 } { PDF file library } { } { Copyright (c) 1998-2008 } { by Alexander Fediachov, } { Fast Reports Inc. } {******************************************} { Add CJK Font support by } { crispin2k@hotmail.com } { http://www.jane.com.tw } {******************************************} unit frxPDFFile; interface {$I frx.inc} {$DEFINE PDF_RC4} uses Windows, Messages, SysUtils, Classes, Graphics, ComObj, ComCtrls, frxClass, frxUtils, JPEG, frxUnicodeUtils {$IFDEF Delphi6}, Variants {$ENDIF} {$IFDEF PDF_RC4}, frxRC4{$ELSE}, frxRC4, frxAES{$ENDIF} {$IFDEF Delphi10} , WideStrings {$ENDIF} {$IFDEF Delphi12} , AnsiStrings {$ENDIF}; type TfrxPDFEncBit = (ePrint, eModify, eCopy, eAnnot); TfrxPDFEncBits = set of TfrxPDFEncBit; TfrxPDFPage = class; TfrxPDFFont = class; TfrxPDFElement = class(TObject) private FXrefPosition: Cardinal; FIndex: Integer; FCR: Boolean; procedure Write(Stream: TStream; const S: AnsiString);{$IFDEF Delphi12} overload; procedure Write(Stream: TStream; const S: String); overload; {$ENDIF} procedure WriteLn(Stream: TStream; const S: AnsiString);{$IFDEF Delphi12} overload; procedure WriteLn(Stream: TStream; const S: String); overload; {$ENDIF} public constructor Create; procedure SaveToStream(const Stream: TStream); virtual; property XrefPosition: Cardinal read FXrefPosition; property Index: Integer read FIndex write FIndex; end; TfrxPDFFile = class(TfrxPDFElement) private FPages: TList; FFonts: TList; FXRef: TStringList; FObjNo: Integer; FCounter: Integer; FTitle: String; FStartXRef: Cardinal; FStartFonts: Integer; FStartPages: Integer; FPagesRoot: Integer; FCompressed: Boolean; FPrintOpt: Boolean; FOutline: Boolean; FPreviewOutline: TfrxCustomOutline; FSubject: String; FAuthor: String; FBackground: Boolean; FCreator: String; FHTMLTags: Boolean; FPageNumbers: String; FTotalPages: Integer; FFileID: AnsiString; FProtection: Boolean; FEncBits: Cardinal; FProtectionFlags: TfrxPDFEncBits; FOwnerPassword: AnsiString; FUserPassword: AnsiString; FEncKey: AnsiString; FOPass: AnsiString; FUPass: AnsiString; FKeywords: WideString; FProducer: WideString; FPrintScaling: Boolean; FFitWindow: Boolean; FHideMenubar: Boolean; FCenterWindow: Boolean; FHideWindowUI: Boolean; FHideToolbar: Boolean; procedure PrepareKeys; function GetOwnerPassword: AnsiString; function GetUserPassword: AnsiString; procedure SetProtectionFlags(const Value: TfrxPDFEncBits); public FStreamObjects: TStream; FTempStreamFile: String; FEmbedded: Boolean; FFontDCnt: Integer; constructor Create(const UseFileCache: Boolean; const TempDir: String); destructor Destroy; override; procedure Clear; procedure XRefAdd(Stream: TStream; ObjNo: Integer); procedure SaveToStream(const Stream: TStream); override; function AddPage(const Page: TfrxReportPage): TfrxPDFPage; function AddFont(const Font: TFont): Integer; procedure Start; property Pages: TList read FPages; property Fonts: TList read FFonts; property Counter: Integer read FCounter write FCounter; property Compressed: Boolean read FCompressed write FCompressed; property EmbeddedFonts: Boolean read FEmbedded write FEmbedded default True; property PrintOptimized: Boolean read FPrintOpt write FPrintOpt; property Outline: Boolean read FOutline write FOutline; property PreviewOutline: TfrxCustomOutline read FPreviewOutline write FPreviewOutline; property Background: Boolean read FBackground write FBackground; property Title: String read FTitle write FTitle; property Creator: String read FCreator write FCreator; property Producer: WideString read FProducer write FProducer; property Keywords: WideString read FKeywords write FKeywords; property Author: String read FAuthor write FAuthor; property Subject: String read FSubject write FSubject; property HTMLTags: Boolean read FHTMLTags write FHTMLTags; property PageNumbers: String read FPageNumbers write FPageNumbers; property TotalPages: Integer read FTotalPages write FTotalPages; property Protection: Boolean read FProtection write FProtection; property UserPassword: AnsiString read FUserPassword write FUserPassword; property OwnerPassword: AnsiString read FOwnerPassword write FOwnerPassword; property ProtectionFlags: TfrxPDFEncBits read FProtectionFlags write SetProtectionFlags; property HideToolbar: Boolean read FHideToolbar write FHideToolbar; property HideMenubar: Boolean read FHideMenubar write FHideMenubar; property HideWindowUI: Boolean read FHideWindowUI write FHideWindowUI; property FitWindow: Boolean read FFitWindow write FFitWindow; property CenterWindow: Boolean read FCenterWindow write FCenterWindow; property PrintScaling: Boolean read FPrintScaling write FPrintScaling; end; TfrxPDFPage = class(TfrxPDFElement) private FStreamOffset: Longint; FParent: TfrxPDFFile; FWidth: Extended; FHeight: Extended; FMarginLeft: Extended; FMarginTop: Extended; FStream: TStream; FStreamSize: Longint; FDivider: Extended; FLastColor: TColor; FLastColorResult: String; FBMP: TBitmap; FDefFontCharSet: Integer; function CodepageByCharset(const Charset: Integer): Integer; public constructor Create; destructor Destroy; override; procedure SaveToStream(const Stream: TStream); override; procedure AddObject(const Obj: TfrxView); property StreamOffset: Longint read FStreamOffset write FStreamOffset; property StreamSize: Longint read FStreamSize write FStreamSize; property OutStream: TStream read FStream write FStream; property Parent: TfrxPDFFile read FParent write FParent; property Width: Extended read FWidth write FWidth; property Height: Extended read FHeight write FHeight; property MarginLeft: Extended read FMarginLeft write FMarginLeft; property MarginTop: Extended read FMarginTop write FMarginTop; end; TfrxPDFFont = class(TfrxPDFElement) private FFont: TFont; FParent: TfrxPDFFile; FFontDCnt: Integer; FCodepage: Integer; public constructor Create; destructor Destroy; override; procedure SaveToStream(const Stream: TStream); override; property Parent: TfrxPDFFile read FParent write FParent; property Font: TFont read FFont; property Codepage: Integer read FCodepage write FCodepage; end; TfrxPDFOutlineNode = class(TObject) private FNumber: Integer; FDest: Integer; FTop: Integer; FCountTree: Integer; FCount: Integer; FTitle: String; FLast: TfrxPDFOutlineNode; FNext: TfrxPDFOutlineNode; FParent: TfrxPDFOutlineNode; FPrev: TfrxPDFOutlineNode; FFirst: TfrxPDFOutlineNode; public constructor Create; destructor Destroy; override; property Title: String read FTitle write FTitle; property Dest: Integer read FDest write FDest; property Top: Integer read FTop write FTop; property Number: Integer read FNumber write FNumber; property CountTree: Integer read FCountTree write FCountTree; property Count: Integer read FCount write FCount; property First: TfrxPDFOutlineNode read FFirst write FFirst; property Last: TfrxPDFOutlineNode read FLast write FLast; property Parent: TfrxPDFOutlineNode read FParent write FParent; property Prev: TfrxPDFOutlineNode read FPrev write FPrev; property Next: TfrxPDFOutlineNode read FNext write FNext; end; implementation uses frxGraphicUtils, frxGzip, frxMD5, ActiveX, SyncObjs, math; var pdfCS: TCriticalSection; const PDF_VER = '1.5'; PDF_DIVIDER = 0.75; PDF_MARG_DIVIDER = 0.05; PDF_PRINTOPT = 3; PDF_PK: array [ 1..32 ] of Byte = ( $28, $BF, $4E, $5E, $4E, $75, $8A, $41, $64, $00, $4E, $56, $FF, $FA, $01, $08, $2E, $2E, $00, $B6, $D0, $68, $3E, $80, $2F, $0C, $A9, $FE, $64, $53, $69, $7A ); type PABC = ^ABCarray; ABCarray = array [0..255] of ABC; function GetID: AnsiString; var AGUID: TGUID; AGUIDString: widestring; begin CoCreateGUID(AGUID); SetLength(AGUIDString, 39); StringFromGUID2(AGUID, PWideChar(AGUIDString), 39); Result := AnsiString(PWideChar(AGUIDString)); MD5String(AnsiString(PWideChar(AGUIDString))); end; function frxReverseStringU(const AText: WideString): WideString; var I: Integer; P: PWideChar; begin SetLength(Result, Length(AText)); P := PWideChar(Result); for I := Length(AText) downto 1 do begin P^ := AText[I]; Inc(P); end; end; function GetLocaleInformation(Flag: Integer): AnsiString; var pcLCA: array[0..20] of AnsiChar; begin if (GetLocaleInfoA(LOCALE_SYSTEM_DEFAULT, Flag, pcLCA,19) <= 0 ) then pcLCA[0] := #0; Result := pcLCA; end; function CheckOEM(const Value: WideString): boolean; var i: integer; begin result := false; for i := 1 to Length(Value) do if (ByteType(Value, i) <> mbSingleByte) or (Ord(Value[i]) > 122) or (Ord(Value[i]) < 32) then begin result := true; Break; end; end; function StrToUTF16U(const Value: WideString): AnsiString; var i: integer; pwc: ^Word; begin result := 'FEFF'; for i := 1 to Length(Value) do begin pwc := @Value[i]; result := result + AnsiString(IntToHex(pwc^, 4)); end; end; function StrToHex(const Value: AnsiString): AnsiString; var i: integer; begin result := ''; for i := 1 to Length(Value) do result := result + AnsiString(IntToHex(Byte(Value[i]), 2)); end; function StrToUTF16(const Value: AnsiString): AnsiString; var PW: Pointer; Len: integer; i: integer; pwc: ^Word; begin result := 'FEFF'; Len := MultiByteToWideChar(0, CP_ACP, PAnsiChar(Value), Length(Value), nil, 0); GetMem(PW, Len * 2); try Len := MultiByteToWideChar(0, CP_ACP, PAnsiChar(Value), Length(Value), PW, Len * 2); pwc := PW; for i := 0 to Len - 1 do begin result := result + AnsiString(IntToHex(pwc^, 4)); Inc(pwc); end; finally FreeMem(PW); end; end; function HexEncode7F(Str: WideString): AnsiString; var s: AnsiString; Index, Len: Integer; begin s := ''; Len := Length(Str); Index := 0; while Index < Len do begin Index := Index + 1; if Byte(Str[Index]) > $7F then s := s + '#' + AnsiString(IntToHex(Byte(Str[Index]), 2)) else s := s + AnsiString(Str[Index]); end; Result := s; end; function Dec2Oct(const i: Longint): AnsiString; var m, j: Longint; Begin Result := ''; j := i; while j > 0 Do begin m := j mod 8; Result := AnsiChar(m + Ord('0')) + Result; j := j div 8; end; Result := StringOfChar(AnsiChar('0'), 3 - Length(Result)) + Result; end; function StrToOct(const Value: AnsiString): AnsiString; var i: Integer; begin Result := ''; for i := 1 to Length(Value) do Result := Result + '\' + Dec2Oct(Ord(Value[i])); end; function EscapeSpecialChar(TextStr: AnsiString): AnsiString; var I: Integer; begin Result := ''; for I := 1 to Length ( TextStr ) do case TextStr [ I ] of '(': Result := Result + '\('; ')': Result := Result + '\)'; '\': Result := Result + '\\'; #13: Result := result + '\r'; #10: Result := result + '\n'; else Result := Result + AnsiChar(chr ( Ord ( textstr [ i ] ) )); end; end; function CryptStr(Source: AnsiString; Key: AnsiString; Enc: Boolean; id: Integer): AnsiString; var {$IFDEF PDF_RC4} k: array [ 1..21 ] of Byte; rc4: TfrxRC4; {$ELSE} k: array [ 1..25 ] of Byte; aes: TfrxAES; {$ENDIF} s, s1, ss: AnsiString; begin if Enc then begin {$IFDEF PDF_RC4} rc4 := TfrxRC4.Create; {$ELSE} aes := TfrxAES.Create; {$ENDIF} try s := Key; FillChar(k, 21, 0); Move(s[1], k, 16); Move(id, k [17], 3); {$IFDEF PDF_RC4} SetLength(s1, 21); MD5Buf(@k, 21, @s1[1]); {$ELSE} k[22] := $73; k[23] := $41; k[24] := $6c; k[25] := $54; SetLength(s1, 25); MD5Buf(@k, 25, @s1[1]); {$ENDIF} ss := Source; {$IFDEF PDF_RC4} SetLength(Result, Length(ss)); rc4.Start(@s1[1], 16); rc4.Crypt(@ss[1], @Result[1], Length(ss)); Result := EscapeSpecialChar(Result); {$ELSE} aes.Start(s1); Result := EscapeSpecialChar(aes.Crypt(ss)); {$ENDIF} finally {$IFDEF PDF_RC4} rc4.Free; {$ELSE} aes.Free; {$ENDIF} end; end else Result := EscapeSpecialChar(Source); end; function CryptStream(Source: TStream; Target: TStream; Key: AnsiString; id: Integer): AnsiString; var s: AnsiString; {$IFDEF PDF_RC4} k: array [ 1..21 ] of Byte; rc4: TfrxRC4; m1, m2: TMemoryStream; {$ELSE} k: array [ 1..25 ] of Byte; aes: TfrxAES; {$ENDIF} begin FillChar(k, 21, 0); Move(Key[1], k, 16); Move(id, k[17], 3); {$IFDEF PDF_RC4} SetLength(s, 16); MD5Buf(@k, 21, @s[1]); {$ELSE} k[22] := $73; k[23] := $41; k[24] := $6c; k[25] := $54; SetLength(s, 25); MD5Buf(@k, 25, @s[1]); {$ENDIF} {$IFDEF PDF_RC4} m1 := TMemoryStream.Create; m2 := TMemoryStream.Create; rc4 := TfrxRC4.Create; {$ELSE} aes := TfrxAES.Create; {$ENDIF} try {$IFDEF PDF_RC4} m1.LoadFromStream(Source); m2.SetSize(m1.Size); rc4.Start(@s[1], 16); rc4.Crypt(m1.Memory, m2.Memory, m1.Size); m2.SaveToStream(Target); {$ELSE} aes.Start(s); SetLength(s, Source.Size); Source.Read(s[1], Source.Size); s := aes.Crypt(s); Target.Write(Stream, s[1], Length(s)); {$ENDIF} finally {$IFDEF PDF_RC4} m1.Free; m2.Free; rc4.Free; {$ELSE} aes.Free; {$ENDIF} end; end; function PrepareString(const Text: WideString; Key: AnsiString; Enc: Boolean; id: Integer): AnsiString; begin if Enc then begin Result := '(' + CryptStr(AnsiString(Text), Key, Enc, id) + ')' end else Result := '<' + StrToUTF16(AnsiString(Text)) + '>' end; function UnicodeToANSI(const Str: WideString; Codepage: Integer): AnsiString; var i: Integer; begin Result := ''; i := WideCharToMultiByte(CodePage, 0, @Str[1], Length(Str), nil, 0, nil, nil); if i <> 0 then begin SetLength(Result, i); WideCharToMultiByte(CodePage, 0, @Str[1], Length(Str), @Result[1], i, nil, nil) end; end; { TfrxPDFFile } constructor TfrxPDFFile.Create(const UseFileCache: Boolean; const TempDir: String); begin inherited Create; FPages := TList.Create; FFonts := TList.Create; FXRef := TStringList.Create; FCounter := 4; FStartPages := 0; FStartXRef := 0; FStartFonts := 0; FCompressed := True; FPrintOpt := False; FOutline := False; FPreviewOutline := nil; FHTMLTags := False; FFontDCnt := 0; FObjNo := 0; if UseFileCache then begin FTempStreamFile := frxCreateTempFile(TempDir); FStreamObjects := TFileStream.Create(FTempStreamFile, fmCreate); end else FStreamObjects := TMemoryStream.Create; ProtectionFlags := [ePrint, eModify, eCopy, eAnnot]; end; destructor TfrxPDFFile.Destroy; begin Clear; FXRef.Free; FPages.Free; FFonts.Free; FStreamObjects.Free; try DeleteFile(FTempStreamFile); except end; inherited; end; procedure TfrxPDFFile.Clear; var i: Integer; begin for i := 0 to FPages.Count - 1 do TfrxPDFPage(FPages[i]).Free; FPages.Clear; for i := 0 to FFonts.Count - 1 do TfrxPDFFont(FFonts[i]).Free; FFonts.Clear; FXRef.Clear; ProtectionFlags := [ePrint, eModify, eCopy, eAnnot]; end; procedure TfrxPDFFile.SaveToStream(const Stream: TStream); var i, j: Integer; s, s1: {Ansi}String; Page, Top: Integer; Text: String; Parent: Integer; OutlineCount: Integer; NodeNumber: Integer; OutlineTree: TfrxPDFOutlineNode; pgN: TStringList; FOutlineN: Integer; function CheckPageInRange(const PageN: Integer): Boolean; begin Result := True; if (pgN.Count <> 0) and (pgN.IndexOf(IntToStr(PageN + 1)) = -1) then Result := False; end; procedure DoPrepareOutline(Node: TfrxPDFOutlineNode); var i: Integer; p: TfrxPDFOutlineNode; prev: TfrxPDFOutlineNode; begin Inc(NodeNumber); prev := nil; p := nil; for i := 0 to FPreviewOutline.Count - 1 do begin FPreviewOutline.GetItem(i, Text, Page, Top); if CheckPageInRange(Page) then begin p := TfrxPDFOutlineNode.Create; p.Title := Text; p.Dest := Page; p.Top := Top; p.Prev := prev; if prev <> nil then prev.Next := p else Node.First := p; prev := p; p.Parent := Node; FPreviewOutline.LevelDown(i); DoPrepareOutline(p); Node.Count := Node.Count + 1; Node.CountTree := Node.CountTree + p.CountTree + 1; FPreviewOutline.LevelUp; end; end; Node.Last := p; end; procedure DoWriteOutline(Node: TfrxPDFOutlineNode; Parent: Integer); var p: TfrxPDFOutlineNode; i: Integer; begin p := Node; if p.Dest = -1 then p.Number := Parent else begin p.Number := FCounter; Inc(FObjNo); XRefAdd(Stream, FObjNo); WriteLn(Stream, IntToStr(FCounter) + ' 0 obj'); Inc(FCounter); WriteLn(Stream, '<<'); WriteLn(Stream, '/Title ' + PrepareString(p.Title, FEncKey, FProtection, FCounter - 1)); WriteLn(Stream, '/Parent ' + IntToStr(Parent) + ' 0 R'); if p.Prev <> nil then WriteLn(Stream, '/Prev ' + IntToStr(p.Prev.Number) + ' 0 R'); if p.First <> nil then begin WriteLn(Stream, '/First ' + IntToStr(p.Number + 1) + ' 0 R'); WriteLn(Stream, '/Last ' + IntToStr(p.Number + p.CountTree - p.Last.CountTree ) + ' 0 R'); WriteLn(Stream, '/Count ' + IntToStr(p.Count)); end; if p.Next <> nil then WriteLn(Stream, '/Next ' + IntToStr(p.Number + p.CountTree + 1) + ' 0 R'); if CheckPageInRange(p.Dest) then begin if FEmbedded then i := FFontDCnt + 1 else i := FFontDCnt; if pgN.Count > 0 then s := '/Dest [' + IntToStr(FpagesRoot + FFonts.Count * i + pgN.IndexOf(IntToStr(p.Dest + 1)) * 2 + 1) + ' 0 R /XYZ 0 ' + IntToStr(Round(TfrxPDFPage(FPages[pgN.IndexOf(IntToStr(p.Dest + 1))]).Height - p.Top * PDF_DIVIDER)) + ' 0]' else s := '/Dest [' + IntToStr(FpagesRoot + FFonts.Count * i + p.Dest * 2 + 1) + ' 0 R /XYZ 0 ' + IntToStr(Round(TfrxPDFPage(FPages[p.Dest]).Height - p.Top * PDF_DIVIDER)) + ' 0]'; WriteLn(Stream, s); end; WriteLn(Stream, '>>'); WriteLn(Stream, 'endobj'); end; if p.First <> nil then DoWriteOutline(p.First, p.Number); if p.Next <> nil then DoWriteOutline(p.Next, Parent); end; begin inherited SaveToStream(Stream); OutlineCount := 0; OutlineTree := nil; if FOutline then if not Assigned(FPreviewOutline) then FOutline := False else FPreviewOutline.LevelRoot; FCounter := 1; WriteLn(Stream, '%PDF-' + PDF_VER); WriteLn(Stream, '%'#226#227#207#211); Inc(FObjNo); XRefAdd(Stream, FObjNo); WriteLn(Stream, IntToStr(FCounter) + ' 0 obj'); Inc(FCounter); WriteLn(Stream, '<<'); WriteLn(Stream, '/Type /Catalog'); i := 0; if FOutline then begin OutlineTree := TfrxPDFOutlineNode.Create; pgN := TStringList.Create; NodeNumber := 0; frxParsePageNumbers(PageNumbers, pgN, FTotalPages); DoPrepareOutline(OutlineTree); if OutlineTree.CountTree > 0 then begin OutlineCount := OutlineTree.CountTree - OutlineTree.Last.CountTree; i := OutlineTree.CountTree + 1; end else begin OutlineTree.Free; pgN.Free; FOutline := False; end; end; FPagesRoot := FObjNo + 2 + i; WriteLn(Stream, '/Pages ' + IntToStr(FPagesRoot) + ' 0 R'); if FOutline then s1 := '/UseOutlines' else s1 := '/UseNone'; WriteLn(Stream, '/PageMode ' + s1); if FOutline then WriteLn(Stream, '/Outlines ' + IntToStr(FCounter + 1) + ' 0 R'); WriteLn(Stream, '/ViewerPreferences <<'); if FTitle <> '' then WriteLn(Stream, '/DisplayDocTitle true'); if FHideToolbar then WriteLn(Stream, '/HideToolbar true'); if FHideMenubar then WriteLn(Stream, '/HideMenubar true'); if FHideWindowUI then WriteLn(Stream, '/HideWindowUI true'); if FFitWindow then WriteLn(Stream, '/FitWindow true'); if FCenterWindow then WriteLn(Stream, '/CenterWindow true'); if not FPrintScaling then WriteLn(Stream, '/PrintScaling /None'); WriteLn(Stream, '>>'); WriteLn(Stream, '>>'); WriteLn(Stream, 'endobj'); Inc(FObjNo); XRefAdd(Stream, FObjNo); WriteLn(Stream, IntToStr(FCounter) + ' 0 obj'); Inc(FCounter); WriteLn(Stream, '<<'); WriteLn(Stream, '/Title ' + PrepareString(FTitle, FEncKey, FProtection, FCounter - 1)); WriteLn(Stream, '/Author ' + PrepareString(FAuthor, FEncKey, FProtection, FCounter - 1)); WriteLn(Stream, '/Subject ' + PrepareString(FSubject, FEncKey, FProtection, FCounter - 1)); WriteLn(Stream, '/Keywords ' + PrepareString(FKeywords, FEncKey, FProtection, FCounter - 1)); WriteLn(Stream, '/Creator ' + PrepareString(FCreator, FEncKey, FProtection, FCounter - 1)); WriteLn(Stream, '/Producer ' + PrepareString(FProducer, FEncKey, FProtection, FCounter - 1)); s := 'D:' + FormatDateTime('yyyy', Now) + FormatDateTime('mm', Now) + FormatDateTime('dd', Now) + FormatDateTime('hh', Now) + FormatDateTime('nn', Now) + FormatDateTime('ss', Now); if FProtection then begin WriteLn(Stream, '/CreationDate ' + PrepareString(s, FEncKey, FProtection, FCounter - 1)); WriteLn(Stream, '/ModDate ' + PrepareString(s, FEncKey, FProtection, FCounter - 1)); end else begin WriteLn(Stream, '/CreationDate (' + s + ')'); WriteLn(Stream, '/ModDate (' + s + ')'); end; WriteLn(Stream, '>>'); WriteLn(Stream, 'endobj'); if FOutline then begin Inc(FObjNo); XRefAdd(Stream, FObjNo); FOutlineN := FCounter; WriteLn(Stream, IntToStr(FOutlineN) + ' 0 obj'); Parent := FCounter; Inc(FCounter); FPreviewOutline.LevelRoot; WriteLn(Stream, '<<'); WriteLn(Stream, '/Count ' + IntToStr(FPreviewOutline.Count)); WriteLn(Stream, '/First ' + IntToStr(FCounter) + ' 0 R'); WriteLn(Stream, '/Last ' + IntToStr(FCounter + OutlineCount - 1) + ' 0 R'); WriteLn(Stream, '>>'); WriteLn(Stream, 'endobj'); try DoWriteOutline(OutlineTree, Parent); finally OutlineTree.Free; end; pgN.Free; FCounter := FCounter + FPreviewOutline.Count; end; FStartFonts := FObjNo; Inc(FObjNo); for i := 0 to FFonts.Count - 1 do TfrxPDFFont(FFonts[i]).SaveToStream(Stream); FStartPages := FObjNo + 1; for i := 0 to FPages.Count - 1 do begin TfrxPDFPage(FPages[FPages.Count - 1]).StreamSize := FStreamObjects.Size - TfrxPDFPage(FPages[FPages.Count - 1]).StreamOffset; TfrxPDFPage(FPages[i]).SaveToStream(Stream); end; XRefAdd(Stream, FPagesRoot); WriteLn(Stream, IntToStr(FPagesRoot) + ' 0 obj'); WriteLn(Stream, '<<'); WriteLn(Stream, '/Type /Pages'); Write(Stream, '/Kids ['); for i := 0 to FPages.Count - 1 do Write(Stream, IntToStr(FStartPages + i * 2) + ' 0 R '); WriteLn(Stream, ']'); WriteLn(Stream, '/Count ' + IntToStr(FPages.Count)); WriteLn(Stream, '>>'); WriteLn(Stream, 'endobj'); FStartXRef := Stream.Position; WriteLn(Stream, 'xref'); WriteLn(Stream, '0 ' + IntToStr(FXRef.Count + 1)); WriteLn(Stream, '0000000000 65535 f'); for i := 1 to FXRef.Count do begin j := FXRef.IndexOfObject(TObject(i)); if j <> -1 then WriteLn(Stream, FXRef.Strings[j] + ' 00000 n'); end; WriteLn(Stream, 'trailer'); WriteLn(Stream, '<<'); WriteLn(Stream, '/Size ' + IntToStr(FXref.Count + 1)); WriteLn(Stream, '/Root 1 0 R'); WriteLn(Stream, '/Info 2 0 R'); WriteLn(Stream, '/ID [<' + FFileID + '><' + FFileID + '>]'); if FProtection then begin WriteLn(Stream, '/Encrypt <<'); WriteLn(Stream, '/Filter /Standard' ); {$IFDEF PDF_RC4} WriteLn(Stream, '/V 2'); WriteLn(Stream, '/R 3'); {$ELSE} WriteLn(Stream, '/V 4'); WriteLn(Stream, '/R 4'); WriteLn(Stream, '/CF <<'); WriteLn(Stream, '/StdCF <<'); WriteLn(Stream, '/Type /CryptAlgorithm'); WriteLn(Stream, '/CFM /AESV2'); WriteLn(Stream, '/AuthEvent /DocOpen'); WriteLn(Stream, '>>'); WriteLn(Stream, '>>'); WriteLn(Stream, '/StrF /StdCF'); WriteLn(Stream, '/StmF /StdCF'); {$ENDIF} WriteLn(Stream, '/Length 128'); WriteLn(Stream, '/P ' + IntToStr(Integer(FEncBits))); WriteLn(Stream, '/O (' + EscapeSpecialChar(GetOwnerPassword) + ')'); WriteLn(Stream, '/U (' + EscapeSpecialChar(GetUserPassword) + ')'); WriteLn(Stream, '>>'); end; WriteLn(Stream, '>>'); WriteLn(Stream, 'startxref'); WriteLn(Stream, IntToStr(FStartXRef)); WriteLn(Stream, '%%EOF'); end; procedure TfrxPDFFile.XRefAdd(Stream: TStream; ObjNo: Integer); begin FXRef.AddObject(StringOfChar('0', 10 - Length(IntToStr(Stream.Position))) + IntToStr(Stream.Position), TObject(ObjNo)); end; function TfrxPDFFile.AddFont(const Font: TFont): Integer; var Font2: TfrxPDFFont; i, j: Integer; begin j := -1; for i := 0 to FFonts.Count - 1 do begin Font2 := TfrxPDFFont(FFonts[i]); if (Font2.Font.Name = Font.Name) and (Font2.Font.Style = Font.Style) and (Font2.Font.Charset = Font.Charset) then begin j := i; break; end; end; if j = -1 then begin Font2 := TfrxPDFFont.Create; Font2.Parent := Self; Font2.Font.Assign(Font); FFonts.Add(Font2); j := FFonts.Count - 1; Font2.Index := j + 1 end; Result := j; end; function TfrxPDFFile.AddPage(const Page: TfrxReportPage): TfrxPDFPage; var PDFPage: TfrxPDFPage; begin PDFPage := TfrxPDFPage.Create; PDFPage.Width := Page.Width * PDF_DIVIDER; PDFPage.Height := Page.Height * PDF_DIVIDER; PDFPage.MarginLeft := Page.LeftMargin * PDF_MARG_DIVIDER; PDFPAge.MarginTop := Page.TopMargin * PDF_MARG_DIVIDER; PDFPage.Parent := Self; PDFPage.OutStream := FStreamObjects; PDFPage.StreamOffset := FStreamObjects.Position; if FPages.Count > 0 then TfrxPDFPage(FPages[FPages.Count - 1]).StreamSize := FStreamObjects.Position - TfrxPDFPage(FPages[FPages.Count - 1]).StreamOffset; FPages.Add(PDFPage); PDFPage.Index := FPages.Count; Result := PDFPage; FFontDCnt := 2; end; function PMD52Str(p: Pointer): AnsiString; begin SetLength(Result, 16); Move(p^, Result[1], 16); end; function PadPassword(Password: AnsiString): AnsiString; var i: Integer; begin i := Length(Password); Result := Copy(Password, 1, i); SetLength(Result, 32); if i < 32 then Move(PDF_PK, Result[i + 1], 32 - i); end; procedure TfrxPDFFile.PrepareKeys; var s, s1, p, p1, fid: AnsiString; i, j: Integer; rc4: TfrxRC4; md5: TfrxMD5; begin // OWNER KEY if FOwnerPassword = '' then FOwnerPassword := FUserPassword; p := PadPassword(FOwnerPassword); md5 := TfrxMD5.Create; try md5.Init; md5.Update(@p[1], 32); md5.Finalize; s := PMD52Str(md5.Digest); for i := 1 to 50 do begin md5.Init; md5.Update(@s[1], 16); md5.Finalize; s := PMD52Str(md5.Digest); end; finally md5.Free; end; rc4 := TfrxRC4.Create; try p := PadPassword(FUserPassword); SetLength(s1, 32); rc4.Start(@s[1], 16); rc4.Crypt(@p[1], @s1[1], 32); SetLength(p1, 16); for i := 1 to 19 do begin for j := 1 to 16 do p1[j] := AnsiChar(Byte(s[j]) xor i); rc4.Start(@p1[1], 16); rc4.Crypt(@s1[1], @s1[1], 32); end; FOPass := s1; finally rc4.Free; end; // ENCRYPTION KEY p := PadPassword(FUserPassword); md5 := TfrxMD5.Create; try md5.Init; md5.Update(@p[1], 32); md5.Update(@FOPass[1], 32); md5.Update(@FEncBits, 4); fid := ''; for i := 1 to 16 do fid := fid + AnsiChar(chr(Byte(StrToInt('$' + String(FFileID[i * 2 - 1] + FFileID[i * 2]))))); md5.Update(@fid[1], 16); md5.Finalize; s := PMD52Str(md5.Digest); for i := 1 to 50 do begin md5.Init; md5.Update(@s[1], 16); md5.Finalize; s := PMD52Str(md5.Digest); end; finally md5.Free; end; FEncKey := s; // USER KEY md5 := TfrxMD5.Create; try md5.Update(@PDF_PK, 32); md5.Update(@fid[1], 16); md5.Finalize; s := PMD52Str(md5.Digest); s1 := FEncKey; rc4 := TfrxRC4.Create; try rc4.Start(@s1[1], 16 ); rc4.Crypt(@s[1], @s[1], 16 ); SetLength(p1, 16); for i := 1 to 19 do begin for j := 1 to 16 do p1[j] := AnsiChar(Byte(s1[j]) xor i); rc4.Start(@p1[1], 16 ); rc4.Crypt(@s[1], @s[1], 16 ); end; FUPass := s; finally rc4.Free; end; SetLength(FUPass, 32); FillChar(FUPass[17], 16, 0); finally md5.Free; end; end; function TfrxPDFFile.GetOwnerPassword: AnsiString; begin Result := FOPass; end; function TfrxPDFFile.GetUserPassword: AnsiString; begin Result := FUPass; end; procedure TfrxPDFFile.SetProtectionFlags(const Value: TfrxPDFEncBits); begin FProtectionFlags := Value; FEncBits := $FFFFFFC0; FEncBits := FEncBits + (Cardinal(ePrint in Value) shl 2 + Cardinal(eModify in Value) shl 3 + Cardinal(eCopy in Value) shl 4 + Cardinal(eAnnot in Value) shl 5); end; procedure TfrxPDFFile.Start; begin FFileID := MD5String(GetID); if FProtection then PrepareKeys; end; { TfrxPDFPage } constructor TfrxPDFPage.Create; begin inherited; FMarginLeft := 0; FMarginTop := 0; FDivider := frxDrawText.DefPPI / frxDrawText.ScrPPI; FLastColor := clBlack; FLastColorResult := '0 0 0'; FBMP := TBitmap.Create; FDefFontCharSet := GetDefFontCharSet; end; procedure TfrxPDFPage.SaveToStream(const Stream: TStream); var i, id: Integer; s: String; TmpPageStream: TMemoryStream; TmpPageStream2: TMemoryStream; begin inherited SaveToStream(Stream); Inc(Parent.FObjNo); Parent.XRefAdd(Stream, Parent.FObjNo); id := Parent.FFontDCnt + Parent.FStartFonts + (Index - 1) * 2; WriteLn(Stream, IntToStr(id) + ' 0 obj'); WriteLn(Stream, '<<'); WriteLn(Stream, '/Type /Page'); WriteLn(Stream, '/Parent ' + IntToStr(Parent.FPagesRoot) + ' 0 R'); WriteLn(Stream, '/MediaBox [0 0 ' + frFloat2Str(FWidth) + ' ' + frFloat2Str(FHeight) + ' ]'); WriteLn(Stream, '/Resources <<'); WriteLn(Stream, '/Font <<'); for i := 0 to Parent.FFonts.Count - 1 do WriteLn(Stream, '/F' + IntToStr(TfrxPDFFont(Parent.FFonts[i]).Index - 1) + ' ' + IntToStr(TfrxPDFFont(Parent.FFonts[i]).FFontDCnt + Parent.FStartFonts) + ' 0 R'); WriteLn(Stream, '>>'); WriteLn(Stream, '/XObject <<'); WriteLn(Stream, '>>'); WriteLn(Stream, '/ProcSet [/PDF /Text /ImageC ]'); WriteLn(Stream, '>>'); WriteLn(Stream, '/Contents ' + IntToStr(id + 1) + ' 0 R'); WriteLn(Stream, '>>'); WriteLn(Stream, 'endobj'); Inc(Parent.FObjNo); Parent.XRefAdd(Stream, Parent.FObjNo); id := id + 1; WriteLn(Stream, IntToStr(id) + ' 0 obj'); Write(Stream, '<< '); TmpPageStream := TMemoryStream.Create; TmpPageStream2 := TMemoryStream.Create; try OutStream.Position := FStreamOffset; TmpPageStream2.CopyFrom(OutStream, FStreamSize); if Parent.FCompressed then begin frxDeflateStream(TmpPageStream2, TmpPageStream, gzFastest); s := '/Filter /FlateDecode /Length ' + IntToStr(TmpPageStream.Size) + ' /Length1 ' + IntToStr(TmpPageStream2.Size); end else s := '/Length ' + IntToStr(TmpPageStream2.Size); WriteLn(Stream, s + ' >>'); WriteLn(Stream, 'stream'); if Parent.FCompressed then begin if Parent.Protection then CryptStream(TmpPageStream, Stream, Parent.FEncKey, id) else Stream.CopyFrom(TmpPageStream, 0); WriteLn(Stream, ''); end else if Parent.Protection then CryptStream(TmpPageStream2, Stream, Parent.FEncKey, id) else Stream.CopyFrom(TmpPageStream2, 0); finally TmpPageStream2.Free; TmpPageStream.Free; end; WriteLn(Stream, 'endstream'); WriteLn(Stream, 'endobj'); end; function TfrxPDFPage.CodepageByCharset(const Charset: Integer): Integer; var i: Integer; begin if Charset = DEFAULT_CHARSET then i := FDefFontCharSet else i := CharSet; case i of EASTEUROPE_CHARSET: Result := 1250; RUSSIAN_CHARSET: Result := 1251; GREEK_CHARSET: Result := 1253; TURKISH_CHARSET: Result := 1254; HEBREW_CHARSET: Result := 1255; ARABIC_CHARSET: Result := 1256; BALTIC_CHARSET: Result := 1257; VIETNAMESE_CHARSET: Result := 1258; JOHAB_CHARSET: Result := 1361; THAI_CHARSET: Result := 874; SHIFTJIS_CHARSET: Result := 932; GB2312_CHARSET: Result := 936; HANGEUL_CHARSET: Result := 949; CHINESEBIG5_CHARSET: Result := 950; SYMBOL_CHARSET: Result := 42; OEM_CHARSET: Result := CP_OEMCP; else Result := 1252; end; end; procedure TfrxPDFPage.AddObject(const Obj: TfrxView); var FontIndex: Integer; x, y, dx, dy, fdx, fdy, PGap, FCharSpacing, ow, oh: Extended; i, iz: Integer; Jpg: TJPEGImage; s: AnsiString; su: WideString; Lines: TWideStrings; TempBitmap: TBitmap; OldFrameWidth: Extended; TempColor: TColor; Left, Right, Top, Bottom, Width, Height, BWidth, BHeight: String; FUnderlineSize: Double; FRealBounds: TfrxRect; FLineHeight: Extended; FTextHeight: Extended; FHeightWoMargin: Extended; FTextWidth: Extended; alpha, cosa, sina, rx, ry: Extended; function GetLeft(const Left: Extended): Extended; begin Result := FMarginLeft + Left * PDF_DIVIDER end; function GetTop(const Top: Extended): Extended; begin Result := FHeightWoMargin - Top * PDF_DIVIDER end; function GetVTextPos(const Top: Extended; const Height: Extended; const Text: String; const Align: TfrxVAlign; const Line: Integer = 0; const Count: Integer = 1): Extended; var i: Integer; begin if Line <= Count then i := Line else i := 0; if Align = vaBottom then Result := Top + Height - FLineHeight * (Count - i - 1) else if Align = vaCenter then Result := Top + (Height - (FLineHeight * Count)) / 2 + FLineHeight * (i + 1) else Result := Top + FLineHeight * i + FTextHeight; end; function GetHTextPos(const Left: Extended; const Width: Extended; const CharSpacing: Extended; const Text: String; const Align: TfrxHAlign): Extended; begin if (Align = haLeft) or (Align = haBlock) then Result := Left else begin FBMP.Canvas.Lock; try FBMP.Canvas.Font.Assign(frxDrawText.Canvas.Font); FTextWidth := FBMP.Canvas.TextWidth(Text) / FDivider + Length(Text) * CharSpacing; finally FBMP.Canvas.Unlock; end; if Align = haCenter then Result := Left + (Width - FTextWidth) / 2 else Result := Left + Width - FTextWidth; end; end; function GetPDFColor(const Color: TColor): String; var TheRgbValue : TColorRef; begin if Color = clBlack then Result := '0 0 0' else if Color = clWhite then Result := '1 1 1' else if Color = FLastColor then Result := FLastColorResult else begin TheRgbValue := ColorToRGB(Color); Result:= frFloat2Str(Byte(TheRGBValue) / 255) + ' ' + frFloat2Str(Byte(TheRGBValue shr 8) / 255) + ' ' + frFloat2Str(Byte(TheRGBValue shr 16) / 255); FLastColor := Color; FLastColorResult := Result; end; end; procedure MakeUpFrames; begin if (Obj.Frame.Typ <> []) and (Obj.Frame.Color <> clNone) then begin Write(OutStream, GetPDFColor(Obj.Frame.Color) + ' RG'#13#10 + frFloat2Str(Obj.Frame.Width * PDF_DIVIDER) + ' w'#13#10); if Obj.Frame.Typ = [ftTop, ftRight, ftBottom, ftLeft] then Write(OutStream, Left + ' ' + Top + ' m'#13#10 + Right + ' ' + Top + ' l'#13#10 + Right + ' ' + Bottom + ' l'#13#10 + Left + ' ' + Bottom + ' l'#13#10 + Left + ' ' + Top + ' l'#13#10's'#13#10) else begin if ftTop in Obj.Frame.Typ then Write(OutStream, Left + ' ' + Top + ' m'#13#10 + Right + ' ' + Top + ' l'#13#10'S'#13#10); if ftRight in Obj.Frame.Typ then Write(OutStream, Right + ' ' + Top + ' m'#13#10 + Right + ' ' + Bottom + ' l'#13#10'S'#13#10); if ftBottom in Obj.Frame.Typ then Write(OutStream, Left + ' ' + Bottom + ' m'#13#10 + Right + ' ' + Bottom + ' l'#13#10'S'#13#10); if ftLeft in Obj.Frame.Typ then Write(OutStream, Left + ' ' + Top + ' m'#13#10 + Left + ' ' + Bottom + ' l'#13#10'S'#13#10); end; end; end; function HTMLTags(const View: TfrxCustomMemoView): Boolean; begin if View.AllowHTMLTags then Result := FParent.HTMLTags and (Pos('<' ,View.Memo.Text) > 0) else Result := False; end; function TruncReturns(const Str: WideString): WideString; var l: Integer; begin l := Length(Str); if (l > 1) and (Str[l - 1] = #13) and (Str[l] = #10) then Result := Copy(Str, 1, l - 2) else Result := Str; end; function CheckOutPDFChars(const Str: WideString): WideString; var i: Integer; begin Result := ''; for i := 1 to Length(Str) do if Str[i] = '\' then Result := Result + '\\' else if Str[i] = '(' then Result := Result + '\(' else if Str[i] = ')' then Result := Result + '\)' else Result := Result + Str[i]; end; function Str2RTL(const Str: WideString): WideString; var DC: HDC; {$IFDEF Delphi10} GCP: TGCPResultsW; {$ELSE} GCP: TGCPResults; {$ENDIF} buffer: WideString; len: Integer; begin len := Length(Str); SetLength(buffer, Len); DC := GetDc(0); try {$IFDEF Delphi10} GCP.lStructSize := SizeOf(TGCPResultsW); {$ELSE} GCP.lStructSize := SizeOf(TGCPResults); {$ENDIF} GCP.lpOutString := Pointer(buffer); GCP.lpOrder := nil; GCP.lpDx := nil; GCP.lpCaretPos := nil; GCP.lpClass := nil; GCP.lpGlyphs := nil; GCP.nGlyphs := len; GCP.nMaxFit := 0; {$IFNDEF Delphi7} GetCharacterPlacementW(DC, pointer(Str), LongBool(len), LongBool(512), GCP, GCP_REORDER or GCP_DIACRITIC); {$ELSE} {$IFDEF Delphi9} {$IFDEF Delphi10} GetCharacterPlacementW(DC, pointer(Str), len, 512, GCP, DWORD(GCP_REORDER or GCP_DIACRITIC)); {$ELSE} GetCharacterPlacementW(DC, pointer(Str), LongBool(len), LongBool(512), GCP, GCP_REORDER or GCP_DIACRITIC); {$ENDIF} {$ELSE} GetCharacterPlacementW(DC, pointer(Str), len, 512, GCP, GCP_REORDER or GCP_DIACRITIC); {$ENDIF} {$ENDIF} buffer := Copy(buffer, 1, len); finally ReleaseDc(0, DC); end; Result := buffer; end; procedure DrawArrow(Obj: TfrxCustomLineView; x1, y1, x2, y2: Extended); var k1, a, b, c, D: Double; xp, yp, x3, y3, x4, y4, ld, wd: Extended; begin wd := Obj.ArrowWidth * PDF_DIVIDER; ld := Obj.ArrowLength * PDF_DIVIDER; if abs(x2 - x1) > 0 then begin k1 := (y2 - y1) / (x2 - x1); a := Sqr(k1) + 1; b := 2 * (k1 * ((x2 * y1 - x1 * y2) / (x2 - x1) - y2) - x2); c := Sqr(x2) + Sqr(y2) - Sqr(ld) + Sqr((x2 * y1 - x1 * y2) / (x2 - x1)) - 2 * y2 * (x2 * y1 - x1 * y2) / (x2 - x1); D := Sqr(b) - 4 * a * c; xp := (-b + Sqrt(D)) / (2 * a); if (xp > x1) and (xp > x2) or (xp < x1) and (xp < x2) then xp := (-b - Sqrt(D)) / (2 * a); yp := xp * k1 + (x2 * y1 - x1 * y2) / (x2 - x1); if y2 <> y1 then begin x3 := xp + wd * sin(ArcTan(k1)); y3 := yp - wd * cos(ArcTan(k1)); x4 := xp - wd * sin(ArcTan(k1)); y4 := yp + wd * cos(ArcTan(k1)); end else begin x3 := xp; y3 := yp - wd; x4 := xp; y4 := yp + wd; end; end else begin xp := x2; yp := y2 - ld; if (yp > y1) and (yp > y2) or (yp < y1) and (yp < y2) then yp := y2 + ld; x3 := xp - wd; y3 := yp; x4 := xp + wd; y4 := yp; end; WriteLn(OutStream, frFloat2Str(x3) + ' ' + frFloat2Str(y3) + ' m'#13#10 + frFloat2Str(x2) + ' ' + frFloat2Str(y2) + ' l'#13#10 + frFloat2Str(x4) + ' ' + frFloat2Str(y4) + ' l'); if Obj.ArrowSolid then WriteLn(OutStream, '1 j'#13#10 + GetPDFColor(Obj.Frame.Color) + ' rg'#13#10'b') else WriteLn(OutStream, 'S'); end; begin FHeightWoMargin := FHeight - FMarginTop; Left := frFloat2Str(GetLeft(Obj.AbsLeft)); Top := frFloat2Str(GetTop(Obj.AbsTop)); Right := frFloat2Str(GetLeft(Obj.AbsLeft + Obj.Width)); Bottom := frFloat2Str(GetTop(Obj.AbsTop + Obj.Height)); Width := frFloat2Str(Obj.Width * PDF_DIVIDER); Height := frFloat2Str(Obj.Height * PDF_DIVIDER); OldFrameWidth := 0; // Text if (Obj is TfrxCustomMemoView){ and (TfrxCustomMemoView(Obj).Rotation = 0)} and (TfrxCustomMemoView(Obj).BrushStyle in [bsSolid, bsClear]) and (not HTMLTags(TfrxCustomMemoView(Obj))) then begin // save clip to stack Write(OutStream, 'q'#13#10); Write(OutStream, frFloat2Str(GetLeft(Obj.AbsLeft - Obj.Frame.Width)) + ' ' + frFloat2Str(GetTop(Obj.AbsTop + Obj.Height + Obj.Frame.Width)) + ' ' + frFloat2Str((Obj.Width + Obj.Frame.Width * 2) * PDF_DIVIDER) + ' ' + frFloat2Str((Obj.Height + Obj.Frame.Width * 2) * PDF_DIVIDER) + ' re'#13#10'W'#13#10'n'#13#10); ow := Obj.Width - Obj.Frame.ShadowWidth; oh := Obj.Height - Obj.Frame.ShadowWidth; // Shadow if Obj.Frame.DropShadow then begin Width := frFloat2Str(ow * PDF_DIVIDER); Height := frFloat2Str(oh * PDF_DIVIDER); Right := frFloat2Str(GetLeft(Obj.AbsLeft + ow)); Bottom := frFloat2Str(GetTop(Obj.AbsTop + oh)); s := AnsiString(GetPDFColor(Obj.Frame.ShadowColor)); Write(OutStream, s + ' rg'#13#10 + s + ' RG'#13#10 + AnsiString(frFloat2Str(GetLeft(Obj.AbsLeft + ow)) + ' ' + frFloat2Str(GetTop(Obj.AbsTop + oh + Obj.Frame.ShadowWidth)) + ' ' + frFloat2Str(Obj.Frame.ShadowWidth * PDF_DIVIDER) + ' ' + frFloat2Str(oh * PDF_DIVIDER) + ' re'#13#10'B'#13#10 + frFloat2Str(GetLeft(Obj.AbsLeft + Obj.Frame.ShadowWidth)) + ' ' + frFloat2Str(GetTop(Obj.AbsTop + oh + Obj.Frame.ShadowWidth)) + ' ' + frFloat2Str(ow * PDF_DIVIDER) + ' ' + frFloat2Str(Obj.Frame.ShadowWidth * PDF_DIVIDER) + ' re'#13#10'B'#13#10)); end; if TfrxCustomMemoView(Obj).Highlight.Active and Assigned(TfrxCustomMemoView(Obj).Highlight.Font) then begin Obj.Font.Assign(TfrxCustomMemoView(Obj).Highlight.Font); Obj.Color := TfrxCustomMemoView(Obj).Highlight.Color; end; if Obj.Color <> clNone then Write(OutStream, GetPDFColor(Obj.Color) + ' rg'#13#10 + Left + ' ' + Bottom + ' ' + Width + ' ' + Height + ' re'#13#10'f'#13#10); // Frames MakeUpFrames; {$IFDEF Delphi10} Lines := TfrxWideStrings.Create; {$ELSE} Lines := TWideStrings.Create; {$ENDIF} Lines.Text := TfrxCustomMemoView(Obj).WrapText(True); if Lines.Count > 0 then begin FontIndex := Parent.AddFont(Obj.Font); Write(OutStream, '/F' + IntToStr(TfrxPDFFont(Parent.FFonts[FontIndex]).Index - 1) + ' ' + IntToStr(Obj.Font.Size) + ' Tf'#13#10); if Obj.Font.Color <> clNone then TempColor := Obj.Font.Color else TempColor := clBlack; Write(OutStream, GetPDFColor(TempColor) + ' rg'#13#10); FCharSpacing := TfrxCustomMemoView(Obj).CharSpacing * PDF_DIVIDER; if TfrxCustomMemoView(Obj).CharSpacing <> 0 then Write(OutStream, frFloat2Str(FCharSpacing) + ' Tc'#13#10); pdfCS.Enter; try frxDrawText.SetFont(TfrxCustomMemoView(Obj).Font); frxDrawText.SetGaps(0, 0, TfrxCustomMemoView(Obj).LineSpacing); FLineHeight := frxDrawText.LineHeight; FTextHeight := frxDrawText.TextHeight; // Underlines by FuxMedia if TfrxCustomMemoView(Obj).Underlines then begin iz := Trunc(Obj.Height / FLineHeight); for i:= 0 to iz do begin y := GetTop(GetVTextPos(Obj.AbsTop + TfrxCustomMemoView(Obj).GapY + 1, Obj.Height - TfrxCustomMemoView(Obj).GapY * 2, 'XYZ', TfrxCustomMemoView(Obj).VAlign, i, iz)); Write(OutStream, GetPDFColor(Obj.Frame.Color) + ' RG'#13#10 + frFloat2Str(Obj.Frame.Width * PDF_DIVIDER) + ' w'#13#10 + Left + ' ' + frFloat2Str(y) + ' m'#13#10 + Right + ' ' + frFloat2Str(y) + ' l'#13#10'S'#13#10); end; end; // output lines of memo FUnderlineSize := Obj.Font.Size * 0.12; for i := 0 to Lines.Count - 1 do begin if i = 0 then PGap := TfrxCustomMemoView(Obj).ParagraphGap else PGap := 0; if TfrxCustomMemoView(Obj).RTLReading then su := Str2RTL(TruncReturns(Lines[i])) else su := TruncReturns(Lines[i]); if Length(Trim(su)) > 0 then begin // Text output if TfrxCustomMemoView(Obj).HAlign <> haRight then FCharSpacing := 0; s := UnicodeToANSI(su, CodepageByCharset(TfrxCustomMemoView(Obj).Font.Charset)); if TfrxCustomMemoView(Obj).Font.Charset = OEM_CHARSET then s := OemToStr(s); x := FCharSpacing + GetLeft(GetHTextPos(Obj.AbsLeft + TfrxCustomMemoView(Obj).GapX + Obj.Font.Size * 0.01 + TfrxCustomMemoView(Obj).GapX / 2 + PGap, ow - TfrxCustomMemoView(Obj).GapX * 2 - PGap, TfrxCustomMemoView(Obj).CharSpacing, String(s), TfrxCustomMemoView(Obj).HAlign)); y := GetTop(GetVTextPos(Obj.AbsTop + TfrxCustomMemoView(Obj).GapY - (Obj.Font.Size * 0.05) + TfrxCustomMemoView(Obj).GapY / 4, oh - TfrxCustomMemoView(Obj).GapY * 2, Lines[i], TfrxCustomMemoView(Obj).VAlign, i, Lines.Count)); Write(OutStream, 'BT'#13#10); if TfrxCustomMemoView(Obj).Rotation > 0 then begin alpha := TfrxCustomMemoView(Obj).Rotation * Pi / 180; cosa := Cos(alpha); sina := Sin(alpha); rx := x - cosa * FTextWidth * PDF_DIVIDER / 2 + FTextWidth * PDF_DIVIDER / 2; ry := y - sina * FTextWidth * PDF_DIVIDER / 2; Write(OutStream, frFloat2Str(cosa) + ' ' + frFloat2Str(sina) + ' ' + frFloat2Str(-sina) + ' ' + frFloat2Str(cosa) + ' ' + frFloat2Str(rx) + ' ' + frFloat2Str(ry) + ' Tm'#13#10); end else Write(OutStream, frFloat2Str(x) + ' ' + frFloat2Str(y) + ' Td'#13#10); Write(OutStream, '<' + StrToHex(s) + '> Tj'#13#10'ET'#13#10); // set Underline if (fsUnderline in (TfrxCustomMemoView(Obj).Font.Style)) and (TfrxCustomMemoView(Obj).Rotation = 0) then Write(OutStream, GetPDFColor(Obj.Font.Color) + ' RG'#13#10 + frFloat2Str(Obj.Font.Size * 0.08) + ' w'#13#10 + frFloat2Str(x) + ' ' + frFloat2Str(y - FUnderlineSize) + ' m'#13#10 + frFloat2Str(x + (frxDrawText.Canvas.TextWidth(Lines[i]) / FDivider + Length(Lines[i]) * TfrxCustomMemoView(Obj).CharSpacing) * PDF_DIVIDER) + ' ' + frFloat2Str(y - FUnderlineSize) + ' l'#13#10'S'#13#10); end; end; finally pdfCS.Leave; end; end; // restore clip Write(OutStream, 'Q'#13#10); Lines.Free; end // Lines else if Obj is TfrxCustomLineView then begin Write(OutStream, GetPDFColor(Obj.Frame.Color) + ' RG'#13#10 + frFloat2Str(Obj.Frame.Width * PDF_DIVIDER) + ' w'#13#10 + Left + ' ' + Top + ' m'#13#10 + Right + ' ' + Bottom + ' l'#13#10'S'#13#10); if TfrxCustomLineView(Obj).ArrowStart then DrawArrow(TfrxCustomLineView(Obj), GetLeft(Obj.AbsLeft + Obj.Width), GetTop(Obj.AbsTop + Obj.Height), GetLeft(Obj.AbsLeft), GetTop(Obj.AbsTop)); if TfrxCustomLineView(Obj).ArrowEnd then DrawArrow(TfrxCustomLineView(Obj), GetLeft(Obj.AbsLeft), GetTop(Obj.AbsTop), GetLeft(Obj.AbsLeft + Obj.Width), GetTop(Obj.AbsTop + Obj.Height)); end // Rects else if (Obj is TfrxShapeView) and (TfrxShapeView(Obj).Shape = skRectangle) then begin Write(OutStream, GetPDFColor(Obj.Frame.Color) + ' RG'#13#10 + frFloat2Str(Obj.Frame.Width * PDF_DIVIDER) + ' w'#13#10 + GetPDFColor(Obj.Color) + ' rg'#13#10 + Left + ' ' + Bottom + ' '#13#10 + Width + ' ' + Height + ' re'#13#10); if Obj.Color <> clNone then Write(OutStream, 'B'#13#10) else Write(OutStream, 'S'#13#10); end // Shape line 1 else if (Obj is TfrxShapeView) and (TfrxShapeView(Obj).Shape = skDiagonal1) then Write(OutStream, GetPDFColor(Obj.Frame.Color) + ' RG'#13#10 + frFloat2Str(Obj.Frame.Width * PDF_DIVIDER) + ' w'#13#10 + Left + ' ' + Bottom + ' m'#13#10 + Right + ' ' + Top + ' l'#13#10'S'#13#10) // Shape line 2 else if (Obj is TfrxShapeView) and (TfrxShapeView(Obj).Shape = skDiagonal2) then Write(OutStream, GetPDFColor(Obj.Frame.Color) + ' RG'#13#10 + frFloat2Str(Obj.Frame.Width * PDF_DIVIDER) + ' w'#13#10 + Left + ' ' + Top + ' m'#13#10 + Right + ' ' + Bottom + ' l'#13#10'S'#13#10) else // Bitmaps if not ((Obj.Name = '_pagebackground') and (not Parent.Background)) and (Obj.Height > 0) and (Obj.Width > 0) then begin if Obj.Frame.Width > 0 then begin OldFrameWidth := Obj.Frame.Width; Obj.Frame.Width := 0; end; FRealBounds := Obj.GetRealBounds; dx := FRealBounds.Right - FRealBounds.Left; dy := FRealBounds.Bottom - FRealBounds.Top; if (dx = Obj.Width) or (Obj.AbsLeft = FRealBounds.Left) then fdx := 0 else if (Obj.AbsLeft + Obj.Width) = FRealBounds.Right then fdx := (dx - Obj.Width) else fdx := (dx - Obj.Width) / 2; if (dy = Obj.Height) or (Obj.AbsTop = FRealBounds.Top) then fdy := 0 else if (Obj.AbsTop + Obj.Height) = FRealBounds.Bottom then fdy := (dy - Obj.Height) else fdy := (dy - Obj.Height) / 2; TempBitmap := TBitmap.Create; TempBitmap.PixelFormat := pf24bit; if (Parent.PrintOptimized or (Obj is TfrxCustomMemoView)) and (Obj.BrushStyle in [bsSolid, bsClear]) then i := PDF_PRINTOPT else i := 1; iz := 0; if (Obj.ClassName = 'TfrxBarCodeView') and not Parent.PrintOptimized then begin i := 2; iz := i; end; TempBitmap.Width := Round(dx * i) + i; TempBitmap.Height := Round(dy * i) + i; Obj.Draw(TempBitmap.Canvas, i, i, -Round((Obj.AbsLeft - fdx) * i) + iz, -Round((Obj.AbsTop - fdy)* i)); if dx <> 0 then BWidth := frFloat2Str(dx * PDF_DIVIDER) else BWidth := '1'; if dy <> 0 then BHeight := frFloat2Str(dy * PDF_DIVIDER) else BHeight := '1'; Write(OutStream, 'q'#13#10 + BWidth + ' 0 0 ' + BHeight + ' ' + frFloat2Str(GetLeft(Obj.AbsLeft - fdx)) + ' ' + frFloat2Str(GetTop(Obj.AbsTop - fdy + dy)) + ' cm'#13#10'BI'#13#10 + '/W ' + IntToStr(TempBitmap.Width) + #13#10 + '/H ' + IntToStr(TempBitmap.Height) + #13#10'/CS /RGB'#13#10'/BPC 8'#13#10'/I true'#13#10'/F [/DCT]'#13#10'ID'#13#10); Jpg := TJPEGImage.Create; if (Obj.ClassName = 'TfrxBarCodeView') or (Obj is TfrxCustomLineView) or (Obj is TfrxShapeView) then begin Jpg.PixelFormat := jf8Bit; Jpg.CompressionQuality := 85; end else begin Jpg.PixelFormat := jf24Bit; Jpg.CompressionQuality := 80; end; Jpg.Assign(TempBitmap); Jpg.SaveToStream(OutStream); Jpg.Free; Write(OutStream, #13#10'EI'#13#10'Q'#13#10); TempBitmap.Free; if OldFrameWidth > 0 then Obj.Frame.Width := OldFrameWidth; MakeUpFrames; end; end; destructor TfrxPDFPage.Destroy; begin FBMP.Free; inherited; end; { TfrxPDFFont } constructor TfrxPDFFont.Create; begin inherited; FFont := TFont.Create; end; destructor TfrxPDFFont.Destroy; begin FFont.Free; inherited; end; procedure TfrxPDFFont.SaveToStream(const Stream: TStream); var s: AnsiString; b: TBitmap; pm: ^OUTLINETEXTMETRICA; FontName: String; i: Cardinal; id: Integer; pfont: PAnsiChar; FirstChar, LastChar : Integer; MemStream: TMemoryStream; MemStream1: TMemoryStream; pwidths: PABC; Charset: TFontCharSet; // support DBCS font name encoding function PrepareFontName(const Font: TFont): String; begin Result := StringReplace(Font.Name, ' ', '#20', [rfReplaceAll]); Result := StringReplace(Result, '(', '#28', [rfReplaceAll]); Result := StringReplace(Result, ')', '#29', [rfReplaceAll]); s := ''; if fsBold in Font.Style then s := s + 'Bold'; if fsItalic in Font.Style then s := s + 'Italic'; if s <> '' then Result := Result + ',' + String(s); Result := String(HexEncode7F(Result)); end; begin inherited SaveToStream(Stream); b := TBitmap.Create; try b.Canvas.Lock; b.Canvas.Font.Assign(Font); b.Canvas.Font.PixelsPerInch := 96; b.Canvas.Font.Size := 750; i := GetOutlineTextMetrics(b.Canvas.Handle, 0, nil); if i = 0 then begin b.Canvas.Font.Name := 'Arial'; i := GetOutlineTextMetrics(b.Canvas.Handle, 0, nil); end; if i <> 0 then begin pm := GlobalAllocPtr(GMEM_MOVEABLE or GMEM_SHARE, i); try if pm <> nil then i := GetOutlineTextMetricsA(b.Canvas.Handle, i, pm) else i := 0; if i <> 0 then begin FirstChar := Ord(pm.otmTextMetrics.tmFirstChar); LastChar := Ord(pm.otmTextMetrics.tmLastChar); FontName := PrepareFontName(b.Canvas.Font); Charset := pm.otmTextMetrics.tmCharSet; if Font.Charset = OEM_CHARSET then Charset := GetDefFontCharSet; FFontDCnt := Parent.FFontDCnt; Inc(Parent.FObjNo); Parent.XRefAdd(Stream, Parent.FObjNo); WriteLn(Stream, IntToStr(Parent.FFontDCnt + Parent.FStartFonts) + ' 0 obj'); Parent.FFontDCnt := Parent.FFontDCnt + 1; WriteLn(Stream, '<<'); WriteLn(Stream, '/Type /Font'); WriteLn(Stream, '/Name /F' + IntToStr(Index - 1)); WriteLn(Stream, '/BaseFont /' + FontName); if not (Charset in [CHINESEBIG5_CHARSET, GB2312_CHARSET,SHIFTJIS_CHARSET,HANGEUL_CHARSET]) then WriteLn(Stream, '/Subtype /TrueType') else WriteLn(Stream, '/Subtype /Type0'); case Charset of SYMBOL_CHARSET: WriteLn(Stream, '/Encoding /MacRomanEncoding'); ANSI_CHARSET: WriteLn(Stream, '/Encoding /WinAnsiEncoding'); RUSSIAN_CHARSET: {1251} begin WriteLn(Stream, '/Encoding <>'); end; EASTEUROPE_CHARSET: {1250} begin WriteLn(Stream, '/Encoding <>'); end; VIETNAMESE_CHARSET: {1258} begin WriteLn(Stream, '/Encoding <>'); end; THAI_CHARSET: {874} begin WriteLn(Stream, '/Encoding <>'); end; GREEK_CHARSET: {1253} begin WriteLn(Stream, '/Encoding <>'); end; TURKISH_CHARSET: {1254} begin WriteLn(Stream, '/Encoding <>'); end; HEBREW_CHARSET: {1255} begin WriteLn(Stream, '/Encoding <>'); end; ARABIC_CHARSET: begin WriteLn(Stream, '/Encoding <>'); end; BALTIC_CHARSET: begin WriteLn(Stream, '/Encoding <>'); end; CHINESEBIG5_CHARSET: {136} begin WriteLn(Stream, '/DescendantFonts [' + IntToStr(Parent.FFontDCnt + Parent.FStartFonts) + ' 0 R]'); WriteLn(Stream, '/Encoding /ETenms-B5-H'); WriteLn(Stream, '>>'); WriteLn(Stream, 'endobj'); Inc(Parent.FObjNo); Parent.XRefAdd(Stream, Parent.FObjNo); WriteLn(Stream, IntToStr(Parent.FFontDCnt + Parent.FStartFonts) + ' 0 obj'); Parent.FFontDCnt := Parent.FFontDCnt + 1; WriteLn(Stream, '<<'); WriteLn(Stream, '/Type /Font'); WriteLn(Stream, '/Subtype'); WriteLn(Stream, '/CIDFontType2'); WriteLn(Stream, '/BaseFont /'+ HexEncode7F(FontName)); WriteLn(Stream, '/WinCharSet 136'); WriteLn(Stream, '/FontDescriptor ' + IntToStr(Parent.FFontDCnt + Parent.FStartFonts) + ' 0 R'); WriteLn(Stream, '/CIDSystemInfo'); WriteLn(Stream, '<<'); WriteLn(Stream, '/Registry(Adobe)'); WriteLn(Stream, '/Ordering(CNS1)'); WriteLn(Stream, '/Supplement 0'); WriteLn(Stream, '>>'); WriteLn(Stream, '/DW 1000'); WriteLn(Stream, '/W [1 95 500]'); WriteLn(Stream, '>>'); WriteLn(Stream, 'endobj'); Inc(Parent.FObjNo); Parent.XRefAdd(Stream, Parent.FObjNo); WriteLn(Stream, IntToStr(Parent.FFontDCnt + Parent.FStartFonts) + ' 0 obj'); Parent.FFontDCnt := Parent.FFontDCnt + 1; WriteLn(Stream, '<<'); WriteLn(Stream, '/Type /FontDescriptor'); if Parent.FEmbedded then WriteLn(Stream, '/FontFile2 ' + IntToStr(Parent.FFontDCnt + Parent.FStartFonts) + ' 0 R'); WriteLn(Stream, '/FontName /' + HexEncode7F(FontName)); WriteLn(Stream, '/Flags 7'); WriteLn(Stream, '/FontBBox [' + IntToStr(pm^.otmrcFontBox.Left) + ' '+ IntToStr(pm^.otmrcFontBox.Bottom) + ' '+ IntToStr(pm^.otmrcFontBox.Right) + ' '+ IntToStr(pm^.otmrcFontBox.Top) + ' ]'); WriteLn(Stream, '/Style << /Panose <010502020300000000000000> >>'); WriteLn(Stream, '/Ascent ' + IntToStr(pm^.otmAscent)); WriteLn(Stream, '/Descent ' + IntToStr(pm^.otmDescent)); WriteLn(Stream, '/CapHeight ' + IntToStr(pm^.otmTextMetrics.tmHeight)); WriteLn(Stream, '/StemV ' + IntToStr(50 + Round(sqr(pm^.otmTextMetrics.tmWeight / 65)))); WriteLn(Stream, '/ItalicAngle ' + IntToStr(pm^.otmItalicAngle)); WriteLn(Stream, '>>'); WriteLn(Stream, 'endobj'); end; GB2312_CHARSET: {134} begin WriteLn(Stream, '/DescendantFonts [' + IntToStr(Parent.FFontDCnt + Parent.FStartFonts) + ' 0 R]'); WriteLn(Stream, '/Encoding /GB-EUC-H'); WriteLn(Stream, '>>'); WriteLn(Stream, 'endobj'); Inc(Parent.FObjNo); Parent.XRefAdd(Stream, Parent.FObjNo); WriteLn(Stream, IntToStr(Parent.FFontDCnt + Parent.FStartFonts) + ' 0 obj'); Parent.FFontDCnt := Parent.FFontDCnt + 1; WriteLn(Stream, '<<'); WriteLn(Stream, '/Type /Font'); WriteLn(Stream, '/Subtype'); WriteLn(Stream, '/CIDFontType2'); WriteLn(Stream, '/BaseFont /'+ HexEncode7F(FontName)); WriteLn(Stream, '/WinCharSet 134'); WriteLn(Stream, '/FontDescriptor ' + IntToStr(Parent.FFontDCnt + Parent.FStartFonts) + ' 0 R'); WriteLn(Stream, '/CIDSystemInfo'); WriteLn(Stream, '<<'); WriteLn(Stream, '/Registry(Adobe)'); WriteLn(Stream, '/Ordering(GB1)'); WriteLn(Stream, '/Supplement 2'); WriteLn(Stream, '>>'); WriteLn(Stream, '/DW 1000'); WriteLn(Stream, '/W [ 1 95 500 814 939 500 7712 [ 500 ] 7716 [ 500 ] ]'); WriteLn(Stream, '>>'); WriteLn(Stream, 'endobj'); Inc(Parent.FObjNo); Parent.XRefAdd(Stream, Parent.FObjNo); WriteLn(Stream, IntToStr(Parent.FFontDCnt + Parent.FStartFonts) + ' 0 obj'); Parent.FFontDCnt := Parent.FFontDCnt + 1; WriteLn(Stream, '<<'); WriteLn(Stream, '/Type /FontDescriptor'); if Parent.FEmbedded then WriteLn(Stream, '/FontFile2 ' + IntToStr(Parent.FFontDCnt + Parent.FStartFonts) + ' 0 R'); WriteLn(Stream, '/FontName /' + HexEncode7F(FontName)); WriteLn(Stream, '/Flags 6'); WriteLn(Stream, '/FontBBox [' + IntToStr(pm^.otmrcFontBox.Left) + ' '+ IntToStr(pm^.otmrcFontBox.Bottom) + ' '+ IntToStr(pm^.otmrcFontBox.Right) + ' '+ IntToStr(pm^.otmrcFontBox.Top) + ' ]'); WriteLn(Stream, '/Style << /Panose <010502020400000000000000> >>'); WriteLn(Stream, '/Ascent ' + IntToStr(pm^.otmAscent)); WriteLn(Stream, '/Descent ' + IntToStr(pm^.otmDescent)); WriteLn(Stream, '/CapHeight ' + IntToStr(pm^.otmTextMetrics.tmHeight)); WriteLn(Stream, '/StemV ' + IntToStr(50 + Round(sqr(pm^.otmTextMetrics.tmWeight / 65)))); WriteLn(Stream, '/ItalicAngle ' + IntToStr(pm^.otmItalicAngle)); WriteLn(Stream, '>>'); WriteLn(Stream, 'endobj'); end; SHIFTJIS_CHARSET: {80} begin WriteLn(Stream, '/DescendantFonts [' + IntToStr(Parent.FFontDCnt + Parent.FStartFonts) + ' 0 R]'); WriteLn(Stream, '/Encoding /90msp-RKSJ-H'); WriteLn(Stream, '>>'); WriteLn(Stream, 'endobj'); Inc(Parent.FObjNo); Parent.XRefAdd(Stream, Parent.FObjNo); WriteLn(Stream, IntToStr(Parent.FFontDCnt + Parent.FStartFonts) + ' 0 obj'); Parent.FFontDCnt := Parent.FFontDCnt + 1; WriteLn(Stream, '<<'); WriteLn(Stream, '/Type /Font'); WriteLn(Stream, '/Subtype'); WriteLn(Stream, '/CIDFontType2'); WriteLn(Stream, '/BaseFont /'+ HexEncode7F(FontName)); WriteLn(Stream, '/WinCharSet 80'); Write(Stream, '/FontDescriptor ' + IntToStr(Parent.FFontDCnt + Parent.FStartFonts) + ' 0 R'); WriteLn(Stream, '/CIDSystemInfo'); WriteLn(Stream, '<<'); WriteLn(Stream, '/Registry(Adobe)'); WriteLn(Stream, '/Ordering(Japan1)'); WriteLn(Stream, '/Supplement 2'); WriteLn(Stream, '>>'); WriteLn(Stream, '/DW 1000'); WriteLn(Stream, '/W [ 1 95 500 231 632 500 ]'); WriteLn(Stream, '>>'); WriteLn(Stream, 'endobj'); Inc(Parent.FObjNo); Parent.XRefAdd(Stream, Parent.FObjNo); WriteLn(Stream, IntToStr(Parent.FFontDCnt + Parent.FStartFonts) + ' 0 obj'); Parent.FFontDCnt := Parent.FFontDCnt + 1; WriteLn(Stream, '<<'); WriteLn(Stream, '/Type /FontDescriptor'); if Parent.FEmbedded then WriteLn(Stream, '/FontFile2 ' + IntToStr(Parent.FFontDCnt + Parent.FStartFonts) + ' 0 R'); WriteLn(Stream, '/FontName /' + HexEncode7F(FontName)); WriteLn(Stream, '/Flags 6'); WriteLn(Stream, '/FontBBox [' + IntToStr(pm^.otmrcFontBox.Left) + ' '+ IntToStr(pm^.otmrcFontBox.Bottom) + ' '+ IntToStr(pm^.otmrcFontBox.Right) + ' '+ IntToStr(pm^.otmrcFontBox.Top) + ' ]'); WriteLn(Stream, '/Style << /Panose <010502020400000000000000> >>'); WriteLn(Stream, '/Ascent ' + IntToStr(pm^.otmAscent)); WriteLn(Stream, '/Descent ' + IntToStr(pm^.otmDescent)); WriteLn(Stream, '/CapHeight ' + IntToStr(pm^.otmTextMetrics.tmHeight)); WriteLn(Stream, '/StemV ' + IntToStr(50 + Round(sqr(pm^.otmTextMetrics.tmWeight / 65)))); WriteLn(Stream, '/ItalicAngle ' + IntToStr(pm^.otmItalicAngle)); WriteLn(Stream, '>>'); WriteLn(Stream, 'endobj'); end; HANGEUL_CHARSET: {129} begin WriteLn(Stream, '/DescendantFonts [' + IntToStr(Index + 1 + Parent.FStartFonts) + ' 0 R]'); WriteLn(Stream, '/Encoding /KSCms-UHC-H'); WriteLn(Stream, '>>'); WriteLn(Stream, 'endobj'); Inc(Parent.FObjNo); Parent.XRefAdd(Stream, Parent.FObjNo); WriteLn(Stream, IntToStr(Parent.FFontDCnt + Parent.FStartFonts) + ' 0 obj'); Parent.FFontDCnt := Parent.FFontDCnt + 1; WriteLn(Stream, '<<'); WriteLn(Stream, '/Type /Font'); WriteLn(Stream, '/Subtype'); WriteLn(Stream, '/CIDFontType2'); WriteLn(Stream, '/BaseFont /'+ HexEncode7F(FontName)); WriteLn(Stream, '/WinCharSet 129'); Write(Stream, '/FontDescriptor '+ IntToStr(Parent.FFontDCnt + Parent.FStartFonts) + ' 0 R'); WriteLn(Stream, '/CIDSystemInfo'); WriteLn(Stream, '<<'); WriteLn(Stream, '/Registry(Adobe)'); WriteLn(Stream, '/Ordering(Korea1)'); WriteLn(Stream, '/Supplement 1'); WriteLn(Stream, '>>'); WriteLn(Stream, '/DW 1000'); WriteLn(Stream, '/W [ 1 95 500 8094 8190 500 ]'); WriteLn(Stream, '>>'); WriteLn(Stream, 'endobj'); Inc(Parent.FObjNo); Parent.XRefAdd(Stream, Parent.FObjNo); WriteLn(Stream, IntToStr(Parent.FFontDCnt + Parent.FStartFonts) + ' 0 obj'); Parent.FFontDCnt := Parent.FFontDCnt + 1; WriteLn(Stream, '<<'); WriteLn(Stream, '/Type /FontDescriptor '); if Parent.FEmbedded then WriteLn(Stream, '/FontFile2 ' + IntToStr(Parent.FFontDCnt + Parent.FStartFonts) + ' 0 R'); WriteLn(Stream, '/FontName /' + HexEncode7F(FontName)); WriteLn(Stream, '/Flags 6'); WriteLn(Stream, '/FontBBox [' + IntToStr(pm^.otmrcFontBox.Left) + ' '+ IntToStr(pm^.otmrcFontBox.Bottom) + ' '+ IntToStr(pm^.otmrcFontBox.Right) + ' '+ IntToStr(pm^.otmrcFontBox.Top) + ' ]'); WriteLn(Stream, '/Style << /Panose <010502020400000000000000> >>'); WriteLn(Stream, '/Ascent ' + IntToStr(pm^.otmAscent)); WriteLn(Stream, '/Descent ' + IntToStr(pm^.otmDescent)); WriteLn(Stream, '/CapHeight ' + IntToStr(pm^.otmTextMetrics.tmHeight)); WriteLn(Stream, '/StemV ' + IntToStr(50 + Round(sqr(pm^.otmTextMetrics.tmWeight / 65)))); WriteLn(Stream, '/ItalicAngle ' + IntToStr(pm^.otmItalicAngle)); WriteLn(Stream, '>>'); WriteLn(Stream, 'endobj'); end; end; if not (Charset in [CHINESEBIG5_CHARSET, GB2312_CHARSET, SHIFTJIS_CHARSET, HANGEUL_CHARSET]) then begin WriteLn(Stream, '/FontDescriptor ' + IntToStr(Parent.FFontDCnt + Parent.FStartFonts) + ' 0 R'); WriteLn(Stream, '/FirstChar ' + IntToStr(FirstChar)); WriteLn(Stream, '/LastChar ' + IntToStr(LastChar)); pwidths := GlobalAllocPtr(GMEM_MOVEABLE or GMEM_SHARE, SizeOf(ABCArray)); try Write(Stream, '/Widths ['); GetCharABCWidthsA(b.Canvas.Handle, FirstChar, LastChar, pwidths^); for i := 0 to (LastChar - FirstChar) do Write(Stream, IntToStr(pwidths^[i].abcA + Integer(pwidths^[i].abcB) + pwidths^[i].abcC) + ' '); WriteLn(Stream, ']'); finally GlobalFreePtr(pwidths); end; WriteLn(Stream, '>>'); WriteLn(Stream, 'endobj'); Inc(Parent.FObjNo); Parent.XRefAdd(Stream, Parent.FObjNo); WriteLn(Stream, IntToStr(Parent.FFontDCnt + Parent.FStartFonts) + ' 0 obj'); Parent.FFontDCnt := Parent.FFontDCnt + 1; WriteLn(Stream, '<<'); WriteLn(Stream, '/Type /FontDescriptor'); WriteLn(Stream, '/FontName /' + FontName); WriteLn(Stream, '/Flags 32'); WriteLn(Stream, '/FontBBox [' + IntToStr(pm^.otmrcFontBox.Left) + ' '+ IntToStr(pm^.otmrcFontBox.Bottom) + ' '+ IntToStr(pm^.otmrcFontBox.Right) + ' '+ IntToStr(pm^.otmrcFontBox.Top) + ' ]'); WriteLn(Stream, '/ItalicAngle ' + IntToStr(pm^.otmItalicAngle)); WriteLn(Stream, '/Ascent ' + IntToStr(pm^.otmAscent)); WriteLn(Stream, '/Descent ' + IntToStr(pm^.otmDescent)); WriteLn(Stream, '/Leading ' + IntToStr(pm^.otmTextMetrics.tmInternalLeading)); WriteLn(Stream, '/CapHeight ' + IntToStr(pm^.otmTextMetrics.tmHeight)); WriteLn(Stream, '/StemV ' + IntToStr(50 + Round(sqr(pm^.otmTextMetrics.tmWeight / 65)))); WriteLn(Stream, '/AvgWidth ' + IntToStr(pm^.otmTextMetrics.tmAveCharWidth)); WriteLn(Stream, '/MaxWidth ' + IntToStr(pm^.otmTextMetrics.tmMaxCharWidth)); WriteLn(Stream, '/MissingWidth ' + IntToStr(pm^.otmTextMetrics.tmAveCharWidth)); if Parent.FEmbedded then WriteLn(Stream, '/FontFile2 ' + IntToStr(Parent.FFontDCnt + Parent.FStartFonts) + ' 0 R'); WriteLn(Stream, '>>'); WriteLn(Stream, 'endobj'); end; if Parent.FEmbedded then begin Inc(Parent.FObjNo); Parent.XRefAdd(Stream, Parent.FObjNo); id := Parent.FFontDCnt + Parent.FStartFonts; WriteLn(Stream, IntToStr(id) + ' 0 obj'); Parent.FFontDCnt := Parent.FFontDCnt + 1; i := GetFontData(b.Canvas.Handle, 0, 0, nil, 1); pfont := GlobalAllocPtr(GMEM_MOVEABLE or GMEM_SHARE, i); try i := GetFontData(b.Canvas.Handle, 0, 0, pfont, i); MemStream := TMemoryStream.Create; try MemStream.Write(pfont^, i); MemStream1 := TMemoryStream.Create; try frxDeflateStream(MemStream, MemStream1, gzMax); WriteLn(Stream, '<< /Length ' + IntToStr(MemStream1.Size) + ' /Filter /FlateDecode /Length1 ' + IntToStr(MemStream.Size) + ' >>'); WriteLn(Stream, 'stream'); if Parent.Protection then CryptStream(MemStream1, Stream, Parent.FEncKey, id) else Stream.CopyFrom(MemStream1, 0); finally MemStream1.Free; end; finally MemStream.Free; end; finally GlobalFreePtr(pfont); end; WriteLn(Stream, ''); WriteLn(Stream, 'endstream'); WriteLn(Stream, 'endobj'); end; end; finally GlobalFreePtr(pm); end; end else Exception.Create('Error on get font info'); finally b.Canvas.Unlock; b.Free; end; end; { TfrxPDFElement } constructor TfrxPDFElement.Create; begin FIndex := 0; FXrefPosition := 0; FCR := False; end; procedure TfrxPDFElement.Write(Stream: TStream; const S: AnsiString); begin Stream.Write(S[1], Length(S)); end; procedure TfrxPDFElement.WriteLn(Stream: TStream; const S: AnsiString); begin Stream.Write(S[1], Length(S)); {$IFDEF Delphi12} Stream.Write(AnsiChar(#13)+AnsiChar(#10), 2); {$ELSE} Stream.Write(#13#10, 2); {$ENDIF} end; {$IFDEF Delphi12} procedure TfrxPDFElement.WriteLn(Stream: TStream; const S: String); begin WriteLn(Stream, AnsiString(s)); end; procedure TfrxPDFElement.Write(Stream: TStream; const S: String); begin Write(Stream, AnsiString(S)); end; {$ENDIF} procedure TfrxPDFElement.SaveToStream(const Stream: TStream); begin FXrefPosition := Stream.Position; end; { TfrxPDFOutlineNode } constructor TfrxPDFOutlineNode.Create; begin Title := ''; Dest := -1; Number := 0; Count := 0; CountTree :=0; Parent := nil; First := nil; Prev := nil; Next := nil; Last := nil; end; destructor TfrxPDFOutlineNode.Destroy; begin if Next <> nil then Next.Free; if First <> nil then First.Free; inherited; end; initialization pdfCS := TCriticalSection.Create; finalization pdfCS.Free; end.