www.pudn.com > PDF-Library.rar > QPDFRender.pas, change:2016-12-27,size:34918b


unit QPDFRender; 
 
interface 
 
uses Windows, Classes, Contnrs, Graphics; 
 
type 
  TRenderTextAlign = (rtaLeft, rtaRight, rtaCenter, rtaJustify, rtaTerminal ); 
 
  TRenderAtom = class; 
  TRenderCell = class; 
  TRenderRow = class; 
  TRenderTable = class; 
 
  TRenderWriter = class 
  protected 
    DrawThinkness, DrawColor, CurPageNum, TotalPages: Integer; 
    constructor Create; 
    procedure DrawLine( x, y, x2, y2: Integer ); virtual; abstract; 
    procedure DrawRect( x, y, width, height: Integer ); virtual; abstract; 
    procedure FillRect( x, y, width, height, color: Integer ); virtual; abstract; 
    procedure DrawWord( x, y: Integer; Word: String; Font: TFont ); virtual; abstract; 
    procedure DrawImage( x, y, width, height: Integer; Image: TGraphic; Raster: Boolean ); virtual; abstract; 
    procedure SetLink( Target: String; Bounds: TRect ); virtual; abstract; 
    procedure ResolveLink( Target: String; X, Y: Integer ); virtual; abstract; 
  end; 
 
  TRenderItems = class(TObjectList) 
  private 
    function GetItems(Index: Integer): TRenderAtom; 
    procedure SetItems(Index: Integer; const Value: TRenderAtom); 
  protected 
    property Items[Index: Integer]: TRenderAtom read GetItems write SetItems; default; 
  end; 
 
  TRenderSplitMode = (splitTest, splitRegular, splitForced); 
 
  TRenderAtom = class 
  private 
    FX, FY: Integer; 
    FWidth, FHeight: Integer; 
    FParent: TRenderAtom; 
    FCell: TRenderCell; 
    FHSpacing: Integer; 
    function GetHeight: Integer; virtual; 
    function ParentColor: Integer; 
    function XWidth: Integer; 
  protected 
    constructor Create( _Width: Integer; _Parent: TRenderAtom ); overload; 
    procedure Draw( Writer: TRenderWriter; X, Y: Integer ); virtual; 
    function Split( Y: Integer; mode: TRenderSplitMode ): TRenderAtom; virtual; 
    property Parent: TRenderAtom read FParent; 
  public 
    BgColor: Integer; 
    LinkTarget, LinkName: String; 
    constructor Create( _Width: Integer ); overload; 
    destructor Destroy; override; 
    property Height: Integer read GetHeight; 
    property Width: Integer read FWidth; 
  end; 
 
  TRenderCell = class(TRenderAtom) 
    FAtoms: TRenderItems; 
    function GetHeight: Integer; override; 
    function GetParent: TRenderRow; 
    function GetPadding: TRect; 
  protected 
    constructor Create( _Width: Integer; _Row: TRenderRow ); 
    procedure Draw( Writer: TRenderWriter; X, Y: Integer ); override; 
    function Split( Y: Integer; mode: TRenderSplitMode ): TRenderAtom; override; 
    property Atoms: TRenderItems read FAtoms; 
    property Parent: TRenderRow read GetParent; 
  public 
    BorderLeft,BorderRight,BorderTop,BorderBottom, Indent: Integer; 
    Padding: TRect; 
    destructor Destroy; override; 
    function InternalWidth: Integer; 
    procedure AddText( _Text: WideString; _Font: TFont; _Align: TRenderTextAlign = rtaLeft; _Link: String = '' ); 
    procedure AddTextItem( _Item: TRenderAtom ); 
    procedure AddItem( X, Y: Integer; Item: TRenderAtom ); 
    function AddParagraph: TRenderCell; 
  end; 
 
  TRenderRow = class(TRenderAtom) 
  private 
    FForceHeight: Integer; 
    FCells: TRenderItems; 
    function GetHeight: Integer; override; 
    function GetParent: TRenderTable; 
    function GetCell(Index: Integer): TRenderCell; 
    function GetCellCount: Integer; 
  protected 
    constructor Create( _Width: Integer; _Table: TRenderTable ); 
    procedure Draw( Writer: TRenderWriter; X, Y: Integer ); override; 
    property Parent: TRenderTable read GetParent; 
    function Split( Y: Integer; mode: TRenderSplitMode ): TRenderAtom; override; 
  public 
    destructor Destroy; override; 
    property Cell[Index: Integer]: TRenderCell read GetCell; 
    property CellCount: Integer read GetCellCount; 
  end; 
 
  TRenderTable = class(TRenderAtom) 
  private 
    FRows: TRenderItems; 
    FColWidths: array of Integer; 
    FSplitted: Boolean; 
    function GetHeight: Integer; override; 
    function GetCells(x, y: Integer): TRenderCell; 
    function GetRow(Index: Integer): TRenderRow; 
    function GetRowCount: Integer; 
  protected 
    constructor Create( _Widths: array of Integer; _Parent: TRenderAtom ); overload; 
  public 
    Border: Integer; 
    Padding: TRect; 
    HasHeader, HasFooter: Boolean; 
    constructor Create( _Widths: array of Integer ); overload; 
    destructor Destroy; override; 
 
    function Split( Y: Integer; mode: TRenderSplitMode ): TRenderAtom; override; 
    procedure Draw( Writer: TRenderWriter; X, Y: Integer ); override; 
    procedure AdjustFooter( Y: Integer ); 
    procedure ForceRows( Index: Integer ); 
 
    function AddRow: TRenderRow; 
    function LastRow: TRenderRow; 
    property Rows[Index: Integer]: TRenderRow read GetRow; 
    property RowCount: Integer read GetRowCount; 
    property Cell[x,y: Integer]: TRenderCell read GetCells; default; 
  end; 
 
  TRenderWord = class(TRenderAtom) 
  private 
    FXWidth: Integer; 
    FFont: TFont; 
    FWord: WideString; 
    FAlterWord: WideString; 
  protected 
    procedure Draw( Writer: TRenderWriter; X, Y: Integer ); override; 
    function XWidth: Integer; 
  public 
    constructor Create( _Word: WideString; _Font: TFont ); 
    destructor Destroy; override; 
  end; 
 
 
  TRenderSequence = class(TRenderAtom) 
  private 
    FItems: TRenderItems; 
    FAlign: TRenderTextAlign; 
    FSplitUp,FSplitDown: Boolean; 
    function GetHeight: Integer; override; 
  protected 
    procedure Draw( Writer: TRenderWriter; X, Y: Integer ); override; 
    function Split( Y: Integer; mode: TRenderSplitMode ): TRenderAtom; override; 
  public 
    constructor Create( _Text: WideString; _Font: TFont; _Align: TRenderTextAlign = rtaLeft; _Link: String = '' ); 
    destructor Destroy; override; 
    procedure AddText( _Text: WideString; _Font: TFont; _Link: String = '' ); 
    procedure AddItem( _Item: TRenderAtom ); 
    procedure NewLine; 
  end; 
 
  TRenderImage = class(TRenderAtom) 
  private 
    FImage: TGraphic; 
    FRaster: Boolean; 
  protected 
    procedure Draw( Writer: TRenderWriter; X, Y: Integer ); override; 
  public 
    constructor Create( _graphic: TGraphic; _Raster: Boolean = false ); overload; 
    constructor Create( _Width: Integer; _graphic: TGraphic; _Raster: Boolean = false ); overload; 
    constructor Create( _Width, _Height: Integer; _graphic: TGraphic; _Raster: Boolean = false ); overload; 
  end; 
 
var 
    DebugTotalAtoms: TObjectList; 
 
implementation 
 
uses SysUtils, QPDFTTF; 
 
var 
    CalcFontHeight: Integer; 
 
procedure SetFontForCalc( Font: TFont ); 
begin 
    fontManager.SelectFont( font.Name, fsBold in Font.Style, fsItalic in Font.Style ); 
    CalcFontHeight := Font.Height; 
end; 
 
function CalcStringWidth( str: WideString ): Integer; 
begin 
    Result := CalcFontHeight * fontManager.StringWidth(str) div 10; 
end; 
 
 
{ TRenderItems } 
 
function TRenderItems.GetItems(Index: Integer): TRenderAtom; 
begin 
    Result := inherited Items[Index] as TRenderAtom; 
end; 
 
procedure TRenderItems.SetItems(Index: Integer; const Value: TRenderAtom); 
begin 
    OwnsObjects := False; 
    inherited Items[Index] := Value; 
    OwnsObjects := True; 
end; 
 
{ TRenderAtom } 
 
constructor TRenderAtom.Create( _Width: Integer; _Parent: TRenderAtom ); 
begin 
    FWidth := _Width; 
    FParent := _Parent; 
    FHeight := 0; 
    BgColor := -1; 
    FHSpacing := 0; 
    LinkName := ''; 
    LinkTarget := ''; 
 
    if not Assigned(DebugTotalAtoms) then begin 
        DebugTotalAtoms := TObjectList.Create; 
        DebugTotalAtoms.OwnsObjects := False; 
        end; 
//    DebugTotalAtoms.Add( Self ); 
end; 
 
constructor TRenderAtom.Create(_Width: Integer); 
begin 
    Create( _Width, nil ); 
end; 
 
destructor TRenderAtom.Destroy; 
begin 
    inherited; 
//    DebugTotalAtoms.Extract(Self); 
end; 
 
procedure TRenderAtom.Draw( Writer: TRenderWriter; X, Y: Integer ); 
begin 
    if LinkTarget <> '' then 
        Writer.SetLink( LinkTarget, Rect( X, Y, X+Width, Y+Height ) ); 
    if LinkName <> '' then 
        Writer.ResolveLink( LinkName, X, Y ); 
end; 
 
function TRenderAtom.GetHeight: Integer; 
begin 
    Result := FHeight; 
end; 
 
function TRenderAtom.ParentColor: Integer; 
var 
    a: TRenderAtom; 
begin 
    a := Parent; 
    while Assigned(a) and (a.BgColor < 0) do 
        a := a.Parent; 
    Result := -1; 
    if Assigned(a) then Result := a.BgColor; 
end; 
 
function TRenderAtom.Split(Y: Integer; mode: TRenderSplitMode): TRenderAtom; 
begin 
    Result := nil; 
end; 
 
function TRenderAtom.XWidth: Integer; 
begin 
    Result := Width * 100; 
end; 
 
{ TRenderCell } 
 
constructor TRenderCell.Create( _Width: Integer; _Row: TRenderRow ); 
begin 
    inherited Create( _Width, _Row ); 
    FAtoms := TRenderItems.Create; 
    BorderLeft := -2; 
    BorderRight := -2; 
    BorderTop := -2; 
    BorderBottom := -2; 
    Padding := Rect(-1,-1,-1,-1); 
    Indent := 0; 
end; 
 
destructor TRenderCell.Destroy; 
begin 
    inherited; 
    FAtoms.Free; 
end; 
 
procedure TRenderCell.AddItem(X, Y: Integer; Item: TRenderAtom); 
begin 
    Item.FX := X; 
    Item.FY := Y; 
    Item.FCell := Self; 
    Item.FParent := Self; 
    Atoms.Add( Item ); 
end; 
 
procedure TRenderCell.Draw( Writer: TRenderWriter; X, Y: Integer ); 
var 
    i: Integer; 
    r: TRect; 
begin 
    inherited; 
    if (BgColor >= 0) and (BgColor <> ParentColor) then 
        writer.FillRect( x, y, Width, Parent.Height, BgColor ); 
    r := GetPadding; 
    Inc(X, r.Left ); 
    Inc(Y, r.Top ); 
    for i := 0 to Pred(Atoms.Count) do with Atoms[i] do 
        Atoms[i].Draw( Writer, X+FX, Y+FY ); 
end; 
 
function TRenderCell.GetHeight: Integer; 
var 
    i, h: Integer; 
    r: TRect; 
begin 
    Result := 0; 
    for i := 0 to Pred(Atoms.Count) do with Atoms[i] do begin 
        h := FY + Height; 
        if h > Result then Result := h; 
        end; 
    r := GetPadding; 
    Inc( Result, r.Top + r.Bottom ); 
end; 
 
function TRenderCell.GetParent: TRenderRow; 
begin 
    Result := FParent as TRenderRow; 
end; 
 
function TRenderCell.Split(Y: Integer; mode: TRenderSplitMode): TRenderAtom; 
var 
    Atom: TRenderAtom; 
    Cel: TRenderCell; 
    pad: TRect; 
begin 
    Result := nil; 
    pad := GetPadding; 
    if (Y <= pad.Top+pad.Bottom) or (Y >= Height) then Exit; 
    if (Atoms.Count <> 1) or (Atoms[0].FY <> 0) then Exit; 
    Dec(Y,pad.Top+Pad.Bottom); 
    Atom := Atoms[0].Split( Y, mode ); 
    if Atom = nil then 
        Exit; 
    if mode = splitTest then begin 
        Integer(Result) := Integer(Atom)+pad.Top+pad.Bottom; 
        Exit; 
        end; 
    cel := TRenderCell.Create( Width, nil ); 
    cel.BgColor := BgColor; 
    cel.Padding := Padding; 
    cel.AddItem( Atoms[0].FX, 0, Atom ); 
    Result := cel; 
end; 
 
procedure TRenderCell.AddText(_Text: WideString; _Font: TFont; _Align: TRenderTextAlign; _Link: String ); 
begin 
    if (Atoms.Count = 0) then 
        AddItem( 0, 0, TRenderSequence.Create( _Text, _Font, _Align, _Link ) ) 
    else if Atoms[Atoms.Count-1] is TRenderSequence then 
        TRenderSequence(Atoms[Atoms.Count-1]).AddText( _Text, _Font, _Link ) 
    else if Atoms[0] is TRenderTable then 
        (Atoms[0] as TRenderTable).LastRow.Cell[0].AddText( _Text, _Font, _Align, _Link ) 
    else 
        AddItem( 0, 0, TRenderSequence.Create( _Text, _Font, _Align, _Link ) ) 
end; 
 
procedure TRenderCell.AddTextItem(_Item: TRenderAtom); 
var 
    seq: TRenderSequence; 
begin 
    if (Atoms.Count > 0) and (Atoms[Atoms.Count-1] is TRenderSequence) then 
        TRenderSequence(Atoms[Atoms.Count-1]).AddItem( _Item ) 
    else if (Atoms.Count > 0) and (Atoms[0] is TRenderTable) then 
        (Atoms[0] as TRenderTable).LastRow.Cell[0].AddTextItem( _Item ) 
    else begin 
        seq := TRenderSequence.Create( '', nil, rtaJustify ); 
        AddItem( 0, 0, seq ); 
        seq.AddItem( _Item ); 
        end; 
end; 
 
function TRenderCell.AddParagraph: TRenderCell; 
var 
    tab: TRenderTable; 
    a: TRenderAtom; 
begin 
    if (Atoms.Count > 0) and (Atoms[0] is TRenderTable) then begin 
        TRenderTable(Atoms[0]).AddRow; 
        Result := TRenderTable(Atoms[0]).LastRow.Cell[0]; 
        Exit; 
        end; 
    tab := TRenderTable.Create( [InternalWidth], Self ); 
    tab.Border := -1; 
    if Atoms.Count > 0 then begin 
        while Atoms.Count > 0 do begin 
            a := TRenderAtom(Atoms.Extract(Atoms[0])); 
            tab.Cell[0,0].AddItem( a.FX, a.FY, a ); 
            end; 
        tab.Cell[0,0].Indent := Indent; 
        tab.Cell[0,0].Padding := Padding; 
        Indent := 0; 
        Padding := Rect(-1,-1,-1,-1); 
        end; 
    AddItem( 0, 0, tab ); 
    tab.AddRow; 
    Result := tab.LastRow.Cell[0]; 
end; 
 
function TRenderCell.GetPadding: TRect; 
var 
    r: TRect; 
begin 
    Result := Padding; 
    r := Parent.Parent.Padding; 
    if Result.Left < 0 then Result.Left := r.Left; 
    if Result.Right < 0 then Result.Right := r.Right; 
    if Result.Top < 0 then Result.Top := r.Top; 
    if Result.Bottom < 0 then Result.Bottom := r.Bottom; 
end; 
 
function TRenderCell.InternalWidth: Integer; 
var 
    r: TRect; 
begin 
    r := GetPadding; 
    Result := Width - r.Left - r.Right; 
end; 
 
{ TRenderRow } 
 
constructor TRenderRow.Create( _Width: Integer; _Table: TRenderTable ); 
begin 
    inherited Create( _Width, _Table ); 
    FCells := TRenderItems.Create; 
    FForceHeight := 0; 
end; 
 
destructor TRenderRow.Destroy; 
begin 
    inherited; 
    FCells.Free; 
end; 
 
procedure TRenderRow.Draw( Writer: TRenderWriter; X, Y: Integer ); 
var 
    i: Integer; 
begin 
    inherited; 
    if (BgColor >= 0) and (BgColor <> ParentColor) then 
        writer.FillRect( x, y, Width, Height, BgColor ); 
    for i := 0 to Pred(FCells.Count) do begin 
        Cell[i].Draw( Writer, X, Y ); 
        Inc( X, Cell[i].Width ); 
        end; 
end; 
 
function TRenderRow.GetCell(Index: Integer): TRenderCell; 
begin 
    Result := FCells[Index] as TRenderCell; 
end; 
 
function TRenderRow.GetCellCount: Integer; 
begin 
    Result := FCells.Count; 
end; 
 
function TRenderRow.GetHeight: Integer; 
var 
    i, h: Integer; 
begin 
    Result := 0; 
    for i := 0 to Pred(FCells.Count) do begin 
        h := Cell[i].Height; 
        if h > Result then Result := h; 
        end; 
    if Result < FForceHeight then Result := FForceHeight; 
end; 
 
 
function TRenderRow.GetParent: TRenderTable; 
begin 
    Result := FParent as TRenderTable; 
end; 
 
function TRenderRow.Split(Y: Integer; mode: TRenderSplitMode): TRenderAtom; 
var 
    i, h, hh: Integer; 
    row: TRenderRow; 
    cel: TRenderCell; 
begin 
    Result := nil; 
    if Height <= Y then Exit; 
 
    h := 0; 
    hh := 0; 
    for i := 0 to Pred(FCells.Count) do begin 
        if Cell[i].Height < y then 
            hh := Cell[i].Height 
        else begin 
            hh := Integer(Cell[i].Split( Y, splitTest )); 
            if (hh = 0) and (mode <> splitForced) then Exit; 
            end; 
        if hh > h then h := hh; 
        end; 
    if mode = splitTest then begin 
        Result := TRenderAtom(h); 
        Exit; 
        end; 
    if (mode = splitForced) and (hh > 0) then 
        mode := splitRegular; 
 
    Parent.AddRow; 
    row := TRenderRow(Parent.FRows.Extract(Parent.LastRow)); 
    row.BgColor := BgColor; 
    for i := 0 to Pred(FCells.Count) do begin 
        cel := Cell[i].Split( Y, mode ) as TRenderCell; 
        if Assigned(cel) then begin 
            cel.FParent := row; 
            row.FCells[i].Free; 
            row.FCells[i] := cel; 
            end 
        end; 
    Result := row; 
end; 
 
{ TRenderTable } 
 
constructor TRenderTable.Create( _Widths: array of Integer; _Parent: TRenderAtom ); 
var 
    i, w: Integer; 
begin 
    Padding := Rect(0,0,0,0); 
    Border := 0; 
    HasHeader := False; 
    HasFooter := False; 
    FSplitted := False; 
    w := 0; for i := 0 to High(_Widths) do Inc(w,_Widths[i]); 
    inherited Create( w, _Parent ); 
    SetLength( FColWidths, Length( _Widths ) ); 
    for i := 0 to High(_Widths) do 
        FColWidths[i] := _Widths[i]; 
    FRows := TRenderItems.Create; 
end; 
 
destructor TRenderTable.Destroy; 
begin 
    inherited; 
    if FSplitted then begin 
        if HasFooter and (FRows.Count > 0) then 
            FRows.Extract(LastRow); 
        if HasHeader and (FRows.Count > 0) then 
            FRows.Extract(FRows[0]); 
        end; 
    FRows.Free; 
end; 
 
function TRenderTable.AddRow: TRenderRow; 
var 
    row: TRenderRow; 
    i: Integer; 
begin 
    row := TRenderRow.Create( FWidth, Self ); 
    for i := 0 to High(FColWidths) do 
        row.FCells.Add( TRenderCell.Create( FColWidths[i], row ) ); 
    FRows.Add( row ); 
    Result := LastRow; 
end; 
 
function TRenderTable.LastRow: TRenderRow; 
begin 
    if FRows.Count = 0 then 
        AddRow; 
    Result := FRows[FRows.Count-1] as TRenderRow; 
end; 
 
procedure TRenderTable.Draw( Writer: TRenderWriter; X, Y: Integer ); 
var 
    i,j,xx,yy: Integer; 
    brd: Integer; 
begin 
    inherited; 
    if (BgColor >= 0) and (BgColor <> ParentColor) then 
        writer.FillRect( x, y, Width, Height, BgColor ); 
    yy := y; 
    for i := 0 to Pred(FRows.Count) do begin 
        FRows[i].Draw( Writer, X, yy ); 
        Inc( yy, FRows[i].Height ); 
        end; 
    with Writer do begin 
        DrawColor := 0; 
        yy := Y; 
        for i := 0 to FRows.Count do begin 
            xx := X; 
            for j := 0 to Length(FColWidths) do begin 
                if j < Length(FColWidths) then begin 
                    Brd := Border; 
                    if (i > 0) and (Cell[j,i-1].BorderBottom <> -2) then 
                        Brd := Cell[j,i-1].BorderBottom; 
                    if (i < FRows.Count) and (Cell[j,i].BorderTop <> -2) then 
                        Brd := Cell[j,i].BorderTop; 
                    if Brd >= 0 then begin 
                        DrawThinkness := Brd; 
                        DrawLine( xx, yy, FColWidths[j], 0 ); 
                        end; 
                    end; 
                if i < FRows.Count then begin 
                    Brd := Border; 
                    if (j > 0) and (Cell[j-1,i].BorderRight <> -2) then 
                        Brd := Cell[j-1,i].BorderRight; 
                    if (j < Length(FColWidths)) and (Cell[j,i].BorderLeft <> -2) then 
                        Brd := Cell[j,i].BorderLeft; 
                    if Brd >= 0 then begin 
                        DrawThinkness := Brd; 
                        DrawLine( xx, yy, 0, Rows[i].Height ); 
                        end; 
                    end; 
                if j < Length(FColWidths) then 
                    Inc( xx, FColWidths[j] ); 
                end; 
            if i < FRows.Count then 
                Inc( yy, Rows[i].Height ); 
            end; 
        end; 
{ 
        DrawThinkness := border; 
        DrawRect( X, Y, Width, Height ); 
        yy := Y; 
        for i := 0 to Rows.Count-2 do begin 
            Inc( yy, Rows[i].Height ); 
            DrawLine( x, yy, Width, 0 ); 
            end; 
        for i := 0 to High(FColWidths)-1 do begin 
            Inc( X, FColWidths[i] ); 
            DrawLine( X, Y, 0, Height ); 
            end; 
        end; 
} 
end; 
 
function TRenderTable.GetHeight: Integer; 
var 
    i: Integer; 
begin 
    Result := 0; 
    for i := 0 to Pred(RowCount) do 
        Inc( Result, Rows[i].Height ); 
 
end; 
 
function TRenderTable.Split(Y: Integer; mode: TRenderSplitMode ): TRenderAtom; 
var 
    i, yy: Integer; 
    row: TRenderRow; 
    tab: TRenderTable; 
    suby, first, last: Integer; 
begin 
    Result := nil; 
    suby := 0; first := 0; last := Pred(RowCount); 
    if HasHeader then begin 
        suby := Rows[0].Height; 
        Inc(first); 
        end; 
    if HasFooter then begin Inc(suby,LastRow.Height); Dec(last); end; 
    if (Y >= Height) and (Assigned(Parent) or (last = first)) then Exit; 
    Dec(Y,suby); 
    yy := 0; 
    for i := first to last do begin 
        if yy + Rows[i].Height > Y then 
            break; 
        if (Parent = nil) and (i > first) then 
            break; 
        Inc(yy,Rows[i].Height ); 
        end; 
    if Assigned(Parent) or (i = first) then begin 
        if (i > first) and (mode = splitForced) then 
            mode := splitRegular; 
        row := TRenderRow(Rows[i].Split( Y-YY, mode )); 
        end 
    else 
        row := nil; 
    if mode = splitTest then begin 
        if (i > first) or Assigned(row) then 
            Result := TRenderAtom(yy+Integer(row)+suby); 
        Exit; 
        end; 
 
    if (i = first) and (row = nil) and (mode = splitForced) then begin 
//        raise Exception.Create( 'split force' ); 
        i := first+1; 
        end; 
    tab := TRenderTable.Create( FColWidths, nil ); 
    tab.Padding := Padding; 
    tab.BgColor := BgColor; 
    tab.Border  := Border; 
    tab.HasHeader := HasHeader; 
    tab.HasFooter := HasFooter; 
    if HasHeader then begin 
        tab.FRows.Add( FRows[0] ); 
        tab.LastRow.FParent := tab; 
        end; 
    if row <> nil then begin 
        Inc(yy,Rows[i].Height); 
        tab.FRows.Add( row ); 
        row.FParent := tab; 
        Inc(i); 
        end; 
    while i < FRows.Count do begin 
        if HasFooter and (i = Pred(RowCount)) then 
            break; 
        tab.FRows.Add( FRows.Extract(FRows[i]) ); 
        tab.LastRow.FParent := tab; 
        end; 
    if HasFooter then begin 
        tab.FRows.Add( LastRow ); 
        tab.LastRow.FParent := tab; 
        if (Parent = nil) and (FRows.Count > 1) then 
            Rows[RowCount-2].FForceHeight := Rows[RowCount-2].Height + Y-yy; 
        end; 
    FSplitted := True; 
    Result := tab; 
end; 
 
 
function TRenderTable.GetCells(x, y: Integer): TRenderCell; 
begin 
    if (y < 0) or (x < 0) or (x >= Length(FColWidths)) then 
        raise Exception.CreateFmt( 'Cell indexes (%d,%d) out of bounds', [x,y] ); 
    while RowCount <= y do 
        AddRow; 
    Result := Rows[y].Cell[x]; 
end; 
 
procedure TRenderTable.AdjustFooter(Y: Integer); 
begin 
    Dec( Y, Height ); 
    if Y <= 0 then Exit; 
    if not HasFooter then Exit; 
    if RowCount < 2 then Exit; 
    Rows[RowCount-2].FForceHeight := Rows[RowCount-2].Height + Y; 
 
end; 
 
function TRenderTable.GetRow(Index: Integer): TRenderRow; 
begin 
    Result := FRows[Index] as TRenderRow; 
end; 
 
function TRenderTable.GetRowCount: Integer; 
begin 
    Result := FRows.Count; 
end; 
 
procedure TRenderTable.ForceRows(Index: Integer); 
var 
    row: TRenderRow; 
begin 
    if Index > RowCount-3 then begin 
        row := LastRow; 
        FRows.Extract( row ); 
        while Index > RowCount-3 do 
            AddRow; 
        FRows.Add( row ); 
        end; 
end; 
 
constructor TRenderTable.Create(_Widths: array of Integer); 
begin 
    Create( _Widths, nil ); 
end; 
 
{ TRenderWriter } 
 
constructor TRenderWriter.Create; 
begin 
    DrawThinkness := 0; 
    DrawColor := 0; 
    CurPageNum := 0; 
    TotalPages := 0; 
end; 
 
 
{ TRenderImage } 
 
constructor TRenderImage.Create(_graphic: TGraphic; _Raster: Boolean); 
begin 
    inherited Create( _graphic.Width, nil ); 
    FImage := _graphic; 
    FHeight := FImage.Height; 
    FRaster := _Raster; 
end; 
 
constructor TRenderImage.Create(_Width: Integer; _graphic: TGraphic; _Raster: Boolean); 
begin 
    Create( _graphic, _Raster ); 
    FHeight := FHeight * _Width div FWidth; 
    FWidth := _Width; 
end; 
 
constructor TRenderImage.Create(_Width,_Height: Integer; _graphic: TGraphic; _Raster: Boolean); 
begin 
    Create( _graphic, _raster ); 
    FHeight := _Height; 
    FWidth := _Width; 
end; 
 
procedure TRenderImage.Draw(Writer: TRenderWriter; X, Y: Integer); 
begin 
    inherited; 
    Writer.DrawImage( X, Y, FWidth, FHeight, FImage, FRaster ); 
end; 
 
{ TRenderWord } 
 
constructor TRenderWord.Create(_Word: WideString; _Font: TFont); 
begin 
    FWord := _Word; 
    FFont := TFont.Create; 
    FFont.Assign(_Font); 
    if FFont.Height < 0 then 
        FFont.Height := -FFont.Height; 
    SetFontForCalc( FFont ); 
    if (FWord = '@PAGE@') or (FWord = '@PAGES@') then 
        FXWidth := CalcStringWidth( '0123456789' ) 
    else 
        FXWidth := CalcStringWidth( FWord ); 
    inherited Create( FXWidth div 100, nil ); 
    FHSpacing := CalcStringWidth( ' ' ); 
    FHeight := FFont.Height; 
end; 
 
destructor TRenderWord.Destroy; 
begin 
    inherited; 
    FFont.Free; 
end; 
 
procedure TRenderWord.Draw(Writer: TRenderWriter; X, Y: Integer); 
begin 
    inherited; 
    Writer.DrawWord( x*100, y*100, FWord, FFont ); 
end; 
 
function TRenderWord.XWidth: Integer; 
begin 
    Result := FXWidth; 
end; 
 
{ TRenderSequence } 
type 
  TRenderSplit = class(TRenderAtom) 
  end; 
 
constructor TRenderSequence.Create( _Text: WideString; _Font: TFont;  _Align: TRenderTextAlign; _Link: String ); 
begin 
    inherited Create( 0, nil ); 
    FSplitUp := False; 
    FSplitDown := False; 
    FItems := TRenderItems.Create; 
    FAlign := _Align; 
    if _Text <> '' then 
        AddText( _Text, _Font, _Link ); 
end; 
 
procedure TRenderSequence.AddText( _Text: WideString; _Font: TFont; _Link: String ); 
var 
    i, j: Integer; 
    wrd: WideString; 
    saveCount: Integer; 
begin 
    saveCount := FItems.Count; 
    j := 1; 
    if FAlign = rtaTerminal then begin 
        i := 1; 
        while i <= Length(_Text) do begin 
            wrd := ''; 
            while (i <= Length(_Text)) and (_Text[i] <> #10) do begin 
                if _Text[i] <> #13 then 
                    wrd := wrd + _Text[i]; 
                Inc(i); 
                end; 
            if wrd <> '' then 
                FItems.Add( TRenderWord.Create( wrd, _Font ) ); 
            if i <= Length(_Text) then begin 
                if (FItems.Count = 0) or (FItems[FItems.Count-1] is TRenderSplit) then 
                    FItems.Add( TRenderWord.Create( ' ', _Font ) ); 
                NewLine; 
                Inc(i); 
                end; 
            end; 
        if _Link <> '' then 
            while saveCount < FItems.Count do begin 
                FItems[saveCount].LinkTarget := _Link; 
                Inc(saveCount); 
                end; 
        Exit; 
        end; 
    while j <= Length( _Text ) do begin 
        i := j; 
        wrd := ''; 
        while (i <= Length(_Text)) and (_Text[i] <= ' ') do begin 
            if (_Text[i] = #13) or (_Text[i] = #10) then begin 
                if (FItems.Count = 0) or (FItems[FItems.Count-1] is TRenderSplit) then 
                    FItems.Add( TRenderWord.Create( ' ', _Font ) ); 
                NewLine; 
                end; 
            Inc(i); 
            end; 
        j := i; 
        while (j <= Length(_Text)) and (_Text[j] > ' ') do begin 
            wrd := wrd + _Text[j]; 
            Inc(j); 
            end; 
        if wrd = '' then break; 
        FItems.Add( TRenderWord.Create( wrd, _Font ) ); 
        end; 
    if _Link <> '' then 
        while saveCount < FItems.Count do begin 
            FItems[saveCount].LinkTarget := _Link; 
            Inc(saveCount); 
            end; 
end; 
 
destructor TRenderSequence.Destroy; 
begin 
    inherited; 
    FItems.Free; 
end; 
 
procedure TRenderSequence.Draw(Writer: TRenderWriter; X, Y: Integer); 
var 
    w: Integer; 
    i,j,curw, curspc, ww, spc, sumspc, maxh: Integer; 
    r: TRect; 
    Indent: Integer; 
    PrevLink, SaveLink: String; 
    PrevLinkRect: TRect; 
    Underline: Boolean; 
    PrevUnderlineLeft, PrevUnderlineRight: Integer; 
    UnderlineColor: Integer; 
 
procedure MakeLink; 
begin 
    if PrevLink = '' then Exit; 
    Writer.SetLink( PrevLink, PrevLinkRect ); 
    PrevLink := ''; 
end; 
 
procedure MakeUnderline; 
begin 
    if PrevUnderlineLeft = -1 then Exit; 
    Writer.DrawThinkness := maxh div 20; 
    Writer.DrawColor := UnderlineColor; 
    Writer.DrawLine( PrevUnderlineLeft, y + maxh * 9 div 10, PrevUnderlineRight-PrevUnderlineLeft, 0 ); 
    PrevUnderlineLeft := -1; 
end; 
 
procedure Draw; 
var 
    xx: Integer; 
    sw, asw: Integer; 
    n,k: Integer; 
    s: String; 
begin 
    inherited; 
    PrevLink := ''; 
    PrevUnderlineLeft := -1; 
    sw := sumspc; 
 
    case FAlign of 
        rtaRight: xx := w - ww - sw; 
        rtaCenter: xx := (w - ww - sw) div 2; 
        else xx := 0; 
        end; 
    Inc(xx,Indent); 
    asw := w - ww - sw; 
    n := 1; 
    k := i - j - 1; 
 
    asw := asw; 
    xx := xx; 
    while j < i do begin 
        SaveLink := FItems[j].LinkTarget; 
        FItems[j].LinkTarget := ''; 
        Underline := False; 
        if FItems[j] is TRenderWord then begin 
            s := TRenderWord(FItems[j]).FAlterWord; 
            if s = '' then s := TRenderWord(FItems[j]).FWord; 
            Writer.DrawWord( x+xx, (y+maxh-FItems[j].Height)*100, s, TRenderWord(FItems[j]).FFont ); 
            Underline := fsUnderline in TRenderWord(FItems[j]).FFont.Style; 
            end 
        else 
            FItems[j].Draw( writer, (x + xx) div 100, y+maxh-FItems[j].Height ); 
        if Underline then begin 
            if PrevUnderlineLeft = -1 then begin 
                PrevUnderlineLeft := (x+xx) div 100; 
                UnderlineColor := TRenderWord(FItems[j]).FFont.Color; 
                end; 
            PrevUnderlineRight := ((x+xx) div 100) + FItems[j].Width; 
            end 
        else 
            MakeUnderline; 
        FItems[j].LinkTarget := SaveLink; 
        if SaveLink <> PrevLink then begin 
            MakeLink; 
            PrevLink := SaveLink; 
            PrevLinkRect := Rect( (x + xx) div 100, y, x+xx+FItems[j].Width, y+maxh ); 
            end 
        else if PrevLink <> '' then 
            PrevLinkRect.Right := (x+xx) div 100+FItems[j].Width; 
        Inc(xx,FItems[j].XWidth); 
        if j < i-1 then begin 
            spc := FItems[j].FHSpacing; 
            if spc < FItems[j+1].FHSpacing then 
                spc := FItems[j+1].FHSpacing; 
            Inc(xx,spc); 
            end; 
        if (FAlign = rtaJustify) and (k <> 0) and not (((i = FItems.Count) and not FSplitDown) or (FItems[i-1] is TRenderSplit)) then 
            Inc(xx,asw * n div k - asw * (n-1) div k); 
        Inc(n); 
        Inc(j); 
        end; 
    MakeLink; 
    MakeUnderline; 
end; 
 
begin 
    x := x * 100; 
    if FItems.Count = 0 then Exit; 
    r := TRenderCell(Parent).GetPadding; 
    w := (Parent.Width-r.Left-r.Right) * 100; 
    Indent := TRenderCell(Parent).Indent * 100; 
    if FSplitUp or not (FAlign in [rtaLeft,rtaJustify]) then Indent := 0; 
 
    for i := 1 to Pred(FItems.Count) do 
        if FItems[i] is TRenderWord then with TRenderWord(FItems[i]) do 
            if (FWord = '@PAGE@') or (FWord = '@PAGES@') then begin 
                if FWord = '@PAGE@' then FAlterWord := IntToStr( Writer.CurPageNum ) else FAlterWord := IntToStr( Writer.TotalPages ); 
                SetFontForCalc( FFont ); 
                FXWidth := CalcStringWidth( FAlterWord ); 
                FWidth := FXWidth div 100; 
                end; 
 
 
    curw := FItems[0].XWidth + Indent; 
    maxh := FItems[0].Height; 
    curspc := FItems[0].FHSpacing; 
    ww := curw; 
    j := 0; 
    sumspc := 0; 
    for i := 1 to Pred(FItems.Count) do begin 
        spc := FItems[i].FHSpacing; 
        if spc > curspc then 
            curspc := spc; 
        Inc(curw,curspc+FItems[i].XWidth); 
        Inc(sumspc,curspc); 
        if (curw > w) or (FItems[i-1] is TRenderSplit) and not (FItems[i] is TRenderSplit) then begin 
            Dec(sumspc,curspc); 
            Draw; 
            Indent := 0; 
            Inc(Y, maxh); 
            curw := FItems[i].XWidth; 
            maxh := FItems[i].Height; 
            sumspc := 0; 
            ww := 0; 
            end; 
        if maxh < FItems[i].Height then 
            maxh := FItems[i].Height; 
        curspc := spc; 
        Inc(ww,FItems[i].XWidth); 
        end; 
    i := FItems.Count; 
    Draw; 
end; 
 
function TRenderSequence.GetHeight: Integer; 
var 
    w: Integer; 
    i,curw,maxh,spc: Integer; 
    r: TRect; 
    Indent: Integer; 
begin 
    Result := 0; 
    if FItems.Count = 0 then 
        Exit; 
    r := TRenderCell(Parent).GetPadding; 
    w := (Parent.Width-r.Left-r.Right) * 100; 
    Indent := TRenderCell(Parent).Indent * 100; 
    if FSplitUp or not (FAlign in [rtaLeft,rtaJustify]) then Indent := 0; 
    curw := FItems[0].XWidth + Indent; 
    maxh := FItems[0].Height; 
    for i := 1 to Pred(FItems.Count) do begin 
        spc := FItems[i-1].FHSpacing; 
        if spc < FItems[i].FHSpacing then 
            spc := FItems[i].FHSpacing; 
        Inc(curw,spc+FItems[i].XWidth); 
        if (curw > w) or (FItems[i-1] is TRenderSplit) and not (FItems[i] is TRenderSplit) then begin 
            Inc(Result,maxh); 
            curw := FItems[i].XWidth; 
            maxh := 0; 
            end; 
        if maxh < FItems[i].Height then 
            maxh := FItems[i].Height; 
        end; 
    Inc(Result,maxh); 
end; 
 
function TRenderSequence.Split(Y: Integer; mode: TRenderSplitMode): TRenderAtom; 
var 
    h, w, spc, curw, maxh, i, splitPos: Integer; 
    txt: TRenderSequence; 
    r: TRect; 
    Indent: Integer; 
begin 
    Result := nil; 
    if Height <= Y then Exit; 
    r := TRenderCell(Parent).GetPadding; 
    w := (Parent.Width-r.Left-r.Right) * 100; 
    Indent := TRenderCell(Parent).Indent * 100; 
    if FSplitUp or not (FAlign in [rtaLeft,rtaJustify]) then Indent := 0; 
    curw := FItems[0].XWidth + Indent; 
    maxh := FItems[0].Height; 
    splitPos := 0; 
    h := 0; 
    for i := 1 to Pred(FItems.Count) do begin 
        spc := FItems[i-1].FHSpacing; 
        if spc < FItems[i].FHSpacing then 
            spc := FItems[i].FHSpacing; 
        Inc(curw,spc+FItems[i].XWidth); 
        if (curw > w) or (FItems[i-1] is TRenderSplit) and not (FItems[i] is TRenderSplit) then begin 
            if h + maxh > Y then 
                break; 
            splitPos := i; 
            Inc(h,maxh); 
            curw := FItems[i].XWidth; 
            maxh := 0; 
            end; 
        if maxh < FItems[i].Height then 
            maxh := FItems[i].Height; 
        end; 
    if mode = splitTest then begin 
        Result := TRenderAtom(h); 
        Exit; 
        end; 
    txt := TRenderSequence.Create( '', nil, FAlign ); 
    while splitPos < FItems.Count do 
        txt.FItems.Add( FItems.Extract( FItems[SplitPos] ) ); 
    Result := Txt; 
    FSplitDown := True; 
    txt.FSplitUp := True; 
end; 
 
procedure TRenderSequence.NewLine; 
begin 
    FItems.Add( TRenderSplit.Create( 0, Self ) ); 
end; 
 
procedure TRenderSequence.AddItem(_Item: TRenderAtom); 
begin 
    FItems.Add( _Item ); 
end; 
 
{ TRenderTextLink } 
 
 
end.