FastReport_2022_VCL/LibD28x64/frxHTMLUn2.pas
2024-01-01 16:13:08 +01:00

2279 lines
64 KiB
ObjectPascal

{
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}
unit frxHTMLUn2;
interface
uses
{$ifdef UseInline}
Math,
{$endif}
{$ifdef LCL}
LclIntf, IntfGraphics, FpImage, LclType, LResources, LMessages, frxHTMLMisc,
{$else}
Windows,
{$endif}
SysUtils, Contnrs, Classes, Graphics, ClipBrd, Messages, Variants, Types,
{$ifdef Compiler20_Plus}
CommCtrl,
{$endif}
frxHTMLBuffer,
frxGif2,
frxHTMLGlobals,
frxHTMLImages,
frxHTMLStyleTypes,
frxHTMLSymb;
const
VersionNo = '11.9';
MaxHScroll = 100000; {max horizontal display in pixels}
Tokenleng = 300;
TopLim = -200; {drawing limits}
BotLim = 5000;
FmCtl = WideChar(#2);
ImgPan = WideChar(#4);
BrkCh = WideChar(#8);
htDefFontName = 'Serif';
htDefPreFontName = 'Monospace';
type
// BG, 26.12.2011:
TWidthType = (
wtNone,
wtAbsolute,
wtPercent,
wtRelative);
// BG, 26.12.2011:
TSpecWidth = record
Value: Integer;
VType: TWidthType; // treat wtNone like "0*" (Value = 0.0, CType = wtRelative)
end;
ThtJustify = (NoJustify, Left, Centered, Right, FullJustify);
ThtDirection = (diLTR, diRTL, diAuto);
TRowType = (THead, TBody, TFoot);
//------------------------------------------------------------------------------
// tag attributes
//------------------------------------------------------------------------------
TfrxHtAttribute = class {holds a tag attribute}
public
Which: TAttrSymb; {symbol of attribute such as HrefSy}
WhichName: ThtString;
Value: Integer; {numeric value if appropriate}
DblValue: Double; {numeric value if appropriate}
Name: ThtString; {ThtString (mixed case), value after '=' sign}
CodePage: Integer;
constructor Create(ASym: TAttrSymb; const AValue: Double; const NameStr, ValueStr: ThtString; ACodePage: Integer);
constructor CreateCopy(ASource: TfrxHtAttribute);
property AsString: ThtString read Name;
property AsInteger: Integer read Value;
property AsDouble: Double read DblValue;
end;
TfrxHtAttributeList = class(TObjectList) {a list of tag attributes,(TAttributes)}
private
SaveID: ThtString;
function GetClass: ThtString;
function GetID: ThtString;
function GetTitle: ThtString;
function GetAttribute(Index: Integer): TfrxHtAttribute; {$ifdef UseInline} inline; {$endif}
public
constructor CreateCopy(ASource: TfrxHtAttributeList);
function Clone: TfrxHtAttributeList; virtual;
procedure Clear; override;
function Find(const Name: ThtString; var T: TfrxHtAttribute): Boolean; overload; {$ifdef UseInline} inline; {$endif}
function Find(Sy: TAttrSymb; var T: TfrxHtAttribute): Boolean; overload; {$ifdef UseInline} inline; {$endif}
function CreateStringList: ThtStringList;
property TheClass: ThtString read GetClass;
property TheID: ThtString read GetID;
property TheTitle: ThtString read GetTitle;
property Items[Index: Integer]: TfrxHtAttribute read GetAttribute; default;
end;
//------------------------------------------------------------------------------
//
//------------------------------------------------------------------------------
TfrxHtFontObjBaseList = class;
{holds start and end point of URL text}
TutText = record //BG, 03.03.2011: changed to record. no need to use a class
Start: Integer;
Last: Integer;
end;
TfrxHtUrlTarget = class
public
URL: ThtString;
Target: ThtString;
ID: Integer;
Attr: ThtString;
utText: TutText;
TabIndex: Integer;
constructor Create;
destructor Destroy; override;
procedure Assign(const AnUrl, ATarget: ThtString; L: TfrxHtAttributeList; AStart: Integer); overload;
procedure Assign(const UT: TfrxHtUrlTarget); overload;
procedure Clear;
procedure SetLast(List: TfrxHtFontObjBaseList; ALast: Integer);
property Start: Integer read utText.Start;
property Last: Integer read utText.Last;
end;
TfrxHtFontObjBase = class {font information}
public
UrlTarget: TfrxHtUrlTarget;
end;
TfrxHtFontObjBaseList = class(TObjectList)
private
function GetBase(Index: Integer): TfrxHtFontObjBase; {$ifdef UseInline} inline; {$endif}
public
property Items[Index: Integer]: TfrxHtFontObjBase read GetBase; default;
end;
//------------------------------------------------------------------------------
// indentation manager
//------------------------------------------------------------------------------
TfrxHtIndentRec = class
public
X: Integer; // left or right indentation relative to LfEdge.
YT: Integer; // top Y inclusive coordinate for this record relative to document top.
YB: Integer; // bottom Y exclusive coordinate for this record relative to document top.
ID: TObject; // block level indicator for this record, 0 for not applicable
end;
TfrxHtIndentRecList = class(TObjectList)
private
function Get(Index: Integer): TfrxHtIndentRec; {$ifdef UseInline} inline; {$endif}
public
property Items[Index: Integer]: TfrxHtIndentRec read Get; default;
end;
TfrxHtIndentManager = class
private
function LeftEdge(Y: Integer): Integer;
function RightEdge(Y: Integer): Integer;
public
LfEdge: Integer; // left edge of the block content area.
// TfrxHtCell.DoLogic calculates with LfEdge = 0.
// TfrxHtCell.Draw then may shift the block by setting LfEdge to X.
Width: Integer; // width of the block content area.
ClipWidth: Integer; // clip width ???
L: TfrxHtIndentRecList; // list of left side indentations of type IndentRec.
R: TfrxHtIndentRecList; // list of right side indentations of type IndentRec.
CurrentID: TObject; // the current block level (a TfrxHTBlock pointer)
LTopMin: Integer;
RTopMin: Integer;
public
constructor Create;
destructor Destroy; override;
function AddLeft(YT, YB, W: Integer): TfrxHtIndentRec;
function AddRight(YT, YB, W: Integer): TfrxHtIndentRec;
function AlignLeft(var Y: Integer; W: Integer): Integer;
function AlignRight(var Y: Integer; W: Integer): Integer;
function GetNextWiderY(Y: Integer): Integer;
function ImageBottom: Integer;
function LeftIndent(Y: Integer): Integer;
function RightSide(Y: Integer): Integer;
function SetLeftIndent(XLeft, Y: Integer): Integer;
function SetRightIndent(XRight, Y: Integer): Integer;
procedure FreeLeftIndentRec(I: Integer);
procedure FreeRightIndentRec(I: Integer);
procedure GetClearY(out CL, CR: Integer);
procedure Init(Lf, Wd: Integer);
procedure Reset(Lf: Integer);
// AdjustY() is called after an inline row has been produced. If floating objects have been moved
// down before the actual height of the entire row was computed, their Y coordinates aren't too
// small now. AdjustY() moves them down below given Y + Height.
procedure AdjustY(FirstLeftIndex, FirstRightIndex, Y, Height: Integer);
end;
//------------------------------------------------------------------------------
// parser
//------------------------------------------------------------------------------
{Simplified variant of TokenObj, to temporarily keep a ThtString of unicode
characters along with their original indices.}
{ TfrxHtCharCollection }
TfrxHtCharCollection = class
private
FChars: ThtString;
FIndices: array of Integer;
FCurrentIndex: Integer;
function GetSize: Integer;
function GetAsString: ThtString;
function GetCapacity: Integer;
procedure SetCapacity(NewCapacity: Integer);
public
constructor Create;
procedure Add(C: ThtChar; Index: Integer); overload;
procedure Add(const S: ThtString; Index: Integer); overload;
procedure Clear;
// procedure Concat(T: TfrxHtCharCollection);
property AsString: ThtString read GetAsString;
property Capacity: Integer read GetCapacity write SetCapacity;
property Size: Integer read GetSize;
end;
{ TokenObj }
TfrxHtTokenObj = class
private
St: UnicodeString;
StringOK: boolean;
FCount: Integer;
function GetCapacity: Integer;
function GetString: UnicodeString;
procedure SetCapacity(NewCapacity: Integer);
public
C: array of ThtChar;
I: array of Integer;
constructor Create;
procedure AddUnicodeChar(Ch: WideChar; Ind: Integer);
procedure AddString(S: TfrxHtCharCollection);
procedure Clear;
property Capacity: Integer read GetCapacity write SetCapacity;
property Count: Integer read FCount;
property S: UnicodeString read GetString;
end;
//------------------------------------------------------------------------------
// TfrxHtIDObject is base class for all tag objects.
//------------------------------------------------------------------------------
// If they have an ID, the parser puts them into the HtmlViewer's IDNameList,
// a TfrxHtIDObjectList, where they can be obtained from by ID.
// Their Y coordinates can be retrieved and HtmlViewer can scroll to them.
//
// Most descendants are objects representing an HTML tag, except TfrxHtChPosObj.
//------------------------------------------------------------------------------
TfrxHtIDObject = class
private
function GetId(): ThtString; //>-- DZ
protected
FHtmlId: ThtString; //>-- DZ real ID from HTML if any
FGlobalId: ThtString; //>-- DZ global unique ID
function GetYPosition: Integer; virtual; abstract;
function FreeMe: Boolean; virtual; // some objects the TIDObjectsList owns, some others not.
public
constructor Create(const AHtmlID: ThtString);
property YPosition: Integer read GetYPosition;
property Id: ThtString read GetId; //>-- DZ if FhtmlId then FglobalId will be returned as result
property HtmlId: ThtString read FHtmlId; //>-- DZ
property GlobalId: ThtString read FGlobalId; //>-- DZ
end;
//BG, 04.03.2011: TIDNameList renamed to TfrxHtIDObjectList and used TObject changed to TfrxHtIDObject.
TfrxHtIDObjectList = class(ThtStringList)
private
function GetObject(Index: Integer): TfrxHtIDObject; reintroduce;
public
constructor Create;
destructor Destroy; override;
function AddObject(const S: ThtString; AObject: TfrxHtIDObject): Integer; reintroduce;
procedure Clear; override;
property Objects[Index: Integer]: TfrxHtIDObject read GetObject; default;
end;
ThtColorArray = packed array[0..3] of TColor;
ThtBorderStyleArray = packed array[0..3] of ThtBorderStyle;
//BG, 11.09.2010: moved to this unit to reduce circular dependencies:
ThtguResultType = set of (guUrl, guControl, guTitle);
//------------------------------------------------------------------------------
// TfrxHtControlBase is base class for TfrxHtViewerBase and TFrameBase
//------------------------------------------------------------------------------
TfrxHtControlBase = class(TPersistent);
//------------------------------------------------------------------------------
// TfrxHtViewerBase is base class for THtmlViewer and TFrameViewer
//------------------------------------------------------------------------------
TGetStreamEvent = procedure(Sender: TObject; const SRC: ThtString; var Stream: TStream) of object;
TGottenStreamEvent = TGetStreamEvent;
TIncludeType = procedure(Sender: TObject; const Command: ThtString; Params: ThtStrings; out IncludedDocument: TBuffer) of object;
TLinkType = procedure(Sender: TObject; const Rel, Rev, Href: ThtString) of object;
TMetaType = procedure(Sender: TObject; const HttpEq, Name, Content: ThtString) of object;
TPagePrinted = procedure(Sender: TObject; Canvas: TCanvas; NumPage, W, H: Integer; var StopPrinting: Boolean) of object;
TParseEvent = procedure(Sender: TObject; var Source: TBuffer) of object;
TProcessingEvent = procedure(Sender: TObject; ProcessingOn: Boolean) of object;
TScriptEvent = procedure(Sender: TObject; const Name, ContentType, Src, Script: ThtString) of object;
TSoundType = procedure(Sender: TObject; const SRC: ThtString; Loop: Integer; Terminate: boolean) of object;
THTMLViewPrinted = TNotifyEvent;
THTMLViewPrinting = procedure(Sender: TObject; var StopPrinting: Boolean) of object;
TLinkDrawnEvent = procedure(Sender: TObject; Page: Integer; const Url, Target: ThtString; ARect: TRect) of object;
TFileBrowseEvent = procedure(Sender, Obj: TObject; var S: ThtString) of object;
TGetImageEvent = procedure(Sender: TObject; const SRC: ThtString; var Stream: TStream) of object;
TGottenImageEvent = TGetImageEvent;
TfrxHtViewerBase = class(TfrxHtControlBase)
private
FBackGround, FHotSpotColor, FVisitedColor, FOverColor: TColor;
FCharset: TFontCharset;
FCodePage: TBuffCodePage;
FFontColor: TColor;
FFontName, FPreFontName: TFontName;
FFontSize: Double;
FHistoryMaxCount, FVisitedMaxCount: Integer;
FMarginWidth, FMarginHeight: Integer;
FNoSelect: Boolean;
FPrintMarginLeft, FPrintMarginRight, FPrintMarginTop, FPrintMarginBottom: Double;
FPrintMaxHPages: Integer;
FServerRoot: ThtString;
//
function StoreFontName: Boolean;
function StorePreFontName: Boolean;
protected
procedure SetActiveColor(const Value: TColor); virtual;
procedure SetCharset(const Value: TFontCharset); virtual;
procedure SetCodePage(const Value: Integer); virtual;
procedure SetDefBackground(const Value: TColor); virtual;
procedure SetFontColor(const Value: TColor); virtual;
procedure SetFontName(const Value: TFontName); virtual;
procedure SetFontSize(const Value: Double); virtual;
procedure SetHistoryMaxCount(const Value: Integer); virtual;
procedure SetHotSpotColor(const Value: TColor); virtual;
procedure SetMarginHeight(const Value: Integer); virtual;
procedure SetMarginWidth(const Value: Integer); virtual;
procedure SetNoSelect(const Value: Boolean); virtual;
procedure SetPreFontName(const Value: TFontName); virtual;
procedure SetPrintMarginBottom(const Value: Double); virtual;
procedure SetPrintMarginLeft(const Value: Double); virtual;
procedure SetPrintMarginRight(const Value: Double); virtual;
procedure SetPrintMarginTop(const Value: Double); virtual;
procedure SetPrintMaxHPages(const Value: Integer); virtual;
procedure SetServerRoot(const Value: ThtString); virtual;
procedure SetVisitedColor(const Value: TColor); virtual;
procedure SetVisitedMaxCount(const Value: Integer); virtual;
public
constructor Create; virtual;
constructor CreateCopy(Source: TfrxHtViewerBase); virtual;
// Load(Url): Url might be an absolute Url or an absolute PathName or a relative Url/PathName.
procedure Load(const Url: ThtString); virtual; abstract;
// HtmlExpandFilename(Filename, CurrentFilename): Try to get the absolute pathname of the given filename in the local filesystem
function HtmlExpandFilename(const Filename: ThtString; const CurrentFilename: ThtString = ''): ThtString; virtual; abstract;
// set to determine if child objects should be in "quirks" mode
property CodePage: Integer read FCodePage write SetCodePage default 0;
property CharSet: TFontCharset read FCharSet write SetCharset default DEFAULT_CHARSET;
property DefBackground: TColor read FBackground write SetDefBackground default clBtnFace;
property DefFontColor: TColor read FFontColor write SetFontColor default clBtnText;
property DefFontName: TFontName read FFontName write SetFontName stored StoreFontName;
property DefFontSize: Double read FFontSize write SetFontSize;
property DefHotSpotColor: TColor read FHotSpotColor write SetHotSpotColor default clBlue;
property DefOverLinkColor: TColor read FOverColor write SetActiveColor default clBlue;
property DefPreFontName: TFontName read FPreFontName write SetPreFontName stored StorePreFontName;
property DefVisitedLinkColor: TColor read FVisitedColor write SetVisitedColor default clPurple;
property HistoryMaxCount: Integer read FHistoryMaxCount write SetHistoryMaxCount;
property MarginHeight: Integer read FMarginHeight write SetMarginHeight default 5;
property MarginWidth: Integer read FMarginWidth write SetMarginWidth default 10;
property NoSelect: Boolean read FNoSelect write SetNoSelect;
property PrintMarginBottom: Double read FPrintMarginBottom write SetPrintMarginBottom;
property PrintMarginLeft: Double read FPrintMarginLeft write SetPrintMarginLeft;
property PrintMarginRight: Double read FPrintMarginRight write SetPrintMarginRight;
property PrintMarginTop: Double read FPrintMarginTop write SetPrintMarginTop;
property PrintMaxHPages: Integer read FPrintMaxHPages write SetPrintMaxHPages default 2;
property ServerRoot: ThtString read FServerRoot write SetServerRoot;
property VisitedMaxCount: Integer read FVisitedMaxCount write SetVisitedMaxCount default 50;
published
end;
TTablePartType = (Normal, DoHead, DoBody1, DoBody2, DoBody3, DoFoot);
TTablePartRec = class
public
TablePart: TTablePartType;
PartStart: Integer;
PartHeight: Integer;
FootHeight: Integer;
end;
TfrxHtmlViewerBase = class(TfrxHtViewerBase)
protected
FWidth: Integer;
FHeight: Integer;
public
TablePartRec: TTablePartRec;
property Width: Integer read FWidth write FWidth;
property Height: Integer read FHeight write FHeight;
end;
//------------------------------------------------------------------------------
// string methods
//------------------------------------------------------------------------------
function StrLenW(Str: PWideChar): Cardinal;
function StrPosW(Str, SubStr: PWideChar): PWideChar;
function StrScanW(const Str: PWideChar; Chr: WideChar): PWideChar;
function StrRScanW(const Str: PWideChar; Chr: WideChar): PWideChar;
function WidePos(SubStr, S: UnicodeString): Integer;
function WideSameText1(const S1, S2: UnicodeString): boolean; {$ifdef UseInline} inline; {$endif}
function WideSameStr1(const S1, S2: UnicodeString): boolean; {$ifdef UseInline} inline; {$endif}
//function WideStringToMultibyte(CodePage: Integer; W: UnicodeString): AnsiString;
function FitText(DC: HDC; S: PWideChar; Max, Width: Integer; out Extent: TSize): Integer;
function GetTextExtent(DC: HDC; P: PWideChar; N: Integer): TSize;
procedure WrapTextW(Canvas: TCanvas; X1, Y1, X2, Y2: Integer; S: UnicodeString);
//------------------------------------------------------------------------------
// misc. methods
//------------------------------------------------------------------------------
// BG, 26.12.2011: new type TSpecWidth
function SpecWidth(Value: Integer; VType: TWidthType): TSpecWidth;
function ToSpecWidth(AsInteger: Integer; AsString: ThtString): TSpecWidth;
//------------------------------------------------------------------------------
// canvas methods
//------------------------------------------------------------------------------
function CalcClipRect(Canvas: TCanvas; const Rect: TRect; Printing: boolean): TRect;
procedure GetClippingRgn(Canvas: TCanvas; const ARect: TRect; Printing: boolean; var Rgn, SaveRgn: HRgn);
procedure FillRectWhite(Canvas: TCanvas; X1, Y1, X2, Y2: Integer; Color: TColor);
procedure DrawBorder(Canvas: TCanvas; ORect, IRect: TRect; const C: ThtColorArray;
const S: ThtBorderStyleArray; BGround: TColor; Print: boolean);
function DirectionByText(WC: PWideChar; Len: Integer): ThtDirection;
type
THtBorderPointArray = array[0..3] of TPoint;
implementation
uses
Forms, {$ifndef UseInline} Math, {$endif}
{$ifdef UseVCLStyles}
Vcl.Themes,
VCL.Controls,
{$endif}
{$ifdef HasSystemUITypes}
System.UITypes,
{$endif}
{$ifndef FPC_TODO}jpeg, {$endif}
{$IFDEF UNICODE} PngImage, {$ENDIF}
frxHTMLStylePars;
function DirectionByText(WC: PWideChar; Len: Integer): ThtDirection;
var
i: Integer;
C: Word;
function Range(w1, w2: Word): Boolean;
begin
Result := (C >= w1) and (C <= w2);
end;
begin
Result := diLTR;
for i := 0 to Len - 1 do
begin
C := Word(WC[i]);
if (C = $200E) // LEFT-TO-RIGHT MARK
or Range($0041, $005A) or Range($0061, $007A) or Range($00C0, $00D6) or Range($00D8, $00F6) or Range($00F8, $01F1) // Latin
or Range($0400, $04FF) or Range($0500, $052F) or Range($2DE0, $2DFF) or Range($A640, $A69F) or Range($1C80, $1C8F) // Cyrillic
or Range($0370, $03FF) or Range($1F00, $1FFF) // Greek and Coptic
or Range($0530, $058F) or Range($FB00, $FB4F) // Armenian
or Range($10A0, $10FF) or Range($2D00, $2D2F) or Range($1C90, $1CBF) // Georgian
or Range($1200, $137F) or Range($1380, $139F) or Range($2D80, $2DDF) or Range($AB00, $AB2F) // Ethiopic
then
Break
else if (C = $200F) // RIGHT-TO-LEFT MARK (RLM)
or (C = $061C) // ARABIC LETTER MARK (ALM)
or Range($0590, $05FF) or Range($FB00, $FB4F) // Hebrew
or Range($0600, $06FF) or Range($0750, $077F) or Range($08A0, $08FF) or Range($0870, $089F) or Range($FB50, $FDFF) or Range($FB50, $FDFF) // Arabic
or Range($0700, $074F) or Range($0860, $086F) // Syriac
or Range($0840, $085F) // Mandaic
or Range($07C0, $07FF) // NKo
or Range($0800, $083F) // Samaritan
or Range($0780, $07BF) // Thaana
then
begin
Result := diRTL;
Break;
end;
end;
end;
function StrLenW(Str: PWideChar): Cardinal;
{$ifdef UseInline} inline; {$endif}
begin
Result := 0;
if Str <> nil then
while Str[Result] <> #0 do
Inc(Result);
end;
function StrPosW(Str, SubStr: PWideChar): PWideChar;
{$ifdef UseInline} inline; {$endif}
var
StrPos : PWideChar;
SubstrPos : PWideChar;
begin
if SubStr^ = #0 then // Make sure substring not null string
begin
Result := nil;
Exit;
end;
Result := Str;
while Result^ <> #0 do // Until reach end of string
begin
StrPos := Result;
SubstrPos := SubStr;
while SubstrPos^ <> #0 do // Until reach end of substring
begin
if StrPos^ <> SubstrPos^ then // No point in continuing?
Break;
StrPos := StrPos + 1;
SubstrPos := SubstrPos + 1;
end;
if SubstrPos^ = #0 then // Break because reached end of substring?
Exit;
Result := Result + 1;
end;
Result := nil;
end;
function StrRScanW(const Str: PWideChar; Chr: WideChar): PWideChar;
{$ifdef UseInline} inline; {$endif}
begin
Result := StrScanW(Str, #0);
if Chr = #0 then // Null-terminating char considered part of string.
Exit;
while Result <> Str do
begin
Result := Result - 1;
if Result^ = Chr then
Exit;
end;
Result := nil;
end;
function StrScanW(const Str: PWideChar; Chr: WideChar): PWideChar;
{$ifdef UseInline} inline; {$endif}
begin
Result := Str;
while Result^ <> #0 do
begin
if Result^ = Chr then
Exit;
Result := Result + 1;
end;
if Chr = #0 then
Exit; // Null-terminating char considered part of string. See call
// searching for #0 to find end of string.
Result := nil;
end;
{----------------FitText}
function FitText(DC: HDC; S: PWideChar; Max, Width: Integer; out Extent: TSize): Integer;
{$ifdef UseInline} inline; {$endif}
{return count <= Max which fits in Width. Return X, the extent of chars that fit}
var
Ints: array of Integer;
begin
Extent.cx := 0;
Extent.cy := 0;
Result := 0;
if (Width <= 0) or (Max = 0) then
Exit;
SetLength(Ints, Max);
if GetTextExtentExPointW(DC, S, Max, Width, @Result, @Ints[0], Extent) then
if Result > 0 then
Extent.cx := Ints[Result - 1]
else
Extent.cx := 0;
end;
{----------------WidePos}
function WidePos(SubStr, S: UnicodeString): Integer;
{$ifdef UseInline} inline; {$endif}
// Unicode equivalent for Pos() function.
var
P: PWideChar;
begin
P := StrPosW(PWideChar(S), PWideChar(SubStr));
if P = nil then
Result := 0
else
Result := P - PWideChar(S) + 1;
end;
function WideSameText1(const S1, S2: UnicodeString): boolean;
{$ifdef UseInline} inline; {$endif}
begin
Result := htUpperCase(S1) = htUpperCase(S2);
end;
function WideSameStr1(const S1, S2: UnicodeString): boolean;
{$ifdef UseInline} inline; {$endif}
begin
Result := S1 = S2;
end;
//-- BG ---------------------------------------------------------- 06.10.2010 --
function ScaleRect(const Rect: TRect; ScaleX, ScaleY: Double): TRect;
{$ifdef UseInline} inline; {$endif}
begin
Result.Left := Round(Rect.Left * ScaleX);
Result.Right := Round(Rect.Right * ScaleX);
Result.Top := Round(Rect.Top * ScaleY);
Result.Bottom := Round(Rect.Bottom * ScaleY);
end;
//-- BG ---------------------------------------------------------- 06.10.2010 --
function CalcClipRect(Canvas: TCanvas; const Rect: TRect; Printing: boolean): TRect;
{$ifdef UseInline} inline; {$endif}
var
Point: TPoint;
SizeV, SizeW: TSize;
begin
GetWindowOrgEx(Canvas.Handle, Point); {when scrolling or animated Gifs, canvas may not start at X=0, Y=0}
Result := Rect;
OffsetRect(Result, -Point.X, -Point.Y);
if Printing then
begin
GetViewportExtEx(Canvas.Handle, SizeV);
GetWindowExtEx(Canvas.Handle, SizeW);
Result := ScaleRect(Result, SizeV.cx / SizeW.cx, SizeV.cy / SizeW.cy);
end;
end;
procedure GetClippingRgn(Canvas: TCanvas; const ARect: TRect; Printing: boolean; var Rgn, SaveRgn: HRgn);
{$ifdef UseInline} inline; {$endif}
var
Point: TPoint;
SizeV, SizeW: TSize;
HF, VF: double;
Rslt: Integer;
begin
{find a clipregion to prevent overflow. First check to see if there is
already a clip region. Return the old region, SaveRgn, (or 0) so it can be
restored later.}
SaveRgn := CreateRectRgn(0, 0, 1, 1);
Rslt := GetClipRgn(Canvas.Handle, SaveRgn); {Rslt = 1 for existing region, 0 for none}
if Rslt = 0 then
begin
DeleteObject(SaveRgn);
SaveRgn := 0;
end;
{Form the region}
GetWindowOrgEx(Canvas.Handle, Point); {when scrolling or animated Gifs, canvas may not start at X=0, Y=0}
with ARect do
if not Printing then
Rgn := CreateRectRgn(Left - Point.X, Top - Point.Y, Right - Point.X, Bottom - Point.Y)
else
begin
GetViewportExtEx(Canvas.Handle, SizeV);
GetWindowExtEx(Canvas.Handle, SizeW);
HF := (SizeV.cx / SizeW.cx); {Horizontal adjustment factor}
VF := (SizeV.cy / SizeW.cy); {Vertical adjustment factor}
Rgn := CreateRectRgn(Round(HF * (Left - Point.X)), Round(VF * (Top - Point.Y)), Round(HF * (Right - Point.X)), Round(VF * (Bottom - Point.Y)));
end;
if Rslt = 1 then {if there was a region, use the intersection with this region}
CombineRgn(Rgn, Rgn, SaveRgn, Rgn_And);
end;
function WideTrim(const S: UnicodeString): UnicodeString;
{$ifdef UseInline} inline; {$endif}
var
I, L: Integer;
begin
L := Length(S);
I := 1;
while (I <= L) and (S[I] <= ' ') do
Inc(I);
if I > L then
Result := ''
else
begin
while S[L] <= ' ' do
Dec(L);
Result := Copy(S, I, L - I + 1);
end;
end;
procedure WrapTextW(Canvas: TCanvas; X1, Y1, X2, Y2: Integer; S: UnicodeString);
{$ifdef UseInline} inline; {$endif}
{Wraps text in a clipping rectangle. Font must be set on entry}
var
ARect: TRect;
TAlign: Integer;
begin
TAlign := SetTextAlign(Canvas.Handle, TA_Top or TA_Left);
ARect := Rect(X1, Y1, X2, Y2);
DrawTextW(Canvas.Handle, PWideChar(S), Length(S), ARect, DT_Wordbreak);
SetTextAlign(Canvas.Handle, TAlign);
end;
function GetTextExtent(DC: HDC; P: PWideChar; N: Integer): TSize;
{$ifdef UseInline} inline; {$endif}
var
Dummy: Integer;
begin
GetTextExtentExPointW(DC, P, N, 0, @Dummy, nil, Result)
end;
procedure FillRectWhite(Canvas: TCanvas; X1, Y1, X2, Y2: Integer; Color: TColor);
{$ifdef UseInline} inline; {$endif}
var
OldBrushStyle: TBrushStyle;
OldBrushColor: TColor;
begin
with Canvas do
begin
OldBrushStyle := Brush.Style; {save style first}
OldBrushColor := Brush.Color;
Brush.Color := ThemedColor(Color);
Brush.Style := bsSolid;
FillRect(Rect(X1, Y1, X2, Y2));
Brush.Color := OldBrushColor;
Brush.Style := OldBrushStyle; {style after color as color changes style}
end;
end;
{$IFDEF Ver90}
procedure Assert(B: boolean; const S: ThtString);
begin {dummy Assert for Delphi 2}
end;
{$ENDIF}
//-- BG ---------------------------------------------------------- 26.12.2011 --
function SpecWidth(Value: Integer; VType: TWidthType): TSpecWidth;
{$ifdef UseInline} inline; {$endif}
begin
Result.Value := Value;
Result.VType := VType;
end;
//-- BG ---------------------------------------------------------- 26.12.2011 --
function ToSpecWidth(AsInteger: Integer; AsString: ThtString): TSpecWidth;
// Return a TSpecWidth prepared with values given in AsInteger *and* AsString.
// AsString is used to evaluate the type while AsInteger is used to evaluate the value.
// BG, 26.12.2011: Currently percentage is still converted to permille as done before Value became type Integer.
{$ifdef UseInline} inline; {$endif}
begin
if Pos('%', AsString) > 0 then
begin
Result.Value := Min(100, AsInteger) * 10;
Result.VType := wtPercent;
end
else if Pos('*', AsString) > 0 then // this is not specified for <td>, <th>. Only <col> and <colgroup> support it officially.
begin
Result.Value := AsInteger;
Result.VType := wtRelative;
end
else
begin
Result.Value := AsInteger;
Result.VType := wtAbsolute;
end;
end;
{ TfrxHtAttribute }
constructor TfrxHtAttribute.Create(ASym: TAttrSymb; const AValue: Double; const NameStr, ValueStr: ThtString; ACodePage: Integer);
begin
inherited Create;
Which := ASym;
DblValue := AValue;
Value := Trunc(AValue);
WhichName := NameStr;
Name := ValueStr;
CodePage := ACodePage;
end;
//-- BG ---------------------------------------------------------- 27.01.2013 --
constructor TfrxHtAttribute.CreateCopy(ASource: TfrxHtAttribute);
begin
inherited Create;
Which := ASource.Which;
WhichName := ASource.WhichName;
Value := ASource.Value;
DblValue := ASource.DblValue;
Name := ASource.Name;
CodePage := ASource.CodePage;
end;
{----------------TfrxHtAttributeList}
procedure TfrxHtAttributeList.Clear;
begin
inherited Clear;
SaveID := '';
end;
//-- BG ---------------------------------------------------------- 21.10.2016 --
function TfrxHtAttributeList.Clone: TfrxHtAttributeList;
begin
Result := TfrxHtAttributeList.CreateCopy(Self);
end;
//-- BG ---------------------------------------------------------- 27.01.2013 --
constructor TfrxHtAttributeList.CreateCopy(ASource: TfrxHtAttributeList);
var
I: Integer;
begin
inherited Create;
if ASource <> nil then
for I := 0 to ASource.Count - 1 do
Add(TfrxHtAttribute.CreateCopy(ASource[I]));
end;
function TfrxHtAttributeList.CreateStringList: ThtStringList;
var
I: Integer;
begin
Result := ThtStringList.Create;
for I := 0 to Count - 1 do
with Items[I] do
Result.Add(WhichName + '=' + Name);
end;
function TfrxHtAttributeList.Find(const Name: ThtString; var T: TfrxHtAttribute): Boolean;
var
I: Integer;
begin
for I := 0 to Count - 1 do
if Items[I].WhichName = Name then
begin
Result := True;
T := Items[I];
Exit;
end;
Result := False;
end;
function TfrxHtAttributeList.Find(Sy: TAttrSymb; var T: TfrxHtAttribute): Boolean;
var
I: Integer;
begin
for I := 0 to Count - 1 do
if Items[I].Which = Sy then
begin
Result := True;
T := Items[I];
Exit;
end;
Result := False;
end;
function TfrxHtAttributeList.GetAttribute(Index: Integer): TfrxHtAttribute;
begin
Result := Get(Index);
end;
function TfrxHtAttributeList.GetClass: ThtString;
var
T: TfrxHtAttribute;
S: ThtString;
I: Integer;
begin
Result := '';
T := nil;
if Find(ClassSy, T) then
begin
S := Lowercase(Trim(T.Name));
I := Pos(' ', S);
if I <= 0 then {a single class name}
Result := S
else
begin {multiple class names. Format as "class1.class2.class3"}
repeat
Result := Result + '.' + System.Copy(S, 1, I - 1);
System.Delete(S, 1, I);
S := Trim(S);
I := Pos(' ', S);
until I <= 0;
Result := Result + '.' + S;
Result := SortContextualItems(Result); {put in standard multiple order}
System.Delete(Result, 1, 1); {remove initial '.'}
end;
end;
end;
function TfrxHtAttributeList.GetID: ThtString;
var
T: TfrxHtAttribute;
begin
Result := SaveID;
T := nil;
if (Result = '') and Find(IDSy, T) then
begin
Result := Lowercase(T.Name);
SaveID := Result;
end;
end;
function TfrxHtAttributeList.GetTitle: ThtString;
var
T: TfrxHtAttribute;
begin
T := nil;
if Find(TitleSy, T) then
Result := T.Name
else
Result := '';
end;
{----------------TfrxHtUrlTarget.Create}
constructor TfrxHtUrlTarget.Create;
begin
inherited Create;
//utText := TutText.Create;
utText.Start := -1;
utText.Last := -1;
end;
destructor TfrxHtUrlTarget.Destroy;
begin
//FreeAndNil(utText);
inherited Destroy;
end;
var
Sequence: Integer = 10;
procedure TfrxHtUrlTarget.Assign(const AnUrl, ATarget: ThtString; L: TfrxHtAttributeList; AStart: Integer);
var
SL: ThtStringList;
begin
Url := AnUrl;
Target := ATarget;
ID := Sequence;
Inc(Sequence);
utText.Start := AStart;
SL := L.CreateStringList;
try
Attr := SL.Text;
finally
SL.Free;
end;
end;
procedure TfrxHtUrlTarget.Assign(const UT: TfrxHtUrlTarget);
begin
Url := UT.Url;
Target := UT.Target;
ID := UT.ID;
TabIndex := UT.TabIndex;
Attr := UT.Attr;
utText.Start := UT.utText.Start;
utText.Last := UT.utText.Last;
end;
procedure TfrxHtUrlTarget.Clear;
begin
Url := '';
Target := '';
ID := 0;
TabIndex := 0;
Attr := '';
utText.Start := -1;
utText.Last := -1;
end;
procedure TfrxHtUrlTarget.SetLast(List: TfrxHtFontObjBaseList; ALast: Integer);
var
I: Integer;
begin
utText.Last := ALast;
if List.Count > 0 then
for I := List.Count - 1 downto 0 do
if ID = List[I].UrlTarget.ID then
List[I].UrlTarget.utText.Last := ALast
else
Break;
end;
{ TfrxHtIndentManager }
constructor TfrxHtIndentManager.Create;
begin
inherited Create;
R := TfrxHtIndentRecList.Create;
L := TfrxHtIndentRecList.Create;
end;
destructor TfrxHtIndentManager.Destroy;
begin
R.Free;
L.Free;
inherited Destroy;
end;
//-- BG ---------------------------------------------------------- 05.02.2011 --
function TfrxHtIndentManager.AddLeft(YT, YB, W: Integer): TfrxHtIndentRec;
// For a floating block, update the left edge information.
begin
Result := TfrxHtIndentRec.Create;
Result.YT := YT;
Result.YB := YB;
Result.X := LeftEdge(YT) + W;
L.Add(Result);
LTopMin := YT;
end;
//-- BG ---------------------------------------------------------- 05.02.2011 --
function TfrxHtIndentManager.AddRight(YT, YB, W: Integer): TfrxHtIndentRec;
// For a floating block, update the right edge information.
begin
Result := TfrxHtIndentRec.Create;
Result.YT := YT;
Result.YB := YB;
Result.X := RightEdge(YT) - W;
R.Add(Result);
RTopMin := YT;
end;
//-- BG ---------------------------------------------------------- 12.08.2013 --
procedure TfrxHtIndentManager.AdjustY(FirstLeftIndex, FirstRightIndex, Y, Height: Integer);
var
I, D: Integer;
IR: TfrxHtIndentRec;
begin
D := 0;
for I := FirstLeftIndex to L.Count - 1 do
begin
IR := L[I];
if IR.YT > Y then
begin
if IR.YT < Y + Height then
D := Y + Height - IR.YT;
Inc(IR.YT, D);
Inc(IR.YB, D);
end;
end;
D := 0;
for I := FirstRightIndex to R.Count - 1 do
begin
IR := R[I];
if IR.YT > Y then
begin
if IR.YT < Y + Height then
D := Y + Height - IR.YT;
Inc(IR.YT, D);
Inc(IR.YB, D);
end;
end;
end;
{----------------TfrxHtIndentManager.Reset}
//-- BG ---------------------------------------------------------- 23.02.2011 --
procedure TfrxHtIndentManager.Init(Lf, Wd: Integer);
begin
LfEdge := Lf;
Width := Wd;
R.Clear;
L.Clear;
LTopMin := 0;
RTopMin := 0;
CurrentID := nil;
end;
procedure TfrxHtIndentManager.Reset(Lf: Integer);
begin
LfEdge := Lf;
CurrentID := nil;
end;
const
BigY = 9999999;
//-- BG ---------------------------------------------------------- 23.02.2011 --
function TfrxHtIndentManager.LeftEdge(Y: Integer): Integer;
// Returns the right most left indentation at Y relative to LfEdge.
// If there are no left indentations at Y, returns 0.
var
I: Integer;
IR: TfrxHtIndentRec;
MinX: Integer;
begin
Result := -MaxInt;
MinX := 0;
for I := 0 to L.Count - 1 do
begin
IR := L[I];
if (Y >= IR.YT) and (Y < IR.YB) and (Result < IR.X) then
if (IR.ID = nil) or (IR.ID = CurrentID) then
Result := IR.X;
if IR.ID = CurrentID then
MinX := IR.X;
end;
if Result = -MaxInt then
Result := MinX;
end;
//-- BG ---------------------------------------------------------- 23.02.2011 --
function TfrxHtIndentManager.LeftIndent(Y: Integer): Integer;
// Returns the right most left indentation at Y relative to block.
// If there are no left indentations at Y, returns LfEdge.
begin
Result := LeftEdge(Y) + LfEdge;
end;
//-- BG ---------------------------------------------------------- 23.02.2011 --
function TfrxHtIndentManager.RightEdge(Y: Integer): Integer;
// Returns the left most right indentation at Y relative LfEdge.
// If there are no indentations at Y, returns Width.
var
I: Integer;
IR: TfrxHtIndentRec;
MinX: Integer;
begin
Result := MaxInt;
for I := 0 to R.Count - 1 do
begin
IR := R[I];
if (Y >= IR.YT) and (Y < IR.YB) and (Result > IR.X) then
if (IR.ID = nil) or (IR.ID = CurrentID) then
Result := IR.X;
end;
if Result = MaxInt then
begin
//BG, 01.03.2011: Issue 77: Error of the elements
MinX := 0;
for I := L.Count - 1 downto 0 do
begin
IR := L[I];
if IR.ID = CurrentID then
begin
MinX := IR.X;
break;
end;
end;
Result := Width + MinX;
end;
end;
//-- BG ---------------------------------------------------------- 23.02.2011 --
function TfrxHtIndentManager.RightSide(Y: Integer): Integer;
// Returns the left most right indentation at Y relative to block.
// If there are no indentations at Y, returns Width + LfEdge.
begin
Result := RightEdge(Y) + LfEdge;
end;
function TfrxHtIndentManager.ImageBottom: Integer;
// Returns the bottom of the last floating image.
var
I: Integer;
begin
Result := 0;
for I := 0 to L.Count - 1 do
with L[I] do
if (ID = nil) and (YB > Result) then
Result := YB;
for I := 0 to R.Count - 1 do
with R[I] do
if (ID = nil) and (YB > Result) then
Result := YB;
end;
procedure TfrxHtIndentManager.GetClearY(out CL, CR: Integer);
{returns the left and right Y values which will clear image margins}
var
I: Integer;
begin
CL := -1;
for I := 0 to L.Count - 1 do
with L[I] do
if (ID = nil) and (YB > CL) then
CL := YB;
CR := -1;
for I := 0 to R.Count - 1 do
with R[I] do
if (ID = nil) and (YB > CR) then
CR := YB;
Inc(CL);
Inc(CR);
end;
//-- BG ---------------------------------------------------------- 06.02.2011 --
function TfrxHtIndentManager.AlignLeft(var Y: Integer; W: Integer): Integer;
// Find an available area to the left at or below Y with a width of W pixels.
//
// Returns the absolute X position of the found area.
// On return Y may be adjusted to a larger value if at original Y there is not
// enough width W.
var
I, CL, CR, LX, RX, XL, XR, YY, MinX: Integer;
begin
Y := Max(Y, LTopMin);
Result := LeftEdge(Y);
if Result + W > RightEdge(Y) then
begin
// too wide, must find a wider place below:
YY := Y;
MinX := 0;
CL := Y;
XL := Result; // valium for the compiler
for I := L.Count - 1 downto 0 do
with L[I] do
begin
if ID = CurrentID then
begin
MinX := X;
break;
end;
if (ID = nil) and (YB > Y) and ((YB < CL) or (CL = Y)) then
begin
if X = LeftEdge(YB - 1) then
begin
// This is the right most left indentation
LX := LeftEdge(YB);
RX := RightEdge(YB) - W;
if YY < YB then
YY := YB;
if RX >= LX then
begin
CL := YB;
XL := LX;
end;
end;
end;
end;
CR := Y;
XR := Result; // valium for the compiler
for I := R.Count - 1 downto 0 do
with R[I] do
begin
if ID = CurrentID then
break;
if (ID = nil) and (YB > Y) and ((YB < CR) or (CR = Y)) then
begin
if X = RightEdge(YB - 1) then
begin
// This is the left most right indentation
LX := LeftEdge(YB);
RX := RightEdge(YB) - W;
if YY < YB then
YY := YB;
if RX >= LX then
begin
CR := YB;
XR := LX;
end;
end;
end;
end;
if CL = Y then
begin
if CR = Y then
begin
// no better place found, just append at the end.
Y := YY;
Result := MinX;
end
else
begin
Y := CR;
Result := XR;
end
end
else if CR = Y then
begin
Y := CL;
Result := XL;
end
else if CL < CR then
begin
Y := CL;
Result := XL;
end
else
begin
Y := CR;
Result := XR;
end;
end;
Inc(Result, LfEdge);
end;
function TfrxHtIndentManager.AlignRight(var Y: Integer; W: Integer): Integer;
// Find an available area to the right at or below Y with a width of W pixels.
//
// Returns the absolute X position of the found area.
// On return Y may be adjusted to a larger value if at original Y there is not
// enough width W.
var
I, CL, CR, LX, RX, XL, XR, YY, MaxX: Integer;
begin
Y := Max(Y, RTopMin);
Result := RightEdge(Y) - W;
if Result < LeftEdge(Y) then
begin
// too wide, must find a wider place below:
YY := Y;
MaxX := Width - W;
CL := Y;
XL := Result; // valium for the compiler
for I := L.Count - 1 downto 0 do
with L[I] do
begin
if ID = CurrentID then
break;
if (ID = nil) and (YB > Y) and ((YB < CL) or (CL = Y)) then
begin
if X = LeftEdge(YB - 1) then
begin
// This is the right most left indentation
LX := LeftEdge(YB);
RX := RightEdge(YB) - W;
if YY < YB then
YY := YB;
if RX >= LX then
begin
CL := YB;
XL := RX;
end;
end;
end;
end;
CR := Y;
XR := Result; // valium for the compiler
for I := R.Count - 1 downto 0 do
with R[I] do
begin
if ID = CurrentID then
begin
MaxX := X - W;
break;
end;
if (ID = nil) and (YB > Y) and ((YB < CR) or (CR = Y)) then
begin
if X = RightEdge(YB - 1) then
begin
// This is the left most right indentation
LX := LeftEdge(YB);
RX := RightEdge(YB) - W;
if YY < YB then
YY := YB;
if RX >= LX then
begin
CR := YB;
XR := RX;
end;
end;
end;
end;
if CL = Y then
begin
if CR = Y then
begin
// no better place found, just append at the end.
Y := YY;
Result := MaxX;
end
else
begin
Y := CR;
Result := XR;
end
end
else if CR = Y then
begin
Y := CL;
Result := XL;
end
else if CL < CR then
begin
Y := CL;
Result := XL;
end
else
begin
Y := CR;
Result := XR;
end;
end;
Inc(Result, LfEdge);
end;
function TfrxHtIndentManager.GetNextWiderY(Y: Integer): Integer;
{returns the next Y value which offers a wider space or Y if none}
var
I, CL, CR: Integer;
begin
CL := Y;
for I := 0 to L.Count - 1 do
with L[I] do
if not Assigned(ID) and (YB > Y) and ((YB < CL) or (CL = Y)) then
CL := YB;
CR := Y;
for I := 0 to R.Count - 1 do
with R[I] do
if not Assigned(ID) and (YB > Y) and ((YB < CR) or (CR = Y)) then
CR := YB;
if CL = Y then
Result := CR
else if CR = Y then
Result := CL
else
Result := Min(CL, CR);
end;
function TfrxHtIndentManager.SetLeftIndent(XLeft, Y: Integer): Integer;
var
IR: TfrxHtIndentRec;
begin
IR := TfrxHtIndentRec.Create;
with IR do
begin
YT := Y;
YB := BigY;
X := XLeft;
ID := CurrentID;
end;
Result := L.Add(IR);
end;
function TfrxHtIndentManager.SetRightIndent(XRight, Y: Integer): Integer;
var
IR: TfrxHtIndentRec;
begin
IR := TfrxHtIndentRec.Create;
with IR do
begin
YT := Y;
YB := BigY;
X := XRight;
ID := CurrentID;
end;
Result := R.Add(IR);
end;
procedure TfrxHtIndentManager.FreeLeftIndentRec(I: Integer);
begin
L.Delete(I);
end;
procedure TfrxHtIndentManager.FreeRightIndentRec(I: Integer);
begin
R.Delete(I);
end;
function CopyPalette(Source: hPalette): hPalette;
{$ifdef UseInline} inline; {$endif}
var
LP: ^TLogPalette;
NumEntries: Integer;
begin
Result := 0;
if ColorBits > 8 then
Exit;
GetMem(LP, Sizeof(TLogPalette) + 256 * Sizeof(TPaletteEntry));
try
with LP^ do
begin
palVersion := $300;
palNumEntries := 256;
NumEntries := GetPaletteEntries(Source, 0, 256, palPalEntry);
if NumEntries > 0 then
begin
palNumEntries := NumEntries;
Result := CreatePalette(LP^);
end;
end;
finally
FreeMem(LP, Sizeof(TLogPalette) + 256 * Sizeof(TPaletteEntry));
end;
end;
{----------------TfrxHtCharCollection.GetAsString:}
function TfrxHtCharCollection.GetAsString: ThtString;
begin
Result := Copy(FChars, 1, FCurrentIndex);
end;
function TfrxHtCharCollection.GetCapacity: Integer;
begin
Result := Length(FChars);
end;
function TfrxHtCharCollection.GetSize: Integer;
begin
Result := FCurrentIndex;
end;
constructor TfrxHtCharCollection.Create;
begin
inherited;
FCurrentIndex := 0;
Capacity := TokenLeng;
end;
procedure TfrxHtCharCollection.SetCapacity(NewCapacity: Integer);
begin
if NewCapacity <> Capacity then
begin
SetLength(FChars, NewCapacity);
SetLength(FIndices, NewCapacity + 1);
end;
end;
procedure TfrxHtCharCollection.Add(C: ThtChar; Index: Integer);
begin
Inc(FCurrentIndex);
if Capacity <= FCurrentIndex then
Capacity := Capacity + 50;
FIndices[FCurrentIndex] := Index;
FChars[FCurrentIndex] := C;
end;
procedure TfrxHtCharCollection.Add(const S: ThtString; Index: Integer);
var
K, L: Integer;
begin
L := Length(S);
if L > 0 then
begin
K := FCurrentIndex + L;
if Capacity <= K then
Capacity := K + 50;
Move(S[1], FChars[FCurrentIndex + 1], L * SizeOf(ThtChar));
while FCurrentIndex < K do
begin
Inc(FCurrentIndex);
FIndices[FCurrentIndex] := Index;
end;
end;
end;
procedure TfrxHtCharCollection.Clear;
begin
FCurrentIndex := 0;
SetLength(FChars, 0);
end;
{ TokenObj }
constructor TfrxHtTokenObj.Create;
begin
inherited;
Capacity := TokenLeng;
FCount := 0;
St := '';
StringOK := True;
end;
procedure TfrxHtTokenObj.AddUnicodeChar(Ch: WideChar; Ind: Integer);
{Ch must be Unicode in this method}
begin
if Capacity <= Count then
Capacity := Capacity + 50;
Inc(FCount);
C[Count] := Ch;
I[Count] := Ind;
StringOK := False;
end;
procedure TfrxHtTokenObj.Clear;
begin
FCount := 0;
St := '';
StringOK := True;
end;
procedure TfrxHtTokenObj.AddString(S: TfrxHtCharCollection);
var
K: Integer;
begin
K := Count + S.FCurrentIndex;
if Capacity <= K then
Capacity := K + 50;
Move(S.FChars[1], C[Count + 1], S.FCurrentIndex * Sizeof(ThtChar));
Move(S.FIndices[1], I[Count + 1], S.FCurrentIndex * Sizeof(Integer));
FCount := K;
StringOK := False;
end;
function TfrxHtTokenObj.GetCapacity: Integer;
begin
Result := Length(C) - 1;
end;
//-- BG ---------------------------------------------------------- 20.01.2011 --
procedure TfrxHtTokenObj.SetCapacity(NewCapacity: Integer);
begin
if NewCapacity <> Capacity then
begin
SetLength(C, NewCapacity + 1);
SetLength(I, NewCapacity + 1);
if NewCapacity < Count then
begin
FCount := NewCapacity;
if StringOK then
St := Copy(St, 1, Count);
end;
end;
end;
function TfrxHtTokenObj.GetString: UnicodeString;
begin
if not StringOK then
begin
SetLength(St, Count);
if Count > 0 then
Move(C[1], St[1], SizeOf(WideChar) * Count);
StringOK := True;
end;
Result := St;
end;
{----------------TfrxHtIDObjectList}
function TfrxHtIDObjectList.AddObject(const S: ThtString; AObject: TfrxHtIDObject): Integer;
var
I: Integer;
O: TfrxHtIDObject;
begin
I := -1;
if Find(S, I) then
begin
try
O := Objects[I];
if O.FreeMe then
O.Free;
except
end;
Delete(I);
end;
Result := inherited AddObject(S, AObject);
end;
procedure TfrxHtIDObjectList.Clear;
var
I: Integer;
O: TfrxHtIDObject;
begin
for I := 0 to Count - 1 do
try
O := Objects[I];
if O.FreeMe then
O.Free;
except
end;
inherited Clear;
end;
constructor TfrxHtIDObjectList.Create;
begin
inherited Create;
Sorted := True;
end;
destructor TfrxHtIDObjectList.Destroy;
begin
Clear;
inherited
end;
//-- BG ---------------------------------------------------------- 04.03.2011 --
function TfrxHtIDObjectList.GetObject(Index: Integer): TfrxHtIDObject;
begin
Result := TfrxHtIDObject(inherited GetObject(Index));
end;
// BG, 17.04.2013: Color of DrawOnePolygon() must be a real RGB or palette value. Themed or system colors are not supported!
procedure DrawOnePolygon(Canvas: TCanvas; P: THtBorderPointArray; Color: TColor; Side: byte; Printing: Boolean);
{$ifdef UseInline} inline; {$endif}
{Here we draw a 4 sided polygon (by filling a region). This represents one
side (or part of a side) of a border.
For single pixel thickness, drawing is done by lines for better printing}
//BG, 22.08.2010: in print preview results are better without the single pixel exception.
{$ifdef BorderSinglePixelException}
type
SideArray = array[0..3, 1..4] of Integer;
const
AD: SideArray = ((0, 1, 0, 3),
(0, 1, 1, 1),
(2, 0, 2, 1),
(1, 3, 3, 3));
AP: SideArray = ((0, 1, 0, 3),
(0, 1, 2, 1),
(2, 0, 2, 2),
(1, 3, 3, 3));
{$endif}
var
R: HRgn;
{$ifdef BorderSinglePixelException}
OldWidth: Integer;
OldStyle: TPenStyle;
OldColor: TColor;
Thickness: Integer;
P1, P2: TPoint;
I: SideArray;
{$endif}
begin
{$ifdef BorderSinglePixelException}
if Side in [0, 2] then
Thickness := Abs(P[2].X - P[1].X)
else
Thickness := Abs(P[1].Y - P[2].Y);
if Thickness = 1 then
begin
with Canvas do
begin
OldColor := Pen.Color;
OldStyle := Pen.Style;
OldWidth := Pen.Width;
Pen.Color := Color;
Pen.Style := psSolid;
Pen.Width := 1;
if Printing then
I := AP
else
I := AD;
P1 := Point(P[I[Side, 1]].X, P[I[Side, 2]].Y);
P2 := Point(P[I[Side, 3]].X, P[I[Side, 4]].Y);
MoveTo(P1.X, P1.Y);
LineTo(P2.X, P2.Y);
Pen.Width := OldWidth;
Pen.Style := OldStyle;
Pen.Color := OldColor;
end;
end
else
{$endif}
begin
R := CreatePolygonRgn(P, 4, Alternate);
try
with Canvas do
begin
Brush.Style := bsSolid;
Brush.Color := Color;
FillRgn(Handle, R, Brush.Handle);
end;
finally
DeleteObject(R);
end;
end;
end;
{----------------DrawBorder}
procedure DrawBorder(Canvas: TCanvas; ORect, IRect: TRect; const C: ThtColorArray;
const S: ThtBorderStyleArray; BGround: TColor; Print: boolean);
{$ifdef UseInline} inline; {$endif}
{Draw the 4 sides of a border. The sides may be of different styles or colors.
The side indices, 0,1,2,3, represent left, top, right, bottom.
ORect is the outside rectangle of the border, IRect the inside Rectangle.
BGround is the background color used for the bssDouble style}
var
PO, PI, PM, P1, P2, Bnd: THtBorderPointArray;
I: Integer;
Cl, Color: TColor;
MRect: TRect;
lb: TLogBrush;
Pn, OldPn: HPen;
W, D: array[0..3] of Integer;
InPath: boolean;
PenType, Start: Integer;
StyleSet: set of ThtBorderStyle;
OuterRegion, InnerRegion: HRGN;
Brush: TBrush;
begin
{Limit the borders to somewhat more than the screen size}
ORect.Bottom := Min(ORect.Bottom, BotLim);
ORect.Top := Max(ORect.Top, TopLim);
IRect.Bottom := Min(IRect.Bottom, BotLim);
IRect.Top := Max(IRect.Top, TopLim);
{Widths are needed for Dashed, Dotted, and Double}
W[0] := IRect.Left - Orect.Left;
W[1] := IRect.Top - Orect.Top;
W[2] := ORect.Right - IRect.Right;
W[3] := ORect.Bottom - IRect.Bottom;
if (W[0] = 0) and (W[1] = 0) and (W[2] = 0) and (W[3] = 0) then
exit;
{Find out what style types are represented in this border}
StyleSet := [];
for I := 0 to 3 do
Include(StyleSet, S[I]);
{find the outside and inside corner points for the border segments}
with ORect do
begin
PO[0] := Point(Left, Bottom);
PO[1] := TopLeft;
PO[2] := Point(Right, Top);
PO[3] := BottomRight;
end;
with IRect do
begin
PI[0] := Point(Left, Bottom);
PI[1] := TopLeft;
PI[2] := Point(Right, Top);
PI[3] := BottomRight;
end;
{Points midway between the outer and inner rectangle are needed for
ridge, groove, dashed, dotted styles}
if [bssRidge, bssGroove, bssDotted, bssDashed] * StyleSet <> [] then
begin
MRect := Rect((ORect.Left + IRect.Left) div 2, (ORect.Top + IRect.Top) div 2,
(ORect.Right + IRect.Right) div 2, (ORect.Bottom + IRect.Bottom) div 2);
with MRect do
begin
PM[0] := Point(Left, Bottom);
PM[1] := TopLeft;
PM[2] := Point(Right, Top);
PM[3] := BottomRight;
end;
end;
{the Double style needs the space between inner and outer rectangles divided
into three parts}
if bssDouble in StyleSet then
begin
for I := 0 to 3 do
begin
D[I] := W[I] div 3;
if W[I] mod 3 = 2 then
Inc(D[I]);
end;
with ORect do
MRect := Rect(Left + D[0], Top + D[1], Right - D[2], Bottom - D[3]);
with MRect do
begin
P1[0] := Point(Left, Bottom);
P1[1] := TopLeft;
P1[2] := Point(Right, Top);
P1[3] := BottomRight;
end;
with IRect do
MRect := Rect(Left - D[0], Top - D[1], Right + D[2], Bottom + D[3]);
with MRect do
begin
P2[0] := Point(Left, Bottom);
P2[1] := TopLeft;
P2[2] := Point(Right, Top);
P2[3] := BottomRight;
end;
end;
{double, dotted, dashed styles need a background fill}
if (BGround <> clNone) and ([bssDouble, bssDotted, bssDashed] * StyleSet <> []) then
begin
with ORect do
OuterRegion := CreateRectRgn(Left, Top, Right, Bottom);
with IRect do
InnerRegion := CreateRectRgn(Left, Top, Right, Bottom);
CombineRgn(OuterRegion, OuterRegion, InnerRegion, RGN_DIFF);
Brush := TBrush.Create;
try
Brush.Color := ThemedColor(BGround) or PalRelative;
Brush.Style := bsSolid;
FillRgn(Canvas.Handle, OuterRegion, Brush.Handle);
finally
Brush.Free;
DeleteObject(OuterRegion);
DeleteObject(InnerRegion);
end;
end;
InPath := False;
Pn := 0;
OldPn := 0;
Start := 0;
try
for I := 0 to 3 do
begin
Color := ThemedColor(C[I]);
case S[I] of
bssSolid, bssInset, bssOutset:
begin
Bnd[0] := PO[I];
Bnd[1] := PO[(I + 1) mod 4];
Bnd[2] := PI[(I + 1) mod 4];
Bnd[3] := PI[I];
case S[I] of
bssInset:
if I in [0, 1] then
Color := Darker(Color)
else
Color := Lighter(Color);
bssOutset:
if I in [2, 3] then
Color := Darker(Color)
else
Color := Lighter(Color);
end;
DrawOnePolygon(Canvas, Bnd, Color or PalRelative, I, Print);
end;
bssRidge, bssGroove:
begin {ridge or groove}
Cl := Color;
Bnd[0] := PO[I];
Bnd[1] := PO[(I + 1) mod 4];
Bnd[2] := PM[(I + 1) mod 4];
Bnd[3] := PM[I];
case S[I] of
bssGroove:
if I in [0, 1] then
Color := Darker(Color)
else
Color := Lighter(Color);
bssRidge:
if I in [2, 3] then
Color := Darker(Color)
else
Color := Lighter(Color);
end;
DrawOnePolygon(Canvas, Bnd, Color or PalRelative, I, Print);
Color := Cl;
Bnd[0] := PM[I];
Bnd[1] := PM[(I + 1) mod 4];
Bnd[2] := PI[(I + 1) mod 4];
Bnd[3] := PI[I];
case S[I] of
bssRidge:
if I in [0, 1] then
Color := Darker(Color)
else
Color := Lighter(Color);
bssGroove:
if (I in [2, 3]) then
Color := Darker(Color)
else
Color := Lighter(Color);
end;
DrawOnePolygon(Canvas, Bnd, Color or PalRelative, I, Print);
end;
bssDouble:
begin
Color := Color or PalRelative;
Bnd[0] := PO[I];
Bnd[1] := PO[(I + 1) mod 4];
Bnd[2] := P1[(I + 1) mod 4];
Bnd[3] := P1[I];
DrawOnePolygon(Canvas, Bnd, Color, I, Print);
Bnd[0] := P2[I];
Bnd[1] := P2[(I + 1) mod 4];
Bnd[2] := PI[(I + 1) mod 4];
Bnd[3] := PI[I];
DrawOnePolygon(Canvas, Bnd, Color, I, Print);
end;
bssDashed, bssDotted:
begin
if not InPath then
begin
lb.lbStyle := BS_SOLID;
lb.lbColor := Color or PalRelative;
lb.lbHatch := 0;
if S[I] = bssDotted then
PenType := PS_Dot or ps_EndCap_Round
else
PenType := PS_Dash or ps_EndCap_Square;
Pn := ExtCreatePen(PS_GEOMETRIC or PenType or ps_Join_Miter, W[I], lb, 0, nil);
OldPn := SelectObject(Canvas.Handle, Pn);
BeginPath(Canvas.Handle);
MoveToEx(Canvas.Handle, PM[I].x, PM[I].y, nil);
Start := I;
InPath := True;
end;
LineTo(Canvas.Handle, PM[(I + 1) mod 4].x, PM[(I + 1) mod 4].y);
if (I = 3) or (S[I + 1] <> S[I]) or (C[I + 1] <> C[I]) or (W[I + 1] <> W[I]) then
begin
if (I = 3) and (Start = 0) then
CloseFigure(Canvas.Handle); {it's a closed path}
EndPath(Canvas.Handle);
StrokePath(Canvas.Handle);
SelectObject(Canvas.Handle, OldPn);
DeleteObject(Pn);
Pn := 0;
InPath := False;
end;
end;
end;
end;
finally
if Pn <> 0 then
begin
SelectObject(Canvas.Handle, OldPn);
DeleteObject(Pn);
end;
end;
end;
{ TfrxHtViewerBase }
//-- BG ---------------------------------------------------------- 24.11.2011 --
constructor TfrxHtViewerBase.Create;
begin
inherited;
PrintMarginLeft := 2.0;
PrintMarginRight := 2.0;
PrintMarginTop := 2.0;
PrintMarginBottom := 2.0;
PrintMaxHPages := 2;
Charset := DEFAULT_CHARSET;
MarginHeight := 5;
MarginWidth := 10;
DefBackground := clBtnFace;
DefFontColor := clBtnText;
DefHotSpotColor := clBlue;
DefOverLinkColor := clBlue;
DefVisitedLinkColor := clPurple;
VisitedMaxCount := 50;
DefFontSize := 12;
DefFontName := htDefFontName;
DefPreFontName := htDefPreFontName;
end;
//-- BG ---------------------------------------------------------- 16.11.2011 --
constructor TfrxHtViewerBase.CreateCopy(Source: TfrxHtViewerBase);
begin
Create;
Charset := Source.Charset;
CodePage := Source.CodePage;
DefBackGround := Source.DefBackGround;
DefFontColor := Source.DefFontColor;
DefFontName := Source.DefFontName;
DefFontSize := Source.DefFontSize;
DefHotSpotColor := Source.DefHotSpotColor;
DefOverLinkColor := Source.DefOverLinkColor;
DefPreFontName := Source.DefPreFontName;
DefVisitedLinkColor := Source.DefVisitedLinkColor;
NoSelect := Source.NoSelect;
ServerRoot := Source.ServerRoot;
MarginHeight := Source.MarginHeight;
MarginWidth := Source.MarginWidth;
PrintMarginBottom := Source.PrintMarginBottom;
PrintMarginLeft := Source.PrintMarginLeft;
PrintMarginRight := Source.PrintMarginRight;
PrintMarginTop := Source.PrintMarginTop;
PrintMaxHPages := Source.PrintMaxHPages;
HistoryMaxCount := Source.HistoryMaxCount;
VisitedMaxCount := Source.VisitedMaxCount;
end;
procedure TfrxHtViewerBase.SetActiveColor(const Value: TColor);
begin
FOverColor := Value;
end;
procedure TfrxHtViewerBase.SetCharset(const Value: TFontCharset);
begin
FCharSet := Value;
end;
procedure TfrxHtViewerBase.SetCodePage(const Value: Integer);
begin
FCodePage := Value;
end;
procedure TfrxHtViewerBase.SetDefBackground(const Value: TColor);
begin
FBackground := Value;
end;
procedure TfrxHtViewerBase.SetFontColor(const Value: TColor);
begin
FFontColor := Value;
end;
procedure TfrxHtViewerBase.SetFontName(const Value: TFontName);
begin
FFontName := Value;
end;
procedure TfrxHtViewerBase.SetFontSize(const Value: Double);
begin
FFontSize := Value * 96.0 / Screen.PixelsPerInch;
end;
procedure TfrxHtViewerBase.SetHistoryMaxCount(const Value: Integer);
begin
FHistoryMaxCount := Value;
end;
procedure TfrxHtViewerBase.SetHotSpotColor(const Value: TColor);
begin
FHotSpotColor := Value;
end;
procedure TfrxHtViewerBase.SetMarginHeight(const Value: Integer);
begin
FMarginHeight := Value;
end;
procedure TfrxHtViewerBase.SetMarginWidth(const Value: Integer);
begin
FMarginWidth := Value;
end;
procedure TfrxHtViewerBase.SetNoSelect(const Value: Boolean);
begin
FNoSelect := Value;
end;
procedure TfrxHtViewerBase.SetPreFontName(const Value: TFontName);
begin
FPreFontName := Value;
end;
procedure TfrxHtViewerBase.SetPrintMarginBottom(const Value: Double);
begin
FPrintMarginBottom := Value;
end;
procedure TfrxHtViewerBase.SetPrintMarginLeft(const Value: Double);
begin
FPrintMarginLeft := Value;
end;
procedure TfrxHtViewerBase.SetPrintMarginRight(const Value: Double);
begin
FPrintMarginRight := Value;
end;
procedure TfrxHtViewerBase.SetPrintMarginTop(const Value: Double);
begin
FPrintMarginTop := Value;
end;
procedure TfrxHtViewerBase.SetPrintMaxHPages(const Value: Integer);
begin
FPrintMaxHPages := Value;
end;
procedure TfrxHtViewerBase.SetServerRoot(const Value: ThtString);
begin
FServerRoot := ExcludeTrailingPathDelimiter(Trim(Value));
end;
procedure TfrxHtViewerBase.SetVisitedColor(const Value: TColor);
begin
FVisitedColor := Value;
end;
procedure TfrxHtViewerBase.SetVisitedMaxCount(const Value: Integer);
begin
FVisitedMaxCount := Value;
end;
function TfrxHtViewerBase.StoreFontName: Boolean;
begin
Result := FFontName <> htDefFontName;
end;
function TfrxHtViewerBase.StorePreFontName: Boolean;
begin
Result := FPreFontName <> htDefPreFontName;
end;
{ TfrxHtIDObject }
//-- BG ---------------------------------------------------------- 06.03.2011 --
function TfrxHtIDObject.FreeMe: Boolean;
begin
Result := False;
end;
//>-- DZ 18.09.2011
constructor TfrxHtIDObject.Create(const AHtmlId: ThtString);
begin
inherited Create;
FHtmlID:= Trim(AHtmlId);
end;
function TfrxHtIDObject.GetId(): ThtString;
begin
Result := FHtmlId;
end;
//<-- DZ
{ TfrxHtIndentRecList }
//-- BG ---------------------------------------------------------- 06.10.2016 --
function TfrxHtIndentRecList.Get(Index: Integer): TfrxHtIndentRec;
begin
Result := inherited Get(Index);
end;
{ TfrxHtFontObjBaseList }
//-- BG ---------------------------------------------------------- 06.10.2016 --
function TfrxHtFontObjBaseList.GetBase(Index: Integer): TfrxHtFontObjBase;
begin
Result := inherited Get(Index);
end;
end.