📄 updftextwriter.pas
字号:
{ * * * *** * * * *** TPDFTextColumn *** * * * *** * * * }
{$IFDEF DEBUG}
//just a simple counter as an information how often the code in
//~[link TPDFTextColumn] is executed
var CreateCounterTPDFTextColumn: Cardinal = 0;
{$ENDIF}
{Creates the object to represent a cell of a table without content.
~param Owner the manager of the current table row which owns this text object
~param Writer the writer of the PDF file
~param Left horizontal position of the left border of the column
~param Right horizontal position of the right border of the column }
constructor TPDFTextColumn.CreateEmptyColumn(Owner: TPDFTextWriterManager;
Writer: TPDFWriter;
Left, Right: TPDFValue);
begin
Assert(Right > Left);
{$IFDEF DEBUG}
Inc(CreateCounterTPDFTextColumn); //count number of instances
{$ENDIF}
inherited Create; //create the object
FManager := Owner; //save the manager
Owner.AddTextObject(Self); //and register itself to it
FLeftPosition := Left; //save position and width
FColumnWidth := Right - Left; //of the table cell
FAlignment := taLeft; //use default alignment
FVerticalAlignment := vaTop;
//use the current font and select its color
FCurrentParagraphFont.Font := Writer.FontType;
FCurrentParagraphFont.Style := Writer.FontStyle;
FCurrentParagraphFont.Size := Writer.Size;
FCurrentParagraphFont.Color := Writer.Color;
end;
{Creates the object to represent the text of a cell of a table.
~param Owner the manager of the current table row which owns this
text object
~param Writer the writer of the PDF file
~param Left horizontal position of the left border of the column
~param Right horizontal position of the right border of the column
~param Alignment the horizontal alignment of the text in the column
~param VerticalAlignment the vertical alignment of the content of the cell
~param FirstCharacter the index of the first character in the array that is
part of this cell
~param LastCharacter the index of the last character in the array that is
still part of this cell
~param Characters the characters that are the text of the cell
~param CharWidths the widths of the characters
~param Breakable the kinds of the characters in the text, used to
determine where to break it
~param Attributes the attributes of the text or parts of it
~param FirstAttr the index of the first attribute inside this cell
~param LastAttr the index of the last attribute inside this cell
~param Links the links inside the text of this cell
~param FirstLink the index of the first link inside this cell
~param LastLink the index of the last link inside this cell }
constructor TPDFTextColumn.CreateColumn(Owner: TPDFTextWriterManager;
Writer: TPDFWriter;
Left, Right: TPDFValue;
Alignment: TTextAlignment;
VerticalAlignment: TVerticalAlignment;
FirstCharacter, LastCharacter: Integer;
const Characters: TCharacterArray;
const CharWidths: TPDFValueArray;
const Breakable: TCharacterBreakables;
const Attributes: TParagraphAttributes;
FirstAttr, LastAttr: Integer;
const Links: TParagraphLinks;
FirstLink, LastLink: Integer);
begin
//create object and initialize base value
CreateEmptyColumn(Owner, Writer, Left, Right);
FAlignment := Alignment; //save the alignment
FVerticalAlignment := VerticalAlignment;
if LastCharacter >= FirstCharacter then //cell is really not empty?
begin
//copy the characters and the attributes and links on them
FCharacters := Copy(Characters, FirstCharacter,
LastCharacter - FirstCharacter + 1);
FAttributes := Copy(Attributes, FirstAttr, LastAttr - FirstAttr + 1);
FLinks := Copy(Links, FirstLink, LastLink - FirstLink + 1);
//adjust index to absolute values to the copied characters
AdjustAttributeIndices(FirstCharacter);
//calculate meta data of the text needed to write it
CalculateTextMetaData(CharWidths, Breakable, FirstCharacter);
end;
end;
{Creates the object to represent the text of a paragraph.
~param Owner the manager of the paragraph which owns this text object
~param Writer the writer of the PDF file
~param Left horizontal position of the left border of the text
~param Right horizontal position of the right border of the text
~param Alignment the horizontal alignment of the paragraph
~param CharacterCount the number of valid characters in the array for the
paragraph
~param Characters the characters that are the text of the paragraph
~param CharWidths the widths of the characters
~param Breakable the kinds of the characters in the paragraph, used to
determine where to break it
~param Attributes the attributes of the text or parts of it
~param AttrCount the number of valid attributes in the array
~param Links the links inside the text of the paragraph
~param LinkCount the number of valid links in the array
~param InLink whether the beginning of the paragraph is already in a
link, i.e. whether the first link has already be
started in the previous paragraph }
constructor TPDFTextColumn.CreateTheText(Owner: TPDFTextWriterManager;
Writer: TPDFWriter;
Left, Right: TPDFValue;
Alignment: TTextAlignment;
CharacterCount: Integer;
const Characters: TCharacterArray;
const CharWidths: TPDFValueArray;
const Breakable: TCharacterBreakables;
const Attributes: TParagraphAttributes;
AttrCount: Integer;
const Links: TParagraphLinks;
LinkCount: Integer;
InLink: Boolean);
begin
//create object and initialize base value
CreateEmptyColumn(Owner, Writer, Left, Right);
FAlignment := Alignment; //save the alignment and
FCharacters := Copy(Characters, 0, CharacterCount); //copy all the data
FAttributes := Copy(Attributes, 0, AttrCount);
FLinks := Copy(Links, 0, LinkCount);
FInLink := InLink;
Assert(not InLink or (LinkCount > 0));
//if in a link, set the color of the text to signal the link
if InLink then
FCurrentParagraphFont.Color := Owner.FTextWriter.LinkColors[Links[0].
LinkType];
if CharacterCount > 0 then //paragraph not empty?
//calculate meta data of the text needed to write it
CalculateTextMetaData(CharWidths, Breakable, 0);
end;
{Frees the object and unregisters it from its manager. }
destructor TPDFTextColumn.Destroy;
begin
if Assigned(FManager) then //assigned to a manager object?
FManager.TextObjectFreed(Self); //unregister it from the manager
inherited Destroy; //free the object
end;
{Adjusts the indices of characters for the attributes and links if only part of
the text is written with this object.
~param By the value by which the indices have to be adjusted }
procedure TPDFTextColumn.AdjustAttributeIndices(By: Integer);
var i :Integer; //counter through attributes and links
begin
for i := Low(FAttributes) to High(FAttributes) do //for each attribute
begin
Assert(FAttributes[i].StartIndex > By);
Dec(FAttributes[i].StartIndex, By); //adjust its starting index
end;
for i := Low(FLinks) to High(FLinks) do //for each link
begin
Assert(FLinks[i].StartIndex > By);
Dec(FLinks[i].StartIndex, By); //adjust its indices
Assert(FLinks[i].EndIndex <> -1);
Assert((FLinks[i].EndIndex = -1) or (FLinks[i].EndIndex > By));
Dec(FLinks[i].EndIndex, By);
end;
end;
{Calculates meta data about the text after the object has been created and the
text saved.
~param CharWidths an array with the widths of all characters in
~[link FCharacters]
~param Breakable the kinds of the characters in ~[link FCharacters], used to
determine where to break the text
~param FirstIndex the index of the first valid entries in the two arrays }
procedure TPDFTextColumn.CalculateTextMetaData(
const CharWidths: TPDFValueArray;
const Breakable: TCharacterBreakables;
FirstIndex: Integer);
begin
//create array with the summed-up widths of characters
SetLength(FSumCharWidths, Length(FCharacters) + 1);
//and calculate the summed-up widths
CalculateSumCharWidths(@CharWidths[FirstIndex], FSumCharWidths);
//calculate the (maximum) font size of the text
CalculateFontSize(FAttributes);
//calculate the number of lines needed to show the text and where to break it
CalculateLines(@Breakable[FirstIndex]);
end;
{Calculates the summed up widths of the characters in the paragraph.
~param CharWidths the widths of all characters in ~[link FCharacters] to
sum up
~param SumCharWidths the array in which to save the summed-up widths }
procedure TPDFTextColumn.CalculateSumCharWidths(
CharWidths: PPDFValue;
const SumCharWidths :TPDFValueArray);
//type to access the next value in an array of TPDFValues by pointer
type TPDFValueIndexer = array[0..1] of TPDFValue;
var i :Integer; //counter through the widths
Sums :PPDFValue; //runner through the sums
begin
SumCharWidths[0] := 0; //for no characters the width is 0
Sums := @SumCharWidths[0];
//calculate sum of widths of all characters in the paragraph
for i := 1 to High(SumCharWidths) do
begin
//add the width of the character to the last summed-up width
TPDFValueIndexer(Pointer(Sums)^)[1] := Sums^ + CharWidths^;
Inc(Sums);
Inc(CharWidths);
end;
end;
{Calculates the (maximum) font size used for the text.
~param Attributes the list of attributes that may change the size of the used
font, i.e. ~[link FAttributes] }
procedure TPDFTextColumn.CalculateFontSize(Attributes: TParagraphAttributes);
var Size :TPDFValue; //the used maximum font size
//whether the first font attribute has still to be found
First :Boolean;
i :Integer; //counter through all attributes
begin
Size := 0; //no font size found so far
First := True;
for i := Low(Attributes) to High(Attributes) do //for each attribute
if Attributes[i].Attribute = aFont then //if it sets the font
begin
if First then //is the first font?
begin
First := False; //not anymore
//some characters are before it?
if Attributes[i].StartIndex <> 1 then
Size := FManager.Writer.Size; //also use default font
end;
if Attributes[i].Font.Size > Size then //is a bigger font?
Size := Attributes[i].Font.Size; //use its size
end;
if Size = 0 then //no font has been set?
Size := FManager.TextWriter.CurrentFont.Size; //use default font size
FFontSize := Size; //and set the font size
Assert(Size <> 0);
end;
{Calculates the number of lines necessary to show the text in the available
width and where to break it.
~param Breakables the kinds of the characters in ~[link FCharacters], used to
determine where to break the text }
procedure TPDFTextColumn.CalculateLines(Breakables: PCharacterBreakable);
var Count :Integer; //number of lines
StartChar :Integer; //the first characters of the lines
LastChar :Integer; //the last character of the paragraph
begin
Assert(FColumnWidth > 0);
Assert(Length(FCharacters) <> 0);
//get room for the information of the lines
SetLength(FLineInfo,
Trunc(2 * FSumCharWidths[High(FSumCharWidths)] / FColumnWidth) + 1);
Count := 0; //no lines found yet
StartChar := 1; //start with the first character
LastChar := Length(FCharacters); //up to the last one
repeat //until whole paragraph splitted up in lines
if Count >= Length(FLineInfo) then //not enough room for next line?
if Count < 2 then //grow array for more room
SetLength(FLineInfo, 2)
else
SetLength(FLineInfo, Count + Count div 2);
//calculate the amount of text of the paragraph fitting in the next line
FLineInfo[Count] := CalculateWrapPoint(StartChar, LastChar, FColumnWidth,
FSumCharWidths,
PCharBreaksArray(Breakables));
StartChar := FLineInfo[Count].ResumePoint; //get beginning of the next line
Inc(Count); //another line added
until StartChar > LastChar; //until whole paragraph splitted up in lines
FLineCount := Count; //save number of lines
end;
{Returns the number of spaces in the specified range of the paragraph.
~param FromIndex the beginning of the range to count the spaces in (1-based)
~param ToIndex the end of the range to count the spaces in (1-based)
~result the number of spaces in the range }
function TPDFTextColumn.GetSpaceCount(FromIndex, ToIndex: Integer): Integer;
var Chars :PChar; //runner through characters
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -