{ Version 11.9 Copyright (c) 1995-2008 by L. David Baldwin Copyright (c) 2008-2018 by HtmlViewer Team Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. Note that the source modules HTMLGIF1.PAS and DITHERUNIT.PAS are covered by separate copyright notices located in those modules. } {$I frxHTMLCons.inc} { This module is comprised mostly of the various Section object definitions. As the HTML document is parsed, it is divided up into sections. Some sections are quite simple, like TParagraphSpace. Others are more complex such as TfrxHtSection which can hold a complete paragraph. The HTML document is then stored as a list of type TfrxHtDocument, of the various sections. Closely related to TfrxHtDocument is TfrxHtCell. TfrxHtCell holds the list of sections for each cell in a Table (the TfrxHtmlTable section). In this way each table cell may contain a document of it's own. The Section objects each store relevant data for the section such as the text, fonts, images, and other info needed for formating. Each Section object is responsible for its own formated layout. The layout is done in the DrawLogic method. Layout for the whole document is done in the TfrxHtDocument.DoLogic method which essentially just calls all the Section DrawLogic's. It's only necessary to call TfrxHtDocument.DoLogic when a new layout is required (when the document is loaded or when its width changes). Each Section is also responsible for drawing itself (its Draw method). The whole document is drawn with the TfrxHtDocument.Draw method. } unit frxHTMLSubs; {-$define DO_BLOCK_INLINE} {$ifdef DO_BLOCK_INLINE} {$endif} //TODO -oBG, 15.03.2014: support display:inline {-$define DO_PD_INLINE} //TODO -oBG, 15.03.2014: support display:inline {$ifdef DO_PD_INLINE} {$endif} interface uses {$ifdef UseInline} frxHTMLCaches, {$endif} {$ifdef VCL} Windows, frxNetUtils, {$endif} {$IFDEF FPC} frxNetUtils, {$ENDIF} Messages, Graphics, Classes, SysUtils, Variants, Math, Contnrs, {$ifdef LCL} LclIntf, LclType, Types, frxHTMLMisc, {$endif} frxHTMLGlobals, frxHTMLFonts, frxHTMLStyleTypes, frxHTMLImages, // use before HTMLUn2, as both define a TGetImageEvent, but we need the one of HTMLUn2 here. frxHTMLUn2, frxHTMLBuffer, frxHTMLSymb, frxHTMLStyleUn, frxHTMLURLSubs, frxGif2, frxMD5; type TfrxHtBlock = class; TfrxHtSection = class; TfrxHtCellBasic = class; TfrxHtDocument = class; //------------------------------------------------------------------------------ // TfrxHtFontObj, contains an atomic font info for a part of a section. // TfrxHtFontList, contains and owns all font infos of a section // TfrxHtLinkList, references but does not own all link infos of a document. The objects are still owned by their sections. //------------------------------------------------------------------------------ TfrxHtFontList = class; TfrxHtFontObj = class(TfrxHtFontObjBase) {font information} // BG, 10.08.2013: deprecated // Is used to handle the link states, but the full range of CSS // properties can be applied to :link, :hover, :visited, etc. // private Title: ThtString; FYValue: Integer; Active: Boolean; function GetURL: ThtString; procedure CreateFIArray; public Pos: Integer; {0..Len Index where font takes effect} TheFont: TfrxHtFont; FIArray: TFontInfoObj; FontHeight, {tmHeight+tmExternalLeading} tmHeight, tmMaxCharWidth, Overhang, Descent: Integer; SScript: ThtAlignmentStyle; constructor Create(ASection: TfrxHtSection; F: TfrxHtFont; Position: Integer); constructor CreateCopy(ASection: TfrxHtSection; T: TfrxHtFontObj); destructor Destroy; override; procedure ReplaceFont(F: TfrxHtFont); procedure ConvertFont(const FI: TfrxHtFontInfo); procedure FontChanged; function GetOverhang: Integer; function GetHeight(var Desc: Integer): Integer; property URL: ThtString read GetURL; property DrawYY: Integer read FYValue; end; // BG, 10.02.2013: owns its objects. TfrxHtFontList = class(TfrxHtFontObjBaseList) {a list of TfrxHtFontObj's} private function GetFont(Index: Integer): TfrxHtFontObj; {$ifdef UseInline} inline; {$endif} public constructor CreateCopy(ASection: TfrxHtSection; T: TfrxHtFontList); function GetFontAt(Posn: Integer; out OHang: Integer): TfrxHtFont; // function GetFontCountAt(Posn, Leng: Integer): Integer; function GetFontObjAt(Posn: Integer): TfrxHtFontObj; overload; function GetFontObjAt(Posn, Leng: Integer; out Obj: TfrxHtFontObj): Integer; overload; procedure Decrement(N: Integer; Document: TfrxHtDocument); property Items[Index: Integer]: TfrxHtFontObj read GetFont; default; end; // BG, 10.02.2013: does not own its font objects. TfrxHtLinkList = class(TfrxHtFontList) public constructor Create; end; //------------------------------------------------------------------------------ // TfrxHtmlNode is base class for all objects in the HTML document tree. //------------------------------------------------------------------------------ TfrxHtmlNode = class(TfrxHtIDObject) private FDocument: TfrxHtDocument; // the document it belongs to FOwnerBlock: TfrxHtBlock; // the parental block it is placed in FOwnerCell: TfrxHtCellBasic; // the parent's child list it is placed in FAttributes: TfrxHtAttributeList; FProperties: TfrxHTProperties; FDisplay: ThtDisplayStyle; // how it is displayed FEmSize, FExSize: Integer; function GetSymbol(): TElemSymb; function GetAttribute(const Name: ThtString): ThtString; function GetContainingBlock: TfrxHtBlock; virtual; function GetProperty(PropIndex: ThtPropIndices): Variant; function GetContainingBox: TRect; function GetClearSpace(IMgr: TfrxHtIndentManager; Y: Integer): Integer; protected FPositioning: ThtBoxPositionStyle; FPositions: ThtRectIntegers; // in Pixels FFloating: ThtAlignmentStyle; FClearing: ThtClearStyle; FIndent: Integer; {Indentation of floating object relative to Draw-X} procedure GetVMarginArray(var MArray: ThtVMarginArray); function TryGetClear(var Clear: ThtClearStyle): Boolean; virtual; function CalcDisplayExtern: ThtDisplayStyle; virtual; function CalcDisplayIntern: ThtDisplayStyle; virtual; function FindAttribute(const Name: ThtString; out Attribute: TfrxHtAttribute): Boolean; overload; function IsCopy: Boolean; virtual; property ContainingBox: TRect read GetContainingBox; public constructor Create(Parent: TfrxHtCellBasic; Attributes: TfrxHtAttributeList; Properties: TfrxHTProperties; const ID: ThtString); constructor CreateCopy(Parent: TfrxHtCellBasic; Source: TfrxHtmlNode); virtual; destructor Destroy; override; function IsInFlow: Boolean; {$ifdef UseInline} inline; {$endif} function IndexOf(Child: TfrxHtmlNode): Integer; virtual; procedure AfterConstruction; override; property Symbol: TElemSymb read GetSymbol; property OwnerBlock: TfrxHtBlock read FOwnerBlock; //BG, 07.02.2011: public for reading document structure (see issue 24). Renamed from MyBlock to Owner to clarify the relation. property OwnerCell: TfrxHtCellBasic read FOwnerCell write FOwnerCell; property Document: TfrxHtDocument read FDocument; property Display: ThtDisplayStyle read FDisplay write FDisplay; property EmSize: Integer read FEmSize write FEmSize; property ExSize: Integer read FExSize write FExSize; property AttributeValue[const AttrName: ThtString]: ThtString read GetAttribute; property PropertyValue[PropIndex: ThtPropIndices]: Variant read GetProperty; end; //------------------------------------------------------------------------------ // TfrxHtSectionBase, the abstract base class for all document sections //------------------------------------------------------------------------------ // Each block is a section (see TfrxHtBlock and its derivates) and a series of text // and non block building "inline" tags is held in a section (see TfrxHtSection) // // Base class for TfrxHtSection, TfrxHtBlock, TfrxHtmlTable, TfrxHtPage and TfrxHtHorzLine //------------------------------------------------------------------------------ TfrxHtSectionBase = class(TfrxHtmlNode) protected OwnerIndent: Integer; function GetYPosition: Integer; override; procedure SetDocument(List: TfrxHtDocument); public // source buffer reference StartCurs: Integer; // where the section starts in the source buffer. Len: Integer; // number of bytes in source buffer the section represents. // Z coordinates are calculated in Create() ZIndex: Integer; // Y coordinates calculated in DrawLogic1() are still valid in Draw1() YDraw: Integer; // where the section starts. DrawTop: Integer; // where the border starts. In case of a block this is YDraw + MarginTop ContentTop: Integer; // where the content starts. In case of a block this is YDraw + MarginTop + BorderTopWidth + PaddingTop ContentBot: Integer; // where the section ends. In case of a block this is Block.ClientContentBot + PaddingBottom + BorderBottomWidth + MarginBottom DrawBot: Integer; // where the border ends. In case of a block this is Max(Block.ClientContentBot, MyCell.tcDrawBot) + PaddingBottom + BorderBottomWidth SectionHeight: Integer; // pixel height of section. = ContentBot - YDraw DrawHeight: Integer; // floating image may overhang. = Max(ContentBot, DrawBot) - YDraw // X coordinates calculated in DrawLogic1() may be shifted in Draw1(), if section is centered or right aligned TagClass: ThtString; {debugging aid} constructor Create(Parent: TfrxHtCellBasic; Attributes: TfrxHtAttributeList; Properties: TfrxHTProperties; const TheId, TheClass: ThtString); overload; constructor Create(Parent: TfrxHtCellBasic; Attributes: TfrxHtAttributeList; Properties: TfrxHTProperties); overload; constructor CreateCopy(Parent: TfrxHtCellBasic; Source: TfrxHtmlNode); override; function DrawLogic1(Canvas: TCanvas; X, Y, XRef, YRef, AWidth, AHeight, BlHt: Integer; IMgr: TfrxHtIndentManager; var MaxWidth, Curs: Integer; ARemainHeight: Integer): Integer; virtual; function Draw1(Canvas: TCanvas; const ARect: TRect; IMgr: TfrxHtIndentManager; X, XRef, YRef: Integer): Integer; virtual; function FindDocPos(SourcePos: Integer; Prev: Boolean): Integer; virtual; function FindSourcePos(DocPos: Integer): Integer; virtual; function FindString(From: Integer; const ToFind: UnicodeString; MatchCase: Boolean): Integer; virtual; function FindStringR(From: Integer; const ToFind: UnicodeString; MatchCase: Boolean): Integer; virtual; function GetChAtPos(Pos: Integer; out Ch: WideChar; out Obj: TfrxHtSectionBase): Boolean; virtual; procedure AddSectionsToList; virtual; procedure MinMaxWidth(Canvas: TCanvas; out Min, Max: Integer; AvailableWidth, AvailableHeight: Integer); virtual; end; TfrxHtSectionBaseList = class(TObjectList) private function GetItem(Index: Integer): TfrxHtSectionBase; {$ifdef UseInline} inline; {$endif} public function FindDocPos(SourcePos: Integer; Prev: Boolean): Integer; virtual; property Items[Index: Integer]: TfrxHtSectionBase read GetItem; default; end; //------------------------------------------------------------------------------ // TfrxHtFloatingObj, an inline block for floating blocks. //------------------------------------------------------------------------------ TfrxHtBlockBase = class(TfrxHtSectionBase) public constructor Create(Parent: TfrxHtCellBasic; Position: Integer; Attributes: TfrxHtAttributeList; Prop: TfrxHTProperties); constructor CreateCopy(Parent: TfrxHtCellBasic; Source: TfrxHtmlNode); override; function GetPosition(const Index: ThtRectEdge): Integer; property Positioning: ThtBoxPositionStyle read FPositioning; property Floating: ThtAlignmentStyle read FFloating; property Indent: Integer read FIndent; {Indentation of floated object} end; TfrxHtFloatingObj = class(TfrxHtBlockBase) protected // begin copy by move() // source buffer reference VertAlign: ThtAlignmentStyle; HSpaceL, HSpaceR: Integer; {horizontal extra space} VSpaceT, VSpaceB: Integer; {vertical extra space} PercentWidth: Boolean; {if width is percent} PercentHeight: Boolean; {if height is percent} // end copy by move() function Clone(Parent: TfrxHtCellBasic): TfrxHtFloatingObj; function GetClientHeight: Integer; virtual; abstract; function GetClientWidth: Integer; virtual; abstract; procedure SetClientHeight(Value: Integer); virtual; abstract; procedure SetClientWidth(Value: Integer); virtual; abstract; public DrawYY: Integer; // where the object starts. DrawXX: Integer; // where the object starts. constructor Create(Parent: TfrxHtCellBasic; Position: Integer; L: TfrxHtAttributeList; Prop: TfrxHTProperties); constructor CreateCopy(Parent: TfrxHtCellBasic; Source: TfrxHtmlNode); override; function DrawLogic1(Canvas: TCanvas; X, Y, XRef, YRef, AWidth, AHeight, BlHt: Integer; IMgr: TfrxHtIndentManager; var MaxWidth, Curs: Integer; ARemainHeight: Integer = 0): Integer; override; function Draw1(Canvas: TCanvas; const ARect: TRect; IMgr: TfrxHtIndentManager; X, XRef, YRef: Integer): Integer; override; function GetYPosition: Integer; override; procedure DrawLogicInline(Canvas: TCanvas; FO: TfrxHtFontObj; AvailableWidth, AvailableHeight: Integer); virtual; abstract; procedure DrawInline(Canvas: TCanvas; X, Y, YBaseline: Integer; FO: TfrxHtFontObj); virtual; abstract; property ClientHeight: Integer read GetClientHeight write SetClientHeight; property ClientWidth: Integer read GetClientWidth write SetClientWidth; function TotalHeight: Integer; {$ifdef UseInline} inline; {$endif} function TotalWidth: Integer; {$ifdef UseInline} inline; {$endif} end; TFloatingObjClass = class of TfrxHtFloatingObj; TfrxFrHtImageObj = class; TFloatingObjList = class(TObjectList) {a list of TfrxHtFloatingObj's} private function GetItem(Index: Integer): TfrxHtFloatingObj; {$ifdef UseInline} inline; {$endif} procedure SetItem(Index: Integer; const Item: TfrxHtFloatingObj); {$ifdef UseInline} inline; {$endif} public constructor CreateCopy(Parent: TfrxHtCellBasic; T: TFloatingObjList); procedure Decrement(N: Integer); {$ifdef UseInline} inline; {$endif} function FindObject(Posn: Integer): TfrxHtFloatingObj; {$ifdef UseInline} inline; {$endif} // GetObjectAt() returns number of positions from Posn to next object. If it returns 0, Obj is at Posn. function GetObjectAt(Posn: Integer; out Obj): Integer; property Items[Index: Integer]: TfrxHtFloatingObj read GetItem write SetItem; default; end; //------------------------------------------------------------------------------ // TfrxHtCellBasic, the base class for content //------------------------------------------------------------------------------ // Base class for table cells, block content and the entire document //------------------------------------------------------------------------------ //BG, 02.12.2013: handling display:inline is too complex in this class structure, where TfrxHtSection is both home of a couple // of HTML elements, the renderer and the rendering results (in the ThtLineRecs). // // A straight forward rendering would require overall Lines collecting the text of all consecutive inline level elements // no matter where it is defined in the document tree. // // //BG, 01.12.2013: handle display:inline // //This list mixes up block elements and autogenerated anonymous blocks for consecutive inline level elements based on // // TInlineSection. All objects based on TInlineSection are owned (and therefore freed) by this list all other kind of // // objects are assumed to be referenced and thus are not freed. // TRenderSectionBaseList = class(TfrxHtSectionBaseList) // protected // procedure Notify(Ptr: Pointer; Action: TListNotification); override; // end; TfrxHtCellBasic = class(TfrxHtSectionBaseList) {a list of sections and blocks} private FDocument: TfrxHtDocument; // the document it belongs to FOwnerBlock: TfrxHtBlock; // the parental block it is placed in // FRenderList: TRenderSectionBaseList; //BG, 01.12.2013: The list of block level elements to render. Filled in DoLogic() protected function CalcDisplayExtern: ThtDisplayStyle; // returns either pdInline or pdBlock public // source buffer reference StartCurs: Integer; // where the section starts in the source buffer. Len: Integer; // number of bytes in source buffer the section represents. // IMgr: TfrxHtIndentManager; // Each element displayed as a block needs an indent manager. BkGnd: Boolean; BkColor: TColor; tcDrawTop: Integer; tcContentBot: Integer; tcDrawBot: Integer; constructor Create(Parent: TfrxHtBlock); constructor CreateCopy(Parent: TfrxHtBlock; T: TfrxHtCellBasic); destructor Destroy; override; function CheckLastBottomMargin: Boolean; function DoLogic(Canvas: TCanvas; Y, Width, AHeight, BlHt: Integer; var ScrollWidth, Curs: Integer; ARemainHeight: Integer = 0): Integer; virtual; function Draw(Canvas: TCanvas; ARect: TRect; ClipWidth, X, Y, XRef, YRef: Integer): Integer; virtual; function FindSourcePos(DocPos: Integer): Integer; function FindString(From: Integer; const ToFind: UnicodeString; MatchCase: Boolean): Integer; function FindStringR(From: Integer; const ToFind: UnicodeString; MatchCase: Boolean): Integer; function GetChAtPos(Pos: Integer; out Ch: WideChar; out Obj: TfrxHtSectionBase): Boolean; function IsCopy: Boolean; procedure Add(Item: TfrxHtSectionBase; TagIndex: Integer); overload; procedure Add(var Section: TfrxHtSection; TagIndex: Integer); overload; procedure AddSectionsToList; {$ifdef UseFormTree} procedure FormTree(const Indent: ThtString; var Tree: ThtString); {$endif UseFormTree} procedure MinMaxWidth(Canvas: TCanvas; out Min, Max: Integer; AvailableWidth, AvailableHeight: Integer); virtual; property Document: TfrxHtDocument read FDocument; {the TfrxHtDocument that holds the whole document} property OwnerBlock: TfrxHtBlock read FOwnerBlock; end; TfrxHtCell = class(TfrxHtCellBasic) private DrawYY: Integer; public constructor Create(Parent: TfrxHtBlock); constructor CreateCopy(Parent: TfrxHtBlock; T: TfrxHtCellBasic); destructor Destroy; override; function DoLogic(Canvas: TCanvas; Y, Width, AHeight, BlHt: Integer; var ScrollWidth, Curs: Integer; ARemainHeight: Integer = 0): Integer; override; function Draw(Canvas: TCanvas; ARect: TRect; ClipWidth, X, Y, XRef, YRef: Integer): Integer; override; end; //------------------------------------------------------------------------------ // TfrxHtSizeableObj is base class for floating objects TfrxFrHtImageObj, TFrameOBj and TPanelObj. // // These objects may appear in text flow or attribute ALIGN or style FLOAT or // style POSITION may push them out of the flow floating to the left or right // side or anywhere else in the containing block or even fixed in the viewport // resp. on printed pages. //------------------------------------------------------------------------------ TfrxHtSizeableObj = class(TfrxHtFloatingObj) private FAlt: ThtString; {the alt= attribute} FClientHeight: Integer; {does not include VSpace} FClientWidth: Integer; {does not include HSpace} public ClientSizeKnown: Boolean; {know size of image} SpecWidth: Integer; {as specified by tag} SpecHeight: Integer; {as specified by tag} Title: ThtString; protected procedure CalcSize(AvailableWidth, AvailableHeight, SetWidth, SetHeight: Integer; IsClientSizeSpecified: Boolean); function GetClientHeight: Integer; override; function GetClientWidth: Integer; override; procedure SetClientHeight(Value: Integer); override; procedure SetClientWidth(Value: Integer); override; public NoBorder: Boolean; {set if don't want blue border} BorderWidth: Integer; MargArray: ThtMarginArray; constructor Create(Parent: TfrxHtCellBasic; Position: Integer; L: TfrxHtAttributeList; Prop: TfrxHTProperties); constructor CreateCopy(Parent: TfrxHtCellBasic; Source: TfrxHtmlNode); override; constructor SimpleCreate(Parent: TfrxHtCellBasic); procedure DrawLogicInline(Canvas: TCanvas; FO: TfrxHtFontObj; AvailableWidth, AvailableHeight: Integer); override; procedure DrawInline(Canvas: TCanvas; X, Y, YBaseline: Integer; FO: TfrxHtFontObj); override; procedure ProcessProperties(Prop: TfrxHTProperties); procedure SetAlt(CodePage: Integer; const Value: ThtString); property Alt: ThtString read FAlt; end; TfrxHtSizeableObjList = class(TFloatingObjList) {a list of TfrxFrHtImageObj's, TPanelObj's , and TFrameObj's} end; // inline image node TfrxFrHtImageObj = class(TfrxHtSizeableObj) private FSource: ThtString; FImage: TfrxHtImage; OrigImage: TfrxHtImage; {same as above unless swapped} Transparent: ThtImageTransparency; {None, Lower Left Corner, or Transp GIF} AltHeight, AltWidth: Integer; function GetGraphic: TGraphic; procedure DoDraw(Canvas: TCanvas; XX, Y: Integer; ddImage: TfrxHtImage); public ObjHeight, ObjWidth: Integer; {width as drawn} IsMap, UseMap: Boolean; MapName: ThtString; Swapped: Boolean; {image has been replaced} Missing: Boolean; {waiting for image to be downloaded} constructor Create(Parent: TfrxHtCellBasic; Position: Integer; L: TfrxHtAttributeList; Prop: TfrxHTProperties); constructor SimpleCreate(Parent: TfrxHtCellBasic; const AnURL: ThtString); constructor CreateCopy(Parent: TfrxHtCellBasic; Source: TfrxHtmlNode); override; procedure DrawLogicInline(Canvas: TCanvas; FO: TfrxHtFontObj; AvailableWidth, AvailableHeight: Integer); override; procedure DrawInline(Canvas: TCanvas; X: Integer; Y, YBaseline: Integer; FO: TfrxHtFontObj); override; property Graphic: TGraphic read GetGraphic; property Image: TfrxHtImage read FImage ; //write SetImage; property Source: ThtString read FSource; {the src= attribute} end; TfrxHtDrawList = class(TObjectList) procedure AddImage(Obj: TfrxFrHtImageObj; Canvas: TCanvas; X, Y, YBaseline: Integer; FO: TfrxHtFontObj); procedure DrawImages; end; //------------------------------------------------------------------------------ // TfrxHtSection holds a series of text and inline tags like images and panels. //------------------------------------------------------------------------------ // Holds tags like A, B, I, FONT, SPAN, IMG, ... //------------------------------------------------------------------------------ TfrxHtBorderRec = class {record for inline borders} private BStart, BEnd: Integer; OpenStart, OpenEnd: Boolean; BRect: TRect; MargArray: ThtMarginArray; procedure DrawTheBorder(Canvas: TCanvas; XOffset, YOffSet: Integer; Printing: Boolean); end; TfrxHtBorderRecList = class(TObjectList) private function Get(Index: Integer): TfrxHtBorderRec; {$ifdef UseInline} inline; {$endif} public property Items[Index: Integer]: TfrxHtBorderRec read Get; default; end; TfrxHtLineRec = class {holds info on a line of text} private Start: PWideChar; SpaceBefore, SpaceAfter, LineHt, {total height of line} LineImgHt, {top to bottom including any floating image} Ln, {# chars in line} Descent, LineIndent: Integer; // relative to section's left edge DrawXX, DrawWidth: Integer; DrawY: Integer; Spaces, Extra: Integer; BorderList: TfrxHtBorderRecList; {List of inline borders (TfrxHtBorderRec's) in this Line} FirstDraw: Boolean; {set if border processing needs to be done when first drawn} Drawn: Boolean; // set if drawn by section and thus actual position has been calculated FirstX: Integer; {x value at FirstDraw} Shy: Boolean; public constructor Create(SL: TfrxHtDocument); procedure Clear; destructor Destroy; override; end; TfrxHtLineRecList = class(TObjectList) private function Get(Index: Integer): TfrxHtLineRec; {$ifdef UseInline} inline; {$endif} public property Items[Index: Integer]: TfrxHtLineRec read Get; default; end; PXArray = array of Integer; TfrxHtIndexObj = class public Pos: Integer; Index: Integer; end; TfrxHtIndexObjList = class(TObjectList) private function Get(Index: Integer): TfrxHtIndexObj; {$ifdef UseInline} inline; {$endif} public property Items[Index: Integer]: TfrxHtIndexObj read Get; default; end; ThtTextWrap = ( twNo, // 'n' twYes, // 'y' twSoft, // 's' twOptional // 'a' ); ThtTextWrapArray = array of ThtTextWrap; // TODO: stop creating TfrxHtSection, which mix up several inline elements into one instance. // Therefore we cannot control individual properties/attributes of single elements in it. // TfrxHtSection has to be reduced to a simple inline block for text of a single inline element // resp. to an anonymous block for text of a block element. // Also remove rendering code as soon as rendering is done by TInlineSection. // As a document is a TSectionList, TInlineSection will do it. TfrxHtSection = class(TfrxHtSectionBase) {TfrxHtSection holds and renders inline content. Mainly text and floating images, panel, frames, and form controls.} private BreakWord: Boolean; DrawWidth: Integer; FLPercent: Integer; LineHeight: Integer; StoredMin, StoredMax: TSize; SectionNumber: Integer; ThisCycle: Integer; BuffS: ThtString; {holds the text or one of #2 (Form), #4 (Image/Panel), #8 (break char) for the section} //TODO -oBG, 30.11.2013: #1 (Table for display:inline-table) FBuff: PWideChar; {same as above} Brk: ThtTextWrapArray; //string; // Brk[n]: Can I wrap to new line after BuffS[n]? One entry per character in BuffS SIndexList: TfrxHtIndexObjList; {list of Source index changes} Lines: TfrxHtLineRecList; {List of ThtLineRecs, info on all the lines in section} function GetIndexObj(I: Integer): TfrxHtIndexObj; property PosIndex[I: Integer]: TfrxHtIndexObj read GetIndexObj; procedure CheckForInlines(LR: TfrxHtLineRec); public Images: TfrxHtSizeableObjList; {list of TfrxHtSizeableObj's, the images, panels and iframes in section} AnchorName: Boolean; Fonts: TfrxHtFontList; {List of FontObj's in this section} Justify: ThtJustify; {Left, Centered, Right} JustifyByDirection: Boolean; Direction: ThtDirection; TextWidth: Integer; WhiteSpaceStyle: ThtWhiteSpaceStyle; FirstLineIndent: Integer; // Issue 365: public for TRichView property Buff: PWideChar read FBuff; // Issue 365: public for TRichView protected XP: PXArray; constructor Create(Parent: TfrxHtCellBasic); overload; function TryGetClear(var Clear: ThtClearStyle): Boolean; override; procedure SureDirection(WC: PWideChar; Len: Integer); procedure SetJustifyByDirection; public constructor Create(Parent: TfrxHtCellBasic; Attr: TfrxHtAttributeList; Prop: TfrxHTProperties; AnURL: TfrxHtUrlTarget; FirstItem: Boolean); overload; constructor CreateCopy(Parent: TfrxHtCellBasic; Source: TfrxHtmlNode); override; destructor Destroy; override; function AddImage(L: TfrxHtAttributeList; ACell: TfrxHtCellBasic; Index: Integer; Prop: TfrxHTProperties): TfrxFrHtImageObj; function Draw1(Canvas: TCanvas; const ARect: TRect; IMgr: TfrxHtIndentManager; X, XRef, YRef: Integer): Integer; override; function DrawLogic1(Canvas: TCanvas; X, Y, XRef, YRef, AWidth, AHeight, BlHt: Integer; IMgr: TfrxHtIndentManager; var MaxWidth, Curs: Integer; ARemainHeight: Integer = 0): Integer; override; function FindCountThatFits(Canvas: TCanvas; Width: Integer; Start: PWideChar; Max: Integer): Integer; function FindDocPos(SourcePos: Integer; Prev: Boolean): Integer; override; function FindSourcePos(DocPos: Integer): Integer; override; function FindString(From: Integer; const ToFind: UnicodeString; MatchCase: Boolean): Integer; override; function FindStringR(From: Integer; const ToFind: UnicodeString; MatchCase: Boolean): Integer; override; function FindTextSize(Canvas: TCanvas; Start: PWideChar; N: Integer; RemoveSpaces: Boolean): TSize; function FindTextWidthA(Canvas: TCanvas; Start: PWideChar; N: Integer): Integer; function GetChAtPos(Pos: Integer; out Ch: WideChar; out Obj: TfrxHtSectionBase): Boolean; override; function GetXPLen: Integer; procedure AddChar(C: WideChar; Index: Integer); virtual; procedure AddOpBrk; procedure AddTokenObj(T: TfrxHtTokenObj); virtual; procedure Allocate(N: Integer); procedure ChangeFont(Prop: TfrxHTProperties); procedure CheckFree; procedure Finish; procedure HRef(IsHRef: Boolean; List: TfrxHtDocument; AnURL: TfrxHtUrlTarget; Attributes: TfrxHtAttributeList; Prop: TfrxHTProperties); procedure MinMaxWidth(Canvas: TCanvas; out Min, Max: Integer; AvailableWidth, AvailableHeight: Integer); override; procedure ProcessText(TagIndex: Integer); virtual; end; // //BG, 01.12.2013: handle display:inline // // anonymous block for consecutive inline level elements. // TInlineSection = class(TfrxHtSection) // public // constructor Create(Parent: TfrxHtCellBasic); // procedure AddElement(Section: TfrxHtSection); // end; //------------------------------------------------------------------------------ // TfrxHtBlock represents block tags. //------------------------------------------------------------------------------ // A block is a rectangular area which may have a border // with margins outside and padding inside the border. It contains a // cell, which itself contains any kind of the html document content. // // Holds tags like DIV, FORM, PRE, P, H1..H6, UL, OL, DL, DIR, MENU, ... //------------------------------------------------------------------------------ TfrxHtBlockCell = class(TfrxHtCellBasic) private CellHeight: Integer; TextWidth: Integer; function DoLogicX(Canvas: TCanvas; X, Y, XRef, YRef, Width, AHeight, BlHt: Integer; out ScrollWidth: Integer; var Curs: Integer; ARemainHeight: Integer = 0): Integer; end; TfrxHtBlock = class(TfrxHtBlockBase) protected function GetBorderWidth: Integer; virtual; function CalcDisplayExtern: ThtDisplayStyle; override; function CalcDisplayIntern: ThtDisplayStyle; override; procedure ContentMinMaxWidth(Canvas: TCanvas; out Min, Max: Integer; AvailableWidth, AvailableHeight: Integer); virtual; procedure ConvMargArray(BaseWidth, BaseHeight: Integer; out AutoCount: Integer); virtual; procedure DrawBlockBorder(Canvas: TCanvas; const ORect, IRect: TRect); virtual; property BorderWidth: Integer read GetBorderWidth; public MyCell: TfrxHtBlockCell; // the block content MargArrayO: ThtVMarginArray; BGImage: TfrxFrHtImageObj; //TODO -oBG, 10.03.2011: see also bkGnd and bkColor in TfrxHtCellBasic one background should be enough. BlockTitle: ThtString; // Notice: styling tag attributes are deprecated by W3C and must be translated // to the corresponding style properties with a very low priority. // BEGIN: this area is copied by move() in CreateCopy() - NO string types allowed! MargArray: ThtMarginArray; FGColor: Integer; HasBorderStyle: Boolean; PRec: PtPositionRec; // background image position Visibility: ThtVisibilityStyle; TopAuto: Boolean; BottomAuto: Boolean; BreakBefore, BreakAfter, KeepIntact: Boolean; HideOverflow: Boolean; Justify: ThtJustify; RTL: Boolean; Converted: Boolean; // END: this area is copied by move() in CreateCopy() ContentWidth: Integer; ClearAddon: Integer; NeedDoImageStuff: Boolean; TiledImage: TfrxHtImage; TopP, LeftP: Integer; DrawList: TfrxHtSectionBaseList; NoMask: Boolean; ClientContentBot: Integer; MyRect: TRect; // calculated by DrawBlock(): the outmost rectangle of this block incl. border and padding but without margins in screen coordinates. MyIMgr: TfrxHtIndentManager; RefIMgr: TfrxHtIndentManager; constructor Create(Parent: TfrxHtCellBasic; Attributes: TfrxHtAttributeList; Prop: TfrxHTProperties); constructor CreateCopy(Parent: TfrxHtCellBasic; Source: TfrxHtmlNode); override; destructor Destroy; override; function Draw1(Canvas: TCanvas; const ARect: TRect; IMgr: TfrxHtIndentManager; X, XRef, YRef: Integer): Integer; override; function DrawLogic1(Canvas: TCanvas; X, Y, XRef, YRef, AWidth, AHeight, BlHt: Integer; IMgr: TfrxHtIndentManager; var MaxWidth, Curs: Integer; ARemainHeight: Integer = 0): Integer; override; function FindDocPos(SourcePos: Integer; Prev: Boolean): Integer; override; function FindSourcePos(DocPos: Integer): Integer; override; function FindString(From: Integer; const ToFind: UnicodeString; MatchCase: Boolean): Integer; override; function FindStringR(From: Integer; const ToFind: UnicodeString; MatchCase: Boolean): Integer; override; function FindWidth(Canvas: TCanvas; AWidth, AHeight, AutoCount: Integer): Integer; virtual; function GetChAtPos(Pos: Integer; out Ch: WideChar; out Obj: TfrxHtSectionBase): Boolean; override; procedure AddSectionsToList; override; procedure CollapseAdjoiningMargins; procedure CollapseNestedMargins; procedure DrawBlock(Canvas: TCanvas; const ARect: TRect; IMgr: TfrxHtIndentManager; X, Y, XRef, YRef: Integer); procedure DrawSort; procedure DrawTheList(Canvas: TCanvas; const ARect: TRect; ClipWidth, X, XRef, YRef: Integer); {$ifdef UseFormTree} procedure FormTree(const Indent: ThtString; var Tree: ThtString); {$endif UseFormTree} procedure MinMaxWidth(Canvas: TCanvas; out Min, Max: Integer; AvailableWidth, AvailableHeight: Integer); override; end; ThtListType = (None, Ordered, Unordered, Definition, liAlone); //------------------------------------------------------------------------------ // some blocks //------------------------------------------------------------------------------ { TfrxHtHRBlock } TfrxHtHRBlock = class(TfrxHtBlock) public Align: ThtJustify; MyHRule: TfrxHtSectionBase; constructor CreateCopy(OwnerCell: TfrxHtCellBasic; Source: TfrxHtmlNode); override; function FindWidth(Canvas: TCanvas; AWidth, AHeight, AutoCount: Integer): Integer; override; end; TfrxHtListBlock = class(TfrxHtBlock) end; TfrxHtBlockLI = class(TfrxHtBlock) private FListType: ThtListType; FListNumb: Integer; FListStyleType: ThtBulletStyle; FListFont: TFont; Image: TfrxFrHtImageObj; FirstLineHt: Integer; procedure SetListFont(const Value: TFont); public constructor Create(Parent: TfrxHtCellBasic; Attributes: TfrxHtAttributeList; Prop: TfrxHTProperties; Sy: TElemSymb; APlain: Boolean; AIndexType: ThtChar; AListNumb, ListLevel: Integer); constructor CreateCopy(OwnerCell: TfrxHtCellBasic; Source: TfrxHtmlNode); override; destructor Destroy; override; function DrawLogic1(Canvas: TCanvas; X, Y, XRef, YRef, AWidth, AHeight, BlHt: Integer; IMgr: TfrxHtIndentManager; var MaxWidth, Curs: Integer; ARemainHeight: Integer = 0): Integer; override; function Draw1(Canvas: TCanvas; const ARect: TRect; IMgr: TfrxHtIndentManager; X, XRef, YRef: Integer): Integer; override; property ListNumb: Integer read FListNumb write FListNumb; property ListStyleType: ThtBulletStyle read FListStyleType write FListStyleType; property ListType: ThtListType read FListType write FListType; property ListFont: TFont read FListFont write SetListFont; end; TfrxHtFieldsetBlock = class(TfrxHtBlock) private FLegend: TfrxHtBlockCell; protected procedure ConvMargArray(BaseWidth, BaseHeight: Integer; out AutoCount: Integer); override; procedure ContentMinMaxWidth(Canvas: TCanvas; out Min, Max: Integer; AvailableWidth, AvailableHeight: Integer); override; public constructor Create(Parent: TfrxHtCellBasic; Attributes: TfrxHtAttributeList; Prop: TfrxHTProperties); constructor CreateCopy(Parent: TfrxHtCellBasic; Source: TfrxHtmlNode); override; destructor Destroy; override; function DrawLogic1(Canvas: TCanvas; X, Y, XRef, YRef, AWidth, AHeight, BlHt: Integer; IMgr: TfrxHtIndentManager; var MaxWidth, Curs: Integer; ARemainHeight: Integer = 0): Integer; override; function Draw1(Canvas: TCanvas; const ARect: TRect; IMgr: TfrxHtIndentManager; X, XRef, YRef: Integer): Integer; override; property Legend: TfrxHtBlockCell read FLegend; end; TfrxHtBodyBlock = class(TfrxHtBlock) public constructor Create(Parent: TfrxHtCellBasic; Attributes: TfrxHtAttributeList; Prop: TfrxHTProperties); function DrawLogic1(Canvas: TCanvas; X, Y, XRef, YRef, AWidth, AHeight, BlHt: Integer; IMgr: TfrxHtIndentManager; var MaxWidth, Curs: Integer; ARemainHeight: Integer = 0): Integer; override; function Draw1(Canvas: TCanvas; const ARect: TRect; IMgr: TfrxHtIndentManager; X, XRef, YRef: Integer): Integer; override; end; //------------------------------------------------------------------------------ // TfrxHtmlTable, a block that represents a html table //------------------------------------------------------------------------------ TTableFrame = (tfVoid, tfAbove, tfBelow, tfHSides, tfLhs, tfRhs, tfVSides, tfBox, tfBorder); TTableRules = (trNone, trGroups, trRows, trCols, trAll); TIntArray = array of Integer; TWidthTypeArray = array of TWidthType; TIntegerPerWidthType = array [TWidthType] of Integer; TfrxHtTableBlock = class; TfrxHtCellObjCell = class(TfrxHtCell) private MyRect: TRect; Title: ThtString; Url, Target: ThtString; public constructor CreateCopy(Parent: TfrxHtBlock; T: TfrxHtCellObjCell); end; TfrxHtfrcHtCellObjBase = class(TObject) protected // BEGIN: this area is copied by move() in AssignTo() - NO string types or any other references like objects allowed! FColSpan: Integer; {column spans for this cell} FRowSpan: Integer; {row spans for this cell} FHzSpace: Integer; FVrSpace: Integer; FSpecWd: TSpecWidth; {Width attribute (percentage or absolute)} FSpecHt: TSpecWidth; {Height as specified} // END: this area is copied by move() in AssignTo() function GetCell: TfrxHtCellObjCell; virtual; abstract; procedure Draw(Canvas: TCanvas; const ARect: TRect; X, Y, CellSpacingHorz, CellSpacingVert: Integer; Border: Boolean; Light, Dark: TColor); virtual; abstract; procedure DrawLogic2(Canvas: TCanvas; Y, CellSpacingHorz, CellSpacingVert : Integer; var Curs: Integer); virtual; abstract; public function Clone(Parent: TfrxHtBlock): TfrxHtfrcHtCellObjBase; virtual; abstract; procedure AssignTo(Destin: TfrxHtfrcHtCellObjBase); virtual; property Cell: TfrxHtCellObjCell read GetCell; property ColSpan: Integer read FColSpan write FColSpan; {column and row spans for this cell} property RowSpan: Integer read FRowSpan write FRowSpan; {column and row spans for this cell} property HzSpace: Integer read FHzSpace write FHzSpace; property VrSpace: Integer read FVrSpace write FVrSpace; property SpecHt: TSpecWidth read FSpecHt write FSpecHt; {Height as specified} // BG, 12.01.2012: not C++-Builder compatible // property SpecHtType: TWidthType read FSpecHt.VType write FSpecHt.VType; {Height as specified} // property SpecHtValue: Double read FSpecHt.Value write FSpecHt.Value; {Height as specified} property SpecWd: TSpecWidth read FSpecWd write FSpecWd; {Width as specified} // BG, 12.01.2012: not C++-Builder compatible // property SpecWdType: TWidthType read FSpecWd.VType write FSpecWd.VType; {Height as specified} // property SpecWdValue: Double read FSpecWd.Value write FSpecWd.Value; {Height as specified} end; TfrxHtDummyCellObj = class(TfrxHtfrcHtCellObjBase) {holds one dummy cell of the table} protected function GetCell: TfrxHtCellObjCell; override; procedure Draw(Canvas: TCanvas; const ARect: TRect; X, Y, CellSpacingHorz, CellSpacingVert: Integer; Border: Boolean; Light, Dark: TColor); override; procedure DrawLogic2(Canvas: TCanvas; Y, CellSpacingHorz, CellSpacingVert : Integer; var Curs: Integer); override; public constructor Create(RSpan: Integer); function Clone(Parent: TfrxHtBlock): TfrxHtfrcHtCellObjBase; override; end; TfrxHtCellObj = class(TfrxHtfrcHtCellObjBase) {holds one cell of the table and some other information} private // BEGIN: this area is copied by move() in CreateCopy() - NO string types allowed! FWd: Integer; {total width (may cover more than one column)} FHt: Integer; {total height (may cover more than one row)} FVSize: Integer; {Actual vertical size of contents} FYIndent: Integer; {Vertical indent} FVAlign: ThtAlignmentStyle; {Top, Middle, or Bottom} FEmSize, FExSize: Integer; FPRec: PtPositionRec; // background image position info FPad: TRect; FBrd: TRect; FHideOverflow: Boolean; FHasBorderStyle: Boolean; FShowEmptyCells: Boolean; // END: this area is copied by move() in CreateCopy() FCell: TfrxHtCellObjCell; procedure Initialize(TablePadding: Integer; const BkImageName: ThtString; const APRec: PtPositionRec; Border: Boolean); protected function GetCell: TfrxHtCellObjCell; override; procedure Draw(Canvas: TCanvas; const ARect: TRect; X, Y, CellSpacingHorz, CellSpacingVert : Integer; Border: Boolean; Light, Dark: TColor); override; procedure DrawLogic2(Canvas: TCanvas; Y, CellSpacingHorz, CellSpacingVert: Integer; var Curs: Integer); override; private // BG, 08.01.2012: Issue 109: C++Builder cannot handle properties that reference record members. // - added for legacy support only, will be removed in a near future release. // Please use properties Border and Padding instead. function GetBorderBottom: Integer; function GetBorderLeft: Integer; function GetBorderRight: Integer; function GetBorderTop: Integer; function GetPaddingBottom: Integer; function GetPaddingLeft: Integer; function GetPaddingRight: Integer; function GetPaddingTop: Integer; procedure SetBorderBottom(const Value: Integer); procedure SetBorderLeft(const Value: Integer); procedure SetBorderRight(const Value: Integer); procedure SetBorderTop(const Value: Integer); procedure SetPaddingBottom(const Value: Integer); procedure SetPaddingLeft(const Value: Integer); procedure SetPaddingRight(const Value: Integer); procedure SetPaddingTop(const Value: Integer); public NeedDoImageStuff: Boolean; BGImage: TfrxFrHtImageObj; TiledImage: TfrxHtImage; MargArray: ThtMarginArray; MargArrayO: ThtVMarginArray; NoMask: Boolean; BreakBefore, BreakAfter, KeepIntact: Boolean; constructor Create(Parent: TfrxHtTableBlock; AVAlign: ThtAlignmentStyle; Attr: TfrxHtAttributeList; Prop: TfrxHTProperties); constructor CreateCopy(Parent: TfrxHtBlock; T: TfrxHtCellObj); destructor Destroy; override; function Clone(Parent: TfrxHtBlock): TfrxHtfrcHtCellObjBase; override; procedure AssignTo(Destin: TfrxHtfrcHtCellObjBase); override; property Border: TRect read FBrd write FBrd; //was: BrdTop, BrdRight, BrdBottom, BrdLeft: Integer; property BrdBottom: Integer read getBorderBottom write setBorderBottom; property BrdLeft: Integer read getBorderLeft write setBorderLeft; property BrdRight: Integer read getBorderRight write setBorderRight; property BrdTop: Integer read getBorderTop write setBorderTop; property Cell: TfrxHtCellObjCell read FCell; property EmSize: Integer read FEmSize write FEmSize; property ExSize: Integer read FExSize write FExSize; property HasBorderStyle: Boolean read FHasBorderStyle write FHasBorderStyle; property Ht: Integer read FHt {write FHt}; {total height (may cover more than one row)} property Padding: TRect read FPad write FPad; //was: PadTop, PadRight, PadBottom, PadLeft: Integer; property PadBottom: Integer read getPaddingBottom write setPaddingBottom; property PadLeft: Integer read getPaddingLeft write setPaddingLeft; property PadRight: Integer read getPaddingRight write setPaddingRight; property PadTop: Integer read getPaddingTop write setPaddingTop; property PRec: PtPositionRec read FPRec write FPRec; property ShowEmptyCells: Boolean read FShowEmptyCells write FShowEmptyCells; property HideOverflow: Boolean read FHideOverflow write FHideOverflow; property VAlign: ThtAlignmentStyle read FVAlign write FVAlign; {Top, Middle, or Bottom} property VSize: Integer read FVSize write FVSize; {Actual vertical size of contents} property Wd: Integer read FWd write FWd; {total width (may cover more than one column)} property YIndent: Integer read FYIndent write FYIndent; {Vertical indent} end; TfrxHtCellList = class(TObjectList) {holds one row of the html table, a list of TfrxHtCellObj} private function GetCellObj(Index: Integer): TfrxHtfrcHtCellObjBase; {$ifdef UseInline} inline; {$endif} public RowHeight: Integer; SpecRowHeight: TSpecWidth; RowSpanHeight: Integer; {height of largest rowspan} BkGnd: Boolean; BkColor: TColor; BkImage: ThtString; APRec: PtPositionRec; BreakBefore, BreakAfter, KeepIntact: Boolean; RowType: TRowType; constructor Create(Attr: TfrxHtAttributeList; Prop: TfrxHTProperties); constructor CreateCopy(Parent: TfrxHtBlock; T: TfrxHtCellList); procedure Initialize; function DrawLogicA(Canvas: TCanvas; const Widths: TIntArray; Span, CellSpacingHorz,CellSpacingVert, AHeight, Rows: Integer; out Desired: Integer; out Spec, More: Boolean): Integer; procedure DrawLogicB(Canvas: TCanvas; Y, CellSpacingHorz, CellSpacingVert: Integer; var Curs: Integer); function Draw(Canvas: TCanvas; Document: TfrxHtDocument; const ARect: TRect; const Widths: TIntArray; X, Y, YOffset, CellSpacingHorz,CellSpacingVert: Integer; Border: Boolean; Light, Dark: TColor; MyRow: Integer): Integer; procedure Add(CellObjBase: TfrxHtfrcHtCellObjBase); procedure RTLSwap; property Items[Index: Integer]: TfrxHtfrcHtCellObjBase read GetCellObj; default; end; // BG, 26.12.2011: TfrxHtRowList = class(TObjectList) private function GetItem(Index: Integer): TfrxHtCellList; {$ifdef UseInline} inline; {$endif} public procedure RTLSwap; property Items[Index: Integer]: TfrxHtCellList read GetItem; default; end; TfrxHtColSpec = class private FWidth: TSpecWidth; FAlign: ThtString; FVAlign: ThtAlignmentStyle; public constructor Create(const Width: TSpecWidth; Align: ThtString; VAlign: ThtAlignmentStyle); constructor CreateCopy(const ColSpec: TfrxHtColSpec); property ColWidth: TSpecWidth read FWidth; property ColAlign: ThtString read FAlign; property ColVAlign: ThtAlignmentStyle read FVAlign; end; // BG, 26.12.2011: TfrxHtColSpecList = class(TObjectList) private function GetItem(Index: Integer): TfrxHtColSpec; {$ifdef UseInline} inline; {$endif} public property Items[Index: Integer]: TfrxHtColSpec read GetItem; default; end; TfrxHtmlTable = class; TfrxHtTableBlock = class(TfrxHtBlock) protected function GetBorderWidth: Integer; override; public Table: TfrxHtmlTable; WidthAttr: Integer; AsPercent: Boolean; BkColor: TColor; BkGnd: Boolean; HSpace, VSpace: Integer; HasCaption: Boolean; TableBorder: Boolean; TableIndent: Integer; constructor Create(Parent: TfrxHtCellBasic; Attr: TfrxHtAttributeList; Prop: TfrxHTProperties; TableLevel: Integer); constructor CreateCopy(OwnerCell: TfrxHtCellBasic; Source: TfrxHtmlNode); override; function DrawLogic1(Canvas: TCanvas; X, Y, XRef, YRef, AWidth, AHeight, BlHt: Integer; IMgr: TfrxHtIndentManager; var MaxWidth, Curs: Integer; ARemainHeight: Integer = 0): Integer; override; function Draw1(Canvas: TCanvas; const ARect: TRect; IMgr: TfrxHtIndentManager; X, XRef, YRef: Integer): Integer; override; procedure MinMaxWidth(Canvas: TCanvas; out Min, Max: Integer; AvailableWidth, AvailableHeight: Integer); override; function FindWidth(Canvas: TCanvas; AWidth, AHeight, AutoCount: Integer): Integer; override; function FindWidth1(Canvas: TCanvas; AWidth, AHeight, ExtMarg: Integer): Integer; procedure AddSectionsToList; override; end; TfrxHtTableAndCaptionBlock = class(TfrxHtBlock) private procedure SetCaptionBlock(Value: TfrxHtBlock); public TopCaption: Boolean; TableBlock: TfrxHtTableBlock; FCaptionBlock: TfrxHtBlock; TableID: ThtString; constructor Create(Parent: TfrxHtCellBasic; Attributes: TfrxHtAttributeList; Prop: TfrxHTProperties; ATableBlock: TfrxHtTableBlock); constructor CreateCopy(OwnerCell: TfrxHtCellBasic; Source: TfrxHtmlNode); override; procedure CancelUsage; function FindWidth(Canvas: TCanvas; AWidth, AHeight, AutoCount: Integer): Integer; override; procedure MinMaxWidth(Canvas: TCanvas; out Min, Max: Integer; AvailableWidth, AvailableHeight: Integer); override; function FindDocPos(SourcePos: Integer; Prev: Boolean): Integer; override; property CaptionBlock: TfrxHtBlock read FCaptionBlock write SetCaptionBlock; end; TfrxHtmlTable = class(TfrxHtSectionBase) private FOwnerTableBlock: TfrxHtTableBlock; TablePartRec: TTablePartRec; HeaderHeight, HeaderRowCount, FootHeight, FootStartRow, FootOffset: Integer; BodyBreak: Integer; HeadOrFoot: Boolean; FColSpecs: TfrxHtColSpecList; // Column width specifications // calculated in GetMinMaxWidths Percents: TIntArray; {percent widths of columns} Multis: TIntArray; {multi widths of columns} MaxWidths: TIntArray; MinWidths: TIntArray; ColumnCounts: TIntegerPerWidthType; ColumnSpecs: TWidthTypeArray; CycleNumber: Integer; // these fields are copied via Move() in CreateCopy. Don't add reference counted data like strings and arrays. Initialized: Boolean; brdWidthAttr: Integer; {Width attribute as entered} HasBorderWidthAttr: Boolean; {width of border has been set by attr} Frame: TTableFrame; Float: Boolean; {if floating} NumCols: Integer; {Number columns in table} TableWidth: Integer; {width of table} tblWidthAttr: Integer; {Width attribute as entered} FCellPadding: Integer; FCellSpacingHorz, FCellSpacingVert: Integer; BorderColorLight: TColor; BorderColorDark: TColor; EndList: Boolean; {marker for copy} // end of Move()d fields DrawX: Integer; //DrawY: Integer; FBkGnd: Boolean; FBkColor: TColor; Widths: TIntArray; {holds calculated column widths} Heights: TIntArray; {holds calculated row heights} // procedure IncreaseWidthsByWidth(WidthType: TWidthType; var Widths: TIntArray; StartIndex, EndIndex, Required, Spanned, Count: Integer); procedure IncreaseWidthsByPercentage(var Widths: TIntArray; StartIndex, EndIndex, Required, Spanned, Percent, Count: Integer); procedure IncreaseWidthsByMinMaxDelta(WidthType: TWidthType; var Widths: TIntArray; StartIndex, EndIndex, Excess, DeltaWidth, Count: Integer; const Deltas: TIntArray); procedure IncreaseWidthsRelatively(var Widths: TIntArray; StartIndex, EndIndex, Required, SpannedMultis: Integer; ExactRelation: Boolean); procedure IncreaseWidthsEvenly(WidthType: TWidthType; var Widths: TIntArray; StartIndex, EndIndex, Required, Spanned, Count: Integer); procedure Initialize; // add dummy cells, initialize cells, prepare arrays procedure GetMinMaxWidths(Canvas: TCanvas; AvailableWidth, AvailableHeight: Integer); protected procedure Reverse(A: TIntArray); overload; procedure Reverse(A: TWidthTypeArray); overload; public Rows: TfrxHtRowList; {a list of TCellLists} Rules: TTableRules; BorderWidth: Integer; {width of border} BorderColor: TColor; //BG, 13.06.2010: added for Issue 5: Table border versus stylesheets constructor Create(OwnerTableBlock: TfrxHtTableBlock; Parent: TfrxHtCellBasic; Attr: TfrxHtAttributeList; Prop: TfrxHTProperties); constructor CreateCopy(OwnerCell: TfrxHtCellBasic; Source: TfrxHtmlNode); override; destructor Destroy; override; procedure DoColumns(Count: Integer; const SpecWidth: TSpecWidth; VAlign: ThtAlignmentStyle; const Align: ThtString); procedure MinMaxWidth(Canvas: TCanvas; out Min, Max: Integer; AvailableWidth, AvailableHeight: Integer); override; function DrawLogic1(Canvas: TCanvas; X, Y, XRef, YRef, AWidth, AHeight, BlHt: Integer; IMgr: TfrxHtIndentManager; var MaxWidth, Curs: Integer; ARemainHeight: Integer = 0): Integer; override; function Draw1(Canvas: TCanvas; const ARect: TRect; IMgr: TfrxHtIndentManager; X, XRef, YRef: Integer): Integer; override; function GetChAtPos(Pos: Integer; out Ch: WideChar; out Obj: TfrxHtSectionBase): Boolean; override; function FindString(From: Integer; const ToFind: UnicodeString; MatchCase: Boolean): Integer; override; function FindStringR(From: Integer; const ToFind: UnicodeString; MatchCase: Boolean): Integer; override; function FindSourcePos(DocPos: Integer): Integer; override; function FindDocPos(SourcePos: Integer; Prev: Boolean): Integer; override; property ColSpecs: TfrxHtColSpecList read FColSpecs; property TableHeight: Integer read SectionHeight write SectionHeight; {height of table itself, not incl caption} property CellPadding: Integer read FCellPadding; property CellSpacingHorz: Integer read FCellSpacingHorz; property CellSpacingVert: Integer read FCellSpacingVert; property BkGnd: Boolean read FBkGnd; property BkColor: TColor read FBkColor; end; //------------------------------------------------------------------------------ // TfrxHtChPosObj, a pseudo object for ID attributes. //------------------------------------------------------------------------------ // It is a general purpose ID marker, that finds its position by byte // position in the document buffer. This object is deprecated. // The corresponding tag object has to be added to the IDNameList instead. //------------------------------------------------------------------------------ // deprecated TfrxHtChPosObj = class(TfrxHtIDObject) private FDocument: TfrxHtDocument; FChPos: Integer; protected function GetYPosition: Integer; override; function FreeMe: Boolean; override; public constructor Create(Document: TfrxHtDocument; Pos: Integer); property ChPos: Integer read FChPos write FChPos; property Document: TfrxHtDocument read FDocument; end; //------------------------------------------------------------------------------ // TfrxHtDocument, a complete html document, that can draw itself on a canvas. //------------------------------------------------------------------------------ TExpandNameEvent = procedure(Sender: TObject; const SRC: ThtString; var Result: ThtString) of object; TfrxHtmlStyleList = class(TfrxHtStyleList) {a list of all the styles -- the stylesheet} private Document: TfrxHtDocument; protected procedure SetLinksActive(Value: Boolean); override; public constructor Create(AMasterList: TfrxHtDocument); end; TfrxHtmlPropStack = class(TfrxHtPropStack) public Document: TfrxHtDocument; SIndex: Integer; //BG, 26.12.2010: seems, this is the current position in the original html-file. procedure PopAProp(Sym: TElemSymb); procedure PopProp; procedure PushNewProp(Sym: TElemSymb; Properties: TfrxHTProperties; Attributes: TfrxHtAttributeList; const APseudo: ThtString = ''); end; TfrxHtDocument = class(TfrxHtCell) {a list of all the sections -- the html document} private FPropStack: TfrxHtmlPropStack; FPageArea: TRect; FPrinted: Boolean; {set if actually printed anything else but background} procedure AddSectionsToPositionList(Sections: TfrxHtSectionBase); procedure SetPageArea(const Value: TRect); function GetViewPort: TRect; procedure SetPrinted(const Value: Boolean); public // copied by move() in CreateCopy() ShowImages: Boolean; {set if showing images} YOff: Integer; {marks top of window that's displayed} YOffChange: Boolean; {when above changes} XOffChange: Boolean; {when x offset changes} NoPartialLine: Boolean; {set when printing if no partial line allowed at page bottom} LinkVisitedColor, LinkActiveColor, HotSpotColor: TColor; PrintTableBackground: Boolean; PrintBackground: Boolean; PrintMonoBlack: Boolean; TheOwner: TfrxHtmlViewerBase; {the viewer that owns this document} GetImage: TGetImageEvent; {for OnImageRequest Event} GottenImage: TGottenImageEvent; {for OnImageRequest Event} OnExpandName: TExpandNameEvent; FileBrowse: TFileBrowseEvent; BackGround: TColor; // end of copied by move() in CreateCopy() // don't copy strings via move() PreFontName: TFontName; {
,  font for document}

    OnBackgroundChange: TNotifyEvent;
    BackgroundImage: TfrxHtImage;
    BackgroundImagePosition: PtPositionRec;
    BackgroundImageName: ThtString; {name of background bitmap}
    BackgroundImageLoaded: Boolean; {if background bitmap is loaded}
    ScriptEvent: TScriptEvent;
    PageBottom: Integer;
    PageShortened: Boolean;
    MissingImages: ThtStringList; {images to be supplied later}
    ControlEnterEvent: TNotifyEvent;
    LinkList: TfrxHtLinkList; {List of links (TfrxHtFontObj's)}
    LinksActive: Boolean;
    ShowDummyCaret: Boolean;
    Styles: TfrxHtmlStyleList; {the stylesheet}
    DrawList: TfrxHtDrawList;
    FirstLineHtPtr: PInteger;
    IDNameList: TfrxHtIDObjectList;
    PositionList: TfrxHtSectionBaseList;
    ImageCache: TfrxHtImageCache;
    SectionCount: Integer;
    CycleNumber: Integer;
    IsCopy: Boolean; {set when printing or making bitmap/metafile}
    NoOutput: Boolean;
    FirstPageItem: Boolean;
    StopTab: Boolean;
    InlineList: TObjectList; {actually TInlineList, a list of ThtInlineRec's}
    TableNestLevel: Integer;
    InLogic2: Boolean;
    LinkDrawnEvent: TLinkDrawnEvent;
    LinkPage: Integer;
    PrintingTable: TfrxHtmlTable;
    ScaleX, ScaleY: single;
    SkipDraw: Boolean;
    FNoBreak : Boolean;
    FCurrentStyle: TFontStyles;

    constructor Create(Owner: TfrxHtmlViewerBase);
    constructor CreateCopy(T: TfrxHtDocument);
    destructor Destroy; override;
    function AddChPosObjectToIDNameList(const S: ThtString; Pos: Integer): Integer; {$ifdef UseInline} inline; {$endif}
    function DoLogic(Canvas: TCanvas; Y: Integer; Width, AHeight, BlHt: Integer; var ScrollWidth, Curs: Integer; ARemainHeight: Integer = 0): Integer; override;
    function Draw(Canvas: TCanvas; ARect: TRect; ClipWidth, X: Integer; Y, XRef, YRef: Integer): Integer; override;
    function FindDocPos(SourcePos: Integer; Prev: Boolean): Integer; override;
    function FindSectionAtPosition(Pos: Integer; out TopPos, Index: Integer): TfrxHtSectionBase;
    function GetTheImage(const BMName: ThtString; var Transparent: ThtImageTransparency; out FromCache, Delay: Boolean): TfrxHtImage;
    procedure Clear; override;
    procedure GetBackgroundImage;
    procedure ProcessInlines(SIndex: Integer; Prop: TfrxHTProperties; Start: Boolean);
    procedure SetBackground(ABackground: TColor);
    procedure SetBackgroundImage(const Name: ThtString; const APrec: PtPositionRec);
    procedure SetYOffset(Y: Integer);
    procedure SetFonts(const Name, PreName: ThtString; ASize: Double;
      AColor, AHotSpot, AVisitedColor, AActiveColor, ABackground: TColor; LnksActive, LinkUnderLine: Boolean;
      ACodePage: TBuffCodePage; ACharSet: TFontCharSet; MarginHeight, MarginWidth: Integer);
    property PropStack : TfrxHtmlPropStack read FPropStack write FPropStack;
    property Printed: Boolean read FPrinted write SetPrinted;

    property NoBreak : Boolean read FNoBreak write FNoBreak;  {set when in }
    property ViewPort: TRect read GetViewPort;  {ViewPort is the APaintPanel's client rect, except while printing}
    property PageArea: TRect write SetPageArea; {is the ViewPort while printing, set by THtmlViewer.Print}

    property CurrentStyle: TFontStyles read FCurrentStyle write FCurrentStyle;  {as set by , , etc.}
  end;

//------------------------------------------------------------------------------
// some more base sections
//------------------------------------------------------------------------------

  TfrxHtLineBreak = class(TfrxHtSectionBase)
  protected
    function TryGetClear(var Clear: ThtClearStyle): Boolean; override;
  end;

  TfrxHtPage = class(TfrxHtSectionBase)
  public
    constructor Create(Parent: TfrxHtCellBasic; Attributes: TfrxHtAttributeList; Properties: TfrxHTProperties);
    function Draw1(Canvas: TCanvas; const ARect: TRect; IMgr: TfrxHtIndentManager; X, XRef, YRef: Integer): Integer; override;
  end;

  TfrxHtHorzLine = class(TfrxHtSectionBase) {a horizontal line, 
} public VSize: Integer; Color: TColor; Align: ThtJustify; UseDefBorder: Boolean; NoShade: Boolean; BkGnd: Boolean; Width, Indent: Integer; constructor Create(Parent: TfrxHtCellBasic; L: TfrxHtAttributeList; Prop: TfrxHTProperties); constructor CreateCopy(Parent: TfrxHtCellBasic; Source: TfrxHtmlNode); override; function DrawLogic1(Canvas: TCanvas; X, Y, XRef, YRef, AWidth, AHeight, BlHt: Integer; IMgr: TfrxHtIndentManager; var MaxWidth, Curs: Integer; ARemainHeight: Integer = 0): Integer; override; function Draw1(Canvas: TCanvas; const ARect: TRect; IMgr: TfrxHtIndentManager; X, XRef, YRef: Integer): Integer; override; end; TfrxHtPreFormated = class(TfrxHtSection) // {section for preformated,
}
//  public
//    procedure ProcessText(TagIndex: Integer); override;
//    function DrawLogic(Canvas: TCanvas; X, Y, XRef, YRef, AWidth, AHeight, BlHt: Integer; IMgr: TfrxHtIndentManager;
//      var MaxWidth, Curs: Integer): Integer; override;
//    procedure MinMaxWidth(Canvas: TCanvas; out Min, Max: Integer); override;
  end;

var
  WaitStream: TMemoryStream;
  ErrorStream: TMemoryStream;
  WaitBitmap: TBitmap;
  ErrorBitmap: TBitmap;
{$ifdef UNICODE}
{$else}
  UnicodeControls: Boolean;
{$endif}

implementation

uses
{$ifdef UseVCLStyles}
  System.Types,
  System.UITypes,
  Vcl.Themes,
  VCL.Controls,
{$endif}
{$ifdef Compiler28_Plus}
 System.NetEncoding,
{$endif}
  frxHTMLViewer, frxUtils,
  frxProtocolFactory;

type
  TSameWidthFix = class
  private
    FAtLeastOneFix: Boolean;
    FNeedFix: array of Boolean;
    FFirstIndex: TIntArray;
  public
    constructor Create(Widths, MinWidths, MaxWidths: TIntArray; ColumnSpecs: TWidthTypeArray);
    procedure Fix(Widths: TIntArray);
  end;

//-- BG ---------------------------------------------------------- 14.01.2012 --
function Sum(const Arr: TIntArray; StartIndex, EndIndex: Integer): Integer; overload;
 {$ifdef UseInline} inline; {$endif}
// Return sum of array elements from StartIndex to EndIndex.
var
  I: Integer;
begin
  Result := 0;
  for I := StartIndex to EndIndex do
    Inc(Result, Arr[I]);
end;

//-- BG ---------------------------------------------------------- 14.01.2012 --
function Sum(const Arr: TIntArray): Integer; overload;
 {$ifdef UseInline} inline; {$endif}
// Return sum of all array elements.
begin
  Result := Sum(Arr, Low(Arr), High(Arr));
end;

//-- BG ---------------------------------------------------------- 14.01.2012 --
function Sum(const Arr: TIntegerPerWidthType; StartIndex, EndIndex: TWidthType): Integer; overload;
 {$ifdef UseInline} inline; {$endif}
// Return sum of array elements from StartIndex to EndIndex.
var
  I: TWidthType;
begin
  Result := 0;
  for I := StartIndex to EndIndex do
    Inc(Result, Arr[I]);
end;

//-- BG ---------------------------------------------------------- 14.01.2012 --
function Sum(const Arr: TIntegerPerWidthType): Integer; overload;
 {$ifdef UseInline} inline; {$endif}
// Return sum of all array elements.
begin
  Result := Sum(Arr, Low(Arr), High(Arr));
end;

//-- BG ---------------------------------------------------------- 17.01.2012 --
function SubArray(const Arr, Minus: TIntArray): TIntArray;
 {$ifdef UseInline} inline; {$endif}
// Return array with differences per index.
var
  I: Integer;
begin
  Result := Copy(Arr);
  for I := 0 to Min(High(Result), High(Minus)) do
    Dec(Result[I], Minus[I]);
end;

//-- BG ---------------------------------------------------------- 16.01.2012 --
procedure SetArray(var Arr: TIntArray; Value, StartIndex, EndIndex: Integer); overload;
 {$ifdef UseInline} inline; {$endif}
var
  I: Integer;
begin
  for I := StartIndex to EndIndex do
    Arr[I] := Value;
end;

//-- BG ---------------------------------------------------------- 16.01.2012 --
procedure SetArray(var Arr: TIntArray; Value: Integer); overload;
 {$ifdef UseInline} inline; {$endif}
begin
  SetArray(Arr, Value, Low(Arr), High(Arr));
end;

//-- BG ---------------------------------------------------------- 16.01.2012 --
procedure SetArray(var Arr: TIntegerPerWidthType; Value: Integer); overload;
 {$ifdef UseInline} inline; {$endif}
var
  I: TWidthType;
begin
  for I := Low(TWidthType) to High(TWidthType) do
    Arr[I] := Value;
end;

//-- BG ---------------------------------------------------------- 18.01.2012 --
procedure CountsPerType(
  out CountsPerType: TIntegerPerWidthType;
  const ColumnSpecs: TWidthTypeArray;
  StartIndex, EndIndex: Integer);
 {$ifdef UseInline} inline; {$endif}
var
  I: Integer;
begin
  SetArray(CountsPerType, 0);
  for I := StartIndex to EndIndex do
    Inc(CountsPerType[ColumnSpecs[I]]);
end;

//-- BG ---------------------------------------------------------- 19.01.2012 --
function SumOfType(
  WidthType: TWidthType;
  const ColumnSpecs: TWidthTypeArray;
  const Widths: TIntArray;
  StartIndex, EndIndex: Integer): Integer;
  {$ifdef UseInline} inline; {$endif}
var
  I: Integer;
begin
  Result := 0;
  for I := StartIndex to EndIndex do
    if ColumnSpecs[I] = WidthType then
      Inc(Result, Widths[I]);
end;

//-- BG ---------------------------------------------------------- 17.06.2012 --
function SumOfNotType(
  WidthType: TWidthType;
  const ColumnSpecs: TWidthTypeArray;
  const Widths: TIntArray;
  StartIndex, EndIndex: Integer): Integer;
  {$ifdef UseInline} inline; {$endif}
var
  I: Integer;
begin
  Result := 0;
  for I := StartIndex to EndIndex do
    if ColumnSpecs[I] <> WidthType then
      Inc(Result, Widths[I]);
end;

procedure InitializeFontSizes(Size: Double);
   {$ifdef UseInline} inline; {$endif}
var
  I: Integer;
begin
  for I := 1 to 7 do
  begin
    FontConv[I] := FontConvBase[I] * Size / 12.0;
    PreFontConv[I] := PreFontConvBase[I] * Size / 12.0;
  end;
end;

{ TfrxHtmlNode }

//-- BG ---------------------------------------------------------- 24.03.2011 --
procedure TfrxHtmlNode.AfterConstruction;
begin
  inherited AfterConstruction;

  if (Length(Id) > 0) and (Document <> nil) and (Document.IDNameList <> nil) then
    Document.IDNameList.AddObject(Id, Self);
end;

//-- BG ---------------------------------------------------------- 07.09.2013 --
function TfrxHtmlNode.CalcDisplayExtern: ThtDisplayStyle;
begin
  case FDisplay of
    pdInlineBlock,
    pdInlineTable:
      Result := pdInline;
  else
    Result := FDisplay;
  end;
end;

//-- BG ---------------------------------------------------------- 07.09.2013 --
function TfrxHtmlNode.CalcDisplayIntern: ThtDisplayStyle;
begin
  case FDisplay of
    pdInlineTable:
      Result := pdTable;

    pdInlineBlock:
      Result := pdBlock;
  else
    Result := FDisplay;
  end;
end;

//-- BG ---------------------------------------------------------- 24.03.2011 --
constructor TfrxHtmlNode.Create(Parent: TfrxHtCellBasic; Attributes: TfrxHtAttributeList; Properties: TfrxHTProperties; const ID: ThtString);
var
  MargArrayO: ThtVMarginArray;
  MargArray: ThtMarginArray;
  Cd: ThtConvData;
begin
  inherited Create(ID);
  FOwnerCell := Parent;
  if FOwnerCell <> nil then
  begin
    FOwnerBlock := FOwnerCell.OwnerBlock;
    FDocument := FOwnerCell.Document;
  end;
  if Attributes <> nil then
    FAttributes := Attributes.Clone;
  if Properties <> nil then
    FProperties := Properties.Clone;
// defaults are set automatically as Ord() of these defaults is 0:
//  FDisplay := pdUnassigned;
//  FFloating := aNone;
//  FPositioning := posStatic;
  TryGetClear(FClearing);

  if FProperties <> nil then
  begin
    FDisplay := FProperties.Display;
    FPositioning := FProperties.GetPosition;
    if not FProperties.GetFloat(FFloating) or (FPositioning in [posAbsolute, posFixed]) then
      FFloating := ANone;
    FProperties.GetVMarginArray(MargArrayO);
    FEmSize := FProperties.EmSize;
    FExSize := FProperties.ExSize;
    CD := ConvData(100, 100, FEmSize, FExSize, 0);
    ConvMargProp(piTop   , MargArrayO, CD, MargArray);
    ConvMargProp(piLeft  , MargArrayO, CD, MargArray);
    ConvMargProp(piRight , MargArrayO, CD, MargArray);
    ConvMargProp(piBottom, MargArrayO, CD, MargArray);
    FPositions[reTop   ] := MargArray[piTop   ];
    FPositions[reLeft  ] := MargArray[piLeft  ];
    FPositions[reRight ] := MargArray[piRight ];
    FPositions[reBottom] := MargArray[piBottom];
  end;
end;

//-- BG ---------------------------------------------------------- 24.03.2011 --
constructor TfrxHtmlNode.CreateCopy(Parent: TfrxHtCellBasic; Source: TfrxHtmlNode);
begin
  inherited Create( Source.HtmlId );
  FOwnerCell := Parent;
  begin
    FOwnerBlock := FOwnerCell.OwnerBlock;
    FDocument   := FOwnerCell.Document;
  end;
  FAttributes   := Source.FAttributes;
  FProperties   := Source.FProperties;
  FClearing     := Source.FClearing;
  FDisplay      := Source.FDisplay;
  FPositioning  := Source.FPositioning;
  FPositions    := Source.FPositions;
  FFloating     := Source.FFloating;
  FIndent       := Source.FIndent;
end;

//-- BG ---------------------------------------------------------- 21.10.2016 --
destructor TfrxHtmlNode.Destroy;
var
  Idx: Integer;
begin
  if (Length(Id) > 0) and (Document <> nil) and (Document.IDNameList <> nil) then
    with Document.IdNameList do
    begin
      Idx := IndexOfObject(Self);
      if Idx >= 0 then
         Delete(Idx);
    end;

  if not IsCopy then
  begin
    FAttributes.Free;
    FProperties.Free;
  end;
  inherited;
end;

//-- BG ---------------------------------------------------------- 24.02.2016 --
function TfrxHtmlNode.TryGetClear(var Clear: ThtClearStyle): Boolean;
begin
  Result := False;
  if FProperties <> nil then
    Result := FProperties.GetClear(Clear);
end;

//-- BG ---------------------------------------------------------- 24.02.2016 --
function TfrxHtSection.TryGetClear(var Clear: ThtClearStyle): Boolean;
var
  T: TfrxHtAttribute;
begin
  T := nil;
  Result := inherited TryGetClear(Clear);
  if not Result then
    if FAttributes <> nil then
      if FAttributes.Find(ClearSy, T) then
        Result := TryStrToClearStyle(LowerCase(T.Name), Clear);
end;

//-- BG ---------------------------------------------------------- 24.03.2011 --
function TfrxHtmlNode.FindAttribute(const Name: ThtString; out Attribute: TfrxHtAttribute): Boolean;
begin
  Attribute := nil;
  Result := (FAttributes <> nil) and FAttributes.Find(Name, Attribute);
end;

//-- BG ---------------------------------------------------------- 28.10.2015 --
function TfrxHtmlNode.GetAttribute(const Name: ThtString): ThtString;
var
  Attribute: TfrxHtAttribute;
begin
  if FindAttribute(Name, Attribute) then
    Result := Attribute.Name
  else
    SetLength(Result, 0);
end;

//-- BG ---------------------------------------------------------- 24.02.2016 --
function TfrxHtmlNode.GetClearSpace(IMgr: TfrxHtIndentManager; Y: Integer): Integer;
var
  CL, CR: Integer;
begin
  Result := 0;
  if FClearing <> clrNone then
  begin {may need to move down past floating image}
    IMgr.GetClearY(CL, CR);
    case FClearing of
      clrLeft:   Result := Max(0, CL - Y - 1);
      clrRight:  Result := Max(0, CR - Y - 1);
      clrBoth:   Result := Max(CL - Y - 1, Max(0, CR - Y - 1));
    end;
  end;
end;

//-- BG ---------------------------------------------------------- 14.02.2016 --
function TfrxHtmlNode.GetContainingBlock: TfrxHtBlock;
begin
  // if Result is nil, containing block is the viewport resp. page area
  case FPositioning of
    posFixed:
      Result := nil;

    posAbsolute:
    begin
      Result := OwnerBlock;
      while (Result <> nil) and not (Result.Positioning in [posAbsolute, posRelative, posFixed]) do
        Result := Result.OwnerBlock;
    end;

  else
    Result := OwnerBlock;
  end;
end;

//-- BG ---------------------------------------------------------- 14.02.2016 --
function TfrxHtmlNode.GetContainingBox: TRect;
var
  ContainingBlock: TfrxHtBlock;
begin
  ContainingBlock := GetContainingBlock;
  if ContainingBlock <> nil then
  begin
    Result.Left := ContainingBlock.LeftP;
    Result.Top  := ContainingBlock.TopP;
    Result.Right := Result.Left + ContainingBlock.ContentWidth;
    Result.Bottom := Result.Top + ContainingBlock.MyCell.tcContentBot - ContainingBlock.MyCell.tcDrawTop;
  end
  else
    Result := FDocument.ViewPort;
end;

//-- BG ---------------------------------------------------------- 14.02.2016 --
function TfrxHtmlNode.GetProperty(PropIndex: ThtPropIndices): Variant;
begin
  if FProperties <> nil then
    Result := FProperties.Props[PropIndex]
  else
    Result := Unassigned;
end;

//-- BG ---------------------------------------------------------- 04.08.2013 --
function TfrxHtmlNode.GetSymbol: TElemSymb;
begin
  Result := FProperties.PropSym;
end;

//-- BG ---------------------------------------------------------- 21.09.2016 --
procedure TfrxHtmlNode.GetVMarginArray(var MArray: ThtVMarginArray);
begin
  FProperties.GetVMarginArray(MArray);
end;

//function TfrxHtmlNode.GetPseudos: TPseudos;
//begin
//  Result := []; //TODO -oBG, 24.03.2011
//end;

//-- BG ---------------------------------------------------------- 24.03.2011 --
function TfrxHtmlNode.IndexOf(Child: TfrxHtmlNode): Integer;
begin
  Result := -1; //TODO -oBG, 24.03.2011
end;

//-- BG ---------------------------------------------------------- 04.08.2013 --
function TfrxHtmlNode.IsCopy: Boolean;
begin
  Result := Document.IsCopy;
end;

//-- BG ---------------------------------------------------------- 13.02.2016 --
function TfrxHtmlNode.IsInFlow: Boolean;
begin
  Result := not (FFloating in [aLeft, aRight]) and not (FPositioning in [posAbsolute, posFixed]);
end;


{ TfrxHtFontObj }

type
  TSectionClass = class of TfrxHtSectionBase;
  EProcessError = class(Exception);

type
  ThtInlineRec = class
  private
    StartB, EndB, IDB, StartBDoc, EndBDoc: Integer;
    MargArray: ThtMarginArray;
  end;

  TInlineList = class(TObjectList) {a list of ThtInlineRec's}
  private
    NeedsConverting: Boolean;
    Owner: TfrxHtDocument;
    procedure AdjustValues;
    function Get(Index: Integer): ThtInlineRec; {$ifdef UseInline} inline; {$endif}
    function GetStartB(I: Integer): Integer;
    function GetEndB(I: Integer): Integer;
  public
    constructor Create(AnOwner: TfrxHtDocument);
    procedure Clear; override;
    property Items[Index: Integer]: ThtInlineRec read Get; default;
    property StartB[I: Integer]: Integer read GetStartB;
    property EndB[I: Integer]: Integer read GetEndB;
  end;

constructor TfrxHtFontObj.Create(ASection: TfrxHtSection; F: TfrxHtFont; Position: Integer);
begin
  inherited Create;
  TheFont := TfrxHtFont.Create;
  TheFont.Assign(F);
  Pos := Position;
  UrlTarget := TfrxHtUrlTarget.Create;
  FontChanged;
end;

procedure TfrxHtFontObj.CreateFIArray;
begin
  if not Assigned(FIArray) then
    FIArray := TFontInfoObj.Create;
end;

procedure TfrxHtFontObj.ReplaceFont(F: TfrxHtFont);
begin
  TheFont.Assign(F);
  FontChanged;
end;

procedure TfrxHtFontObj.ConvertFont(const FI: TfrxHtFontInfo);
begin
  TheFont.Assign(FI);
  FontChanged;
end;

constructor TfrxHtFontObj.CreateCopy(ASection: TfrxHtSection; T: TfrxHtFontObj);
begin
  inherited Create;
  Pos := T.Pos;
  SScript := T.SScript;
  TheFont := TfrxHtFont.Create;
  TheFont.Assign(T.TheFont);
  if Assigned(T.FIArray) then
    ConvertFont(T.FIArray.Info);
  UrlTarget := TfrxHtUrlTarget.Create;
  UrlTarget.Assign(T.UrlTarget);
  FontChanged;
end;

destructor TfrxHtFontObj.Destroy;
begin
  FIArray.Free;
  TheFont.Free;
  UrlTarget.Free;
  inherited Destroy;
end;

function TfrxHtFontObj.GetURL: ThtString;
begin
  try
    Result := UrlTarget.Url;
  except
    Result := '';
  end;
end;

procedure TfrxHtFontObj.FontChanged;
begin
  tmHeight := TheFont.tmHeight;
  tmMaxCharWidth := TheFont.tmMaxCharWidth;
  FontHeight := TheFont.tmHeight + TheFont.tmExternalLeading;
  Descent := TheFont.tmDescent;
  if fsItalic in TheFont.Style then {estimated overhang}
    Overhang := TheFont.tmheight div 10
  else
    Overhang := 0;
  TheFont.Charset := TheFont.tmCharset;
end;

function TfrxHtFontObj.GetOverhang: Integer;
begin
  Result := Overhang;
end;

function TfrxHtFontObj.GetHeight(var Desc: Integer): Integer;
begin
  Desc := Descent;
  Result := FontHeight;
end;

constructor TfrxHtFontList.CreateCopy(ASection: TfrxHtSection; T: TfrxHtFontList);
var
  I: Integer;
begin
  inherited create;
  for I := 0 to T.Count - 1 do
    Add(TfrxHtFontObj.CreateCopy(ASection, T.Items[I]));
end;

//-- BG ---------------------------------------------------------- 10.02.2013 --
function TfrxHtFontList.GetFont(Index: Integer): TfrxHtFontObj;
begin
  Result := inherited Get(Index);
end;

function TfrxHtFontList.GetFontAt(Posn: Integer; out OHang: Integer): TfrxHtFont;
{given a character index, find the font that's effective there}
var
  I, PosX: Integer;
  F: TfrxHtFontObj;
begin
  I := 0;
  PosX := 0;
  while (I < Count) do
  begin
    PosX := Items[I].Pos;
    Inc(I);
    if PosX >= Posn then
      Break;
  end;
  Dec(I);
  if PosX > Posn then
    Dec(I);
  F := Items[I];
  OHang := F.Overhang;
  Result := F.TheFont;
end;

//-- BG ---------------------------------------------------------- 25.08.2013 --
function TfrxHtFontList.GetFontObjAt(Posn, Leng: Integer; out Obj: TfrxHtFontObj): Integer;
{Given a position, returns the FontObj which applies there and the number of chars before the font changes}
var
  I: Integer;
begin
  I := Count;
  while I > 0 do
  begin
    Dec(I);
    Obj := Items[I];
    if Obj.Pos <= Posn then
    begin
      Result := Leng - Posn;
      Exit;
    end;
    Leng := Obj.Pos;
  end;
  Obj := nil;
  Result := Leng - Posn;
end;

{----------------TfrxHtFontList.GetFontObjAt}

function TfrxHtFontList.GetFontObjAt(Posn: Integer): TfrxHtFontObj;
{Given a position, returns the FontObj which applies there}
var
  I: Integer;
begin
  I := Count;
  while I > 0 do
  begin
    Dec(I);
    Result := Items[I];
    if Result.Pos <= Posn then
      Exit;
  end;
  Result := nil;
end;

{----------------TfrxHtFontList.Decrement}

procedure TfrxHtFontList.Decrement(N: Integer; Document: TfrxHtDocument);
{called when a character is removed to change the Position figure}
var
  I, J: Integer;
  FO, FO1: TfrxHtFontObj;
begin
  I := 0;
  while I < Count do
  begin
    FO := Items[I];
    if FO.Pos > N then
      Dec(FO.Pos);
    if (I > 0) and (Items[I - 1].Pos = FO.Pos) then
    begin
      FO1 := Items[I - 1];
      J := Document.LinkList.IndexOf(FO1);
      if J >= 0 then
        Document.LinkList.Delete(J);
      Delete(I - 1);
    end
    else
      Inc(I);
  end;
end;

{ TfrxHtLinkList }

//-- BG ---------------------------------------------------------- 10.02.2013 --
constructor TfrxHtLinkList.Create;
begin
  inherited Create(False);
end;

{ TfrxFrHtImageObj.Create }

constructor TfrxFrHtImageObj.Create(Parent: TfrxHtCellBasic; Position: Integer; L: TfrxHtAttributeList; Prop: TfrxHTProperties);
var
  I: Integer;
  S: ThtString;
  T: TfrxHtAttribute;
begin
  inherited Create(Parent,Position,L,Prop);
  for I := 0 to L.Count - 1 do
    with L[I] do
      case Which of
        SrcSy:
          FSource := htTrim(Name);

        AltSy:
          begin
            SetAlt(CodePage, Name);
            Title := Alt;
          end;

        BorderSy:
          begin
            BorderWidth := Min(Max(0, Value), 10);
//            if not Prop.HasBorderWidth then
//              Prop.BorderWidth := Min(Max(0, Value), 10);
//            if not Prop.HasBorderStyle then
//              Prop.BorderStyle := bssSolid;
          end;

        IsMapSy:
          IsMap := True;

        UseMapSy:
          begin
            UseMap := True;
            S := htUpperCase(htTrim(Name));
            if (Length(S) > 1) and (S[1] = '#') then
              System.Delete(S, 1, 1);
            MapName := S;
          end;

        ActiveSy:
          begin
          end;

        NameSy:
          Document.IDNameList.AddObject(Name, Self);
      end;

  NoBorder := (BorderWidth = 0) and (not Prop.HasBorderStyle or not Prop.HasBorderWidth);
  T := nil;
  if L.Find(TitleSy, T) then
    Title := T.Name; {has higher priority than Alt loaded above}
end;

constructor TfrxFrHtImageObj.SimpleCreate(Parent: TfrxHtCellBasic; const AnURL: ThtString);
begin
  inherited SimpleCreate(Parent);
  FSource := AnURL;
end;

constructor TfrxFrHtImageObj.CreateCopy(Parent: TfrxHtCellBasic; Source: TfrxHtmlNode);
var
  T: TfrxFrHtImageObj absolute Source;
begin
  inherited CreateCopy(Parent,Source);
  AltHeight := T.AltHeight;
  AltWidth := T.AltWidth;
  FImage := T.Image;
  FSource := T.FSource;
  IsMap := T.IsMap;
  MapName := T.MapName;
  Missing := T.Missing;
  ObjHeight := T.ObjHeight;
  ObjWidth := T.ObjWidth;
  OrigImage := T.OrigImage;
  Swapped := T.Swapped;
  Transparent := T.Transparent;
  UseMap := T.UseMap;
end;

function TfrxFrHtImageObj.GetGraphic: TGraphic;
begin
  if (Image = nil) or (Image = ErrorImage) then
    Result := nil
  else
    Result := Image.Graphic;
end;

{----------------TfrxFrHtImageObj.DrawLogic}

procedure TfrxFrHtImageObj.DrawLogicInline(Canvas: TCanvas; FO: TfrxHtFontObj; AvailableWidth, AvailableHeight: Integer);
{calculate the height and width}
var
  TmpImage: TfrxHtImage;
  ViewImages, FromCache: Boolean;
  Rslt: ThtString;
  ARect: TRect;
  SubstImage: Boolean;
  HasBlueBox: Boolean;
  UName: ThtString;
begin
  ViewImages := Document.ShowImages;
  case FDisplay of
    pdNone:
    begin
      ObjHeight := 0;
      ObjWidth := 0;

      ClientHeight := ObjHeight;
      ClientWidth := ObjWidth;
      Exit;
    end;
  end;

  if ViewImages then
  begin
    if FImage = nil then
    begin
      TmpImage := nil;
      UName := htTrim(FSource);
      if UName <> '' then
      begin
        if not Assigned(Document.GetImage) then
          FSource := htTrim(Document.TheOwner.HtmlExpandFilename(FSource))
        else if Assigned(Document.OnExpandName) then
        begin
          Document.OnExpandName(Document.TheOwner, FSource, Rslt);
          FSource := htTrim(Rslt);
        end;
        if Document.MissingImages.IndexOf(FSource) = -1 then
          TmpImage := Document.GetTheImage(FSource, Transparent, FromCache, Missing)
        else
          Missing := True; {already in list, don't request it again}
      end;

      if TmpImage = nil then
      begin
        if Missing then
        begin
          FImage := DefImage;
          Document.MissingImages.AddObject(FSource, Self); {add it even if it's there already}
        end
        else
        begin
          FImage := ErrorImage;
        end;
      end
      else
        FImage := TmpImage;
      OrigImage := TmpImage;
    end;
  end
  else
    FImage := DefImage;

  SubstImage := (Image = ErrorImage) or (Image = DefImage);

  HasBlueBox := not NoBorder and Assigned(FO) and (FO.URLTarget.Url <> '');
  if HasBlueBox then
    BorderWidth := Max(1, BorderWidth);

  if not ClientSizeKnown or PercentWidth or PercentHeight then
  begin
    CalcSize(AvailableWidth, AvailableHeight, Image.Width, Image.Height, not SubstImage);
    ObjWidth  := ClientWidth; // - MargArray[BorderLeftWidth] - MargArray[BorderRightWidth];
    ObjHeight := ClientHeight;// - MargArray[BorderTopWidth] - MargArray[BorderBottomWidth];
  end;

  if not ViewImages or SubstImage then
  begin
    if ClientSizeKnown or (not SubstImage and ((SpecWidth >= 0) or (SpecHeight >= 0))) then
    begin {size to whatever is specified}
      AltWidth := ObjWidth;
      AltHeight := ObjHeight;
    end
    else
    begin
      if FAlt <> '' then {Alt text and no size specified, take as much space as necessary}
      begin
        Canvas.Font.Name := FontSans; {use same font as in Draw}
        Canvas.Font.Size := 8;
        ARect := Rect(0, 0, 0, 0);
        DrawTextW(Canvas.Handle, PWideChar(FAlt + CRLF), -1, ARect, DT_CALCRECT);
        with ARect do
        begin
          AltWidth := Right + 16 + 8 + 2;
          AltHeight := Max(16 + 8, Bottom);
        end;
      end
      else
      begin {no Alt text and no size specified}
        AltWidth := Max(ObjWidth, 16 + 8);
        AltHeight := Max(ObjHeight, 16 + 8);
      end;
      ClientHeight := AltHeight;
      ClientWidth  := AltWidth;
    end;
  end;
end;

{----------------TfrxFrHtImageObj.DoDraw}
procedure TfrxFrHtImageObj.DoDraw(Canvas: TCanvas; XX, Y: Integer; ddImage: TfrxHtImage);
{Y relative to top of display here}
var
  W, H: Integer;
begin
  if (ddImage = ErrorImage) or (ddImage = DefImage) then
  begin
    W := ddImage.Width;
    H := ddImage.Height;
  end
  else if ddImage = nil then
    exit
  else
  begin
    W := ObjWidth;
    H := ObjHeight;
  end;
  try
    // Yunqa.de removed: ddImage.Transp := Transparent;
    if (W > 0) and (H > 0) then
      if IsCopy then
        ddImage.Print(Canvas, XX, Y, W, H, clWhite)
      else
        ddImage.Draw(Canvas, XX, Y, W, H);
  except
    //on E: Exception do
    //begin
    //  LastExceptionMessage := E.Message;
    //  LastDdImage := ddImage;
    //end;
  end;
end;

{----------------TfrxFrHtImageObj.Draw}

//-- BG ---------------------------------------------------------- 12.06.2010 --
procedure GetRaisedColors(SectionList: TfrxHtDocument; Canvas: TCanvas; out Light, Dark: TColor);  {$ifdef UseInline} inline; {$endif}
 {$ifdef UseInline} inline; {$endif}
var
  White: Boolean;
begin
  White := (ThemedColor(SectionList.Background) = clWhite);
  Dark := ThemedColor(clBtnShadow);
  if White then
    Light := clSilver
  else
    Light := ThemedColor(clBtnHighLight);
end;

//BG, 15.10.2010: issue 28: Borland C++ Builder does not accept an array as a result of a function.
// Thus move htStyles and htColors from HtmlUn2.pas to HtmlSubs.pas the only unit where they are used

function htStyles(P0, P1, P2, P3: ThtBorderStyle): ThtBorderStyleArray;
 {$ifdef UseInline} inline; {$endif}
begin
  Result[0] := P0;
  Result[1] := P1;
  Result[2] := P2;
  Result[3] := P3;
end;

function htColors(C0, C1, C2, C3: TColor): ThtColorArray;
 {$ifdef UseInline} inline; {$endif}
begin
  Result[0] := C0;
  Result[1] := C1;
  Result[2] := C2;
  Result[3] := C3;
end;

//-- BG ---------------------------------------------------------- 12.06.2010 --
function htRaisedColors(Light, Dark: TColor; Raised: Boolean): ThtColorArray; overload;
  {$ifdef UseInline} inline; {$endif}
begin
  if Raised then
    Result := htColors(Light, Light, Dark, Dark)
  else
    Result := htColors(Dark, Dark, Light, Light);
end;

//-- BG ---------------------------------------------------------- 12.06.2010 --
function htRaisedColors(SectionList: TfrxHtDocument; Canvas: TCanvas; Raised: Boolean): ThtColorArray; overload;
  {$ifdef UseInline} inline; {$endif}
var
  Light, Dark: TColor;
begin
  GetRaisedColors(SectionList, Canvas, Light, Dark);
  Result := htRaisedColors(Light, Dark, Raised);
end;

//-- BG ---------------------------------------------------------- 12.06.2010 --
procedure RaisedRectColor(Canvas: TCanvas;
  const ORect, IRect: TRect;
  const Colors: ThtColorArray;
  Styles: ThtBorderStyleArray); overload;
  {$ifdef UseInline} inline; {$endif}
{Draws colored raised or lowered rectangles for table borders}
begin
  DrawBorder(Canvas, ORect, IRect, Colors, Styles, clNone, False);
end;

procedure RaisedRect(SectionList: TfrxHtDocument; Canvas: TCanvas;
  X1, Y1, X2, Y2: Integer;
  Raised: Boolean;
  W: Integer);
  {$ifdef UseInline} inline; {$endif}
{Draws raised or lowered rectangles for table borders}
begin
  RaisedRectColor(Canvas,
    Rect(X1, Y1, X2, Y2),
    Rect(X1 + W, Y1 + W, X2 - W, Y2 - W),
    htRaisedColors(SectionList, Canvas, Raised),
    htStyles(bssSolid, bssSolid, bssSolid, bssSolid));
end;

procedure TfrxFrHtImageObj.DrawInline(Canvas: TCanvas; X, Y, YBaseline: Integer; FO: TfrxHtFontObj);
var
  TmpImage: TfrxHtImage;
  MiddleAlignTop: Integer;
  ViewImages: Boolean;
  SubstImage: Boolean;
  Ofst: Integer;
  SaveColor: TColor;
  ARect: TRect;
  YY: Integer;
begin
  ViewImages := Document.ShowImages;
  Dec(Y, Document.YOff);
  Dec(YBaseLine, Document.YOff);

  if ViewImages then
    TmpImage := Image
  else
    TmpImage := DefImage;
  SubstImage := not ViewImages or (TmpImage = ErrorImage) or (TmpImage = DefImage); {substitute image}

  with Canvas do
  begin
    Brush.Style := bsClear;
    Font.Size := 8;
    Font.Name := FontSans; {make this a property?}
    Font.Style := Font.Style - [fsBold];
  end;

  if SubstImage then
    Ofst := 4
  else
    Ofst := 0;

  if VertAlign = AMiddle then
    MiddleAlignTop := YBaseLine + FO.Descent - (FO.tmHeight div 2) - ((ClientHeight - VSpaceT + VSpaceB) div 2)
  else
    MiddleAlignTop := 0; {not used}

  DrawXX := X;
  DrawYY := Y;
  if Floating = ANone then
  begin
    case Positioning of
      posAbsolute:
      begin
        ARect := ContainingBox;
        if not IsAuto(GetPosition(reLeft)) then DrawXX := GetPosition(reLeft) + ARect.Left;
        if not IsAuto(GetPosition(reTop)) then DrawYY := GetPosition(reTop) + ARect.Top;
      end;

      posFixed:
      begin
        ARect := FDocument.ViewPort;
        if not IsAuto(GetPosition(reLeft)) then DrawXX := GetPosition(reLeft) + ARect.Left;
        if not IsAuto(GetPosition(reTop)) then DrawYY := GetPosition(reTop) + ARect.Top;
      end;

    else
      case VertAlign of
        ATop, ANone:
          DrawYY := Y + VSpaceT;
        AMiddle:
          DrawYY := MiddleAlignTop;
        ABottom, ABaseline:
          DrawYY := YBaseLine - ClientHeight - VSpaceB;
      end;

      if Positioning = posRelative then
      begin
        if not IsAuto(GetPosition(reLeft)) then Inc(DrawXX, GetPosition(reLeft));
        if not IsAuto(GetPosition(reTop)) then Inc(DrawYY, GetPosition(reTop));
      end;

    end;
//    if MargArray[BorderLeftWidth] > 0 then
//      Inc(DrawXX, MargArray[BorderLeftWidth]);
//    if MargArray[BorderTopWidth] > 0 then
//      Inc(DrawYY, MargArray[BorderTopWidth]);
  end;

  if not SubstImage or ((AltHeight >= 16 + 8) and (AltWidth >= 16 + 8)) then
    Self.DoDraw(Canvas, DrawXX + Ofst, DrawYY + Ofst, TmpImage);
  Inc(DrawYY, Document.YOff);

  SetTextAlign(Canvas.Handle, TA_Top);
  if SubstImage and (BorderWidth = 0) then
  begin
    Canvas.Font.Color := ThemedColor(FO.TheFont.Color);
  {calc the offset from the image's base to the alt= text baseline}
    case VertAlign of
      ATop, ANone:
        begin
          if FAlt <> '' then
            WrapTextW(         Canvas, X + 24, Y + Ofst                              , X + AltWidth - 2, Y + AltHeight - 1, FAlt);
          RaisedRect(Document, Canvas, X     , Y                                     , X + AltWidth    , Y + AltHeight    , False, 1);
        end;
      AMiddle:
        begin {MiddleAlignTop is always initialized}
          if FAlt <> '' then
            WrapTextW(         Canvas, X + 24, MiddleAlignTop + Ofst                 , X + AltWidth - 2, MiddleAlignTop + AltHeight - 1, FAlt);
          RaisedRect(Document, Canvas, X     , MiddleAlignTop                        , X + AltWidth    , MiddleAlignTop + AltHeight    , False, 1);
        end;
      ABottom, ABaseline:
        begin
          if FAlt <> '' then
            WrapTextW(         Canvas, X + 24, YBaseLine - AltHeight + Ofst - VSpaceB, X + AltWidth - 2, YBaseLine - VSpaceB - 1, FAlt);
          RaisedRect(Document, Canvas, X     , YBaseLine - AltHeight        - VSpaceB, X + AltWidth    , YBaseLine - VSpaceB    , False, 1);
        end;
    end;
  end;

  if (MargArray[BorderTopWidth] > 0) or (MargArray[BorderRightWidth] > 0) or (MargArray[BorderBottomWidth] > 0) or (MargArray[BorderLeftWidth] > 0) then
    with Canvas do
    begin
//      SaveColor := Pen.Color;
//      SaveWidth := Pen.Width;
//      SaveStyle := Pen.Style;
      Font.Color := ThemedColor(FO.TheFont.Color);
//      Pen.Color := Font.Color;
//      Pen.Width := BorderSize;
//      Pen.Style := psInsideFrame;
      try
        if (FAlt <> '') and SubstImage then
        begin
          {output Alt message}
          YY := DrawYY - Document.YOff;
          case VertAlign of
            ATop, ANone:
              WrapTextW(Canvas, DrawXX + 24, YY + Ofst, DrawXX + AltWidth - 2, YY + AltHeight - 1, FAlt);
            AMiddle:
              WrapTextW(Canvas, DrawXX + 24, YY + Ofst, DrawXX + AltWidth - 2, YY + AltHeight - 1, FAlt);
            ABottom, ABaseline:
              WrapTextW(Canvas, DrawXX + 24, YY + Ofst, DrawXX + AltWidth - 2, YY + AltHeight - 1, FAlt);
          end;
        end;

      finally
//        Pen.Color := SaveColor;
//        Pen.Width := SaveWidth;
//        Pen.Style := SaveStyle;
      end;
    end;

  if IsCopy and Assigned(Document.LinkDrawnEvent) and (FO.UrlTarget.Url <> '')
  then
    with Canvas do
    begin
      SaveColor := SetTextColor(Handle, clBlack);
      Brush.Color := clWhite;
      case VertAlign of
        ATop, ANone:
          ARect := Rect(X, Y + VSpaceT, X + ClientWidth, Y + VSpaceT + ClientHeight);
        AMiddle:
          ARect := Rect(X, MiddleAlignTop, X + ClientWidth, MiddleAlignTop + ClientHeight);
        ABottom, ABaseline:
          ARect := Rect(X, YBaseLine - ClientHeight - VSpaceB, X + ClientWidth, YBaseLine - VSpaceB);
      end;
      if IsCopy then
        Document.LinkDrawnEvent(Document.TheOwner, Document.LinkPage,
          FO.UrlTarget.Url, FO.UrlTarget.Target, ARect);
      SetTextColor(handle, SaveColor);
    end;
end;

{----------------TfrxHtCellBasic.Create}

constructor TfrxHtCellBasic.Create(Parent: TfrxHtBlock);
begin
  inherited Create;
  FOwnerBlock := Parent;
  if FOwnerBlock <> nil then
    FDocument := FOwnerBlock.Document;
end;

{----------------TfrxHtCellBasic.CreateCopy}

constructor TfrxHtCellBasic.CreateCopy(Parent: TfrxHtBlock; T: TfrxHtCellBasic);
var
  I: Integer;
  Tmp, Tmp1: TfrxHtSectionBase;
begin
  inherited Create;
  FOwnerBlock := Parent;
  if FOwnerBlock <> nil then
    FDocument := FOwnerBlock.Document;
  for I := 0 to T.Count - 1 do
  begin
    Tmp := T.Items[I];
    Tmp1 := TSectionClass(Tmp.ClassType).CreateCopy(Self, Tmp);
    Add(Tmp1, 0);
  end;
//  FRenderList := TRenderSectionBaseList.Create(false);
end;

//-- BG ---------------------------------------------------------- 01.12.2013 --
destructor TfrxHtCellBasic.Destroy;
begin
//  FRenderList.Free;
  inherited;
end;

{----------------TfrxHtCellBasic.Add}

procedure TfrxHtCellBasic.Add(Item: TfrxHtSectionBase; TagIndex: Integer);
var
  Section: TfrxHtSection absolute Item;
begin
  if Assigned(Item) then
  begin
    if Item is TfrxHtSection then
      Add(Section, TagIndex)
    else
      inherited Add(Item);
    Item.SetDocument(Document);
  end;
end;

//-- BG ---------------------------------------------------------- 14.01.2015 --
procedure TfrxHtCellBasic.Add(var Section: TfrxHtSection; TagIndex: Integer);
begin
  if Assigned(Section) then
  begin
    if Length(Section.XP) <> 0 then
    begin
      Section.ProcessText(TagIndex);
      if not (Section.WhiteSpaceStyle in [wsPre, wsPreWrap, wsPreLine]) and (Section.Len = 0)
        and not Section.AnchorName and (Section.FClearing = clrNone) then
      begin
        Section.CheckFree;
        FreeAndNil(Section); {discard empty TSections that aren't anchors}
        Exit;
      end;
    end;
    inherited Add(Section);
    Section.SetDocument(Document);
  end;
end;

//-- BG ---------------------------------------------------------- 07.09.2013 --
function TfrxHtCellBasic.CalcDisplayExtern: ThtDisplayStyle;
var
  I: Integer;
begin
  // a list of elements is displayed inline, if all elements' are displayed inline.
  for I := Count - 1 downto 0 do
    if Items[i].CalcDisplayExtern <> pdInline then
    begin
      Result := pdBlock;
      Exit;
    end;
  Result := pdInline;
end;

function TfrxHtCellBasic.CheckLastBottomMargin: Boolean;
{Look at the last item in this cell.  If its bottom margin was set to Auto,
 set it to 0}
var
  TB: TfrxHtSectionBase;
  Block: TfrxHtBlock absolute TB;
  I, J: Integer;
begin
  Result := False;

  I := 0; {find first block that isn't absolute positioning}
  while I < Count do
  begin
    TB := Items[I];
    if TB is TfrxHtSection and (Length(Trim(TfrxHtSection(TB).BuffS)) = 0) then
    else if not (TB is TfrxHtBlock) or (TfrxHtBlock(TB).Positioning <> PosAbsolute) then
        break;

    Inc(I);
  end;
  if I < Count then
  begin
    if TB is TfrxHtBlock then
    begin
      if Block.TopAuto then
      begin
        Block.MargArray[MarginTop] := 0;
//        Result := True;
      end;
//      if TB is TfrxHtBlockLI then
//        Result := TfrxHtBlockLI(TB).MyCell.CheckLastBottomMargin;
    end
  end;

  J := Count - 1; {find the preceding block that isn't absolute positioning}
  while J >= I do
  begin
    TB := Items[J];
    if TB is TfrxHtSection and (Length(Trim(TfrxHtSection(TB).BuffS)) = 0) then
    else if not (TB is TfrxHtBlock) or (TfrxHtBlock(TB).Positioning <> PosAbsolute) then
      break;

    Dec(J);
  end;
  if J >= 0 then
  begin
    if TB is TfrxHtBlock then
    begin
      if Block.BottomAuto then
      begin
        Block.MargArray[MarginBottom] := 0;
        Result := True;
      end;
      if TB is TfrxHtBlockLI then
        Result := TfrxHtBlockLI(TB).MyCell.CheckLastBottomMargin;
    end;
  end;
end;

//-- BG ---------------------------------------------------------- 04.08.2013 --
function TfrxHtCellBasic.IsCopy: Boolean;
begin
  Result := Document.IsCopy;
end;

procedure TfrxHtCellBasic.AddSectionsToList;
var
  I: Integer;
begin
  for I := 0 to Count - 1 do
    Items[I].AddSectionsToList;
end;

{----------------TfrxHtCellBasic.FindString}

function TfrxHtCellBasic.FindString(From: Integer; const ToFind: UnicodeString; MatchCase: Boolean): Integer;
var
  I: Integer;
begin
  Result := -1;
  for I := 0 to Count - 1 do
  begin
    Result := Items[I].FindString(From, ToFind, MatchCase);
    if Result >= 0 then
      Break;
  end;
end;

{----------------TfrxHtCellBasic.FindStringR}

function TfrxHtCellBasic.FindStringR(From: Integer; const ToFind: UnicodeString; MatchCase: Boolean): Integer;
var
  I: Integer;
begin
  Result := -1;
  for I := Count - 1 downto 0 do
  begin
    Result := Items[I].FindStringR(From, ToFind, MatchCase);
    if Result >= 0 then
      Break;
  end;
end;

{----------------TfrxHtCellBasic.FindSourcePos}

function TfrxHtCellBasic.FindSourcePos(DocPos: Integer): Integer;
var
  I: Integer;
begin
  Result := -1;
  for I := 0 to Count - 1 do
  begin
    Result := Items[I].FindSourcePos(DocPos);
    if Result >= 0 then
      Break;
  end;
end;

{$ifdef UseFormTree}
procedure TfrxHtCellBasic.FormTree(const Indent: ThtString; var Tree: ThtString);
var
  I: Integer;
  Item: TfrxHtSectionBase;
begin
  for I := 0 to Count - 1 do
  begin
    Item := Items[I];
    if Item is TfrxHtBlock then
      TfrxHtBlock(Item).FormTree(Indent, Tree)
    else if Item is TfrxHtSection then
      Tree := Tree + Indent + Copy(TfrxHtSection(Item).BuffS, 1, 10) + CrChar + LfChar
    else
      Tree := Tree + Indent + '----'^M^J;
  end;
end;
{$endif UseFormTree}

{----------------TfrxHtCellBasic.GetChAtPos}

function TfrxHtCellBasic.GetChAtPos(Pos: Integer; out Ch: WideChar; out Obj: TfrxHtSectionBase): Boolean;
var
  I: Integer;
begin
  Result := False;
  if (Pos >= StartCurs) and (Pos <= StartCurs + Len) then
    for I := 0 to Count - 1 do
    begin
      Result := TfrxHtSectionBase(Items[I]).GetChAtPos(Pos, Ch, Obj);
      if Result then
        Break;
    end;
end;


{----------------TfrxHtCellBasic.DoLogic}

function TfrxHtCellBasic.DoLogic(Canvas: TCanvas; Y, Width, AHeight, BlHt: Integer;
  var ScrollWidth: Integer; var Curs: Integer; ARemainHeight: Integer = 0): Integer;
{Do the entire layout of the cell or document.  Return the total cell or document pixel height}
var
  I, Sw, TheCount: Integer;
  H: Integer;
//  InlineSection: TInlineSection;
//  Item: TfrxHtSectionBase;
begin
//TODO -oBG, 15.03.2014: support display:inline
{$ifdef DO_PD_INLINE}
//  //TODO -oBG, 24.06.2012: merge sections with display=inline etc.
//  //TODO -oBG, 30.11.2013: To be more precise:
//  // Reading the HTML file must produce the actual document tree rather than preprocessing inline elements to TSections.
//  //
//  // The Renderer (DoLogic()/DrawLogic()) must produce a processing list. In the list consecutive elements of all child
//  // levels with display=inline are gathered in the (anonymous) TInlineSection element while block-level elements are
//  // added to the processing list directly. This ends up in a list of block level elements, just like TfrxHtCellBasic has
//  // been before property Display was introduced.
//  //
//  // Actually TInlineSection is intended to collect content of TSections of consecutive inline elements and must draw
//  // borders and background of the inlined elements.
//  InlineSection := nil;
//  FRenderList.Clear;
//  for I := 0 to Count - 1 do
//  begin
//    Item := Items[I];
//    if Item.CalcDisplayExtern = pdInline then
//    begin
//      if InlineSection = nil then
//      begin
//        InlineSection := TInlineSection.Create(Self);
//        FRenderList.Add(InlineSection);
//      end;
//      if Item is TfrxHtSection then
//        InlineSection.AddElement(TfrxHtSection(Item));
//    end
//    else
//    begin
//      InlineSection := nil;
//      FRenderList.Add(Items[I]);
//    end;
//  end;
{$endif}

  StartCurs := Curs;
  H := 0;
  ScrollWidth := 0;
  TheCount := Count;
  I := 0;
  while I < TheCount do
  begin
    try
      Sw := 0;
      Inc(H, Items[I].DrawLogic1(Canvas, 0, Y + H, 0, 0, Width, AHeight, BlHt, IMgr, Sw, Curs, ARemainHeight));
      ScrollWidth := Max(ScrollWidth, Sw);
      Inc(I);
    except
      on E: EProcessError do
      begin
        // Yunqa.de - Don't want message dialog for individual errors.
        // Yunqa.de MessageDlg(e.Message, mtError, [mbOK], 0);
        Delete(I);
        Dec(TheCount);
      end;
    end;
  end;
  Len := Curs - StartCurs;
  Result := H;
end;

{----------------TfrxHtCellBasic.MinMaxWidth}

procedure TfrxHtCellBasic.MinMaxWidth(Canvas: TCanvas; out Min, Max: Integer; AvailableWidth, AvailableHeight: Integer);
{Find the Width the cell would take if no wordwrap, Max, and the width if wrapped
 at largest word, Min}
var
  I, Mn, Mx: Integer;
begin
  Max := 0; Min := 0;
  for I := 0 to Count - 1 do
  begin
    Items[I].MinMaxWidth(Canvas, Mn, Mx, AvailableWidth, AvailableHeight);
    Max := Math.Max(Max, Mx);
    Min := Math.Max(Min, Mn);
  end;
end;

{----------------TfrxHtCellBasic.Draw}

function TfrxHtCellBasic.Draw(Canvas: TCanvas; ARect: TRect; ClipWidth, X: Integer; Y, XRef, YRef: Integer): Integer;
{draw the document or cell.  Note: individual sections not in ARect don't bother
 drawing}
var
  I: Integer;
  H: Integer;
begin
  H := Y;
  for I := 0 to Count - 1 do
    H := Items[I].Draw1(Canvas, ARect, IMgr, X, XRef, YRef);
  Result := H;
end;

{----------------TfrxHtBlock.Create}

constructor TfrxHtBlock.Create(Parent: TfrxHtCellBasic; Attributes: TfrxHtAttributeList; Prop: TfrxHTProperties);
var
  S: ThtString;
begin
  inherited Create(Parent, 0, Attributes, Prop);
  MyCell := TfrxHtBlockCell.Create(Self);
  DrawList := TfrxHtSectionBaseList.Create(False);

  Prop.GetVMarginArray(MargArrayO);
  HasBorderStyle := Prop.HasBorderStyle;
  FGColor := Prop.Props[Color];

  BlockTitle := Prop.PropTitle;
  if not (Self is TfrxHtBodyBlock) and not (Self is TfrxHtTableAndCaptionBlock)
    and Prop.GetBackgroundImage(S) and (S <> '') then
  begin {body handles its own image}
    BGImage := TfrxFrHtImageObj.SimpleCreate(MyCell, S);
    Prop.GetBackgroundPos(EmSize, ExSize, PRec);
  end;

  Visibility := Prop.GetVisibility;
  Prop.GetPageBreaks(BreakBefore, BreakAfter, KeepIntact);
  if Positioning <> posStatic then
  begin
    ZIndex := 10 * Prop.GetZIndex;
    if (Positioning = posAbsolute) and (ZIndex = 0) then
      ZIndex := 1; {abs on top unless otherwise specified}
  end;
  if (Floating in [ALeft, ARight]) and (ZIndex = 0) then
    ZIndex := 1;
  if not IsInFlow then
  begin
    MyIMgr := TfrxHtIndentManager.Create;
    MyCell.IMgr := MyIMgr;
  end;
  if not (Self is TfrxHtTableBlock) and not (Self is TfrxHtTableAndCaptionBlock) then
    CollapseAdjoiningMargins;
  HideOverflow := Prop.IsOverflowHidden;

  RTL := Prop.Props[TextDirection] = 'rtl';

  if VarIsEmpty(Prop.Props[TextAlign]) then // check for unassigned
  begin
    if RTL then
      Justify := Right
    else
      Justify := Left;
  end
  else if Prop.Props[TextAlign] = 'right' then
    Justify := Right
  else if Prop.Props[TextAlign] = 'center' then
    Justify := Centered
  else
    Justify := Left;

end;

//-- BG ---------------------------------------------------------- 17.05.2014 --
function Collapse(A, B: Integer): Integer; {$ifdef UseInline} inline; {$endif}
begin
  if A >= 0 then
    if B >= 0 then
      Result := Max(A, B)
    else
      Result := A + B
  else
    if B >= 0 then
      Result := B + A
    else
      Result := Min(A, B);
end;

procedure TfrxHtBlock.CollapseAdjoiningMargins;
{adjacent vertical margins need to be reduced}
var
  TB: TfrxHtSectionBase;
  Block: TfrxHtBlock absolute TB;
  LastMargin, I: Integer;
  Tag: TElemSymb;
  CD: ThtConvData;
begin
  CD := ConvData(100, 100 {width and height not known at this point},  EmSize, ExSize, BorderWidth);
  ConvVertMargins(MargArrayO, CD, MargArray);
  TopAuto := MarginTop in CD.IsAutoParagraph;
  BottomAuto := MarginBottom in CD.IsAutoParagraph;
  if (MargArray[PaddingTop] <> 0) or (MargArray[BorderTopWidth] <> 0) then {do nothing}
  else if Positioning = posAbsolute then
  begin
    if TopAuto then
      MargArray[MarginTop] := 0;
  end
  else if Floating in [ALeft, ARight] then {do nothing}
  else if Display = pdNone then {do nothing}
  else
  begin
    TB := nil;
    I := OwnerCell.Count - 1; {find the preceding block that isn't absolute positioning}
    while I >= 0 do
    begin
      TB := OwnerCell[I];
      if TB.Display <> pdNone then
        if not (TB is TfrxHtBlock) or (TfrxHtBlock(TB).Positioning <> PosAbsolute) then
          break;
      Dec(I);
    end;

    if OwnerCell.OwnerBlock <> nil then
      Tag := OwnerCell.OwnerBlock.Symbol
    else
      Tag := OtherChar;
    if I < 0 then
    begin {no previous non absolute block, remove any Auto paragraph space}
      case Tag of
        BodySy:
          MargArray[MarginTop] := Max(0, MargArray[MarginTop] - OwnerBlock.MargArray[MarginTop]);
      else
        if TopAuto then
          MargArray[MarginTop] := 0;
      end;
    end
    else
    begin
      if ((TB is TfrxHtTableBlock) or (TB is TfrxHtTableAndCaptionBlock)) and (TfrxHtBlock(TB).Floating in [ALeft, ARight]) and TopAuto then
        MargArray[MarginTop] := 0
      else if TB is TfrxHtBlock then
      begin
        if Block.Positioning = posStatic then
        begin
          LastMargin := Block.MargArray[MarginBottom];
          Block.MargArray[MarginBottom] := 0;
          MargArray[MarginTop] := Collapse(LastMargin, MargArray[MarginTop]);
        end
      end
      else if (Tag = LISy) and TopAuto and (Symbol in [ULSy, OLSy]) then
        MargArray[MarginTop] := 0; {removes space from nested lists}
    end;
  end;
end;

//-- BG ---------------------------------------------------------- 16.05.2014 --
procedure TfrxHtBlock.CollapseNestedMargins;
var
  TB: TfrxHtSectionBase;
  Block: TfrxHtBlock absolute TB;
  I, J, M: Integer;
begin
  if (OwnerCell <> Document) and (MargArray[piHeight] = 0) and (MargArray[piMinHeight] = 0) then
  begin
    J := 0;
    if MargArray[BorderTopWidth] = 0 then
    begin
      // collapse with first sectionbase, if it is an in-flow block.

      // find block to collapse
      while J < MyCell.Count do
      begin
        TB := MyCell[J];
        if TB.Display <> pdNone then
          if not (TB is TfrxHtBlock) or ((Block.Positioning <> PosAbsolute) and (Block.Floating = aNone)) then
            break;
        Inc(J);
      end;

      if J < MyCell.Count then
      begin
        if (TB is TfrxHtBlock) and (Block.Positioning = posStatic) then
        begin
          // collapse margins, if it has no top padding
          if (Block.MargArray[PaddingTop] = 0) then
          begin
            M := Block.MargArray[MarginTop];
            Block.MargArray[MarginTop] := 0;
            MargArray[MarginTop] := Collapse(MargArray[MarginTop], M);
          end;
        end;
      end;
    end;

    if MargArray[BorderBottomWidth] = 0 then
    begin
      // collapse with last sectionbase, if it is an in-flow block.

      // find block to collapse
      I := MyCell.Count - 1;
      while I >= J do
      begin
        TB := MyCell[I];
        if TB.Display <> pdNone then
          if not (TB is TfrxHtBlock) or ((Block.Positioning <> PosAbsolute) and (Block.Floating = aNone)) then
            break;
        Dec(I);
      end;

      if I >= 0 then
      begin
        if (TB is TfrxHtBlock) and (Block.Positioning = posStatic) then
        begin
          // collapse margins, if it has no bottom padding
          if (Block.MargArray[PaddingBottom] = 0) then
          begin
            M := Block.MargArray[MarginBottom];
            Block.MargArray[MarginBottom] := 0;
            MargArray[MarginBottom] := Collapse(MargArray[MarginBottom], M);
          end;
        end;
      end;
    end;
  end;
end;

//-- BG ---------------------------------------------------------- 09.10.2010 --
procedure TfrxHtBlock.ContentMinMaxWidth(Canvas: TCanvas; out Min, Max: Integer; AvailableWidth, AvailableHeight: Integer);
begin
  MyCell.MinMaxWidth(Canvas, Min, Max, AvailableWidth, AvailableHeight);
end;

//-- BG ---------------------------------------------------------- 06.10.2010 --
procedure TfrxHtBlock.ConvMargArray(BaseWidth, BaseHeight: Integer; out AutoCount: Integer);
begin
  frxHTMLStyleUn.ConvMargArray(MargArrayO, BaseWidth, BaseHeight, EmSize, ExSize, BorderWidth, AutoCount, MargArray);
end;

{----------------TfrxHtBlock.CreateCopy}

constructor TfrxHtBlock.CreateCopy(Parent: TfrxHtCellBasic; Source: TfrxHtmlNode);
var
  T: TfrxHtBlock absolute Source;
begin
  inherited CreateCopy(Parent,Source);
  System.Move(T.MargArray, MargArray, PtrSub(@Converted, @MargArray) + Sizeof(Converted));
  MyCell := TfrxHtBlockCell.CreateCopy(Self, T.MyCell);
  DrawList := TfrxHtSectionBaseList.Create(False);
  if Assigned(T.BGImage) and Document.PrintTableBackground then
    BGImage := TfrxFrHtImageObj.CreateCopy(MyCell, T.BGImage);
  MargArrayO := T.MargArrayO;
  if not IsInFlow then
  begin
    MyIMgr := TfrxHtIndentManager.Create;
    MyCell.IMgr := MyIMgr;
  end;
  BlockTitle := T.BlockTitle; // Thanks to Nagy Ervin.
end;

destructor TfrxHtBlock.Destroy;
begin
  BGImage.Free;
  TiledImage.Free;
  if MyIMgr <> nil then
  begin
    MyCell.IMgr := nil;
    FreeAndNil(MyIMgr);
  end;
  FreeAndNil(MyCell);
  DrawList.Free;
  inherited Destroy;
end;

procedure TfrxHtBlock.MinMaxWidth(Canvas: TCanvas; out Min, Max: Integer; AvailableWidth, AvailableHeight: Integer);
var
  MinCell, MaxCell: Integer;
  LeftSide, RightSide, AutoCount: Integer;
begin
  if (Display = pdNone) or (Positioning in [PosAbsolute, posFixed]) then
  begin
    Min := 0;
    Max := 0;
    Exit;
  end;

{$ifdef DO_BLOCK_INLINE}
  if Display = pdInline then
  begin
    inherited MinMaxWidth(Canvas, Min, Max, AvailableWidth, AvailableHeight);
    exit;
  end;
{$endif}

  ConvMargArray(AvailableWidth, AvailableHeight, AutoCount);
  //HideOverflow := HideOverflow and (MargArray[piWidth] <> Auto) and (MargArray[piWidth] > 20);
  if HideOverflow and (MargArray[piWidth] <> Auto) and (MargArray[piWidth] > 20) then
  begin
    MinCell := MargArray[piWidth];
    MaxCell := MinCell;
  end
  else
    ContentMinMaxWidth(Canvas, MinCell, MaxCell, AvailableWidth, AvailableHeight);
  if MargArray[MarginLeft] = Auto then
    MargArray[MarginLeft] := 0;
  if MargArray[MarginRight] = Auto then
    MargArray[MarginRight] := 0;
  if MargArray[piWidth] = Auto then
    MargArray[piWidth] := 0;
  LeftSide := MargArray[MarginLeft] + MargArray[BorderLeftWidth] + MargArray[PaddingLeft];
  RightSide := MargArray[MarginRight] + MargArray[BorderRightWidth] + MargArray[PaddingRight];
  if MargArray[piWidth] > 0 then
  begin
    Min := MargArray[piWidth] + LeftSide + RightSide;
    Max := Min;
  end
  else
  begin
    Min := Math.Max(MinCell, MargArray[piWidth]) + LeftSide + RightSide;
    Max := Math.Max(MaxCell, MargArray[piWidth]) + LeftSide + RightSide;
  end;
  if (MargArray[piMinWidth] > 0) and (MargArray[piMinWidth] > Min) then
    Min := MargArray[piMinWidth];
  if (MargArray[piMaxWidth] > 0) and (MargArray[piMaxWidth] < Max) then
    Max := MargArray[piMaxWidth];
end;

{----------------TfrxHtBlock.FindString}

function TfrxHtBlock.FindString(From: Integer; const ToFind: UnicodeString; MatchCase: Boolean): Integer;
begin
  case Display of
    pdNone: Result := -1;
{$ifdef DO_BLOCK_INLINE}
    pdInline: Result := inherited FindString(From, ToFind, MatchCase);
{$endif}
  else
    Result := MyCell.FindString(From, ToFind, MatchCase);
  end;
end;

{----------------TfrxHtBlock.FindStringR}

function TfrxHtBlock.FindStringR(From: Integer; const ToFind: UnicodeString; MatchCase: Boolean): Integer;
begin
  case Display of
    pdNone: Result := -1;
{$ifdef DO_BLOCK_INLINE}
    pdInline: Result := inherited FindStringR(From, ToFind, MatchCase);
{$endif}
  else
    Result := MyCell.FindStringR(From, ToFind, MatchCase);
  end;
end;

procedure TfrxHtBlock.AddSectionsToList;
begin
  MyCell.AddSectionsToList;
end;

{----------------TfrxHtBlock.GetChAtPos}

function TfrxHtBlock.GetChAtPos(Pos: Integer; out Ch: WideChar; out Obj: TfrxHtSectionBase): Boolean;
begin
  Obj := nil;
  case Display of
    pdNone:   Result := False;
{$ifdef DO_BLOCK_INLINE}
    pdInline: Result := inherited GetChAtPos(Pos, Ch, Obj);
{$endif}
  else
    Result := MyCell.GetChAtPos(Pos, Ch, Obj);
  end;
end;

//-- BG ---------------------------------------------------------- 30.11.2013 --
function TfrxHtBlock.CalcDisplayExtern: ThtDisplayStyle;
begin
  Result := inherited CalcDisplayExtern;
  case Result of
    pdInline:
      Result := MyCell.CalcDisplayExtern;
  end;
end;

//-- BG ---------------------------------------------------------- 07.09.2013 --
function TfrxHtBlock.CalcDisplayIntern: ThtDisplayStyle;
begin
  Result := inherited CalcDisplayIntern;
//  case Result of
//    pdNone:;
//  else
//    //Result := MyCell.CalcDisplayExtern;
//  end;
end;

function TfrxHtBlock.FindDocPos(SourcePos: Integer; Prev: Boolean): Integer;
begin
  case Display of
    pdNone:   Result := -1;
{$ifdef DO_BLOCK_INLINE}
    pdInline: Result := inherited FindDocPos(SourcePos, Prev);
{$endif}
  else
    Result := MyCell.FindDocPos(SourcePos, Prev);
  end;
end;

function TfrxHtBlock.FindSourcePos(DocPos: Integer): Integer;
begin
  case Display of
    pdNone:   Result := -1;
{$ifdef DO_BLOCK_INLINE}
    pdInline: Result := inherited FindSourcePos(DocPos);
{$endif}
  else
    Result := MyCell.FindSourcePos(DocPos);
  end;
end;

{----------------TfrxHtBlock.FindWidth}

function TfrxHtBlock.FindWidth(Canvas: TCanvas; AWidth, AHeight, AutoCount: Integer): Integer;
var
  Marg2: Integer;
  MinWidth, MaxWidth: Integer;

  function BordPad: Integer;
  begin
    Result := MargArray[BorderLeftWidth] + MargArray[BorderRightWidth] +
              MargArray[PaddingLeft] + MargArray[PaddingRight];
  end;

  function BordWidth: Integer;
  begin
    Result := MargArray[BorderLeftWidth] + MargArray[BorderRightWidth] +
              MargArray[PaddingLeft] + MargArray[PaddingRight] +
              MargArray[MarginLeft] + MargArray[MarginRight];
  end;

  procedure CalcWidth;
  begin
    if Positioning = posAbsolute then
    begin
      if (MargArray[piLeft] = Auto) and (MargArray[piRight] <> Auto) then
        MargArray[piWidth] := MaxWidth
      else
        MargArray[piWidth] := Max(MinWidth, AWidth - BordWidth - LeftP);
    end
    else if (Floating in [ALeft, ARight]) then
      MargArray[piWidth] := Min(MaxWidth, AWidth - BordWidth)
    else
      MargArray[piWidth] := Max(MinWidth, AWidth - BordWidth);
  end;

  procedure CalcMargRt;
  begin
    MargArray[MarginRight] := Max(0, AWidth - BordPad - MargArray[MarginLeft] - MargArray[piWidth]);
  end;

  procedure CalcMargLf;
  begin
    MargArray[MarginLeft] := Max(0, AWidth - BordPad - MargArray[MarginRight] - MargArray[piWidth]);
  end;

begin
  ContentMinMaxWidth(Canvas, MinWidth, MaxWidth, AWidth, AHeight);
  //HideOverflow := HideOverflow and (MargArray[piWidth] <> Auto) and (MargArray[piWidth] > 20);
  case AutoCount of
    0:
      begin
        if (Justify in [centered, Right]) and (Positioning = posStatic)
          and not (Floating in [ALeft, ARight]) and
          (MargArray[MarginLeft] = 0) and (MargArray[MarginRight] = 0) then
        begin
          ApplyBoxWidthSettings(MargArray, MinWidth, MaxWidth);
          Marg2 := Max(0, AWidth - MargArray[piWidth] - BordPad);
          case Justify of
            centered:
              begin
                MargArray[MarginLeft] := Marg2 div 2;
                MargArray[MarginRight] := Marg2 div 2;
              end;
            right:
              MargArray[MarginLeft] := Marg2;
          end;
        end;
      end;

    1:
      if MargArray[piWidth] = Auto then begin
        ApplyBoxWidthSettings(MargArray, MinWidth, MaxWidth);
        CalcWidth;
      end
      else
      begin
        if MargArray[MarginRight] = Auto then
          if (Floating in [ALeft, ARight]) then
            MargArray[MarginRight] := 0
          else
            CalcMargRt
        else
          CalcMargLf;
      end;

    2:
      if MargArray[piWidth] = Auto then
      begin
        if MargArray[MarginLeft] = Auto then
          MargArray[MarginLeft] := 0
        else
          MargArray[MarginRight] := 0;
        ApplyBoxWidthSettings(MargArray, MinWidth, MaxWidth);
        CalcWidth;
      end
      else
      begin
        Marg2 := Max(0, AWidth - MargArray[piWidth] - BordPad);
        MargArray[MarginLeft] := Marg2 div 2;
        MargArray[MarginRight] := Marg2 - MargArray[MarginLeft];
      end;

    3:
      begin
        MargArray[MarginLeft] := 0;
        MargArray[MarginRight] := 0;
        ApplyBoxWidthSettings(MargArray, MinWidth, MaxWidth);
        CalcWidth;
      end;
  end;
  Result := MargArray[piWidth];
end;

{----------------TfrxHtBlock.DrawLogic}

function TfrxHtBlock.DrawLogic1(Canvas: TCanvas; X, Y, XRef, YRef, AWidth, AHeight, BlHt: Integer; IMgr: TfrxHtIndentManager;
  var MaxWidth, Curs: Integer; ARemainHeight: Integer): Integer;
var
  ScrollWidth, YClear: Integer;
  LIndex, RIndex: Integer;
  SaveID: TObject;
  TotalWidth, LeftWidths, RightWidths, MiscWidths: Integer;
  AutoCount: Integer;
  BlockHeight: Integer;
  IB, Xin: Integer;

  function GetClientContentBot(ClientContentBot: Integer): Integer;
  var
    H: Integer;
  begin
    if VarIsIntNull(MargArrayO[piHeight]) then
      Result := Max(ContentTop, ClientContentBot)
    else
    begin
      if Pos('%', VarToStr(MargArrayO[piHeight])) > 0 then
        H := LengthConv(MargArrayO[piHeight], False, AHeight, EmSize, ExSize, 0)
      else
        H := MargArray[piHeight];
      Result := Max(H + ContentTop, ContentTop);
    end;
  end;

  procedure DrawLogicAsBlock;
  var
    LIndent, RIndent: Integer;
    ARect: TRect;
  begin
    YDraw := Y;
    Xin := X;
    ClearAddOn := GetClearSpace(IMgr, Y);
    StartCurs := Curs;
    MaxWidth := AWidth;

    ConvMargArray(AWidth, AHeight, AutoCount);
    HasBorderStyle :=
      (ThtBorderStyle(MargArray[BorderTopStyle]) <> bssNone) or
      (ThtBorderStyle(MargArray[BorderRightStyle]) <> bssNone) or
      (ThtBorderStyle(MargArray[BorderBottomStyle]) <> bssNone) or
      (ThtBorderStyle(MargArray[BorderLeftStyle]) <> bssNone);

    ApplyBoxSettings(MargArray);
    ContentWidth := FindWidth(Canvas, AWidth, AHeight, AutoCount);
    LeftWidths   := MargArray[MarginLeft] + MargArray[PaddingLeft] + MargArray[BorderLeftWidth];
    RightWidths  := MargArray[MarginRight] + MargArray[PaddingRight] + MargArray[BorderRightWidth];
    MiscWidths   := LeftWidths + RightWidths;
    TotalWidth   := MiscWidths + ContentWidth;

    case Positioning of
      posAbsolute:
        ARect := ContainingBox;

      posFixed:
        ARect := Document.ViewPort;
    end;

    FIndent := LeftWidths;
    TopP := 0;
    LeftP := 0;
    case Positioning of
      posAbsolute,
      posFixed:
      begin
        if not IsAuto(GetPosition(reLeft)) then
          X := ARect.Left + GetPosition(reLeft)
        else if not IsAuto(GetPosition(reRight)) and not IsAuto(MargArray[piWidth]) then
          X := ARect.Right - GetPosition(reRight) - MargArray[piWidth];
        LeftP := X;

        if not IsAuto(GetPosition(reTop)) then
          Y := ARect.Top + GetPosition(reTop)
        else if not IsAuto(GetPosition(reBottom)) and not IsAuto(MargArray[piHeight]) then
          Y := ARect.Bottom - GetPosition(reBottom) - MargArray[piHeight];
        TopP := Y;

        DrawTop := TopP + Max(0, MargArray[MarginTop]); {Border top}
      end;
    else
      if Positioning = posRelative then
      begin
        if not IsAuto(GetPosition(reLeft)) then Inc(LeftP, GetPosition(reLeft));
        if not IsAuto(GetPosition(reTop)) then Inc(TopP, GetPosition(reTop));
      end;

      case Floating of
        ALeft:
        begin
          YClear := Y;
          LIndent := IMgr.AlignLeft(YClear, TotalWidth);
          FIndent := LIndent + LeftWidths - X;
        end;

        ARight:
        begin
          YClear := Y;
          RIndent := IMgr.AlignRight(YClear, TotalWidth);
          FIndent := RIndent + LeftWidths - X;
        end;
      else
        YClear := Y + ClearAddon;
      end;
      DrawTop := YClear + Max(0, MargArray[MarginTop]); {Border top}
    end;
    Inc(X, FIndent);

    ContentTop := DrawTop + MargArray[PaddingTop] + MargArray[BorderTopWidth];

    if not IsInFlow then
    begin
      RefIMgr := IMgr;
      if MyCell.IMgr = nil then
      begin
        MyIMgr := TfrxHtIndentManager.Create;
        MyCell.IMgr := MyIMgr;
      end;
      IMgr := MyCell.IMgr;
      IMgr.Init(0, ContentWidth);
    end
    else
    begin
      MyCell.IMgr := IMgr;
    end;

    SaveID := IMgr.CurrentID;
    IMgr.CurrentID := Self;

    LIndex := IMgr.SetLeftIndent(X, YClear);
    RIndex := IMgr.SetRightIndent(X + ContentWidth, YClear);

    if MargArray[piHeight] > 0 then
      BlockHeight := MargArray[piHeight]
    else if AHeight > 0 then
      BlockHeight := AHeight
    else
      BlockHeight := BlHt;

    case Positioning of
      posRelative:
      begin
        MyCell.DoLogicX(Canvas,
          X,
          ContentTop + TopP,
          XRef,
          ContentTop + TopP,
          ContentWidth, MargArray[piHeight], BlockHeight, ScrollWidth, Curs, ARemainHeight);
        MaxWidth := ScrollWidth + MiscWidths - MargArray[MarginRight] + LeftP - Xin;
        ClientContentBot := GetClientContentBot(MyCell.tcContentBot - TopP);
      end;

      posAbsolute:
      begin
        MyCell.DoLogicX(Canvas,
          X,
          ContentTop,
          XRef + LeftP + MargArray[MarginLeft] + MargArray[BorderLeftWidth],
          YRef + TopP + MargArray[MarginTop] + MargArray[BorderTopWidth],
          ContentWidth, MargArray[piHeight], BlockHeight, ScrollWidth, Curs, ARemainHeight);
        MaxWidth := ScrollWidth + MiscWidths - MargArray[MarginRight] + LeftP - Xin;
        ClientContentBot := GetClientContentBot(MyCell.tcContentBot);
        IB := IMgr.ImageBottom; {check for image overhang}
        if IB > ClientContentBot then
          ClientContentBot := IB;
      end;

      posFixed:
      begin
        MyCell.DoLogicX(Canvas,
          X,
          ContentTop,
          XRef + LeftP + MargArray[MarginLeft] + MargArray[BorderLeftWidth],
          YRef + TopP + MargArray[MarginTop] + MargArray[BorderTopWidth],
          ContentWidth, MargArray[piHeight], BlockHeight, ScrollWidth, Curs, ARemainHeight);
        MaxWidth := ScrollWidth + MiscWidths - MargArray[MarginRight] + LeftP - Xin;
        ClientContentBot := GetClientContentBot(MyCell.tcContentBot);
        IB := IMgr.ImageBottom; {check for image overhang}
        if IB > ClientContentBot then
          ClientContentBot := IB;
      end;

    else
      MyCell.DoLogicX(Canvas,
        X,
        ContentTop,
        XRef,
        YRef,
        ContentWidth, MargArray[piHeight], BlockHeight, ScrollWidth, Curs, ARemainHeight);
      MaxWidth := Indent + ScrollWidth + RightWidths;
      ClientContentBot := GetClientContentBot(MyCell.tcContentBot);
    end;
    Len := Curs - StartCurs;

    ContentBot := ClientContentBot + MargArray[PaddingBottom] + MargArray[BorderBottomWidth] + MargArray[MarginBottom];
    if ContentBot = ContentTop then
      if IsInFlow then
      begin
        // no content, hide top margin
        DrawTop := YClear;
        ContentTop := DrawTop;
        ClientContentBot := ContentTop;
        ContentBot := ClientContentBot;
      end;

    DrawBot := Max(ClientContentBot, MyCell.tcDrawBot) + MargArray[PaddingBottom] + MargArray[BorderBottomWidth];

    Result := ContentBot - Y;

    if Assigned(BGImage) and Document.ShowImages then
    begin
      BGImage.DrawLogicInline(Canvas, nil, 100, 0);
      if BGImage.Image = ErrorImage then
      begin
        FreeAndNil(BGImage);
        NeedDoImageStuff := False;
      end
      else
      begin
        BGImage.ClientSizeKnown := True; {won't need reformat on InsertImage}
        NeedDoImageStuff := True;
      end;
    end;
    SectionHeight := Result;
    IMgr.FreeLeftIndentRec(LIndex);
    IMgr.FreeRightIndentRec(RIndex);
    if not IsInFlow then
    begin
      case Positioning of
        posAbsolute,
        posFixed:
          DrawHeight := 0
      else
        DrawHeight := 0; //SectionHeight;
        case Floating of
          ALeft:  RefIMgr.AddLeft(YClear, ContentBot, TotalWidth);
          ARight: RefIMgr.AddRight(YClear, ContentBot, TotalWidth);
        end;
      end;
      //SectionHeight := 0;
      Result := 0;
    end
    else
    begin
      DrawHeight := IMgr.ImageBottom - Y; {in case image overhangs}
      if DrawHeight < SectionHeight then
        DrawHeight := SectionHeight;
    end;
    IMgr.CurrentID := SaveID;
    if DrawList.Count = 0 then
      DrawSort;
  end;

  procedure DrawLogicInline;
  begin
    //TODO -oBG, 15.03.2014: draw logic inline
    DrawLogicAsBlock;
  end;

  procedure Invisible;
  begin
    SectionHeight := 0;
    DrawHeight := 0;
    ContentBot := 0;
    DrawBot := 0;
    MaxWidth := 0;
    Result := 0;
  end;

begin {TfrxHtBlock.DrawLogic1}
  case CalcDisplayIntern of

    pdInline:
      DrawLogicInline;

    pdNone:
      Invisible;
  else
//    pdBlock:
//    pdTable:
      DrawLogicAsBlock;
  end;
end;

{----------------TfrxHtBlock.DrawSort}

procedure TfrxHtBlock.DrawSort;
var
  I, ZeroIndx, EndZeroIndx, SBZIndex: Integer;
  SB: TfrxHtSectionBase;

  procedure InsertSB(I1, I2: Integer);
  var
    J: Integer;
    Inserted: Boolean;
  begin
    Inserted := False;
    for J := I1 to I2 - 1 do
      if SBZIndex < DrawList[J].ZIndex then
      begin
        DrawList.Insert(J, SB);
        Inserted := True;
        Break;
      end;
    if not Inserted then
      DrawList.Insert(I2, SB);
  end;

begin
  ZeroIndx := 0;
  EndZeroIndx := 0;
  for I := 0 to MyCell.Count - 1 do
  begin
    SB := MyCell.Items[I];
    SB.FOwnerBlock := Self;
    SBZIndex := SB.ZIndex;
    if SBZIndex < 0 then
    begin
      InsertSB(0, ZeroIndx);
      Inc(ZeroIndx);
      Inc(EndZeroIndx);
    end
    else if SBZIndex = 0 then {most items go here}
    begin
      DrawList.Insert(EndZeroIndx, SB);
      Inc(EndZeroIndx);
    end
    else
      InsertSB(EndZeroIndx, DrawList.Count);
  end;
end;

{----------------TfrxHtBlock.Draw1}

function TfrxHtBlock.Draw1(Canvas: TCanvas; const ARect: TRect; IMgr: TfrxHtIndentManager; X, XRef, YRef: Integer): Integer;

  procedure DrawAsBlock;
  var
    Y: Integer;
  begin
    Y := YDraw;
    Result := Y + SectionHeight;

    if Visibility = viHidden then
      Exit;

    if Document.SkipDraw then
    begin
      Document.SkipDraw := False;
      Exit;
    end;

    case Positioning of
      posRelative:
        DrawBlock(Canvas, ARect, IMgr, X + LeftP, Y + TopP, XRef, YRef);

      posAbsolute:
        DrawBlock(Canvas, ARect, IMgr, LeftP, TopP, 0, 0);

      posFixed:
        DrawBlock(Canvas, ARect, IMgr, LeftP, TopP, 0, 0);

    else
      if Floating in [ALeft, ARight] then
        DrawBlock(Canvas, ARect, IMgr, X, Y, XRef, YRef)
      else
        DrawBlock(Canvas, ARect, IMgr, X, Y, XRef, YRef);
    end;
  end;

  procedure DrawInline;
  begin
    DrawAsBlock;
  end;

begin
  case CalcDisplayIntern of

    pdInline:
      DrawInline;

    pdNone:
      Result := 0;

  else
//    pdBlock:
//    pdTable:
      DrawAsBlock;
  end;
end;

{----------------TfrxHtBlock.DrawBlock}

procedure TfrxHtBlock.DrawBlock(Canvas: TCanvas; const ARect: TRect;
  IMgr: TfrxHtIndentManager; X, Y, XRef, YRef: Integer);

var
  YOffset: Integer;
  XR, YB, RefX, RefY, TmpHt: Integer;
  SaveID: TObject;
  ImgOK, HasBackgroundColor: Boolean;
  IT, IH, FT, IW: Integer;
  Rgn, SaveRgn, SaveRgn1: HRgn;
  OpenRgn: Boolean;
  PdRect, CnRect: TRect; // padding rect, content rect
  NegativeMargins: Boolean;
begin
  YOffset := Document.YOff;

  case Floating of
    ALeft, ARight:
    begin
      //X := IMgr.LfEdge + Indent;
      X := X + Indent;
      RefX := X - MargArray[PaddingLeft] - MargArray[BorderLeftWidth];
      XR := X + ContentWidth + MargArray[PaddingRight] + MargArray[BorderRightWidth];
      RefY := DrawTop;
      YB := ContentBot - MargArray[MarginBottom];
    end;
  else
// BG, 08.09.2013: inline vs block:
//  X of centered blocks may differ from (DrawRect.Left - MargArray[MarginLeft]) calculated in DrawLogic1().
    RefX := X + MargArray[MarginLeft];

    X := X + Indent;

//    RefX := DrawRect.Left;
//    X := RefX - MargArray[MarginLeft] + Indent;
    XR := X + ContentWidth + MargArray[PaddingRight] + MargArray[BorderRightWidth]; {current right edge}
    RefY := Y + ClearAddon + MargArray[MarginTop];
    YB := ContentBot - MargArray[MarginBottom];
    case Positioning of
      posRelative:
        Inc(YB, TopP);

    end;
  end;

  // MyRect is the outmost rectangle of this block incl. border and padding but without margins in screen coordinates.
  case Positioning of
    posFixed:
      MyRect := Rect(RefX, RefY, XR, YB);
  else
    MyRect := Rect(RefX, RefY - YOffset, XR, YB - YOffset);
  end;

  // PdRect is the border rectangle of this block incl. padding in screen coordinates
  PdRect.Left   := MyRect.Left   + MargArray[BorderLeftWidth];
  PdRect.Top    := MyRect.Top    + MargArray[BorderTopWidth];
  PdRect.Right  := MyRect.Right  - MargArray[BorderRightWidth];
  PdRect.Bottom := MyRect.Bottom - MargArray[BorderBottomWidth];

  // CnRect is the content rectangle of this block in screen coordinates
  CnRect.Left   := PdRect.Left   + MargArray[PaddingLeft];
  CnRect.Top    := PdRect.Top    + MargArray[PaddingTop];
  CnRect.Right  := PdRect.Right  - MargArray[PaddingRight];
  CnRect.Bottom := PdRect.Bottom - MargArray[PaddingBottom];

  IT := Max(0, ARect.Top - 2 - PdRect.Top);
  FT := Max(PdRect.Top, ARect.Top - 2); {top of area drawn, screen coordinates}
  IH := Min(PdRect.Bottom, ARect.Bottom) - FT; {height of area actually drawn}
  IW := PdRect.Right - PdRect.Left;

  NegativeMargins := (MargArray[MarginLeft] < 0) or (MargArray[MarginTop] < 0) or (MargArray[MarginBottom] < 0) or (MargArray[MarginRight] < 0);
  SaveRgn1 := 0;
  OpenRgn := ((Positioning <> PosStatic) or NegativeMargins) and (Document.TableNestLevel > 0);
  if OpenRgn then
  begin
    SaveRgn1 := CreateRectRgn(0, 0, 1, 1);
    case GetClipRgn(Canvas.Handle, SaveRgn1) of
      -1, // failed to get the clip region
       0: // there is no clip region
        begin
          DeleteObject(SaveRgn1);
          SaveRgn1 := 0;
        end;
    else
      //1: got the clip region
      SelectClipRgn(Canvas.Handle, 0);
    end;
  end;
  try
    if (MyRect.Top <= ARect.Bottom) and (MyRect.Bottom >= ARect.Top) then
    begin
      HasBackgroundColor := MargArray[BackgroundColor] <> clNone;
      try
        if NeedDoImageStuff and Assigned(BGImage) and (BGImage.Image <> DefImage) then
        begin
          if BGImage.Image = ErrorImage then {Skip the background image}
            FreeAndNil(BGImage)
          else
          try
            if Floating in [ALeft, ARight] then
              TmpHt := DrawBot - ContentTop + MargArray[PaddingTop] + MargArray[PaddingBottom]
            else
              TmpHt := ClientContentBot - ContentTop + MargArray[PaddingTop] + MargArray[PaddingBottom];

            BgImage.Image.TileImage( PRec,
              MargArray[PaddingLeft] + ContentWidth + MargArray[PaddingRight], TmpHt,
              TiledImage, NoMask);
            if IsCopy and Assigned(TiledImage) then
              TiledImage.Bitmap.HandleType := bmDIB;
          except {bad image, get rid of it}
            FreeAndNil(BGImage);
            FreeAndNil(TiledImage);
          end;
          NeedDoImageStuff := False;
        end;

        if Document.NoOutput then
          exit;

        ImgOK := not NeedDoImageStuff and (BGImage <> nil) and (BGImage.Image <> DefImage) and (TiledImage <> nil) and Document.ShowImages;

        if HasBackgroundColor {and (not Document.Printing or Document.PrintBackground)} then
        begin {color the Padding Region}
          Canvas.Brush.Style := bsSolid;
          Canvas.Brush.Color := ThemedColor(MargArray[BackgroundColor]) or PalRelative;
        end
        else
          Canvas.Brush.Style := bsClear;

        if ImgOK then
          if not IsCopy then
            TiledImage.DrawUnstretched(Canvas, PdRect.Left, FT, IW, IH, 0, IT, HasBackgroundColor )
          else
            TiledImage.PrintUnstretched(Canvas, PdRect.Left, FT, IW, IH, 0, IT, HasBackgroundColor and Document.PrintBackground )
        else
          Canvas.FillRect(Rect(PdRect.Left, FT, PdRect.Right, FT + IH));
      except
      end;
    end;

    if HideOverflow then
    begin
      Rgn := 0;
      SaveRgn := 0;
      if Floating = ANone then
        GetClippingRgn(Canvas, CnRect, False, Rgn, SaveRgn)
      else
        GetClippingRgn(Canvas, PdRect, False, Rgn, SaveRgn);
      SelectClipRgn(Canvas.Handle, Rgn);
    end;
    try
      SaveID := IMgr.CurrentID;
      Imgr.CurrentID := Self;
      case Positioning of
        posRelative:
          DrawTheList(Canvas, ARect, ContentWidth, X,
            RefX + MargArray[BorderLeftWidth] + MargArray[PaddingLeft],
            Y + MargArray[MarginTop] + MargArray[BorderTopWidth] + MargArray[PaddingTop]);

        posAbsolute:
          DrawTheList(Canvas, ARect, ContentWidth, X,
            RefX + MargArray[BorderLeftWidth],
            Y + MargArray[MarginTop] + MargArray[BorderTopWidth]);

        posFixed:
          DrawTheList(Canvas, ARect, ContentWidth, X,
            PdRect.Left,
            PdRect.Top);
      else
        DrawTheList(Canvas, ARect, ContentWidth, X, XRef, YRef);
      end;
      Imgr.CurrentID := SaveID;
    finally
      if HideOverflow then {restore any previous clip region}
      begin
        SelectClipRgn(Canvas.Handle, SaveRgn);
        DeleteObject(Rgn);
        if SaveRgn <> 0 then
          DeleteObject(SaveRgn);
      end;
    end;
    DrawBlockBorder(Canvas, MyRect, PdRect);
  finally
    if SaveRgn1 <> 0 then
    begin
      SelectClipRgn(Canvas.Handle, SaveRgn1);
      DeleteObject(SaveRgn1);
    end;
  end;
end;

procedure TfrxHtBlock.DrawBlockBorder(Canvas: TCanvas; const ORect, IRect: TRect);
begin
  if HasBorderStyle then
    if (ORect.Left <> IRect.Left) or (ORect.Top <> IRect.Top) or (ORect.Right <> IRect.Right) or (ORect.Bottom <> IRect.Bottom) then
      DrawBorder(Canvas, ORect, IRect,
        htColors(MargArray[BorderLeftColor], MargArray[BorderTopColor], MargArray[BorderRightColor], MargArray[BorderBottomColor]),
        htStyles(ThtBorderStyle(MargArray[BorderLeftStyle]), ThtBorderStyle(MargArray[BorderTopStyle]), ThtBorderStyle(MargArray[BorderRightStyle]), ThtBorderStyle(MargArray[BorderBottomStyle])),
        MargArray[BackgroundColor], False)
end;

procedure TfrxHtBlock.DrawTheList(Canvas: TCanvas; const ARect: TRect; ClipWidth, X, XRef, YRef: Integer);
{draw the list sorted by Z order.}
var
  I: Integer;
  SaveID: TObject;
begin
  if not IsInFlow then
    with MyCell do
    begin
      SaveID := IMgr.CurrentID;
      IMgr.Reset(RefIMgr.LfEdge);
      IMgr.ClipWidth := ClipWidth;
      IMgr.CurrentID := SaveID;
    end
  else
    MyCell.IMgr.ClipWidth := ClipWidth;
  for I := 0 to DrawList.Count - 1 do
  begin
    if RTL then
      if (DrawList[I] is TfrxHtBlockLI) then
        DrawList[I].OwnerIndent := Indent
      else if (Self is TfrxHtBlockLI) and (DrawList[I] is TfrxHtSection) then
        DrawList[I].OwnerIndent := OwnerIndent;
    DrawList[I].Draw1(Canvas, ARect, MyCell.IMgr, X, XRef, YRef);
  end;
end;

{$ifdef UseFormTree}
procedure TfrxHtBlock.FormTree(const Indent: ThtString; var Tree: ThtString);
var
  MyIndent: ThtString;
  TM, BM: ThtString;
begin
  MyIndent := Indent + '   ';
  TM := IntToStr(MargArray[MarginTop]);
  BM := IntToStr(MargArray[MarginBottom]);
  Tree := Tree + Indent + TagClass + '  ' + TM + '  ' + BM + CrChar + LfChar;
  MyCell.FormTree(MyIndent, Tree);
end;
{$endif UseFormTree}

//-- BG ---------------------------------------------------------- 24.08.2010 --
function TfrxHtBlock.GetBorderWidth: Integer;
begin
  Result := 3;
end;

{----------------TfrxHtTableAndCaptionBlock.Create}

constructor TfrxHtTableAndCaptionBlock.Create(
  Parent: TfrxHtCellBasic; Attributes: TfrxHtAttributeList; Prop: TfrxHTProperties; ATableBlock: TfrxHtTableBlock);
var
  I: Integer;
begin
  inherited Create(Parent, Attributes, Prop);
  TableBlock := ATableBlock;
  Justify := TableBlock.Justify;

  for I := 0 to Attributes.Count - 1 do
    with Attributes[I] do
      case Which of
        AlignSy:
          if htCompareText(Name, 'CENTER') = 0 then
            Justify := Centered
          else if htCompareText(Name, 'LEFT') = 0 then
          begin
            if FFloating = ANone then
              FFloating := ALeft;
          end
          else if htCompareText(Name, 'RIGHT') = 0 then
          begin
            if FFloating = ANone then
              FFloating := ARight;
          end;
      end;
  TableID := Attributes.TheID;

{CollapseMargins has already been called by TableBlock, copy the results here}
  MargArray[MarginTop] := TableBlock.MargArray[MarginTop];
  MargArray[MarginBottom] := TableBlock.MargArray[MarginBottom];

  TagClass := 'TableAndCaption.';
end;

{----------------TfrxHtTableAndCaptionBlock.CancelUsage}

procedure TfrxHtTableAndCaptionBlock.CancelUsage;
{called when it's found that this block isn't needed (no caption)}
begin
{assign the ID back to the Table}
  if TableID <> '' then
    Document.IDNameList.AddObject(TableID, TableBlock);
end;

{----------------TfrxHtTableAndCaptionBlock.CreateCopy}

constructor TfrxHtTableAndCaptionBlock.CreateCopy(OwnerCell: TfrxHtCellBasic; Source: TfrxHtmlNode);
var
  T: TfrxHtTableAndCaptionBlock absolute Source;
  Item: TObject;
  I1, I2: Integer;
begin
  inherited CreateCopy(OwnerCell,Source);
  TopCaption := T.TopCaption;
  TagClass := 'TableAndCaption.';
  I1 := Ord(TopCaption);
  I2 := Ord(not TopCaption);
  Item := MyCell.Items[I2];
  FCaptionBlock := (Item as TfrxHtBlock);
  Item := MyCell.Items[I1];
  TableBlock := (Item as TfrxHtTableBlock);
end;

procedure TfrxHtTableAndCaptionBlock.SetCaptionBlock(Value: TfrxHtBlock);
begin
  FCaptionBlock := Value;
  TableBlock.HasCaption := True;
end;

{----------------TfrxHtTableAndCaptionBlock.FindWidth}

function TfrxHtTableAndCaptionBlock.FindWidth(Canvas: TCanvas; AWidth, AHeight, AutoCount: Integer): Integer;
var
  Mx, Mn, FWidth: Integer;
begin
  HasBorderStyle := False; //bssNone; {has no border}
  MargArray[BorderLeftWidth] := 0;
  MargArray[BorderTopWidth] := 0;
  MargArray[BorderRightWidth] := 0;
  MargArray[BorderBottomWidth] := 0;
  MargArray[PaddingLeft] := 0;
  MargArray[PaddingTop] := 0;
  MargArray[PaddingRight] := 0;
  MargArray[PaddingBottom] := 0;
  MargArray[BackgroundColor] := clNone;

  TableBlock.FFloating := ANone;
  TableBlock.Table.Float := False;

  CaptionBlock.MinMaxWidth(Canvas, Mn, Mx, AWidth, AHeight);
  FWidth := TableBlock.FindWidth1(Canvas, AWidth, AHeight, MargArray[MarginLeft] + MargArray[MarginRight]);
  Result := Max(FWidth, Mn);
  if (Result < AWidth) and (MargArray[MarginLeft] = 0) and (MargArray[MarginRight] = 0) then
    case Justify of
      Centered:
        MargArray[MarginLeft] := (AWidth - Result) div 2;
      Right:
        MargArray[MarginLeft] := AWidth - Result;
    end;
  TableBlock.Justify := Centered;
end;

{----------------TfrxHtTableAndCaptionBlock.MinMaxWidth}

procedure TfrxHtTableAndCaptionBlock.MinMaxWidth(Canvas: TCanvas; out Min, Max: Integer; AvailableWidth, AvailableHeight: Integer);
var
  Mx, Mn, MxTable, MnTable: Integer;
begin
  TableBlock.MinMaxWidth(Canvas, MnTable, MxTable, AvailableWidth, AvailableHeight);
  FCaptionBlock.MinMaxWidth(Canvas, Mn, Mx, AvailableWidth, AvailableHeight);
  Min := Math.Max(MnTable, Mn);
  Max := Math.Max(MxTable, Mn);
end;

function TfrxHtTableAndCaptionBlock.FindDocPos(SourcePos: Integer; Prev: Boolean): Integer;
begin
  if not Prev then
  begin
    Result := FCaptionBlock.FindDocPos(SourcePos, Prev);
    if Result < 0 then
      Result := TableBlock.FindDocPos(SourcePos, Prev);
  end
  else {Prev, iterate backwards}
  begin
    Result := TableBlock.FindDocPos(SourcePos, Prev);
    if Result < 0 then
      Result := FCaptionBlock.FindDocPos(SourcePos, Prev);
  end;
end;

{----------------TfrxHtTableBlock.Create}

constructor TfrxHtTableBlock.Create(Parent: TfrxHtCellBasic; Attr: TfrxHtAttributeList; Prop: TfrxHTProperties; TableLevel: Integer);
var
  I, AutoCount: Integer;
  BorderWidth: Integer;
  Percent: Boolean;
  TheProps, MyProps: TfrxHTProperties;
begin
  Table := TfrxHtmlTable.Create(Self, Parent, Attr, Prop);

  // BG, 20.01.2013: translate table attributes to block property defaults:
  MyProps := TfrxHTProperties.CreateCopy(Prop);
  TheProps := MyProps;
  try
    if Table.BorderColor <> clNone then
      MyProps.SetPropertyDefaults([BorderBottomColor, BorderRightColor, BorderTopColor, BorderLeftColor], Table.BorderColor)
    else
    begin
      if Table.HasBorderWidthAttr then
        MyProps.SetPropertyDefaults([BorderBottomColor, BorderRightColor, BorderTopColor, BorderLeftColor], clGray)
      else
        MyProps.SetPropertyDefaults([BorderBottomColor, BorderRightColor, BorderTopColor, BorderLeftColor], clNone);
    end;

    MyProps.SetPropertyDefault(BorderSpacingHorz, Table.CellSpacingHorz);
    MyProps.SetPropertyDefault(BorderSpacingVert, Table.CellSpacingVert);

    if Table.HasBorderWidthAttr then
      MyProps.SetPropertyDefaults([BorderBottomWidth, BorderRightWidth, BorderTopWidth, BorderLeftWidth], Table.brdWidthAttr);

    case Table.Frame of
      tfBox, tfBorder:
        MyProps.SetPropertyDefaults([BorderBottomStyle, BorderRightStyle, BorderTopStyle, BorderLeftStyle], bssOutset);

      tfHSides:
      begin
        MyProps.SetPropertyDefaults([BorderTopStyle, BorderBottomStyle], bssSolid);
        MyProps.SetPropertyDefaults([BorderLeftStyle, BorderRightStyle], bssNone);
      end;

      tfVSides:
      begin
        MyProps.SetPropertyDefaults([BorderTopStyle, BorderBottomStyle], bssNone);
        MyProps.SetPropertyDefaults([BorderLeftStyle, BorderRightStyle], bssSolid);
      end;

      tfAbove:
      begin
        MyProps.SetPropertyDefault(BorderTopStyle, bssSolid);
        MyProps.SetPropertyDefaults([BorderBottomStyle, BorderRightStyle, BorderLeftStyle], bssNone);
      end;

      tfBelow:
      begin
        MyProps.SetPropertyDefault(BorderBottomStyle, bssSolid);
        MyProps.SetPropertyDefaults([BorderRightStyle, BorderTopStyle, BorderLeftStyle], bssNone);
      end;

      tfLhs:
      begin
        MyProps.SetPropertyDefault(BorderLeftStyle, bssSolid);
        MyProps.SetPropertyDefaults([BorderBottomStyle, BorderRightStyle, BorderTopStyle], bssNone);
      end;

      tfRhs:
      begin
        MyProps.SetPropertyDefault(BorderRightStyle, bssSolid);
        MyProps.SetPropertyDefaults([BorderBottomStyle, BorderTopStyle, BorderLeftStyle], bssNone);
      end;
    else
      if Table.HasBorderWidthAttr then
        if Table.brdWidthAttr > 0 then
          MyProps.SetPropertyDefaults([BorderBottomStyle, BorderRightStyle, BorderTopStyle, BorderLeftStyle], bssOutset)
        else
          MyProps.SetPropertyDefaults([BorderBottomStyle, BorderRightStyle, BorderTopStyle, BorderLeftStyle], bssNone);
    end;

    inherited Create(Parent, Attr, TheProps);
  finally
    MyProps.Free;
  end;

  Justify := NoJustify;
  RTL := Prop.Props[TextDirection] = 'rtl';

  for I := 0 to Attr.Count - 1 do
    with Attr[I] do
      case Which of

        AlignSy:
          if htCompareText(Name, 'CENTER') = 0 then
            Justify := Centered
          else if htCompareText(Name, 'LEFT') = 0 then
          begin
//TODO: BG, 14.07.2013: The table block is not floating, but justified!
            if FFloating = ANone then
              FFloating := ALeft;
//            Justify := Left;
          end
          else if htCompareText(Name, 'RIGHT') = 0 then
          begin
//TODO: BG, 14.07.2013: The table block is not floating, but justified!
            if FFloating = ANone then
              FFloating := ARight;
//            Justify := Right;
          end;

        BGColorSy:
          BkGnd := TryStrToColor(Name, False, BkColor);

        BackgroundSy:
          if not Assigned(BGImage) and (Length(Name) > 0) then
          begin
            BGImage := TfrxFrHtImageObj.SimpleCreate(MyCell, Name);
            PRec.X.PosType := bpDim;
            PRec.X.Value := 0;
            PRec.X.RepeatD := True;
            PRec.Y := PRec.X;
          end;

        HSpaceSy:
          HSpace := Min(40, Abs(Value));

        VSpaceSy:
          VSpace := Min(200, Abs(Value));

        WidthSy:
          if Pos('%', Name) > 0 then
          begin
            if (Value > 0) and (Value <= 100) then
              WidthAttr := Value * 10;
            AsPercent := True;
          end
          else
            WidthAttr := Value;

        HeightSy:
          if (VarType(MargArrayO[piHeight]) in VarInt) and (MargArrayO[piHeight] = IntNull) then
            MargArrayO[piHeight] := Name;
      end;

  if Table.BorderWidth > 0 then
    BorderWidth := Table.BorderWidth
  else
    BorderWidth := 3;

{need to see if width is defined in style}
  Percent := (VarIsStr(MargArrayO[piWidth])) and (Pos('%', MargArrayO[piWidth]) > 0);
  frxHTMLStyleUn.ConvMargArray(MargArrayO, 100, 0, EmSize, ExSize, BorderWidth, AutoCount, MargArray);
  if MargArray[piWidth] > 0 then
  begin
    if Percent then
    begin
      AsPercent := True;
      WidthAttr := Min(1000, MargArray[piWidth] * 10);
    end
    else
    begin
      WidthAttr := MargArray[piWidth];
    {By custom (not by specs), tables handle CSS Width property differently.  The
     Width includes the padding and border.}
      MargArray[piWidth] := WidthAttr - MargArray[BorderLeftWidth] - MargArray[BorderRightWidth]
        - MargArray[PaddingLeft] - MargArray[PaddingRight];
      MargArrayO[piWidth] := MargArray[piWidth];
      AsPercent := False;
    end;
  end;

  CollapseAdjoiningMargins;
  Table.Float := Floating in [ALeft, ARight];
  if Table.Float and (ZIndex = 0) then
    ZIndex := 1;
end;

{----------------TfrxHtTableBlock.CreateCopy}

constructor TfrxHtTableBlock.CreateCopy(OwnerCell: TfrxHtCellBasic; Source: TfrxHtmlNode);
var
  T: TfrxHtTableBlock absolute Source;
  Item: TObject;
begin
  inherited CreateCopy(OwnerCell,Source);
  System.Move(T.WidthAttr, WidthAttr, PtrSub(@TableBorder, @WidthAttr) + SizeOf(WidthAttr));
  Item := MyCell.Items[0];
  Table := Item as TfrxHtmlTable;
end;

{----------------TfrxHtTableBlock.MinMaxWidth}

procedure TfrxHtTableBlock.MinMaxWidth(Canvas: TCanvas; out Min, Max: Integer; AvailableWidth, AvailableHeight: Integer);
var
  TmpWidth: Integer;
begin
  if AsPercent then
    TmpWidth := 0
  else
    TmpWidth := Math.Max(0, WidthAttr - MargArray[BorderLeftWidth] - MargArray[BorderRightWidth]
      - MargArray[PaddingLeft] - MargArray[PaddingRight]);
  Table.tblWidthAttr := TmpWidth;
  inherited MinMaxWidth(Canvas, Min, Max, AvailableWidth, AvailableHeight);
  if TmpWidth > 0 then
  begin
    Min := Math.Max(Min, TmpWidth);
    Max := Min;
  end;
end;

{----------------TfrxHtTableBlock.FindWidth1}

function TfrxHtTableBlock.FindWidth1(Canvas: TCanvas; AWidth, AHeight, ExtMarg: Integer): Integer;
{called by TfrxHtTableAndCaptionBlock to assist in it's FindWidth Calculation.
 This method is called before TTableBlockFindWidth but is called only if there
 is a caption on the table.  AWidth is the full width available to the
 TfrxHtTableAndCaptionBlock.}
var
  PaddingAndBorder: Integer;
  Min, Max, Allow: Integer;
begin
  MargArray[MarginLeft] := 0;
  MargArray[MarginRight] := 0;
  MargArray[MarginTop] := 0;
  MargArray[MarginBottom] := 0;

  PaddingAndBorder :=
    MargArray[BorderLeftWidth] + MargArray[PaddingLeft] +
    MargArray[BorderRightWidth] + MargArray[PaddingRight];
  Table.tblWidthAttr := 0;
  if WidthAttr > 0 then
  begin
    if AsPercent then
      Result := Math.Min(MulDiv(AWidth, WidthAttr, 1000), AWidth - ExtMarg)
    else
      Result := WidthAttr;
    Result := Result - PaddingAndBorder;
    Table.tblWidthAttr := Result;
    Table.MinMaxWidth(Canvas, Min, Max, AWidth, AHeight);
    Result := Math.Max(Min, Result);
    Table.tblWidthAttr := Result;
  end
  else
  begin
    Table.MinMaxWidth(Canvas, Min, Max, AWidth, AHeight);
    Allow := AWidth - PaddingAndBorder;
    if Max <= Allow then
      Result := Max
    else if Min >= Allow then
      Result := Min
    else
      Result := Allow;
  end;
  Result := Result + PaddingAndBorder;
end;

//-- BG ---------------------------------------------------------- 24.08.2010 --
function TfrxHtTableBlock.GetBorderWidth: Integer;
begin
  Result := Table.BorderWidth;
  if Result = 0 then
    Result := 3;
end;

{----------------TfrxHtTableBlock.FindWidth}

function TfrxHtTableBlock.FindWidth(Canvas: TCanvas; AWidth, AHeight, AutoCount: Integer): Integer;
var
  LeftSide, RightSide: Integer;
  Min, Max, M, P: Integer;
begin
  if not HasCaption then
  begin
    inherited FindWidth(Canvas, AWidth, AHeight, AUtoCount);
//    if MargArray[MarginLeft] = Auto then
//      MargArray[MarginLeft] := 0;
//    if MargArray[MarginRight] = Auto then
//      MargArray[MarginRight] := 0;
//
//    if Floating in [ALeft, ARight] then
//    begin
//      if MargArray[MarginLeft] = 0 then
//        MargArray[MarginLeft] := HSpace;
//      if MargArray[MarginRight] = 0 then
//        MargArray[MarginRight] := HSpace;
//      if MargArray[MarginTop] = 0 then
//        MargArray[MarginTop] := VSpace;
//      if MargArray[MarginBottom] = 0 then
//        MargArray[MarginBottom] := VSpace;
//    end;
  end
  else
  begin
    MargArray[MarginLeft] := 0;
    MargArray[MarginRight] := 0;
  end;

  if BkGnd and (MargArray[BackgroundColor] = clNone) then
    MargArray[BackgroundColor] := BkColor;
  Table.FBkGnd := (MargArray[BackgroundColor] <> clNone) and not Assigned(BGImage);
  Table.FBkColor := MargArray[BackgroundColor]; {to be passed on to cells}

  LeftSide := MargArray[MarginLeft] + MargArray[PaddingLeft] + MargArray[BorderLeftWidth];
  RightSide := MargArray[MarginRight] + MargArray[PaddingRight] + MargArray[BorderRightWidth];

  if not HasCaption then
    Table.tblWidthAttr := 0;
  if WidthAttr > 0 then
  begin
    if not HasCaption then {already done if HasCaption}
    begin
      if AsPercent then
        Result := MulDiv(AWidth, WidthAttr, 1000) - LeftSide - RightSide
      else
        Result := WidthAttr - (MargArray[PaddingLeft] + MargArray[BorderLeftWidth] + MargArray[PaddingRight] + MargArray[BorderRightWidth]);
      Table.tblWidthAttr := Result;
      Table.MinMaxWidth(Canvas, Min, Max, AWidth, AHeight);
      Table.tblWidthAttr := Math.Max(Min, Math.Min(Max, Result));
    end;
    Result := Table.tblWidthAttr;
  end
  else
  begin
    Result := AWidth - LeftSide - RightSide;
    Table.MinMaxWidth(Canvas, Min, Max, AWidth, AHeight);
    P := Math.Min(Sum(Table.Percents), 1000);
    if P > 0 then
    begin
      P := MulDiv(Result, P, 1000);
      Min := Math.Max(Min, P);
      Max := Math.Max(Max, P);
    end;
    if Result > Max then
      Result := Max
    else if Result < Min then
      Result := Min;
  end;
  MargArray[piWidth] := Result;

  if (MargArray[MarginLeft] = 0) and (MargArray[MarginRight] = 0) and (Result + LeftSide + RightSide < AWidth) then
  begin
     M := AWidth - LeftSide - Result - RightSide;
    case Justify of
      Centered:
      begin
        MargArray[MarginLeft]  := M div 2;
        MargArray[MarginRight] := M - MargArray[MarginLeft];
      end;

      Right:
        MargArray[MarginLeft] := M;

      Left:
        MargArray[MarginRight] := M;

    end;
  end;
end;

function TfrxHtTableBlock.DrawLogic1(Canvas: TCanvas; X, Y, XRef, YRef, AWidth, AHeight, BlHt: Integer; IMgr: TfrxHtIndentManager;
  var MaxWidth, Curs: Integer; ARemainHeight: Integer): Integer;
var
  X1, Tmp: Integer;
begin
  if not (Floating in [ALeft, ARight]) then
  begin
    Tmp := X;
    X := Max(Tmp, IMgr.LeftIndent(Y));
    TableIndent := X - Tmp;
    X1 := Min(Tmp + AWidth, IMgr.RightSide(Y));
    AWidth := X1 - X;
  end;
  Result := inherited DrawLogic1(Canvas, X, Y, XRef, YRef, AWidth, AHeight, BlHt, IMgr, MaxWidth, Curs, ARemainHeight);
end;

function TfrxHtTableBlock.Draw1(Canvas: TCanvas; const ARect: TRect; IMgr: TfrxHtIndentManager; X, XRef, YRef: Integer): Integer;
begin
  X := X + TableIndent;
  Result := inherited Draw1(Canvas, ARect, IMgr, X, XRef, YRef);
end;

procedure TfrxHtTableBlock.AddSectionsToList;
begin {Sections in Table not added only table itself}
  Document.PositionList.Add(Table);
end;

constructor TfrxHtHRBlock.CreateCopy(OwnerCell: TfrxHtCellBasic; Source: TfrxHtmlNode);
var
  T: TfrxHtHRBlock absolute Source;
begin
  inherited CreateCopy(OwnerCell,Source);
  Align := T.Align;
end;

{----------------TfrxHtHRBlock.FindWidth}

function TfrxHtHRBlock.FindWidth(Canvas: TCanvas; AWidth, AHeight, AutoCount: Integer): Integer;
var
  LeftSide, RightSide, SWidth: Integer;
  Diff: Integer;
begin
  if Positioning = posAbsolute then
    Align := Left;
  LeftSide := MargArray[MarginLeft] + MargArray[PaddingLeft] + MargArray[BorderLeftWidth];
  RightSide := MargArray[MarginRight] + MargArray[PaddingRight] + MargArray[BorderRightWidth];
  SWidth := MargArray[piWidth];

  if SWidth > 0 then
    Result := Min(SWidth, AWidth - LeftSide - RightSide)
  else
    Result := Max(15, AWidth - LeftSide - RightSide);
  MargArray[piWidth] := Result;
{note that above could be inherited; if LeftSide and Rightside were fields
of TfrxHtBlock}

  if Align <> Left then
  begin
    Diff := AWidth - Result - LeftSide - RightSide;
    if Diff > 0 then
      case Align of
        Centered: Inc(MargArray[MarginLeft], Diff div 2);
        Right: Inc(MargArray[MarginLeft], Diff);
      end;
  end;
  if not IsCopy then
    TfrxHtHorzLine(MyHRule).VSize := MargArray[piHeight];
end;

{----------------TfrxHtBlockLI.Create}

constructor TfrxHtBlockLI.Create(Parent: TfrxHtCellBasic; Attributes: TfrxHtAttributeList; Prop: TfrxHTProperties;
  Sy: TElemSymb; APlain: Boolean; AIndexType: ThtChar; AListNumb, ListLevel: Integer);
var
  Tmp: ThtBulletStyle;
  S: ThtString;
begin
  inherited Create(Parent, Attributes, Prop);

  Tmp := Prop.GetListStyleType;
  if Tmp <> lbBlank then
    FListStyleType := Tmp;
  case Sy of

    UlSy, DirSy, MenuSy:
      begin
        FListType := Unordered;
        if APlain or (Display = pdInline) then
          FListStyleType := lbNone
        else
          if Tmp = lbBlank then
            case AIndexType of // type="disc|circle|square"
              'd': FListStyleType := lbDisc;
              'c': FListStyleType := lbCircle;
              's': FListStyleType := lbSquare;
            else
              case ListLevel mod 3 of
                1: FListStyleType := lbDisc;
                2: FListStyleType := lbCircle;
                0: FListStyleType := lbSquare;
              end;
            end;  
      end;

    OLSy:
      begin
        FListType := Ordered;
        if Tmp = lbBlank then
          case AIndexType of
            'a': FListStyleType := lbLowerAlpha;
            'A': FListStyleType := lbUpperAlpha;
            'i': FListStyleType := lbLowerRoman;
            'I': FListStyleType := lbUpperRoman;
          else
            FListStyleType := lbDecimal;
          end;
      end;

    DLSy:
      FListType := Definition;
  else
    FListType := liAlone;
    if Tmp = lbBlank then
      FListStyleType := lbDisc;
    if (VarType(MargArrayO[MarginLeft]) in varInt) and
      ((MargArrayO[MarginLeft] = IntNull) or (MargArrayO[MarginLeft] = 0)) then
      MargArrayO[MarginLeft] := 16;
  end;

  if (VarType(MargArrayO[MarginLeft]) in varInt) and (MargArrayO[MarginLeft] = IntNull) then
    case Sy of

      OLSy, ULSy, DirSy, MenuSy, DLSy:
        MargArrayO[MarginLeft] := 0;
        
    else
      MargArrayO[MarginLeft] := ListIndent;
    end;

  FListNumb := AListNumb;
  FListFont := TfrxHtFont.Create;
  FListFont.Assign(Prop.Font);

  S := Prop.GetListStyleImage;
  if S <> '' then
    Image := TfrxFrHtImageObj.SimpleCreate(MyCell, S);
end;

constructor TfrxHtBlockLI.CreateCopy(OwnerCell: TfrxHtCellBasic; Source: TfrxHtmlNode);
var
  T: TfrxHtBlockLI absolute Source;
begin
  inherited CreateCopy(OwnerCell,Source);
  FListType := T.FListType;
  FListNumb := T.FListNumb;
  FListStyleType := T.FListStyleType;
  if Assigned(T.Image) then
    Image := TfrxFrHtImageObj.CreateCopy(MyCell, T.Image);
  FListFont := TfrxHtFont.Create;
  FListFont.Assign(T.ListFont);
end;

destructor TfrxHtBlockLI.Destroy;
begin
  ListFont.Free;
  Image.Free;
  inherited Destroy;
end;

function TfrxHtBlockLI.DrawLogic1(Canvas: TCanvas; X, Y, XRef, YRef, AWidth, AHeight, BlHt: Integer; IMgr: TfrxHtIndentManager;
  var MaxWidth, Curs: Integer; ARemainHeight: Integer): Integer;
begin
  if Assigned(Image) then
  begin
    Image.DrawLogicInline(Canvas, nil, 100, 0);
    if Image.Image = ErrorImage then
      FreeAndNil(Image);
  end;
  Document.FirstLineHtPtr := @FirstLineHt;
  FirstLineHt := 0;
  try
    Result := inherited DrawLogic1(Canvas, X, Y, XRef, YRef, AWidth, AHeight, BlHt, IMgr, MaxWidth, Curs, ARemainHeight);
  finally
    Document.FirstLineHtPtr := nil;
  end;
end;

//-- BG ---------------------------------------------------------- 31.01.2012 --
procedure TfrxHtBlockLI.SetListFont(const Value: TFont);
begin
  FListFont.Assign(Value);
end;

{----------------TfrxHtBlockLI.Draw}

function TfrxHtBlockLI.Draw1(Canvas: TCanvas; const ARect: TRect; IMgr: TfrxHtIndentManager; X, XRef, YRef: Integer): Integer;

const
  MaxNumb = 26;
  LowerAlpha: string = 'abcdefghijklmnopqrstuvwxyz';
  HigherAlpha: string = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ';
  LowerRoman: array[1..MaxNumb] of string = ('i', 'ii', 'iii', 'iv', 'v', 'vi',
    'vii', 'viii', 'ix', 'x', 'xi', 'xii', 'xiii', 'xiv', 'xv', 'xvi', 'xvii',
    'xviii', 'xix', 'xx', 'xxi', 'xxii', 'xxiii', 'xxiv', 'xxv', 'xxvi');
  HigherRoman: array[1..MaxNumb] of string = ('I', 'II', 'III', 'IV', 'V', 'VI',
    'VII', 'VIII', 'IX', 'X', 'XI', 'XII', 'XIII', 'XIV', 'XV', 'XVI', 'XVII',
    'XVIII', 'XIX', 'XX', 'XXI', 'XXII', 'XXIII', 'XXIV', 'XXV', 'XXVI');
var
  NStr: string;
  BkMode, TAlign: Integer;
  PenColor, BrushColor: TColor;
  PenStyle: TPenStyle;
  BrushStyle: TBrushStyle;
  YB, AlphaNumb: Integer;
begin
  Result := inherited Draw1(Canvas, ARect, IMgr, X, XRef, YRef);

  X := IfInt(RTL, X + ContentWidth - 40, X + Indent);

  if FirstLineHt > 0 then
  begin
    YB := FirstLineHt - Document.YOff;
    if (YB < ARect.Top - 50) or (YB > ARect.Bottom + 50) then
      Exit;
    if Assigned(Image) and (Image.Image <> DefImage) and Document.ShowImages then
      Image.DoDraw(Canvas, X - 16, YB - Image.ObjHeight, Image.Image)
    else if not (ListType in [None, Definition]) then
    begin
      if ListStyleType in [lbDecimal, lbLowerAlpha, lbLowerRoman, lbUpperAlpha, lbUpperRoman] then
      begin
        AlphaNumb := Min(ListNumb, MaxNumb);
        case ListStyleType of
          lbLowerAlpha: NStr := LowerAlpha[AlphaNumb];
          lbUpperAlpha: NStr := HigherAlpha[AlphaNumb];
          lbLowerRoman: NStr := LowerRoman[AlphaNumb];
          lbUpperRoman: NStr := HigherRoman[AlphaNumb];
        else
          NStr := IntToStr(ListNumb);
        end;
        Canvas.Font := ListFont;
        Canvas.Font.Color := ThemedColor(ListFont.Color);
        NStr := NStr + '.';
        BkMode := SetBkMode(Canvas.Handle, Transparent);

        if RTL then
          TAlign := SetTextAlign(Canvas.Handle, TA_BASELINE or TA_RTLREADING)
        else
          TAlign := SetTextAlign(Canvas.Handle, TA_BASELINE);
        if RTL then
          Canvas.TextOut(X + 5, YB, NStr)
        else
          Canvas.TextOut(X - 5 - Canvas.TextWidth(NStr), YB, NStr);

        SetTextAlign(Canvas.Handle, TAlign);
        SetBkMode(Canvas.Handle, BkMode);
      end
      else if (ListStyleType in [lbCircle, lbDisc, lbSquare]) then
        with Canvas do
        begin
          PenColor := Pen.Color;
          PenStyle := Pen.Style;
          Pen.Color := ThemedColor(ListFont.Color);
          Pen.Style := psSolid;
          BrushStyle := Brush.Style;
          BrushColor := Brush.Color;
          Brush.Style := bsSolid;
          Brush.Color := ThemedColor(ListFont.Color);
          X := IfInt(RTL, X + 25, X);
          case ListStyleType of
            lbCircle:
              begin
                Brush.Style := bsClear;
                Circle(Canvas, X - 16, YB, 7);
              end;
            lbDisc:
              Circle(Canvas, X - 15, YB - 1, 5);
            lbSquare:
              Rectangle(X - 15, YB - 6, X - 10, YB - 1);
          end;
          Brush.Color := BrushColor;
          Brush.Style := BrushStyle;
          Pen.Color := PenColor;
          Pen.Style := PenStyle;
        end;
    end;
  end;
end;

{----------------TfrxHtBodyBlock.Create}

constructor TfrxHtBodyBlock.Create(Parent: TfrxHtCellBasic; Attributes: TfrxHtAttributeList; Prop: TfrxHTProperties);
var
  PRec: PtPositionRec;
  Image: ThtString;
  Val: TColor;
begin
  inherited Create(Parent,Attributes,Prop);
  FPositioning := PosStatic; {7.28}
  Prop.GetBackgroundPos(0, 0, PRec);
  if Prop.GetBackgroundImage(Image) and (Image <> '') then
    Document.SetBackgroundImage(Image, PRec);
  Val := Prop.GetBackgroundColor;
  if Val <> clNone then
    Document.SetBackGround(Val or PalRelative);
end;


{----------------TfrxHtBodyBlock.DrawLogic}

function TfrxHtBodyBlock.DrawLogic1(Canvas: TCanvas; X, Y, XRef, YRef, AWidth, AHeight, BlHt: Integer; IMgr: TfrxHtIndentManager;
  var MaxWidth, Curs: Integer; ARemainHeight: Integer): Integer;
var
  ScrollWidth: Integer;
  Lindex, RIndex, AutoCount: Integer;
  SaveID: TObject;
  ClientContentBot: Integer;
begin
  YDraw := Y;
  StartCurs := Curs;
  frxHTMLStyleUn.ConvMargArray(MargArrayO, AWidth, AHeight, EmSize, ExSize, BorderWidth, AutoCount, MargArray);
  if IsAuto(MargArray[MarginLeft]) then MargArray[MarginLeft] := 0;
  if IsAuto(MargArray[MarginRight]) then MargArray[MarginRight] := 0;
  ApplyBoxSettings(MargArray);

  X := MargArray[MarginLeft] + MargArray[PaddingLeft] + MargArray[BorderLeftWidth];
  ContentWidth := IMgr.Width - (X + MargArray[MarginRight] + MargArray[PaddingRight] + MargArray[BorderRightWidth]);

  DrawTop := MargArray[MarginTop];

  MyCell.IMgr := IMgr;

  SaveID := IMgr.CurrentID;
  Imgr.CurrentID := Self;
  LIndex := IMgr.SetLeftIndent(X, Y);
  RIndex := IMgr.SetRightIndent(X + ContentWidth, Y);

  ContentTop := Y + MargArray[MarginTop] + MargArray[PaddingTop] + MargArray[BorderTopWidth];
  MyCell.DoLogicX(Canvas, X, ContentTop, 0, 0, ContentWidth,
    AHeight - MargArray[MarginTop] - MargArray[MarginBottom], BlHt, ScrollWidth, Curs, ARemainHeight);

  Len := Curs - StartCurs;

  ClientContentBot := Max(ContentTop, MyCell.tcContentBot);
  ContentBot := ClientContentBot + MargArray[PaddingBottom] + MargArray[BorderBottomWidth] + MargArray[MarginBottom];
  DrawBot := Max(ClientContentBot, MyCell.tcDrawBot) + MargArray[PaddingBottom] + MargArray[BorderBottomWidth];

  MyCell.tcDrawTop := 0;
  MyCell.tcContentBot := 999000;

  Result := DrawBot + MargArray[MarginBottom] - Y;
  SectionHeight := Result;
  IMgr.FreeLeftIndentRec(LIndex);
  IMgr.FreeRightIndentRec(RIndex);
  DrawHeight := IMgr.ImageBottom - Y; {in case image overhangs}
  Imgr.CurrentID := SaveID;
  if DrawHeight < SectionHeight then
    DrawHeight := SectionHeight;
  MaxWidth := Max(IMgr.Width, Max(ScrollWidth, ContentWidth) + MargArray[MarginLeft] + MargArray[MarginRight]);
  if DrawList.Count = 0 then
    DrawSort;
end;

{----------------TfrxHtBodyBlock.Draw}

function TfrxHtBodyBlock.Draw1(Canvas: TCanvas; const ARect: TRect; IMgr: TfrxHtIndentManager; X, XRef, YRef: Integer): Integer;
var
  SaveID: TObject;
  Y: Integer;
begin
  Y := YDraw;
  Result := Y + SectionHeight;

  X := IMgr.LfEdge + MargArray[MarginLeft] + MargArray[BorderLeftWidth] + MargArray[PaddingLeft];
  SaveID := IMgr.CurrentID;
  Imgr.CurrentID := Self;
  DrawTheList(Canvas, ARect, ContentWidth, X, IMgr.LfEdge, 0);
  Imgr.CurrentID := SaveID;
end;

{ TfrxHtDocument }

//-- BG ---------------------------------------------------------- 04.03.2011 --
// moving from TfrxHtIDObjectList to TfrxHtDocument removed field OwnerList from TfrxHtIDObjectList
function TfrxHtDocument.AddChPosObjectToIDNameList(const S: ThtString; Pos: Integer): Integer;
begin
  Result := IDNameList.AddObject(S, TfrxHtChPosObj.Create(Self, Pos));
end;

constructor TfrxHtDocument.Create(Owner: TfrxHtmlViewerBase);
begin
  FDocument := Self;
  inherited Create(nil);
  FPropStack := TfrxHtmlPropStack.Create;
  TheOwner := Owner;
  IDNameList := TfrxHtIDObjectList.Create; //(Self);
  MissingImages := ThtStringList.Create;
  MissingImages.Sorted := False;
  LinkList := TfrxHtLinkList.Create;
  Styles := TfrxHtmlStyleList.Create(Self);
  DrawList := TfrxHtDrawList.Create;
  PositionList := TfrxHtSectionBaseList.Create(False);
  InLineList := TInlineList.Create(Self);
  ScaleX := 1.0;
  ScaleY := 1.0;
end;

//------------------------------------------------------------------------------
constructor TfrxHtDocument.CreateCopy(T: TfrxHtDocument);
begin
  PrintTableBackground := T.PrintTableBackground;
  PrintBackground := T.PrintBackground;
  ImageCache := T.ImageCache; {same list}
  InlineList := T.InlineList; {same list}
  IsCopy := True;
  System.Move(T.ShowImages, ShowImages, PtrSub(@Background, @ShowImages) + Sizeof(Integer));
  PreFontName := T.PreFontName;
  MissingImages := ThtStringList.Create;
  DrawList := TfrxHtDrawList.Create;
  FDocument := Self;
  inherited CreateCopy(nil, T);
  ScaleX := 1.0;
  ScaleY := 1.0;
end;

destructor TfrxHtDocument.Destroy;
begin
  inherited Destroy; // Yunqa.de: Destroy calls Clear, so do this first.
  IDNameList.Free;
  MissingImages.Free;
  LinkList.Free;
  Styles.Free;
  DrawList.Free;
  PositionList.Free;
  if not IsCopy then
    InlineList.Free;
  FPropStack.Free;
end;

//-- BG ---------------------------------------------------------- 14.02.2016 --
function TfrxHtDocument.GetViewPort: TRect;
begin
  Result := Bounds(0, 0, TheOwner.Width, TheOwner.Height);
end;

procedure TfrxHtDocument.SetYOffset(Y: Integer);
begin
  YOff := Y;
  YOffChange := True;
end;

procedure TfrxHtDocument.Clear;
begin
  if not IsCopy then
  begin
    IDNameList.Clear;
    PositionList.Clear;
    TInlineList(InlineList).Clear;
  end;
  BackgroundImage := nil;
  BackgroundImageName := '';
  BackgroundImageLoaded := False;
  MissingImages.Clear;
  if Assigned(LinkList) then
    LinkList.Clear;
  if not IsCopy then
    Styles.Clear;
  inherited Clear;
end;

//------------------------------------------------------------------------------
function TfrxHtDocument.DoLogic(Canvas: TCanvas; Y: Integer; Width, AHeight, BlHt: Integer;
  var ScrollWidth, Curs: Integer; ARemainHeight: Integer): Integer;
begin
  Inc(CycleNumber);
  TableNestLevel := 0;
  InLogic2 := False;
  SetTextJustification(Canvas.Handle, 0, 0);
  TInlineList(InlineList).NeedsConverting := True;

  Result := inherited DoLogic(Canvas, Y, Width, AHeight, BlHt, ScrollWidth, Curs, ARemainHeight);

  if not IsCopy and (PositionList.Count = 0) then
  begin
    AddSectionsToList;
  end;
end;

//-- BG ---------------------------------------------------------- 11.09.2010 --
procedure TfrxHtDocument.AddSectionsToPositionList(Sections: TfrxHtSectionBase);
begin
  inherited;
  PositionList.Add(Sections);
end;

//------------------------------------------------------------------------------
function TfrxHtDocument.Draw(Canvas: TCanvas; ARect: TRect; ClipWidth, X: Integer;
  Y, XRef, YRef: Integer): Integer;
var
  OldPal: HPalette;
begin
  PageBottom := ARect.Bottom + YOff;
  PageShortened := False;
  FirstPageItem := True;
  TableNestLevel := 0;
  SkipDraw := False;

  if (ColorBits <= 8) then
  begin
    OldPal := SelectPalette(Canvas.Handle, ThePalette, True);
    RealizePalette(Canvas.Handle);
  end
  else
    OldPal := 0;
  DrawList.Clear;
  try
    Result := inherited Draw(Canvas, ARect, ClipWidth, X, Y, XRef, YRef);
    if DrawList.Count > 0 then
    begin
      DrawList.DrawImages;
      DrawList.Clear;
      Printed := True;
    end;
  finally
    if OldPal <> 0 then
      SelectPalette(Canvas.Handle, OldPal, True);
  end;
  if YOffChange or XOffChange then
  begin
    XOffChange := False;
    YOffChange := False;
  end;
end;

procedure TfrxHtDocument.SetFonts(const Name, PreName: ThtString; ASize: Double;
  AColor, AHotSpot, AVisitedColor, AActiveColor, ABackground: TColor; LnksActive, LinkUnderLine: Boolean;
  ACodePage: TBuffCodePage; ACharSet: TFontCharSet; MarginHeight, MarginWidth: Integer);
begin
  Styles.Initialize(Name, PreName, ASize, AColor, AHotspot, AVisitedColor,
    AActiveColor, LinkUnderLine, ACodePage, ACharSet, MarginHeight, MarginWidth);
  InitializeFontSizes(ASize);
  PreFontName := htStringToString(PreName);
  HotSpotColor := AHotSpot;
  LinkVisitedColor := AVisitedColor;
  LinkActiveColor := AActiveColor;
  LinksActive := LnksActive;
  SetBackground(ABackground);
end;

procedure TfrxHtDocument.SetBackground(ABackground: TColor);
begin
  Background := ABackground;
  if Assigned(OnBackGroundChange) then
    OnBackgroundChange(Self);
end;

procedure TfrxHtDocument.SetBackgroundImage(const Name: ThtString; const APrec: PtPositionRec);
begin
  BackgroundImage := nil;
  BackgroundImageName := Name;
  BackgroundImageLoaded := False;
  BackgroundImagePosition := APrec;
end;

//------------------------------------------------------------------------------
function TfrxHtDocument.GetTheImage(const BMName: ThtString; var Transparent: ThtImageTransparency; out FromCache, Delay: Boolean): TfrxHtImage;
{Transparent may be set to itrNone or itrLLCorner on entry but may discover it's itrIntrinsic here}

  procedure GetTheStream;
  {Note: streams gotten by OnImageRequest are on "loan".  We do not destroy them}
  var
    Stream: TStream;
  begin
    if Assigned(GetImage) then
    begin {the OnImageRequest}
      Stream := nil;
      GetImage(TheOwner, BMName, Stream);
      if Stream = WaitStream then
        Delay := True
      else if Stream = ErrorStream then
        Result := nil
      else if Stream <> nil then
      begin
        try
          Result := LoadImageFromStream(Stream, Transparent);
        finally
          if Assigned(GottenImage) then
            GottenImage(TheOwner, BMName, Stream);
        end;
      end;
    end;
  end;

  procedure GetTheBase64(Name: ThtString);
  var
    I: Integer;
    Stream: TStream;
    S: String;
    AnsiS: AnsiString;
  begin
    I := Pos(';base64,', Name);
    if I >= 11 then
    begin
      // Firefox 11 saves multiline inline images by writing %0A for the linefeeds.
      // BTW: Internet Explorer 9 shows but does not save inline images at all.
      // Using StringReplace() here is a quick and dirty hack.
      // Better decode %encoded attribute values while reading the attributes.
      S := StringReplace(htStringToString(Name), '%0A', #$0A, [rfReplaceAll]);
      AnsiS := AnsiString(Trim(Copy(S, I + 8, MaxInt)));
      Stream := TMemoryStream.Create;
      try
        AnsiS := Base64Decode(AnsiS);
        Stream.Write(AnsiS[1], Length(AnsiS));
        Stream.Position := 0;
        Result := LoadImageFromStream(Stream, Transparent);
        Result.IsInternal := True;
      finally
        Stream.Free;
      end;
    end;
  end;

  procedure GetTheFile(Name: ThtString);
  var
    Stream: TStream;
    Scheme, Specific, ResType: ThtString;
  begin
    Name := TheOwner.HtmlExpandFilename(Name);
    SplitScheme(Name, Scheme, Specific);
    if Scheme = 'res' then
    begin
      Specific := HTMLToRes(Name, ResType);
      Stream := TResourceStream.Create(HInstance, htStringToString(Specific), PChar({$ifdef LCL}string(ResType){$else}String(ResType){$endif}) );
    end
    else if FileExists(Name) then
      Stream := TFileStream.Create( htStringToString(Name), fmOpenRead or fmShareDenyWrite)
    else
      Stream := nil;

    if Stream <> nil then
      try
        Result := LoadImageFromStream(Stream, Transparent);
      finally
        Stream.Free;
      end;
  end;

  procedure GetTheURL(Name: String);
  var
    Stream: TStream;
    Scheme, Specific: ThtString;
    LProtocol: TfrxCustomDatalinkProtocolClass;
  begin
    if Length(Name) = 0 then Exit;
    Name := TheOwner.HtmlExpandFilename(Name);
    SplitScheme(Name, Scheme, Specific);
    LProtocol := frxDataProtocols.GetProtocol(Name);
    if not Assigned(LProtocol) then Exit;
    Stream := TMemoryStream.Create;
    try
      if LProtocol.LoadBy(Name, Stream) then
      begin
        Stream.Position := 0;
        Result := LoadImageFromStream(Stream, Transparent);
      end;
    except
      Result := nil;
    end;
    Stream.Free;
  end;

var
  UName, Name: ThtString;
  I: Integer;
  bIsEmbedded: Boolean;
begin
  Result := nil;
  Delay := False;
  FromCache := False;
  if BMName <> '' then
  begin
    Name := htTrim(BMName);
    //UName := htUpperCase(Name);
    UName := Name;
    bIsEmbedded := (Copy(Name, 1, 11) = 'data:image/');
    if bIsEmbedded then
      UName := String(MD5String(AnsiString(UName)));
    if ImageCache.IsFound(UName, I) then // handle the case where the image is already loaded
    begin
      Result := ImageCache.GetImage(I);
      FromCache := True;
    end
    else
    begin
      {The image is not loaded yet, need to get it}
      if bIsEmbedded then
        GetTheBase64(Name)
      else
      begin
        if (Result = nil) and not Delay then
          GetTheStream;
        if (Result = nil) and not Delay then
          GetTheFile(BMName);
        if (Result = nil) and not Delay then
          GetTheURL(BMName);
      end;

      if Result <> nil then {put in Image List for use later also}
        ImageCache.AddObject(UName, Result); {put new image in list}
    end;
  end;
  if Assigned(Result) then
    Result.AddRef;
end;

//------------------------------------------------------------------------------
function TfrxHtDocument.FindSectionAtPosition(Pos: Integer; out TopPos, Index: Integer): TfrxHtSectionBase;
var
  I: Integer;
begin
  for I := PositionList.Count - 1 downto 0 do
  begin
    Result := PositionList[I];
    if Result.YPosition <= Pos then
    begin
      TopPos := Result.YPosition;
      Index := I;
      Exit;
    end;
  end;
  Result := nil;
end;

procedure TfrxHtDocument.GetBackgroundImage;
var
  Dummy1: ThtImageTransparency;
  FromCache, Delay: Boolean;
  Rslt: ThtString;
  I: Integer;
  UName: ThtString;
begin
  UName := htTrim(BackgroundImageName);
  if ShowImages and (UName <> '') then
    if BackgroundImage = nil then
    begin
      if BackgroundImageLoaded then
      begin
        I := ImageCache.IndexOf(UName); {first see if the bitmap is already loaded}
        if I >= 0 then
          BackgroundImage := ImageCache.GetImage(I);
      end
      else
      begin
        Dummy1 := itrNone;
        if not Assigned(GetImage) then
          BackgroundImageName := TheOwner.HtmlExpandFilename(BackgroundImageName)
        else if Assigned(OnExpandName) then
        begin
          OnExpandName(TheOwner, BackgroundImageName, Rslt);
          BackgroundImageName := Rslt;
        end;
        BackgroundImage := GetTheImage(BackgroundImageName, Dummy1, FromCache, Delay); {might be Nil}
        if Delay then
          MissingImages.AddObject(htTrim(BackgroundImageName), Self);
        BackgroundImageLoaded := True;
      end;
    end;
end;

//-- BG ---------------------------------------------------------- 14.02.2016 --
procedure TfrxHtDocument.SetPageArea(const Value: TRect);
begin
  FPageArea := Value;
end;

//-- BG ---------------------------------------------------------- 16.07.2017 --
procedure TfrxHtDocument.SetPrinted(const Value: Boolean);
begin
  FPrinted := Value;
end;

//------------------------------------------------------------------------------
function TfrxHtDocument.FindDocPos(SourcePos: Integer; Prev: Boolean): Integer;
begin
  Result := inherited FindDocPos(SourcePos, Prev);
  if Result < 0 then {if not found return 1 past last ThtChar}
    Result := Len;
end;

procedure TfrxHtDocument.ProcessInlines(SIndex: Integer; Prop: TfrxHTProperties; Start: Boolean);
{called when an inline property is found to specify a border}
var
  I, EmSize, ExSize: Integer;
  Result: ThtInlineRec;
  MargArrayO: ThtVMarginArray;
  Dummy1: Integer;
begin
  with TInlineList(InlineList) do
  begin
    if Start then
    begin {this is for border start}
      Result := ThtInlineRec.Create;
      Add(Result);
      with Result do
      begin
        StartBDoc := SIndex; {Source index for border start}
        IDB := Prop.ID; {property ID}
        EndB := 999999; {end isn't known yet}
        Prop.GetVMarginArray(MargArrayO);
        EmSize := Prop.EmSize;
        ExSize := Prop.ExSize;
        ConvMargArray(MargArrayO, 200, 200, EmSize, ExSize, 0{4}, Dummy1, MargArray);
      end;
    end
    else {this call has end information}
      for I := Count - 1 downto 0 do {the record we want is probably the last one}
      begin
        Result := Items[I];
        if Prop.ID = Result.IDB then {check the ID to make sure}
        begin
          Result.EndBDoc := SIndex; {the source position of the border end}
          Break;
        end;
      end;
  end;
end;

{----------------TInlineList.Create}

constructor TInlineList.Create(AnOwner: TfrxHtDocument);
begin
  inherited Create;
  Owner := AnOwner;
  NeedsConverting := True;
end;

procedure TInlineList.Clear;
begin
  inherited Clear;
  NeedsConverting := True;
end;

procedure TInlineList.AdjustValues;
{convert all the list data from source ThtChar positions to display ThtChar positions}
var
  I: Integer;
begin
  for I := 0 to Count - 1 do
    with Items[I] do
    begin
      StartB := Owner.FindDocPos(StartBDoc, False);
      EndB := Owner.FindDocPos(EndBDoc, False);
      if StartB = EndB then
        Dec(StartB); {this takes care of images, form controls}
    end;
  NeedsConverting := False;
end;

function TInlineList.GetStartB(I: Integer): Integer;
begin
  if NeedsConverting then
    AdjustValues;
  if (I < Count) and (I >= 0) then
    Result := Items[I].StartB
  else
    Result := 99999999;
end;

//-- BG ---------------------------------------------------------- 06.10.2016 --
function TInlineList.Get(Index: Integer): ThtInlineRec;
begin
  Result := inherited Get(Index);
end;

function TInlineList.GetEndB(I: Integer): Integer;
begin
  if NeedsConverting then
    AdjustValues;
  if (I < Count) and (I >= 0) then
    Result := Items[I].EndB
  else
    Result := 99999999;
end;

{ TfrxHtfrcHtCellObjBase }

//-- BG ---------------------------------------------------------- 19.02.2013 --
procedure TfrxHtfrcHtCellObjBase.AssignTo(Destin: TfrxHtfrcHtCellObjBase);
begin
  Move(FColSpan, Destin.FColSpan, PtrSub(@FSpecHt, @FColSpan) + sizeof(FSpecHt) );
end;

{ TfrxHtDummyCellObj }

//-- BG ---------------------------------------------------------- 19.02.2013 --
function TfrxHtDummyCellObj.Clone(Parent: TfrxHtBlock): TfrxHtfrcHtCellObjBase;
begin
  Result := TfrxHtDummyCellObj.Create(RowSpan);
  AssignTo(Result);
end;

//-- BG ---------------------------------------------------------- 19.02.2013 --
constructor TfrxHtDummyCellObj.Create(RSpan: Integer);
begin
  inherited Create;
  FColSpan := 0;
  FRowSpan := RSpan;
end;

//-- BG ---------------------------------------------------------- 19.02.2013 --
procedure TfrxHtDummyCellObj.Draw(Canvas: TCanvas; const ARect: TRect; X, Y, CellSpacingHorz, CellSpacingVert: Integer; Border: Boolean; Light,
  Dark: TColor);
begin
  //abstract: inherited Draw(Canvas,ARect,X,Y,CellSpacing,Border,Light,Dark);
end;

//-- BG ---------------------------------------------------------- 19.02.2013 --
procedure TfrxHtDummyCellObj.DrawLogic2(Canvas: TCanvas; Y, CellSpacingHorz,CellSpacingVert: Integer; var Curs: Integer);
begin
  //abstract: inherited DrawLogic2(Canvas,Y,CellSpacing,Curs);
end;

//-- BG ---------------------------------------------------------- 19.02.2013 --
function TfrxHtDummyCellObj.GetCell: TfrxHtCellObjCell;
begin
  Result := nil;
end;

{ TfrxHtCellObj }

//-- BG ---------------------------------------------------------- 19.02.2013 --
procedure TfrxHtCellObj.AssignTo(Destin: TfrxHtfrcHtCellObjBase);
var
  CellObj: TfrxHtCellObj absolute Destin;
begin
  inherited AssignTo(Destin);
  if Destin is TfrxHtCellObj then
  begin
    Move(FWd, CellObj.FWd, PtrSub(@FCell, @FWd));

    if CellObj.Cell.Document.PrintTableBackground then
    begin
      CellObj.Cell.BkGnd := Cell.BkGnd;
      CellObj.Cell.BkColor := Cell.BkColor;
      if Assigned(BGImage) then
        // TODO: BG, 26.08.2013: is this correct?
        CellObj.BGImage := TfrxFrHtImageObj.CreateCopy(CellObj.Cell, BGImage);
    end
    else
      CellObj.Cell.BkGnd := False;
    CellObj.MargArrayO := MargArrayO;
    CellObj.MargArray := MargArray;
  end;
end;

//-- BG ---------------------------------------------------------- 19.02.2013 --
function TfrxHtCellObj.Clone(Parent: TfrxHtBlock): TfrxHtfrcHtCellObjBase;
begin
  Result := TfrxHtCellObj.CreateCopy(Parent, Self);
end;

constructor TfrxHtCellObj.Create(Parent: TfrxHtTableBlock; AVAlign: ThtAlignmentStyle; Attr: TfrxHtAttributeList; Prop: TfrxHTProperties);
{Note: on entry Attr and Prop may be Nil when dummy cells are being created}
var
  I, AutoCount: Integer;
  Color: TColor;
  BackgroundImage: ThtString;
  Algn: ThtAlignmentStyle;
begin
  inherited Create;
  FCell := TfrxHtCellObjCell.Create(Parent);
  if Assigned(Prop) then
    Cell.Title := Prop.PropTitle;
  ColSpan := 1;
  RowSpan := 1;
  VAlign := AVAlign;
  if Assigned(Attr) then
    for I := 0 to Attr.Count - 1 do
      with Attr[I] do
        case Which of
          ColSpanSy:
            if Value > 1 then
              ColSpan := Value;

          RowSpanSy:
            if Value > 1 then
              RowSpan := Value;

          WidthSy:
            if Value >= 0 then
              FSpecWd := ToSpecWidth(Value, Name);

          HeightSy:
            if Value >= 0 then
              FSpecHt := ToSpecWidth(Value, Name);

          BGColorSy:
            Cell.BkGnd := TryStrToColor(Name, False, Cell.BkColor);

          BackgroundSy:
            BackgroundImage := Name;

          HRefSy:
            Cell.Url := Name;

          TargetSy:
            Cell.Target := Name;

        end;

  if Assigned(Prop) then
  begin {Caption does not have Prop}
    if Prop.GetVertAlign(Algn) and (Algn in [Atop, AMiddle, ABottom]) then
      Valign := Algn;
    Prop.GetVMarginArray(MargArrayO);
    EmSize := Prop.EmSize;
    ExSize := Prop.ExSize;
    ConvMargArray(MargArrayO, 100, 0, EmSize, ExSize, 0, AutoCount, MargArray);
    if VarIsStr(MargArrayO[piWidth]) and (MargArray[piWidth] >= 0) then
      FSpecWd := ToSpecWidth(MargArray[piWidth], MargArrayO[piWidth]);
    if VarIsStr(MargArrayO[piHeight]) and (MargArray[piHeight] >= 0) then
      FSpecHt := ToSpecWidth(MargArray[piHeight], MargArrayO[piHeight]);

    Color := Prop.GetBackgroundColor;
    if Color <> clNone then
    begin
      Cell.BkGnd := True;
      Cell.BkColor := Color;
    end;
    Prop.GetBackgroundImage(BackgroundImage); {'none' will change ThtString to empty}
    if BackgroundImage <> '' then
    begin
      BGImage := TfrxFrHtImageObj.SimpleCreate(Cell, BackgroundImage);
      Prop.GetBackgroundPos(EmSize, ExSize, FPRec);
    end;

  {In the following, Padding widths in percent aren't accepted}
    ConvMargArrayForCellPadding(MargArrayO, EmSize, ExSize, MargArray);
    FPad.Top := MargArray[PaddingTop];
    FPad.Right := MargArray[PaddingRight];
    FPad.Bottom := MargArray[PaddingBottom];
    FPad.Left := MargArray[PaddingLeft];

    HasBorderStyle := False;
    if ThtBorderStyle(MargArray[BorderTopStyle]) <> bssNone then
    begin
      HasBorderStyle := True;
      FBrd.Top := MargArray[BorderTopWidth];
    end;
    if ThtBorderStyle(MargArray[BorderRightStyle]) <> bssNone then
    begin
      HasBorderStyle := True;
      FBrd.Right := MargArray[BorderRightWidth];
    end;
    if ThtBorderStyle(MargArray[BorderBottomStyle]) <> bssNone then
    begin
      HasBorderStyle := True;
      FBrd.Bottom := MargArray[BorderBottomWidth];
    end;
    if ThtBorderStyle(MargArray[BorderLeftStyle]) <> bssNone then
    begin
      HasBorderStyle := True;
      FBrd.Left := MargArray[BorderLeftWidth];
    end;

    Prop.GetPageBreaks(BreakBefore, BreakAfter, KeepIntact);
    ShowEmptyCells := Prop.ShowEmptyCells;
    HideOverflow := Prop.IsOverflowHidden;
  end;
end;

constructor TfrxHtCellObj.CreateCopy(Parent: TfrxHtBlock; T: TfrxHtCellObj);
begin
  inherited Create;
  FCell := TfrxHtCellObjCell.CreateCopy(Parent, T.Cell);
  T.AssignTo(Self);
end;

destructor TfrxHtCellObj.Destroy;
begin
  Cell.Free;
  BGImage.Free;
  TiledImage.Free;
  inherited Destroy;
end;

//-- BG ---------------------------------------------------------- 08.01.2012 --
function TfrxHtCellObj.GetBorderBottom: Integer;
begin
  Result := FBrd.Bottom;
end;

//-- BG ---------------------------------------------------------- 08.01.2012 --
function TfrxHtCellObj.GetBorderLeft: Integer;
begin
  Result := FBrd.Left;
end;

//-- BG ---------------------------------------------------------- 08.01.2012 --
function TfrxHtCellObj.GetBorderRight: Integer;
begin
  Result := FBrd.Right;
end;

//-- BG ---------------------------------------------------------- 08.01.2012 --
function TfrxHtCellObj.GetBorderTop: Integer;
begin
  Result := FBrd.Top;
end;

//-- BG ---------------------------------------------------------- 19.02.2013 --
function TfrxHtCellObj.GetCell: TfrxHtCellObjCell;
begin
  Result := FCell;
end;

//-- BG ---------------------------------------------------------- 08.01.2012 --
function TfrxHtCellObj.GetPaddingBottom: Integer;
begin
  Result := FPad.Bottom;
end;

//-- BG ---------------------------------------------------------- 08.01.2012 --
function TfrxHtCellObj.GetPaddingLeft: Integer;
begin
  Result := FPad.Left;
end;

//-- BG ---------------------------------------------------------- 08.01.2012 --
function TfrxHtCellObj.GetPaddingRight: Integer;
begin
  Result := FPad.Right;
end;

//-- BG ---------------------------------------------------------- 08.01.2012 --
function TfrxHtCellObj.GetPaddingTop: Integer;
begin
  Result := FPad.Top;
end;

{----------------TfrxHtCellObj.InitializeCell}

procedure TfrxHtCellObj.Initialize(TablePadding: Integer; const BkImageName: ThtString;
  const APRec: PtPositionRec; Border: Boolean);
begin
  if FPad.Top < 0 then
    FPad.Top := TablePadding;
  if FPad.Right < 0 then
    FPad.Right := TablePadding;
  if FPad.Bottom < 0 then
    FPad.Bottom := TablePadding;
  if FPad.Left < 0 then
    FPad.Left := TablePadding;
  if Border and not HasBorderStyle then // (BorderStyle = bssNone) then
  begin
    FBrd.Left := Max(1, FBrd.Left);
    FBrd.Right := Max(1, FBrd.Right);
    FBrd.Top := Max(1, FBrd.Top);
    FBrd.Bottom := Max(1, FBrd.Bottom);
  end;
  HzSpace := FPad.Left + FBrd.Left + FBrd.Right + FPad.Right;
  VrSpace := FPad.Top + FBrd.Top + FBrd.Bottom + FPad.Bottom;

  if (BkImageName <> '') and not Assigned(BGImage) then
  begin
    BGImage := TfrxFrHtImageObj.SimpleCreate(Cell, BkImageName);
    PRec := APrec;
  end;
end;

{----------------TfrxHtCellObj.DrawLogic2}

procedure TfrxHtCellObj.DrawLogic2(Canvas: TCanvas; Y, CellSpacingHorz, CellSpacingVert: Integer; var Curs: Integer);
var
  Dummy: Integer;
  Tmp: Integer;
begin
  if Cell.Count > 0 then
  begin
    Tmp := Ht - VSize - (VrSpace + CellSpacingVert);
    case VAlign of
      ATop: YIndent := 0;
      AMiddle: YIndent := Tmp div 2;
      ABottom, ABaseline: YIndent := Tmp;
    end;
    Dummy := 0;
    Cell.DoLogic(Canvas, Y + FPad.Top + FBrd.Top + CellSpacingVert + YIndent, Wd - (HzSpace + CellSpacingHorz),
      Ht - VrSpace - CellSpacingVert, 0, Dummy, Curs);
  end;
  if Assigned(BGImage) and Cell.Document.ShowImages then
  begin
    BGImage.DrawLogicInline(Canvas, nil, 100, 0);
    if BGImage.Image = ErrorImage then
      FreeAndNil(BGImage)
    else
    begin
      BGImage.ClientSizeKnown := True; {won't need reformat on InsertImage}
      NeedDoImageStuff := True;
    end;
  end;
end;

{----------------TfrxHtCellObj.Draw}

procedure TfrxHtCellObj.Draw(Canvas: TCanvas; const ARect: TRect; X, Y, CellSpacingHorz, CellSpacingVert: Integer;
  Border: Boolean; Light, Dark: TColor);
var
  YO: Integer;
  BL, BT, BR, BB, PL, PT, PR, PB: Integer;
  ImgOK: Boolean;
  IT, IH, FT, Rslt: Integer;
  Rgn, SaveRgn: HRgn;
  Point: TPoint;
  BRect: TRect;
  IsVisible: Boolean;
begin
  YO := Y - Cell.Document.YOff;

  BL := X + CellSpacingHorz; {Border left and right}
  BR := X + Wd;
  PL := BL + FBrd.Left; {Padding left and right}
  PR := BR - FBrd.Right;

  BT := YO + CellSpacingVert; {Border Top and Bottom}
  BB := YO + Ht;
  PT := BT + FBrd.Top; {Padding Top and Bottom}
  PB := BB - FBrd.Bottom;

  IT := Max(0, ARect.Top - 2 - PT);
  FT := Max(PT, ARect.Top - 2); {top of area drawn, screen coordinates}
  IH := Min(PB - FT, ARect.Bottom - FT); {height of area actually drawn}

  Cell.MyRect := Rect(BL, BT, BR, BB);
  if not (BT <= ARect.Bottom) and (BB >= ARect.Top) then
    Exit;

  try
    if NeedDoImageStuff then
    begin
      if BGImage = nil then
        NeedDoImageStuff := False
      else if BGImage.Image <> DefImage then
      begin
        if BGImage.Image = ErrorImage then {Skip the background image}
          FreeAndNil(BGImage)
        else
        try
          BGImage.Image.TileImage( PRec, Wd - CellSpacingHorz, Ht - CellSpacingVert, TiledImage, NoMask);
          if Cell.IsCopy then
            TiledImage.Bitmap.HandleType := bmDIB;
        except {bad image, get rid of it}
          FreeAndNil(BGImage);
          FreeAndNil(TiledImage);
        end;
        NeedDoImageStuff := False;
      end;
    end;

    ImgOK := not NeedDoImageStuff and Assigned(BGImage) and (BGImage.Image <> DefImage) and Cell.Document.ShowImages;

    if Cell.BkGnd then
    begin
      Canvas.Brush.Color := ThemedColor(Cell.BkColor) or PalRelative;
      Canvas.Brush.Style := bsSolid;
      if not Cell.IsCopy or not ImgOK then
      begin
        {slip under border to fill gap when printing}
        BRect := Rect(PL, FT, PR, FT + IH);
        if not HasBorderStyle then // BorderStyle = bssNone then
        begin
          if MargArray[BorderLeftWidth] > 0 then
            Dec(BRect.Left);
          if MargArray[BorderTopWidth] > 0 then
            Dec(BRect.Top);
          if MargArray[BorderRightWidth] > 0 then
            Inc(BRect.Right);
          if MargArray[BorderBottomWidth] > 0 then
            Inc(BRect.Bottom);
        end
        else
          if Border then
            InflateRect(BRect, 1, 1);
        Canvas.FillRect(BRect);
      end;
    end;

    if ImgOK and (TiledImage <> nil) then
    begin
      if not Cell.IsCopy then
        TiledImage.DrawUnstretched(Canvas, PL, FT, Pr - PL, IH, 0, IT, Cell.BkGnd)
      else
        TiledImage.PrintUnstretched(Canvas, PL, FT, Pr - PL, IH, 0, IT, Cell.BkGnd)
      ;
    end;
  except
  end;

  IsVisible := (YO < ARect.Bottom + 200) and (YO + Ht > -200);
  try
    if IsVisible and (Cell.Count > 0) then
    begin
      Rslt := 0;
      Rgn := 0;
      SaveRgn := 0;
      if HideOverflow then
      begin
      {clip cell contents to prevent overflow.  First check to see if there is
       already a clip region}
        SaveRgn := CreateRectRgn(0, 0, 1, 1);
        Rslt := GetClipRgn(Canvas.Handle, SaveRgn); {Rslt = 1 for existing region, 0 for none}
      {Form the region for this cell}
        GetWindowOrgEx(Canvas.Handle, Point); {when scrolling or animated Gifs, canvas may not start at X=0, Y=0}
        Rgn := CreateRectRgn(BL - Point.X, BT - Point.Y, BR - Point.X, BB - Point.Y);
        if Rslt = 1 then {if there was a region, use the intersection with this region}
          CombineRgn(Rgn, Rgn, SaveRgn, Rgn_And);
        SelectClipRgn(Canvas.Handle, Rgn);
      end;
      try
        Cell.Draw(Canvas, ARect, Wd - HzSpace - CellSpacingHorz,
          X + FPad.Left + FBrd.Left + CellSpacingHorz,
          Y + FPad.Top + FBrd.Top + YIndent, ARect.Left, 0); {possibly should be IRgn.LfEdge}
      finally
        if HideOverflow then
        begin
          if Rslt = 1 then {restore any previous clip region}
            SelectClipRgn(Canvas.Handle, SaveRgn)
          else
            SelectClipRgn(Canvas.Handle, 0);
          DeleteObject(Rgn);
          DeleteObject(SaveRgn);
        end;
      end;
    end;
  except
  end;

  Cell.DrawYY := Y;
  if IsVisible and ((Cell.Count > 0) or ShowEmptyCells) then
    try
      DrawBorder(Canvas, Rect(BL, BT, BR, BB), Rect(PL, PT, PR, PB),
        htColors(MargArray[BorderLeftColor], MargArray[BorderTopColor], MargArray[BorderRightColor], MargArray[BorderBottomColor]),
        htStyles(ThtBorderStyle(MargArray[BorderLeftStyle]), ThtBorderStyle(MargArray[BorderTopStyle]), ThtBorderStyle(MargArray[BorderRightStyle]), ThtBorderStyle(MargArray[BorderBottomStyle])),
        MargArray[BackgroundColor], False);
    except
    end;
end;

//-- BG ---------------------------------------------------------- 08.01.2012 --
procedure TfrxHtCellObj.SetBorderBottom(const Value: Integer);
begin
  FBrd.Bottom := Value;
end;

//-- BG ---------------------------------------------------------- 08.01.2012 --
procedure TfrxHtCellObj.SetBorderLeft(const Value: Integer);
begin
  FBrd.Left := Value;
end;

//-- BG ---------------------------------------------------------- 08.01.2012 --
procedure TfrxHtCellObj.SetBorderRight(const Value: Integer);
begin
  FBrd.Right := Value;
end;

//-- BG ---------------------------------------------------------- 08.01.2012 --
procedure TfrxHtCellObj.SetBorderTop(const Value: Integer);
begin
  FBrd.Top := Value;
end;

//-- BG ---------------------------------------------------------- 08.01.2012 --
procedure TfrxHtCellObj.SetPaddingBottom(const Value: Integer);
begin
  FPad.Bottom := Value;
end;

//-- BG ---------------------------------------------------------- 08.01.2012 --
procedure TfrxHtCellObj.SetPaddingLeft(const Value: Integer);
begin
  FPad.Left := Value;
end;

//-- BG ---------------------------------------------------------- 08.01.2012 --
procedure TfrxHtCellObj.SetPaddingRight(const Value: Integer);
begin
  FPad.Right := Value;
end;

//-- BG ---------------------------------------------------------- 08.01.2012 --
procedure TfrxHtCellObj.SetPaddingTop(const Value: Integer);
begin
  FPad.Top := Value;
end;

{----------------TfrxHtCellList.Create}

constructor TfrxHtCellList.Create(Attr: TfrxHtAttributeList; Prop: TfrxHTProperties);
var
  I: Integer;
  Color: TColor;
begin
  inherited Create;
  if Assigned(Attr) then
    for I := 0 to Attr.Count - 1 do
      with Attr[I] do
        case Which of
          BGColorSy:
            BkGnd := TryStrToColor(Name, False, BkColor);
          BackgroundSy:
            BkImage := Name;
          HeightSy:
            SpecRowHeight := ToSpecWidth(Max(0, Min(Value, 100)), Name);
        end;
  if Assigned(Prop) then
  begin
    Color := Prop.GetBackgroundColor;
    if Color <> clNone then
    begin
      BkGnd := True;
      BkColor := Color;
    end;
    Prop.GetBackgroundImage(BkImage); {'none' will change ThtString to empty}
    if BkImage <> '' then
      Prop.GetBackgroundPos(Prop.EmSize, Prop.ExSize, APRec);
    Prop.GetPageBreaks(BreakBefore, BreakAfter, KeepIntact);
  end;
end;

{----------------TfrxHtCellList.CreateCopy}

constructor TfrxHtCellList.CreateCopy(Parent: TfrxHtBlock; T: TfrxHtCellList);
var
  I: Integer;
begin
  inherited Create;
  BreakBefore := T.BreakBefore;
  BreakAfter := T.BreakAfter;
  KeepIntact := T.KeepIntact;
  RowType := T.Rowtype;
  for I := 0 to T.Count - 1 do
    if Assigned(T[I]) then
      Add(T[I].Clone(Parent))
    else
      Add(nil);
end;

procedure TfrxHtCellList.Add(CellObjBase: TfrxHtfrcHtCellObjBase);
var
  CellObj: TfrxHtCellObj absolute CellObjBase;
begin
  inherited Add(CellObjBase);
  if CellObjBase is TfrxHtCellObj then
  begin
    BreakBefore := BreakBefore or CellObj.BreakBefore;
    BreakAfter := BreakAfter or CellObj.BreakAfter;
    KeepIntact := KeepIntact or CellObj.KeepIntact;
    case SpecRowHeight.VType of
      wtPercent:
        case CellObj.FSpecHt.VType of
          wtPercent:
            if CellObj.FSpecHt.Value < SpecRowHeight.Value then
              CellObj.FSpecHt.Value := SpecRowHeight.Value;

          wtNone,
          wtRelative: // percentage is stronger
            CellObj.FSpecHt := SpecRowHeight;

        else
          // keep specified absolute value
        end;

      wtRelative:
        case CellObj.FSpecHt.VType of
          wtPercent: ; // percentage is stronger

          wtNone:
            CellObj.FSpecHt := SpecRowHeight;

          wtRelative:
            if CellObj.FSpecHt.Value < SpecRowHeight.Value then
              CellObj.FSpecHt.Value := SpecRowHeight.Value;
        else
          // keep specified absolute value
        end;

      wtAbsolute:
        case CellObj.FSpecHt.VType of
          wtAbsolute:
            if CellObj.FSpecHt.Value < SpecRowHeight.Value then
              CellObj.FSpecHt.Value := SpecRowHeight.Value;
        else
          // absolute value is stronger
          CellObj.FSpecHt := SpecRowHeight;
        end;
    end;
  end;
end;

{----------------TfrxHtCellList.Initialize}

procedure TfrxHtCellList.Initialize;
var
  I: Integer;
begin
  if BkGnd then
    for I := 0 to Count - 1 do
      if Items[I] is TfrxHtCellObj then
        with TfrxHtCellObj(Items[I]).Cell do
          if not BkGnd then
          begin
            BkGnd := True;
            BkColor := Self.BkColor;
          end;
end;

procedure TfrxHtCellList.RTLSwap;
var
  i: Integer;
begin
  for i := 0 to Count div 2 - 1 do
    Exchange(i, Count - i - 1);
end;

{----------------TfrxHtCellList.DrawLogic1}

function TfrxHtCellList.DrawLogicA(Canvas: TCanvas; const Widths: TIntArray; Span, CellSpacingHorz,CellSpacingVert, AHeight, Rows: Integer;
  out Desired: Integer; out Spec, More: Boolean): Integer;
{Find vertical size of each cell, Row height of this row.  But final Y position
 is not known at this time.
 Rows is number rows in table.
 AHeight is for calculating percentage heights}
var
  I, Dummy: Integer;
  DummyCurs, GuessHt: Integer;
begin
  Result := 0;
  Desired := 0;
  Spec := False;
  DummyCurs := 0;
  More := False;
  for I := 0 to Count - 1 do
  begin
    if Items[I] is TfrxHtCellObj then
      with TfrxHtCellObj(Items[I]) do
        if ColSpan > 0 then {skip the dummy cells}
        begin
          Wd := Sum(Widths, I, I + ColSpan - 1); {accumulate column widths}
          if Span = RowSpan then
          begin
            Dummy := 0;
            case SpecHt.VType of
              wtAbsolute:
                GuessHt := Trunc(SpecHt.Value);

              wtPercent:
                GuessHt := Trunc(SpecHt.Value * AHeight / 1000.0);
            else
              GuessHt := 0;
            end;

            if (GuessHt = 0) and (Rows = 1) then
              GuessHt := AHeight;

            VSize := Cell.DoLogic(Canvas, 0, Wd - HzSpace - CellSpacingHorz, Max(0, GuessHt - VrSpace), 0, Dummy, DummyCurs);
            Result := Max(Result, VSize + VrSpace);

            case SpecHt.VType of
              wtAbsolute:
              begin
                Result := Max(Result, Max(VSize, Trunc(SpecHt.Value)));
                Spec := True;
              end;

              wtPercent:
              begin
                Desired := Max(Desired, GuessHt);
                Spec := True;
              end;
            end;
          end
          else if RowSpan > Span then
            More := True;
        end;
  end;
  Desired := Max(Result, Desired);
end;

{----------------TfrxHtCellList.DrawLogicB}

procedure TfrxHtCellList.DrawLogicB(Canvas: TCanvas; Y, CellSpacingHorz, CellSpacingVert: Integer; var Curs: Integer);
{Calc Y indents. Set up Y positions of all cells.}
var
  I: Integer;
  CellObj: TfrxHtfrcHtCellObjBase;
begin
  for I := 0 to Count - 1 do
  begin
    CellObj := Items[I];
    if (CellObj <> nil) and (CellObj.ColSpan > 0) and (CellObj.RowSpan > 0) then
      CellObj.DrawLogic2(Canvas, Y, CellSpacingHorz,CellSpacingVert, Curs);
  end;
end;

//-- BG ---------------------------------------------------------- 12.09.2010 --
function TfrxHtCellList.GetCellObj(Index: Integer): TfrxHtfrcHtCellObjBase;
begin
  Result := inherited Get(Index);
end;

{----------------TfrxHtCellList.Draw}

function TfrxHtCellList.Draw(Canvas: TCanvas; Document: TfrxHtDocument; const ARect: TRect; const Widths: TIntArray;
  X, Y, YOffset, CellSpacingHorz,CellSpacingVert: Integer; Border: Boolean; Light, Dark: TColor; MyRow: Integer): Integer;
var
  I: Integer;
  YO: Integer;
  CellObj: TfrxHtfrcHtCellObjBase;
begin
  YO := Y - YOffset;
  Result := RowHeight + Y;

  if (YO + RowSpanHeight >= ARect.Top) and (YO < ARect.Bottom) then
    for I := 0 to Count - 1 do
    begin
      CellObj := Items[I];
      if (CellObj <> nil) and (CellObj.ColSpan > 0) and (CellObj.RowSpan > 0) then
        CellObj.Draw(Canvas, ARect, X, Y, CellSpacingHorz,CellSpacingVert, Border, Light, Dark);
      X := X + Widths[I];
    end;
end;

//-- BG ---------------------------------------------------------- 09.02.2013 --
function TryStrToTableFrame(const Str: ThtString; var Frame: TTableFrame): Boolean;
 {$ifdef UseInline} inline; {$endif}
var
  Upr: ThtString;
begin
  Upr := htUpperCase(Str);
  Result := True;
  if htCompareStr(Upr, 'VOID') = 0 then
    Frame := tfVoid
  else if htCompareStr(Upr, 'ABOVE') = 0 then
    Frame := tfAbove
  else if htCompareStr(Upr, 'BELOW') = 0 then
    Frame := tfBelow
  else if htCompareStr(Upr, 'HSIDES') = 0 then
    Frame := tfHSides
  else if htCompareStr(Upr, 'LHS') = 0 then
    Frame := tfLhs
  else if htCompareStr(Upr, 'RHS') = 0 then
    Frame := tfRhs
  else if htCompareStr(Upr, 'VSIDES') = 0 then
    Frame := tfVSides
  else if htCompareStr(Upr, 'BOX') = 0 then
    Frame := tfBox
  else if htCompareStr(Upr, 'BORDER') = 0 then
    Frame := tfBorder
  else
    Result := False;
end;

//-- BG ---------------------------------------------------------- 09.02.2013 --
function TryStrToTableRules(const Str: ThtString; var Rules: TTableRules): Boolean;
 {$ifdef UseInline} inline; {$endif}
var
  Upr: ThtString;
begin
  Upr := htUpperCase(Str);
  Result := True;
  if htCompareStr(Upr, 'NONE') = 0 then
    Rules := trNone
  else if htCompareStr(Upr, 'GROUPS') = 0 then
    Rules := trGroups
  else if htCompareStr(Upr, 'ROWS') = 0 then
    Rules := trRows
  else if htCompareStr(Upr, 'COLS') = 0 then
    Rules := trCols
  else if htCompareStr(Upr, 'ALL') = 0 then
    Rules := trAll
  else
    Result := False;
end;

{----------------TfrxHtmlTable.Create}

constructor TfrxHtmlTable.Create(OwnerTableBlock: TfrxHtTableBlock; Parent: TfrxHtCellBasic; Attr: TfrxHtAttributeList; Prop: TfrxHTProperties);
var
  I: Integer;
  A: TfrxHtAttribute;
begin
  inherited Create(Parent, Attr, Prop);
  FOwnerTableBlock := OwnerTableBlock;
  if FDisplay = pdUnassigned then
    FDisplay := pdTable;
  Rows := TfrxHtRowList.Create;

  FCellPadding := 1;
  FCellSpacingHorz := 2;
  FCellSpacingVert := 2;
  BorderColor := clBtnFace;
  BorderColorLight := clBtnHighLight;
  BorderColorDark := clBtnShadow;

  // BG, 20.01.2013: process BorderSy before FrameSy and RulesSy as it implies defaults.
  A := nil;
  HasBorderWidthAttr := Attr.Find(BorderSy, A);
  if HasBorderWidthAttr then
  begin
    //BG, 15.10.2010: issue 5: set border width only, if style does not set any border width:
    if A.Name = '' then
      BorderWidth := 1
    else
      BorderWidth := Min(100, Max(0, A.Value)); {Border=0 is no border}
    brdWidthAttr := BorderWidth;

    if BorderWidth <> 0 then
    begin
      Frame := tfBorder;
      Rules := trAll;
    end
    else
    begin
      Frame := tfVoid;
      Rules := trNone;
    end;
  end;

  for I := 0 to Attr.Count - 1 do
    with Attr[I] do
      case Which of
        FrameAttrSy:
          TryStrToTableFrame(Name, Frame);

        RulesSy:
          TryStrToTableRules(Name, Rules);

        CellSpacingSy:
          begin
            FCellSpacingHorz := Min(40, Max(-1, Value));
            FCellSpacingVert := CellSpacingHorz;
          end;

        CellPaddingSy:
          FCellPadding := Min(50, Max(0, Value));

        BorderColorSy:
          TryStrToColor(Name, False, BorderColor);

        BorderColorLightSy:
          TryStrToColor(Name, False, BorderColorLight);

        BorderColorDarkSy:
          TryStrToColor(Name, False, BorderColorDark);
      end;
  if Prop.Collapse then
  begin
    FCellSpacingHorz := -1;
    FCellSpacingVert := -1;
  end
  else if Prop.HasBorderSpacing then
  begin
    FCellSpacingHorz := Prop.GetBorderSpacingHorz;
    FCellSpacingVert := Prop.GetBorderSpacingVert;
  end;
end;

{----------------TfrxHtmlTable.CreateCopy}

constructor TfrxHtmlTable.CreateCopy(OwnerCell: TfrxHtCellBasic; Source: TfrxHtmlNode);
var
  I: Integer;
  HtmlTable: TfrxHtmlTable absolute Source;
begin
  inherited CreateCopy(OwnerCell,Source);
  FOwnerTableBlock := OwnerCell.FOwnerBlock as TfrxHtTableBlock;
  Rows := TfrxHtRowList.Create;
  for I := 0 to HtmlTable.Rows.Count - 1 do
    Rows.Add(TfrxHtCellList.CreateCopy(OwnerCell.OwnerBlock, HtmlTable.Rows[I]));

  Move(HtmlTable.Initialized, Initialized, PtrSub(@EndList, @Initialized));
  Rules := HtmlTable.Rules;
  BorderWidth := HtmlTable.BorderWidth;
  BorderColor := HtmlTable.BorderColor;

  SetLength(Widths, NumCols);
  SetLength(MaxWidths, NumCols);
  SetLength(MinWidths, NumCols);
  SetLength(Percents, NumCols);
  SetLength(Multis, NumCols);
  SetLength(ColumnSpecs, NumCols);

  if HtmlTable.FColSpecs <> nil then
  begin
    FColSpecs := TfrxHtColSpecList.Create;
    for I := 0 to HtmlTable.FColSpecs.Count - 1 do
      FColSpecs.Add(TfrxHtColSpec.CreateCopy(HtmlTable.FColSpecs[I]));
  end;

  if Document.PrintTableBackground then
  begin
    FBkGnd := HtmlTable.BkGnd;
    FBkColor := HtmlTable.BkColor;
  end
  else
    FBkGnd := False;
  TablePartRec := TTablePartRec.Create;
  TablePartRec.TablePart := Normal;
end;

{----------------TfrxHtmlTable.Destroy}

destructor TfrxHtmlTable.Destroy;
begin
  Rows.Free;
  TablePartRec.Free;
  FreeAndNil(FColSpecs);
  inherited Destroy;
end;

{----------------TfrxHtmlTable.DoColumns}

procedure TfrxHtmlTable.DoColumns(Count: Integer; const SpecWidth: TSpecWidth; VAlign: ThtAlignmentStyle; const Align: ThtString);
{add the  /  info to the Cols list}
var
  I: Integer;
begin
  if FColSpecs = nil then
    FColSpecs := TfrxHtColSpecList.Create;
  Count := Min(Count, 10000);
  for I := 0 to Count - 1 do
    FColSpecs.Add(TfrxHtColSpec.Create(SpecWidth, Align, VAlign));
end;

{----------------TfrxHtmlTable.AddDummyCells}

procedure TfrxHtmlTable.Initialize;

  function DummyCell(RSpan: Integer): TfrxHtfrcHtCellObjBase;
  begin
    Result := TfrxHtDummyCellObj.Create(RSpan);
//    if BkGnd then {transfer bgcolor to cell if no Table image}
//    begin
//      Result.Cell.BkGnd := True;
//      Result.Cell.BkColor := BkColor;
//    end;
  end;

  procedure AddDummyCellsForColSpansAndInitializeCells;
  var
    Cl, Rw, RowCount, K: Integer;
    Row: TfrxHtCellList;
    CellObjBase: TfrxHtfrcHtCellObjBase;
    CellObj: TfrxHtCellObj absolute CellObjBase;
  begin
    {initialize cells and put dummy cells in rows to make up for ColSpan > 1}
    NumCols := 0;
    RowCount := Rows.Count;
    for Rw := 0 to RowCount - 1 do
    begin
      Row := Rows[Rw];
      Row.Initialize;
      for Cl := Row.Count - 1 downto 0 do
      begin
        CellObjBase := Row[Cl];
        CellObj.Initialize(CellPadding, Row.BkImage, Row.APRec, Self.BorderWidth > 0);
        if BkGnd and not CellObj.Cell.BkGnd then {transfer bgcolor to cells if no Table image}
        begin
          CellObj.Cell.BkGnd := True;
          CellObj.Cell.BkColor := BkColor;
        end;
        CellObj.RowSpan := Min(CellObj.RowSpan, RowCount - Rw); {So can't extend beyond table}
        for K := Cl + 1 to Cl + CellObj.ColSpan - 1 do
          if CellObj.RowSpan > 1 then
            Row.Insert(K, DummyCell(CellObj.RowSpan)) {these could be
            Nil also except they're needed for expansion in the next section}
          else
            Row.Insert(K, DummyCell(1));
      end;
      NumCols := Max(NumCols, Row.Count); {temporary # cols}
    end;
  end;

  procedure AddDummyCellsForRowSpans;
  var
    Cl, Rw, RowCount, K: Integer;
    Row: TfrxHtCellList;
    CellObj: TfrxHtfrcHtCellObjBase;
  begin
    RowCount := Rows.Count;
    for Cl := 0 to NumCols - 1 do
      for Rw := 0 to RowCount - 1 do
      begin
        Row := Rows[Rw];
        if Row.Count > Cl then
        begin
          CellObj := Row[Cl];
          if CellObj <> nil then
          begin
            CellObj.RowSpan := Min(CellObj.RowSpan, RowCount - Rw); {practical limit}
            if CellObj.RowSpan > 1 then
              for K := Rw + 1 to Rw + CellObj.RowSpan - 1 do
              begin {insert dummy cells in following rows if RowSpan > 1}
                while Rows[K].Count < Cl do {add padding if row is short}
                  Rows[K].Add(DummyCell(0));
                if Rows[K].Count < NumCols then // in an invalid table definition spanned cells may overlap and thus required dummies could be present, yet.
                  Rows[K].Insert(Cl, DummyCell(0));
              end;
          end;
        end;
      end;

    NumCols := 0;
    for Rw := 0 to Rows.Count - 1 do
      NumCols := Max(NumCols, Rows[Rw].Count);
  end;

  procedure AddDummyCellsForUnequalRowLengths;
  var
    Cl: Integer;
    CellObj: TfrxHtfrcHtCellObjBase;
    Row: TfrxHtCellList;

    function IsLastCellOfRow(): Boolean;
    begin
      // Is Row[Cl] resp. CellObj a cell in this row? (Cl >= 0)
      // With respect to rowspans from previous rows is it the last one? (Cl + CellObj.ColSpan >= NumCols)
      Result := (Cl >= 0) and (Cl + CellObj.ColSpan >= Row.Count);
    end;

  var
    Rw, I: Integer;
  begin
    Rw := 0;
    while Rw < Rows.Count do
    begin
      Row := Rows[Rw];
      Cl := -1;
      if Row.Count < NumCols then
      begin
        // this row is too short

        // find the spanning column
        Cl := Row.Count - 1;
        while Cl >= 0 do
        begin
          CellObj := Row[Cl];
          if CellObj.ColSpan > 0 then
            break;
          Dec(Cl);
        end;

        if IsLastCellOfRow then
          // add missing cells
          for I := Row.Count to NumCols - 1 do
            Row.Add(DummyCell(1));
      end;

      // continue with next row not spanned by this cell.
      if (Cl >= 0) and (CellObj.RowSpan > 0) then
        Inc(Rw, CellObj.RowSpan)
      else
        Inc(Rw);
    end;
  end;

var
  Cl, Rw, MaxColSpan, MaxRowSpan: Integer;
  Row: TfrxHtCellList;
  CellObj: TfrxHtfrcHtCellObjBase;
begin
  if not Initialized then
  begin
    AddDummyCellsForColSpansAndInitializeCells;
    AddDummyCellsForRowSpans;
    AddDummyCellsForUnequalRowLengths;

    for Rw := 0 to Rows.Count - 1 do
    begin
      MaxRowSpan := Rows.Count - Rw;
      Row := Rows[Rw];
      for Cl := 0 to Row.Count - 1 do
      begin
        MaxColSpan := NumCols - Cl;
        CellObj := Row[Cl];

        // Reduce excessive colspans.
        if CellObj.ColSpan > MaxColSpan then
          CellObj.ColSpan := MaxColSpan;

        // Reduce excessive rowspans.
        if CellObj.RowSpan > MaxRowSpan then
          CellObj.RowSpan := MaxRowSpan;
      end;
    end;

    SetLength(Widths, NumCols);
    SetLength(MaxWidths, NumCols);
    SetLength(MinWidths, NumCols);
    SetLength(Percents, NumCols);
    SetLength(Multis, NumCols);
    SetLength(ColumnSpecs, NumCols);

    Initialized := True;
  end; {if not ListsProcessed}
end;

procedure TfrxHtmlTable.IncreaseWidthsByWidth(WidthType: TWidthType; var Widths: TIntArray;
  StartIndex, EndIndex, Required, Spanned, Count: Integer);
// Increases width of spanned columns relative to given widths.
var
  I, OldWidth, NewWidth: Integer;
begin
  OldWidth := 0;
  NewWidth := 0;
  for I := EndIndex downto StartIndex do
    if ColumnSpecs[I] = WidthType then
      if Count > 1 then
      begin
        // building sum of all processed columns avoids rounding errors.
        Inc(OldWidth, Widths[I]);
        Widths[I] := MulDiv(OldWidth, Required, Spanned) - NewWidth;
        Inc(NewWidth, Widths[I]);
        Dec(Count);
      end
      else
      begin
        // The remaining pixels are the new first column's width.
        Widths[I] := Required - NewWidth;
        break;
      end;
end;

procedure TfrxHtmlTable.IncreaseWidthsByPercentage(var Widths: TIntArray;
  StartIndex, EndIndex, Required, Spanned, Percent, Count: Integer);
// Increases width of spanned columns relative to given percentage.
var
  Excess, AddedExcess, AddedPercent, I, Add: Integer;
begin
  Excess := Required - Spanned;
  AddedExcess := 0;
  AddedPercent := 0;
  for I := EndIndex downto StartIndex do
    if ColumnSpecs[I] = wtPercent then
      if Count > 1 then
      begin
        Inc(AddedPercent, Percents[I]);
        Add := MulDiv(Excess, AddedPercent, Percent) - AddedExcess;
        Inc(Widths[I], Add);
        Inc(AddedExcess, Add);
        Dec(Count);
      end
      else
      begin
        // add the remaining pixels to the first column's width.
        Inc(Widths[I], Excess - AddedExcess);
        break;
      end;
end;

procedure TfrxHtmlTable.IncreaseWidthsByMinMaxDelta(WidthType: TWidthType; var Widths: TIntArray;
  StartIndex, EndIndex, Excess, DeltaWidth, Count: Integer; const Deltas: TIntArray);
// Increases width of spanned columns relative to difference between min and max widths.
var
  AddedExcess, AddedDelta, I, Add: Integer;
  WidthSum, K, Remaining: Integer;
  SameWidthFix: TSameWidthFix;
begin
  SameWidthFix := TSameWidthFix.Create(Widths, MinWidths, MaxWidths, ColumnSpecs);
  AddedExcess := 0;
  AddedDelta := 0;
  for I := EndIndex downto StartIndex do
    if ColumnSpecs[I] = WidthType then
      if Count > 1 then
      begin
        Inc(AddedDelta, Deltas[I]);
        Add := MulDiv(Excess, AddedDelta, DeltaWidth) - AddedExcess;
        Inc(Widths[I], Add);
        Inc(AddedExcess, Add);
        Dec(Count);
      end
      else
      begin
        // BG, 16.11.2013: thanks to Andreas Hausladen for spreading Excess:
        if WidthType = wtNone then
        begin
          WidthSum := 0;
          for K := EndIndex downto StartIndex do
            if ColumnSpecs[K] = WidthType then
              Inc(WidthSum, Widths[K] + Deltas[K]);

          // spread the remaining pixels to all columns by their width percentage
          Remaining := (Excess - AddedExcess) - DeltaWidth;
          if (Remaining > 0) and (WidthSum > 0) then // wrong display is better than crash
          begin
            for K := EndIndex downto StartIndex + 1 do
            begin
              if ColumnSpecs[K] = WidthType then
              begin
                Add := ((Widths[K] + Deltas[K]) * Remaining) div WidthSum;
                Inc(Widths[K], Add);
                Inc(AddedExcess, Add);
              end;
            end;
          end;
        end;

        // add the remaining pixels to the first column's width.
        Inc(Widths[I], Excess - AddedExcess);
        break;
      end;
  SameWidthFix.Fix(Widths);
  SameWidthFix.Free;
end;

procedure TfrxHtmlTable.IncreaseWidthsRelatively(
  var Widths: TIntArray;
  StartIndex, EndIndex, Required, SpannedMultis: Integer; ExactRelation: Boolean);
// Increases width of spanned columns according to relative columns specification.
// Does not touch columns specified by percentage or absolutely.
var
  RequiredWidthFactor: Double;
  Count, I, AddedWidth, AddedMulti: Integer;
begin
  // Some columns might have Multi=0. Don't widen these. Thus remove their width from Required.
  // Some columns might be wider than required. Widen all columns to preserve the relations.
  RequiredWidthFactor := 0;
  Count := 0;
  for I := EndIndex downto StartIndex do
    if ColumnSpecs[I] = wtRelative then
      if Multis[I] > 0 then
      begin
        Inc(Count);
        if ExactRelation then
          RequiredWidthFactor := Max(RequiredWidthFactor, Widths[I] / Multis[I]);
      end
      else
      begin
        Dec(Required, Widths[I]);
      end;

  RequiredWidthFactor := Max(RequiredWidthFactor, Required / SpannedMultis); // 100 times width of 1*.
  Required := Min(Required, Trunc(RequiredWidthFactor * SpannedMultis)); // don't exceed given requirement.
  // building sum of all processed columns to reduce rounding errors.
  AddedWidth := 0;
  AddedMulti := 0;
  for I := EndIndex downto StartIndex do
    if (ColumnSpecs[I] = wtRelative) and (Multis[I] > 0) then
      if Count > 1 then
      begin
        Inc(AddedMulti, Multis[I]);
        Widths[I] := Trunc(AddedMulti * RequiredWidthFactor) - AddedWidth;
        Inc(AddedWidth, Widths[I]);
        Dec(Count);
      end
      else
      begin
        // The remaining pixels are the new first column's width.
        Widths[I] := Required - AddedWidth;
        break;
      end;
end;

procedure TfrxHtmlTable.IncreaseWidthsEvenly(WidthType: TWidthType; var Widths: TIntArray;
  StartIndex, EndIndex, Required, Spanned, Count: Integer);
// Increases width of spanned columns of given type evenly.
var
  RemainingWidth, I: Integer;
begin
  RemainingWidth := Required;
  for I := EndIndex downto StartIndex do
    if ColumnSpecs[I] = WidthType then
      if Count > 1 then
      begin
        Dec(Count);
        // MulDiv for each column instead of 1 precalculated width for all columns avoids round off errors.
        Widths[I] := MulDiv(Widths[I], Required, Spanned);
        Dec(RemainingWidth, Widths[I]);
      end
      else
      begin
        // add the remaining pixels to the first column's width.
        Widths[I] := RemainingWidth;
        break;
      end;
end;

{----------------TfrxHtmlTable.GetWidths}

procedure TfrxHtmlTable.GetMinMaxWidths(Canvas: TCanvas; AvailableWidth, AvailableHeight: Integer);
// calculate MaxWidths and MinWidths of all columns.

  procedure UpdateColumnSpec(var Counts: TIntegerPerWidthType; var OldType: TWidthType; NewType: TWidthType);
  begin
    // update to stonger spec only:
    case NewType of
      wtAbsolute: if OldType in [wtAbsolute] then Exit;
      wtPercent:  if OldType in [wtAbsolute, wtPercent] then Exit;
      wtRelative: if OldType in [wtAbsolute, wtPercent, wtRelative] then Exit;
    else
      // wtNone:
      Exit;
    end;

    // at this point: NewType is stronger than OldType
    Dec(Counts[OldType]);
    Inc(Counts[NewType]);
    OldType := NewType;
  end;

  procedure UpdateRelativeWidths(var Widths: TIntArray; const ColumnSpecs: TWidthTypeArray; StartIndex, EndIndex: Integer);
  // Increases width of spanned columns according to relative columns specification.
  // Does not touch columns specified by percentage or absolutely.
  var
    RequiredWidthFactor, Count, Multi, I, Required, AddedWidth, AddedMulti: Integer;
  begin
    // Some columns might be wider than required. Widen all columns to preserve the relations.
    RequiredWidthFactor := 100;
    Count := 0;
    Multi := 0;
    for I := EndIndex downto StartIndex do
      if (ColumnSpecs[I] = wtRelative) and (Multis[I] > 0) then
      begin
        Inc(Count);
        Inc(Multi, Multis[I]);
        RequiredWidthFactor := Max(RequiredWidthFactor, MulDiv(Widths[I], 100, Multis[I]));
      end;

    Required := MulDiv(RequiredWidthFactor, Multi, 100);
    // building sum of all processed columns to reduce rounding errors.
    AddedWidth := 0;
    AddedMulti := 0;
    for I := EndIndex downto StartIndex do
      if (ColumnSpecs[I] = wtRelative) and (Multis[I] > 0) then
        if Count > 1 then
        begin
          Inc(AddedMulti, Multis[I]);
          Widths[I] := MulDiv(AddedMulti, RequiredWidthFactor, 100) - AddedWidth;
          Inc(AddedWidth, Widths[I]);
          Dec(Count);
        end
        else
        begin
          // add the remaining pixels to the first column's width.
          Widths[I] := Required - AddedWidth;
          break;
        end;
  end;

var
  // calculated values:
  CellSpec: TWidthType;
  CellMin, CellMax, CellPercent, CellRel: Integer;
  SpannedMin, SpannedMax, SpannedMultis, SpannedPercents: Integer;
  SpannedCounts: TIntegerPerWidthType;

  procedure IncreaseMinMaxWidthsEvenly(WidthType: TWidthType; StartIndex, EndIndex: Integer);
  var
    Untouched, I: Integer;
  begin
    if CellMin > SpannedMin then
    begin
      Untouched := SumOfNotType(WidthType, ColumnSpecs, MinWidths, StartIndex, EndIndex);
      IncreaseWidthsEvenly(WidthType, MinWidths, StartIndex, EndIndex, CellMin - Untouched, SpannedMin - Untouched, SpannedCounts[WidthType]);
    end;
    if CellMax > SpannedMax then
    begin
      Untouched := SumOfNotType(WidthType, ColumnSpecs, MinWidths, StartIndex, EndIndex);
      IncreaseWidthsEvenly(WidthType, MaxWidths, StartIndex, EndIndex, CellMax - Untouched, SpannedMax - Untouched, SpannedCounts[WidthType]);
    end;

    // Prevent MinWidths from getting larger than MaxWidths, otherwise the calculation goes crazy
    for I := StartIndex to EndIndex do
      if MinWidths[I] > MaxWidths[I] then
        MaxWidths[I] := MinWidths[I];
  end;

  procedure IncreaseMinMaxWidthsByMinMaxDelta(WidthType: TWidthType; StartIndex, EndIndex: Integer);
  var
    Deltas: TIntArray;
    I: Integer;
  begin
    Deltas := SubArray(MaxWidths, MinWidths);
    if CellMin > SpannedMin then
      IncreaseWidthsByMinMaxDelta(WidthType, MinWidths, StartIndex, EndIndex, CellMin - SpannedMin, SpannedMax - SpannedMin, SpannedCounts[WidthType], Deltas);
    if CellMax > SpannedMax then
      IncreaseWidthsByMinMaxDelta(WidthType, MaxWidths, StartIndex, EndIndex, CellMax - SpannedMax, SpannedMax - SpannedMin, SpannedCounts[WidthType], Deltas);

    // Prevent MinWidths from getting larger than MaxWidths, otherwise the calculation goes crazy
    for I := StartIndex to EndIndex do
      if MinWidths[I] > MaxWidths[I] then
        MaxWidths[I] := MinWidths[I];
  end;

var
  //
  I, J, K, Span, EndIndex: Integer;
  Cells: TfrxHtCellList;
  CellObj: TfrxHtfrcHtCellObjBase;
  MaxSpans: TIntArray;
  MaxSpan: Integer;
  MultiCount: Integer;
begin
  // initialize default widths
  SetArray(ColumnCounts, 0);
  if FColSpecs <> nil then
    J := FColSpecs.Count
  else
    J := 0;
  MultiCount := 0;
  for I := 0 to NumCols - 1 do
  begin
    MinWidths[I] := 0;
    MaxWidths[I] := 0;
    Percents[I] := 0;
    Multis[I] := 0;
    ColumnSpecs[I] := wtNone;
    if I < J then
      with FColSpecs[I].FWidth do
      begin
        ColumnSpecs[I] := VType;
        Inc(ColumnCounts[VType]);
        case VType of
          wtAbsolute:
          begin
            MinWidths[I] := Value;
            MaxWidths[I] := Value;
          end;

          wtPercent:
            Percents[I] := Value;

          wtRelative:
          begin
            Multis[I] := Value;
            if Value > 0 then
              Inc(MultiCount);
          end;
        end;
      end;
  end;

  if FOwnerTableBlock.RTL then
  begin
    Reverse(MinWidths);
    Reverse(MaxWidths);
    Reverse(Percents);
    Reverse(Multis);
    Reverse(ColumnSpecs);
  end;

  //SetLength(Heights, 0);
  Span := 1;

  //BG, 29.01.2011: data for loop termination and to speed up looping through
  //  very large tables with large spans.
  //  A table with 77 rows and 265 columns and a MaxSpan of 265 in 2 rows
  //  was processed in 3 seconds before the tuning and 80ms afterwards.
  MaxSpan := 1;
  SetLength(MaxSpans, Rows.Count);
  SetArray(MaxSpans, MaxSpan);
  repeat
    for J := 0 to Rows.Count - 1 do
    begin
      //BG, 29.01.2011: tuning: process rows only, if there is at least 1 cell to process left.
      if Span > MaxSpans[J] then
        continue;

      Cells := Rows[J];
      //BG, 29.01.2011: tuning: process up to cells only, if there is at least 1 cell to process left.
      for I := 0 to Cells.Count - Span do
      begin
        CellObj := Cells[I];
        if CellObj = nil then
          continue;

        if CellObj.ColSpan = Span then
        begin
          // get min and max width of this cell:
          CellObj.Cell.MinMaxWidth(Canvas, CellMin, CellMax, 0, 0);
          CellPercent := 0;
          CellRel := 0;
          with CellObj.SpecWd do
          begin
            CellSpec := VType;
            case VType of
              wtPercent:
                CellPercent := Value;

              wtAbsolute:
              begin
                // BG, 07.10.2012: issue 55: wrong CellMax calculation
                //CellMin := Max(CellMin, Value);   // CellMin should be at least the given absolute value
                //CellMax := Min(CellMax, Value);   // CellMax should be at most  the given absolute value
                CellMax := Value;   // CellMax should be at most  the given absolute value
                CellMax := Max(CellMax, CellMin); // CellMax should be at least CellMin
              end;

              wtRelative:
                CellRel := Value;
            end;
          end;
          Inc(CellMin, CellSpacingHorz + CellObj.HzSpace);
          Inc(CellMax, CellSpacingHorz + CellObj.HzSpace);

          if Span = 1 then
          begin
            MinWidths[I] := Max(MinWidths[I], CellMin);
            MaxWidths[I] := Max(MaxWidths[I], CellMax);
            Percents[I] := Max(Percents[I], CellPercent); {collect percents}
            Multis[I] := Max(Multis[I], CellRel);
            UpdateColumnSpec(ColumnCounts, ColumnSpecs[I], CellSpec);
          end
          else
          begin
            EndIndex := I + Span - 1;

            // Get current min and max width of spanned columns.
            SpannedMin := Sum(MinWidths, I, EndIndex);
            SpannedMax := Sum(MaxWidths, I, EndIndex);

            if (CellMin > SpannedMin) or (CellMax > SpannedMax) then
            begin
              { As spanning cell is wider than sum of spanned columns, we must widen the spanned columns.

                How to add the excessive width:
                a) If cell spans columns without any width specifications, then spread excessive width evenly to these columns.
                b) If cell spans columns with relative specifications, then spread excessive width according
                                                               to relative width values to these columns.
                c) If cell spans columns with precentage specifications, then spread excessive width relative
                                                               to percentages to these columns.
                d) If cell spans columns with absolute specifications only, then spread excessive width relative
                                                               to difference between MinWidth and MaxWidth to all columns.

                see also:
                - http://www.w3.org/TR/html401/struct/tables.html
                - http://www.w3.org/TR/html401/appendix/notes.html#h-B.5.2
                Notice:
                - Fixed Layout: experiments showed that IExplore and Firefox *do* respect width attributes of  and 
                  even if there was a  definition although W3C specified differently.
              }
              CountsPerType(SpannedCounts, ColumnSpecs, I, EndIndex);

              if CellPercent > 0 then
              begin
                SpannedPercents := SumOfType(wtPercent, ColumnSpecs, Percents, I, EndIndex);
                if SpannedPercents > CellPercent then
                  continue;

                // BG, 05.02.2012: spread excessive percentage over unspecified columns:
                if SpannedCounts[wtNone] > 0 then
                begin
                  // a) There is at least 1 column without any width constraint: Widen this/these.
                  IncreaseWidthsEvenly(wtNone, Percents, I, EndIndex, CellPercent - SpannedPercents, 0, SpannedCounts[wtNone]);
                  for K := I to EndIndex do
                    ColumnSpecs[K] := wtPercent;
                  continue;
                end
              end;

              if SpannedCounts[wtNone] > 0 then
              begin
                // a) There is at least 1 column without any width constraint: Widen this/these.
                IncreaseMinMaxWidthsEvenly(wtNone, I, EndIndex);
              end
              else if SpannedCounts[wtRelative] > 0 then
              begin
                // b) There is at least 1 column with relative width: Widen this/these.
                SpannedMultis := SumOfType(wtRelative, ColumnSpecs, Multis, I, EndIndex);
                if SpannedMultis > 0 then
                begin
                  if CellMin > SpannedMin then
                    IncreaseWidthsRelatively(MinWidths, I, EndIndex, CellMin, SpannedMultis, True);
                  if CellMax > SpannedMax then
                    IncreaseWidthsRelatively(MaxWidths, I, EndIndex, CellMax, SpannedMultis, True);
                end
                else if SpannedMax > SpannedMin then
                begin
                  // All spanned columns are at 0*.
                  // Widen columns proportional to difference between yet evaluated min and max width.
                  // This ought to fill the table with least height requirements.
                  IncreaseMinMaxWidthsByMinMaxDelta(wtRelative, I, EndIndex);
                end
                else
                begin
                  // All spanned columns are at 0* and minimum = maximum. Spread excess evenly.
                  IncreaseMinMaxWidthsEvenly(wtRelative, I, EndIndex);
                end;
              end
              else if SpannedCounts[wtPercent] > 0 then
              begin
                // c) There is at least 1 column with percentage width: Widen this/these.
                if SpannedMax > SpannedMin then
                begin
                  // Widen columns proportional to difference between yet evaluated min and max width.
                  // This ought to fill the table with least height requirements.
                  IncreaseMinMaxWidthsByMinMaxDelta(wtPercent, I, EndIndex);
                end
                else
                begin
                  SpannedPercents := SumOfType(wtPercent, ColumnSpecs, Percents, I, EndIndex);
                  if SpannedPercents > 0 then
                  begin
                    // Spread excess to columns proportionally to their percentages.
                    // This ought to keep smaller columns small.
                    if CellMin > SpannedMin then
                      IncreaseWidthsByPercentage(MinWidths, I, EndIndex, CellMin, SpannedMin, SpannedPercents, SpannedCounts[wtPercent]);
                    if CellMax > SpannedMax then
                      IncreaseWidthsByPercentage(MaxWidths, I, EndIndex, CellMax, SpannedMax, SpannedPercents, SpannedCounts[wtPercent]);
                  end
                  else
                  begin
                    // All spanned columns are at 0% and minimum = maximum. Spread excess evenly.
                    IncreaseMinMaxWidthsEvenly(wtPercent, I, EndIndex);
                  end;
                end;
              end
              else
              begin
                // d) All columns have absolute widths: Widen these.
                IncreaseMinMaxWidthsEvenly(wtAbsolute, I, EndIndex);
              end;
            end;
          end;
        end
        else
        begin
          //BG, 29.01.2011: at this point: CellObj.ColSpan <> Span
          if Span = 1 then
          begin
            //BG, 29.01.2011: at this point: in the first loop with a CellObj.ColSpan > 1.

            // Collect data for termination and tuning.
            if MaxSpans[J] < CellObj.ColSpan then
            begin
              MaxSpans[J] := CellObj.ColSpan; // data for tuning
              if MaxSpan < MaxSpans[J] then
                MaxSpan := MaxSpans[J]; // data for termination
            end;
          end;
        end;
      end;
    end;
    Inc(Span);
  until Span > MaxSpan;

  if MultiCount > 0 then
  begin
    UpdateRelativeWidths(MinWidths, ColumnSpecs, 0, NumCols - 1);
    UpdateRelativeWidths(MaxWidths, ColumnSpecs, 0, NumCols - 1);
  end;
end;

{----------------TfrxHtmlTable.MinMaxWidth}

procedure TfrxHtmlTable.MinMaxWidth(Canvas: TCanvas; out Min, Max: Integer; AvailableWidth, AvailableHeight: Integer);
begin
  Initialize; {in case it hasn't been done}
  GetMinMaxWidths(Canvas, AvailableWidth, AvailableHeight);
  Min := Math.Max(Sum(MinWidths) + CellSpacingHorz, tblWidthAttr);
  Max := Math.Max(Sum(MaxWidths) + CellSpacingHorz, tblWidthAttr);

  if FOwnerTableBlock.MargArray[piMinWidth] > 0 then
  begin
    Min := Math.Max(Min, FOwnerTableBlock.MargArray[piMinWidth]);
    Max := Math.Max(Max, FOwnerTableBlock.MargArray[piMinWidth]);
  end;

  if FOwnerTableBlock.MargArray[piMaxWidth] > 0 then
  begin
    Min := Math.Min(Min, FOwnerTableBlock.MargArray[piMaxWidth]);
    Max := Math.Min(Max, FOwnerTableBlock.MargArray[piMaxWidth]);
  end;
end;

procedure TfrxHtmlTable.Reverse(A: TWidthTypeArray);
var
  i, j: Integer;
  t: TWidthType;
begin
  for i := 0 to Length(A) div 2 - 1 do
  begin
    j := Length(A) - i - 1;
    t := A[i];
    A[i] := A[j];
    A[j] := t;
  end;
end;

procedure TfrxHtmlTable.Reverse(A: TIntArray);
var
  i, j: Integer;
  t: Integer;
begin
  for i := 0 to Length(A) div 2 - 1 do
  begin
    j := Length(A) - i - 1;
    t := A[i];
    A[i] := A[j];
    A[j] := t;
  end;
end;

{----------------TfrxHtmlTable.DrawLogic}

function TfrxHtmlTable.DrawLogic1(Canvas: TCanvas; X, Y, XRef, YRef, AWidth, AHeight, BlHt: Integer; IMgr: TfrxHtIndentManager;
  var MaxWidth, Curs: Integer; ARemainHeight: Integer): Integer;

  function FindTableWidth: Integer;

    procedure IncreaseWidths(WidthType: TWidthType; MinWidth, NewWidth, Count: Integer);
    var
      Deltas: TIntArray;
      D, W: Integer;
    begin
      Deltas := SubArray(MaxWidths, MinWidths);
      D := SumOfType(WidthType, ColumnSpecs, Deltas, 0, NumCols - 1);
      if D <> 0 then
        IncreaseWidthsByMinMaxDelta(WidthType, Widths, 0, NumCols - 1, NewWidth - MinWidth, D, Count, Deltas)
      else
      begin
        W := SumOfType(WidthType, ColumnSpecs, Widths, 0, NumCols -1);
        IncreaseWidthsByWidth(WidthType, Widths, 0, NumCols - 1, NewWidth - MinWidth + W, W, Count);
      end;
    end;

    procedure CalcPercentDeltas(var PercentDeltas: TIntArray; NewWidth: Integer);
    var
      I: Integer;
      Percent, PercentDelta: Integer;
    begin
      Percent := Max(1000, Sum(Percents));
      for I := NumCols - 1 downto 0 do
        if ColumnSpecs[I] = wtPercent then
        begin
          PercentDelta := Trunc(1000 * (Percents[I] / Percent - MinWidths[I] / NewWidth));
          if PercentDelta > 0 then
            PercentDeltas[I] := PercentDelta;
        end;
    end;

    procedure IncreaseWidthsByPercentage(var Widths: TIntArray;
      const PercentDeltas: TIntArray;
      StartIndex, EndIndex, Required, Spanned, Percent, Count: Integer);
    // Increases width of columns relative to given percentages.
    var
      Excess, AddedExcess, AddedPercent, I, Add, MaxColWidth, SpecPercent: Integer;
    begin
      SpecPercent := Max(1000, Sum(Percents));
      Excess := Required - Spanned;
      AddedExcess := 0;
      AddedPercent := 0;
      for I := EndIndex downto StartIndex do
        if (ColumnSpecs[I] = wtPercent) and (PercentDeltas[I] > 0) then
          if Count > 1 then
          begin
            Inc(AddedPercent, PercentDeltas[I]);
            Add := MulDiv(Excess, AddedPercent, Percent) - AddedExcess;
            MaxColWidth := MulDiv(Required, Percents[I], SpecPercent);
            Widths[I] := Min(Widths[I] + Add, MaxColWidth);
            Inc(AddedExcess, Add);
            Dec(Count);
          end
          else
          begin
            // add the remaining pixels to the first column's width.
            Add := Excess - AddedExcess;
            MaxColWidth := MulDiv(Required, Percents[I], SpecPercent);
            Widths[I] := Min(Widths[I] + Add, MaxColWidth);
            break;
          end;
    end;

  var
    Specified: Boolean;
    NewWidth, MaxWidth, MinWidth, D, W, I: Integer;
    Counts: TIntegerPerWidthType;
    PercentDeltas: TIntArray;
    PercentAbove0Count: Integer;
    PercentDeltaAbove0Count: Integer;
  begin
    Specified := tblWidthAttr > 0;
    if Specified then
      NewWidth := tblWidthAttr
    else
      NewWidth := IMgr.RightSide(Y) - IMgr.LeftIndent(Y);
    Dec(NewWidth, CellSpacingHorz);

    Initialize;

    {Figure the width of each column}
    GetMinMaxWidths(Canvas, AWidth, AHeight);

    MinWidth := Sum(MinWidths);
    if FOwnerTableBlock.MargArray[piMinWidth] > 0 then
      MinWidth := Max(MinWidth, FOwnerTableBlock.MargArray[piMinWidth]);

    MaxWidth := Sum(MaxWidths);
    if FOwnerTableBlock.MargArray[piMaxWidth] > 0 then
      MaxWidth := Min(MaxWidth, FOwnerTableBlock.MargArray[piMaxWidth]);

    {fill in the Widths array}
    if MinWidth > NewWidth then
      // The minimum table width is too wide. Thus use minimum widths, table might expand.
      Widths := Copy(MinWidths)
    else
    begin
      // Table fits into NewWidth.

      Counts[wtPercent] := 0;
      PercentAbove0Count := 0;
      for I := 0 to NumCols - 1 do
        if ColumnSpecs[I] = wtPercent then
        begin
          Inc(Counts[wtPercent]);
          if Percents[I] > 0 then
            Inc(PercentAbove0Count);
        end;

      Widths := Copy(MinWidths);

      // As minimum widths fit into table try to preserve given absolute widths:
      Counts[wtAbsolute] := 0;
      W := 0;
      for I := 0 to NumCols - 1 do
      begin
        case ColumnSpecs[I] of
          wtAbsolute:
          begin
            Widths[i] := MaxWidths[i];
            Inc(Counts[wtAbsolute]);
          end;
        end;
        Inc(W, Widths[i]);
      end;

      if W > NewWidth then
      begin
        // Given absolute widths plus minimum widths of other columns do not fit into table!
        // Thus reduce the absolute widths as less as possible:

        IncreaseWidths(wtAbsolute, W, NewWidth, Counts[wtAbsolute]);
      end
      else
      begin
        if (PercentAbove0Count > 0) and (NewWidth > 0) then
        begin
          // Calculate widths with respect to percentage specifications.
          // Don't shrink Column i below MinWidth[i]! Therefor spread exessive space
          // trying to fit the percentage demands.
          // If there are more than 100% percent reduce linearly to 100% (including
          // the corresponding percentages of the MinWidth of all other columns).
          SetLength(PercentDeltas, NumCols);
          CalcPercentDeltas(PercentDeltas, NewWidth);

          PercentDeltaAbove0Count := 0;
          for I := 0 to NumCols - 1 do
            if PercentDeltas[I] > 0 then
              Inc(PercentDeltaAbove0Count);

          IncreaseWidthsByPercentage(Widths, PercentDeltas, 0, NumCols - 1, NewWidth, MinWidth, Sum(PercentDeltas), PercentDeltaAbove0Count);
        end;
        MinWidth := Sum(Widths);

        if MinWidth > NewWidth then
          // Table (NewWidth) is too small for given percentage specifications.
          // Shrink percentage columns to fit exactly into NewWidth. All other columns are at minimum.
          IncreaseWidths(wtPercent, MinWidth, NewWidth, Counts[wtPercent])
        else if not Specified and (MaxWidth <= NewWidth) then
          // Table width not specified and maximum widths fits into available width, table might be smaller than NewWidth
          Widths := Copy(MaxWidths)
        else if MinWidth < NewWidth then
        begin
          // Expand columns to fit exactly into NewWidth.
          // Prefer widening columns without or with relative specification.
          CountsPerType(Counts, ColumnSpecs, 0, NumCols - 1);

          if Counts[wtNone] > 0 then
          begin
            // a) There is at least 1 column without any width constraint: modify this/these.
            IncreaseWidths(wtNone, MinWidth, NewWidth, Counts[wtNone]);
          end
          else if Counts[wtRelative] > 0 then
          begin
            // b) There is at least 1 column with relative width: modify this/these.
            W := NewWidth - MinWidth;
            D := SumOfType(wtRelative, ColumnSpecs, Widths, 0, NumCols - 1);
            IncreaseWidthsRelatively(Widths, 0, NumCols - 1, D + W, Sum(Multis), False);
          end
          else if Counts[wtPercent] > 0 then
          begin
            // c) There is at least 1 column with percentage width: modify this/these.
            IncreaseWidths(wtPercent, MinWidth, NewWidth, Counts[wtPercent]);
          end
          else
          begin
            // d) All columns have absolute widths: modify relative to current width.
            IncreaseWidths(wtAbsolute, MinWidth, NewWidth, Counts[wtAbsolute]);
          end;
        end;
      end;
    end;
    {Return Table Width}
    Result := CellSpacingHorz + Sum(Widths);
  end;

  function FindTableHeight: Integer;

    procedure FindRowHeights(Canvas: TCanvas; AHeight: Integer);
    var
      I, J, K, H, Span, TotalMinHt, TotalDesHt, AvailHt, AddOn, Sum, AddedOn, Desired, UnSpec: Integer;
      More, Mr, IsSpeced: Boolean;
      MinHts, DesiredHts: TIntArray;
      SpecHts: array of Boolean;
      F: double;
    begin
      if Rows.Count = 0 then
        Exit;

      Dec(AHeight, CellSpacingVert); {calculated heights will include one cellspacing each,
      this removes that last odd cellspacing}
      SetLength(Heights, Rows.Count);
      SetLength(DesiredHts, Rows.Count);
      SetLength(MinHts, Rows.Count);
      SetLength(SpecHts, Rows.Count);
      for I := 0 to Rows.Count - 1 do
      begin
        Heights[I] := 0;
        DesiredHts[I] := 0;
        MinHts[I] := 0;
        SpecHts[I] := False;
      end;
    {Find the height of each row allowing for RowSpans}
      Span := 1;
      More := True;
      AvailHt := Max(0, AHeight - Rows.Count * CellSpacingVert);
      while More do
      begin
        More := False;
        for J := 0 to Rows.Count - 1 do
          with Rows[J] do
          begin
            if J + Span > Rows.Count then
              Break; {otherwise will overlap}
            H := DrawLogicA(Canvas, Widths, Span, CellSpacingHorz, CellSpacingVert, AvailHt, Rows.Count, Desired, IsSpeced, Mr) + CellSpacingVert;
            Inc(Desired, CellspacingVert);
            More := More or Mr;
            if Span = 1 then
            begin
              MinHts[J] := H;
              DesiredHts[J] := Desired;
              SpecHts[J] := SpecHts[J] or IsSpeced;
            end
            else if H > CellspacingVert then {if H=Cellspacing then no rowspan for this span}
            begin
              TotalMinHt := 0; {sum up the heights so far for the rows involved}
              TotalDesHt := 0;
              for K := J to J + Span - 1 do
              begin
                Inc(TotalMinHt, MinHts[K]);
                Inc(TotalDesHt, DesiredHts[K]);
                SpecHts[K] := SpecHts[K] or IsSpeced;
              end;
              if H > TotalMinHt then {apportion the excess over the rows}
              begin
                Addon := ((H - TotalMinHt) div Span);
                AddedOn := 0;
                for K := J to J + Span - 1 do
                begin
                  Inc(MinHts[K], Addon);
                  Inc(AddedOn, Addon);
                end;
                Inc(MinHts[J + Span - 1], (H - TotalMinHt) - AddedOn); {make up for round off error}
              end;
              if Desired > TotalDesHt then {apportion the excess over the rows}
              begin
                Addon := ((Desired - TotalDesHt) div Span);
                AddedOn := 0;
                for K := J to J + Span - 1 do
                begin
                  Inc(DesiredHts[K], Addon);
                  Inc(AddedOn, Addon);
                end;
                Inc(DesiredHts[J + Span - 1], (Desired - TotalDesHt) - AddedOn); {make up for round off error}
              end;
            end;
          end;
        Inc(Span);
      end;

      TotalMinHt := 0;
      TotalDesHt := 0;
      UnSpec := 0;
      for I := 0 to Rows.Count - 1 do
      begin
        Inc(TotalMinHt, MinHts[I]);
        Inc(TotalDesHt, DesiredHts[I]);
        if not SpecHts[I] then
          Inc(UnSpec);
      end;

      if TotalMinHt >= AHeight then
        Heights := Copy(MinHts)
      else if TotalDesHt < AHeight then
      begin
        if UnSpec > 0 then
        begin {expand the unspeced rows to fit}
          Heights := Copy(DesiredHts);
          Addon := (AHeight - TotalDesHt) div UnSpec;
          Sum := 0;
          for I := 0 to Rows.Count - 1 do
            if not SpecHts[I] then
            begin
              Dec(UnSpec);
              if UnSpec > 0 then
              begin
                Inc(Heights[I], AddOn);
                Inc(Sum, Addon);
              end
              else
              begin {last item, complete everything}
                Inc(Heights[I], AHeight - TotalDesHt - Sum);
                Break;
              end;
            end;
        end
        else if TotalDesHt > 0 then
        begin {expand desired hts to fit}
          Sum := 0;
          F := AHeight / TotalDesHt;
          for I := 0 to Rows.Count - 2 do
          begin
            Heights[I] := Round(F * DesiredHts[I]);
            Inc(Sum, Heights[I]);
          end;
          Heights[Rows.Count - 1] := AHeight - Sum; {last row is the difference}
        end
      end
      else if TotalDesHt - TotalMinHt <> 0 then
      begin
        Sum := 0;
        F := (AHeight - TotalMinHt) / (TotalDesHt - TotalMinHt);
        for I := 0 to Rows.Count - 2 do
        begin
          Heights[I] := MinHts[I] + Round(F * (DesiredHts[I] - MinHts[I]));
          Inc(Sum, Heights[I]);
        end;
        Heights[Rows.Count - 1] := AHeight - Sum;
      end;
    end;

  var
    I, J, K: Integer;
    CellObj: TfrxHtCellObj;
    HasBody: Boolean;
  begin
  // Find Row Heights
    if (CycleNumber <> Document.CycleNumber) or (Length(Heights) = 0) then
      FindRowHeights(Canvas, AHeight)
    //else if Document.InLogic2 and (Document.TableNestLevel <= 10) then
    //  FindRowHeights(Canvas, AHeight)
    ;

    Result := 0;
    HeaderHeight := 0;
    HeaderRowCount := 0;
    FootHeight := 0;
    FootStartRow := -1;
    HasBody := False;
    for J := 0 to Rows.Count - 1 do
      with Rows[J] do
      begin
        RowHeight := Heights[J];
        case RowType of
          THead:
          begin
            Inc(HeaderRowCount);
            Inc(HeaderHeight, RowHeight);
          end;

          TFoot:
          begin
            if FootStartRow = -1 then
            begin
              FootStartRow := J;
              FootOffset := Result;
            end;
            Inc(FootHeight, RowHeight);
          end;

          TBody:
            HasBody := True;
        end;
        RowSpanHeight := 0;
        Inc(Result, RowHeight);
        for I := 0 to Count - 1 do
          if Items[I] is TfrxHtCellObj then
          begin
            CellObj := TfrxHtCellObj(Items[I]);
            with CellObj do
            begin {find the actual height, Ht, of each cell}
              FHt := 0;
              for K := J to Min(J + RowSpan - 1, Rows.Count - 1) do
                Inc(FHt, Heights[K]);
              if RowSpanHeight < FHt then
                RowSpanHeight := FHt;
            end;
          end;
      {DrawLogicB is only called in nested tables if the outer table is calling DrawLogic2}
        if Document.TableNestLevel = 1 then
          Document.InLogic2 := True;
        try
          if Document.InLogic2 then
            DrawLogicB(Canvas, Y, CellSpacingHorz,CellSpacingVert, Curs);
        finally
          if Document.TableNestLevel = 1 then
            Document.InLogic2 := False;
        end;
        Inc(Y, RowHeight);
      end;
    HeadOrFoot := ((HeaderHeight > 0) or (FootHeight > 0)) and HasBody;
    Inc(Result, CellSpacingVert);
  end;

  procedure DrawLogicYIndent;
  var
    J: Integer;
  begin
    for J := 0 to Rows.Count - 1 do
      with Rows[J] do
      begin
        RowHeight := Heights[J];
      {DrawLogicB is only called in nested tables if the outer table is calling DrawLogic2}
            DrawLogicB(Canvas, Y, CellSpacingHorz,CellSpacingVert, Curs);
        Inc(Y, RowHeight);
      end;
  end;

var
  TopY: Integer;
  FirstLinePtr: PInteger;
begin {TfrxHtmlTable.DrawLogic}
  Inc(Document.TableNestLevel);
  try
    if (CycleNumber <> Document.CycleNumber) or (YDraw <> Y) or (ContentTop <> Y) or (DrawTop <> Y) or (StartCurs <> Curs) or (Length(Heights) = 0)
    then
    begin
      YDraw := Y;
      TopY := Y;
      ContentTop := Y;
      DrawTop := Y;
      StartCurs := Curs;
      if Assigned(Document.FirstLineHtPtr) and {used for List items}
        (Document.FirstLineHtPtr^ = 0) then
        FirstLinePtr := Document.FirstLineHtPtr {save for later}
      else
        FirstLinePtr := nil;

      TableWidth := FindTableWidth;
      TableHeight := FindTableHeight;
      // Notice: SectionHeight = TableHeight
      Len := Curs - StartCurs;
      DrawHeight := TableHeight;
      ContentBot := TopY + TableHeight;
      DrawBot := TopY + DrawHeight;

      try
        if Assigned(FirstLinePtr) then
          FirstLinePtr^ := YDraw + SectionHeight;
      except
      end;

      CycleNumber := Document.CycleNumber;
    end
    else
    begin
      if Document.TableNestLevel = 1 then
        Document.InLogic2 := True;
      try
        if Document.InLogic2 then
          DrawLogicYIndent
        else
          Curs := StartCurs + Len;
      finally
        if Document.TableNestLevel = 1 then
          Document.InLogic2 := False;
      end;
    end;
  finally
    MaxWidth := TableWidth;
    Result := TableHeight;
    Dec(Document.TableNestLevel);
  end;
end;

{----------------TfrxHtmlTable.Draw}

function TfrxHtmlTable.Draw1(Canvas: TCanvas; const ARect: TRect; IMgr: TfrxHtIndentManager; X, XRef, YRef: Integer): Integer;

  procedure DrawTable(XX, YY, YOffset: Integer);
  var
    I: Integer;
  begin
    for I := 0 to Rows.Count - 1 do
      YY := Rows[I].Draw(Canvas, Document, ARect, Widths,
        XX, YY, YOffset, CellSpacingHorz,CellSpacingVert, BorderWidth > 0, BorderColorLight,
        BorderColorDark, I);
  end;

  procedure DrawTableP(XX, YY, YOffset: Integer);
  {Printing table with thead and/or tfoot}
  var
    TopBorder, BottomBorder: Integer;
    SavePageBottom: Integer;
    Spacing, HeightNeeded: Integer;

    procedure DrawNormal;
    var
      Y, I: Integer;
    begin
      Y := YY;
      Document.PrintingTable := Self;
      if Document.PageBottom - Y >= TableHeight + BottomBorder then
      begin
        for I := 0 to Rows.Count - 1 do {do whole table now}
          YY := Rows[I].Draw(Canvas, Document, ARect, Widths,
            XX, YY, YOffset, CellSpacingHorz,CellSpacingVert, BorderWidth > 0, BorderColorLight,
            BorderColorDark, I);
        Document.PrintingTable := nil;
      end
      else
      begin {see if enough room on this page for header, 1 row, footer}
        if HeadOrFoot then
        begin
          Spacing := CellSpacingVert div 2;
          HeightNeeded := HeaderHeight + FootHeight + Rows[HeaderRowCount].RowHeight;
          if (Y - YOffset > ARect.Top) and (Y + HeightNeeded > Document.PageBottom) and
            (HeightNeeded < ARect.Bottom - ARect.Top) then
          begin {not enough room, start table on next page}
            if YY + Spacing < Document.PageBottom then
            begin
              Document.PageShortened := True;
              Document.PageBottom := YY + Spacing;
            end;
            exit;
          end;
        end;
        {start table. it will not be complete and will go to next page}
        SavePageBottom := Document.PageBottom;
        Document.PageBottom := SavePageBottom - FootHeight - CellspacingVert - BottomBorder - 5; {a little to spare}
        for I := 0 to Rows.Count - 1 do {do part of table}
          YY := Rows[I].Draw(Canvas, Document, ARect, Widths,
            XX, YY, YOffset, CellSpacingHorz, CellSpacingVert, BorderWidth > 0, BorderColorLight,
            BorderColorDark, I);
        BodyBreak := Document.PageBottom;
        if FootStartRow >= 0 then
        begin
          TablePartRec.TablePart := DoFoot;
          TablePartRec.PartStart := Y + FootOffset;
          TablePartRec.PartHeight := FootHeight + Max(2 * CellspacingVert, CellspacingVert + 1) + BottomBorder;
          Document.TheOwner.TablePartRec := TablePartRec;
        end
        else if HeaderHeight > 0 then
        begin {will do header next}
          //Document.PageBottom := SavePageBottom;
          TablePartRec.TablePart := DoHead;
          TablePartRec.PartStart := Y - TopBorder;
          TablePartRec.PartHeight := HeaderHeight + TopBorder;
          Document.TheOwner.TablePartRec := TablePartRec;
        end;
        Document.TheOwner.TablePartRec := TablePartRec;
      end;
    end;

    procedure DrawBody1;
    var
      Y, I: Integer;
    begin
      Y := YY;
      if Document.PageBottom > Y + TableHeight + BottomBorder then
      begin {can complete table now}
        for I := 0 to Rows.Count - 1 do {do remainder of table now}
          YY := Rows[I].Draw(Canvas, Document, ARect, Widths,
            XX, YY, YOffset, CellSpacingHorz, CellSpacingVert, BorderWidth > 0, BorderColorLight,
            BorderColorDark, I);
        Document.TheOwner.TablePartRec.TablePart := Normal;
      end
      else
      begin {will do part of the table now}
    {Leave room for foot later}
        Document.PageBottom := Document.PageBottom - FootHeight + Max(CellspacingVert, 1) - BottomBorder;
        for I := 0 to Rows.Count - 1 do
          YY := Rows[I].Draw(Canvas, Document, ARect, Widths,
            XX, YY, YOffset, CellSpacingHorz, CellSpacingVert, BorderWidth > 0, BorderColorLight,
            BorderColorDark, I);
        BodyBreak := Document.PageBottom;
        if FootStartRow >= 0 then
        begin
          TablePartRec.TablePart := DoFoot;
          TablePartRec.PartStart := Y + FootOffset;
          TablePartRec.PartHeight := FootHeight + Max(2 * CellspacingVert, CellspacingVert + 1) + BottomBorder;
          Document.TheOwner.TablePartRec := TablePartRec;
        end
        else if HeaderHeight > 0 then
        begin
          TablePartRec.TablePart := DoHead;
          TablePartRec.PartStart := Y - TopBorder;
          TablePartRec.PartHeight := HeaderHeight + TopBorder;
          Document.TheOwner.TablePartRec := TablePartRec;
        end;
        Document.TheOwner.TablePartRec := TablePartRec;
      end;
    end;

    procedure DrawBody2;
    var
      Y, I: Integer;
    begin
      Y := YY;
      if Document.PageBottom > Y + TableHeight + BottomBorder then
      begin
        for I := 0 to Rows.Count - 1 do {do remainder of table now}
          YY := Rows[I].Draw(Canvas, Document, ARect, Widths,
            XX, YY, YOffset, CellSpacingHorz,CellSpacingVert, BorderWidth > 0, BorderColorLight,
            BorderColorDark, I);
        Document.TheOwner.TablePartRec.TablePart := Normal;
        Document.PrintingTable := nil;
      end
      else
      begin
        SavePageBottom := Document.PageBottom;
        for I := 0 to Rows.Count - 1 do {do part of table}
          YY := Rows[I].Draw(Canvas, Document, ARect, Widths,
            XX, YY, YOffset, CellSpacingHorz,CellSpacingVert, BorderWidth > 0, BorderColorLight,
            BorderColorDark, I);
        BodyBreak := Document.PageBottom;
        if FootStartRow >= 0 then
        begin
          TablePartRec.TablePart := DoFoot;
          TablePartRec.PartStart := Y + FootOffset;
          TablePartRec.PartHeight := FootHeight + Max(2 * CellspacingVert, CellspacingVert + 1) + BottomBorder;
          Document.TheOwner.TablePartRec := TablePartRec;
        end
        else if HeaderHeight > 0 then
        begin
          Document.PageBottom := SavePageBottom;
          TablePartRec.TablePart := DoHead;
          TablePartRec.PartStart := Y - TopBorder;
          TablePartRec.PartHeight := HeaderHeight + TopBorder;
          Document.TheOwner.TablePartRec := TablePartRec;
        end;
        Document.TheOwner.TablePartRec := TablePartRec;
      end;
    end;

    procedure DrawFoot;
    var
      Y, I: Integer;
    begin
      Y := YY;
      YY := TablePartRec.PartStart;
      if FootStartRow >= 0 then
        for I := FootStartRow to Rows.Count - 1 do
          YY := Rows[I].Draw(Canvas, Document, ARect, Widths,
            XX, YY, YOffset, CellSpacingHorz, CellSpacingVert, BorderWidth > 0, BorderColorLight,
            BorderColorDark, I);
      if HeaderHeight > 0 then
      begin
        TablePartRec.TablePart := DoHead;
        TablePartRec.PartStart := Y - TopBorder;
        TablePartRec.PartHeight := HeaderHeight + TopBorder;
      end
      else
      begin {No THead}
        TablePartRec.TablePart := DoBody3;
        TablePartRec.PartStart := BodyBreak - 1;
        TablePartRec.FootHeight := FootHeight + Max(CellspacingVert, 1);
      end;
      Document.TheOwner.TablePartRec := TablePartRec;
    end;

    procedure DrawHead;
    var
      I: Integer;
    begin
      for I := 0 to HeaderRowCount - 1 do
        YY := Rows[I].Draw(Canvas, Document, ARect, Widths,
          XX, YY, YOffset, CellSpacingHorz,CellSpacingVert, BorderWidth > 0, BorderColorLight,
          BorderColorDark, I);
      TablePartRec.TablePart := DoBody1;
      TablePartRec.PartStart := BodyBreak - 1;
      TablePartRec.FootHeight := FootHeight + Max(CellspacingVert, 1) + BottomBorder;
      Document.TheOwner.TablePartRec := TablePartRec;
    end;

  begin
    if TfrxHtTableBlock(OwnerBlock).TableBorder then
    begin
      TopBorder := BorderWidth;
      BottomBorder := BorderWidth;
    end
    else
    begin
      TopBorder := OwnerBlock.MargArray[BorderTopWidth];
      BottomBorder := OwnerBlock.MargArray[BorderBottomWidth];
    end;

    case TablePartRec.TablePart of
      Normal:   DrawNormal;
      DoBody1:  DrawBody1;
      DoBody2:  DrawBody2;
      DoFoot:   DrawFoot;
      DoHead:   DrawHead;
    end;
  end;

var
  Y, YO, YOffset: Integer;
begin
  Inc(Document.TableNestLevel);
  try
    Y := YDraw;
    Result := Y + SectionHeight;
    YOffset := Document.YOff;
    YO := Y - YOffset;

    DrawX := X;
    //DrawY := Y;

    if (YO + DrawHeight >= ARect.Top) and (YO < ARect.Bottom) then
      DrawTable(X, Y, YOffset);
  finally
    Dec(Document.TableNestLevel);
  end;
end;

{----------------TfrxHtmlTable.GetChAtPos}

function TfrxHtmlTable.GetChAtPos(Pos: Integer; out Ch: WideChar; out Obj: TfrxHtSectionBase): Boolean;
var
  I, J: Integer;
  Row: TfrxHtCellList;
begin
  Obj := nil;
  if (len > 0) and (Pos >= StartCurs) and (Pos < StartCurs + Len) then
    for J := 0 to Rows.Count - 1 do
    begin
      Row := Rows[J];
      for I := 0 to Row.Count - 1 do
      begin
        if Row[I] is TfrxHtCellObj then
        begin
          Result := TfrxHtCellObj(Row[I]).Cell.GetChAtPos(Pos, Ch, Obj);
          if Result then
            Exit;
        end;
      end;
    end;

  Result := False;
end;

{----------------TfrxHtmlTable.FindString}

function TfrxHtmlTable.FindString(From: Integer; const ToFind: UnicodeString; MatchCase: Boolean): Integer;
var
  I, J: Integer;
  Row: TfrxHtCellList;
begin
  for J := 0 to Rows.Count - 1 do
  begin
    Row := Rows[J];
    for I := 0 to Row.Count - 1 do
    begin
      if Row[I] is TfrxHtCellObj then
      begin
        Result := TfrxHtCellObj(Row[I]).Cell.FindString(From, ToFind, MatchCase);
        if Result >= 0 then
          Exit;
      end;
    end;
  end;
  Result := -1;
end;

{----------------TfrxHtmlTable.FindStringR}

function TfrxHtmlTable.FindStringR(From: Integer; const ToFind: UnicodeString; MatchCase: Boolean): Integer;
var
  I, J: Integer;
  Row: TfrxHtCellList;
begin
  for J := Rows.Count - 1 downto 0 do
  begin
    Row := Rows[J];
    for I := Row.Count - 1 downto 0 do
    begin
      if Row[I] is TfrxHtCellObj then
      begin
        Result := TfrxHtCellObj(Row[I]).Cell.FindStringR(From, ToFind, MatchCase);
        if Result >= 0 then
          Exit;
      end;
    end;
  end;
  Result := -1;
end;

{----------------TfrxHtmlTable.FindSourcePos}

function TfrxHtmlTable.FindSourcePos(DocPos: Integer): Integer;
var
  I, J: Integer;
  Row: TfrxHtCellList;
begin
  for J := 0 to Rows.Count - 1 do
  begin
    Row := Rows[J];
    for I := 0 to Row.Count - 1 do
    begin
      if Row[I] is TfrxHtCellObj then
      begin
        Result := TfrxHtCellObj(Row[I]).Cell.FindSourcePos(DocPos);
        if Result >= 0 then
          Exit;
      end;
    end;
  end;
  Result := -1;
end;

{----------------TfrxHtmlTable.FindDocPos}

function TfrxHtmlTable.FindDocPos(SourcePos: Integer; Prev: Boolean): Integer;
var
  I, J: Integer;
  Row: TfrxHtCellList;
begin
  if not Prev then
    for J := 0 to Rows.Count - 1 do
    begin
      Row := Rows[J];
      if Row <> nil then
        for I := 0 to Row.Count - 1 do
        begin
          if Row[I] is TfrxHtCellObj then
          begin
            Result := TfrxHtCellObj(Row[I]).Cell.FindDocPos(SourcePos, Prev);
            if Result >= 0 then
              Exit;
          end;
        end;
    end
  else {Prev , iterate in reverse}
    for J := Rows.Count - 1 downto 0 do
    begin
      Row := Rows[J];
      if Row <> nil then
        for I := Row.Count - 1 downto 0 do
        begin
          if Row[I] is TfrxHtCellObj then
          begin
            Result := TfrxHtCellObj(Row[I]).Cell.FindDocPos(SourcePos, Prev);
            if Result >= 0 then
              Exit;
          end;
        end;
    end;
  Result := -1;
end;


//-- BG ---------------------------------------------------------- 01.12.2013 --
constructor TfrxHtSection.Create(Parent: TfrxHtCellBasic);
begin
  inherited Create(Parent, nil, nil);
  FDisplay := pdInline; // A section is always inline as the section is intended to handle consecutive inline elements.
  FBuff := PWideChar(BuffS);
  Fonts := TfrxHtFontList.Create;
  Images := TfrxHtSizeableObjList.Create;
  Lines := TfrxHtLineRecList.Create;
end;

{----------------TfrxHtSection.Create}

constructor TfrxHtSection.Create(Parent: TfrxHtCellBasic; Attr: TfrxHtAttributeList; Prop: TfrxHTProperties; AnURL: TfrxHtUrlTarget; FirstItem: Boolean);
var
  FO: TfrxHtFontObj;
//  T: TfrxHtAttribute;
  S, C: ThtString;
//  Clr: ThtClearStyle;
  Percent: Boolean;
begin
  if Attr <> nil then
  begin
    S := Attr.TheID;
    C := Attr.TheClass;
  end;
  inherited Create(Parent, Attr, Prop, S, C);
  if FDisplay = pdUnassigned then
    FDisplay := pdInline;
  FBuff := PWideChar(BuffS);
  Fonts := TfrxHtFontList.Create;
  Images := TfrxHtSizeableObjList.Create;
  Lines := TfrxHtLineRecList.Create;

  FO := TfrxHtFontObj.Create(Self, Prop.Font, 0);
  FO.Title := Prop.PropTitle;
  if Assigned(AnURL) and (Length(AnURL.Url) > 0) then
  begin
    FO.CreateFIArray;
    Prop.GetFontInfo(FO.FIArray);
    FO.ConvertFont(FO.FIArray.Info);
    FO.UrlTarget.Assign(AnUrl);
    Document.LinkList.Add(FO);
  end;
  Fonts.Add(FO);

  LineHeight := Prop.GetLineHeight(Abs(FO.TheFont.Height));
  if FirstItem then
  begin
    FirstLineIndent := Prop.GetTextIndent(Percent);
    if Percent then
      FLPercent := Min(FirstLineIndent, 90);
  end;

  if VarIsEmpty(Prop.Props[TextDirection]) then
    Direction := diLTR
  else if Prop.Props[TextDirection] = 'rtl' then
    Direction := diRTL
  else if Prop.Props[TextDirection] = 'auto' then
    Direction := diAuto
  else
    Direction := diLTR;

  if VarIsEmpty(Prop.Props[TextAlign]) then // check for unassigned
    SetJustifyByDirection
  else if Prop.Props[TextAlign] = 'right' then
    Justify := Right
  else if Prop.Props[TextAlign] = 'center' then
    Justify := Centered
  else if Prop.Props[TextAlign] = 'justify' then
    Justify := FullJustify
  else
    Justify := Left;

  BreakWord := Prop.Props[WordWrap] = 'break-word';

  if Self is TfrxHtPreFormated then
    WhiteSpaceStyle := wsPre
  else if Document.NoBreak then
    WhiteSpaceStyle := wsNoWrap
  else
    WhiteSpaceStyle := wsNormal;
  if VarIsOrdinal(Prop.Props[piWhiteSpace]) then
    WhiteSpaceStyle := ThtWhiteSpaceStyle(Prop.Props[piWhiteSpace])
  else if VarIsStr(Prop.Props[piWhiteSpace]) then
  begin
    if not TryStrToWhiteSpace(Prop.Props[piWhiteSpace],WhiteSpaceStyle) then
    begin
    end;
//    if Prop.Props[piWhiteSpace] = 'pre' then
//      WhiteSpaceStyle := wsPre
//    else if Prop.Props[piWhiteSpace] = 'nowrap' then
//      WhiteSpaceStyle := wsNoWrap
//    else if Prop.Props[piWhiteSpace] = 'pre-wrap' then
//      WhiteSpaceStyle := wsPreWrap
//    else if Prop.Props[piWhiteSpace] = 'pre-line' then
//      WhiteSpaceStyle := wsPreLine
//    else if Prop.Props[piWhiteSpace] = 'normal' then
 //     WhiteSpaceStyle := wsNormal;
  end;
end;

{----------------TfrxHtSection.CreateCopy}

constructor TfrxHtSection.CreateCopy(Parent: TfrxHtCellBasic; Source: TfrxHtmlNode);
var
  T: TfrxHtSection absolute Source;
begin
  inherited CreateCopy(Parent,Source);
  Len := T.Len;
  BuffS := T.BuffS;
  SetLength(BuffS, Length(BuffS));
  FBuff := PWideChar(BuffS);
  Brk := T.Brk;
  Fonts := TfrxHtFontList.CreateCopy(Self, T.Fonts);
  //TODO -oBG, 24.03.2011: TfrxHtSection has no Cell, but owns images. Thus Parent must be a TfrxHtmlNode.
  //  and TfrxHtDocument should become a TfrxHtBodyBlock instead of a SectionList.
  Images := TfrxHtSizeableObjList.CreateCopy(Parent {must be Self}, T.Images);
  Lines := TfrxHtLineRecList.Create;
  Justify := T.Justify;
  JustifyByDirection := T.JustifyByDirection;
  Direction := T.Direction;
  LineHeight := T.LineHeight;
  FirstLineIndent := T.FirstLineIndent;
  FLPercent := T.FLPercent;
  BreakWord := T.BreakWord;
  WhiteSpaceStyle := T.WhiteSpaceStyle;
end;

{----------------TfrxHtSection.Destroy}

destructor TfrxHtSection.Destroy;
var
  i: Integer;
begin
  { Yunqa.de: Do not leave references to deleted font objects in the
    HtmlViewer's link list. Otherwise TfrxHtUrlTarget.SetLast might see an access
    violation. }
  if Document <> nil then
    if Document.LinkList <> nil then
      for i := 0 to Fonts.Count - 1 do
        Document.LinkList.Remove(Fonts[i]);

  SIndexList.Free;
  Lines.Free;
  Images.Free;
  Fonts.Free;
  inherited Destroy;
end;

procedure TfrxHtSection.CheckFree;
var
  I, J: Integer;
begin
  if not Assigned(Self) then
    Exit;
  if Assigned(Document) then
  begin
  {Check to see that there isn't a TfrxHtFontObj in LinkList}
    if Assigned(Document.LinkList) then
      for I := 0 to Fonts.Count - 1 do
      begin
        J := Document.LinkList.IndexOf(Fonts[I]);
        if J >= 0 then
          Document.LinkList.Delete(J);
      end;
  {Remove Self from IDNameList if there}
    if Assigned(Document.IDNameList) then
      with Document.IDNameList do
      begin
        I := IndexOfObject(Self);
        if I > -1 then
          Delete(I);
      end;
  end;
end;

{----------------TfrxHtSection.AddChar}

procedure TfrxHtSection.AddChar(C: WideChar; Index: Integer);
var
  Tok: TfrxHtTokenObj;
begin
  Tok := TfrxHtTokenObj.Create;
  Tok.AddUnicodeChar(C, Index);
  AddTokenObj(Tok);
  Tok.Free;
end;

function TfrxHtSection.GetIndexObj(I: Integer): TfrxHtIndexObj;
begin
  Result := SIndexList[I];
end;

function TfrxHtSection.GetXPLen: Integer;
begin
  Result := Length(XP);
end;

procedure TfrxHtSection.AddOpBrk;
var
  L: Integer;
begin
  L := Length(Brk);
  if L > 0 then
    Brk[L - 1] := twOptional;
end;

{----------------TfrxHtSection.AddTokenObj}

procedure TfrxHtSection.AddTokenObj(T: TfrxHtTokenObj);
var
  L, I, J: Integer;
  C: ThtTextWrap;
  St, StU: ThtString;
  Small: Boolean;
  LastProps: TfrxHTProperties;
begin
  if T.Count = 0 then
    Exit;

  { Yunqa.de: Simple hack to support  }
  LastProps := Document.PropStack.Last;
  if (LastProps.Display = pdNone) and (LastProps.PropSym in [
    SpanSy, NoBrSy, WbrSy, FontSy, BSy, ISy, SSy, StrikeSy, USy, SubSy, SupSy, BigSy, SmallSy, TTSy,
    EmSy, StrongSy, CodeSy, KbdSy, SampSy, DelSy, InsSy, CiteSy, VarSy, MarkSy, TimeSy, ASy])
  then
    Exit;

  case LastProps.GetTextTransform of
    txUpper:
      St := htUpperCase(T.S);
    txLower:
      St := htLowerCase(T.S);
  else
    St := T.S;
  end;

  L := Len + T.Count;
  if Length(XP) < L + 3 then
    Allocate(L + 500); {L+3 to permit additions later}

  // add positions in source
  Move(T.I[1], XP[Len], T.Count * Sizeof(Integer));

  // BG, 31.08.2011: added: WhiteSpaceStyle
  if Document.NoBreak or (WhiteSpaceStyle in [wsPre, wsPreLine, wsNoWrap]) then
    C := twNo
  else
    C := twYes;

  J := Length(Brk);
  SetLength(Brk, J + T.Count);
  for I := J to J + T.Count - 1 do
    Brk[I] := C;

  if LastProps.GetFontVariant = 'small-caps' then
  begin
    StU := htUpperCase(St);
    BuffS := BuffS + StU;
    Small := False;
    for I := 1 to Length(St) do
    begin
      case St[I] of
        WideChar(' '), WideChar('0')..WideChar('9'):
          {no font changes for these chars}
          ;
      else
        if not Small then
        begin
          if StU[I] <> St[I] then
          begin {St[I] was lower case}
            Document.PropStack.PushNewProp(SmallSy, nil, nil); {change to smaller font}
            ChangeFont(Document.PropStack.Last);
            Small := True;
          end;
        end
        else if StU[I] = St[I] then
        begin {St[I] was uppercase and Small is set}
          Document.PropStack.PopAProp(SmallSy);
          ChangeFont(Document.PropStack.Last);
          Small := False;
        end;
      end;
      Inc(Len);
    end;
    if Small then {change back to regular font}
    begin
      Document.PropStack.PopAProp(SmallSy);
      ChangeFont(Document.PropStack.Last);
    end;
  end
  else
  begin
    BuffS := BuffS + St;
    Len := L;
  end;
  FBuff := PWideChar(BuffS);
  SureDirection(FBuff, Len);
end;

{----------------TfrxHtSection.ProcessText}

procedure TfrxHtSection.ProcessText(TagIndex: Integer);
const
  Shy = #173; {soft hyphen}
var
  I: Integer;
  FO: TfrxHtFontObj;

  procedure Remove(I: Integer);
  var L: Integer;
  begin
    L := Length(BuffS) - I;
    if L > 0 then
       Move(XP[I], XP[I - 1], L * Sizeof(Integer));
    L := Length(Brk) - I;
    if L > 0 then
        Move(Brk[I], Brk[I - 1], L * Sizeof(ThtTextWrap));
    SetLength(Brk, Length(Brk) - 1);
    System.Delete(BuffS, I, 1);
    Fonts.Decrement(I - 1, Document);
    Images.Decrement(I - 1);
  end;

begin
  if WhiteSpaceStyle in [wsPre] then
  begin
    FO := TfrxHtFontObj(Fonts.Items[Fonts.Count - 1]); {keep font the same for inserted space}
    if FO.Pos = Length(BuffS) then
      Inc(FO.Pos);
    BuffS := BuffS + ' ';
    Allocate(Length(BuffS) + 500);
    XP[Length(BuffS) - 1] := XP[Length(BuffS) - 2] + 1;
  end
  else
  begin
    while (Length(BuffS) > 0) and (BuffS[1] = ' ') do
      Remove(1);

    I := WidePos(Shy, BuffS);
    while I > 0 do
    begin
      Remove(I);
      if (I > 1) and (Brk[I - 2] <> twNo) then
        Brk[I - 2] := twSoft;
      I := WidePos(Shy, BuffS);
    end;

    if WhiteSpaceStyle in [wsNormal, wsNoWrap, wsPreLine] then
    begin
      while (Length(BuffS) > 0) and (BuffS[1] = ' ') do
        Remove(1);

      I := WidePos('  ', BuffS);
      while I > 0 do
      begin
        if Brk[I - 1] = twNo then
          Remove(I)
        else
          Remove(I + 1);
        I := WidePos('  ', BuffS);
      end;

      {After floating images at start, delete an annoying space}
      for I := Length(BuffS) - 1 downto 1 do
        if (BuffS[I] = ImgPan) and (Images.FindObject(I - 1).Floating in [ALeft, ARight])
          and (BuffS[I + 1] = ' ') then
          Remove(I + 1);

      I := WidePos(UnicodeString(' '#8), BuffS); {#8 is break char}
      while I > 0 do
      begin
        Remove(I);
        I := WidePos(UnicodeString(' '#8), BuffS);
      end;

      I := WidePos(UnicodeString(#8' '), BuffS);
      while I > 0 do
      begin
        Remove(I + 1);
        I := WidePos(UnicodeString(#8' '), BuffS);
      end;

      if (Length(BuffS) > 1) and (BuffS[Length(BuffS)] = #8) then
        Remove(Length(BuffS));

      if (Length(BuffS) > 1) and (BuffS[Length(BuffS)] = ' ') then
        Remove(Length(BuffS));

      if (BuffS <> #8) and (Length(BuffS) > 0) and (BuffS[Length(BuffS)] <> ' ') then
      begin
        FO := TfrxHtFontObj(Fonts.Items[Fonts.Count - 1]); {keep font the same for inserted space}
        if FO.Pos = Length(BuffS) then
          Inc(FO.Pos);
        BuffS := BuffS + ' ';
        //XP[Length(BuffS) - 1] := TagIndex;
      end;
    end;
  end;
  Finish;
end;

procedure TfrxHtSection.SetJustifyByDirection;
begin
  JustifyByDirection := True;
  if      Direction = diRTL then
    Justify := Right
  else if Direction = diLTR then
    Justify := Left;
end;

procedure TfrxHtSection.SureDirection(WC: PWideChar; Len: Integer);
begin
  if Direction = diAuto then
  begin
    Direction := DirectionByText(WC, Len);

    if JustifyByDirection then
      SetJustifyByDirection;
  end;
end;

{----------------TfrxHtSection.Finish}

procedure TfrxHtSection.Finish;
{complete some things after all information added}
var
  Last, I: Integer;
  IO: TfrxHtIndexObj;
begin
  FBuff := PWideChar(BuffS);
  Len := Length(BuffS);
  if Len > 0 then
  begin
    SetLength(Brk, Length(Brk) + 1);
    Brk[Length(Brk) - 1] := twYes;

    if not IsCopy then
    begin
      Last := 0; {to prevent warning msg}
      SIndexList := TfrxHtIndexObjList.Create;
      for I := 0 to Len - 1 do
      begin
        if (I = 0) or (XP[I] <> Last + 1) then
        begin
          IO := TfrxHtIndexObj.Create;
          IO.Pos := I;
          IO.Index := XP[I];
          SIndexList.Add(IO);
        end;
        Last := XP[I];
      end;
      SetLength(XP, 0);
    end;

    Inc(Document.SectionCount);
    SectionNumber := Document.SectionCount;
  end;
end;

{----------------TfrxHtSection.Allocate}

procedure TfrxHtSection.Allocate(N: Integer);
begin
  if Length(XP) < N then
    SetLength(XP, N);
end;

{----------------TfrxHtSection.ChangeFont}

procedure TfrxHtSection.ChangeFont(Prop: TfrxHTProperties);
var
  FO: TfrxHtFontObj;
  LastUrl: TfrxHtUrlTarget;
  Align: ThtAlignmentStyle;
begin
  FO := Fonts[Fonts.Count - 1];
  LastUrl := FO.UrlTarget;
  if FO.Pos = Len then
    FO.ReplaceFont(Prop.Font) {fontobj already at this position, modify it}
  else
  begin
    FO := TfrxHtFontObj.Create(Self, Prop.Font, Len);
    FO.URLTarget.Assign(LastUrl);
    Fonts.Add(FO);
  end;
  FO.Title := Prop.PropTitle;
  if LastUrl.Url <> '' then
  begin
    FO.CreateFIArray;
    Prop.GetFontInfo(FO.FIArray);
    FO.ConvertFont(FO.FIArray.Info);
    if Document.LinkList.IndexOf(FO) = -1 then
      Document.LinkList.Add(FO);
  end;
  if Prop.GetVertAlign(Align) and (Align in [ASub, ASuper]) then
    FO.SScript := Align
  else
    FO.SScript := ANone;
end;

{----------------------TfrxHtSection.HRef}

procedure TfrxHtSection.HRef(IsHRef: Boolean; List: TfrxHtDocument; AnURL: TfrxHtUrlTarget;
  Attributes: TfrxHtAttributeList; Prop: TfrxHTProperties);
var
  FO: TfrxHtFontObj;
  Align: ThtAlignmentStyle;
begin
  FO := Fonts[Fonts.Count - 1];
  if FO.Pos = Len then
    FO.ReplaceFont(Prop.Font) {fontobj already at this position, modify it}
  else
  begin
    FO := TfrxHtFontObj.Create(Self, Prop.Font, Len);
    Fonts.Add(FO);
  end;

  if IsHRef then
  begin
    FO.CreateFIArray;
    Prop.GetFontInfo(FO.FIArray);
    FO.ConvertFont(FO.FIArray.Info);
    if Document.LinkList.IndexOf(FO) = -1 then
      Document.LinkList.Add(FO);
  end
  else if Assigned(FO.FIArray) then
  begin
    FO.FIArray.Free;
    FO.FIArray := nil;
  end;
  FO.UrlTarget.Assign(AnUrl);
  if Prop.GetVertAlign(Align) and (Align in [ASub, ASuper]) then
    FO.SScript := Align
  else
    FO.SScript := ANone;
end;

function TfrxHtSection.AddImage(L: TfrxHtAttributeList; ACell: TfrxHtCellBasic; Index: Integer; Prop: TfrxHTProperties): TfrxFrHtImageObj;
begin
  Result := TfrxFrHtImageObj.Create(ACell, Len, L, Prop);
  Images.Add(Result);
  AddChar(ImgPan, Index); {marker for image}
end;

{----------------TfrxHtSection.FindCountThatFits}

function TfrxHtSection.FindCountThatFits(Canvas: TCanvas; Width: Integer; Start: PWideChar; Max: Integer): Integer;
{Given a width, find the count of chars (<= Max) which will fit allowing for
 font changes.  Line wrapping will be done later}
//BG, 06.02.2011: Why are there 2 methods and why can't GetURL and FindCursor use the formatting results of DrawLogic?
//  TfrxHtSection.FindCountThatFits1() is used in TfrxHtSection.DrawLogic().
//  TfrxHtSection.FindCountThatFits() is used in TfrxHtSection.GetURL() and TfrxHtSection.FindCursor().
var
  Cnt, XX, YY, I, J, J1, J2: Integer;
  Picture: Boolean;
  FlObj: TfrxHtFloatingObj; //TfrxHtSizeableObj;
  FO: TfrxHtFontObj;
  Extent: TSize;
const
  OldStart: PWideChar = nil;
  OldResult: Integer = 0;
  OldWidth: Integer = 0;

begin
  if (Width = OldWidth) and (Start = OldStart) then
  begin
    Result := OldResult;
    Exit;
  end;
  OldStart := Start;
  OldWidth := Width;
  Cnt := 0;
  XX := 0;
  YY := 0;
  while True do
  begin
    //Fonts.GetFontAt(Start - Buff, OHang).AssignToCanvas(Canvas);
    J1 := Fonts.GetFontObjAt(Start - Buff, Len, FO);
    FO.TheFont.AssignToCanvas(Canvas);
    J2 := Images.GetObjectAt(Start - Buff, FlObj);
    if (J2 = 0) then
    begin
      I := 1; J := 1;
      Picture := True;
      if FlObj.IsInFlow then
      begin
        Inc(XX, FlObj.TotalWidth);
        if XX > Width then
          break;
      end;
    end
    else
    begin
      Picture := False;
      J := Min(J1, J2);
      I := FitText(Canvas.Handle, Start, J, Width - XX, Extent);
    end;

    if Cnt + I >= Max then {I has been initialized}
    begin
      Cnt := Max;
      Break;
    end
    else
      Inc(Cnt, I);

    if not Picture then
    begin
      if (I < J) or (I = 0) then
        Break;
      XX := XX + Extent.cx;
      YY := Math.Max(YY, Extent.cy);
    end;

    Inc(Start, I);
  end;
  Result := Cnt;
  OldResult := Result;
end;

function WrapChar(C: WideChar): Boolean;
 {$ifdef UseInline} inline; {$endif}
begin
  Result := Ord(C) >= $3000;
end;

//-- BG ---------------------------------------------------------- 27.01.2012 --
function CanWrapAfter(PC: PWideChar): Boolean;
 {$ifdef UseInline} inline; {$endif}
begin
  case PC^ of
    WideChar('-'):
      case (PC+1)^ of
        WideChar('0')..WideChar('9'):
          Result := False;
      else
        Result := True;
      end;

    WideChar('/'), WideChar('?'):
      Result := True
  else
    Result := False;
  end;
end;

//-- BG ---------------------------------------------------------- 20.09.2010 --
function CanWrap(PC: PWideChar): Boolean;
 {$ifdef UseInline} inline; {$endif}
begin
  case PC^ of
    WideChar('-'):
      case (PC+1)^ of
        WideChar('0')..WideChar('9'):
          Result := False;
      else
        Result := True;
      end;

    WideChar(' '), WideChar('/'), WideChar('?'), ImgPan, FmCtl, BrkCh:
      Result := True
  else
    Result := WrapChar(PC^);
  end;
end;

{----------------TfrxHtSection.MinMaxWidth}

procedure TfrxHtSection.MinMaxWidth(Canvas: TCanvas; out Min, Max: Integer; AvailableWidth, AvailableHeight: Integer);
{Min is the width the section would occupy when wrapped as tightly as possible.
 Max, the width if no wrapping were used.}

  procedure MinMaxWidthOfBlocks(Objects: TFloatingObjList);
  var
    I: Integer;
    Obj: TfrxHtFloatingObj;
  begin
    for I := 0 to Objects.Count - 1 do {call drawlogic for all the objects}
    begin
      Obj := Objects[I];
      Obj.DrawLogicInline(Canvas, Fonts.GetFontObjAt(Obj.StartCurs), AvailableWidth, AvailableHeight);
      if not Obj.PercentWidth then
      begin
        if Obj.Positioning in [posAbsolute, posFixed] then
        begin
          // does not affect the block width
          Brk[Obj.StartCurs] := twYes; {allow break after positioned object}
        end
        else if Obj.Floating in [ALeft, ARight] then
        begin
          Inc(Max, Obj.TotalWidth);
          Brk[Obj.StartCurs] := twYes; {allow break after floating object}
          Min := Math.Max(Min, Obj.TotalWidth);
        end
        else
          Min := Math.Max(Min, Obj.ClientWidth);
      end;
    end;
  end;

var
  SoftHyphen: Boolean;

  function FindTextWidthB(Canvas: TCanvas; Start: PWideChar; N: Integer; RemoveSpaces: Boolean): TSize;
  begin
    Result := FindTextSize(Canvas, Start, N, RemoveSpaces);
    if Start = Buff then
      if FLPercent = 0 then {not a percent}
        Inc(Result.cx, FirstLineIndent)
      else
        Result.cx := (100 * Result.cx) div (100 - FLPercent);
    if SoftHyphen then
      Inc(Result.cx, Canvas.TextWidth('-'));
  end;

var
  I, FloatMin: Integer;
  P, P1: PWideChar;
begin
  Min := 0;
  Max := 0;
  if Len = 0 then
    Exit;

  if not BreakWord and (WhiteSpaceStyle in [wsPre, wsNoWrap]) then
  begin
    if StoredMax.cx = 0 then
    begin
      StoredMax := FindTextSize(Canvas, Buff, Len - 1, False);
      Max := StoredMax.cx;
    end
    else
      Max := StoredMax.cx;
    Min := Math.Min(MaxHScroll, Max);
    Exit;
  end;

  if (StoredMin.cx > 0) and (Images.Count = 0) then
  begin
    Min := StoredMin.cx;
    Max := StoredMax.cx;
    Exit;
  end;

  MinMaxWidthOfBlocks(Images);
  FloatMin := Min;

  SoftHyphen := False;
  P := Buff;
  P1 := StrScanW(P, BrkCh); {look for break char}
  while Assigned(P1) do
  begin
    Max := Math.Max(Max, FindTextWidthB(Canvas, P, P1 - P, False).cx);
    P := P1 + 1;
    P1 := StrScanW(P, BrkCh);
  end;
  P1 := StrScanW(P, #0); {look for the end}
  Inc(Max, FindTextWidthB(Canvas, P, P1 - P, True).cx); // + FloatMin;

  P := Buff;
  if not BreakWord then
  begin
    while P^ = ' ' do
      Inc(P);
    P1 := P;
    I := P1 - Buff + 1;
    while P^ <> #0 do
    {find the next string of chars that can't be wrapped}
    begin
      SoftHyphen := False;
      if CanWrap(P1) and (Brk[I - 1] = twYes) then
      begin
        Inc(P1);
        Inc(I);
      end
      else
      begin
        repeat
          Inc(P1);
          Inc(I);
          case Brk[I - 2] of
            twSoft, twOptional:
              break;
          end;
        until (P1^ = #0) or (CanWrap(P1) and (Brk[I - 1] = twYes));
        SoftHyphen := Brk[I - 2] = twSoft;
        if CanWrapAfter(P1) then
        begin
          Inc(P1);
          Inc(I);
        end;
      end;
      Min := Math.Max(Min, FindTextWidthB(Canvas, P, P1 - P, True).cx);
      while True do
        case P1^ of
          WideChar(' '), ImgPan, FmCtl, BrkCh:
          begin
            Inc(P1);
            Inc(I);
          end;
        else
          break;
        end;
      P := P1;
    end;
  end
  else
    while P^ <> #0 do
    begin
      Min := Math.Max(Min, FindTextWidthB(Canvas, P, 1, True).cx);
      Inc(P);
    end;

  Min := Math.Max(FloatMin, Min);
  StoredMin.cx := Min;
  StoredMax.cx := Max;
  StoredMin.cy := 0;
  StoredMax.cy := 0;
end;

{----------------TfrxHtSection.FindTextWidth}

function TfrxHtSection.FindTextSize(Canvas: TCanvas; Start: PWideChar; N: Integer; RemoveSpaces: Boolean): TSize;
{find actual line width of N chars starting at Start.  If RemoveSpaces set,
 don't count spaces on right end}
var
  I, J: Integer;
  FlObj: TfrxHtFloatingObj; //TfrxHtSizeableObj;
  FO: TfrxHtFontObj;
begin
  Result.cx := 0;
  Result.cy := 0;
  if RemoveSpaces then
    while True do
      case (Start + N - 1)^ of
        SpcChar,
        BrkCh:
          Dec(N); {remove spaces on end}
      else
        break;
      end;
  while N > 0 do
  begin
    J := Images.GetObjectAt(Start - Buff, FlObj);
    if (J = 0) then {it's an image or a form control}
    begin
    {Here we count floating images as 1 ThtChar but do not include their width,
      This is required for the call in FindCursor}
      if FlObj.IsInFlow then
      begin
        Inc(Result.cx, FlObj.TotalWidth);
        Result.cy := Max(Result.cy, FlObj.TotalHeight);
      end;
      Dec(N); {image counts as one ThtChar}
      Inc(Start);
    end
    else
    begin
      //Fonts.GetFontAt(Start - Buff, OHang).AssignToCanvas(Canvas);
      I := Min(J, Min(Fonts.GetFontObjAt(Start - Buff, Len, FO), N));
      FO.TheFont.AssignToCanvas(Canvas);
      //Assert(I > 0, 'I less than or = 0 in FindTextWidth');
      with GetTextExtent(Canvas.Handle, Start, I) do
      begin
        Inc(Result.cx, cx + FO.Overhang);
        Result.cy := Max(Result.cy, cy);
      end;
      if I = 0 then
        Break;
      Dec(N, I);
      Inc(Start, I);
    end;
  end;
end;

{----------------TfrxHtSection.FindTextWidthA}

function TfrxHtSection.FindTextWidthA(Canvas: TCanvas; Start: PWideChar; N: Integer): Integer;
{find actual line width of N chars starting at Start.
 BG: The only difference to FindTextWidth is the '- OHang' when incrementing the result.}
var
  I, J: Integer;
  FlObj: TfrxHtFloatingObj; //TfrxHtSizeableObj;
  FO: TfrxHtFontObj;
begin
  Result := 0;
  while N > 0 do
  begin
    J := Images.GetObjectAt(Start - Buff, FlObj);
    if (J = 0) then {it's an image or a form control}
    begin
    {Here we count floating images as 1 ThtChar but do not include their width,
      This is required for the call in FindCursor}
      if FlObj.IsInFlow then
        Inc(Result, FlObj.TotalWidth);
      Dec(N); {image counts as one ThtChar}
      Inc(Start);
    end
    else
    begin
      I := Min(J, Min(Fonts.GetFontObjAt(Start - Buff, Len, FO), N));
      FO.TheFont.AssignToCanvas(Canvas);
      Assert(I > 0, 'I less than or = 0 in FindTextWidthA');
      Inc(Result, GetTextExtent(Canvas.Handle, Start, I).cx - FO.Overhang);
      if I = 0 then
        Break;
      Dec(N, I);
      Inc(Start, I);
    end;
  end;
end;

{----------------TfrxHtSection.DrawLogic}

function TfrxHtSection.DrawLogic1(Canvas: TCanvas; X, Y, XRef, YRef, AWidth, AHeight, BlHt: Integer; IMgr: TfrxHtIndentManager;
  var MaxWidth, Curs: Integer; ARemainHeight: Integer): Integer;
{returns height of the section}

  function FindCountThatFits1(Canvas: TCanvas; Start: PWideChar; MaxChars, X, Y: Integer;
    IMgr: TfrxHtIndentManager; var ImgY, ImgHt: Integer; var DoneFlObjPos: PWideChar): Integer;
  {Given a width, find the count of chars (<= Max) which will fit allowing for font changes.
    Line wrapping will be done later}
  //BG, 06.02.2011: Why are there 2 methods and why can't GetURL and FindCursor use the formatting results of DrawLogic?
  //  FindCountThatFits1() is part of TfrxHtSection.DrawLogic() and fills IMgr with the embedded floating objects.
  //  TfrxHtSection.FindCountThatFits() is used in TfrxHtSection.GetURL() and TfrxHtSection.FindCursor().


  type
    TResultCode = (rsOk, rsContinue, rsBreak);

    function DrawLogicOfObject(FlObj: TfrxHtFloatingObj; var XX, YY, Width, Cnt, FloatingImageCount: Integer): TResultCode;
    var
      X1, X2, W, H: Integer;
    begin
      Result := rsOk;
      if FlObj.Floating in [ALeft, ARight] then
      begin
        if Start > DoneFlObjPos then
        begin
          ImgY := Max(Y, ImgY);
          W := FlObj.TotalWidth;
          H := FlObj.TotalHeight;
          case FlObj.Floating of
            ALeft:
            begin
              FlObj.FIndent := IMgr.AlignLeft(ImgY, W) + FlObj.HSpaceL;
              IMgr.AddLeft(ImgY, ImgY + H, W);
            end;

            ARight:
            begin
              FlObj.FIndent := IMgr.AlignRight(ImgY, W) + FlObj.HSpaceL;
              IMgr.AddRight(ImgY, ImgY + H, W);
            end;
          end;
          FlObj.DrawXX := FlObj.FIndent;
          FlObj.DrawYY := ImgY + FlObj.VSpaceT;
          ImgHt := Max(ImgHt, H);
          DoneFlObjPos := Start;

          // go on with the line:
          X1 := IMgr.LeftIndent(Y);
          X2 := IMgr.RightSide(Y);
          Width := X2 - X1;
          Inc(FloatingImageCount);
          if Cnt >= FloatingImageCount then
            Result := rsContinue;
        end;
      end
      else if not (FlObj.Positioning in [posAbsolute, posFixed]) then
      begin
        ImgHt := Max(ImgHt, FlObj.TotalHeight);
        Inc(XX, FlObj.TotalWidth);
        if XX > Width then
          Result := rsBreak;
      end;
    end;

  var
    Cnt,
    I,  // number of fitting chars
    J,  // number of chars up to next font change or object
    J1, // number of chars up to next font change
    J2, // number of chars up to next image
//    J3, // number of chars up to next form control
    X1, X2, Width, D, H: Integer;
    XX: Integer; // current width of row in pixels (== current horizontal position).
    YY: Integer; // current height of row in pixels.
    Picture: Boolean;
    FlObj: TfrxHtFloatingObj; //TfrxHtSizeableObj;
    FO: TfrxHtFontObj;
    BrChr, TheStart: PWideChar;
    //Font,
    LastFont: TfrxHtFont;
    Save: TSize;
    FoundBreak: Boolean;
    HyphenWidth: Integer;
    FloatingImageCount: Integer;
    InitialFloatingLeftCount: Integer;
    InitialFloatingRightCount: Integer;
  begin
    LastFont := nil;
    TheStart := Start;
    ImgHt := 0;
    InitialFloatingLeftCount := IMgr.L.Count;
    InitialFloatingRightCount := IMgr.R.Count;

    BrChr := StrScanW(TheStart, BrkCh); {see if a break char}
    FoundBreak := Assigned(BrChr) and (BrChr - TheStart < MaxChars);
    if FoundBreak then
    begin
      MaxChars := BrChr - TheStart;
      if MaxChars = 0 then
      begin
        Result := 1;
        Exit; {single character fits}
      end;
    end;

    X1 := IMgr.LeftIndent(Y);
    if Start = Buff then
      Inc(X1, FirstLineIndent);
    X2 := IMgr.RightSide(Y);
    Width := X2 - X1;

    if (Start = Buff) and (Images.Count = 0) then
      if Fonts.GetFontObjAt(0, Len, FO) = Len then
        if MaxChars * Fonts[0].tmMaxCharWidth <= Width then {try a shortcut}
        begin {it will all fit}
          Result := MaxChars;
          if FoundBreak then
            Inc(Result);
          Exit;
        end;

    FloatingImageCount := -1;
    Cnt := 0;
    XX := 0;
    YY := 0;
    while True do
    begin
      J1 := Min(Fonts.GetFontObjAt(Start - Buff, Len, FO), MaxChars - Cnt);
      if FO.TheFont <> LastFont then {may not have to load font}
      begin
        LastFont := FO.TheFont;
        LastFont.AssignToCanvas(Canvas);
      end;
      J2 := Images.GetObjectAt(Start - Buff, FlObj);
      if (J2 = 0) then
      begin {next is an object}
        I := 1;
        J := 1;
        Picture := True;
        case DrawLogicOfObject(FlObj, XX, YY, Width, Cnt, FloatingImageCount) of
          rsContinue:
          begin
            // after an object floating to the left or right, we must retry to
            // fit the remaining space between the floating objects.
            Start := TheStart;
            Cnt := 0;
            XX := 0;
            continue;
          end;

          rsBreak:
            break;
        end;
      end
      else
      begin
        Picture := False;
        J := Min(J1, J2);
        I := FitText(Canvas.Handle, Start, J, Width - XX, Save);
        if (I > 0) and (Brk[TheStart - Buff + Cnt + I - 1] = twSoft) then
        begin {a hyphen could go here}
          HyphenWidth := Canvas.TextWidth('-');
          if XX + Save.cx + HyphenWidth > Width then
            Dec(I);
        end;
      end;

      if Cnt + I >= MaxChars then
      begin
        Cnt := MaxChars;
        Break;
      end
      else
        Inc(Cnt, I);

      if not Picture then {it's a text block}
      begin
        if I < J then
          Break;
        XX := XX + Save.cx;
        YY := Math.Max(YY, Save.cy);
      end;

      Inc(Start, I);
    end;
    Result := Cnt;

    if FoundBreak and (Cnt = MaxChars) then
      Inc(Result);

    // adjust floating objects top position, in case they have been moved down and line height has been changed.
    H := YY; //Max(YY, ImgHt);
    IMgr.AdjustY(InitialFloatingLeftCount, InitialFloatingRightCount, Y, H);

    D := 0;
    for I := 0 to Images.Count - 1 do
      with Images[I] do
        if Floating in [ALeft, ARight] then
          if DrawYY > Y + VSpaceT then
          begin
            if DrawYY < Y + H + VSpaceT then
              D := Y + H + VSpaceT - DrawYY;
            Inc(DrawYY, D);
          end;

  end;

  procedure DoDrawLogic;

    procedure DrawLogicOfObjects(Objects: TFloatingObjList; Width: Integer);
    var
      I: Integer;
      Obj: TfrxHtFloatingObj;
    begin
      for I := 0 to Objects.Count - 1 do {call drawlogic for all the objects}
      begin
        Obj := Objects[I];
        Obj.DrawLogicInline(Canvas, Fonts.GetFontObjAt(Obj.StartCurs), 0, 0);
        // BG, 28.08.2011:
        if OwnerBlock.HideOverflow then
        begin
          if Obj.ClientWidth > Width then
            Obj.ClientWidth := Width;
        end
        else
          MaxWidth := Max(MaxWidth, Obj.ClientWidth); {HScrollBar for wide images}
      end;
    end;

  var
    PStart, Last: PWideChar;
    ImgHt: Integer;
    Finished: Boolean;
    LR: TfrxHtLineRec;
    AccumImgBot: Integer;

    procedure LineComplete(NN: Integer);
    var
      I, J, DHt, Desc, Tmp, TmpRt, Cnt, H, SB, SA: Integer;
      FO: TfrxHtFontObj;
      Align: ThtAlignmentStyle;
      NoChar: Boolean;
      P: PWideChar;
      FlObj: TfrxHtFloatingObj; //TfrxHtSizeableObj;
      LRTextWidth: Integer;
      OHang: Integer;

      function FindSpaces: Integer;
      var
        I: Integer;
      begin
        Result := 0;
        for I := 0 to NN - 2 do {-2 so as not to count end spaces}
          if ((PStart + I)^ = ' ') or ((PStart + I)^ = #160) then
            Inc(Result);
      end;

    begin
      DHt := 0; {for the fonts on this line get the maximum height}
      Cnt := 0;
      Desc := 0;
      P := PStart;
      if (NN = 1) and (P^ = BrkCh) then
        NoChar := False
      else
      begin
        NoChar := True;
        for I := 0 to NN - 1 do
        begin
          case P^ of
            FmCtl, ImgPan, BrkCh:;
          else
            if not ((P = Last) and (Last^ = ' ')) then
            begin {check for the no character case}
              NoChar := False;
              Break;
            end;
          end;
          Inc(P);
        end;
      end;

      Align := ANone;
      if not NoChar then
      begin
        repeat
          J := Fonts.GetFontObjAt(PStart - Buff + Cnt, Len, FO);
          Tmp := FO.GetHeight(Desc);
          DHt := Max(DHt, Tmp);
          LR.Descent := Max(LR.Descent, Desc);
          Inc(Cnt, J);
        until Cnt >= NN;
        Align := FO.SScript;
      end;

      {if there are images or line-height, then maybe they add extra space}
      SB := 0; // vertical space before DHt / Text
      SA := 0; // vertical space after DHt / Text
      if not NoChar then
      begin
        if LineHeight > DHt then
        begin
          // BG, 28.08.2011: too much space below an image: SA and SB depend on Align:
          case Align of
            aTop:
              SA := LineHeight - DHt;

            aMiddle:
              begin
                SB := (LineHeight - DHt) div 2;
                SA := (LineHeight - DHt) - SB;
              end;
          else
//            aNone,
//            aBaseline,
//            aBottom:
              SB := LineHeight - DHt;
          end;
        end
        else if LineHeight >= 0 then
        begin
          SB := (LineHeight - DHt) div 2;
          SA := (LineHeight - DHt) - SB;
        end;
      end;

      Cnt := 0;
      repeat
        Inc(Cnt, Images.GetObjectAt(PStart - Buff + Cnt, FlObj));
        if Cnt < NN then
        begin
          H := FlObj.TotalHeight;
          if FlObj.Floating = ANone then
          begin
            FlObj.DrawYY := Y; {approx y dimension}
            if not (FlObj.Positioning in [posAbsolute, posFixed]) then
            begin
              case FlObj.VertAlign of
                aTop:
                  SA := Max(SA, H - DHt);

                aMiddle:
                  begin
                    if DHt = 0 then
                    begin
                      DHt := Fonts.GetFontObjAt(PStart - Buff).GetHeight(Desc);
                      LR.Descent := Desc;
                    end;
                    Tmp := (H - DHt) div 2;
                    SA := Max(SA, Tmp);
                    SB := Max(SB, (H - DHt - Tmp));
                  end;
                aBaseline,
                aBottom:
                  SB := Max(SB, H - (DHt - LR.Descent));
              end;
            end;
          end;
        end;
        Inc(Cnt); {to skip by the image}
      until Cnt >= NN;

      LR.Start := PStart;
      LR.LineHt := DHt;
      LR.Ln := NN;
      if Brk[PStart - Buff + NN - 1] = twSoft then {see if there is a soft hyphen on the end}
        LR.Shy := True;
      TmpRt := IMgr.RightSide(Y);
      Tmp := IMgr.LeftIndent(Y);
      if PStart = Buff then
        Inc(Tmp, FirstLineIndent);

      LRTextWidth := FindTextSize(Canvas, PStart, NN, True).cx;
      if LR.Shy then
      begin {take into account the width of the hyphen}
        Fonts.GetFontAt(PStart - Buff + NN - 1, OHang).AssignToCanvas(Canvas);
        Inc(LRTextWidth, Canvas.TextWidth('-'));
      end;
      TextWidth := Max(TextWidth, LRTextWidth);
      case Justify of
        Left:     LR.LineIndent := Tmp - X;
        Centered: LR.LineIndent := (TmpRt + Tmp - LRTextWidth) div 2 - X;
        Right:    LR.LineIndent := TmpRt - X - LRTextWidth;
      else
        {Justify = FullJustify}
        LR.LineIndent := Tmp - X;
        if not Finished then
        begin
          LR.Extra := TmpRt - Tmp - LRTextWidth;
          LR.Spaces := FindSpaces;
        end;
      end;
      LR.DrawWidth := TmpRt - Tmp;
      LR.SpaceBefore := LR.SpaceBefore + SB;
      LR.SpaceAfter := SA;
      Lines.Add(LR);
      Inc(PStart, NN);
      SectionHeight := SectionHeight + DHt + SA + LR.SpaceBefore;
      Tmp := DHt + SA + SB;
      Inc(Y, Tmp);
      LR.LineImgHt := Max(Tmp, ImgHt);
    end;

  var
    P: PWideChar;
    MaxChars: Integer;
    N, NN, Width, I: Integer;
    Tmp: Integer;
    Obj: TfrxHtFloatingObj;
    TopY, HtRef: Integer;
    //Ctrl: TFormControlObj;
    //BG, 06.02.2011: floating objects:
    PDoneFlObj: PWideChar;
    YDoneFlObj: Integer;
  begin {DoDrawLogic}
    SectionHeight := 0;
    AccumImgBot := 0;
    TopY := Y;
    PStart := Buff;
    Last := Buff + Len - 1;
    if Len = 0 then
    begin
      Result := GetClearSpace(IMgr, Y);
      DrawHeight := Result;
      SectionHeight := Result;
      ContentBot := Y + Result;
      DrawBot := ContentBot;
      MaxWidth := 0;
      DrawWidth := 0;
      Exit;
    end;
    if FLPercent <> 0 then
      FirstLineIndent := (FLPercent * AWidth) div 100; {percentage calculated}
    Finished := False;
    DrawWidth := IMgr.RightSide(Y) - X;
    Width := Min(IMgr.RightSide(Y) - IMgr.LeftIndent(Y), AWidth);
    MaxWidth := Width;
    if AHeight = 0 then
      HtRef := BlHt
    else
      HtRef := AHeight;

    for I := 0 to Images.Count - 1 do {call drawlogic for all the images}
    begin
      Obj := Images[I];
      Obj.DrawLogicInline(Canvas, Fonts.GetFontObjAt(Obj.StartCurs), Width, HtRef);
      // BG, 28.08.2011:
      if OwnerBlock.HideOverflow then
      begin
        if Obj.ClientWidth > Width then
          Obj.ClientWidth := Width;
      end
      else
        MaxWidth := Max(MaxWidth, Obj.ClientWidth); {HScrollBar for wide images}
    end;

    YDoneFlObj := Y;
    PDoneFlObj := PStart - 1;
    while not Finished do
    begin
      MaxChars := Last - PStart + 1;
      if MaxChars <= 0 then
        Break;
      LR := TfrxHtLineRec.Create(Document); {a new line}
      if Lines.Count = 0 then
      begin {may need to move down past floating image}
        Tmp := GetClearSpace(IMgr, Y);
        if Tmp > 0 then
        begin
          LR.LineHt := Tmp;
          Inc(SectionHeight, Tmp);
          LR.Ln := 0;
          LR.Start := PStart;
          Inc(Y, Tmp);
          Lines.Add(LR);
          LR := TfrxHtLineRec.Create(Document);
        end;
      end;

      ImgHt := 0;
      NN := 0;
      if (WhiteSpaceStyle in [wsPre, wsPreLine, wsNoWrap]) and not BreakWord then
        N := MaxChars
      else
      begin
        NN := FindCountThatFits1(Canvas, PStart, MaxChars, X, Y, IMgr, YDoneFlObj, ImgHt, PDoneFlObj);
        N := Max(NN, 1); {N = at least 1}
      end;

      AccumImgBot := Max(AccumImgBot, Y + ImgHt);
      if NN = 0 then {if nothing fits, see if we can move down}
        Tmp := IMgr.GetNextWiderY(Y) - Y
      else
        Tmp := 0;
      if Tmp > 0 then
      begin
        //BG, 24.01.2010: do not move down images or trailing spaces.
        P := PStart + N - 1; {the last ThtChar that fits}
        if ((P^ = SpcChar) {or (P^ = FmCtl,} or (P^ = ImgPan) or WrapChar(P^)) and (Brk[P - Buff] <> twNo) or (P^ = BrkCh) then
        begin {move past spaces so as not to print any on next line}
          while (N < MaxChars) and ((P + 1)^ = ' ') do
          begin
            Inc(P);
            Inc(N);
          end;
          Finished := N >= MaxChars;
          LineComplete(N);
        end
        else
        begin {move down to where it's wider}
          LR.LineHt := Tmp;
          Inc(SectionHeight, Tmp);
          LR.Ln := 0;
          LR.Start := PStart;
          Inc(Y, Tmp);
          Lines.Add(LR);
        end
      end {else can't move down or don't have to}
      else if N = MaxChars then
      begin {Do the remainder}
        Finished := True;
        LineComplete(N);
      end
      else
      begin
        P := PStart + N - 1; {the last ThtChar that fits}
        if ((P^ = SpcChar) or (P^ = FmCtl) or (p^ = ImgPan) or WrapChar(P^)) and (Brk[P - Buff] <> twNo) or (P^ = BrkCh) then
        begin {move past spaces so as not to print any on next line}
          while (N < MaxChars) and ((P + 1)^ = ' ') do
          begin
            Inc(P);
            Inc(N);
          end;
          Finished := N >= MaxChars;
          LineComplete(N);
        end
        else if (N < MaxChars) and ((P + 1)^ = ' ') and (Brk[P - Buff + 1] <> twNo) then
        begin
          repeat
            Inc(P);
            Inc(N); {pass the space}
          until (N >= MaxChars) or ((P + 1)^ <> ' ');
          Finished := N >= MaxChars;
          LineComplete(N);
        end
        else if (N < MaxChars) and (((P + 1)^ = FmCtl) or ((P + 1)^ = ImgPan)) and (Brk[PStart - Buff + N - 1] <> twNo) then {an image or control}
        begin
          Finished := False;
          LineComplete(N);
        end
        else
        begin
          {non space, wrap it by backing off to previous wrappable char}
          while P > PStart do
          begin
            case Brk[P - Buff] of
              twNo: ;

              twSoft,
              twOptional:
                break;

            else
              if CanWrap(P) or WrapChar((P + 1)^) then
                break; // can wrap after this or before next char.
            end;
            Dec(P);
          end;

          if (P = PStart) and ((not ((P^ = FmCtl) or (P^ = ImgPan))) or (Brk[PStart - Buff] = twNo)) then
          begin
            {no space found, forget the wrap, write the whole word and any spaces found after it}
            if BreakWord then
              LineComplete(N)
            else
            begin
              P := PStart + N - 1;

              while (P <> Last) and not CanWrapAfter(P) and not (Brk[P - Buff] in [twSoft, twOptional])
              do
              begin
                case Brk[P - Buff + 1] of
                  twNo: ; // must not wrap after this char.
                else
                  case (P + 1)^ of
                    ' ', FmCtl, ImgPan, BrkCh:
                      break; // can wrap before this char.
                  else
                    if WrapChar((P + 1)^) then
                      break; // can wrap before this char.
                  end;
                end;
                Inc(P);
              end;

              while (P <> Last) and ((P + 1)^ = ' ') do
              begin
                Inc(P);
              end;
              if (P <> Last) and ((P + 1)^ = BrkCh) then
                Inc(P);
            {Line is too long, add spacer line to where it's clear}
              Tmp := IMgr.GetNextWiderY(Y) - Y;
              if Tmp > 0 then
              begin
                LR.LineHt := Tmp;
                Inc(SectionHeight, Tmp);
                LR.Ln := 0;
                LR.Start := PStart;
                Inc(Y, Tmp);
                Lines.Add(LR);
              end
              else
              begin {line is too long but do it anyway}
                MaxWidth := Max(MaxWidth, FindTextSize(Canvas, PStart, P - PStart + 1, True).cx);
                Finished := P = Last;
                LineComplete(P - PStart + 1);
              end;
            end
          end
          else
          begin {found space}
            while (P + 1)^ = ' ' do
            begin
              if P = Last then
              begin
                Inc(P);
                Dec(P);
              end;
              Inc(P);
            end;
            LineComplete(P - PStart + 1);
          end;
        end;
      end;
    end;
    Curs := StartCurs + Len;

    if Assigned(Document.FirstLineHtPtr) and (Lines.Count > 0) then {used for List items}
      with Lines[0] do
        if (Document.FirstLineHtPtr^ = 0) then
          Document.FirstLineHtPtr^ := YDraw + LineHt - Descent + SpaceBefore;

    DrawHeight := AccumImgBot - TopY; {in case image overhangs}
    if DrawHeight < SectionHeight then
      DrawHeight := SectionHeight;
    Result := SectionHeight;
    ContentBot := TopY + SectionHeight;
    DrawBot := TopY + DrawHeight;
    with Document do
    begin
      ThisCycle := CycleNumber; {only once per cycle}
    end;

    // BG, 28.08.2011:
    if OwnerBlock.HideOverflow then
      if MaxWidth > Width then
        MaxWidth := Width;
  end; { DoDrawLogic}

var
  Dummy: Integer;
  Save: Integer;
begin {TfrxHtSection.DrawLogic}
  YDraw := Y;
  DrawTop := Y;
  ContentTop := Y;
  StartCurs := Curs;
  Lines.Clear;
  TextWidth := 0;

  if WhiteSpaceStyle in [wsPre, wsNoWrap] then
  begin
    if Len = 0 then
    begin
      Result := Fonts.GetFontObjAt(0).FontHeight;
      SectionHeight := Result;
      MaxWidth := 0;
      DrawHeight := Result;
      ContentBot := Y + Result;
      DrawBot := ContentBot;
      exit;
    end;

    if not BreakWord then
    begin
    {call with large width to prevent wrapping}
      Save := IMgr.Width;
      IMgr.Width := 32000;
      DoDrawLogic;
      IMgr.Width := Save;
      MinMaxWidth(Canvas, Dummy, MaxWidth, AWidth, AHeight); {return MaxWidth}
      exit;
    end;
  end;

  DoDrawLogic;

end;

{----------------TfrxHtSection.CheckForInlines}

procedure TfrxHtSection.CheckForInlines(LR: TfrxHtLineRec);
{called before each line is drawn the first time to check if there are any
 inline borders in the line}
var
  I: Integer;
  BR: TfrxHtBorderRec;
  StartBI, EndBI, LineStart: Integer;
begin
  with LR do
  begin
    FirstDraw := False; {this will turn it off if there is no inline border action in this line}
    with TInlineList(Document.InlineList) do
      for I := 0 to Count - 1 do {look thru the inlinelist}
      begin
        StartBI := StartB[I];
        EndBI := EndB[I];
        LineStart := StartCurs + (Start - Buff); {offset from Section start to Line start}
        if (EndBI > LineStart) and (StartBI < LineStart + Ln) then
        begin {it's in this line}
          if not Assigned(BorderList) then
          begin
            BorderList := TfrxHtBorderRecList.Create;
            FirstDraw := True; {there will be more processing needed}
          end;
          BR := TfrxHtBorderRec.Create;
          BorderList.Add(BR);
          with BR do
          begin
            BR.MargArray := Items[I].MargArray; {get border data}
            if StartBI < LineStart then
            begin
              OpenStart := True; {continuation of border on line above, end is open}
              BStart := Start - Buff; {start of this line}
            end
            else
            begin
              OpenStart := False;
              BStart := StartBI - StartCurs; {start is in this line}
            end;
            if EndBI > LineStart + Ln then
            begin
              OpenEnd := True; {will continue on next line, end is open}
              BEnd := Start - Buff + Ln;
            end
            else
            begin
              OpenEnd := False;
              BEnd := EndBI - StartCurs; {end is in this line}
            end;
          end;
        end;
      end;
  end;
end;

{----------------TfrxHtSection.Draw}

function TfrxHtSection.Draw1(Canvas: TCanvas; const ARect: TRect;
  IMgr: TfrxHtIndentManager; X, XRef, YRef: Integer): Integer;
var
  YOffset, Y, Desc: Integer;

  procedure DrawTheText(LineNo: Integer);
  var
    I, J, J1, J2, J3,
    Addon, TopP, BottomP, Tmp, OutX, K: Integer;
    FlObj: TfrxHtFloatingObj;
    FO: TfrxHtFontObj;
    ARect: TRect;
    Inverted, NewCP: Boolean;
    ForeColor, BackColor: TColor;
    BkMode: Integer;
    CPx, CPy, CP1x: Integer;
    BR: TfrxHtBorderRec;
    LR: TfrxHtLineRec;
    Start, OutStart: PWideChar;
    Cnt, Descent: Integer;
    St: ThtString;

    function AddHyphen(P: PWideChar; N: Integer): ThtString;
    var
      I: Integer;
    begin
      SetLength(Result, N + 1);
      for I := 1 to N do
        Result[I] := P[I - 1];
      Result[N + 1] := ThtChar('-');
    end;

    function ChkInversion(Start: PWideChar; out Count: Integer): Boolean;
    var
      LongCount: Integer;
    begin
      Result := False;
      Count := 32000;
      if IsCopy then
        Exit;
      LongCount := 32000;
      if LongCount > 32000 then
        Count := 32000
      else
        Count := LongCount;
    end;

  begin {Y is at bottom of line here}
    LR := Lines[LineNo];
    Start := LR.Start;
    Cnt := LR.Ln;
    Descent := LR.Descent;

    NewCP := True;
    CPy := Y + LR.DrawY;  //Todo: Someone needs to find a sensible default value.
    CPx := X + LR.LineIndent;

//    {$IFDEF DELPHI12}
//    {$IFNDEF Compiler32_Plus}CP1x := CPx;{$ENDIF}
//    {$ENDIF}
    LR.DrawY := Y - LR.LineHt;
    LR.DrawXX := CPx;
    while Cnt > 0 do
    begin
      I := 1;
      J1 := Fonts.GetFontObjAt(Start - Buff, Len, FO) - 1;
      J2 := Images.GetObjectAt(Start - Buff, FlObj) - 1;

    {if an inline border, find it's boundaries}
      if LR.FirstDraw and Assigned(LR.BorderList) then
        for K := 0 to LR.BorderList.Count - 1 do {may be several inline borders}
        begin
          BR := LR.BorderList[K];
          if (Start - Buff = BR.BStart) then
          begin {this is it's start}
            BR.bRect.Top := Y - FO.GetHeight(Desc) - Descent + Desc + 1;
            BR.bRect.Left := CPx;
            BR.bRect.Bottom := Y - Descent + Desc;
          end
          else if (Start - Buff = BR.BEnd) and (BR.bRect.Right = 0) then
            BR.bRect.Right := CPx {this is it's end}
          else if (Start - Buff > BR.BStart) and (Start - Buff < BR.BEnd) then
          begin {this is position within boundary, it's top or bottom may enlarge}
            BR.bRect.Top := Min(BR.bRect.Top, Y - FO.GetHeight(Desc) - Descent + Desc + 1);
            BR.bRect.Bottom := Max(BR.bRect.Bottom, Y - Descent + Desc);
          end;
        end;

      FO.TheFont.AssignToCanvas(Canvas);
      ForeColor := Canvas.Font.Color;
      BackColor := FO.TheFont.bgColor;
      if ForeColor <> clNone then
         ForeColor := ThemedColor(ForeColor);
      if BackColor <> clNone then
         BackColor := ThemedColor(BackColor);
      Canvas.Font.Color := ForeColor;
      if J2 = -1 then
      begin {it's an image or panel}
        if FlObj is TfrxFrHtImageObj then
        begin
          if FlObj.Floating in [ALeft, ARight] then
          begin
            //BG, 02.03.2011: Document is the Document, thus we must
            //  feed it with document coordinates: X,Y is in document coordinates,
            //  but might not be the coordinates of the upper left corner of the
            //  containing block, the origin of the Obj's coordinates. If each block
            //  had its own IMgr and nested blocks had nested IMgrs with coordinates
            //  relative to the containing block, the document coordinates of an inner
            //  block were the sum of all LfEdges of the containing blocks.
            //
            // correct x-position for floating images: IMgr.LfEdge + Obj.Indent
            if not LR.FirstDraw then
              Document.DrawList.AddImage(TfrxFrHtImageObj(FlObj), Canvas,
                IMgr.LfEdge + FlObj.FIndent, FlObj.DrawYY, Y - Descent, FO)
            else if Assigned(LR.BorderList) then
              {if a boundary is on a floating image, remove it}
              for K := LR.BorderList.Count - 1 downto 0 do
              begin
                BR := LR.BorderList[K];
                if (Start - Buff = BR.BStart) and (BR.BEnd = BR.BStart + 1) then
                  LR.BorderList.Delete(K);
              end;
          end
          else
          begin
            SetTextJustification(Canvas.Handle, 0, 0);
//            if OwnerBlock <> nil then
//              FlObj.Positioning := OwnerBlock.Positioning
//            else
//              FlObj.Positioning := posStatic;
            TfrxFrHtImageObj(FlObj).DrawInline(Canvas, CPx + FlObj.HSpaceL, LR.DrawY, Y - Descent, FO);
            Document.Printed := True;
          {see if there's an inline border for the image}
            if LR.FirstDraw and Assigned(LR.BorderList) then
              for K := 0 to LR.BorderList.Count - 1 do
              begin
                BR := LR.BorderList[K];
                if (Start - Buff >= BR.BStart) and (Start - Buff <= BR.BEnd) then
                begin {there is a border here, find the image dimensions}
                  TopP := 0;
                  if not (FlObj.Positioning in [posAbsolute, posFixed]) then
                  begin
                    case FlObj.VertAlign of

                      ATop, ANone:
                        TopP := Y - LR.LineHt + FlObj.VSpaceT;

                      AMiddle:
                        TopP := Y - Descent + FO.Descent - FO.tmHeight div 2 - (FlObj.ClientHeight - FlObj.VSpaceT + FlObj.VSpaceB) div 2;

                      ABottom, ABaseline:
                        TopP := Y - Descent - FlObj.VSpaceB - FlObj.ClientHeight;

                    end;
                    BottomP := TopP + FlObj.ClientHeight;

                    if Start - Buff = BR.BStart then
                    begin {border starts at image}
                      BR.bRect.Top := TopP;
                      BR.bRect.Left := CPx + FlObj.HSpaceL;
                      if BR.BEnd = BR.BStart + 1 then {border ends with image also, rt side set by image width}
                        BR.bRect.Right := BR.bRect.Left + FlObj.ClientWidth;
                      BR.bRect.Bottom := BottomP;
                    end
                    else if Start - Buff <> BR.BEnd then
                    begin {image is included in border and may effect the border top and bottom}
                      BR.bRect.Top := Min(BR.bRect.Top, TopP);
                      BR.bRect.Bottom := Max(BR.bRect.Bottom, BottomP);
                    end;
                  end
                  else if FlObj.Positioning in [posAbsolute, posFixed] {differ only when scrolling} then
                    BR.BRect := Bounds(FlObj.DrawXX, FlObj.DrawYY, FlObj.ClientWidth - 1 {?}, FlObj.ClientHeight)
                  ;
                end;
              end;

            if not (FlObj.Positioning in [posAbsolute, posFixed]) then
            begin
              CPx := CPx + FlObj.TotalWidth;
              NewCP := True;
            end;
          end;
        end
      end
      else
      begin
        J := Min(J1, J2);
        Inverted := ChkInversion(Start, J3);
        J := Min(J, J3 - 1);
        I := Min(Cnt, J + 1);

        BkMode := Opaque;
        if Inverted then
        begin
          if BackColor = clNone then
            Canvas.Font.Color := ForeColor xor $FFFFFF
          else
            Canvas.Font.Color := BackColor;
          BackColor := ForeColor;
        end
        else
          if BackColor = clNone then
            BkMode := Transparent;

        if BkMode = Opaque then
        begin
          Canvas.Brush.Color := BackColor;
          Canvas.Brush.Style := bsSolid;
{$ifdef LCLgtk2}
          // background is missing if Canvas.Brush.Color = GetBkColor(Canvas.Handle)
          if Canvas.Brush.Color = GetBkColor(Canvas.Handle) then
             Canvas.Brush.Color := Canvas.Brush.Color xor $010101;
{$endif}
        end
        else
          Canvas.Brush.Style := bsClear;
        SetBkMode(Canvas.Handle, BkMode);

        SetTextAlign(Canvas.Handle, TA_BaseLine);
      {figure any offset for subscript or superscript}
        with FO do
          if SScript = ANone then
            Addon := 0
          else if SScript = ASuper then
            Addon := -(FontHeight div 3)
          else
            Addon := Descent div 2 + 1;
        NewCP := NewCP or (Addon <> 0);
      {calc a new CP if required}
        if NewCP then
        begin
          CPy := Y - Descent + Addon - YOffset;
          NewCP := Addon <> 0;
        end;

        if not Document.NoOutput and (Canvas.Font.Size <> 0) and (Canvas.Font.Color <> clNone) then
        begin
          Tmp := I;
          if Cnt - I <= 0 then
            case (Start + I - 1)^ of
              ' ', BrkCh:
                Dec(Tmp); {at end of line, don't show space or break}
            end;

          if (WhiteSpaceStyle in [wsPre, wsPreLine, wsNoWrap]) and not OwnerBlock.HideOverflow then
          begin {so will clip in Table cells}
            ARect := Rect(IMgr.LfEdge, Y - LR.LineHt - LR.SpaceBefore - YOffset, X + IMgr.ClipWidth, Y - YOffset + 1);
            if Direction = diRTL then
              ExtTextOutW(Canvas.Handle, CPx, CPy, ETO_CLIPPED + ETO_RTLREADING, @ARect, Start, Tmp, nil)
            else
              ExtTextOutW(Canvas.Handle, CPx, CPy, ETO_CLIPPED, @ARect, Start, Tmp, nil);
            CP1x := CPx + GetTextExtent(Canvas.Handle, Start, Tmp).cx;
          end
          else
          begin
            if LR.Spaces = 0 then
              SetTextJustification(Canvas.Handle, 0, 0)
            else
              SetTextJustification(Canvas.Handle, LR.Extra, LR.Spaces);

            if (Cnt - I <= 0) and LR.Shy then
            begin
              St := AddHyphen(Start, Tmp);
              OutStart := PWideChar(St);
              Tmp := Length(St);
            end
            else
              OutStart := Start;

            if Direction = diRTL then
              SetTextAlign(Canvas.Handle, TA_BASELINE or TA_RTLREADING or TA_RIGHT);

            CP1x := CPx + GetTextExtent(Canvas.Handle, OutStart, Tmp).cx;
            OutX := IfInt(Direction = diRTL, CP1x - IfInt(OwnerBlock is TfrxHtBlockLI, 40), CPx);

            TextOutW(Canvas.Handle, OutX, CPy, OutStart, Tmp);
          end;
          Document.Printed := True;

          if FO.Active or IsCopy and Assigned(Document.LinkDrawnEvent)
            and (FO.UrlTarget.Url <> '') then
          begin
            Tmp := Y - Descent + FO.Descent + Addon - YOffset;
            ARect := Rect(CPx, Tmp - FO.FontHeight, CP1x + 1, Tmp);
            if FO.Active then
            begin
              Canvas.Brush.Color := clWhite;
              Canvas.Font.Color := clBlack; {black font needed for DrawFocusRect}
              Canvas.Handle; {Dummy call needed to make Delphi add font color change to handle}
            end;
            if Assigned(Document.LinkDrawnEvent) then
              Document.LinkDrawnEvent(Document.TheOwner, Document.LinkPage,
                FO.UrlTarget.Url, FO.UrlTarget.Target, ARect);
          end;
          CPx := CP1x;

        end;
      end;
      Dec(Cnt, I);
      Inc(Start, I);
    end;
    SetTextJustification(Canvas.Handle, 0, 0);
  {at the end of this line.  see if there are open borders which need right side set}
    if LR.FirstDraw and Assigned(LR.BorderList) then
      for K := 0 to LR.BorderList.Count - 1 do
      begin
        BR := LR.BorderList[K];
        if BR.OpenEnd or (BR.BRect.Right = 0) then
          BR.BRect.Right := CPx;
      end;
  end;

  procedure DoDraw(I: Integer);
  {draw the Ith line in this section}
  var
    BR: TfrxHtBorderRec;
    K: Integer;
    XOffset: Integer;
  begin
    with Lines[I] do
    begin
      Inc(Y, LineHt + SpaceBefore);
      if FirstDraw then
      begin {see if any inline borders in this line}
        CheckForInlines(Lines[I]);
        if FirstDraw then {if there are, need a first pass to get boundaries}
        begin
          FirstX := X;
          DrawTheText(I);
        end;
      end;
      XOffset := X - FirstX;
      FirstDraw := False;
      Drawn := True;
      if Assigned(BorderList) then {draw any borders found in this line}
        for K := 0 to BorderList.Count - 1 do
        begin
          BR := BorderList[K];
          BR.DrawTheBorder(Canvas, XOffset, YOffSet, False);
        end;
      DrawTheText(I); {draw the text, etc., in this line}
      Inc(Y, SpaceAfter);
    end;
    Document.FirstPageItem := False;
  end;

var
  I: Integer;
  DC: HDC;
begin {TfrxHtSection.Draw}
  Y := YDraw;
  Result := Y + SectionHeight;

  if (OwnerBlock <> nil) and (OwnerBlock.Positioning = posFixed) then
    YOffset := 0
  else
    YOffset := Document.YOff;

{Only draw if will be in display rectangle}
  if (Len > 0) and (Y - YOffset + DrawHeight + 40 >= ARect.Top) and (Y - YOffset - 40 < ARect.Bottom) then
  begin
    DC := Canvas.Handle;
    SetTextAlign(DC, TA_BaseLine);
    for I := 0 to Lines.Count - 1 do
      with Lines[I] do
        if ((Y - YOffset + LineImgHt + 40 >= ARect.Top) and (Y - YOffset - 40 < ARect.Bottom)) then
          DoDraw(I)
        else {do not completely draw extremely long paragraphs}
          Inc(Y, SpaceBefore + LineHt + SpaceAfter);
  end;
end;

{----------------TfrxHtSection.FindString}

function TfrxHtSection.FindString(From: Integer; const ToFind: UnicodeString; MatchCase: Boolean): Integer;
{find the first occurance of the ThtString, ToFind, with a cursor value >= From.
 ToFind is in lower case if MatchCase is False. ToFind is known to have a length of at least one.
}
var
  P: PWideChar;
  I: Integer;
  ToSearch: UnicodeString;

begin
  Result := -1;
  if (Len = 0) or (From >= StartCurs + Len) then
    Exit;
  if From < StartCurs then
    I := 0
  else
    I := From - StartCurs;

  if MatchCase then
    ToSearch := BuffS
  else
    ToSearch := htLowerCase(BuffS); {ToFind already lower case}

  P := StrPosW(PWideChar(ToSearch) + I, PWideChar(ToFind));
  if Assigned(P) then
    Result := StartCurs + (P - PWideChar(ToSearch));
end;

{----------------TfrxHtSection.FindStringR}

function TfrxHtSection.FindStringR(From: Integer; const ToFind: UnicodeString; MatchCase: Boolean): Integer;
{find the first occurance of the ThtString, ToFind, with a cursor value <= to From.
 ToFind is in lower case if MatchCase is False.  ToFind is known to have a length of at least one.
}
var
  P: PWideChar;
  ToFindLen: word;
  ToMatch, ToSearch: UnicodeString;

begin
  Result := -1;
  if (Len = 0) or (From < StartCurs) then
    Exit;
  ToFindLen := Length(ToFind);
  if (Len < ToFindLen) or (From - StartCurs + 1 < ToFindLen) then
    Exit;

  if From >= StartCurs + Len then
    ToSearch := BuffS {search all of BuffS}
  else
    ToSearch := Copy(BuffS, 1, From - StartCurs); {Search smaller part}
  if not MatchCase then
    ToSearch := htLowerCase(ToSearch); {ToFind already lower case}

{search backwards for the end ThtChar of ToFind}
  P := StrRScanW(PWideChar(ToSearch), ToFind[ToFindLen]);
  while Assigned(P) and (P - PWideChar(ToSearch) + 1 >= ToFindLen) do
  begin
  {pick out a ThtString of proper length from end ThtChar to see if it matches}
    SetString(ToMatch, P - ToFindLen + 1, ToFindLen);
    if WideSameStr1(ToFind, ToMatch) then
    begin {matches, return the cursor position}
      Result := StartCurs + (P - ToFindLen + 1 - PWideChar(ToSearch));
      Exit;
    end;
  {doesn't match, shorten ThtString to search for next search}
    ToSearch := Copy(ToSearch, 1, P - PWideChar(ToSearch));
  {and search backwards for end ThtChar again}
    P := StrRScanW(PWideChar(ToSearch), ToFind[ToFindLen]);
  end;
end;

{----------------TfrxHtSection.FindSourcePos}

function TfrxHtSection.FindSourcePos(DocPos: Integer): Integer;
var
  I: Integer;
  IO: TfrxHtIndexObj;
begin
  Result := -1;
  if (Len = 0) or (DocPos >= StartCurs + Len) then
    Exit;

  for I := SIndexList.Count - 1 downto 0 do
  begin
    IO := PosIndex[I];
    if IO.Pos <= DocPos - StartCurs then
    begin
      Result := IO.Index + DocPos - StartCurs - IO.Pos;
      break;
    end;
  end;
end;

{----------------TfrxHtSection.FindDocPos}

function TfrxHtSection.FindDocPos(SourcePos: Integer; Prev: Boolean): Integer;
{for a given Source position, find the nearest document position either Next or
 previous}
var
  I: Integer;
  IO, IOPrev: TfrxHtIndexObj;
begin
  Result := -1;
  if Len = 0 then
    Exit;

  if not Prev then
  begin
    I := SIndexList.Count - 1;
    IO := PosIndex[I];
    if SourcePos > IO.Index + (Len - 1) - IO.Pos then
      Exit; {beyond this section}

    IOPrev := PosIndex[0];
    if SourcePos <= IOPrev.Index then
    begin {in this section but before the start of Document text}
      Result := StartCurs;
      Exit;
    end;

    for I := 1 to SIndexList.Count - 1 do
    begin
      IO := PosIndex[I];
      if (SourcePos >= IOPrev.Index) and (SourcePos < IO.Index) then
      begin {between IOprev and IO}
        if SourcePos - IOPrev.Index + IOPrev.Pos < IO.Pos then
          Result := StartCurs + IOPrev.Pos + (SourcePos - IOPrev.Index)
        else
          Result := StartCurs + IO.Pos;
        Exit;
      end;
      IOPrev := IO;
    end;
  {after the last TfrxHtIndexObj in list}
    Result := StartCurs + IOPrev.Pos + (SourcePos - IOPrev.Index);
  end
  else {prev  -- we're iterating from the end of TfrxHtDocument}
  begin
    IOPrev := PosIndex[0];
    if SourcePos < IOPrev.Index then
      Exit; {before this section}

    I := SIndexList.Count - 1;
    IO := PosIndex[I];
    if SourcePos > IO.Index + (Len - 1) - IO.Pos then
    begin {SourcePos is after the end of this section}
      Result := StartCurs + (Len - 1);
      Exit;
    end;

    for I := 1 to SIndexList.Count - 1 do
    begin
      IO := PosIndex[I];
      if (SourcePos >= IOPrev.Index) and (SourcePos < IO.Index) then
      begin {between IOprev and IO}
        if SourcePos - IOPrev.Index + IOPrev.Pos < IO.Pos then
          Result := StartCurs + IOPrev.Pos + (SourcePos - IOPrev.Index)
        else
          Result := StartCurs + IO.Pos - 1;
        Exit;
      end;
      IOPrev := IO;
    end;
  {after the last TfrxHtIndexObj in list}
    Result := StartCurs + IOPrev.Pos + (SourcePos - IOPrev.Index);
  end;
end;

{----------------TfrxHtSection.GetChAtPos}

function TfrxHtSection.GetChAtPos(Pos: Integer; out Ch: WideChar; out Obj: TfrxHtSectionBase): Boolean;
begin
  Result := False;
  if (Len = 0) or (Pos < StartCurs) or (Pos >= StartCurs + Len) then
    Exit;
  Ch := Buff[Pos - StartCurs];
  Obj := Self;
  Result := True;
end;

{----------------TfrxHtCell.Create}

constructor TfrxHtCell.Create(Parent: TfrxHtBlock);
begin
  inherited Create(Parent);
  IMgr := TfrxHtIndentManager.Create;
end;

{----------------TfrxHtCell.CreateCopy}

constructor TfrxHtCell.CreateCopy(Parent: TfrxHtBlock; T: TfrxHtCellBasic);
begin
  inherited CreateCopy(Parent, T);
  IMgr := TfrxHtIndentManager.Create;
end;

destructor TfrxHtCell.Destroy;
begin
  IMgr.Free;
  inherited Destroy;
end;

{----------------TfrxHtCell.DoLogic}

function TfrxHtCell.DoLogic(Canvas: TCanvas; Y: Integer; Width, AHeight, BlHt: Integer;
  var ScrollWidth, Curs: Integer; ARemainHeight: Integer): Integer;
{Do the entire layout of the cell or document. Return the total document pixel height}
var
  IB: Integer;
  LIndex, RIndex: Integer;
  SaveID: TObject;
begin
  IMgr.Init(0, Width);
  SaveID := IMgr.CurrentID;
  IMgr.CurrentID := Self;

  LIndex := IMgr.SetLeftIndent(0, Y);
  RIndex := IMgr.SetRightIndent(0 + Width, Y);

  Result := inherited DoLogic(Canvas, Y, Width, AHeight, BlHt, ScrollWidth, Curs, ARemainHeight);

  IMgr.FreeLeftIndentRec(LIndex);
  IMgr.FreeRightIndentRec(RIndex);
  IB := IMgr.ImageBottom - Y; //YValue; {check for image overhang}
  IMgr.CurrentID := SaveID;
  if IB > Result then
    Result := IB;
end;

{----------------TfrxHtCell.Draw}

function TfrxHtCell.Draw(Canvas: TCanvas; ARect: TRect; ClipWidth, X, Y, XRef, YRef: Integer): Integer;
{draw the document or cell.  Note: individual sections not in ARect don't bother drawing}
begin
  IMgr.Reset(X);
  IMgr.ClipWidth := ClipWidth;
  DrawYY := Y; {This is overridden in TfrxHtCellObj.Draw}
  Result := inherited Draw(Canvas, ARect, ClipWidth, X, Y, XRef, YRef);
end;

{----------------TfrxHtCellObjCell.CreateCopy}

constructor TfrxHtCellObjCell.CreateCopy(Parent: TfrxHtBlock; T: TfrxHtCellObjCell);
begin
  inherited CreateCopy(Parent, T);
  MyRect := T.MyRect;
end;

{ TfrxHtBlockCell }

function TfrxHtBlockCell.DoLogicX(Canvas: TCanvas; X, Y, XRef, YRef, Width, AHeight, BlHt: Integer;
  out ScrollWidth: Integer; var Curs: Integer; ARemainHeight: Integer): Integer;
{Do the entire layout of the this cell.  Return the total pixel height}

  function DoBlockLogic: Integer;
  // Returns CellHeight
  var
    I, Sw, Tmp: Integer;
    SB: TfrxHtSectionBase;
  begin
    Result := 0;
    for I := 0 to Count - 1 do
    begin
      SB := Items[I];
      Sw := 0;
      //TODO: clean it !
      if (ARemainHeight > 0) then
        Tmp := SB.DrawLogic1(Canvas, X, Y + Result, XRef, YRef, Width, AHeight, BlHt, IMgr, Sw, Curs, ARemainHeight - Result)
      else
        Tmp := SB.DrawLogic1(Canvas, X, Y + Result, XRef, YRef, Width, AHeight, BlHt, IMgr, Sw, Curs, 0);
      if (ARemainHeight > 0) and (ARemainHeight < Tmp + Result) and (Result > 0 ) then
        Break;

      Inc(Result, Tmp);
      if OwnerBlock.HideOverflow then
        ScrollWidth := Width
      else
        ScrollWidth := Max(ScrollWidth, Sw);
      if SB is TfrxHtSection then
        TextWidth := Max(TextWidth, TfrxHtSection(SB).TextWidth);
      if not (SB is TfrxHtBlock) or (TfrxHtBlock(SB).Positioning <> posAbsolute) then
        tcContentBot := Max(tcContentBot, SB.ContentBot);
      tcDrawTop := Min(tcDrawTop, SB.DrawTop);
      tcDrawBot := Max(tcDrawBot, SB.DrawBot);
    end;
  end;

//TODO -oBG, 15.03.2014: support display:inline
{$ifdef DO_PD_INLINE}
//  function DoInlineLogic: Integer;
//  // Returns CellHeight
//  var
//    I, Sw, Tmp: Integer;
//    LineSize: TSize;
//    SB: TfrxHtSectionBase;
//    SC: TfrxHtSection;
//  begin
//    Result := 0;
//    LineSize.cx := 0;
//    LineSize.cy := 0;
//    for I := 0 to Count - 1 do
//    begin
//      SB := Items[I];
//      if SB is TfrxHtSection then
//        SC := TfrxHtSection(SB)
//      else
//        SC := nil;
//      Tmp := SB.DrawLogic1(Canvas, X + LineSize.cx, Y + Result, XRef, YRef, Width, AHeight, BlHt, IMgr, Sw, Curs);
//      if (SC <> nil) {and (SC.WhiteSpaceStyle in [wsPre, wsNoWrap])} then
//      begin
//        // Each section accumulates elements up to complete lines.
//        Inc(Result, Tmp);
//        if OwnerBlock.HideOverflow then
//          ScrollWidth := Width
//        else
//          ScrollWidth := Max(ScrollWidth, Sw);
//        TextWidth := Max(TextWidth, TfrxHtSection(SB).TextWidth);
//      end
//      else
//      begin
//        if LineSize.cy < Tmp then
//          LineSize.cy := Tmp;
//        Inc(LineSize.cx, SB.DrawRect.Right - SB.DrawRect.Left);
//        if LineSize.cx > Width then
//        begin
//          Inc(Result, LineSize.cy);
//          if OwnerBlock.HideOverflow then
//            ScrollWidth := Width
//          else
//            ScrollWidth := Max(ScrollWidth, LineSize.cx);
//          if TextWidth < LineSize.cx then
//            TextWidth := LineSize.cx;
//          LineSize.cx := 0;
//          LineSize.cy := 0;
//        end;
//      end;
//      if not (SB is TfrxHtBlock) or (TfrxHtBlock(SB).Positioning <> posAbsolute) then
//        tcContentBot := Max(tcContentBot, SB.ContentBot);
//      tcDrawTop := Min(tcDrawTop, SB.DrawTop);
//      tcDrawBot := Max(tcDrawBot, SB.DrawBot);
//    end;
//    Inc(Result, LineSize.cy);
//  end;
{$endif}

begin
//  YValue := Y;
  StartCurs := Curs;

  ScrollWidth := 0;
  TextWidth := 0;
  tcContentBot := 0;
  tcDrawTop := 990000;
  tcDrawBot := 0;

//TODO -oBG, 15.03.2014: support display:inline
{$ifdef DO_PD_INLINE}
  if CalcDisplayExtern = pdInline then
    CellHeight := DoInlineLogic
  else
{$endif}
    CellHeight := DoBlockLogic;

  Len := Curs - StartCurs;
  Result := CellHeight;
end;


{ TfrxHtDrawList }

type
  TImageRec = class(TObject)
  public
    AObj: TfrxFrHtImageObj;
    ACanvas: TCanvas;
    AX, AY: Integer;
    AYBaseline: Integer;
    AFO: TfrxHtFontObj;
  end;

procedure TfrxHtDrawList.AddImage(Obj: TfrxFrHtImageObj; Canvas: TCanvas; X, Y, YBaseline: Integer; FO: TfrxHtFontObj);
var
  Result: TImageRec;
begin
  Result := TImageRec.Create;
  Result.AObj := Obj;
  Result.ACanvas := Canvas;
  Result.AX := X;
  Result.AY := Y;
  Result.AYBaseline := YBaseline;
  Result.AFO := FO;
  Add(Result);
end;

procedure TfrxHtDrawList.DrawImages;
var
  I: Integer;
  Item: TObject;
begin
  I := 0;
  while I < Count do {note: Count may increase during this operation}
  begin
    Item := Items[I];
    if (Item is TImageRec) then
      with TImageRec(Item) do
        AObj.DrawInline(ACanvas, AX, AY, AYBaseline, AFO);
    Inc(I);
  end;
end;

{----------------TfrxHtLineRec.Create}

constructor TfrxHtLineRec.Create(SL: TfrxHtDocument);
begin
  inherited Create;
  if SL.InlineList.Count > 0 then
    FirstDraw := True;
end;

procedure TfrxHtLineRec.Clear;
begin
  FreeAndNil(BorderList);
end;

destructor TfrxHtLineRec.Destroy;
begin
  BorderList.Free;
  inherited Destroy;
end;

{----------------TfrxHtBorderRec.DrawTheBorder}

procedure TfrxHtBorderRec.DrawTheBorder(Canvas: TCanvas; XOffset, YOffSet: Integer; Printing: Boolean);
var
  IRect, ORect: TRect;
begin
  IRect := BRect;
  Dec(IRect.Top, YOffSet);
  Dec(IRect.Bottom, YOffSet);
  Inc(IRect.Left, XOffset);
  Inc(IRect.Right, XOffset);
  if OpenStart then
    MargArray[BorderLeftStyle] := ord(bssNone);
  if OpenEnd then
    MargArray[BorderRightStyle] := ord(bssNone);

  if MargArray[BackgroundColor] <> clNone then
  begin
    Canvas.Brush.Color := ThemedColor(MargArray[BackgroundColor]) or PalRelative;
    Canvas.Brush.Style := bsSolid;
    Canvas.FillRect(IRect);
  end;

  ORect.Left := IRect.Left - MargArray[BorderLeftWidth];
  ORect.Top := IRect.Top - MargArray[BorderTopWidth];
  ORect.Right := IRect.Right + MargArray[BorderRightWidth];
  ORect.Bottom := IRect.Bottom + MargArray[BorderBottomWidth];

  DrawBorder(Canvas, ORect, IRect,
    htColors(MargArray[BorderLeftColor], MargArray[BorderTopColor], MargArray[BorderRightColor], MargArray[BorderBottomColor]),
    htStyles(ThtBorderStyle(MargArray[BorderLeftStyle]), ThtBorderStyle(MargArray[BorderTopStyle]), ThtBorderStyle(MargArray[BorderRightStyle]), ThtBorderStyle(MargArray[BorderBottomStyle])),
    MargArray[BackgroundColor], Printing)
end;

{----------------TfrxHtPage.Draw1}

constructor TfrxHtPage.Create(Parent: TfrxHtCellBasic; Attributes: TfrxHtAttributeList; Properties: TfrxHTProperties);
begin
  inherited Create(Parent, Attributes, Properties);
  if FDisplay = pdUnassigned then
    FDisplay := pdBlock;
end;

function TfrxHtPage.Draw1(Canvas: TCanvas; const ARect: TRect; IMgr: TfrxHtIndentManager; X, XRef, YRef: Integer): Integer;
begin
  Result := inherited Draw1(Canvas, ARect, Imgr, X, XRef, YRef);
end;

{----------------TfrxHtHorzLine.Create}

constructor TfrxHtHorzLine.Create(Parent: TfrxHtCellBasic; L: TfrxHtAttributeList; Prop: TfrxHTProperties);
var
  LwName: ThtString;
  I: Integer;
  TmpColor: TColor;
begin
  inherited Create(Parent, L, Prop);
  if FDisplay = pdUnassigned then
    FDisplay := pdBlock;
  VSize := 2;
  Align := Centered;
  Color := clNone;
  for I := 0 to L.Count - 1 do
    with L[I] do
      case Which of
        SizeSy: if (Value > 0) and (Value <= 20) then
          begin
            VSize := Value;
          end;
        WidthSy:
          if Value > 0 then
            if Pos('%', Name) > 0 then
            begin
              if (Value <= 100) then
                Prop.Assign(IntToStr(Value) + '%', piWidth);
            end
            else
              Prop.Assign(Value, piWidth);
        ColorSy: if TryStrToColor(Name, False, Color) then
            Prop.Assign(Color, frxHTMLStyleUn.Color);
        AlignSy:
          begin
            LwName := Lowercase(Name);
            if LwName = 'left' then
              Align := Left
            else if LwName = 'right' then
              Align := Right;
          end;
        NoShadeSy: NoShade := True;
      end;
  UseDefBorder := not Prop.BorderStyleNotBlank;
  Prop.Assign(VSize, piHeight); {assigns if no property exists yet}
  TmpColor := Prop.GetOriginalForegroundColor;
  if TmpColor <> clNone then
    Color := TmpColor;
  with Prop do
    if (VarIsStr(Props[TextAlign])) and Originals[TextAlign] then
      if Props[TextAlign] = 'left' then
        Align := Left
      else if Props[TextAlign] = 'right' then
        Align := Right
      else if Props[TextAlign] = 'center' then
        Align := Centered;
end;

constructor TfrxHtHorzLine.CreateCopy(Parent: TfrxHtCellBasic; Source: TfrxHtmlNode);
var
  T: TfrxHtHorzLine absolute Source;
begin
  inherited CreateCopy(Parent,Source);
  System.Move(T.VSize, VSize, PtrSub(@BkGnd, @VSize) + Sizeof(BkGnd));
end;

function TfrxHtHorzLine.DrawLogic1(Canvas: TCanvas; X, Y, XRef, YRef, AWidth, AHeight, BlHt: Integer; IMgr: TfrxHtIndentManager;
  var MaxWidth: Integer; var Curs: Integer; ARemainHeight: Integer): Integer;
begin
  YDraw := Y;
  StartCurs := Curs;
{Note: VSize gets updated in TfrxHtHRBlock.FindWidth}
  ContentTop := Y;
  DrawTop := Y;
  Indent := Max(0, IMgr.LeftIndent(Y) - X);
  Width := Min(AWidth, IMgr.RightSide(Y) - X) - Indent;
  MaxWidth := Width;
  SectionHeight := VSize;
  DrawHeight := SectionHeight;
  ContentBot := Y + SectionHeight;
  DrawBot := Y + DrawHeight;
  Result := SectionHeight;
end;

{----------------TfrxHtHorzLine.Draw}

function TfrxHtHorzLine.Draw1(Canvas: TCanvas; const ARect: TRect; IMgr: TfrxHtIndentManager; X, XRef, YRef: Integer): Integer;
var
  XR: Integer;
  YT, YO, Y: Integer;
  White, BlackBorder: Boolean;
begin
  Y := YDraw;
  Result := inherited Draw1(Canvas, ARect, IMgr, X, XRef, YRef);
  YO := Y - Document.YOff;
  if (YO + SectionHeight >= ARect.Top) and (YO < ARect.Bottom) then
  begin
    Inc(X, Indent);
    YT := YO;
    XR := X + Width - 1;
    with Canvas do
    begin
      if Color <> clNone then
      begin
        Brush.Color := ThemedColor(Color) or $2000000;
        Brush.Style := bsSolid;
        FillRect(Rect(X, YT, XR + 1, YT + VSize));
      end
      else
      begin
        if UseDefBorder then begin
          with Document do
          begin
            White := (ThemedColor(Background) = clWhite);
            BlackBorder := NoShade;
          end;
          if BlackBorder then
            Pen.Color := clBlack
          else if White then
            Pen.Color := clSilver
          else
            Pen.Color := ThemedColor(clBtnHighLight);
          MoveTo(XR, YT);
          LineTo(XR, YT + VSize - 1);
          LineTo(X, YT + VSize - 1);
          if BlackBorder then
            Pen.Color := clBlack
          else
            Pen.Color := ThemedColor(clBtnShadow);
          LineTo(X, YT);
          LineTo(XR, YT);
        end;
      end;
      Document.FirstPageItem := False; {items after this will not be first on page}
    end;
  end;
end;

{ TfrxHtmlPropStack }

{ Add a TfrxHTProperties to the PropStack. }
procedure TfrxHtmlPropStack.PushNewProp(Sym: TElemSymb; Properties: TfrxHTProperties; Attributes: TfrxHtAttributeList; const APseudo: ThtString = '');
var
  NewProp: TfrxHTProperties;
  Tag: ThtString;
begin
  Tag := SymbToStr(Sym);
  NewProp := TfrxHTProperties.Create(Self);
  NewProp.PropSym := Sym;
  NewProp.Inherit(Tag, Last);
  Add(NewProp);
  NewProp.Combine(Document.Styles, Sym, Tag, APseudo, Properties, Attributes, Count - 1);
end;

procedure TfrxHtmlPropStack.PopProp;
{pop and free a TfrxHTProperties from the Prop stack}
var
  TopIndex: Integer;
begin
  TopIndex := Count - 1;
  if TopIndex > 0 then
    Delete(TopIndex);
end;

procedure TfrxHtmlPropStack.PopAProp(Sym: TElemSymb);
{pop and free a TfrxHTProperties from the Prop stack.  It should be on top but in
 case of a nesting error, find it anyway}
var
  I, J: Integer;
begin
  for I := Count - 1 downto 1 do
    if Items[I].PropSym = Sym then
    begin
      if Items[I].HasBorderStyle then
      {this would be the end of an inline border}
        Document.ProcessInlines(SIndex, Items[I], False);
      Delete(I);
      if I > 1 then {update any stack items which follow the deleted one}
        for J := I to Count - 1 do
          Items[J].Update(Items[J - 1], Document.Styles, J);
      Break;
    end;
end;

//-- BG ---------------------------------------------------------- 12.09.2010 --
constructor TfrxHtmlStyleList.Create(AMasterList: TfrxHtDocument);
begin
  inherited Create;
  Document := AMasterList;
end;

//-- BG ---------------------------------------------------------- 08.03.2011 --
procedure TfrxHtmlStyleList.SetLinksActive(Value: Boolean);
begin
//  inherited SetLinksActive(Value);
  Document.LinksActive := Value;
end;

{ TfrxHtFieldsetBlock }

//-- BG ---------------------------------------------------------- 09.10.2010 --
procedure TfrxHtFieldsetBlock.ContentMinMaxWidth(Canvas: TCanvas; out Min, Max: Integer; AvailableWidth, AvailableHeight: Integer);
var
  LegendMin, LegendMax: Integer;
  ContentMin, ContentMax: Integer;
begin
  Legend.MinMaxWidth(Canvas, LegendMin, LegendMax, AvailableWidth, AvailableHeight);
  inherited ContentMinMaxWidth(Canvas, ContentMin, ContentMax, AvailableWidth, AvailableHeight);
  Min := Math.Max(ContentMin, LegendMin);
  Max := Math.Max(ContentMax, LegendMax);
end;

//-- BG ---------------------------------------------------------- 06.10.2010 --
procedure TfrxHtFieldsetBlock.ConvMargArray(BaseWidth, BaseHeight: Integer; out AutoCount: Integer);
var
  PaddTop, Delta: Integer;
begin
  inherited ConvMargArray(BaseWidth, BaseHeight,AutoCount);
  MargArray[MarginTop] := VMargToMarg(MargArrayO[MarginTop], False, BaseHeight, EmSize, ExSize, 10);
  Delta := Legend.CellHeight - (MargArray[MarginTop] + MargArray[BorderTopWidth] + MargArray[PaddingTop]);
  if Delta > 0 then
  begin
    PaddTop := Delta div 2;
    MargArray[MarginTop] := MargArray[MarginTop] + Delta - PaddTop;
    MargArray[PaddingTop] := MargArray[PaddingTop] + PaddTop;
  end;
end;

//-- BG ---------------------------------------------------------- 05.10.2010 --
constructor TfrxHtFieldsetBlock.Create(Parent: TfrxHtCellBasic; Attributes: TfrxHtAttributeList; Prop: TfrxHTProperties);
var
  Index: ThtPropIndices;
begin
  inherited Create(Parent,Attributes,Prop);
  HasBorderStyle := True;
  for Index := BorderTopStyle to BorderLeftStyle do
    if VarIsIntNull(MargArrayO[Index]) or VarIsEmpty(MargArrayO[Index]) then
      MargArrayO[Index] := bssSolid;
  for Index := BorderTopColor to BorderLeftColor do
    if VarIsIntNull(MargArrayO[Index]) or VarIsEmpty(MargArrayO[Index]) then
      MargArrayO[Index] := RGB(165, 172, 178);
  for Index := BorderTopWidth to BorderLeftWidth do
    if VarIsIntNull(MargArrayO[Index]) or VarIsEmpty(MargArrayO[Index]) then
      MargArrayO[Index] := 1;
//  for Index := MarginTop to MarginLeft do
//    if VarIsIntNull(MargArrayO[Index]) or VarIsEmpty(MargArrayO[Index]) then
//      MargArrayO[Index] := 10;
  for Index := PaddingTop to PaddingLeft do
    if VarIsIntNull(MargArrayO[Index]) or VarIsEmpty(MargArrayO[Index]) then
      MargArrayO[Index] := 10;
  FLegend := TfrxHtBlockCell.Create(Self);
end;

//-- BG ---------------------------------------------------------- 05.10.2010 --
constructor TfrxHtFieldsetBlock.CreateCopy(Parent: TfrxHtCellBasic; Source: TfrxHtmlNode);
var
  T: TfrxHtFieldsetBlock absolute Source;
begin
  inherited CreateCopy(Parent,Source);
  FLegend := TfrxHtBlockCell.CreateCopy(Self, T.FLegend);
end;

//-- BG ---------------------------------------------------------- 05.10.2010 --
destructor TfrxHtFieldsetBlock.Destroy;
begin
  FLegend.Free;
  inherited Destroy;
end;

//-- BG ---------------------------------------------------------- 06.10.2010 --
function TfrxHtFieldsetBlock.Draw1(Canvas: TCanvas; const ARect: TRect; IMgr: TfrxHtIndentManager; X, XRef, YRef: Integer): Integer;
var
  Rect: TRect;
begin
  case Display of
    pdNone:   Result := 0;
  else
    Rect.Left := X + MargArray[MarginLeft] + MargArray[BorderLeftWidth] + MargArray[PaddingLeft] - 2;
    Rect.Right := Rect.Left + Legend.TextWidth + 4;
    Rect.Top := YDraw - Document.YOff;
    Rect.Bottom := Rect.Top + Legend.CellHeight;
    Legend.Draw(Canvas, ARect, ContentWidth, Rect.Left + 2, YDraw, XRef, YRef);
    Rect := CalcClipRect(Canvas, Rect, False);
    ExcludeClipRect(Canvas.Handle, Rect.Left, Rect.Top, Rect.Right, Rect.Bottom);
    Result := inherited Draw1(Canvas, ARect, IMgr, X, XRef, YRef);
  end;
end;

//-- BG ---------------------------------------------------------- 05.10.2010 --
function TfrxHtFieldsetBlock.DrawLogic1(Canvas: TCanvas; X, Y, XRef, YRef, AWidth, AHeight, BlHt: Integer;
  IMgr: TfrxHtIndentManager; var MaxWidth, Curs: Integer; ARemainHeight: Integer): Integer;
var
  BorderWidth: TRect;
  AutoCount, BlockHeight, ScrollWidth, L, LI, RI: Integer;
  SaveID: TObject;
begin
  case Display of

    pdNone:
    begin
      SectionHeight := 0;
      DrawHeight := 0;
      ContentBot := 0;
      DrawBot := 0;
      MaxWidth := 0;
      Result := 0;
    end;

  else
    frxHTMLStyleUn.ConvMargArray(MargArrayO, AWidth, AHeight, EmSize, ExSize, self.BorderWidth, AutoCount, MargArray);
    frxHTMLStyleUn.ApplyBoxSettings(MargArray);
    BorderWidth.Left := MargArray[MarginLeft] + MargArray[BorderLeftWidth] + MargArray[PaddingLeft];
    BorderWidth.Right := MargArray[MarginRight] + MargArray[BorderRightWidth] + MargArray[PaddingRight];
    BorderWidth.Top  := MargArray[MarginTop] + MargArray[BorderTopWidth] + MargArray[PaddingTop];
    if MargArray[piHeight] > 0 then
      BlockHeight := MargArray[piHeight]
    else if AHeight > 0 then
      BlockHeight := AHeight
    else
      BlockHeight := BlHt;

    L := X + BorderWidth.Left;
    ContentWidth := AWidth - BorderWidth.Left - BorderWidth.Right;

    SaveID := IMgr.CurrentID;
    IMgr.CurrentID := Self;
    Legend.IMgr := IMgr;
    LI := IMgr.SetLeftIndent(L, Y);
    RI := IMgr.SetRightIndent(L + ContentWidth, Y);
    Legend.DoLogicX(Canvas, X + BorderWidth.Left, Y, XRef, YRef, ContentWidth, MargArray[piHeight], BlockHeight, ScrollWidth, Curs, ARemainHeight);
    IMgr.FreeLeftIndentRec(LI);
    IMgr.FreeRightIndentRec(RI);
    IMgr.CurrentID := SaveID;

    Result := inherited DrawLogic1(Canvas, X, Y, XRef, YRef, AWidth, AHeight, BlHt, IMgr, MaxWidth, Curs, ARemainHeight);
  end;
end;

{ TfrxHtSizeableObj }

//-- BG ---------------------------------------------------------- 12.11.2011 --
procedure TfrxHtSizeableObj.CalcSize(AvailableWidth, AvailableHeight, SetWidth, SetHeight: Integer; IsClientSizeSpecified: Boolean);
// Extracted from TPanelObj.DrawLogic() and TfrxFrHtImageObj.DrawLogic()

  function CroppedSize(S: Integer; piMin, piMax: ThtPropertyIndex): Integer;
  begin
    Result := S;

    if MargArray[piMax] > 0 then
      Result := Min(Result, MargArray[piMax]);

    if MargArray[piMin] > 0 then
      Result := Max(Result, MargArray[piMin]);
  end;

var
  MargArrayO: ThtVMarginArray;
  H, W: Integer;
begin
  if FProperties <> nil then
    FProperties.GetVMarginArray(MargArrayO)
  else
  begin
    MargArrayO[piMinWidth] := IntNull;
    MargArrayO[piMaxWidth] := IntNull;
    MargArrayO[piMinHeight] := IntNull;
    MargArrayO[piMaxHeight] := IntNull;
  end;

  ConvInlineMargArray(MargArrayO, AvailableWidth, AvailableHeight, EmSize, ExSize, BorderWidth, MargArray);

  if PercentWidth then
  begin
    W := CroppedSize(MulDiv(AvailableWidth, SpecWidth, 100), piMinWidth, piMaxWidth);
    if SpecHeight > 0 then
      if PercentHeight then
        H := MulDiv(AvailableHeight, SpecHeight, 100)
      else
        H := SpecHeight
    else
      H := MulDiv(W, SetHeight, SetWidth);
    H := CroppedSize(H, piMinHeight, piMaxHeight);
  end
  else if PercentHeight then
  begin
    H := CroppedSize(MulDiv(AvailableHeight, SpecHeight, 100), piMinHeight, piMaxHeight);
    if SpecWidth > 0 then
      W := SpecWidth
    else
      W := MulDiv(H, SetWidth, SetHeight);
    W := CroppedSize(W, piMinWidth, piMaxWidth);
  end
  else if (SpecWidth > 0) and (SpecHeight > 0) then
  begin {Both width and height specified}
    H := SpecHeight;
    W := SpecWidth;
    H := CroppedSize(H, piMinHeight, piMaxHeight);
    W := CroppedSize(W, piMinWidth, piMaxWidth);
    ClientSizeKnown := True;
  end
  else if SpecHeight > 0 then
  begin
    H := SpecHeight;
    H := CroppedSize(H, piMinHeight, piMaxHeight);
    W := MulDiv(H, SetWidth, SetHeight);
    W := CroppedSize(W, piMinWidth, piMaxWidth);
    ClientSizeKnown := IsClientSizeSpecified;
  end
  else if SpecWidth > 0 then
  begin
    W := SpecWidth;
    W := CroppedSize(W, piMinWidth, piMaxWidth);
    H := MulDiv(W, SetHeight, SetWidth);
    H := CroppedSize(H, piMinHeight, piMaxHeight);
    ClientSizeKnown := IsClientSizeSpecified;
  end
  else
  begin {neither height and width specified}
    H := SetHeight;
    W := SetWidth;
    CalcAutoMinMaxConstraints(W, H, MargArray[piMinWidth], MargArray[piMaxWidth], MargArray[piMinHeight], MargArray[piMaxHeight], W, H);
    ClientSizeKnown := IsClientSizeSpecified;
  end;

  ClientWidth := W;
  ClientHeight := H;
  if ClientSizeKnown then
    if (Pos('%', MargArrayO[piMinWidth]) > 0) or (Pos('%', MargArrayO[piMaxWidth]) > 0) or (Pos('%', MargArrayO[piMinHeight]) > 0) or (Pos('%', MargArrayO[piMaxHeight]) > 0) then
      ClientSizeKnown := False;
end;

//-- BG ---------------------------------------------------------- 12.11.2011 --
constructor TfrxHtSizeableObj.Create(Parent: TfrxHtCellBasic; Position: Integer; L: TfrxHtAttributeList; Prop: TfrxHTProperties);
var
  I: Integer;
  NewHSpace: Integer;
  NewVSpace: Integer;
  S: ThtString;
begin
  inherited Create(Parent,Position,L,Prop);
  NewHSpace := -1;
  NewVSpace := -1;
  SpecHeight := -1;
  SpecWidth := -1;

  for I := 0 to L.Count - 1 do
    with L[I] do
      case Which of

        HeightSy:
        begin
          if System.Pos('%', Name) = 0 then
          begin
              SpecHeight := Value;
          end
          else if (Value >= 0) and (Value <= 100) then
          begin
            SpecHeight := Value;
            PercentHeight := True;
          end;
        end;

        WidthSy:
          if System.Pos('%', Name) = 0 then
          begin
            SpecWidth := Value;
          end
          else if (Value >= 0) and (Value <= 100) then
          begin
            SpecWidth := Value;
            PercentWidth := True;
          end;

        HSpaceSy:
          NewHSpace := Min(40, Abs(Value));

        VSpaceSy:
          NewVSpace := Min(40, Abs(Value));

        AlignSy:
          begin
            S := htUpperCase(htTrim(Name));
            if S = 'TOP' then
              VertAlign := ATop
            else if (S = 'MIDDLE') or (S = 'ABSMIDDLE') then
              VertAlign := AMiddle
            else if S = 'LEFT' then
            begin
              VertAlign := ANone;
              FFloating := ALeft;
            end
            else if S = 'RIGHT' then
            begin
              VertAlign := ANone;
              FFloating := ARight;
            end;
          end;
      end;

  if NewVSpace >= 0 then
  begin
    VSpaceT := NewVSpace;
    VSpaceB := VSpaceT;
  end;

  if NewHSpace >= 0 then
  begin
    HSpaceL := NewHSpace;
    HSpaceR := HSpaceL;
  end
  else
  begin
    HSpaceR := HSpaceL;
  end;

end;

constructor TfrxHtSizeableObj.CreateCopy(Parent: TfrxHtCellBasic; Source: TfrxHtmlNode);
var
  T: TfrxHtSizeableObj absolute Source;
begin
  inherited CreateCopy(Parent,Source);
  BorderWidth := T.BorderWidth;
  FAlt := T.FAlt;
  FClientHeight := T.FClientHeight;
  FClientWidth := T.FClientWidth;
  ClientSizeKnown := T.ClientSizeKnown;
  NoBorder := T.NoBorder;
  SpecHeight := T.SpecHeight;
  SpecWidth := T.SpecWidth;
  Title := T.Title;
end;

//-- BG ---------------------------------------------------------- 30.08.2013 --
procedure TfrxHtSizeableObj.DrawInline(Canvas: TCanvas; X, Y, YBaseline: Integer; FO: TfrxHtFontObj);
begin
  if not IsCopy then
  begin
    DrawXX := X;
    DrawYY := Y;
  end;
end;

//-- BG ---------------------------------------------------------- 21.09.2016 --
procedure TfrxHtSizeableObj.DrawLogicInline(Canvas: TCanvas; FO: TfrxHtFontObj;
  AvailableWidth, AvailableHeight: Integer);
begin

end;

//-- BG ---------------------------------------------------------- 06.08.2013 --
function TfrxHtSizeableObj.GetClientHeight: Integer;
begin
  Result := FClientHeight;
end;

//-- BG ---------------------------------------------------------- 06.08.2013 --
function TfrxHtSizeableObj.GetClientWidth: Integer;
begin
  Result := FClientWidth;
end;

procedure TfrxHtSizeableObj.ProcessProperties(Prop: TfrxHTProperties);
const
  DummyHtWd = 200;
var
  MargArrayO: ThtVMarginArray;
  Align: ThtAlignmentStyle;
begin
  if Prop.GetVertAlign(Align) then
    VertAlign := Align;
  if Prop.GetFloat(Align) and (Align <> ANone) then
  begin
//    if HSpaceR = 0 then
//    begin {default is different for Align = left/right}
//      HSpaceR := ImageSpace;
//      HSpaceL := ImageSpace;
//    end;
    VertAlign := ANone;
    FFloating := Align;
  end;
  if Title = '' then {a Title attribute will have higher priority than inherited}
    Title := Prop.PropTitle;
  Prop.GetVMarginArray(MargArrayO);
  EmSize := Prop.EmSize;
  ExSize := Prop.ExSize;
  ConvInlineMargArray(MargArrayO, DummyHtWd, DummyHtWd, EmSize, ExSize, BorderWidth, MargArray);

  if MargArray[MarginLeft] <> IntNull then
    HSpaceL := MargArray[MarginLeft];
  if MargArray[MarginRight] <> IntNull then
    HSpaceR := MargArray[MarginRight];
  if MargArray[MarginTop] <> IntNull then
    VSpaceT := MargArray[MarginTop];
  if MargArray[MarginBottom] <> IntNull then
    VSpaceB := MargArray[MarginBottom];

  if MargArray[piWidth] <> IntNull then
  begin
    PercentWidth := False;
    if MargArray[piWidth] <> Auto then
      if (VarIsStr(MargArrayO[piWidth]))
        and (System.Pos('%', MargArrayO[piWidth]) > 0) then
      begin
        PercentWidth := True;
        SpecWidth := MulDiv(MargArray[piWidth], 100, DummyHtWd);
      end
      else
        SpecWidth := MargArray[piWidth];
  end;
  if MargArray[piHeight] <> IntNull then
  begin
    PercentHeight := False;
    if MargArray[piHeight] <> Auto then
      if (VarIsStr(MargArrayO[piHeight]))
        and (System.Pos('%', MargArrayO[piHeight]) > 0) then
      begin
        PercentHeight := True;
        SpecHeight := MulDiv(MargArray[piHeight], 100, DummyHtWd);
      end
      else
        SpecHeight := MargArray[piHeight];
  end;

  if Prop.GetVertAlign(Align) then
    VertAlign := Align;
  if Prop.GetFloat(Align) and (Align <> ANone) then
    FFloating := Align;

  if Prop.BorderStyleNotBlank then
  begin
    NoBorder := True; {will have inline border instead}
    BorderWidth := 0;
  end
  else if Prop.HasBorderStyle then
  begin
    Inc(HSpaceL, MargArray[BorderLeftWidth]);
    Inc(HSpaceR, MargArray[BorderRightWidth]);
    Inc(VSpaceT, MargArray[BorderTopWidth]);
    Inc(VSpaceB, MargArray[BorderBottomWidth]);
  end;
end;

//-- BG ---------------------------------------------------------- 30.11.2010 --
procedure TfrxHtSizeableObj.SetAlt(CodePage: Integer; const Value: ThtString);
begin
  FAlt := Value;
  while Length(FAlt) > 0 do
    case FAlt[Length(FAlt)] of
      CrChar, LfChar:
        Delete(FAlt, Length(FAlt), 1);
    else
      break;
    end;
end;

//-- BG ---------------------------------------------------------- 31.08.2013 --
procedure TfrxHtSizeableObj.SetClientHeight(Value: Integer);
begin
  FClientHeight := Value;
end;

//-- BG ---------------------------------------------------------- 31.08.2013 --
procedure TfrxHtSizeableObj.SetClientWidth(Value: Integer);
begin
  FClientWidth := Value;
end;

//-- BG ---------------------------------------------------------- 12.11.2011 --
constructor TfrxHtSizeableObj.SimpleCreate(Parent: TfrxHtCellBasic);
begin
  inherited Create(Parent, 0, nil, nil);
  VertAlign := ABottom; {default}
  NoBorder := True;
  BorderWidth := 0;
  SpecHeight := -1;
  SpecWidth := -1;
end;

{ TfrxHtSectionBase }

//-- BG ---------------------------------------------------------- 20.09.2009 --
constructor TfrxHtSectionBase.Create(Parent: TfrxHtCellBasic; Attributes: TfrxHtAttributeList; Properties: TfrxHTProperties);
var
  TheId, TheClass: ThtString;
begin
  if Properties <> nil then
  begin
    TheID := Properties.PropID;
    TheClass := Properties.PropClass;
  end
  else if Attributes <> nil then
  begin
    TheId := Attributes.TheId;
    TheClass := Attributes.TheClass;
  end;
  Create(Parent, Attributes, Properties, TheId, TheClass);
end;

//-- BG ---------------------------------------------------------- 20.09.2009 --
constructor TfrxHtSectionBase.Create(Parent: TfrxHtCellBasic; Attributes: TfrxHtAttributeList; Properties: TfrxHTProperties; const TheId, TheClass: ThtString);
begin
  inherited Create(Parent, Attributes, Properties, TheId);
  if Properties <> nil then
  begin
    TagClass := Properties.PropTag;
  end;
  htAppendStr(TagClass, '.' + TheClass + '#' + TheId);

  ContentTop := 999999999; {large number in case it has Display: none; }
end;

constructor TfrxHtSectionBase.CreateCopy(Parent: TfrxHtCellBasic; Source: TfrxHtmlNode);
var
  T: TfrxHtSectionBase absolute Source;
begin
  inherited CreateCopy(Parent,Source);
  StartCurs := T.StartCurs;
  Len := T.Len;
  SectionHeight := T.SectionHeight;
  ZIndex := T.ZIndex;
end;

function TfrxHtSectionBase.GetYPosition: Integer;
begin
  Result := ContentTop;
end;

function TfrxHtSectionBase.DrawLogic1(Canvas: TCanvas; X, Y, XRef, YRef, AWidth, AHeight, BlHt: Integer; IMgr: TfrxHtIndentManager;
  var MaxWidth, Curs: Integer; ARemainHeight: Integer): Integer;
// Computes all coordinates of the section.
//
// Normal sections, absolutely positioned blocks and floating blocks start at given (X,Y) relative to document origin.
// Table cells start at given (X,Y) coordinates relative to the outmost containing block.
//
// Returns the nominal height of the section (without overhanging floating blocks)
var
  ClearAddon: Integer;
begin
  case FDisplay of
    pdNone:
      ClearAddon := 0;
  else
    ClearAddon := GetClearSpace(IMgr, Y);
  end;

  StartCurs := Curs;
  Len := 0;
  MaxWidth := 0;
  SectionHeight := 0;
  DrawHeight := SectionHeight;
  YDraw := Y + ClearAddon;

  ContentTop := YDraw;
  ContentBot := ContentTop + SectionHeight;

  DrawTop := YDraw;
  DrawBot := DrawTop + DrawHeight;

  Result := ContentBot - Y;
end;

function TfrxHtSectionBase.Draw1(Canvas: TCanvas; const ARect: TRect; IMgr: TfrxHtIndentManager; X, XRef, YRef: Integer): Integer;
// returns the pixel row, where the section ends.
begin
  Result := YDraw + SectionHeight;
end;

function TfrxHtSectionBase.FindString(From: Integer; const ToFind: UnicodeString; MatchCase: Boolean): Integer;
begin
  Result := -1;
end;

function TfrxHtSectionBase.FindStringR(From: Integer; const ToFind: UnicodeString; MatchCase: Boolean): Integer;
begin
  Result := -1;
end;

function TfrxHtSectionBase.FindSourcePos(DocPos: Integer): Integer;
begin
  Result := -1;
end;

function TfrxHtSectionBase.FindDocPos(SourcePos: Integer; Prev: Boolean): Integer;
begin
  Result := -1;
end;

function TfrxHtSectionBase.GetChAtPos(Pos: Integer; out Ch: WideChar; out Obj: TfrxHtSectionBase): Boolean;
begin
  Result := False;
  Ch := #0;
  Obj := nil;
end;

procedure TfrxHtSectionBase.SetDocument(List: TfrxHtDocument);
begin
  FDocument := List;
end;

procedure TfrxHtSectionBase.MinMaxWidth(Canvas: TCanvas; out Min, Max: Integer; AvailableWidth, AvailableHeight: Integer);
begin
  Min := 0;
  Max := 0;
end;

procedure TfrxHtSectionBase.AddSectionsToList;
begin
  Document.addSectionsToPositionList(Self);
end;

{ TfrxHtSectionBaseList }

function TfrxHtSectionBaseList.FindDocPos(SourcePos: Integer; Prev: Boolean): Integer;
var
  I: Integer;
begin
  Result := -1;
  if not Prev then
    for I := 0 to Count - 1 do
    begin
      Result := Items[I].FindDocPos(SourcePos, Prev);
      if Result >= 0 then
        Break;
    end
  else {Prev, iterate backwards}
    for I := Count - 1 downto 0 do
    begin
      Result := Items[I].FindDocPos(SourcePos, Prev);
      if Result >= 0 then
        Break;
    end
end;

function TfrxHtSectionBaseList.GetItem(Index: Integer): TfrxHtSectionBase;
begin
  Result := inherited Get(Index);
end;

{ TfrxHtChPosObj }

//-- BG ---------------------------------------------------------- 04.03.2011 --
constructor TfrxHtChPosObj.Create(Document: TfrxHtDocument; Pos: Integer);
begin
  inherited Create('');
  FChPos := Pos;
  FDocument := Document;
end;

//-- BG ---------------------------------------------------------- 06.03.2011 --
function TfrxHtChPosObj.FreeMe: Boolean;
begin
  Result := True;
end;

function TfrxHtChPosObj.GetYPosition: Integer;
begin
  Result := 0;
end;

//-- BG ---------------------------------------------------------- 26.12.2011 --
function TfrxHtRowList.GetItem(Index: Integer): TfrxHtCellList;
begin
  Result := Get(Index);
end;

{ TfrxHtColSpecList }

//-- BG ---------------------------------------------------------- 26.12.2011 --
function TfrxHtColSpecList.GetItem(Index: Integer): TfrxHtColSpec;
begin
  Result := Get(Index);
end;

{ TfrxHtColSpec }

//-- BG ---------------------------------------------------------- 12.01.2012 --
constructor TfrxHtColSpec.Create(const Width: TSpecWidth; Align: ThtString; VAlign: ThtAlignmentStyle);
begin
  inherited Create;
  FWidth := Width;
  FAlign := Align;
  FVAlign := VAlign;
end;

//-- BG ---------------------------------------------------------- 27.01.2012 --
constructor TfrxHtColSpec.CreateCopy(const ColSpec: TfrxHtColSpec);
begin
  Create(ColSpec.FWidth, ColSpec.FAlign, ColSpec.FVAlign);
end;

{ TfrxHtFloatingObj }

//-- BG ---------------------------------------------------------- 12.11.2011 --
function TfrxHtFloatingObj.Clone(Parent: TfrxHtCellBasic): TfrxHtFloatingObj;
begin
  Result := TFloatingObjClass(ClassType).CreateCopy(Parent, Self);
end;

//-- BG ---------------------------------------------------------- 04.08.2013 --
constructor TfrxHtFloatingObj.Create(Parent: TfrxHtCellBasic; Position: Integer; L: TfrxHtAttributeList; Prop: TfrxHTProperties);
begin
  inherited Create(Parent,Position,L,Prop);
  StartCurs := Position;
  VertAlign := ABottom; {default}
end;

//-- BG ---------------------------------------------------------- 04.08.2013 --
constructor TfrxHtFloatingObj.CreateCopy(Parent: TfrxHtCellBasic; Source: TfrxHtmlNode);
var
  T: TfrxHtFloatingObj absolute Source;
begin
  inherited CreateCopy(Parent,Source);
  System.Move(T.VertAlign, VertAlign, PtrSub(@PercentHeight, @VertAlign) + SizeOf(PercentHeight));
  DrawXX := T.DrawXX;
  DrawYY := T.DrawYY;
end;

//-- BG ---------------------------------------------------------- 08.09.2013 --
function TfrxHtFloatingObj.Draw1(Canvas: TCanvas; const ARect: TRect; IMgr: TfrxHtIndentManager; X, XRef, YRef: Integer): Integer;
begin
  Result := ContentBot;
end;

//-- BG ---------------------------------------------------------- 08.09.2013 --
function TfrxHtFloatingObj.DrawLogic1(Canvas: TCanvas; X, Y, XRef, YRef, AWidth, AHeight, BlHt: Integer; IMgr: TfrxHtIndentManager;
  var MaxWidth, Curs: Integer; ARemainHeight: Integer): Integer;
begin
  if Positioning in [posAbsolute, posFixed] then
    Result := 0
  else
    Result := SectionHeight;
end;

//-- BG ---------------------------------------------------------- 14.02.2016 --
function TfrxHtFloatingObj.GetYPosition: Integer;
begin
  Result := DrawYY;
end;

//-- BG ---------------------------------------------------------- 02.03.2011 --
function TfrxHtFloatingObj.TotalHeight: Integer;
begin
  Result := VSpaceT + ClientHeight + VSpaceB;
end;

//-- BG ---------------------------------------------------------- 02.03.2011 --
function TfrxHtFloatingObj.TotalWidth: Integer;
begin
  Result := HSpaceL + ClientWidth + HSpaceR;
end;

{ TFloatingObjList }

//-- BG ---------------------------------------------------------- 05.08.2013 --
constructor TFloatingObjList.CreateCopy(Parent: TfrxHtCellBasic; T: TFloatingObjList);
var
  I: Integer;
begin
  inherited Create;
  if T <> nil then
    for I := 0 to T.Count - 1 do
      Add(T.Items[I].Clone(Parent));
end;

//-- BG ---------------------------------------------------------- 07.08.2013 --
procedure TFloatingObjList.Decrement(N: Integer);
{called when a character is removed to change the Position figure}
var
  I: Integer;
begin
  for I := Count - 1 downto 0 do
    with Items[I] do
      if StartCurs > N then
        Dec(StartCurs)
      else
        break;
end;

//-- BG ---------------------------------------------------------- 07.08.2013 --
function TFloatingObjList.FindObject(Posn: Integer): TfrxHtFloatingObj;
{find the object at a given character position}
begin
  if GetObjectAt(Posn, Result) <> 0 then
    Result := nil;
end;

//-- BG ---------------------------------------------------------- 07.08.2013 --
function TFloatingObjList.GetItem(Index: Integer): TfrxHtFloatingObj;
begin
  Result := Get(Index);
end;

//-- BG ---------------------------------------------------------- 05.08.2013 --
function TFloatingObjList.GetObjectAt(Posn: Integer; out Obj): Integer;
var
  I: Integer;
begin
  for I := 0 to Count - 1 do
  begin
    Result := Items[I].StartCurs - Posn;
    if Result >= 0 then
    begin
      TfrxHtFloatingObj(Obj) := Items[I];
      Exit;
    end;
  end;

  TfrxHtFloatingObj(Obj) := nil;
  Result := 99999999;
end;

//-- BG ---------------------------------------------------------- 07.08.2013 --
procedure TFloatingObjList.SetItem(Index: Integer; const Item: TfrxHtFloatingObj);
begin
  Put(Index, Item);
end;

{ TfrxHtBlockBase }

//-- BG ---------------------------------------------------------- 31.08.2013 --
constructor TfrxHtBlockBase.Create(Parent: TfrxHtCellBasic; Position: Integer; Attributes: TfrxHtAttributeList; Prop: TfrxHTProperties);
begin
  inherited Create(Parent, Attributes, Prop);
  if FDisplay = pdUnassigned then
    FDisplay := pdBlock;
  StartCurs := Position;
end;

//-- BG ---------------------------------------------------------- 31.08.2013 --
constructor TfrxHtBlockBase.CreateCopy(Parent: TfrxHtCellBasic; Source: TfrxHtmlNode);
//var
//  T: TfrxHtBlockBase absolute Source;
begin
  inherited CreateCopy(Parent,Source);
end;

function TfrxHtBlockBase.GetPosition(const Index: ThtRectEdge): Integer;
begin
  Result := FPositions[Index];
end;

//{ TRenderSectionBaseList }
//
////-- BG ---------------------------------------------------------- 01.12.2013 --
//procedure TRenderSectionBaseList.Notify(Ptr: Pointer; Action: TListNotification);
//var
//  Obj: TObject absolute Ptr;
//begin
//  inherited;
//  if (Action = lnDeleted) and (Obj is TInlineSection) then
//    Obj.Free;
//end;
//
//{ TInlineSection }
//
////-- BG ---------------------------------------------------------- 01.12.2013 --
//procedure TInlineSection.AddElement(Section: TfrxHtSection);
//var
//  I: Integer;
//begin
//  // append Buff, Fonts, Images and FormControls
//  BuffS := Buffs + Section.BuffS;
//  Buff := PWideChar(Buffs);
//
//  for I := 0 to Section.Fonts.Count - 1 do
//    Fonts.Add(TfrxHtFontObj.CreateCopy(Section, Section.Fonts[I]));
//
//  for I := 0 to Section.Images.Count - 1 do
//    Images.Add(Section.Images[I].Clone(Section.Images[I].OwnerCell));
//
//  for I := 0 to Section.FormControls.Count - 1 do
//    FormControls.Add(Section.FormControls[I].Clone(Section.FormControls[I].OwnerCell));
//end;
//
////-- BG ---------------------------------------------------------- 01.12.2013 --
//constructor TInlineSection.Create(Parent: TfrxHtCellBasic);
//begin
//  inherited Create(Parent);
//end;

//-- BG ---------------------------------------------------------- 24.02.2016 --

{ TfrxHtLineBreak }

//-- BG ---------------------------------------------------------- 24.02.2016 --
function TfrxHtLineBreak.TryGetClear(var Clear: ThtClearStyle): Boolean;
var
  T: TfrxHtAttribute;
begin
  Result := inherited TryGetClear(Clear);
  if not Result then
    if FAttributes <> nil then
    begin
      T := nil;
      if FAttributes.Find(ClearSy, T) then
        Result := TryStrToClearStyle(LowerCase(T.Name), Clear);
    end;
end;

{ TfrxHtIndexObjList }

//-- BG ---------------------------------------------------------- 06.10.2016 --
function TfrxHtIndexObjList.Get(Index: Integer): TfrxHtIndexObj;
begin
  Result := inherited Get(Index);
end;

{ TfrxHtBorderRecList }

//-- BG ---------------------------------------------------------- 06.10.2016 --
function TfrxHtBorderRecList.Get(Index: Integer): TfrxHtBorderRec;
begin
  Result := inherited Get(Index);
end;

{ TfrxHtLineRecList }

//-- BG ---------------------------------------------------------- 06.10.2016 --
function TfrxHtLineRecList.Get(Index: Integer): TfrxHtLineRec;
begin
  Result := inherited Get(Index);
end;

{ TSameWidthFix }

constructor TSameWidthFix.Create(Widths, MinWidths, MaxWidths: TIntArray; ColumnSpecs: TWidthTypeArray);
var
  i, j, Len: Integer;
begin
  Len := Length(Widths);
  SetLength(FNeedFix, Len);
  SetLength(FFirstIndex, Len);
  for i := 0 to Len - 2 do
    if (ColumnSpecs[i] = wtNone) and not FNeedFix[i] then
      for j := i + 1 to Len - 1 do
        if (ColumnSpecs[j] = wtNone) and not FNeedFix[j] and (Widths[i] = Widths[j]) and
           (MinWidths[i] = MinWidths[j]) and (MaxWidths[i] = MaxWidths[j]) then
        begin
          FAtLeastOneFix := True;
          FNeedFix[i] := True;
          FNeedFix[j] := True;
          FFirstIndex[i] := i;
          FFirstIndex[j] := i;
        end;
end;

procedure TSameWidthFix.Fix(Widths: TIntArray);
var
  i, j, Len, SumWidth, Count: Integer;
begin
  if not FAtLeastOneFix then
    Exit;
  Len := Length(Widths);
  for i := 0 to Len - 2 do
    if FNeedFix[i] and (FFirstIndex[i] = i) then
    begin
      SumWidth := 0;
      Count := 0;
      for j := i to Len - 1 do
        if FFirstIndex[j] = i then
        begin
          SumWidth := SumWidth + Widths[j];
          Inc(Count);
        end;
      for j := i to Len - 1 do
        if FFirstIndex[j] = i then
        begin
          Widths[j] := Round(SumWidth / Count);
          SumWidth := SumWidth - Widths[j];
          Dec(Count);
        end;
    end;
end;

procedure TfrxHtRowList.RTLSwap;
var
  i: Integer;
begin
  for i := 0 to Count - 1 do
    Items[i].RTLSwap;
end;

initialization
{$ifdef UNICODE}
{$else}
  {$ifdef UseElPack}
    UnicodeControls := True;
  {$endif}
{$endif}
  WaitStream := TMemoryStream.Create;
  ErrorStream := TMemoryStream.Create;
  WaitBitmap := TBitmap.Create;
  ErrorBitmap := TBitmap.Create;
finalization
  ErrorBitmap.Free;
  WaitBitmap.Free;
  ErrorStream.Free;
  WaitStream.Free;
end.