{ 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