📄 updfwriter.pas
字号:
FStream.WriteBuffer(Pointer(Text)^, Length(Text)); //write the text
if NewLine then //wants line break
FStream.WriteBuffer(Pointer(UPDFWriter.NewLine)^, //write it
Length(UPDFWriter.NewLine));
end;
{Writes some formatted text into the stream.
~param Fmt the format of the text to write
~param Args the arguments for the format to include in the text to be
written
~param NewLine whether an end-of-line sequence should also be written }
procedure TPDFWriter.WriteFormatted(const Fmt: String;
const Args: array of const;
NewLine: Boolean = True);
begin
Write(Format(Fmt, Args), NewLine); //format and write the text
end;
{Writes some text into the page.
~param S the text to write
~param NewLine if an end-of-line sequence should also be written }
procedure TPDFWriter.WritePage(const S: String; NewLine: Boolean = True);
begin
FPageStream.WriteBuffer(Pointer(S)^, Length(S)); //write the text
if NewLine then //wants line break
FPageStream.WriteBuffer(Pointer(UPDFWriter.NewLine)^, //write it
Length(UPDFWriter.NewLine));
end;
{Writes a PDF number value as part of a command into the page. The value of the
number has to be below 20 million and is written with two digits after the
decimal point.
~param Number the value to be written }
procedure TPDFWriter.WritePageCommandNumber(const Number: TPDFValue);
const BufferSize = 15; //size of the buffer to convert the number
var S :String[BufferSize]; //used to convert
TheValue :Integer; //number * 100 as an integer
pS :PChar; //runner through the buffer
AbsValue :Cardinal; //positiv number * 100 as an integer
begin
TheValue := Round(Number * 100); //get the number to be written
pS := PChar(@S[BufferSize]); //initialize buffer
if TheValue = 0 then //number is zero?
ps^ := '0' //write the zero
else
begin
AbsValue := Abs(TheValue); //first write positive number
ps^ := Char(Ord('0') + AbsValue mod 10); //add last digit
AbsValue := AbsValue div 10;
if (AbsValue = 0) or (pS^ <> '0') then //is relevant?
Dec(pS);
ps^ := Char(Ord('0') + AbsValue mod 10); //add "tenth"-digit
AbsValue := AbsValue div 10;
if (pS^ <> '0') or (pS <> PChar(@S[BufferSize])) then //is relevant?
begin
Dec(pS);
pS^ := '.'; //add decimal point
Dec(pS);
end;
repeat //add all other digits
ps^ := Char(Ord('0') + AbsValue mod 10);
Dec(pS);
AbsValue := AbsValue div 10;
until AbsValue = 0;
if TheValue < 0 then //value was negative?
pS^ := '-' //prepend signum
else
Inc(pS);
end;
FPageStream.WriteBuffer(pS^, @S[BufferSize] - pS + 1); //write the number
{
Str2Ext(Number, 1, 2, S); //get number as text
// Str(Number:1:2, S); //get number as text
FPageStream.WriteBuffer(S[1], Length(S)); //write the text
}
end;
{Ends the current page. }
procedure TPDFWriter.EndPage;
var PObj :TPDFObjectNumber; //index of the PDF object of the page
Parent :TPDFObjectNumber; //parent object of list (tree) of pages
Obj :TPDFObjectNumber; //index of a new PDF object
i :Integer; //general counter
AliasInd :Integer; //counter through destination aliases
TmpStream :TMemoryStream; //compressed content of the page
begin
DoDrawCommands; //draw everything that has been drawn
//get future index of the PDF object of the page
PObj := FObjPositions.Count + 1 + FDestinations.Count + FAnnotations.Count;
//register the page reference (might change it) and get parent entry
Parent := RegisterPageNumber(PObj);
AliasInd := 0; //start with first alias
for i := 0 to FDestinations.Count - 1 do //for each destination in the page
begin
Obj := GetAddObjPosNumber; //create a new object for it
Write(IntToStr(Obj) + ' 0 obj <<'); //write the PDF object for it
Write('/D [' + IntToStr(PObj) + ' 0 R /XYZ ' + //write destination
FDestinations[i] + ' 0.00 ]');
Write('>> endobj');
//add destination to name tree of the destinations
FDestNameTreeItems.AddObject(FDestinationNames[i], Pointer(Obj));
//check whether aliases exist for that destination
while (AliasInd < FDestinationAliases.Count) and
(Integer(FDestinationAliases.Objects[AliasInd]) = i) do
begin
//add destination with the alias to name tree of the destinations
FDestNameTreeItems.AddObject(FDestinationAliases[AliasInd], Pointer(Obj));
Inc(AliasInd); //resume with next alias
end;
end;
Assert(AliasInd = FDestinationAliases.Count);
FDestinations.Clear; //clear lists of the destinations
FDestinationNames.Clear;
FDestinationAliases.Clear;
for i := 0 to FAnnotations.Count - 1 do //for each annotation
begin
Obj := GetAddObjPosNumber; //create a new object for it
FAnnotations.Objects[i] := Pointer(Obj); //save the index
Write(IntToStr(Obj) + ' 0 obj <<'); //create a PDF object for it
Write('/Type /Annot');
Write('/Subtype /Link'); //write the annotation
Write(FAnnotations[i]); //write data of link target
Write('/Border [0 0 0 ]');
Write('>> endobj');
end;
Obj := GetAddObjPosNumber; //create PDF object for the page
assert(PObj = Obj);
Write(IntToStr(Obj) + ' 0 obj <<'); //start PDF object for the page
Write('/Type /Page');
Write(Format('/Parent %d 0 R', [Parent])); //parent in the tree of the pages
//write size of the pages of the file
Write('/MediaBox [0 0 ' + PDFNumberToStr(FPageWidth) + ' ' +
PDFNumberToStr(FPageHeight) + ' ]');
if FImageReferences.Count <> 0 then //some images drawn in the page?
begin
WriteResources(FImageReferences); //use them as resource
FImageReferences.Clear; //clear for the next page
end;
if FAnnotations.Count <> 0 then //annotations in the page?
begin
Write('/Annots [', False); //create entry for them
for i := 0 to FAnnotations.Count - 1 do //write references to annotations
Write(IntToStr(Integer(FAnnotations.Objects[i])) + ' 0 R ', False);
Write(']');
FAnnotations.Clear;
end;
//the directly following object contains the content of the page
Write('/Contents ' + IntToStr(Obj + 1) + ' 0 R');
Write('>> endobj');
//create the PDF object for the content of the page
Write(IntToStr(GetAddObjPosNumber) + ' 0 obj <<');
if FImageDrawCommands <> '' then //some images should be drawn?
begin
//set new and final size
FPageStream.Size := FPageStream.Position + Length(FImageDrawCommands);
//move the whole stream to free some space for this commands for the images
Move(FPageStream.Memory^,
Pointer(Integer(FPageStream.Memory) + Length(FImageDrawCommands))^,
FPageStream.Position);
//write the commands to draw the images at the beginning
Move(Pointer(FImageDrawCommands)^, FPageStream.Memory^,
Length(FImageDrawCommands));
FPageStream.Position := FPageStream.Size; //move to the end
FImageDrawCommands := '';
end;
TmpStream := FPageStream; //use stream for content of page
try
FPageStream.Size := FPageStream.Position; //copy only valid data
if FCompression then //compression enabled?
begin
//create stream for compressed content
TmpStream := TMemoryStream.Create;
with TCompressionStream.Create(clMax, TmpStream) do //create compressor
try
CopyFrom(FPageStream, 0); //compress content
finally
Free; //free compressor
end;
end; //if FCompression
//write length of the (compressed) content
Write('/Length ' + IntToStr(TmpStream.Size));
if FCompression then //compression enabled?
Write('/Filter [/FlateDecode ]') //write the decompressor to use
else
Write('/Filter []');
Write('>>');
Write('stream' + NewLine, False); //start the content of the page
TmpStream.Position := 0;
Stream.CopyFrom(TmpStream, 0); //copy the content of the page
finally
if TmpStream <> FPageStream then //compression enabled?
TmpStream.Free; //free the stream
end;
Write(#10'endstream endobj'); //end the content of the page
end;
{Registers a page number and returns its parent object in the tree of pages.
~param Page the number of a (not yet written) page to register into the tree of
pages, might be modified (incremented) slightly in case new objects
have to be inserted
~result the number of the parent entry in the tree }
function TPDFWriter.RegisterPageNumber(
var Page: TPDFObjectNumber): TPDFObjectNumber;
//Write the current node of the specified level of the tree of pages.
procedure WritePagesNode(Level, PageCount: Integer); forward;
//Adds a new node into the tree of pages.
procedure AddPagesNode(Level, PageCount: Integer); forward;
{Write the current node of the specified level of the tree of pages.
~param Level the level in the tree to write the current node of
~param PageCount the number of pages in the node }
procedure WritePagesNode(Level, PageCount: Integer);
var ObjNum :TPDFObjectNumber; //number of the object of the node
Parent :TPDFObjectNumber; //the number of the parent's object
i :Integer; //counter through child entries
begin
Assert(Length(FPageTreeObjectsCounter) > Level);
//is the first node on this level?
if FPageTreeObjectsCounter[Level] = 1 then
//we need a new parent node, so add (register) it
AddPagesNode(Level + 1, PageCount * FPagesTreeNumberInNodes);
//get number of the object
ObjNum := FPageTreeObjects[Level][FPageTreeObjectsCounter[Level] - 1];
//get number of the object of the parent entry
Parent := FPageTreeObjects[Level + 1][FPageTreeObjectsCounter[Level + 1] -
1];
//save position of the pages-object
FObjPositions[ObjNum - 1] := Pointer(FStream.Position);
Write(Format('%d 0 obj <<', [ObjNum])); //start the object
Write('/Type /Pages'); //a node in the tree of pages
Write(Format('/Parent %d 0 R', [Parent])); //reference to parent node
//write the references to the child nodes
Write('/Kids [', False);
for i := 0 to FPagesTreeNumberInNodes - 1 do
Write(Format('%d 0 R ', [FPageTreeObjects[Level - 1][i]]), False);
Write(']');
Write(Format('/Count %d', [PageCount])); //write number of pages
Write('>> endobj'); //end the object
FPageTreeObjectsCounter[Level - 1] := 0; //clear list of child nodes
end;
{Adds a new node into the tree of pages.
~param Level the level in the tree to add a new node to
~param PageCount the number of pages in the node }
procedure AddPagesNode(Level, PageCount: Integer);
begin
Assert(Length(FPageTreeObjectsCounter) >= Level);
//is the first node on the level?
if Length(FPageTreeObjectsCounter) = Level then
begin
SetLength(FPageTreeObjects, Level + 1); //add the new level
SetLength(FPageTreeObjects[Level], FPagesTreeNumberInNodes);
SetLength(FPageTreeObjectsCounter, Level + 1);
FPageTreeObjectsCounter[Level] := 0; //no nodes in it so far
end
else //level is already filled?
if FPageTreeObjectsCounter[Level] = FPagesTreeNumberInNodes then
//write references to all the nodes (i.e. write their parent node) and
//clear
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -