
git-svn-id: https://svn.code.sf.net/p/lazarus-ccr/svn@585 8e941d3f-bd1b-0410-a28a-d453659cc2b4
2798 lines
73 KiB
ObjectPascal
2798 lines
73 KiB
ObjectPascal
{*
|
|
* << P o w e r P d f >> -- PdfDoc.pas
|
|
*
|
|
* Copyright (c) 1999-2001 Takezou. <takeshi_kanno@est.hi-ho.ne.jp>
|
|
*
|
|
* This library is free software; you can redistribute it and/or modify it
|
|
* under the terms of the GNU Library General Public License as published
|
|
* by the Free Software Foundation; either version 2 of the License, or any
|
|
* later version.
|
|
*
|
|
* This library is distributed in the hope that it will be useful, but WITHOUT
|
|
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
|
|
* FOR A PARTICULAR PURPOSE. See the GNU Library general Public License for more
|
|
* details.
|
|
*
|
|
* You should have received a copy of the GNU Library General Public License
|
|
* along with this library.
|
|
*
|
|
* 2000.09.10 create.
|
|
* 2001.06.30 move FloatToStrR method to PdfTypes.pas.
|
|
* 2001.07.01 implemented text annotation.
|
|
* 2001.07.10 move TPDF_STR_TBL and TPDF_INT_TBL defination to top (for BCB).
|
|
* 2001.07.21 changed TPdfDictionaryWrapper's properties(Data and HasData) to
|
|
* public.
|
|
* 2001.07.28 fixed bug of TPdfCanvas.SetPage.
|
|
* 2001.08.01 added TPdfCatalog.PageLayout
|
|
* 2001.08.09 moved some constans from PdfTypes.pas.
|
|
* 2001.08.12 changed the implementation of outlines.
|
|
* 2001.08.12 changed the implementation of annotation.
|
|
* 2001.08.18 added GetNextWord routine.
|
|
* 2001.08.18 changed the parameter of MoveToTextPoint routine.
|
|
* 2001.08.20 added Text utility routines.
|
|
* 2001.08.20 added Leading property to TPdfCanvasAttribute.
|
|
* 2001.08.22 change the method name MesureText to MeasureText(Spelling mistake :-)
|
|
* 2001.08.26 changed some definations and methods to work with kylix.
|
|
* 2001.09.01 changed the implementation of the image.
|
|
* 2001.09.08 added OpenAction function.
|
|
* change AddAnnotation method to CreateAnnotation.
|
|
* 2001.09.13 added ViewerPreference functions.
|
|
*}
|
|
{$IFDEF LAZ_POWERPDF}
|
|
{$H+}
|
|
{$ENDIF}
|
|
unit PdfDoc;
|
|
|
|
interface
|
|
|
|
// if use "FlateDecode" compression, comment out the next line.
|
|
// (this unit and PdfTypes.pas)
|
|
{$DEFINE NOZLIB}
|
|
|
|
uses
|
|
SysUtils, Classes, PdfTypes
|
|
{$IFDEF LINUX}
|
|
, Types
|
|
{$ELSE}
|
|
, Windows
|
|
{$ENDIF}
|
|
;
|
|
|
|
const
|
|
POWER_PDF_VERSION_TEXT = 'PowerPdf version 0.9';
|
|
|
|
{*
|
|
* PreDefined page size
|
|
*}
|
|
PDF_PAGE_WIDTH_A4 = 596;
|
|
PDF_PAGE_HEIGHT_A4 = 842;
|
|
|
|
{*
|
|
* Dafault page size.
|
|
*}
|
|
PDF_DEFAULT_PAGE_WIDTH = PDF_PAGE_WIDTH_A4;
|
|
PDF_DEFAULT_PAGE_HEIGHT = PDF_PAGE_HEIGHT_A4;
|
|
|
|
{*
|
|
* collection of flags defining various characteristics of the font.
|
|
*}
|
|
PDF_FONT_FIXED_WIDTH = 1;
|
|
PDF_FONT_SERIF = 2;
|
|
PDF_FONT_SYMBOLIC = 4;
|
|
PDF_FONT_SCRIPT = 8;
|
|
// Reserved = 16
|
|
PDF_FONT_STD_CHARSET = 32;
|
|
PDF_FONT_ITALIC = 64;
|
|
// Reserved = 128
|
|
// Reserved = 256
|
|
// Reserved = 512
|
|
// Reserved = 1024
|
|
// Reserved = 2048
|
|
// Reserved = 4096
|
|
// Reserved = 8192
|
|
// Reserved = 16384
|
|
// Reserved = 32768
|
|
PDF_FONT_ALL_CAP = 65536;
|
|
PDF_FONT_SMALL_CAP = 131072;
|
|
PDF_FONT_FOURCE_BOLD = 262144;
|
|
|
|
PDF_DEFAULT_FONT = 'Arial';
|
|
PDF_DEFAULT_FONT_SIZE = 10;
|
|
|
|
PDF_MIN_HORIZONTALSCALING = 10;
|
|
PDF_MAX_HORIZONTALSCALING = 300;
|
|
PDF_MAX_WORDSPACE = 300;
|
|
PDF_MIN_CHARSPACE = -30;
|
|
PDF_MAX_CHARSPACE = 300;
|
|
PDF_MAX_FONTSIZE = 300;
|
|
PDF_MAX_ZOOMSIZE = 10;
|
|
PDF_MAX_LEADING = 300;
|
|
|
|
PDF_PAGE_LAYOUT_NAMES: array[0..3] of string = ('SinglePage',
|
|
'OneColumn',
|
|
'TwoColumnLeft',
|
|
'TwoColumnRight');
|
|
|
|
PDF_PAGE_MODE_NAMES: array[0..3] of string = ('UseNone',
|
|
'UseOutlines',
|
|
'UseThumbs',
|
|
'FullScreen');
|
|
|
|
PDF_ANNOTATION_TYPE_NAMES: array[0..12] of string = ('Text',
|
|
'Link',
|
|
'Sound',
|
|
'FreeText',
|
|
'Stamp',
|
|
'Square',
|
|
'Circle',
|
|
'StrikeOut',
|
|
'Highlight',
|
|
'Underline',
|
|
'Ink',
|
|
'FileAttachment',
|
|
'Popup');
|
|
|
|
PDF_DESTINATION_TYPE_NAMES: array[0..7] of string = ('XYZ',
|
|
'Fit',
|
|
'FitH',
|
|
'FitV',
|
|
'FitR',
|
|
'FitB',
|
|
'FitBH',
|
|
'FitBV');
|
|
|
|
type
|
|
{*
|
|
* The pagemode determines how the document should appear when opened.
|
|
*}
|
|
TPdfPageMode = (pmUseNone,
|
|
pmUseOutlines,
|
|
pmUseThumbs,
|
|
pmFullScreen);
|
|
|
|
{*
|
|
* The line cap style specifies the shape to be used at the ends of open
|
|
* subpaths when they are stroked.
|
|
*}
|
|
TLineCapStyle = (lcButt_End,
|
|
lcRound_End,
|
|
lcProjectingSquareEnd);
|
|
|
|
{*
|
|
* The line join style specifies the shape to be used at the corners of paths
|
|
* that are stroked.
|
|
*}
|
|
TLineJoinStyle = (ljMiterJoin,
|
|
ljRoundJoin,
|
|
ljBevelJoin);
|
|
|
|
{*
|
|
* The text rendering mode determines whether text is stroked, filled, or used
|
|
* as a clipping path.
|
|
*}
|
|
TTextRenderingMode = (trFill,
|
|
trStroke,
|
|
trFillThenStroke,
|
|
trInvisible,
|
|
trFillClipping,
|
|
trStrokeClipping,
|
|
trFillStrokeClipping,
|
|
trClipping);
|
|
|
|
{*
|
|
* The annotation types determines the valid annotation subtype of TPdfDoc.
|
|
*}
|
|
TPdfAnnotationSubType = (asTextNotes,
|
|
asLink);
|
|
|
|
{*
|
|
* The TPdfDestinationType determines default user space coordinate system of
|
|
* Explicit destinations.
|
|
*}
|
|
TPdfDestinationType = (dtXYZ,
|
|
dtFit,
|
|
dtFitH,
|
|
dtFitV,
|
|
dtFitR,
|
|
dtFitB,
|
|
dtFitBH,
|
|
dtFitBV);
|
|
|
|
{*
|
|
* TPdfPageLayout specifying the page layout to be used when the document is
|
|
* opened:
|
|
*}
|
|
TPdfPageLayout = (plSinglePage,
|
|
plOneColumn,
|
|
plTwoColumnLeft,
|
|
plTwoColumnRight);
|
|
|
|
|
|
TPdfViewerPreference = (vpHideToolbar,
|
|
vpHideMenubar,
|
|
vpHideWindowUI,
|
|
vpFitWindow,
|
|
vpCenterWindow);
|
|
TPdfViewerPreferences = set of TPdfViewerPreference;
|
|
|
|
{$IFDEF NOZLIB}
|
|
TPdfCompressionMethod = (cmNone);
|
|
{$ELSE}
|
|
TPdfCompressionMethod = (cmNone, cmFlateDecode);
|
|
{$ENDIF}
|
|
|
|
TPdfColor = -$7FFFFFFF-1..$7FFFFFFF;
|
|
TXObjectID = integer;
|
|
|
|
TPDF_STR_TBL = record
|
|
KEY: string;
|
|
VAL: string;
|
|
end;
|
|
TPDF_INT_TBL = record
|
|
KEY: string;
|
|
VAL: integer;
|
|
end;
|
|
|
|
TPdfHeader = class(TObject)
|
|
protected
|
|
procedure WriteToStream(const AStream: TStream);
|
|
end;
|
|
|
|
TPdfTrailer = class(TObject)
|
|
private
|
|
FAttributes: TPdfDictionary;
|
|
FXrefAddress: integer;
|
|
protected
|
|
procedure WriteToStream(const AStream: TStream);
|
|
public
|
|
constructor Create(AObjectMgr: TPdfObjectMgr);
|
|
destructor Destroy; override;
|
|
property XrefAddress: integer read FXrefAddress write FXrefAddress;
|
|
property Attributes: TPdfDictionary read FAttributes;
|
|
end;
|
|
|
|
TPdfXrefEntry = class(TObject)
|
|
private
|
|
FEntryType: string;
|
|
FByteOffset: integer;
|
|
FGenerationNumber: integer;
|
|
FValue: TPdfObject;
|
|
function GetAsString: string;
|
|
public
|
|
constructor Create(AValue: TPdfObject);
|
|
destructor Destroy; override;
|
|
property EntryType: string read FEntryType write FEntryType;
|
|
property ByteOffset: integer read FByteOffSet write FByteOffset;
|
|
property GenerationNumber: integer
|
|
read FGenerationNumber write FGenerationNumber;
|
|
property AsString: string read GetAsString;
|
|
property Value: TPdfObject read FValue;
|
|
end;
|
|
|
|
TPdfXref = class(TPdfObjectMgr)
|
|
private
|
|
FXrefEntries: TList;
|
|
function GetItem(ObjectID: integer): TPdfXrefEntry;
|
|
function GetItemCount: integer;
|
|
protected
|
|
procedure WriteToStream(const AStream: TStream);
|
|
public
|
|
constructor Create;
|
|
destructor Destroy; override;
|
|
procedure AddObject(AObject: TPdfObject); override;
|
|
function GetObject(ObjectID: integer): TPdfObject; override;
|
|
property Items[ObjectID: integer]: TPdfXrefEntry read GetItem;
|
|
property ItemCount: integer read GetItemCount;
|
|
end;
|
|
|
|
TPdfCanvas = class;
|
|
TPdfInfo = class;
|
|
TPdfCatalog = class;
|
|
TPdfFont = class;
|
|
TPdfDestination = class;
|
|
// TPdfLink = class;
|
|
TPdfOutlineEntry = class;
|
|
TPdfOutlineRoot = class;
|
|
TAbstractPReport = class(TComponent);
|
|
|
|
TPdfDoc = class(TObject)
|
|
private
|
|
FRoot: TPdfCatalog;
|
|
FCurrentPages: TPdfDictionary;
|
|
FCanvas: TPdfCanvas;
|
|
FHeader: TPdfHeader;
|
|
FTrailer: TPdfTrailer;
|
|
FXref: TPdfXref;
|
|
FInfo: TPdfInfo;
|
|
FHasDoc: boolean;
|
|
FFontList: TList;
|
|
FObjectList: TList;
|
|
FOutlineRoot: TPdfOutlineRoot;
|
|
FXObjectList: TPdfArray;
|
|
FDefaultPageWidth: Word;
|
|
FDefaultPageHeight: Word;
|
|
FCompressionMethod: TPdfCompressionMethod;
|
|
FUseOutlines: boolean;
|
|
function GetCanvas: TPdfCanvas;
|
|
function GetInfo: TPdfInfo;
|
|
function GetRoot: TPdfCatalog;
|
|
function GetOutlineRoot: TPdfOutlineRoot;
|
|
protected
|
|
procedure CreateInfo;
|
|
procedure CreateOutlines;
|
|
function CreateCatalog: TPdfDictionary;
|
|
function CreateFont(FontName: string): TPdfFont;
|
|
function CreatePages(Parent: TPdfDictionary): TPdfDictionary;
|
|
public
|
|
procedure RegisterXObject(AObject: TPdfXObject; AName: string);
|
|
constructor Create;
|
|
destructor Destroy; override;
|
|
procedure NewDoc;
|
|
procedure FreeDoc;
|
|
procedure AddPage;
|
|
procedure AddXObject(AName: string; AXObject: TPdfXObject);
|
|
procedure SaveToStream(AStream: TStream);
|
|
procedure SetVirtualMode;
|
|
function GetFont(FontName: string): TPdfFont;
|
|
function GetXObject(AName: string): TPdfXObject;
|
|
function CreateAnnotation(AType: TPdfAnnotationSubType; ARect: TPdfRect): TPdfDictionary;
|
|
function CreateDestination: TPdfDestination;
|
|
property HasDoc: boolean read FHasDoc;
|
|
property Canvas: TPdfCanvas read GetCanvas;
|
|
property Info: TPdfInfo read GetInfo;
|
|
property Root: TPdfCatalog read GetRoot;
|
|
property OutlineRoot: TPdfOutlineRoot read GetOutlineRoot;
|
|
property DefaultPageWidth: word read FDefaultPageWidth write FDefaultPageWidth;
|
|
property DefaultPageHeight: word read FDefaultPageHeight write FDefaultPageHeight;
|
|
property CompressionMethod: TPdfCompressionMethod
|
|
read FCompressionMethod write FCompressionMethod;
|
|
property UseOutlines: boolean read FUseoutlines write FUseoutlines;
|
|
end;
|
|
|
|
TPdfCanvasAttribute = class(TObject)
|
|
private
|
|
FWordSpace: Single;
|
|
FCharSpace: Single;
|
|
FFontSize: Single;
|
|
FFont: TPdfFont;
|
|
FLeading: Single;
|
|
FHorizontalScaling: Word;
|
|
procedure SetWordSpace(Value: Single);
|
|
procedure SetCharSpace(Value: Single);
|
|
procedure SetFontSize(Value: Single);
|
|
procedure SetHorizontalScaling(Value: Word);
|
|
procedure SetLeading(Value: Single);
|
|
public
|
|
function TextWidth(Text: string): Single;
|
|
function MeasureText(Text: string; Width: Single): integer;
|
|
property WordSpace: Single read FWordSpace write SetWordSpace;
|
|
property CharSpace: Single read FCharSpace write SetCharSpace;
|
|
property HorizontalScaling: Word read FHorizontalScaling
|
|
write SetHorizontalScaling;
|
|
property Leading: Single read FLeading write SetLeading;
|
|
property FontSize: Single read FFontSize write SetFontSize;
|
|
property Font: TPdfFont read FFont write FFont;
|
|
end;
|
|
|
|
TPdfCanvas = class(TObject)
|
|
private
|
|
FContents: TPdfStream;
|
|
FPage: TPdfDictionary;
|
|
FPdfDoc: TPdfDoc;
|
|
FAttr: TPdfCanvasAttribute;
|
|
FIsVirtual: boolean;
|
|
procedure SetPageWidth(AValue: integer);
|
|
procedure SetPageHeight(AValue: integer);
|
|
procedure WriteString(S: string);
|
|
function GetDoc: TPdfDoc;
|
|
function GetPage: TPdfDictionary;
|
|
function GetPageWidth: Integer;
|
|
function GetPageHeight: Integer;
|
|
function GetColorStr(Color: TPdfColor): string;
|
|
protected
|
|
public
|
|
constructor Create(APdfDoc: TPdfDoc);
|
|
destructor Destroy; override;
|
|
|
|
{* Special Graphics State *}
|
|
procedure GSave; { q }
|
|
procedure GRestore; { Q }
|
|
procedure Concat(a, b, c, d, e, f: Single); { cm }
|
|
|
|
{* General Graphics State *}
|
|
procedure SetFlat(flatness: Byte); { i }
|
|
procedure SetLineCap(linecap: TLineCapStyle); { J }
|
|
procedure SetDash(aarray: array of Byte; phase: Byte); { d }
|
|
procedure SetLineJoin(linejoin: TLineJoinStyle); { j }
|
|
procedure SetLineWidth(linewidth: Single); { w }
|
|
procedure SetMiterLimit(miterlimit: Byte); { M }
|
|
|
|
{* Paths *}
|
|
procedure MoveTo(x, y: Single); { m }
|
|
procedure LineTo(x, y: Single); { l }
|
|
procedure CurveToC(x1, y1, x2, y2, x3, y3: Single); { c }
|
|
procedure CurveToV(x2, y2, x3, y3: Single); { v }
|
|
procedure CurveToY(x1, y1, x3, y3: Single); { y }
|
|
procedure Rectangle(x, y, width, height: Single); { re }
|
|
procedure Closepath; { h }
|
|
procedure NewPath; { n }
|
|
procedure Stroke; { S }
|
|
procedure ClosePathStroke; { s }
|
|
procedure Fill; { f }
|
|
procedure Eofill; { f* }
|
|
procedure FillStroke; { B }
|
|
procedure ClosepathFillStroke; { b }
|
|
procedure EofillStroke; { B* }
|
|
procedure ClosepathEofillStroke; { b* }
|
|
procedure Clip; { W }
|
|
procedure Eoclip; { W* }
|
|
|
|
{* Test state *}
|
|
procedure SetCharSpace(charSpace: Single); { Tc }
|
|
procedure SetWordSpace(wordSpace: Single); { Tw }
|
|
procedure SetHorizontalScaling(hScaling: Word); { Tz }
|
|
procedure SetLeading(leading: Single); { TL }
|
|
procedure SetFontAndSize(fontname: string; size: Single); { Tf }
|
|
procedure SetTextRenderingMode(mode: TTextRenderingMode); { Tr }
|
|
procedure SetTextRise(rise: Word); { Ts }
|
|
procedure BeginText; { BT }
|
|
procedure EndText; { ET }
|
|
procedure MoveTextPoint(tx, ty: Single); { Td }
|
|
procedure SetTextMatrix(a, b, c, d, x, y: Word); { Tm }
|
|
procedure MoveToNextLine; { T* }
|
|
procedure ShowText(s: string); { Tj }
|
|
procedure ShowTextNextLine(s: string); { ' }
|
|
|
|
{* external objects *}
|
|
procedure ExecuteXObject(xObject: string); { Do }
|
|
|
|
{* Device-dependent color space operators *}
|
|
procedure SetRGBFillColor(Value: TPdfColor); { rg }
|
|
procedure SetRGBStrokeColor(Value: TPdfColor); { RG }
|
|
|
|
{* utility routines *}
|
|
procedure SetPage(APage: TPdfDictionary);
|
|
procedure SetFont(AName: string; ASize: Single);
|
|
procedure TextOut(X, Y: Single; Text: string);
|
|
procedure TextRect(ARect: TPdfRect; Text: string;
|
|
Alignment: TPdfAlignment; Clipping: boolean);
|
|
procedure MultilineTextRect(ARect: TPdfRect;
|
|
Text: string; WordWrap: boolean);
|
|
procedure DrawXObject(X, Y, AWidth, AHeight: Single;
|
|
AXObjectName: string);
|
|
procedure DrawXObjectEx(X, Y, AWidth, AHeight: Single;
|
|
ClipX, ClipY, ClipWidth, ClipHeight: Single; AXObjectName: string);
|
|
procedure Ellipse(x, y, width, height: Single);
|
|
function TextWidth(Text: string): Single;
|
|
function MeasureText(Text: string; AWidth: Single): integer;
|
|
function GetNextWord(const S: string; var Index: integer): string;
|
|
|
|
property Attribute: TPdfCanvasAttribute read FAttr;
|
|
property Contents: TPdfStream read FContents;
|
|
property Page: TPdfDictionary read GetPage;
|
|
property Doc: TPdfDoc read GetDoc;
|
|
property PageWidth: integer read GetPageWidth write SetPageWidth;
|
|
property PageHeight: integer read GetPageHeight write SetPageHeight;
|
|
end;
|
|
|
|
TPdfDictionaryWrapper = class(TPersistent)
|
|
private
|
|
FData: TPdfDictionary;
|
|
function GetHasData: boolean;
|
|
protected
|
|
procedure SetData(AData: TPdfDictionary); virtual;
|
|
public
|
|
property Data: TPdfDictionary read FData write SetData;
|
|
property HasData: boolean read GetHasData;
|
|
end;
|
|
|
|
TPdfInfo = class(TPdfDictionaryWrapper)
|
|
private
|
|
function GetAuthor: string;
|
|
procedure SetAuthor(Value: string);
|
|
function GetCreationDate: TDateTime;
|
|
procedure SetCreationDate(Value: TDateTime);
|
|
function GetCreator: string;
|
|
procedure SetCreator(Value: string);
|
|
function GetKeywords: string;
|
|
procedure SetKeywords(Value: string);
|
|
function GetSubject: string;
|
|
procedure SetSubject(Value: string);
|
|
function GetTitle: string;
|
|
procedure SetTitle(Value: string);
|
|
function GetModDate: TDateTime;
|
|
procedure SetModDate(Value: TDateTime);
|
|
public
|
|
property Author: string read GetAuthor write SetAuthor;
|
|
property CreationDate: TDateTime read GetCreationDate write SetCreationDate;
|
|
property Creator: string read GetCreator write SetCreator;
|
|
property Keywords: string read GetKeywords write SetKeywords;
|
|
property ModDate: TDateTime read GetModDate write SetModDate;
|
|
property Subject: string read GetSubject write SetSubject;
|
|
property Title: string read GetTitle write SetTitle;
|
|
end;
|
|
|
|
TPdfCatalog = class(TPdfDictionaryWrapper)
|
|
private
|
|
FOpenAction: TPdfDestination;
|
|
procedure SetPageLayout(Value: TPdfPageLayout);
|
|
procedure SetPageMode(Value: TPdfPageMode);
|
|
procedure SetNonFullScreenPageMode(Value: TPdfPageMode);
|
|
procedure SetViewerPreference(Value: TPdfViewerPreferences);
|
|
procedure SetPages(APage: TPdfDictionary);
|
|
function GetPageLayout: TPdfPageLayout;
|
|
function GetPageMode: TPdfPageMode;
|
|
function GetNonFullScreenPageMode: TPdfPageMode;
|
|
function GetViewerPreference: TPdfViewerPreferences;
|
|
function GetPages: TPdfDictionary;
|
|
protected
|
|
procedure SaveOpenAction;
|
|
public
|
|
property OpenAction: TPdfDestination read FOpenAction write FOpenAction;
|
|
property PageLayout: TPdfPageLayout read GetPageLayout write SetPageLayout;
|
|
property NonFullScreenPageMode: TPdfPageMode
|
|
read GetNonFullScreenPageMode write SetNonFullScreenPageMode;
|
|
property PageMode: TPdfPageMode read GetPageMode write SetPageMode;
|
|
property ViewerPreference: TPdfViewerPreferences
|
|
read GetViewerPreference write SetViewerPreference;
|
|
property Pages: TPdfDictionary read GetPages write SetPages;
|
|
end;
|
|
|
|
TPdfFont = class(TPdfDictionaryWrapper)
|
|
private
|
|
FName: string;
|
|
protected
|
|
procedure AddStrElements(ADic: TPdfDictionary; ATable: array of TPDF_STR_TBL);
|
|
procedure AddIntElements(ADic: TPdfDictionary; ATable: array of TPDF_INT_TBL);
|
|
public
|
|
constructor Create(AXref: TPdfXref; AName: string); virtual;
|
|
function GetCharWidth(AText: string; APos: integer): integer; virtual;
|
|
property Name: string read FName;
|
|
end;
|
|
|
|
TPdfDestination = class(TObject)
|
|
private
|
|
FDoc: TPdfDoc;
|
|
FPage: TPdfDictionary;
|
|
FType: TPdfDestinationType;
|
|
FValues: array[0..3] of Integer;
|
|
FZoom: Single;
|
|
FReference: TObject;
|
|
procedure SetElement(Index: integer; Value: Integer);
|
|
procedure SetZoom(Value: Single);
|
|
function GetElement(Index: integer): Integer;
|
|
function GetPageWidth: Integer;
|
|
function GetPageHeight: Integer;
|
|
public
|
|
constructor Create(APdfDoc: TPdfDoc);
|
|
destructor Destroy; override;
|
|
function GetValue: TPdfArray;
|
|
property DestinationType: TPdfDestinationType read FType write FType;
|
|
property Doc: TPdfDoc read FDoc;
|
|
property Left: Integer index 0 read GetElement write SetElement;
|
|
property Top: Integer index 1 read GetElement write SetElement;
|
|
property Right: Integer index 2 read GetElement write SetElement;
|
|
property Bottom: Integer index 3 read GetElement write SetElement;
|
|
property PageHeight: Integer read GetPageHeight;
|
|
property PageWidth: Integer read GetPageWidth;
|
|
property Zoom: Single read FZoom write SetZoom;
|
|
property Reference: TObject read FReference write FReference;
|
|
end;
|
|
|
|
TPdfOutlineEntry = class(TPdfDictionaryWrapper)
|
|
private
|
|
FParent: TPdfOutlineEntry;
|
|
FNext: TPdfOutlineEntry;
|
|
FPrev: TPdfOutlineEntry;
|
|
FFirst: TPdfOutlineEntry;
|
|
FLast: TPdfOutlineEntry;
|
|
FDest: TPdfDestination;
|
|
FDoc: TPdfDoc;
|
|
FTitle: string;
|
|
FOpened: boolean;
|
|
FCount: integer;
|
|
FReference: TObject;
|
|
protected
|
|
constructor CreateEntry(AParent: TPdfOutlineEntry); virtual;
|
|
procedure Save; virtual;
|
|
public
|
|
destructor Destroy; override;
|
|
function AddChild: TPdfOutlineEntry;
|
|
property Doc: TPdfDoc read FDoc;
|
|
property Parent: TPdfOutlineEntry read FParent;
|
|
property Next: TPdfOutlineEntry read FNext;
|
|
property Prev: TPdfOutlineEntry read FPrev;
|
|
property First: TPdfOutlineEntry read FFirst;
|
|
property Last: TPdfOutlineEntry read FLast;
|
|
property Dest: TPdfDestination read FDest write FDest;
|
|
property Title: string read FTitle write FTitle;
|
|
property Opened: boolean read FOpened write FOpened;
|
|
property Reference: TObject read FReference write FReference;
|
|
end;
|
|
|
|
TPdfOutlineRoot = class(TPdfOutlineEntry)
|
|
protected
|
|
constructor CreateRoot(ADoc: TPdfDoc); virtual;
|
|
public
|
|
procedure Save; override;
|
|
end;
|
|
|
|
implementation
|
|
|
|
{ Utility functions }
|
|
|
|
// _Pages_AddKids
|
|
procedure _Pages_AddKids(AParent: TPdfDictionary; AKid: TPdfDictionary);
|
|
var
|
|
FKids: TPdfArray;
|
|
begin
|
|
// adding page object to the parent pages object.
|
|
FKids := AParent.PdfArrayByName('Kids');
|
|
FKids.AddItem(AKid);
|
|
AParent.PdfNumberByName('Count').Value := FKids.ItemCount;
|
|
end;
|
|
|
|
// _Page_GetResources
|
|
function _Page_GetResources(APage: TPdfDictionary; AName: string): TPdfDictionary;
|
|
var
|
|
FResources: TPdfDictionary;
|
|
begin
|
|
FResources := APage.PdfDictionaryByName('Resources');
|
|
Result := FResources.PdfDictionaryByName(AName);
|
|
end;
|
|
|
|
{ TPdfHeader }
|
|
|
|
// WriteToStream
|
|
procedure TPdfHeader.WriteToStream(const AStream: TStream);
|
|
begin
|
|
_WriteString('%PDF-1.2 '#13#10, AStream);
|
|
end;
|
|
|
|
{ TPdfTrailer }
|
|
|
|
// WriteToStream
|
|
procedure TPdfTrailer.WriteToStream(const AStream: TStream);
|
|
begin
|
|
_WriteString('trailer' + CRLF, AStream);
|
|
FAttributes.WriteToStream(AStream);
|
|
_WriteString(CRLF + 'startxref' + CRLF, AStream);
|
|
_WriteString(IntToStr(FXrefAddress) + CRLF, AStream);
|
|
_WriteString('%%EOF' + CRLF, AStream);
|
|
end;
|
|
|
|
// Create
|
|
constructor TPdfTrailer.Create(AObjectMgr: TPdfObjectMgr);
|
|
begin
|
|
inherited Create;
|
|
FAttributes := TPdfDictionary.CreateDictionary(AObjectMgr);
|
|
FAttributes.AddItem('Size', TPdfNumber.CreateNumber(0));
|
|
end;
|
|
|
|
// Destroy
|
|
destructor TPdfTrailer.Destroy;
|
|
begin
|
|
FAttributes.Free;
|
|
inherited;
|
|
end;
|
|
|
|
{ TPdfXrefEntry }
|
|
|
|
// Create
|
|
constructor TPdfXrefEntry.Create(AValue: TPdfObject);
|
|
begin
|
|
FByteOffset := -1;
|
|
if AValue <> nil then
|
|
begin
|
|
FEntryType := PDF_IN_USE_ENTRY;
|
|
FGenerationNumber := AValue.GenerationNumber;
|
|
FValue := AValue;
|
|
end
|
|
else
|
|
begin
|
|
FEntryType := PDF_FREE_ENTRY;
|
|
FGenerationNumber := 0;
|
|
end;
|
|
end;
|
|
|
|
// Destroy
|
|
destructor TPdfXrefEntry.Destroy;
|
|
begin
|
|
if FEntryType = PDF_IN_USE_ENTRY then
|
|
FValue.Free;
|
|
inherited;
|
|
end;
|
|
|
|
// GetAsString
|
|
function TPdfXrefEntry.GetAsString: string;
|
|
function FormatIntToString(Value: integer; Len: integer): string;
|
|
var
|
|
S: string;
|
|
i, j: integer;
|
|
begin
|
|
Result := '';
|
|
if Value < 0 then
|
|
S := '0'
|
|
else
|
|
S := IntToStr(Value);
|
|
i := Len - Length(S);
|
|
for j := 0 to i - 1 do
|
|
Result := Result + '0';
|
|
Result := Result + S;
|
|
end;
|
|
begin
|
|
Result := FormatIntToString(FByteOffset, 10) +
|
|
' ' +
|
|
FormatIntToString(FGenerationNumber, 5) +
|
|
' ' +
|
|
FEntryType;
|
|
end;
|
|
|
|
{ TPdfXref }
|
|
|
|
// Create
|
|
constructor TPdfXref.Create;
|
|
var
|
|
RootEntry: TPdfXrefEntry;
|
|
begin
|
|
FXrefEntries := TList.Create;
|
|
RootEntry := TPdfXrefEntry.Create(nil);
|
|
RootEntry.GenerationNumber := PDF_MAX_GENERATION_NUM;
|
|
FXrefEntries.Add(RootEntry);
|
|
end;
|
|
|
|
// Destroy
|
|
destructor TPdfXref.Destroy;
|
|
var
|
|
i: integer;
|
|
begin
|
|
for i := 1 to FXrefEntries.Count - 1 do
|
|
GetItem(i).Free;
|
|
FXrefEntries.Free;
|
|
inherited;
|
|
end;
|
|
|
|
// AddObject
|
|
procedure TPdfXref.AddObject(AObject: TPdfObject);
|
|
var
|
|
ObjectNumber: integer;
|
|
XrefEntry: TPdfXrefEntry;
|
|
begin
|
|
// register object to xref table, and set objectnumber.
|
|
if AObject.ObjectType <> otDirectObject then
|
|
raise EPdfInvalidOperation.Create('AddObject --wrong object type.');
|
|
XrefEntry := TPdfXrefEntry.Create(AObject);
|
|
ObjectNumber := FXrefEntries.Add(XrefEntry);
|
|
AObject.SetObjectNumber(ObjectNumber);
|
|
end;
|
|
|
|
// GetItem
|
|
function TPdfXref.GetItem(ObjectID: integer): TPdfXrefEntry;
|
|
begin
|
|
Result := TPdfXrefEntry(FXrefEntries.Items[ObjectID]);
|
|
end;
|
|
|
|
// GetItemCount
|
|
function TPdfXref.GetItemCount: integer;
|
|
begin
|
|
Result := FXrefEntries.Count;
|
|
end;
|
|
|
|
// GetObject
|
|
function TPdfXref.GetObject(ObjectID: integer): TPdfObject;
|
|
begin
|
|
Result := GetItem(ObjectID).Value;
|
|
end;
|
|
|
|
// WriteToStream
|
|
procedure TPdfXref.WriteToStream(const AStream: TStream);
|
|
var
|
|
i: integer;
|
|
S: string;
|
|
Count: integer;
|
|
begin
|
|
Count := FXrefEntries.Count;
|
|
S := 'xref' +
|
|
CRLF +
|
|
'0 ' +
|
|
IntToStr(Count) +
|
|
CRLF;
|
|
for i := 0 to Count - 1 do
|
|
S := S + Items[i].AsString + CRLF;
|
|
_WriteString(S, AStream);
|
|
end;
|
|
|
|
{ TPdfDoc }
|
|
|
|
// Create
|
|
constructor TPdfDoc.Create;
|
|
begin
|
|
inherited Create;
|
|
FHasDoc := false;
|
|
FCanvas := TPdfCanvas.Create(Self);
|
|
FDefaultPageWidth := PDF_DEFAULT_PAGE_WIDTH;
|
|
FDefaultPageHeight := PDF_DEFAULT_PAGE_HEIGHT;
|
|
FInfo := nil;
|
|
FRoot := nil;
|
|
end;
|
|
|
|
// GetCanvas
|
|
function TPdfDoc.GetCanvas: TPdfCanvas;
|
|
begin
|
|
if not HasDoc then
|
|
raise EPdfInvalidOperation.Create('GetCanvas --Document is null');
|
|
Result := FCanvas;
|
|
end;
|
|
|
|
// GetInfo
|
|
function TPdfDoc.GetInfo: TPdfInfo;
|
|
begin
|
|
if not HasDoc then
|
|
raise EPdfInvalidOperation.Create('GetInfo --this method can not use this state..');
|
|
if FInfo = nil then
|
|
CreateInfo;
|
|
Result := FInfo;
|
|
end;
|
|
|
|
// GetRoot
|
|
function TPdfDoc.GetRoot: TPdfCatalog;
|
|
begin
|
|
if not HasDoc then
|
|
raise EPdfInvalidOperation.Create('GetRoot --this method can not use this state..');
|
|
Result := FRoot;
|
|
end;
|
|
|
|
// GetOutlineRoot
|
|
function TPdfDoc.GetOutlineRoot: TPdfOutlineRoot;
|
|
begin
|
|
if not HasDoc then
|
|
raise EPdfInvalidOperation.Create('GetOutlineRoot --document is null..');
|
|
if not UseOutlines then
|
|
raise EPdfInvalidOperation.Create('GetOutlineRoot --not use outline mode..');
|
|
Result := FOutlineRoot;
|
|
end;
|
|
|
|
// Destroy
|
|
destructor TPdfDoc.Destroy;
|
|
begin
|
|
FreeDoc;
|
|
FCanvas.Free;
|
|
inherited;
|
|
end;
|
|
|
|
// CreateCatalog
|
|
function TPdfDoc.CreateCatalog: TPdfDictionary;
|
|
begin
|
|
// create catalog object and register to xref.
|
|
Result := TPdfDictionary.CreateDictionary(FXref);
|
|
FXref.AddObject(Result);
|
|
Result.AddItem('Type', TPdfName.CreateName('Catalog'));
|
|
FTrailer.Attributes.AddItem('Root', Result);
|
|
end;
|
|
|
|
// CreateFont
|
|
function TPdfDoc.CreateFont(FontName: string): TPdfFont;
|
|
var
|
|
PdfFont: TPdfFont;
|
|
begin
|
|
// create new font (not regist to xref -- because font object registed by
|
|
// TPdfFont).
|
|
{$IFDEF LAZ_POWERPDF}
|
|
PdfFont := TPdfFont(PdfLazFindClass(FontName).Create);
|
|
{$ELSE}
|
|
PdfFont := TPdfFont(FindClass(FontName).Create);
|
|
{$ENDIF}
|
|
if PdfFont = nil then
|
|
raise Exception.Create('CreateFont --InvalidFontName:' + FontName);
|
|
Result := PdfFont.Create(FXref, FontName);
|
|
Result.Data.AddItem('Name',
|
|
TPdfName.CreateName('F' + IntToStr(FFontList.Count)));
|
|
FFontList.Add(Result);
|
|
end;
|
|
|
|
// RegisterXObject
|
|
procedure TPdfDoc.RegisterXObject(AObject: TPdfXObject; AName: string);
|
|
begin
|
|
// check object and register it.
|
|
if AObject = nil then
|
|
raise EPdfInvalidValue.Create('RegisterXObject --AObject is null');
|
|
if _GetTypeOf(AObject.Attributes) <> 'XObject' then
|
|
raise EPdfInvalidValue.Create('RegisterXObject --not XObject');
|
|
if AObject.ObjectType <> otIndirectObject then
|
|
FXref.AddObject(AObject);
|
|
if AObject.Attributes.ValueByName('Name') = nil then
|
|
begin
|
|
if GetXObject(AName) <> nil then
|
|
raise EPdfInvalidValue.Createfmt('RegisterXObject --dupulicate name: %s', [AName]);
|
|
FXObjectList.AddItem(AObject);
|
|
AObject.Attributes.AddItem('Name', TPdfName.CreateName(AName));
|
|
end;
|
|
end;
|
|
|
|
// CreateInfo
|
|
procedure TPdfDoc.CreateInfo;
|
|
var
|
|
FInfoDictionary: TPdfDictionary;
|
|
begin
|
|
FInfoDictionary := TPdfDictionary.CreateDictionary(FXref);
|
|
FXref.AddObject(FInfoDictionary);
|
|
FInfoDictionary.AddItem('Producer', TPdfText.CreateText(POWER_PDF_VERSION_TEXT));
|
|
FTrailer.Attributes.AddItem('Info', FInfoDictionary);
|
|
FInfo := TPdfInfo.Create;
|
|
FInfo.SetData(FInfoDictionary);
|
|
FObjectList.Add(FInfo);
|
|
end;
|
|
|
|
// CreatePages
|
|
function TPdfDoc.CreatePages(Parent: TPdfDictionary): TPdfDictionary;
|
|
begin
|
|
// create pages object and register to xref.
|
|
result := TPdfDictionary.CreateDictionary(FXref);
|
|
FXref.AddObject(Result);
|
|
with Result do
|
|
begin
|
|
AddItem('Type', TPdfName.CreateName('Pages'));
|
|
AddItem('Kids', TPdfArray.CreateArray(FXref));
|
|
AddItem('Count', TPdfNumber.CreateNumber(0));
|
|
end;
|
|
|
|
if (Parent <> nil) and (_GetTypeOf(Parent) = 'Pages') then
|
|
_Pages_AddKids(Parent, Result)
|
|
else
|
|
FRoot.Pages := Result;
|
|
end;
|
|
|
|
// CreateOutlines
|
|
procedure TPdfDoc.CreateOutlines;
|
|
begin
|
|
FOutlineRoot := TPdfOutlineRoot.CreateRoot(Self);
|
|
FRoot.Data.AddItem('Outlines', FOutlineRoot.Data);
|
|
end;
|
|
|
|
// GetFont
|
|
function TPdfDoc.GetFont(FontName: string): TPdfFont;
|
|
var
|
|
FFont: TPdfFont;
|
|
i :integer;
|
|
begin
|
|
if not HasDoc then
|
|
raise EPdfInvalidOperation.Create('GetFont --document is null.');
|
|
|
|
// if specified font exists in fontlist, return it. otherwise, create the font.
|
|
Result := nil;
|
|
for i := 0 to FFontList.Count - 1 do
|
|
begin
|
|
FFont := TPdfFont(FFontList.Items[i]);
|
|
if FFont.Name = FontName then
|
|
begin
|
|
Result := FFont;
|
|
Break;
|
|
end;
|
|
end;
|
|
if Result = nil then
|
|
Result := CreateFont(FontName);
|
|
end;
|
|
|
|
// GetXObject
|
|
function TPdfDoc.GetXObject(AName: string): TPdfXObject;
|
|
var
|
|
FXObject: TPdfXObject;
|
|
i :integer;
|
|
begin
|
|
// return the XObject which name is muched with specified name.
|
|
Result := nil;
|
|
for i := 0 to FXObjectList.ItemCount - 1 do
|
|
begin
|
|
FXObject := TPdfXObject(FXObjectList.Items[i]);
|
|
if TPdfName(FXObject.Attributes.ValueByName('Name')).Value = AName then
|
|
begin
|
|
Result := FXObject;
|
|
Break;
|
|
end;
|
|
end;
|
|
end;
|
|
|
|
// CreateAnnotation
|
|
function TPdfDoc.CreateAnnotation(AType: TPdfAnnotationSubType; ARect: TPdfRect): TPdfDictionary;
|
|
var
|
|
FAnnotation: TPdfDictionary;
|
|
FArray: TPdfArray;
|
|
FPage: TPdfDictionary;
|
|
begin
|
|
if not HasDoc then
|
|
raise EPdfInvalidOperation.Create('AddAnotation --document is null.');
|
|
|
|
// create new annotation and set the properties.
|
|
FAnnotation := TPdfDictionary.CreateDictionary(FXref);
|
|
FXref.AddObject(FAnnotation);
|
|
with FAnnotation do
|
|
begin
|
|
AddItem('Type', TPdfName.CreateName('Annot'));
|
|
AddItem('Subtype', TPdfName.CreateName(PDF_ANNOTATION_TYPE_NAMES[ord(AType)]));
|
|
FArray := TPdfArray.CreateArray(nil);
|
|
with FArray, ARect do
|
|
begin
|
|
AddItem(TPdfReal.CreateReal(Left));
|
|
AddItem(TPdfReal.CreateReal(Top));
|
|
AddItem(TPdfReal.CreateReal(Right));
|
|
AddItem(TPdfReal.CreateReal(Bottom));
|
|
end;
|
|
AddItem('Rect', FArray);
|
|
end;
|
|
|
|
// adding annotation to the current page
|
|
FPage := FCanvas.Page;
|
|
FArray := FPage.PdfArrayByName('Annots');
|
|
if FArray = nil then
|
|
begin
|
|
FArray := TPdfArray.CreateArray(nil);
|
|
FPage.AddItem('Annots', FArray);
|
|
end;
|
|
FArray.AddItem(FAnnotation);
|
|
|
|
Result := FAnnotation;
|
|
end;
|
|
|
|
// CreateDestination
|
|
function TPdfDoc.CreateDestination: TPdfDestination;
|
|
begin
|
|
Result := TPdfDestination.Create(Self);
|
|
FObjectList.Add(Result);
|
|
end;
|
|
|
|
// NewDoc
|
|
procedure TPdfDoc.NewDoc;
|
|
begin
|
|
{*
|
|
* create new document.
|
|
*}
|
|
FreeDoc;
|
|
FXref := TPdfXref.Create;
|
|
FHeader := TPdfHeader.Create;
|
|
FTrailer := TPdfTrailer.Create(FXref);
|
|
FFontList := TList.Create;
|
|
FXObjectList := TPdfArray.CreateArray(FXref);
|
|
FObjectList := TList.Create;
|
|
|
|
FRoot := TPdfCatalog.Create;
|
|
FRoot.SetData(CreateCatalog);
|
|
FObjectList.Add(FRoot);
|
|
|
|
if UseOutlines then
|
|
CreateOutlines;
|
|
|
|
CreateInfo;
|
|
FInfo.CreationDate := now;
|
|
|
|
FCurrentPages := CreatePages(nil);
|
|
FRoot.SetPages(FCurrentPages);
|
|
|
|
FHasDoc := true;
|
|
end;
|
|
|
|
// AddXObject
|
|
procedure TPdfDoc.AddXObject(AName: string; AXObject: TPdfXObject);
|
|
begin
|
|
if GetXObject(AName) <> nil then
|
|
raise Exception.CreateFmt('AddImage --the image named %s is already exists..', [AName]);
|
|
|
|
// check whether AImage is valid PdfImage or not.
|
|
if (AXObject = nil) or (AXObject.Attributes = nil) or
|
|
(_GetTypeOf(AXObject.Attributes) <> 'XObject') or
|
|
(AXObject.Attributes.PdfNameByName('Subtype').Value <> 'Image') then
|
|
raise Exception.Create('AddImage --the image is not valid TPdfImage..');
|
|
|
|
FXref.AddObject(AXObject);
|
|
RegisterXObject(AXObject, AName);
|
|
end;
|
|
|
|
// AddPage
|
|
procedure TPdfDoc.AddPage;
|
|
var
|
|
FPage: TPdfDictionary;
|
|
FMediaBox: TPdfArray;
|
|
FContents: TPdfStream;
|
|
FResources: TPdfDictionary;
|
|
FProcSet: TPdfArray;
|
|
FFontArray: TPdfDictionary;
|
|
FXObjectArray: TPdfDictionary;
|
|
{$IFNDEF NOZLIB}
|
|
FFilter: TPdfArray;
|
|
{$ENDIF}
|
|
begin
|
|
if FCurrentPages = nil then
|
|
raise EPdfInvalidOperation.Create('AddPage --current pages null.');
|
|
|
|
// create new page object and add it to the current pages object.
|
|
FPage := TPdfDictionary.CreateDictionary(FXref);
|
|
FXref.AddObject(FPage);
|
|
|
|
_Pages_AddKids(FCurrentPages, FPage);
|
|
|
|
FPage.AddItem('Type', TPdfName.CreateName('Page'));
|
|
FPage.AddItem('Parent', FCurrentPages);
|
|
|
|
FMediaBox := TPdfArray.CreateArray(FXref);
|
|
with FMediabox do
|
|
begin
|
|
AddItem(TPdfNumber.CreateNumber(0));
|
|
AddItem(TPdfNumber.CreateNumber(0));
|
|
AddItem(TPdfNumber.CreateNumber(DefaultPageWidth));
|
|
AddItem(TPdfNumber.CreateNumber(DefaultPageHeight));
|
|
end;
|
|
FPage.AddItem('MediaBox', FMediaBox);
|
|
|
|
FResources := TPdfDictionary.CreateDictionary(FXref);
|
|
FPage.AddItem('Resources', FResources);
|
|
|
|
FFontArray := TPdfDictionary.CreateDictionary(FXref);
|
|
FResources.AddItem('Font', FFontArray);
|
|
|
|
FXObjectArray := TPdfDictionary.CreateDictionary(FXref);
|
|
FResources.AddItem('XObject', FXObjectArray);
|
|
|
|
FProcSet := TPdfArray.CreateArray(FXref);
|
|
with FProcSet do
|
|
begin
|
|
AddItem(TPdfName.CreateName('PDF'));
|
|
AddItem(TPdfName.CreateName('Text'));
|
|
AddItem(TPdfName.CreateName('ImageC'));
|
|
end;
|
|
FResources.AddItem('ProcSet', FProcSet);
|
|
|
|
FContents := TPdfStream.CreateStream(FXref);
|
|
FXref.AddObject(FContents);
|
|
{$IFNDEF NOZLIB}
|
|
FFilter := FContents.Attributes.PdfArrayByName('Filter');
|
|
if FCompressionMethod = cmFlateDecode then
|
|
FFilter.AddItem(TPdfName.CreateName('FlateDecode'));
|
|
{$ENDIF}
|
|
FPage.AddItem('Contents', FContents);
|
|
|
|
FCanvas.SetPage(FPage);
|
|
end;
|
|
|
|
// FreeDoc
|
|
procedure TPdfDoc.FreeDoc;
|
|
var
|
|
i: integer;
|
|
begin
|
|
if FHasDoc then
|
|
begin
|
|
FXObjectList.Free;
|
|
|
|
for i := FFontList.Count - 1 downto 0 do
|
|
TObject(FFontList.Items[i]).Free;
|
|
FFontList.Free;
|
|
|
|
for i := FObjectList.Count - 1 downto 0 do
|
|
TObject(FObjectList.Items[i]).Free;
|
|
FObjectList.Free;
|
|
|
|
FXref.Free;
|
|
FHeader.Free;
|
|
FTrailer.Free;
|
|
|
|
FInfo := nil;
|
|
FRoot := nil;
|
|
FOutlineRoot := nil;
|
|
|
|
FHasDoc := false;
|
|
end;
|
|
end;
|
|
|
|
// SaveToStream
|
|
procedure TPdfDoc.SaveToStream(AStream: TStream);
|
|
var
|
|
i: integer;
|
|
Pos: integer;
|
|
PdfNumber: TPdfNumber;
|
|
begin
|
|
if not HasDoc or (FCanvas.Page = nil) then
|
|
raise EPdfInvalidOperation.Create('SaveToStream --there is no document to save.');
|
|
// write all objects to specified stream.
|
|
|
|
FInfo.ModDate := Now;
|
|
FRoot.SaveOpenAction;
|
|
|
|
// saving outline tree.
|
|
if UseOutlines then
|
|
FOutlineRoot.Save;
|
|
|
|
AStream.Position := 0;
|
|
FHeader.WriteToStream(AStream);
|
|
for i := 1 to FXref.ItemCount - 1 do
|
|
begin
|
|
Pos := AStream.Position;
|
|
FXref.Items[i].Value.WriteValueToStream(AStream);
|
|
FXref.Items[i].ByteOffset := Pos;
|
|
end;
|
|
FTrailer.XrefAddress := AStream.Position;
|
|
FXref.WriteToStream(AStream);
|
|
PdfNumber := FTrailer.Attributes.PdfNumberByName('Size');
|
|
PdfNumber.Value := FXref.ItemCount;
|
|
FTrailer.WriteToStream(AStream);
|
|
end;
|
|
|
|
// SetVirtualMode
|
|
procedure TPdfDoc.SetVirtualMode;
|
|
begin
|
|
NewDoc;
|
|
AddPage;
|
|
FCanvas.FIsVirtual := true;
|
|
end;
|
|
|
|
|
|
{ TPdfCanvasAttribute }
|
|
|
|
// SetWordSpace
|
|
procedure TPdfCanvasAttribute.SetWordSpace(Value: Single);
|
|
begin
|
|
if Value < 0 then
|
|
raise EPdfInvalidValue.Create('SetWordSpace --invalid word space');
|
|
if Value <> FWordSpace then
|
|
FWordSpace := Value;
|
|
end;
|
|
|
|
// SetCharSpace
|
|
procedure TPdfCanvasAttribute.SetCharSpace(Value: Single);
|
|
begin
|
|
if (Value < PDF_MIN_CHARSPACE) or (VALUE > PDF_MAX_CHARSPACE) then
|
|
raise EPdfInvalidValue.Create('SetCharSpace --invalid char space');
|
|
if Value <> FCharSpace then
|
|
FCharSpace := Value;
|
|
end;
|
|
|
|
// SetFontSize
|
|
procedure TPdfCanvasAttribute.SetFontSize(Value: Single);
|
|
begin
|
|
if (Value < 0) or (Value > PDF_MAX_FONTSIZE) then
|
|
raise EPdfInvalidValue.Create('SetCharSpace --invalid font size');
|
|
if Value <> FFontSize then
|
|
FFontSize := Value;
|
|
end;
|
|
|
|
// SetHorizontalScaling
|
|
procedure TPdfCanvasAttribute.SetHorizontalScaling(Value: Word);
|
|
begin
|
|
if (Value < PDF_MIN_HORIZONTALSCALING) or
|
|
(Value > PDF_MAX_HORIZONTALSCALING) then
|
|
raise EPdfInvalidValue.Create('SetHorizontalScaling --invalid font size');
|
|
if Value <> FHorizontalScaling then
|
|
FHorizontalScaling := Value;
|
|
end;
|
|
|
|
// SetLeading
|
|
procedure TPdfCanvasAttribute.SetLeading(Value: Single);
|
|
begin
|
|
if (Value < 0) or (Value > PDF_MAX_LEADING) then
|
|
raise EPdfInvalidValue.Create('SetLeading --invalid font size');
|
|
if Value <> FLeading then
|
|
FLeading := Value;
|
|
end;
|
|
|
|
// TextWidth
|
|
function TPdfCanvasAttribute.TextWidth(Text: string): Single;
|
|
var
|
|
i: integer;
|
|
ch: char;
|
|
tmpWidth: Single;
|
|
begin
|
|
Result := 0;
|
|
|
|
// calculate width of specified text from current attributes
|
|
for i := 1 to Length(Text) do
|
|
begin
|
|
ch := Text[i];
|
|
tmpWidth := FFont.GetCharWidth(Text, i) * FFontSize / 1000;
|
|
if FHorizontalScaling <> 100 then
|
|
tmpWidth := tmpWidth * FHorizontalScaling / 100;
|
|
if tmpWidth > 0 then
|
|
tmpWidth := tmpWidth + FCharSpace
|
|
else
|
|
tmpWidth := 0;
|
|
if (ch = ' ') and (FWordSpace > 0) and (i <> Length(Text)) then
|
|
tmpWidth := tmpWidth + FWordSpace;
|
|
|
|
Result := Result + tmpWidth;
|
|
end;
|
|
Result := Result - FCharSpace;
|
|
end;
|
|
|
|
// MeasureText
|
|
function TPdfCanvasAttribute.MeasureText(Text: string; Width: Single): integer;
|
|
var
|
|
i: integer;
|
|
ch: char;
|
|
tmpWidth: Single;
|
|
tmpTotalWidth: Single;
|
|
begin
|
|
Result := 0;
|
|
tmpTotalWidth := 0;
|
|
|
|
// calculate number of charactor contain in the specified width.
|
|
for i := 1 to Length(Text) do
|
|
begin
|
|
ch := Text[i];
|
|
tmpWidth := FFont.GetCharWidth(Text, i) * FFontSize / 1000;
|
|
if FHorizontalScaling <> 100 then
|
|
tmpWidth := tmpWidth * FHorizontalScaling / 100;
|
|
if tmpWidth > 0 then
|
|
tmpWidth := tmpWidth + FCharSpace
|
|
else
|
|
tmpWidth := 0;
|
|
if (ch = ' ') and (FWordSpace > 0) and (i <> Length(Text)) then
|
|
tmpWidth := tmpWidth + FWordSpace;
|
|
|
|
tmpTotalWidth := tmpTotalWidth + tmpWidth;
|
|
if tmpTotalWidth > Width then
|
|
Break;
|
|
inc(Result);
|
|
end;
|
|
end;
|
|
|
|
{ TPdfCanvas }
|
|
|
|
// Create
|
|
constructor TPdfCanvas.Create(APdfDoc: TPdfDoc);
|
|
begin
|
|
FPdfDoc := APdfDoc;
|
|
FPage := nil;
|
|
FContents := nil;
|
|
FAttr := TPdfCanvasAttribute.Create;
|
|
FIsVirtual := false;
|
|
end;
|
|
|
|
// Destroy
|
|
destructor TPdfCanvas.Destroy;
|
|
begin
|
|
FAttr.Free;
|
|
inherited;
|
|
end;
|
|
|
|
// SetPageWidth
|
|
procedure TPdfCanvas.SetPageWidth(AValue: integer);
|
|
var
|
|
FMediaBox: TPdfArray;
|
|
begin
|
|
FMediaBox := TPdfArray(Page.ValueByName('MediaBox'));
|
|
if FMediaBox <> nil then
|
|
TPdfNumber(FMediaBox.Items[2]).Value := AValue
|
|
else
|
|
EPdfInvalidOperation.Create('Can not chenge width of this page..');
|
|
end;
|
|
|
|
// SetPageHeight
|
|
procedure TPdfCanvas.SetPageHeight(AValue: integer);
|
|
var
|
|
FMediaBox: TPdfArray;
|
|
begin
|
|
FMediaBox := TPdfArray(Page.ValueByName('MediaBox'));
|
|
if FMediaBox <> nil then
|
|
TPdfNumber(FMediaBox.Items[3]).Value := AValue
|
|
else
|
|
EPdfInvalidOperation.Create('Can not chenge width of this page..');
|
|
end;
|
|
|
|
// WriteString
|
|
procedure TPdfCanvas.WriteString(S: string);
|
|
begin
|
|
if (not FIsVirtual) and (FContents <> nil) then
|
|
_WriteString(S, FContents.Stream);
|
|
end;
|
|
|
|
// GetPage
|
|
function TPdfCanvas.GetPage: TPdfDictionary;
|
|
begin
|
|
if FPage <> nil then
|
|
result := FPage
|
|
else
|
|
raise EPdfInvalidOperation.Create('GetPage --the Page is nil');
|
|
end;
|
|
|
|
// GetPageWidth
|
|
function TPdfCanvas.GetPageWidth: Integer;
|
|
var
|
|
FMediaBox: TPdfArray;
|
|
begin
|
|
FMediaBox := TPdfArray(Page.ValueByName('MediaBox'));
|
|
if FMediaBox <> nil then
|
|
result := TPdfNumber(FMediaBox.Items[2]).Value
|
|
else
|
|
result := FPdfDoc.DefaultPageWidth;
|
|
end;
|
|
|
|
// GetPageHeight
|
|
function TPdfCanvas.GetPageHeight: Integer;
|
|
var
|
|
FMediaBox: TPdfArray;
|
|
begin
|
|
FMediaBox := TPdfArray(Page.ValueByName('MediaBox'));
|
|
if FMediaBox <> nil then
|
|
result := TPdfNumber(FMediaBox.Items[3]).Value
|
|
else
|
|
result := FPdfDoc.DefaultPageHeight;
|
|
end;
|
|
|
|
// GetColorStr
|
|
function TPDFCanvas.GetColorStr(Color: TPdfColor): string;
|
|
var
|
|
X: array[0..3] of Byte;
|
|
rgb: integer;
|
|
begin
|
|
if Color > 0 then
|
|
rgb := integer(Color)
|
|
else
|
|
rgb := 0;
|
|
Move(rgb, x[0], 4);
|
|
result := _FloatToStrR(X[0] / 255) + ' ' +
|
|
_FloatToStrR(X[1] / 255) + ' ' +
|
|
_FloatToStrR(X[2] / 255);
|
|
end;
|
|
|
|
// SetPage
|
|
procedure TPdfCanvas.SetPage(APage: TPdfDictionary);
|
|
procedure GetCurrentFont;
|
|
var
|
|
AFont: TPdfName;
|
|
begin
|
|
AFont := Page.PdfNameByName('_Font');
|
|
with FAttr do
|
|
if AFont <> nil then
|
|
begin
|
|
Font := FPdfDoc.GetFont(AFont.Value);
|
|
FontSize := FPage.PdfNumberByName('_Font_Size').Value;
|
|
WordSpace := FPage.PdfRealByName('_Word_Space').Value;
|
|
CharSpace := FPage.PdfRealByName('_Char_Space').Value;
|
|
HorizontalScaling := FPage.PdfNumberByName('_HScalling').Value;
|
|
Leading := FPage.PdfNumberByName('_Leading').Value;
|
|
end
|
|
else
|
|
begin
|
|
Font := nil;
|
|
SetFont(PDF_DEFAULT_FONT, PDF_DEFAULT_FONT_SIZE);
|
|
CharSpace := 0;
|
|
WordSpace := 0;
|
|
HorizontalScaling := 100;
|
|
Leading := 0;
|
|
end;
|
|
end;
|
|
begin
|
|
// save current canvas attributes to internal objects.
|
|
if FPage <> nil then
|
|
with FPage do
|
|
begin
|
|
AddInternalItem('_Font', TPdfName.CreateName(FAttr.Font.Name));
|
|
AddInternalItem('_Font_Size', TPdfReal.CreateReal(FAttr.FontSize));
|
|
AddInternalItem('_Word_Space', TPdfReal.CreateReal(FAttr.WordSpace));
|
|
AddInternalItem('_Char_Space', TPdfReal.CreateReal(FAttr.CharSpace));
|
|
AddInternalItem('_HScalling', TPdfNumber.CreateNumber(FAttr.HorizontalScaling));
|
|
AddInternalItem('_Leading', TPdfReal.CreateReal(FAttr.Leading));
|
|
end;
|
|
FPage := APage;
|
|
FContents := TPdfStream(FPage.ValueByName('Contents'));
|
|
GetCurrentFont;
|
|
end;
|
|
|
|
// SetFont
|
|
procedure TPdfCanvas.SetFont(AName: string; ASize: Single);
|
|
var
|
|
FFont: TPdfFont;
|
|
FFontList: TPdfDictionary;
|
|
FFontName: string;
|
|
begin
|
|
// get font object from pdfdoc object, then find fontlist from page object
|
|
// by internal name. if font is not registered, register it to page object.
|
|
FFont := FPdfDoc.GetFont(AName);
|
|
if (FAttr.Font = FFont) and (FAttr.FontSize = ASize) then Exit;
|
|
FFontList := _Page_GetResources(FPage, 'Font');
|
|
FFontName := FFont.Data.PdfNameByName('Name').Value;
|
|
if FFontList.ValueByName(FFontName) = nil then
|
|
FFontList.AddItem(FFontName, FFont.Data);
|
|
if FContents <> nil then
|
|
SetFontAndSize('/' + FFontName, ASize);
|
|
FAttr.Font := FFont;
|
|
FAttr.FontSize := ASize;
|
|
end;
|
|
|
|
// TextOut
|
|
procedure TPdfCanvas.TextOut(X, Y: Single; Text: string);
|
|
begin
|
|
BeginText;
|
|
MoveTextPoint(X, Y);
|
|
ShowText(Text);
|
|
EndText;
|
|
end;
|
|
|
|
// TextRect
|
|
procedure TPdfCanvas.TextRect(ARect: TPdfRect; Text: string;
|
|
Alignment: TPdfAlignment; Clipping: boolean);
|
|
var
|
|
tmpWidth: Single;
|
|
XPos: Single;
|
|
begin
|
|
// calculate text width.
|
|
tmpWidth := TextWidth(Text);
|
|
|
|
case Alignment of
|
|
paCenter: XPos := Round((ARect.Right - ARect.Left - tmpWidth) / 2);
|
|
paRightJustify: XPos := ARect.Right - ARect.Left - Round(tmpWidth);
|
|
else
|
|
XPos := 0;
|
|
end;
|
|
|
|
// clipping client rect if needed.
|
|
if Clipping then
|
|
begin
|
|
GSave;
|
|
with ARect do
|
|
begin
|
|
MoveTo(Left, Top);
|
|
LineTo(Left, Bottom);
|
|
LineTo(Right, Bottom);
|
|
LineTo(Right, Top);
|
|
end;
|
|
ClosePath;
|
|
Clip;
|
|
NewPath;
|
|
end;
|
|
|
|
BeginText;
|
|
MoveTextPoint(ARect.Left + XPos, ARect.Top - FAttr.FontSize * 0.85);
|
|
ShowText(Text);
|
|
EndText;
|
|
|
|
if Clipping then
|
|
GRestore;
|
|
end;
|
|
|
|
// MultilineTextRect
|
|
procedure TPdfCanvas.MultilineTextRect(ARect: TPdfRect;
|
|
Text: string; WordWrap: boolean);
|
|
var
|
|
i: integer;
|
|
S1, S2: string;
|
|
XPos, YPos: Single;
|
|
tmpXPos: Single;
|
|
tmpWidth: Single;
|
|
ln: integer;
|
|
FourceReturn: boolean;
|
|
FText: string;
|
|
|
|
procedure InternalShowText(S: string; AWidth: Single);
|
|
var
|
|
i: Integer;
|
|
begin
|
|
i := MeasureText(S, AWidth);
|
|
S := Copy(S, 1, i);
|
|
ShowText(S);
|
|
end;
|
|
|
|
begin
|
|
YPos := ARect.Top - FAttr.FontSize*0.85;
|
|
XPos := ARect.Left;
|
|
FText := Text;
|
|
|
|
BeginText;
|
|
|
|
MoveTextPoint(XPos, YPos);
|
|
i := 1;
|
|
S2 := GetNextWord(FText, i);
|
|
XPos := XPos + TextWidth(S2);
|
|
if (Length(S2) > 0) and (S2[Length(S2)] = ' ') then
|
|
XPos := XPos + FAttr.WordSpace;
|
|
|
|
while i <= Length(FText) do
|
|
begin
|
|
ln := Length(S2);
|
|
if (ln >= 2) and (S2[ln] = #10) and (S2[ln-1] = #13) then
|
|
begin
|
|
S2 := Copy(S2, 1, ln - 2);
|
|
FourceReturn := true;
|
|
end
|
|
else
|
|
FourceReturn := false;
|
|
|
|
S1 := GetNextWord(FText, i);
|
|
tmpWidth := TextWidth(S1);
|
|
TmpXPos := XPos + tmpWidth;
|
|
|
|
if (WordWrap and (TmpXPos > ARect.Right)) or
|
|
FourceReturn then
|
|
begin
|
|
if S2 <> '' then
|
|
InternalShowText(S2, ARect.Right - ARect.Left);
|
|
S2 := '';
|
|
MoveToNextLine;
|
|
ARect.Top := ARect.Top - FAttr.Leading;
|
|
if ARect.Top < ARect.Bottom + FAttr.FontSize then
|
|
Break;
|
|
XPos := ARect.Left;
|
|
end;
|
|
XPos := XPos + tmpWidth;
|
|
if (Length(S1) > 0) and (S1[Length(S1)] = ' ') then
|
|
XPos := XPos + FAttr.WordSpace;
|
|
S2 := S2 + S1;
|
|
end;
|
|
|
|
if S2 <> '' then
|
|
InternalShowText(S2, ARect.Right - ARect.Left);
|
|
EndText;
|
|
end;
|
|
|
|
// DrawXObject
|
|
procedure TPdfCanvas.DrawXObject(X, Y, AWidth, AHeight: Single;
|
|
AXObjectName: string);
|
|
var
|
|
XObject: TPdfXObject;
|
|
FXObjectList: TPdfDictionary;
|
|
begin
|
|
// drawing object must be registered. check object name.
|
|
XObject := FPdfDoc.GetXObject(AXObjectName);
|
|
if XObject = nil then
|
|
raise EPdfInvalidValue.CreateFmt('DrawXObject --XObject not found: %s', [AXObjectName]);
|
|
|
|
FXObjectList := _Page_GetResources(FPage, 'XObject');
|
|
if FXObjectList.ValueByName(AXObjectName) = nil then
|
|
FXObjectList.AddItem(AXObjectName, XObject);
|
|
|
|
GSave;
|
|
Concat(AWidth, 0, 0, AHeight, X, Y);
|
|
ExecuteXObject(XObject.Attributes.PdfNameByName('Name').Value);
|
|
GRestore;
|
|
end;
|
|
|
|
// DrawXObjectEx
|
|
procedure TPdfCanvas.DrawXObjectEx(X, Y, AWidth, AHeight: Single;
|
|
ClipX, ClipY, ClipWidth, ClipHeight: Single; AXObjectName: string);
|
|
var
|
|
XObject: TPdfXObject;
|
|
FXObjectList: TPdfDictionary;
|
|
begin
|
|
// drawing object must be registered. check object name.
|
|
XObject := FPdfDoc.GetXObject(AXObjectName);
|
|
if XObject = nil then
|
|
raise EPdfInvalidValue.CreateFmt('DrawXObjectEx --XObject not found: %s', [AXObjectName]);
|
|
|
|
FXObjectList := _Page_GetResources(FPage, 'XObject');
|
|
if FXObjectList.ValueByName(AXObjectName) = nil then
|
|
FXObjectList.AddItem(AXObjectName, XObject);
|
|
|
|
GSave;
|
|
Rectangle(ClipX, ClipY, ClipWidth, ClipHeight);
|
|
Clip;
|
|
NewPath;
|
|
Concat(AWidth, 0, 0, AHeight, X, Y);
|
|
ExecuteXObject(XObject.Attributes.PdfNameByName('Name').Value);
|
|
GRestore;
|
|
end;
|
|
|
|
{* Special Graphics State *}
|
|
|
|
// GSave
|
|
procedure TPdfCanvas.GSave;
|
|
begin
|
|
WriteString('q'#10);
|
|
end;
|
|
|
|
// GRestore
|
|
procedure TPdfCanvas.GRestore;
|
|
begin
|
|
WriteString('Q'#10);
|
|
end;
|
|
|
|
// Concat
|
|
procedure TPdfCanvas.Concat(a, b, c, d, e, f: Single);
|
|
var
|
|
S: string;
|
|
begin
|
|
S := _FloatToStrR(a) + ' ' +
|
|
_FloatToStrR(b) + ' ' +
|
|
_FloatToStrR(c) + ' ' +
|
|
_FloatToStrR(d) + ' ' +
|
|
_FloatToStrR(e) + ' ' +
|
|
_FloatToStrR(f) + ' cm'#10;
|
|
WriteString(S);
|
|
end;
|
|
|
|
{* General Graphics State *}
|
|
|
|
// SetFlat
|
|
procedure TPdfCanvas.SetFlat(flatness: Byte);
|
|
var
|
|
S: string;
|
|
begin
|
|
S := IntToStr(flatness) + ' i'#10;
|
|
WriteString(S);
|
|
end;
|
|
|
|
// SetLineCap
|
|
procedure TPdfCanvas.SetLineCap(linecap: TLineCapStyle);
|
|
var
|
|
S: string;
|
|
begin
|
|
S := IntToStr(ord(linecap)) + ' J'#10;
|
|
WriteString(S);
|
|
end;
|
|
|
|
// SetDash
|
|
procedure TPdfCanvas.SetDash(aarray: array of Byte; phase: Byte);
|
|
var
|
|
S: string;
|
|
i: integer;
|
|
begin
|
|
S := '[';
|
|
if (High(aarray) >= 0) and (aarray[0] <> 0) then
|
|
for i := 0 to High(aarray) do
|
|
S := S + IntToStr(aarray[i]) + ' ';
|
|
S := S + '] ' + IntToStr(phase) + ' d'#10;
|
|
WriteString(S);
|
|
end;
|
|
|
|
// SetLineJoin
|
|
procedure TPdfCanvas.SetLineJoin(linejoin: TLineJoinStyle);
|
|
var
|
|
S: string;
|
|
begin
|
|
S := IntToStr(ord(linejoin)) + ' j'#10;
|
|
WriteString(S);
|
|
end;
|
|
|
|
// SetLineWidth
|
|
procedure TPdfCanvas.SetLineWidth(linewidth: Single);
|
|
var
|
|
S: string;
|
|
begin
|
|
S := _FloatToStrR(linewidth) + ' w'#10;
|
|
WriteString(S);
|
|
end;
|
|
|
|
// SetMiterLimit
|
|
procedure TPdfCanvas.SetMiterLimit(miterlimit: Byte);
|
|
var
|
|
S: string;
|
|
begin
|
|
S := IntToStr(miterlimit) + ' M'#10;
|
|
WriteString(S);
|
|
end;
|
|
|
|
{* Paths *}
|
|
|
|
// MoveTo
|
|
procedure TPdfCanvas.MoveTo(x, y: Single);
|
|
var
|
|
S: string;
|
|
begin
|
|
S := _FloatToStrR(x) + ' ' + _FloatToStrR(y) + ' m'#10;
|
|
WriteString(S);
|
|
end;
|
|
|
|
// LineTo
|
|
procedure TPdfCanvas.LineTo(x, y: Single);
|
|
var
|
|
S: string;
|
|
begin
|
|
S := _FloatToStrR(x) + ' ' + _FloatToStrR(y) + ' l'#10;
|
|
WriteString(S);
|
|
end;
|
|
|
|
// CurveToC
|
|
procedure TPdfCanvas.CurveToC(x1, y1, x2, y2, x3, y3: Single);
|
|
var
|
|
S: string;
|
|
begin
|
|
S := _FloatToStrR(x1) + ' ' +
|
|
_FloatToStrR(y1) + ' ' +
|
|
_FloatToStrR(x2) + ' ' +
|
|
_FloatToStrR(y2) + ' ' +
|
|
_FloatToStrR(x3) + ' ' +
|
|
_FloatToStrR(y3) + ' c'#10;
|
|
WriteString(S);
|
|
end;
|
|
|
|
// CurveToV
|
|
procedure TPdfCanvas.CurveToV(x2, y2, x3, y3: Single);
|
|
var
|
|
S: string;
|
|
begin
|
|
S := _FloatToStrR(x2) + ' ' +
|
|
_FloatToStrR(y2) + ' ' +
|
|
_FloatToStrR(x3) + ' ' +
|
|
_FloatToStrR(y3) + ' v'#10;
|
|
WriteString(S);
|
|
end;
|
|
|
|
// CurveToY
|
|
procedure TPdfCanvas.CurveToY(x1, y1, x3, y3: Single);
|
|
var
|
|
S: string;
|
|
begin
|
|
S := _FloatToStrR(x1) + ' ' +
|
|
_FloatToStrR(y1) + ' ' +
|
|
_FloatToStrR(x3) + ' ' +
|
|
_FloatToStrR(y3) + ' y'#10;
|
|
WriteString(S);
|
|
end;
|
|
|
|
// Rectangle
|
|
procedure TPdfCanvas.Rectangle(x, y, width, height: Single);
|
|
var
|
|
S: string;
|
|
begin
|
|
S := _FloatToStrR(x) + ' ' +
|
|
_FloatToStrR(y) + ' ' +
|
|
_FloatToStrR(width) + ' ' +
|
|
_FloatToStrR(height) + ' re'#10;
|
|
WriteString(S);
|
|
end;
|
|
|
|
// Closepath
|
|
procedure TPdfCanvas.Closepath;
|
|
begin
|
|
WriteString('h'#10);
|
|
end;
|
|
|
|
// NewPath
|
|
procedure TPdfCanvas.NewPath;
|
|
begin
|
|
WriteString('n'#10);
|
|
end;
|
|
|
|
// Stroke
|
|
procedure TPdfCanvas.Stroke;
|
|
begin
|
|
WriteString('S'#10);
|
|
end;
|
|
|
|
// ClosePathStroke
|
|
procedure TPdfCanvas.ClosePathStroke;
|
|
begin
|
|
WriteString('s'#10);
|
|
end;
|
|
|
|
// Fill
|
|
procedure TPdfCanvas.Fill;
|
|
begin
|
|
WriteString('f'#10);
|
|
end;
|
|
|
|
// Eofill
|
|
procedure TPdfCanvas.Eofill;
|
|
begin
|
|
WriteString('f*'#10);
|
|
end;
|
|
|
|
// FillStroke
|
|
procedure TPdfCanvas.FillStroke;
|
|
begin
|
|
WriteString('B'#10);
|
|
end;
|
|
|
|
// ClosepathFillStroke
|
|
procedure TPdfCanvas.ClosepathFillStroke;
|
|
begin
|
|
WriteString('b'#10);
|
|
end;
|
|
|
|
// EofillStroke
|
|
procedure TPdfCanvas.EofillStroke;
|
|
begin
|
|
WriteString('B*'#10);
|
|
end;
|
|
|
|
// ClosepathEofillStroke
|
|
procedure TPdfCanvas.ClosepathEofillStroke;
|
|
begin
|
|
WriteString('b*'#10);
|
|
end;
|
|
|
|
// Clip
|
|
procedure TPdfCanvas.Clip;
|
|
begin
|
|
WriteString('W'#10);
|
|
end;
|
|
|
|
// Eoclip
|
|
procedure TPdfCanvas.Eoclip;
|
|
begin
|
|
WriteString('W*'#10);
|
|
end;
|
|
|
|
{* Test state *}
|
|
|
|
// SetCharSpace
|
|
procedure TPdfCanvas.SetCharSpace(charSpace: Single);
|
|
begin
|
|
if FAttr.CharSpace = charSpace then Exit;
|
|
FAttr.SetCharSpace(charSpace);
|
|
if Contents <> nil then
|
|
WriteString(_FloatToStrR(charSpace) + ' Tc'#10);
|
|
end;
|
|
|
|
// SetWordSpace
|
|
procedure TPdfCanvas.SetWordSpace(wordSpace: Single);
|
|
begin
|
|
if FAttr.WordSpace = wordSpace then Exit;
|
|
FAttr.SetWordSpace(wordSpace);
|
|
if Contents <> nil then
|
|
WriteString(_FloatToStrR(wordSpace) + ' Tw'#10);
|
|
end;
|
|
|
|
// SetHorizontalScaling
|
|
procedure TPdfCanvas.SetHorizontalScaling(hScaling: Word);
|
|
begin
|
|
if FAttr.HorizontalScaling = hScaling then Exit;
|
|
FAttr.SetHorizontalScaling(hScaling);
|
|
WriteString(IntToStr(hScaling) + ' Tz'#10);
|
|
end;
|
|
|
|
// SetLeading
|
|
procedure TPdfCanvas.SetLeading(leading: Single);
|
|
begin
|
|
if FAttr.Leading = leading then Exit;
|
|
FAttr.SetLeading(leading);
|
|
WriteString(_FloatToStrR(leading) + ' TL'#10);
|
|
end;
|
|
|
|
// SetFontAndSize
|
|
procedure TPdfCanvas.SetFontAndSize(fontname: string; size: Single);
|
|
var
|
|
S: string;
|
|
begin
|
|
S := fontname + ' ' +
|
|
_FloatToStrR(size) + ' Tf'#10;
|
|
WriteString(S);
|
|
end;
|
|
|
|
// SetTextRenderingMode
|
|
procedure TPdfCanvas.SetTextRenderingMode(mode: TTextRenderingMode);
|
|
begin
|
|
WriteString(IntToStr(ord(mode)) + ' Tr'#10);
|
|
end;
|
|
|
|
// SetTextRise
|
|
procedure TPdfCanvas.SetTextRise(rise: Word);
|
|
begin
|
|
WriteString(IntToStr(rise) + ' Ts'#10);
|
|
end;
|
|
|
|
// BeginText
|
|
procedure TPdfCanvas.BeginText;
|
|
begin
|
|
WriteString('BT'#10);
|
|
end;
|
|
|
|
// EndText
|
|
procedure TPdfCanvas.EndText;
|
|
begin
|
|
WriteString('ET'#10);
|
|
end;
|
|
|
|
// MoveTextPoint
|
|
procedure TPdfCanvas.MoveTextPoint(tx, ty: Single);
|
|
var
|
|
S: string;
|
|
begin
|
|
S := _FloatToStrR(tx) + ' ' +
|
|
_FloatToStrR(ty) + ' Td'#10;
|
|
WriteString(S);
|
|
end;
|
|
|
|
// SetTextMatrix
|
|
procedure TPdfCanvas.SetTextMatrix(a, b, c, d, x, y: Word);
|
|
var
|
|
S: string;
|
|
begin
|
|
S := IntToStr(a) + ' ' +
|
|
IntToStr(b) + ' ' +
|
|
IntToStr(c) + ' ' +
|
|
IntToStr(d) + ' ' +
|
|
IntToStr(x) + ' ' +
|
|
IntToStr(y) + ' Tm'#10;
|
|
WriteString(S);
|
|
end;
|
|
|
|
// MoveToNextLine
|
|
procedure TPdfCanvas.MoveToNextLine;
|
|
begin
|
|
WriteString('T*'#10);
|
|
end;
|
|
|
|
// ShowText
|
|
procedure TPdfCanvas.ShowText(s: string);
|
|
var
|
|
FString: string;
|
|
begin
|
|
if _HasMultiByteString(s) then
|
|
FString := '<' + _StrToHex(s) + '>'
|
|
else
|
|
FString := '(' + _EscapeText(s) + ')';
|
|
WriteString(FString + ' Tj'#10);
|
|
end;
|
|
|
|
// ShowTextNextLine
|
|
procedure TPdfCanvas.ShowTextNextLine(s: string);
|
|
var
|
|
FString: string;
|
|
begin
|
|
if _HasMultiByteString(s) then
|
|
FString := '<' + _StrToHex(s) + '>'
|
|
else
|
|
FString := '(' + _EscapeText(s) + ')';
|
|
WriteString(FString + ' '''#10);
|
|
end;
|
|
|
|
{* external objects *}
|
|
|
|
// ExecuteXObject
|
|
procedure TPdfCanvas.ExecuteXObject(xObject: string);
|
|
var
|
|
S: string;
|
|
begin
|
|
S := '/' + xObject + ' Do'#10;
|
|
WriteString(S);
|
|
end;
|
|
|
|
{* Device-dependent color space operators *}
|
|
|
|
// SetRGBFillColor
|
|
procedure TPdfCanvas.SetRGBFillColor(Value: TPdfColor);
|
|
var
|
|
S: string;
|
|
begin
|
|
S := GetColorStr(Value) + ' rg'#10;
|
|
WriteString(S);
|
|
end;
|
|
|
|
// SetRGBStrokeColor
|
|
procedure TPdfCanvas.SetRGBStrokeColor(Value: TPdfColor);
|
|
var
|
|
S: string;
|
|
begin
|
|
S := GetColorStr(Value) + ' RG'#10;
|
|
WriteString(S);
|
|
end;
|
|
|
|
{ TPdfCanvas common routine }
|
|
|
|
// TextWidth
|
|
function TPdfCanvas.TextWidth(Text: string): Single;
|
|
begin
|
|
result := FAttr.TextWidth(Text);
|
|
end;
|
|
|
|
// MeasureText
|
|
function TPdfCanvas.MeasureText(Text: string; AWidth: Single): integer;
|
|
begin
|
|
result := FAttr.MeasureText(Text, AWidth);
|
|
end;
|
|
|
|
// Ellipse
|
|
procedure TPdfCanvas.Ellipse(x, y, width, height: Single);
|
|
begin
|
|
MoveTo(x, y+height/2);
|
|
CurveToC(x,
|
|
y+height/2-height/2*11/20,
|
|
x+width/2-width/2*11/20,
|
|
y,
|
|
x+width/2,
|
|
y);
|
|
CurveToC(x+width/2+width/2*11/20,
|
|
y,
|
|
x+width,
|
|
y+height/2-height/2*11/20,
|
|
x+width,
|
|
y+height/2);
|
|
CurveToC(x+width,
|
|
y+height/2+height/2*11/20,
|
|
x+width/2+width/2*11/20,
|
|
y+height,
|
|
x+width/2,
|
|
y+height);
|
|
CurveToC(x+width/2-width/2*11/20,
|
|
y+height,
|
|
x,
|
|
y+height/2+height/2*11/20,
|
|
x,
|
|
y+height/2);
|
|
end;
|
|
|
|
// GetNextWord
|
|
function TPdfCanvas.GetNextWord(const S: string;
|
|
var Index: integer): string;
|
|
var
|
|
ln: integer;
|
|
i: integer;
|
|
begin
|
|
// getting a word from text.
|
|
result := '';
|
|
ln := Length(S);
|
|
if Index > ln then
|
|
Exit;
|
|
i := Index;
|
|
while true do
|
|
if (S[i] = #10) and (S[i-1] = #13) or (S[i] = ' ') then
|
|
begin
|
|
result := Copy(S, Index, i - (Index -1));
|
|
break;
|
|
end
|
|
else
|
|
if i >= ln then
|
|
begin
|
|
result := Copy(S, Index, i - (Index - 1));
|
|
break;
|
|
end
|
|
{$IFDEF USE_JPFONTS}
|
|
else
|
|
if ByteType(S, i) = mbTrailByte then
|
|
if ((Copy(S, i+1, 2) <> #129#66) and
|
|
(Copy(S, i+1, 2) <> #129#65)) then
|
|
begin
|
|
result := Copy(S, Index, i - (Index - 1));
|
|
break;
|
|
end
|
|
else
|
|
inc(i)
|
|
else
|
|
if ((i < ln) and (ByteType(S, i + 1) = mbLeadByte)) then
|
|
begin
|
|
result := Copy(S, Index, i - (Index - 1));
|
|
break;
|
|
end
|
|
{$ENDIF}
|
|
else
|
|
inc(i);
|
|
|
|
Index := i + 1;
|
|
end;
|
|
|
|
// GetDoc
|
|
function TPdfCanvas.GetDoc: TPdfDoc;
|
|
begin
|
|
result := nil;
|
|
if FPdfDoc <> nil then
|
|
result := FPdfDoc
|
|
else
|
|
EPdfInvalidOperation.Create('ERROR: GetDoc documant is nil.');
|
|
end;
|
|
|
|
{ TPdfDictionaryWrapper }
|
|
|
|
// SetData
|
|
procedure TPdfDictionaryWrapper.SetData(AData: TPdfDictionary);
|
|
begin
|
|
FData := AData;
|
|
end;
|
|
|
|
// GetHasData
|
|
function TPdfDictionaryWrapper.GetHasData: boolean;
|
|
begin
|
|
result := (FData = nil);
|
|
end;
|
|
|
|
{ TPdfInfo }
|
|
|
|
// SetAuthor
|
|
procedure TPdfInfo.SetAuthor(Value: string);
|
|
begin
|
|
FData.AddItem('Author', TPdfText.CreateText(Value));
|
|
end;
|
|
|
|
// SetCreationDate
|
|
procedure TPdfInfo.SetCreationDate(Value: TDateTime);
|
|
begin
|
|
FData.AddItem('CreationDate', TPdfText.CreateText(_DateTimeToPdfDate(Value)));
|
|
end;
|
|
|
|
// SetModDate
|
|
procedure TPdfInfo.SetModDate(Value: TDateTime);
|
|
begin
|
|
FData.AddItem('ModDate', TPdfText.CreateText(_DateTimeToPdfDate(Value)));
|
|
end;
|
|
|
|
// SetCreator
|
|
procedure TPdfInfo.SetCreator(Value: string);
|
|
begin
|
|
FData.AddItem('Creator', TPdfText.CreateText(Value));
|
|
end;
|
|
|
|
// SetTitle
|
|
procedure TPdfInfo.SetTitle(Value: string);
|
|
begin
|
|
FData.AddItem('Title', TPdfText.CreateText(Value));
|
|
end;
|
|
|
|
// SetSubject
|
|
procedure TPdfInfo.SetSubject(Value: string);
|
|
begin
|
|
FData.AddItem('Subject', TPdfText.CreateText(Value));
|
|
end;
|
|
|
|
// SetKeywords
|
|
procedure TPdfInfo.SetKeywords(Value: string);
|
|
begin
|
|
FData.AddItem('Keywords', TPdfText.CreateText(Value));
|
|
end;
|
|
|
|
// GetAuthor
|
|
function TPdfInfo.GetAuthor: string;
|
|
begin
|
|
if FData.ValueByName('Author') <> nil then
|
|
result := FData.PdfTextByName('Author').Value
|
|
else
|
|
result := '';
|
|
end;
|
|
|
|
// GetCreationDate
|
|
function TPdfInfo.GetCreationDate: TDateTime;
|
|
begin
|
|
if FData.ValueByName('CreationDate') <> nil then
|
|
try
|
|
result := _PdfDateToDateTime(FData.PdfTextByName('CreationDate').Value);
|
|
except
|
|
result := 0;
|
|
end
|
|
else
|
|
result := 0;
|
|
end;
|
|
|
|
// GetModDate
|
|
function TPdfInfo.GetModDate: TDateTime;
|
|
begin
|
|
if FData.ValueByName('ModDate') <> nil then
|
|
try
|
|
result := _PdfDateToDateTime(FData.PdfTextByName('ModDate').Value);
|
|
except
|
|
result := 0;
|
|
end
|
|
else
|
|
result := 0;
|
|
end;
|
|
|
|
// GetCreator
|
|
function TPdfInfo.GetCreator: string;
|
|
begin
|
|
if FData.ValueByName('Creator') <> nil then
|
|
result := FData.PdfTextByName('Creator').Value
|
|
else
|
|
result := '';
|
|
end;
|
|
|
|
// GetTitle
|
|
function TPdfInfo.GetTitle: string;
|
|
begin
|
|
if FData.ValueByName('Title') <> nil then
|
|
result := FData.PdfTextByName('Title').Value
|
|
else
|
|
result := '';
|
|
end;
|
|
|
|
// GetSubject
|
|
function TPdfInfo.GetSubject: string;
|
|
begin
|
|
if FData.ValueByName('Subject') <> nil then
|
|
result := FData.PdfTextByName('Subject').Value
|
|
else
|
|
result := '';
|
|
end;
|
|
|
|
// GetKeywords
|
|
function TPdfInfo.GetKeywords: string;
|
|
begin
|
|
if FData.ValueByName('Keywords') <> nil then
|
|
result := FData.PdfTextByName('Keywords').Value
|
|
else
|
|
result := '';
|
|
end;
|
|
|
|
{ TPdfCatalog }
|
|
|
|
// SaveOpenAction
|
|
procedure TPdfCatalog.SaveOpenAction;
|
|
begin
|
|
if (FOpenAction = nil) then
|
|
FData.RemoveItem('OpenAction')
|
|
else
|
|
FData.AddItem('OpenAction', FOpenAction.GetValue);
|
|
end;
|
|
|
|
// SetPageLayout
|
|
procedure TPdfCatalog.SetPageLayout(Value: TPdfPageLayout);
|
|
var
|
|
FPageLayout: TPdfName;
|
|
begin
|
|
FPageLayout := TPdfName(FData.ValueByName('PageLayout'));
|
|
if (FPageLayout = nil) or (not (FPageLayout is TPdfName)) then
|
|
FData.AddItem('PageLayout', TPdfName.CreateName(PDF_PAGE_LAYOUT_NAMES[Ord(Value)]))
|
|
else
|
|
FPageLayout.Value := PDF_PAGE_LAYOUT_NAMES[Ord(Value)];
|
|
end;
|
|
|
|
// GetPageLayout
|
|
function TPdfCatalog.GetPageLayout: TPdfPageLayout;
|
|
var
|
|
FPageLayout: TPdfName;
|
|
S: string;
|
|
i: integer;
|
|
begin
|
|
result := plSinglePage;
|
|
FPageLayout := TPdfName(FData.ValueByName('PageLayout'));
|
|
if (FPageLayout = nil) or (not (FPageLayout is TPdfName)) then
|
|
Exit
|
|
else
|
|
begin
|
|
S := FPageLayout.Value;
|
|
for i := 0 to High(PDF_PAGE_LAYOUT_NAMES) do
|
|
if PDF_PAGE_LAYOUT_NAMES[i] = S then
|
|
begin
|
|
result := TPdfPageLayout(i);
|
|
Break;
|
|
end;
|
|
end;
|
|
end;
|
|
|
|
function TPdfCatalog.GetNonFullScreenPageMode: TPdfPageMode;
|
|
var
|
|
FDictionary: TPdfDictionary;
|
|
FPageMode: TPdfName;
|
|
S: string;
|
|
i: integer;
|
|
begin
|
|
result := pmUseNone;
|
|
FDictionary := TPdfDictionary(FData.ValueByName('NonFullScreenPageMode'));
|
|
|
|
if FDictionary = nil then
|
|
Exit;
|
|
|
|
FPageMode := TPdfName(FDictionary.ValueByName('NonFullScreenPageMode'));
|
|
if (FPageMode = nil) or (not (FPageMode is TPdfName)) then
|
|
Exit;
|
|
|
|
S := FPageMode.Value;
|
|
for i := 0 to High(PDF_PAGE_MODE_NAMES) do
|
|
if PDF_PAGE_MODE_NAMES[i] = S then
|
|
begin
|
|
result := TPdfPageMode(i);
|
|
Break;
|
|
end;
|
|
end;
|
|
|
|
function TPdfCatalog.GetViewerPreference: TPdfViewerPreferences;
|
|
var
|
|
FDictionary: TPdfDictionary;
|
|
FValue: TPdfBoolean;
|
|
begin
|
|
result := [];
|
|
FDictionary := TPdfDictionary(FData.ValueByName('ViewerPreference'));
|
|
|
|
if FDictionary = nil then
|
|
Exit;
|
|
|
|
FValue := FData.PdfBooleanByName('HideToolbar');
|
|
if (FValue <> nil) or FValue.Value then
|
|
result := result + [vpHideToolbar];
|
|
|
|
FValue := FData.PdfBooleanByName('HideMenubar');
|
|
if (FValue <> nil) or FValue.Value then
|
|
result := result + [vpHideMenubar];
|
|
|
|
FValue := FData.PdfBooleanByName('HideWindowUI');
|
|
if (FValue <> nil) or FValue.Value then
|
|
result := result + [vpHideWindowUI];
|
|
|
|
FValue := FData.PdfBooleanByName('FitWindow');
|
|
if (FValue <> nil) or FValue.Value then
|
|
result := result + [vpFitWindow];
|
|
|
|
FValue := FData.PdfBooleanByName('CenterWindow');
|
|
if (FValue <> nil) or FValue.Value then
|
|
result := result + [vpCenterWindow];
|
|
end;
|
|
|
|
// SetPageMode
|
|
procedure TPdfCatalog.SetPageMode(Value: TPdfPageMode);
|
|
var
|
|
FPageMode: TPdfName;
|
|
begin
|
|
FPageMode := TPdfName(FData.ValueByName('PageMode'));
|
|
if (FPageMode = nil) or (not (FPageMode is TPdfName)) then
|
|
FData.AddItem('PageMode', TPdfName.CreateName(PDF_PAGE_MODE_NAMES[Ord(Value)]))
|
|
else
|
|
FPageMode.Value := PDF_PAGE_MODE_NAMES[Ord(Value)];
|
|
end;
|
|
|
|
procedure TPdfCatalog.SetNonFullScreenPageMode(Value: TPdfPageMode);
|
|
var
|
|
FDictionary: TPdfDictionary;
|
|
FPageMode: TPdfName;
|
|
begin
|
|
FDictionary := TPdfDictionary(FData.ValueByName('ViewerPreferences'));
|
|
|
|
if FDictionary = nil then
|
|
begin
|
|
FDictionary := TPdfDictionary.CreateDictionary(Data.ObjectMgr);
|
|
Data.AddItem('ViewerPreferences', FDictionary);
|
|
end;
|
|
|
|
// if Value is pmFullScreen, remove 'PageMode' element(use default value).
|
|
if (Value = pmFullScreen) or (Value = pmUseNone) then
|
|
FDictionary.RemoveItem('NonFullScreenPageMode')
|
|
else
|
|
begin
|
|
FPageMode := TPdfName(FDictionary.ValueByName('NonFullScreenPageMode'));
|
|
if (FPageMode = nil) or (not (FPageMode is TPdfName)) then
|
|
FDictionary.AddItem('NonFullScreenPageMode',
|
|
TPdfName.CreateName(PDF_PAGE_MODE_NAMES[Ord(Value)]))
|
|
else
|
|
FPageMode.Value := PDF_PAGE_MODE_NAMES[Ord(Value)];
|
|
end;
|
|
end;
|
|
|
|
procedure TPdfCatalog.SetViewerPreference(Value: TPdfViewerPreferences);
|
|
var
|
|
FDictionary: TPdfDictionary;
|
|
begin
|
|
FDictionary := TPdfDictionary(FData.ValueByName('ViewerPreferences'));
|
|
|
|
if (FDictionary = nil) and (Value <> []) then
|
|
begin
|
|
FDictionary := TPdfDictionary.CreateDictionary(Data.ObjectMgr);
|
|
FData.AddItem('ViewerPreferences', FDictionary);
|
|
end;
|
|
|
|
if (vpHideToolbar in Value) then
|
|
FDictionary.AddItem('HideToolbar', TPdfBoolean.CreateBoolean(true))
|
|
else
|
|
FDictionary.RemoveItem('HideToolbar');
|
|
|
|
if (vpHideMenubar in Value) then
|
|
FDictionary.AddItem('HideMenubar', TPdfBoolean.CreateBoolean(true))
|
|
else
|
|
FDictionary.RemoveItem('HideMenubar');
|
|
|
|
if (vpHideWindowUI in Value) then
|
|
FDictionary.AddItem('HideWindowUI', TPdfBoolean.CreateBoolean(true))
|
|
else
|
|
FDictionary.RemoveItem('HideWindowUI');
|
|
|
|
if (vpFitWindow in Value) then
|
|
FDictionary.AddItem('FitWindow', TPdfBoolean.CreateBoolean(true))
|
|
else
|
|
FDictionary.RemoveItem('FitWindow');
|
|
|
|
if (vpCenterWindow in Value) then
|
|
FDictionary.AddItem('CenterWindow', TPdfBoolean.CreateBoolean(true))
|
|
else
|
|
FDictionary.RemoveItem('CenterWindow');
|
|
end;
|
|
|
|
// GetPageMode
|
|
function TPdfCatalog.GetPageMode: TPdfPageMode;
|
|
var
|
|
FPageMode: TPdfName;
|
|
S: string;
|
|
i: integer;
|
|
begin
|
|
result := pmUseNone;
|
|
FPageMode := TPdfName(FData.ValueByName('PageMode'));
|
|
if (FPageMode = nil) or (not (FPageMode is TPdfName)) then
|
|
Exit
|
|
else
|
|
begin
|
|
S := FPageMode.Value;
|
|
for i := 0 to High(PDF_PAGE_MODE_NAMES) do
|
|
if PDF_PAGE_MODE_NAMES[i] = S then
|
|
begin
|
|
result := TPdfPageMode(i);
|
|
Break;
|
|
end;
|
|
end;
|
|
end;
|
|
|
|
// GetPages
|
|
function TPdfCatalog.GetPages: TPdfDictionary;
|
|
begin
|
|
result := TPdfDictionary(FData.ValueByName('Pages'));
|
|
if result = nil then
|
|
raise EPdfInvalidOperation.Create('GetPages --page object is null..');
|
|
end;
|
|
|
|
// SetPages
|
|
procedure TPdfCatalog.SetPages(APage: TPdfDictionary);
|
|
begin
|
|
if _GetTypeOf(APage) = 'Pages' then
|
|
FData.AddItem('Pages', APage);
|
|
end;
|
|
|
|
{ TPdfFont }
|
|
|
|
// AddStrElements
|
|
procedure TPdfFont.AddStrElements(ADic: TPdfDictionary;
|
|
ATable: array of TPDF_STR_TBL);
|
|
var
|
|
i: integer;
|
|
begin
|
|
{ utility routine for making font dictinary. }
|
|
for i := 0 to High(ATable) do
|
|
ADic.AddItem(ATable[i].KEY, TPdfName.CreateName(ATable[i].VAL));
|
|
end;
|
|
|
|
// AddIntElements
|
|
procedure TPdfFont.AddIntElements(ADic: TPdfDictionary;
|
|
ATable: array of TPDF_INT_TBL);
|
|
var
|
|
i: integer;
|
|
begin
|
|
{ utility routine for making font dictionary. }
|
|
for i := 0 to High(ATable) do
|
|
ADic.AddItem(ATable[i].KEY, TPdfNumber.CreateNumber(ATable[i].VAL));
|
|
end;
|
|
|
|
// GetCharWidth
|
|
function TPdfFont.GetCharWidth(AText: string; APos: integer): integer;
|
|
begin
|
|
result := 0;
|
|
end;
|
|
|
|
// Create
|
|
constructor TPdfFont.Create(AXref: TPdfXref; AName: string);
|
|
begin
|
|
inherited Create;
|
|
FName := AName;
|
|
end;
|
|
|
|
{ PdfDestination }
|
|
|
|
// Create
|
|
constructor TPdfDestination.Create(APdfDoc: TPdfDoc);
|
|
var
|
|
i: integer;
|
|
begin
|
|
inherited Create;
|
|
FDoc := APdfDoc;
|
|
if (FDoc = nil) or (not FDoc.HasDoc) then
|
|
raise EPdfInvalidOperation.Create('TPdfDestination --cannot destination object.');
|
|
FPage := FDoc.Canvas.Page;
|
|
for i := 0 to 4 do
|
|
FValues[i] := 0;
|
|
FZoom := 1;
|
|
end;
|
|
|
|
// Destroy
|
|
destructor TPdfDestination.Destroy;
|
|
begin
|
|
if FReference <> nil then
|
|
FReference.Free;
|
|
inherited;
|
|
end;
|
|
|
|
// GetElement
|
|
function TPdfDestination.GetElement(Index: integer): Integer;
|
|
begin
|
|
result := FValues[Index];
|
|
end;
|
|
|
|
// SetElement
|
|
procedure TPdfDestination.SetElement(Index: integer; Value: Integer);
|
|
begin
|
|
if FValues[Index] <> Value then
|
|
if Value < 0 then
|
|
FValues[Index] := -1
|
|
else
|
|
FValues[Index] := Value;
|
|
end;
|
|
|
|
// SetZoom
|
|
procedure TPdfDestination.SetZoom(Value: Single);
|
|
begin
|
|
if Value <> FZoom then
|
|
if Value < 0 then
|
|
raise EPdfInvalidValue.Create('Zoom property cannot set to under 0.')
|
|
else
|
|
if Value > PDF_MAX_ZOOMSIZE then
|
|
raise EPdfInvalidValue.CreateFmt('Zoom property cannot set to over %d.', [PDF_MAX_ZOOMSIZE])
|
|
else
|
|
FZoom := Value;
|
|
end;
|
|
|
|
// GetPageWidth
|
|
function TPdfDestination.GetPageWidth: Integer;
|
|
var
|
|
FMediaBox: TPdfArray;
|
|
begin
|
|
FMediaBox := FPage.PdfArrayByName('MediaBox');
|
|
if FMediaBox <> nil then
|
|
result := TPdfNumber(FMediaBox.Items[2]).Value
|
|
else
|
|
result := FDoc.DefaultPageWidth;
|
|
end;
|
|
|
|
// GetPageHeight
|
|
function TPdfDestination.GetPageHeight: Integer;
|
|
var
|
|
FMediaBox: TPdfArray;
|
|
begin
|
|
FMediaBox := FPage.PdfArrayByName('MediaBox');
|
|
if FMediaBox <> nil then
|
|
result := TPdfNumber(FMediaBox.Items[3]).Value
|
|
else
|
|
result := FDoc.DefaultPageHeight;
|
|
end;
|
|
|
|
// GetValue
|
|
function TPdfDestination.GetValue: TPdfArray;
|
|
const
|
|
DEST_MAX_VALUE = 100;
|
|
begin
|
|
// create TPdfArray object from the specified values.
|
|
// the values which are not used are ignored.
|
|
result := TPdfArray.CreateArray(FDoc.FXref);
|
|
with result do
|
|
begin
|
|
AddItem(FPage);
|
|
AddItem(TPdfName.CreateName(PDF_DESTINATION_TYPE_NAMES[ord(FType)]));
|
|
case FType of
|
|
// if the type is dtXYZ, only Left, Top and Zoom values are used,
|
|
// other properties are ignored.
|
|
dtXYZ:
|
|
begin
|
|
if FValues[0] >= -DEST_MAX_VALUE then
|
|
AddItem(TPdfNumber.CreateNumber(Left))
|
|
else
|
|
AddItem(TPdfNull.Create);
|
|
if FValues[1] >= -DEST_MAX_VALUE then
|
|
AddItem(TPdfNumber.CreateNumber(Top))
|
|
else
|
|
AddItem(TPdfNull.Create);
|
|
if FZoom < 0 then
|
|
FZoom := 0;
|
|
AddItem(TPdfReal.CreateReal(FZoom));
|
|
end;
|
|
// if the type is dtFitR, all values except Zoom are used.
|
|
dtFitR:
|
|
begin
|
|
if FValues[0] >= -DEST_MAX_VALUE then
|
|
AddItem(TPdfNumber.CreateNumber(Left))
|
|
else
|
|
AddItem(TPdfNull.Create);
|
|
if FValues[1] >= -DEST_MAX_VALUE then
|
|
AddItem(TPdfNumber.CreateNumber(Bottom))
|
|
else
|
|
AddItem(TPdfNull.Create);
|
|
if FValues[2] >= 0 then
|
|
AddItem(TPdfNumber.CreateNumber(Right))
|
|
else
|
|
AddItem(TPdfNull.Create);
|
|
if FValues[3] >= 0 then
|
|
AddItem(TPdfNumber.CreateNumber(Top))
|
|
else
|
|
AddItem(TPdfNull.Create);
|
|
end;
|
|
// if the type is dtFitH or dtFitBH, only Top property is used.
|
|
dtFitH, dtFitBH:
|
|
if FValues[1] >= -DEST_MAX_VALUE then
|
|
AddItem(TPdfNumber.CreateNumber(Top))
|
|
else
|
|
AddItem(TPdfNull.Create);
|
|
// if the type is dtFitV or dtFitBV, only Top property is used.
|
|
dtFitV, dtFitBV:
|
|
if FValues[0] >= -DEST_MAX_VALUE then
|
|
AddItem(TPdfNumber.CreateNumber(Left))
|
|
else
|
|
AddItem(TPdfNull.Create);
|
|
end;
|
|
end;
|
|
end;
|
|
|
|
{ TPdfOutlineEntry }
|
|
|
|
// CreateEntry
|
|
constructor TPdfOutlineEntry.CreateEntry(AParent: TPdfOutlineEntry);
|
|
begin
|
|
inherited Create;
|
|
|
|
if AParent = nil then
|
|
Raise Exception.Create('CreateEntry --invalid parent.');
|
|
|
|
FParent := AParent;
|
|
FCount := 0;
|
|
FDoc := AParent.Doc;
|
|
Data := TPdfDictionary.CreateDictionary(FDoc.FXref);
|
|
FDoc.FXref.AddObject(Data);
|
|
FDoc.FObjectList.Add(Self);
|
|
end;
|
|
|
|
// Destroy
|
|
destructor TPdfOutlineEntry.Destroy;
|
|
begin
|
|
if FReference <> nil then
|
|
FReference.Free;
|
|
inherited;
|
|
end;
|
|
|
|
// AddChild
|
|
function TPdfOutlineEntry.AddChild: TPdfOutlineEntry;
|
|
var
|
|
TmpEntry: TPdfOutlineEntry;
|
|
begin
|
|
// increment Count variable recursive.
|
|
inc(FCount);
|
|
TmpEntry := Parent;
|
|
while TmpEntry <> nil do
|
|
begin
|
|
TmpEntry.FCount := TmpEntry.FCount + 1;
|
|
TmpEntry := TmpEntry.Parent;
|
|
end;
|
|
|
|
result := TPdfOutlineEntry.CreateEntry(Self);
|
|
if FFirst = nil then
|
|
FFirst := Result;
|
|
if FLast <> nil then
|
|
FLast.FNext := Result;
|
|
Result.FPrev := FLast;
|
|
FLast := Result;
|
|
end;
|
|
|
|
// Save
|
|
procedure TPdfOutlineEntry.Save;
|
|
begin
|
|
if Opened then
|
|
Data.AddItem('Count', TPdfNumber.CreateNumber(FCount))
|
|
else
|
|
Data.AddItem('Count', TPdfNumber.CreateNumber(-FCount));
|
|
|
|
Data.AddItem('Title', TPdfText.CreateText(FTitle));
|
|
|
|
if FDest <> nil then
|
|
Data.AddItem('Dest', FDest.GetValue);
|
|
|
|
if FFirst <> nil then
|
|
begin
|
|
Data.AddItem('First', FFirst.Data);
|
|
FFirst.Save;
|
|
end;
|
|
if FLast <> nil then
|
|
Data.AddItem('Last', FLast.Data);
|
|
if FPrev <> nil then
|
|
Data.AddItem('Prev', FPrev.Data);
|
|
if FNext <> nil then
|
|
begin
|
|
Data.AddItem('Next', FNext.Data);
|
|
FNext.Save;
|
|
end;
|
|
end;
|
|
|
|
{ TPdfOutlineRoot }
|
|
|
|
// CreateRoot
|
|
constructor TPdfOutlineRoot.CreateRoot(ADoc: TPdfDoc);
|
|
begin
|
|
inherited Create;
|
|
FCount := 0;
|
|
FDoc := ADoc;
|
|
FOpened := true;
|
|
Data := TPdfDictionary.CreateDictionary(ADoc.FXref);
|
|
FDoc.FXref.AddObject(Data);
|
|
with Data do
|
|
AddItem('Type', TPdfName.CreateName('Outlines'));
|
|
FDoc.FObjectList.Add(Self);
|
|
end;
|
|
|
|
// Save
|
|
procedure TPdfOutlineRoot.Save;
|
|
begin
|
|
if Opened then
|
|
Data.AddItem('Count', TPdfNumber.CreateNumber(FCount))
|
|
else
|
|
Data.AddItem('Count', TPdfNumber.CreateNumber(-FCount));
|
|
if FFirst <> nil then
|
|
begin
|
|
Data.AddItem('First', FFirst.Data);
|
|
FFirst.Save;
|
|
end;
|
|
if FLast <> nil then
|
|
Data.AddItem('Last', FLast.Data);
|
|
end;
|
|
|
|
end.
|