{Version 9.45} {*********************************************************} {* HTMLUN2.PAS *} {*********************************************************} { Copyright (c) 1995-2008 by L. David Baldwin 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, PNGZLIB1.PAS, DITHERUNIT.PAS, and URLCON.PAS are covered by separate copyright notices located in those modules. } {$i htmlcons.inc} unit htmlun2; interface uses SysUtils, Classes, {$IFNDEF LCL} Windows, Messages, {$ELSE} LclIntf, LMessages, Types, LclType, LResources, IntfGraphics, HtmlMisc, {$ENDIF} Graphics, Controls, Forms, Dialogs, StdCtrls, ExtCtrls, Clipbrd, StyleUn, GDIPL2A; const VersionNo = '9.45'; MaxHScroll = 6000; {max horizontal display in pixels} {$IFNDEF LCL} HandCursor = 10101; {$ELSE} HandCursor = crHandPoint; {$ENDIF} OldThickIBeamCursor = 2; UpDownCursor = 10103; UpOnlyCursor = 10104; DownOnlyCursor = 10105; Tokenleng = 300; TopLim = -200; {drawing limits} BotLim = 5000; FmCtl = WideChar(#2); ImgPan = WideChar(#4); BrkCh = WideChar(#8); var IsWin95: Boolean; IsWin32Platform: boolean; {win95, 98, ME} type TgpObject = TObject; TScriptEvent = procedure(Sender: TObject; const Name, Language, Script: string) of Object; TFreeList = class(TList) {like a TList but frees it's items. Use only descendents of TObject} destructor Destroy; override; {$Warnings Off} procedure Clear; {do not override} end; {$Warnings On} Transparency = (NotTransp, LLCorner, TGif, TPng); JustifyType = (NoJustify, Left, Centered, Right, FullJustify); TRowType = (THead, TBody, TFoot); Symb = ( HtmlSy, TitleSy, BodySy, HeadSy, PSy, PEndSy, BSy, BEndSy, ISy, IEndSy, HtmlEndSy, TitleEndSy, BodyEndSy, HeadEndSy, BRSy, HeadingSy, HeadingEndSy, EmSy, EmEndSy, StrongSy, StrongEndSy, USy, UEndSy, HRSy, CiteSy, VarSy, CiteEndSy, VarEndSy, BaseSy, {Keep order} TTSy, CodeSy, KbdSy, SampSy, TTEndSy, CodeEndSy, KbdEndSy, SampEndSy, {end order} OLSy, OLEndSy, LISy, ULSy, ULEndSy, DirSy, DirEndSy, MenuSy, MenuEndSy, DLSy, DLEndSy, DDSy, DTSy, AddressSy, AddressEndSy, BlockQuoteSy, BlockQuoteEndSy, PreSy, PreEndSy, ImageSy, Centersy, CenterEndSy, OtherAttribute, ASy, AEndSy, HrefSy, NameSy, SrcSy, AltSy, AlignSy, OtherChar, OtherSy, CommandSy, TextSy, EofSy, LinkSy, BGColorSy, BackgroundSy, TableSy, TableEndSy, TDSy, TDEndSy, TRSy, TREndSy, THSy, THEndSy, ColSpanSy, RowSpanSy, BorderSy, CellPaddingSy, CellSpacingSy, VAlignSy, WidthSy, CaptionSy, CaptionEndSy, StartSy, ButtonSy, InputSy, ValueSy, TypeSy, CheckBoxSy, RadioSy, FormSy, FormEndSy, MethodSy, ActionSy, CheckedSy, SizeSy, MaxLengthSy, TextAreaSy, TextAreaEndSy, ColsSy, RowsSy, SelectSy, SelectEndSy, OptionSy, OptionEndSy, SelectedSy, MultipleSy, FontSy, FontEndSy, ColorSy, FaceSy, BaseFontSy, TranspSy, SubSy, SubEndSy, SupSy, SupEndSy, ClearSy, IsMapSy, BigSy, BigEndSy, SmallSy, SmallEndSy, BorderColorSy, MapSy, MapEndSy, AreaSy, ShapeSy, CoordsSy, NoHrefSy, UseMapSy, HeightSy, PlainSy, FrameSetSy, FrameSetEndSy, FrameSy, TargetSy, NoFramesSy, NoFramesEndSy, NoResizeSy, ScrollingSy, PageSy, HSpaceSy, VSpaceSy, ScriptSy, ScriptEndSy, LanguageSy, DivSy, DivEndSy, SSy, SEndSy, StrikeSy, StrikeEndSy, FrameBorderSy, MarginWidthSy, MarginHeightSy, BgSoundSy, LoopSy, OnClickSy, WrapSy, NoShadeSy, MetaSy, HttpEqSy, ContentSy, EncTypeSy, VLinkSy, OLinkSy, ActiveSy, PanelSy, NoBrSy, NoBrEndSy, WbrSy, ClassSy, IDSy, StyleSy, StyleEndSy, SpanSy, SpanEndSy, liAloneSy, RelSy, RevSy, NoWrapSy, BorderColorLightSy, BorderColorDarkSy, CharSetSy, RatioSy, OnFocusSy, OnBlurSy, OnChangeSy, ColSy, ColGroupSy, ColGroupEndSy, TabIndexSy, BGPropertiesSy, DisabledSy, TopMarginSy, LeftMarginSy, LabelSy, LabelEndSy, THeadSy, TBodySy, TFootSy, THeadEndSy, TBodyEndSy, TFootEndSy, ObjectSy, ObjectEndSy, ParamSy, ReadonlySy, EolSy); TAttribute = class(TObject) {holds a tag attribute} public Which: Symb; {symbol of attribute such as HrefSy} WhichName: string; Value: integer; {numeric value if appropriate} Percent: boolean;{if value is in percent} Name: String; {String (mixed case), value after '=' sign} CodePage: integer; constructor Create(ASym: Symb; AValue: integer; Const NameStr, ValueStr: string; ACodePage: integer); destructor Destroy; override; end; TAttributeList = class(TFreeList) {a list of tag attributes,(TAttributes)} private Prop: TProperties; SaveID: string; function GetClass: string; function GetID: string; function GetTitle: string; function GetStyle: TProperties; public destructor Destroy; override; procedure Clear; function Find(Sy: Symb; var T: TAttribute): boolean; function CreateStringList: TStringList; property TheClass: string read GetClass; property TheID: string read GetID; property TheTitle: string read GetTitle; property TheStyle: TProperties read GetStyle; end; TBitmapItem = class(TObject) public AccessCount: integer; UsageCount: integer; {how many in use} Transp: Transparency; {identifies what the mask is for} MImage: TgpObject; {main image, bitmap or animated GIF} Mask: TBitmap; {its mask} constructor Create(AImage: TgpObject; AMask: TBitmap; Tr: Transparency); destructor Destroy; override; end; TStringBitmapList = class(TStringList) {a list of bitmap filenames and TBitmapItems} public MaxCache: integer; constructor Create; destructor Destroy; override; procedure Clear; override; function AddObject(const S: string; AObject: TObject): Integer; override; procedure DecUsage(const S: string); procedure IncUsage(const S: string); procedure BumpAndCheck; procedure PurgeCache; function GetImage(I: integer): TgpObject; procedure SetCacheCount(N: integer); end; SelTextCount = class(TObject) private Buffer: PWideChar; BufferLeng: integer; Leng: integer; public procedure AddText(P: PWideChar; Size: integer); virtual; procedure AddTextCR(P: PWideChar; Size: integer); function Terminate: integer; virtual; end; SelTextBuf = class(SelTextCount) public constructor Create(ABuffer: PWideChar; Size: integer); procedure AddText(P: PWideChar; Size: integer); override; function Terminate: integer; override; end; ClipBuffer = class(SelTextBuf) private procedure CopyToClipboard; public constructor Create(Leng: integer); destructor Destroy; override; function Terminate: integer; override; end; TutText = class {holds start and end point of URL text} Start: integer; Last: integer; end; TUrlTarget = Class(TObject) private function GetStart: integer; function GetLast: integer; public URL, Target: string; ID: integer; Attr: string; utText: TutText; TabIndex: integer; constructor Create; procedure Copy(UT: TUrlTarget); destructor Destroy; override; procedure Assign(AnUrl, ATarget: String; L: TAttributeList; AStart: integer); procedure Clear; procedure SetLast(List: TList; ALast: integer); property Start: integer read GetStart; property Last: integer read GetLast; end; TMapItem = class(TObject) {holds a client map info} MapName: String; Areas: TStringList; {holds the URL and region handle} AreaTargets: TStringList; {holds the target window} AreaTitles: TStringList; {the Title strings} constructor Create; destructor Destroy; override; function GetURL(X, Y: integer; var URLTarg: TURLTarget; var ATitle: string): boolean; procedure AddArea(Attrib: TAttributeList); end; TDib = class(TObject) private Info : PBitmapInfoHeader; InfoSize: integer; Image: Pointer; ImageSize : integer; FHandle: THandle; procedure InitializeBitmapInfoHeader(Bitmap: HBITMAP); procedure GetDIBX(DC: HDC; Bitmap: HBITMAP; Palette: HPALETTE); procedure Allocate(Size: integer); procedure DeAllocate; public constructor CreateDIB(DC: HDC; Bitmap: TBitmap); destructor Destroy; override; function CreateDIBmp: hBitmap; procedure DrawDIB(DC: HDC; X: Integer; Y: integer; W, H: integer; ROP: DWord); end; IndentRec = Class(TObject) X: integer; {indent for this record} YT, YB: integer; {top and bottom Y values for this record} ID: TObject; {level inicator for this record, 0 for not applicable} Float: boolean; {set if Floating block boundary} end; IndentManagerBasic = class(TObject) Width, ClipWidth: Integer; L, R: TFreeList; {holds info (IndentRec's) on left and right indents} CurrentID: TObject; {the current level (a TBlock pointer)} LfEdge, RtEdge: integer; {current extreme edges} constructor Create; destructor Destroy; override; procedure Clear; procedure Reset(Lf, Rt: integer); procedure UpdateTable(Y: integer; IW: integer; IH: integer; Justify: JustifyType); function LeftIndent(Y: integer): integer; function RightSide(Y: integer): integer; function ImageBottom: integer; procedure GetClearY(var CL, CR: integer); function GetNextWiderY(Y: integer): integer; function SetLeftIndent(XLeft, Y: integer): integer; function SetRightIndent(XRight, Y: integer): integer; procedure FreeLeftIndentRec(I: integer); procedure FreeRightIndentRec(I: integer); end; AllocRec = Class(TObject) Ptr: Pointer; ASize: integer; AHandle: THandle; end; // IndexArray = array[1..TokenLeng] of integer; IndexArray = array[1..30000] of integer; // LCL port: To avoid range-check error in TCharCollection.Add. PIndexArray = ^IndexArray; // ChrArray = array[1..TokenLeng] of WideChar; ChrArray = array[1..30000] of WideChar; // LCL port: To avoid range-check error in TokenObj.AddUnicodeChar. {Simplified variant of TokenObj, to temporarily keep a string of ANSI characters along with their original indices.} TCharCollection = class private FChars: string; FIndices: PIndexArray; FCurrentIndex: Integer; function GetSize: Integer; function GetAsString: string; public constructor Create; destructor Destroy; override; procedure Add(C: Char; Index: Integer); procedure Clear; procedure Concat(T: TCharCollection); property AsString: string read GetAsString; property Chars: string read FChars; property Indices: PIndexArray read FIndices; property Size: Integer read GetSize; end; TokenObj= class private St: WideString; StringOK: boolean; function GetString: WideString; public C: ^ChrArray; I: ^IndexArray; MaxIndex, Leng: integer; constructor Create; destructor Destroy; override; procedure AddUnicodeChar(Ch: WideChar; Ind: integer); procedure AddString(S: TCharCollection; CodePage: Integer); procedure Concat(T: TokenObj); procedure Clear; procedure Remove(N: integer); procedure Replace(N: integer; Ch: WideChar); property S: WideString read GetString; end; TIDObject = class(TObject) protected function GetYPosition: integer; virtual; abstract; public property YPosition: integer read GetYPosition; destructor Destroy; override; end; TChPosObj = class (TIDObject) private ChPos: integer; List: TList; protected function GetYPosition: integer; override; end; TIDNameList = class(TStringList) private OwnerList: TList; public constructor Create(List: TList); destructor Destroy; override; procedure Clear; override; function AddObject(const S: string; AObject: TObject): Integer; override; procedure AddChPosObject(const S: string; Pos: integer); end; {$ifndef NoMetafile} ThtMetaFile = class(TMetaFile) private FBitmap, FMask: TBitmap; FWhiteBGBitmap: TBitmap; function GetBitmap: TBitmap; function GetMask: TBitmap; procedure Construct; function GetWhiteBGBitmap: TBitmap; public destructor Destroy; override; property Bitmap: TBitmap read GetBitmap; property Mask: TBitmap read GetMask; property WhiteBGBitmap: TBitmap read GetWhiteBGBitmap; end; {$endif} ImageType = (NoImage, Bmp, Gif, Gif89, Png, Jpg); SetOfChar = Set of Char; htColorArray = packed array[0..3] of TColor; htBorderStyleArray = packed array[0..3] of BorderStyleType; var ColorBits: Byte; ThePalette: HPalette; {the rainbow palette for 256 colors} PalRelative: integer; DefBitMap, ErrorBitMap, ErrorBitmapMask: TBitMap; ABitmapList: TStringBitmapList; {the image cache} WaitStream: TMemoryStream; function InSet(W: WideChar; S: SetOfChar): boolean; 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 FitText(DC: HDC; S: PWideChar; Max, Width: Integer; var Extent: integer): Integer; function WidePos(SubStr, S: WideString): Integer; function WideTrim(const S : WideString) : WideString; function WideUpperCase1(const S: WideString): WideString; function WideLowerCase1(const S: WideString): WideString; function WideSameText1(const S1, S2: WideString): boolean; function WideSameStr1(const S1, S2: WideString): boolean; function PosX(const SubStr, S: string; Offset: integer = 1): Integer; {find substring in S starting at Offset} function IntMin(A, B: Integer): Integer; function IntMax(A, B: Integer): Integer; procedure GetClippingRgn(Canvas: TCanvas; ARect: TRect; Printing: boolean; var Rgn, SaveRgn: HRgn); function GetImageAndMaskFromFile(const Filename: string; var Transparent: Transparency; var Mask: TBitmap): TgpObject; function HTMLToDos(FName: string): string; {convert an HTML style filename to one for Dos} function HTMLServerToDos(FName, Root: string): string; procedure WrapTextW(Canvas: TCanvas; X1, Y1, X2, Y2: integer; S: WideString); procedure FinishTransparentBitmap (ahdc: HDC; InImage, Mask: TBitmap; xStart, yStart, W, H: integer); function GetImageMask(Image: TBitmap; ColorValid: boolean; AColor: TColor): TBitmap; function TransparentGIF(const FName: string; var Color: TColor): boolean; function Allocate(Size: integer): AllocRec; procedure DeAllocate(AR: AllocRec); function CopyPalette(Source: hPalette): hPalette; procedure SetGlobalPalette(Value: HPalette); function GetImageFromFile(const Filename: String): TBitmap; function GetImageAndMaskFromStream(Stream: TMemoryStream; var Transparent: Transparency; var AMask: TBitmap): TgpObject; function KindOfImageFile(FName: String): ImageType; function KindOfImage(Start: Pointer): ImageType; procedure FillRectWhite(Canvas: TCanvas; X1, Y1, X2, Y2: integer; Color: TColor); procedure FormControlRect(Canvas: TCanvas; X1: integer; Y1: integer; X2: integer; Y2: integer; Raised, PrintMonoBlack, Disabled: boolean; Color: TColor); function GetXExtent(DC: HDC; P: PWideChar; N: integer): integer; procedure RaisedRect(SectionList: TFreeList; Canvas: TCanvas; X1: integer; Y1: integer; X2: integer; Y2: integer; Raised: boolean; W: integer); procedure RaisedRectColor(SectionList: TFreeList; Canvas: TCanvas; X1: integer; Y1: integer; X2: integer; Y2: integer; Light, Dark: TColor; Raised: boolean; W: integer); function EnlargeImage(Image: TGpObject; W, H: integer): TBitmap; procedure PrintBitmap(Canvas: TCanvas; X, Y, W, H: integer; BMHandle: HBitmap); procedure PrintBitmap1(Canvas: TCanvas; X, Y, W, H, YI, HI: integer; BMHandle: HBitmap); procedure PrintTransparentBitmap1(Canvas: TCanvas; X, Y, NewW, NewH: integer; Bitmap, Mask: TBitmap; YI, HI: integer); procedure PrintTransparentBitmap3(Canvas: TCanvas; X, Y, NewW, NewH: integer; Bitmap, Mask: TBitmap; YI, HI: integer); procedure DrawGpImage(Handle: THandle; Image: TGPImage; DestX, DestY: integer); overload; procedure DrawGpImage(Handle: THandle; Image: TGpImage; DestX, DestY, SrcX, SrcY, SrcW, SrcH: integer); overload; procedure StretchDrawGpImage(Handle: THandle; Image: TGpImage; DestX, DestY, DestW, DestH: integer); procedure PrintGpImageDirect(Handle: THandle; Image: TGpImage; DestX, DestY: integer; ScaleX, ScaleY: single); procedure StretchPrintGpImageDirect(Handle: THandle; Image: TGpImage; DestX, DestY, DestW, DestH: integer; ScaleX, ScaleY: single); procedure StretchPrintGpImageOnColor(Canvas: TCanvas; Image: TGpImage; DestX, DestY, DestW, DestH: integer; Color: TColor = clWhite); function htStyles(P0, P1, P2, P3: BorderStyleType): htBorderStyleArray; function htColors(C0, C1, C2, C3: TColor): htColorArray; procedure DrawBorder(Canvas: TCanvas; ORect, IRect: TRect; C: htColorArray; S: htBorderStyleArray; BGround: TColor; Print: boolean); function MultibyteToWideString(CodePage: integer; const S: string): WideString; function WideStringToMultibyte(CodePage: integer; W: WideString): string; function GetImageHeight(Image: TGpObject): integer; function GetImageWidth(Image: TGpObject): integer; implementation uses {$IFNDEF LCL} jpeg, {$ENDIF} DitherUnit, {$ifndef NoOldPng} PngImage1, {$endif} htmlview, htmlsubs, HtmlGif2, StylePars{$IFNDEF LCL}, ActiveX {$ENDIF}; type EGDIPlus = class (Exception); TJpegMod = class(TJpegImage) public {$IFNDEF LCL} //Don't need since TJpegImage is a bitmap. property Bitmap; {$ENDIF} end; var DC: HDC; {$IFDEF CPU86} {----------------StrLenW} function StrLenW(Str: PWideChar): Cardinal; {returns number of characters in a string excluding the null terminator} asm MOV EDX, EDI MOV EDI, EAX MOV ECX, 0FFFFFFFFH XOR AX, AX REPNE SCASW MOV EAX, 0FFFFFFFEH SUB EAX, ECX MOV EDI, EDX end; {----------------StrPosW} function StrPosW(Str, SubStr: PWideChar): PWideChar; // returns a pointer to the first occurance of SubStr in Str asm PUSH EDI PUSH ESI PUSH EBX OR EAX, EAX JZ @@2 OR EDX, EDX JZ @@2 MOV EBX, EAX MOV EDI, EDX XOR AX, AX MOV ECX, 0FFFFFFFFH REPNE SCASW NOT ECX DEC ECX JZ @@2 MOV ESI, ECX MOV EDI, EBX MOV ECX, 0FFFFFFFFH REPNE SCASW NOT ECX SUB ECX, ESI JBE @@2 MOV EDI, EBX LEA EBX, [ESI - 1] @@1: MOV ESI, EDX LODSW REPNE SCASW JNE @@2 MOV EAX, ECX PUSH EDI MOV ECX, EBX REPE CMPSW POP EDI MOV ECX, EAX JNE @@1 LEA EAX, [EDI - 2] JMP @@3 @@2: XOR EAX, EAX @@3: POP EBX POP ESI POP EDI end; {----------------StrRScanW} function StrRScanW(const Str: PWideChar; Chr: WideChar): PWideChar; assembler; asm PUSH EDI MOV EDI,Str MOV ECX,0FFFFFFFFH XOR AX,AX REPNE SCASW NOT ECX STD DEC EDI DEC EDI MOV AX,Chr REPNE SCASW MOV EAX,0 JNE @@1 MOV EAX,EDI INC EAX INC EAX @@1: CLD POP EDI end; {----------------StrScanW} function StrScanW(const Str: PWideChar; Chr: WideChar): PWideChar; assembler; asm PUSH EDI PUSH EAX MOV EDI,Str MOV ECX,$FFFFFFFF XOR AX,AX REPNE SCASW NOT ECX POP EDI MOV AX,Chr REPNE SCASW MOV EAX,0 JNE @@1 MOV EAX,EDI DEC EAX DEC EAX @@1: POP EDI end; {$ELSE} //Pascal-ized equivalents of assembler functions. function StrLenW(Str: PWideChar): Cardinal; begin Result := Length(WideString(Str)); end; function StrPosW(Str, SubStr: PWideChar): PWideChar; 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; 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; 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; {$ENDIF} {----------------FitText} function FitText(DC: HDC; S: PWideChar; Max, Width: Integer; var Extent: integer): Integer; {return count <= Max which fits in Width. Return X, the extent of chars that fit} type // Integers = array[1..1] of integer; Integers = array[1..1000] of Integer; // LCL port: To avoid range-check error below. var ExtS: TSize; Ints: ^Integers; L, H, I: integer; begin Extent := 0; Result := 0; if (Width <= 0) or (Max = 0) then Exit; {$IFDEF MSWINDOWS} //Only compile if GetTextExtentExPointW exists if not IsWin32Platform then begin GetMem(Ints, Sizeof(Integer)* Max); try {$ifdef ver120_plus} if GetTextExtentExPointW(DC, S, Max, Width, @Result, @Ints^, ExtS) then {$else} if GetTextExtentExPointW(DC, S, Max, Width, Result, Integer(Ints^), ExtS) then {$endif} if Result > 0 then Extent := Ints^[Result] else Extent := 0; finally FreeMem(Ints); end; end else {GetTextExtentExPointW not available in win98, 95} {$ENDIF} begin {optimize this by looking for Max to fit first -- it usually does} L := 0; H := Max; I := H; while L <= H do begin GetTextExtentPoint32W(DC, S, I, ExtS); if ExtS.cx < Width then L := I+1 else H := I-1; if ExtS.cx = Width then Break; I := (L+H) shr 1; end; Result := I; Extent := ExtS.cx; end; end; {----------------WidePos} function WidePos(SubStr, S: WideString): Integer; // 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; {----------------WideUpperCase1} function WideUpperCase1(const S: WideString): WideString; var Len, NewLen: Integer; Tmp: string; begin Len := Length(S); if not IsWin32Platform then begin SetString(Result, PWideChar(S), Len); if Len > 0 then CharUpperBuffW(Pointer(Result), Len); end else begin {win95,98,ME} SetLength(Tmp, 2*Len); NewLen := WideCharToMultiByte(CP_ACP, 0, PWideChar(S), Len, PChar(Tmp), 2*Len, Nil, Nil); SetLength(Tmp, NewLen); Tmp := AnsiUppercase(Tmp); SetLength(Result, Len); MultibyteToWideChar(CP_ACP, 0, PChar(Tmp), NewLen, PWideChar(Result), Len); end; end; function WideLowerCase1(const S: WideString): WideString; var Len, NewLen: Integer; Tmp: string; begin Len := Length(S); if not IsWin32Platform then begin SetString(Result, PWideChar(S), Len); if Len > 0 then CharLowerBuffW(Pointer(Result), Len); end else begin {win95,98,ME} SetLength(Tmp, 2*Len); NewLen := WideCharToMultiByte(CP_ACP, 0, PWideChar(S), Len, PChar(Tmp), 2*Len, Nil, Nil); SetLength(Tmp, NewLen); Tmp := AnsiLowercase(Tmp); SetLength(Result, Len); MultibyteToWideChar(CP_ACP, 0, PChar(Tmp), NewLen, PWideChar(Result), Len); end; end; function WideSameText1(const S1, S2: WideString): boolean; begin Result := WideUpperCase1(S1) = WideUpperCase1(S2); end; function WideSameStr1(const S1, S2: WideString): boolean; begin Result := S1 = S2; end; function PosX(const SubStr, S: string; Offset: integer = 1): Integer; {find substring in S starting at Offset} var S1: string; I: integer; begin if Offset <= 1 then Result := Pos(SubStr, S) else begin S1 := Copy(S, Offset, Length(S)-Offset+1); I := Pos(SubStr, S1); if I > 0 then Result := I+Offset-1 else Result := 0; end; end; {$IFDEF CPU86} function IntMin(A, B: Integer): Integer; asm cmp edx, eax jnle @1 mov eax, edx @1: end; Function IntMax(A, B : Integer) : Integer; asm cmp edx, eax jl @1 mov eax, edx @1: end; {$ELSE} //Pascal-ized equivalents. function IntMin(A, B: Integer): Integer; begin if A < B then Result := A else Result := B; end; Function IntMax(A, B : Integer) : Integer; begin if A > B then Result := A else Result := B; end; {$ENDIF} procedure GetClippingRgn(Canvas: TCanvas; ARect: TRect; Printing: boolean; var Rgn, SaveRgn: HRgn); 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 restroed 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); SelectClipRgn(Canvas.Handle, Rgn); end; function HTMLServerToDos(FName, Root: string): string; {Add Prefix Root only if first character is '\' but not '\\'} begin Result := Trim(HTMLToDos(FName)); if (Result <> '') and (Root <> '') then begin {$IFDEF MSWINDOWS} if Pos('\\', Result) = 1 then Exit; if Pos(':', Result) = 2 then Exit; if Result[1] = '\' then Result := Root+Result; {$ELSE} if Result[1] <> '/' then Result := Root+Result; {$ENDIF} end; end; function HTMLToDos(FName: string): string; {convert an HTML style filename to one for Dos} var I: integer; procedure Replace(Old, New: char); var I: integer; begin I := Pos(Old, FName); while I > 0 do begin FName[I] := New; I := Pos(Old, FName); end; end; procedure ReplaceEscapeChars; var S: string[3]; I: integer; begin I := Pos('%', FName); while (I > 1) and (I <= Length(FName)-2) do begin S := '$'+FName[I+1]+FName[I+2]; try FName[I] := chr(StrToInt(S)); Delete(FName, I+1, 2); except {ignore exception} Exit; end; I := Pos('%', FName); end; end; begin ReplaceEscapeChars; I := pos('/', FName); if I <> 0 then begin I := Pos('file:///', Lowercase(FName)); if I > 0 then System.Delete(FName, I, 8) else begin I := Pos('file://', Lowercase(FName)); if I > 0 then System.Delete(FName, I, 7) else begin I := Pos('file:/', Lowercase(FName)); if I > 0 then System.Delete(FName, I, 6); end; end; {$IFDEF MSWINDOWS} Replace('|', ':'); Replace('/', '\'); {$ENDIF} end; Result := FName; end; function WideTrim(const S: WideString): WideString; 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: WideString); {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 Allocate(Size: integer): AllocRec; begin Result := AllocRec.Create; with Result do begin ASize := Size; if Size < $FF00 then GetMem(Ptr, Size) else begin AHandle := GlobalAlloc(HeapAllocFlags, Size); if AHandle = 0 then ABort; Ptr := GlobalLock(AHandle); end; end; end; procedure DeAllocate(AR: AllocRec); begin with AR do if ASize < $FF00 then Freemem(Ptr, ASize) else begin GlobalUnlock(AHandle); GlobalFree(AHandle); end; AR.Free; end; function GetXExtent(DC: HDC; P: PWideChar; N: integer): integer; var ExtS: TSize; Dummy: integer; begin {$IFDEF MSWINDOWS} //Only compile if GetTextExtentExPointW exists if not IsWin32Platform then GetTextExtentExPointW(DC, P, N, 0, @Dummy, Nil, ExtS) else {$ENDIF} GetTextExtentPoint32W(DC, P, N, ExtS); {win95, 98 ME} Result := ExtS.cx; end; procedure FillRectWhite(Canvas: TCanvas; X1, Y1, X2, Y2: integer; Color: TColor); var OldBrushStyle: TBrushStyle; OldBrushColor: TColor; begin with Canvas do begin OldBrushStyle := Brush.Style; {save style first} OldBrushColor := Brush.Color; Brush.Color := 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; procedure FormControlRect(Canvas: TCanvas; X1: integer; Y1: integer; X2: integer; Y2: integer; Raised, PrintMonoBlack, Disabled: boolean; Color: TColor); {Draws lowered rectangles for form control printing} var OldStyle: TPenStyle; OldWid: integer; OldBrushStyle: TBrushStyle; OldBrushColor: TColor; MonoBlack: boolean; begin with Canvas do begin MonoBlack := PrintMonoBlack and (GetDeviceCaps(Handle, BITSPIXEL) = 1) and (GetDeviceCaps(Handle, PLANES) = 1); Dec(X2); Dec(Y2); OldWid := Pen.Width; OldStyle := Pen.Style; OldBrushStyle := Brush.Style; {save style first} OldBrushColor := Brush.Color; if not MonoBlack and Disabled then Brush.Color := clBtnFace else Brush.Color := color; Brush.Style := bsSolid; FillRect(Rect(X1, Y1, X2, Y2)); Brush.Color := OldBrushColor; Brush.Style := OldBrushStyle; {style after color as color changes style} Pen.Style := psInsideFrame; if MonoBlack then begin Pen.Width := 1; Pen.Color := clBlack; end else begin Pen.Width := 2; if Raised then Pen.Color := clSilver else Pen.Color := clBtnShadow; end; MoveTo(X1, Y2); LineTo(X1, Y1); LineTo(X2, Y1); if not MonoBlack then if Raised then Pen.Color := clBtnShadow else Pen.Color := clSilver; LineTo(X2, Y2); LineTo(X1, Y2); Pen.Style := OldStyle; Pen.Width := OldWid; end; end; procedure RaisedRect(SectionList: TFreeList; Canvas: TCanvas; X1: integer; Y1: integer; X2: integer; Y2: integer; Raised: boolean; W: integer); {Draws raised or lowered rectangles for table borders} var White, BlackBorder: boolean; Light, Dark: TColor; begin with SectionList as TSectionList, Canvas do begin White := Printing or ((Background and $FFFFFF = clWhite) or ((Background = clWindow) and (GetSysColor(Color_Window) = $FFFFFF))); BlackBorder := Printing and PrintMonoBlack and (GetDeviceCaps(Handle, BITSPIXEL) = 1) and (GetDeviceCaps(Handle, PLANES) = 1); end; if BlackBorder then begin Light := clBlack; Dark := clBlack; end else begin Dark := clBtnShadow; if White then Light := clSilver else Light := clBtnHighLight; end; RaisedRectColor(SectionList, Canvas, X1, Y1, X2, Y2, Light, Dark, Raised, W); end; procedure RaisedRectColor1(Canvas: TCanvas; X1: integer; Y1: integer; X2: integer; Y2: integer; Light, Dark: TColor; Raised: boolean); {Draws single line colored raised or lowered rectangles for table borders} begin Y1 := IntMax(Y1, TopLim); Y2 := IntMin(Y2, BotLim); with Canvas do begin if Raised then Pen.Color := Light else Pen.Color := Dark; MoveTo(X1, Y2); LineTo(X1, Y1); LineTo(X2, Y1); if not Raised then Pen.Color := Light else Pen.Color := Dark; LineTo(X2, Y2); LineTo(X1, Y2); end; end; procedure RaisedRectColor(SectionList: TFreeList; Canvas: TCanvas; X1: integer; Y1: integer; X2: integer; Y2: integer; Light, Dark: TColor; Raised: boolean; W: integer); {Draws colored raised or lowered rectangles for table borders} var Colors: htColorArray; begin if W = 1 then {this looks better in Print Preview} RaisedRectColor1(Canvas, X1, Y1, X2, Y2, Light, Dark, Raised) else begin if Raised then Colors := htColors(Light, Light, Dark, Dark) else Colors := htColors(Dark, Dark, Light, Light); DrawBorder(Canvas, Rect(X1-W+1, Y1-W+1, X2+W, Y2+W), Rect(X1+1, Y1+1, X2, Y2), Colors, htStyles(bssSolid, bssSolid, bssSolid, bssSolid), clNone, False); end; end; {$ifdef Ver90} procedure Assert(B: boolean; const S: string); begin {dummy Assert for Delphi 2} end; {$endif} destructor TFreeList.Destroy; var I: integer; begin for I := 0 to Count-1 do TObject(Items[I]).Free; inherited Destroy; end; procedure TFreeList.Clear; var I: integer; begin for I := 0 to Count-1 do TObject(Items[I]).Free; inherited Clear; end; constructor TBitmapItem.Create(AImage:TgpObject; AMask: TBitmap; Tr: Transparency); begin inherited Create; MImage := AImage; Mask := AMask; AccessCount := 0; Transp := Tr; end; destructor TBitmapItem.Destroy; begin Assert(UsageCount = 0, 'Freeing Image still in use'); MImage.Free; Mask.Free; inherited Destroy; end; constructor TStringBitmapList.Create; begin inherited Create; MaxCache := 4; CheckInitGDIPlus; end; destructor TStringBitmapList.Destroy; var I: integer; begin for I := 0 to Count-1 do (Objects[I] as TBitmapItem).Free; CheckExitGDIPlus; inherited Destroy; end; function TStringBitmapList.AddObject(const S: string; AObject: TObject): Integer; begin Result := inherited AddObject(S, AObject); if AObject is TBitmapItem then Inc(TBitmapItem(AObject).UsageCount); end; procedure TStringBitmapList.DecUsage(const S: string); var I: integer; begin I := IndexOf(S); if I >= 0 then with Objects[I] as TBitmapItem do begin Dec(UsageCount); Assert(UsageCount >= 0, 'Cache image usage count < 0'); end; end; procedure TStringBitmapList.IncUsage(const S: string); var I: integer; begin I := IndexOf(S); if I >= 0 then with Objects[I] as TBitmapItem do Inc(UsageCount); end; procedure TStringBitmapList.SetCacheCount(N: integer); var I: integer; begin for I := Count-1 downto 0 do with (Objects[I] as TBitmapItem)do begin if (AccessCount > N) and (UsageCount <= 0) then begin Delete(I); Free; end; end; MaxCache := N; end; function TStringBitmapList.GetImage(I: integer): TgpObject; begin with Objects[I] as TBitmapItem do begin Result := MImage; AccessCount := 0; Inc(UsageCount); end; end; procedure TStringBitmapList.BumpAndCheck; var I: integer; Tmp: TBitmapItem; begin for I := Count-1 downto 0 do begin Tmp := (Objects[I] as TBitmapItem); with Tmp do begin Inc(AccessCount); if (AccessCount > MaxCache) and (UsageCount <= 0) then begin Delete(I); Free; {the TBitmapItem} end; end; end; end; procedure TStringBitmapList.PurgeCache; var I: integer; Tmp: TBitmapItem; begin for I := Count-1 downto 0 do begin Tmp := (Objects[I] as TBitmapItem); with Tmp do begin if (UsageCount <= 0) then begin Delete(I); Free; {the TBitmapItem} end; end; end; end; procedure TStringBitmapList.Clear; var I: integer; begin for I := 0 to Count-1 do (Objects[I] as TBitmapItem).Free; inherited Clear; end; constructor TAttribute.Create(ASym: Symb; AValue: integer; Const NameStr, ValueStr: string; ACodePage: integer); begin inherited Create; Which := ASym; Value := AValue; WhichName := NameStr; Name := ValueStr; CodePage := ACodePage; end; destructor TAttribute.Destroy; begin inherited Destroy; end; {----------------TAttributeList} destructor TAttributeList.Destroy; begin Prop.Free; inherited; end; procedure TAttributeList.Clear; begin SaveID := ''; inherited Clear; end; function TAttributeList.Find(Sy: Symb; var T: TAttribute): boolean; var I: integer; begin for I := 0 to Count-1 do if TAttribute(Items[I]).which = Sy then begin Result := True; T := Items[I]; Exit; end; Result := False; end; function TAttributeList.CreateStringList: TStringList; var I: integer; begin Result := TStringList.Create; for I := 0 to Count-1 do with TAttribute(Items[I]) do Result.Add(WhichName+'='+Name); end; function TAttributeList.GetClass: string; var T: TAttribute; S: string; I: integer; begin Result := ''; 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 TAttributeList.GetID: string; var T: TAttribute; begin Result := SaveID; if (Result = '') and Find(IDSy, T) then begin Result := Lowercase(T.Name); SaveID := Result; end; end; function TAttributeList.GetTitle: string; var T: TAttribute; begin if Find(TitleSy, T) then Result := T.Name else Result := ''; end; function TAttributeList.GetStyle: TProperties; var T: TAttribute; begin if Find(StyleSy, T) then begin Prop.Free; Prop := TProperties.Create; Result := Prop; ParsePropertyStr(T.Name, Result); end else Result := Nil; end; {----------------TUrlTarget.Create} constructor TUrlTarget.Create; begin inherited Create; utText := TutText.Create; utText.Start := -1; utText.Last := -1; end; destructor TUrlTarget.Destroy; begin FreeAndNil(utText); inherited Destroy; end; var Sequence: integer = 10; procedure TUrlTarget.Assign(AnUrl, ATarget: String; L: TAttributeList; AStart: integer); var SL: TStringList; 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 TUrlTarget.Copy(UT: TUrlTarget); 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 TUrlTarget.Clear; begin Url := ''; Target := ''; ID := 0; TabIndex := 0; Attr := ''; utText.Start := -1; utText.Last := -1; end; function TUrlTarget.GetStart: integer; begin Result := utText.Start end; function TUrlTarget.GetLast: integer; begin Result := utText.Last end; procedure TUrlTarget.SetLast(List: TList; ALast: integer); var I: integer; begin utText.Last := ALast; if (List.Count > 0) then for I := List.Count-1 downto 0 do if (ID = TFontObj(List[I]).UrlTarget.ID) then TFontObj(List[I]).UrlTarget.utText.Last := ALast else Break; end; {----------------SelTextCount} procedure SelTextCount.AddText(P: PWideChar; Size: integer); var I: integer; begin for I := 0 to Size-1 do if not (P[I] in [FmCtl, ImgPan]) then {ImgPan and FmCtl used to mark images, form controls} Inc(Leng); end; procedure SelTextCount.AddTextCR(P: PWideChar; Size: integer); begin AddText(P, Size); AddText(#13#10, 2); end; function SelTextCount.Terminate: integer; begin Result := Leng; end; {----------------SelTextBuf.Create} constructor SelTextBuf.Create(ABuffer: PWideChar; Size: integer); begin inherited Create; Buffer := ABuffer; BufferLeng := Size; end; procedure SelTextBuf.AddText(P: PWideChar; Size: integer); var SizeM1 : integer; I: integer; begin SizeM1 := BufferLeng-1; for I := 0 to Size-1 do if not (P[I] in [FmCtl, ImgPan, BrkCh]) then {ImgPan and FmCtl used to mark images, form controls} if Leng < SizeM1 then begin Buffer[Leng] := P[I]; Inc(Leng); end; end; function SelTextBuf.Terminate: integer; begin Buffer[Leng] := #0; Result := Leng+1; end; {----------------ClipBuffer.Create} constructor ClipBuffer.Create(Leng: integer); begin inherited Create(Nil, 0); BufferLeng := Leng; Getmem(Buffer, BufferLeng*2); end; destructor ClipBuffer.Destroy; begin if Assigned(Buffer) then FreeMem(Buffer); inherited Destroy; end; procedure ClipBuffer.CopyToClipboard; {$IFNDEF LCL} {Unicode clipboard routine courtesy Mike Lischke} var Data: THandle; DataPtr: Pointer; begin Data := GlobalAlloc(GMEM_MOVEABLE + GMEM_DDESHARE, 2 * BufferLeng); try DataPtr := GlobalLock(Data); try Move(Buffer^, DataPtr^, 2 * BufferLeng); Clipboard.SetAsHandle(CF_UNICODETEXT, Data); finally GlobalUnlock(Data); end; except GlobalFree(Data); raise; end; {$ELSE} var Utf8Str : string; begin Utf8Str := WideCharLenToString(Buffer, BufferLeng); Clipboard.AddFormat(CF_TEXT, Utf8Str[1], Length(Utf8Str)); {$ENDIF} end; function ClipBuffer.Terminate: integer; begin Buffer[Leng] := #0; Result := Leng+1; if IsWin32Platform then Clipboard.AsText := Buffer else CopyToClipboard; end; {----------------TMapItem.Create} constructor TMapItem.Create; begin inherited Create; Areas := TStringList.Create; AreaTargets := TStringList.Create; AreaTitles := TStringList.Create; end; destructor TMapItem.Destroy; var I: integer; begin for I := 0 to Areas.Count-1 do DeleteObject(THandle(Areas.Objects[I])); Areas.Free; AreaTargets.Free; AreaTitles.Free; inherited Destroy; end; function TMapItem.GetURL(X, Y: integer; var URLTarg: TUrlTarget; var ATitle: string): boolean; var I: integer; begin Result := False; with Areas do for I := 0 to Count-1 do if PtInRegion(THandle(Objects[I]), X, Y) then begin if Strings[I] <> '' then {could be NoHRef} begin URLTarg := TUrlTarget.Create; URLTarg.URL := Strings[I]; URLTarg.Target := AreaTargets[I]; ATitle := AreaTitles[I]; Result := True; end; Exit; end; end; procedure TMapItem.AddArea(Attrib: TAttributeList); Const MAXCNT = 300; var I, Cnt, Rad: integer; HRef, S, Target, Title: string; S1, Nm: string[20]; Coords: array[0..MAXCNT] of integer; Rect: TRect absolute Coords; Handle: THandle; Shape: (Rec, Circle, Poly); procedure GetSubStr; var J,K: integer; begin J := Pos(',', S); K := Pos(' ', S); {for non comma situations (bad syntax)} if (J > 0) and ((K = 0) or (K > J)) then begin S1 := copy(S, 1, J-1); Delete(S, 1, J); end else if K > 0 then begin S1 := copy(S, 1, K-1); Delete(S, 1, K); end else begin S1 := Trim(S); S := ''; end; while (Length(S) > 0) and ((S[1]=',') or (S[1]=' ')) do Delete(S, 1, 1); end; begin if Areas.Count >= 1000 then Exit; HRef := ''; Target := ''; Title := ''; Shape := Rec; Cnt := 0; Handle := 0; for I := 0 to Attrib.Count-1 do with TAttribute(Attrib[I]) do case Which of HRefSy: HRef := Name; TargetSy: Target := Name; TitleSy: Title := Name; NoHrefSy: HRef := ''; CoordsSy: begin S := Trim(Name); Cnt := 0; GetSubStr; while (S1 <> '') and (Cnt <= MAXCNT) do begin Coords[Cnt] := StrToIntDef(S1, 0); GetSubStr; Inc(Cnt); end; end; ShapeSy: begin Nm := copy(Lowercase(Name),1, 4); if Nm = 'circ' then Shape := Circle else if (Nm = 'poly') then Shape := Poly; end; end; case Shape of Rec: begin if Cnt < 4 then Exit; Inc(Coords[2]); Inc(Coords[3]); Handle := CreateRectRgnIndirect(Rect); end; Circle: begin if Cnt < 3 then Exit; Rad := Coords[2]; Dec(Coords[0],Rad); Dec(Coords[1],Rad); Coords[2] := Coords[0] + 2*Rad +1; Coords[3] := Coords[1] + 2*Rad +1; Handle := CreateEllipticRgnIndirect(Rect); end; Poly: begin if Cnt < 6 then Exit; {$IFNDEF LCL} Handle := CreatePolygonRgn(Coords, Cnt div 2, Winding); {$ELSE} Handle := CreatePolygonRgn(PPoint(@Coords), Cnt div 2, Winding); {$ENDIF} end; end; if Handle <> 0 then begin Areas.AddObject(HRef, TObject(Handle)); AreaTargets.Add(Target); AreaTitles.Add(Title); end; end; function KindOfImageFile(FName: String): ImageType; var Mem: TMemoryStream; begin Result := NoImage; if FileExists(FName) then begin Mem := TMemoryStream.Create; try Mem.LoadFromFile(FName); if Mem.Size >=10 then Result := KindOfImage(Mem.Memory); finally Mem.Free; end; end; end; function KindOfImage(Start: Pointer): ImageType; type ByteArray = array[0..10] of byte; var PB: ^ByteArray absolute Start; PW: ^Word absolute Start; PL: ^DWord absolute Start; begin if PL^ = $38464947 then begin if PB^[4] = Ord('9') then Result := Gif89 else Result := Gif; end else if PW^ = $4D42 then Result := Bmp else if PL^ = $474E5089 then Result := Png else if PW^ = $D8FF then Result := Jpg else Result := NoImage; end; {$A-} {record field alignment off for this routine} function IsTransparent(Stream: TStream; var Color: TColor): boolean; {Makes some simplifying assumptions that seem to be generally true for single images.} Type RGB = record Red, Green, Blue: byte; end; GifHeader = record GIF: array[0..2] of char; Version: array[0..2] of char; ScreenWidth, ScreenHeight: Word; Field: Byte; BackGroundColorIndex: byte; AspectRatio: byte; end; ColorArray = array[0..255] of RGB; var Header: ^GifHeader; X: integer; Colors: ^ColorArray; Buff: array[0..Sizeof(GifHeader)+Sizeof(ColorArray)+8] of byte; P: PChar; OldPosition: integer; begin Result := False; Fillchar(Buff, Sizeof(Buff), 0); {in case read comes short} OldPosition := Stream.Position; Stream.Position := 0; Stream.Read(Buff, Sizeof(Buff)); Stream.Position := OldPosition; Header := @Buff; if KindOfImage(Header) <> Gif89 then Exit; Colors := @Buff[Sizeof(GifHeader)]; with Header^ do begin X := 1 shl ((Field and 7) +1) - 1; {X is last item in color table} if X = 0 then Exit; {no main color table} end; P := PChar(Colors)+(X+1)*Sizeof(RGB); if (P^ <> #$21) or ((P+1)^ <> #$F9) then Exit; {extension block not found} if (ord(P[3]) and 1 <> 1) then Exit; {no transparent color specified} with Colors^[Ord(P[6])] do Color := integer(Blue) shl 16 or integer(Green) shl 8 or integer(Red); Result := True; end; {$A+} {$A-} {record field alignment off for this routine} function IsTransparentPng(Stream: TStream; var Color: TColor): boolean; Type RGB = record Red, Green, Blue: byte; end; PngHeader = record width : integer; height : integer; bitDepth : byte; colorType : byte; compression : byte; filter : byte; interlace : byte; end; var Header: PngHeader; CRC: integer; OldPosition: integer; pngPalette: array[0..255] of RGB; dataSize : integer; chunkType: array[0..4] of Char; chunkTypeStr: string; done : Boolean; Ar: Array[0..10] of byte; Alpha: array[0..255] of byte; I: integer; function IntSwap(data: integer): integer; var byte0 : integer; byte1 : integer; byte2 : integer; byte3 : integer; begin byte0 := data and $FF; byte1 := (data shr 8) and $FF; byte2 := (data shr 16) and $FF; byte3 := (data shr 24) and $FF; result := (byte0 shl 24) or (byte1 shl 16) or (byte2 shl 8) or byte3; end; begin result := false; OldPosition := Stream.Position; try Stream.Position := 0; Stream.Read(Ar, 8); if KindOfImage(@Ar) <> Png then begin Stream.Position := OldPosition; Exit; end; Stream.Position := 8; {past the PNG Signature} done := False; {Read Chunks} repeat Stream.Read(dataSize, 4); dataSize := IntSwap(dataSize); Stream.Read(chunkType, 4); chunkType[4] := #0; {make sure string is NULL terminated} chunkTypeStr := StrPas(chunkType); if chunkTypeStr = 'IHDR' then begin Stream.Read(Header, DataSize); Header.width := IntSwap(Header.width); Header.height := IntSwap(Header.height); Stream.Read(CRC, 4); {read it in case we need to read more} if (Header.colorType < 2) or (Header.colorType > 3) then done := True; {only type 2 and 3 use tRNS} end else if chunkTypeStr = 'PLTE' then begin Stream.Read(pngPalette, DataSize); Stream.Read(CRC, 4); {read it in case we need to read more} end else if chunkTypeStr = 'tRNS' then begin if Header.colorType = 3 then begin {there can be DataSize transparent or partial transparent colors. We only accept one fully transparent color} Stream.Read(Alpha, DataSize); for I := 0 to DataSize -1 do if Alpha[I] = 0 then {0 means full transparency} begin with pngPalette[I] do Color := integer(Blue) shl 16 or integer(Green) shl 8 or integer(Red); Result := True; break; end; end else {has to have been 2} begin {for now I am ignoring this since I can't make one} end; done := true; {got everything we need at this point} end else if chunkTypeStr = 'IDAT' then done := True {if this chunk is hit there is no tRNS} else Stream.Position := Stream.Position + dataSize + 4; {additional 4 for the CRC} if Stream.Position >= Stream.Size then Done := True; until done = True; except end; Stream.Position := OldPosition; end; {$A+} function TransparentGIF(const FName: string; var Color: TColor): boolean; {Looks at a GIF image file to see if it's a transparent GIF.} {Needed for OnBitmapRequest Event handler} var Stream: TFileStream; begin Result := False; try Stream := TFileStream.Create(FName, fmShareDenyWrite or FmOpenRead); try Result := IsTransparent(Stream, Color); finally Stream.Free; end; except end; end; function ConvertImage(Bitmap: TBitmap): TBitmap; {convert bitmap into a form for BitBlt later} function DIBConvert: TBitmap; var DC: HDC; DIB: TDib; OldBmp: HBitmap; OldPal: HPalette; Hnd: HBitmap; begin DC := CreateCompatibleDC(0); OldBmp := SelectObject(DC, Bitmap.Handle); OldPal := SelectPalette(DC, ThePalette, False); RealizePalette(DC); DIB := TDib.CreateDIB(DC, Bitmap); Hnd := DIB.CreateDIBmp; DIB.Free; SelectPalette(DC, OldPal, False); SelectObject(DC, OldBmp); DeleteDC(DC); Bitmap.Free; Result := TBitmap.Create; Result.Handle := Hnd; if (ColorBits = 8) and (Result.Palette = 0) then Result.Palette := CopyPalette(ThePalette); end; begin if not Assigned(Bitmap) then begin Result := Nil; Exit; end; if ColorBits > 8 then begin if Bitmap.PixelFormat <= pf8bit then Result := DIBConvert else Result := Bitmap; Exit; end; if Bitmap.HandleType = bmDIB then begin Result := GetBitmap(Bitmap); Bitmap.Free; Exit; end; Result := DIBConvert; end; {----------------GetImageAndMaskFromFile} function GetImageAndMaskFromFile(const Filename: String; var Transparent: Transparency; var Mask: TBitmap): TgpObject; var Stream: TMemoryStream; begin Result := Nil; Mask := Nil; if not FileExists(Filename) then Exit; if GDIPlusActive and (KindOfImageFile(Filename) = Png) then begin Result := TObject(TGPBitmap.Create(Filename)); end else begin Stream := TMemoryStream.Create; Stream.LoadFromFile(Filename); try Result := GetImageAndMaskFromStream(Stream, Transparent, Mask); finally Stream.Free; end; end; end; {----------------GetBitmapAndMaskFromStream} function GetBitmapAndMaskFromStream(Stream: TMemoryStream; var Transparent: Transparency; var AMask: TBitmap): TBitmap; var IT: ImageType; jpImage: TJpegMod; {$ifndef NoOldPng} PI: TPngImage; Color: TColor; Tmp: TBitmap; {$endif} begin Result := Nil; AMask := Nil; if not Assigned(Stream) or (Stream.Memory = Nil) or (Stream.Size < 20) then Exit; Stream.Position := 0; IT := KindOfImage(Stream.Memory); if not (IT in [Bmp, Jpg, Png]) then Exit; Result := TBitmap.Create; try if IT = Jpg then begin Transparent := NotTransp; jpImage := TJpegMod.Create; try jpImage.LoadFromStream(Stream); if ColorBits <= 8 then begin {$IFNDEF LCL} jpImage.PixelFormat := jf8bit; {$ELSE} jpImage.PixelFormat := pf8bit; {$ENDIF} if not jpImage.GrayScale and (ColorBits = 8) then jpImage.Palette := CopyPalette(ThePalette); end {$IFNDEF LCL} else jpImage.PixelFormat := jf24bit; Result.Assign(jpImage.Bitmap); {$ELSE} else jpImage.PixelFormat := pf24bit; Result.Assign(jpImage); {$ENDIF} finally jpImage.Free; end; end {$ifndef NoOldPng} else if IT = Png then begin if IsTransparentPNG(Stream, Color) then {check for transparent PNG} Transparent := TPng; PI := TPngImage.Create; try PI.LoadFromStream(Stream); Result.Assign(PI); if Result.Handle <> 0 then; {force proper initiation win98/95} finally PI.Free; end; end {$else} else if IT = Png then Result := Nil {$endif} else begin Result.LoadFromStream(Stream); {Bitmap} end; if Transparent = LLCorner then AMask := GetImageMask(Result, False, 0) {$ifdef NoOldPng} ; {$else} else if Transparent = TPng then begin AMask := GetImageMask(Result, True, Color); {Replace the background color with black. This is needed if the Png is a background image.} Tmp := Result; Result := TBitmap.Create; Result.Width := Tmp.Width; Result.Height := Tmp.Height; Result.Palette := CopyPalette(ThePalette); with Result do begin Canvas.Brush.Color := Color; PatBlt(Canvas.Handle, 0, 0, Width, Height, PatCopy); SetBkColor(Canvas.Handle, clWhite); SetTextColor(Canvas.Handle, clBlack); BitBlt(Canvas.Handle, 0, 0, Width, Height, AMask.Canvas.Handle, 0, 0, SrcAnd); BitBlt(Canvas.Handle, 0, 0, Width, Height, Tmp.Canvas.Handle, 0, 0, SrcInvert); end; Tmp.Free; end; {$endif} Result := ConvertImage(Result); except Result.Free; Result := Nil; end; end; var Unique: integer = 183902; {----------------GetImageAndMaskFromStream} function GetImageAndMaskFromStream(Stream: TMemoryStream; var Transparent: Transparency; var AMask: TBitmap): TgpObject; var Filename: string; Path: array[0..Max_Path] of char; F: File; I: integer; begin Result := Nil; AMask := Nil; if not Assigned(Stream) or (Stream.Memory = Nil) or (Stream.Size < 20) then Exit; Stream.Position := 0; if GDIPlusActive and (KindOfImage(Stream.Memory) = png) then begin try GetTempPath(Max_Path, @Path); SetLength(Filename, Max_Path+1); GetTempFilename(@Path, 'png', Unique, PChar(Filename)); Inc(Unique); I := Pos(#0, Filename); SetLength(Filename, I-1); AssignFile(F, Filename); ReWrite(F, 1); BlockWrite(F, Stream.Memory^, Stream.Size); CloseFile(F); Result := TgpImage.Create(Filename, True); {True because it's a temporary file} Transparent := NotTransp; except end; Exit; end; Result := GetBitmapAndMaskFromStream(Stream, Transparent, AMask); {$ifndef NoMetafile} if not Assigned(Result) then begin Result := ThtMetafile.Create; try AMask := Nil; Transparent := NotTransp; ThtMetaFile(Result).LoadFromStream(Stream); except FreeAndNil(Result); end; end; {$endif} end; function GetImageMask(Image: TBitmap; ColorValid: boolean; AColor: TColor): TBitmap; begin try if ColorValid then Image.TransparentColor := AColor; {color has already been selected} {else the transparent color is the lower left pixel of the bitmap} Image.Transparent := True; Result := TBitmap.Create; try Result.Handle := Image.ReleaseMaskHandle; Image.Transparent := False; except FreeAndNil(Result); end; except Result := Nil; end; end; function GetImageFromFile(const Filename: String): TBitmap; {used only externally in OnBitmapRequest handler} var IT: ImageType; Mask: TBitmap; Transparent: Transparency; Stream: TMemoryStream; GpObj: TGpObject; function GetGif: TBitmap; var TmpGif: TGifImage; NonAnimated: boolean; begin Result := Nil; TmpGif := CreateAGifFromStream(NonAnimated, Stream); if Assigned(TmpGif) then begin Result := TBitmap.Create; try //{$IFNDEF LCL} Result.Assign(TmpGif.Bitmap); //{$ENDIF} except Result.Free; Result := Nil; end; TmpGif.Free; end end; begin Result := Nil; try Stream := TMemoryStream.Create; try Stream.LoadFromFile(Filename); IT := KindOfImage(Stream.Memory); if IT in [Gif, Gif89] then Result := GetGif else begin GpObj := GetImageAndMaskFromStream(Stream, Transparent, Mask); Mask.Free; if GpObj is TBitmap then Result := TBitmap(GpObj) {$ifndef NoMetafile} else if GpObj is ThtMetafile then begin Result := TBitmap.Create; Result.Assign(ThtMetafile(GpObj).WhiteBGBitmap); GpObj.Free; end {$endif} else if GpObj is TGpImage then begin Result := TBitmap.Create; Result.Assign(TGpImage(GpObj).GetTBitmap); GpObj.Free; end; end; finally Stream.Free; end; except Result := Nil; end; end; {----------------FinishTransparentBitmap } procedure FinishTransparentBitmap (ahdc: HDC; InImage, Mask: TBitmap; xStart, yStart, W, H: integer); var bmAndBack, bmSave, bmBackOld, bmObjectOld : HBitmap; hdcInvMask, hdcMask, hdcImage: HDC; DestSize, SrcSize : TPoint; OldBack, OldFore: TColor; {$IFNDEF LCL} BM: Windows.TBitmap; {$ELSE} BM: LclType.BITMAP; {$ENDIF} {$IFNDEF MSWINDOWS} ColorMask : TBitmap; ColorMaskIntfImg : TLazIntfImage; MaskIntfImg : TLazIntfImage; {$ENDIF} Image: TBitmap; begin Image := TBitmap.Create; {protect original image} try Image.Assign(InImage); hdcImage := CreateCompatibleDC (ahdc); SelectObject (hdcImage, Image.Handle); { select the bitmap } { convert bitmap dimensions from device to logical points} SrcSize.x := Image.Width; SrcSize.y := Image.Height; DPtoLP(hdcImage, SrcSize, 1); DestSize.x := W; DestSize.y := H; DPtoLP (hdcImage, DestSize, 1); { create a bitmap for each DC} { monochrome DC} {$IFDEF MSWINDOWS} bmAndBack := CreateBitmap (SrcSize.x, SrcSize.y, 1, 1, nil); {$ELSE} // LCL port: Both Carbon and GTK2 widgetsets have trouble with // monochrome bitmaps, so create bitmap with same color format // for mask inverse and also convert mask to color format. bmAndBack := CreateCompatibleBitmap(ahdc, SrcSize.x, SrcSize.y); ColorMask := TBitmap.Create; ColorMask.Width := Mask.Width; ColorMask.Height := Mask.Height; ColorMask.PixelFormat := Image.PixelFormat; ColorMaskIntfImg := ColorMask.CreateIntfImage; MaskIntfImg := Mask.CreateIntfImage; ColorMaskIntfImg.CopyPixels(MaskIntfImg); ColorMask.LoadFromIntfImage(ColorMaskIntfImg); ColorMaskIntfImg.Free; MaskIntfImg.Free; {$ENDIF} bmSave := CreateCompatibleBitmap (ahdc, DestSize.x, DestSize.y); GetObject(bmSave, SizeOf(BM), @BM); if (BM.bmBitsPixel > 1) or (BM.bmPlanes > 1) then begin { create some DCs to hold temporary data} hdcInvMask := CreateCompatibleDC(ahdc); hdcMask := CreateCompatibleDC(ahdc); { each DC must select a bitmap object to store pixel data} bmBackOld := SelectObject (hdcInvMask, bmAndBack); { set proper mapping mode} SetMapMode (hdcImage, GetMapMode (ahdc)); {$IFDEF MSWINDOWS} bmObjectOld := SelectObject(hdcMask, Mask.Handle); {$ELSE} bmObjectOld := SelectObject(hdcMask, ColorMask.Handle); {$ENDIF} { create the inverse of the object mask} BitBlt (hdcInvMask, 0, 0, SrcSize.x, SrcSize.y, hdcMask, 0, 0, NOTSRCCOPY); {set the background color of the source DC to the color contained in the parts of the bitmap that should be transparent, the foreground to the parts that will show} OldBack := SetBkColor(ahDC, clWhite); OldFore := SetTextColor(ahDC, clBlack); { Punch out a black hole in the background where the image will go} SetStretchBltMode(ahDC, WhiteOnBlack); StretchBlt (ahDC, XStart, YStart, DestSize.x, DestSize.y, hdcMask, 0, 0, SrcSize.x, SrcSize.y, SRCAND); { mask out the transparent colored pixels on the bitmap} BitBlt (hdcImage, 0, 0, SrcSize.x, SrcSize.y, hdcInvMask, 0, 0, SRCAND); { XOR the bitmap with the background on the destination DC} SetStretchBltMode(ahDC, ColorOnColor); StretchBlt(ahDC, XStart, YStart, W, H, hdcImage, 0, 0, Image.Width, Image.Height, SRCPAINT); SetBkColor(ahDC, OldBack); SetTextColor(ahDC, OldFore); { delete the memory bitmaps} DeleteObject (SelectObject (hdcInvMask, bmBackOld)); SelectObject (hdcMask, bmObjectOld); { delete the memory DCs} DeleteDC (hdcInvMask); DeleteDC (hdcMask); end else begin DeleteObject(bmAndBack); end; DeleteObject(bmSave); DeleteDC (hdcImage); {$IFNDEF MSWINDOWS} ColorMask.Free; {$ENDIF} finally Image.Free; end; end; {----------------TDib.CreateDIB} constructor TDib.CreateDIB(DC: HDC; Bitmap: TBitmap); {given a TBitmap, construct a device independent bitmap} var ImgSize: DWord; begin InitializeBitmapInfoHeader(Bitmap.Handle); ImgSize := Info^.biSizeImage; Allocate(ImgSize); try GetDIBX(DC, Bitmap.Handle, Bitmap.Palette); except DeAllocate; Raise; end; end; destructor TDib.Destroy; begin DeAllocate; inherited Destroy; end; procedure TDib.Allocate(Size: integer); begin ImageSize := Size; if Size < $FF00 then GetMem(Image, Size) else begin FHandle := GlobalAlloc(HeapAllocFlags, Size); if FHandle = 0 then ABort; Image := GlobalLock(FHandle); end; end; procedure TDib.DeAllocate; begin if ImageSize > 0 then begin if ImageSize < $FF00 then Freemem(Image, ImageSize) else begin GlobalUnlock(FHandle); GlobalFree(FHandle); end; ImageSize := 0; end; if InfoSize > 0 then begin FreeMem(Info, InfoSize); InfoSize := 0; end; end; procedure TDib.InitializeBitmapInfoHeader(Bitmap: HBITMAP); var {$IFNDEF LCL} BM: Windows.TBitmap; {$ELSE} BM: LclType.BITMAP; {$ENDIF} BitCount: integer; function WidthBytes(I: integer): integer; begin Result := ((I + 31) div 32) * 4; end; begin GetObject(Bitmap, SizeOf(BM), @BM); BitCount := BM.bmBitsPixel * BM.bmPlanes; if BitCount > 8 then InfoSize := SizeOf(TBitmapInfoHeader) else InfoSize := SizeOf(TBitmapInfoHeader) + SizeOf(TRGBQuad) * (1 shl BitCount); GetMem(Info, InfoSize); with Info^ do begin biSize := SizeOf(TBitmapInfoHeader); biWidth := BM.bmWidth; biHeight := BM.bmHeight; biBitCount := BM.bmBitsPixel * BM.bmPlanes; biPlanes := 1; biXPelsPerMeter := 0; biYPelsPerMeter := 0; biClrUsed := 0; biClrImportant := 0; biCompression := BI_RGB; if biBitCount in [16, 32] then biBitCount := 24; biSizeImage := WidthBytes(biWidth * biBitCount) * biHeight; end; end; procedure TDib.GetDIBX(DC: HDC; Bitmap: HBITMAP; Palette: HPALETTE); var OldPal: HPALETTE; Rslt: integer; bmInfo: PBitmapInfo; begin OldPal := 0; if Palette <> 0 then begin OldPal := SelectPalette(DC, Palette, False); RealizePalette(DC); end; bmInfo := PBitmapInfo(Info); Rslt := GetDIBits(DC, Bitmap, 0, Info^.biHeight, Image, bmInfo^, DIB_RGB_COLORS); if OldPal <> 0 then SelectPalette(DC, OldPal, False); if Rslt = 0 then begin OutofMemoryError; end; end; procedure TDib.DrawDIB(DC: HDC; X: Integer; Y: integer; W, H: integer; ROP: DWord); var bmInfo: PBitmapInfo; begin bmInfo := PBitmapInfo(Info); with Info^ do StretchDIBits(DC, X, Y, W, H, 0, 0, biWidth, biHeight, Image, bmInfo^, DIB_RGB_COLORS, ROP); end; function TDib.CreateDIBmp: hBitmap; var bmInfo: PBitmapInfo; DC: HDC; OldPal: HPalette; begin bmInfo := PBitmapInfo(Info); DC := GetDC(0); OldPal := SelectPalette(DC, ThePalette, False); RealizePalette(DC); try Result := CreateDIBitmap(DC, bmInfo^.bmiHeader, CBM_INIT, Image, bmInfo^, DIB_RGB_COLORS); finally SelectPalette(DC, OldPal, False); ReleaseDC(0, DC); end; end; {----------------IndentManagerBasic.Create} constructor IndentManagerBasic.Create; begin inherited Create; R := TFreeList.Create; L := TFreeList.Create; end; destructor IndentManagerBasic.Destroy; begin R.Free; L.Free; inherited Destroy; end; procedure IndentManagerBasic.Clear; begin R.Clear; L.Clear; CurrentID := Nil; end; {----------------IndentManagerBasic.Reset} procedure IndentManagerBasic.Reset(Lf, Rt: integer); begin LfEdge := Lf; RtEdge := Rt; CurrentID := Nil; end; procedure IndentManagerBasic.UpdateTable(Y: integer; IW: integer; IH: integer; Justify: JustifyType); {Given a floating table, update the edge information. } var IR: IndentRec; begin IR := IndentRec.Create; if (Justify = Left) then begin with IR do begin X := -LfEdge + IW; YT := Y; YB := Y + IH; L.Add(IR); end; end else if (Justify = Right) then begin with IR do begin X := RightSide(Y) - IW; YT := Y; YB := Y + IH; R.Add(IR); end; end; end; const BigY = 9999999; function IndentManagerBasic.LeftIndent(Y: integer): integer; var I: integer; begin Result := -99999; for I := 0 to L.Count-1 do with IndentRec(L.Items[I]) do begin if (Y >= YT) and (Y < YB) and (Result < X) then if not Assigned(ID) or (ID = CurrentID) then Result := X; end; if Result = -99999 then Result := 0; Inc(Result, LfEdge); end; function IndentManagerBasic.RightSide(Y: integer): integer; {returns the current right side dimension as measured from the left, a positive number} var I: integer; IR: IndentRec; begin Result := 99999; for I := 0 to R.Count-1 do begin IR := IndentRec(R.Items[I]); with IR do if (Y >= YT) and (Y < YB) and (Result > X) then if not Assigned(ID) or (ID = CurrentID) then Result := X; end; if Result = 99999 then Result := RtEdge else Inc(Result, LfEdge); end; function IndentManagerBasic.ImageBottom: integer; {finds the bottom of the last floating image} var I: integer; begin Result := 0; for I := 0 to L.Count-1 do with IndentRec(L.Items[I]) do if not Assigned(ID) and (YB > Result) then Result := YB; for I := 0 to R.Count-1 do with IndentRec(R.Items[I]) do if not Assigned(ID) and (YB > Result) then Result := YB; end; procedure IndentManagerBasic.GetClearY(var 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 IndentRec(L.Items[I]) do if not Assigned(ID)and (YB > CL) then CL := YB; CR := -1; for I := 0 to R.Count-1 do with IndentRec(R.Items[I]) do if not Assigned(ID)and (YB > CR) then CR := YB; Inc(CL); Inc(CR); end; function IndentManagerBasic.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 IndentRec(L.Items[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 IndentRec(R.Items[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 := IntMin(CL, CR); end; function IndentManagerBasic.SetLeftIndent(XLeft, Y: integer): integer; var IR: IndentRec; begin IR := IndentRec.Create; with IR do begin YT := Y; YB := BigY; X := XLeft; ID := CurrentID; end; Result := L.Add(IR); end; function IndentManagerBasic.SetRightIndent(XRight, Y: integer): integer; var IR: IndentRec; begin IR := IndentRec.Create; with IR do begin YT := Y; YB := BigY; X := XRight; ID := CurrentID; end; Result := R.Add(IR); end; procedure IndentManagerBasic.FreeLeftIndentRec(I: integer); begin IndentRec(L.Items[I]).Free; L.Delete(I); end; procedure IndentManagerBasic.FreeRightIndentRec(I: integer); begin IndentRec(R.Items[I]).Free; R.Delete(I); end; procedure SetGlobalPalette(Value: HPalette); begin end; function CopyPalette(Source: hPalette): hPalette; 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; procedure CalcPalette(DC: HDC); {calculate a rainbow palette, one with equally spaced colors} const Values: array[0..5] of integer = (55, 115, 165, 205, 235, 255); var LP: ^TLogPalette; I, J, K, Sub: integer; begin GetMem(LP, Sizeof(TLogPalette) + 256*Sizeof(TPaletteEntry)); try with LP^ do begin palVersion := $300; palNumEntries := 256; GetSystemPaletteEntries(DC, 0, 256, palPalEntry); Sub := 10; {start at entry 10} for I := 0 to 5 do for J := 0 to 5 do for K := 0 to 5 do if not ((I=5) and (J=5) and (K=5)) then {skip the white} with palPalEntry[Sub] do begin peBlue := Values[I]; peGreen := Values[J]; peRed := Values[K]; peFlags := 0; Inc(Sub); end; for I := 1 to 24 do if not (I in [7, 15, 21]) then {these would be duplicates} with palPalEntry[Sub] do begin peBlue := 130 + 5*I; peGreen := 130 + 5*I; peRed := 130 + 5*I; peFlags := 0; Inc(Sub); end; Sub := 245; with palPalEntry[Sub] do begin peBlue := 254; peGreen := 255; peRed := 255; peFlags := 0; end; ThePalette := CreatePalette(LP^); end; finally FreeMem(LP, Sizeof(TLogPalette) + 256*Sizeof(TPaletteEntry)); end; end; const DefaultBitmap = 1002; ErrBitmap = 1001; ErrBitmapMask = 1005; Hand_Cursor = 1003; ThickIBeam_Cursor = 1006; procedure ThisExit; far; begin if ThePalette <> 0 then DeleteObject(ThePalette); DefBitMap.Free; ErrorBitMap.Free; ErrorBitMapMask.Free; WaitStream.Free; end; {----------------TIDNameList} constructor TIDNameList.Create(List: TList); begin inherited Create; Sorted := True; OwnerList := List; end; destructor TIDNameList.Destroy; begin Clear; inherited end; procedure TIDNameList.Clear; var I: integer; begin for I := 0 to Count-1 do try if Objects[I] is TChPosObj then Objects[I].Free; except end; inherited Clear; end; function TIDNameList.AddObject(const S: string; AObject: TObject): Integer; var I: integer; begin if Find(S, I) then begin try if Objects[I] is TChPosObj then Objects[I].Free; except end; Delete(I); end; Result := inherited AddObject(S, AObject); end; procedure TIDNameList.AddChPosObject(const S: string; Pos: integer); var ChPosObj: TChPosObj; begin ChPosObj := TChPosObj.Create; ChPosObj.List := OwnerList; ChPosObj.ChPos := Pos; AddObject(S, ChPosObj); end; destructor TIDObject.Destroy; begin inherited; end; {----------------TChPosObj.GetYPosition:} function TChPosObj.GetYPosition: integer; var Pos, X, Y: integer; begin with List as TSectionList do begin Pos := FindDocPos(ChPos, False); if CursorToXY(Nil, Pos, X, Y) then Result := Y else Result := 0; end; end; {$ifndef NoMetafile} procedure ThtMetaFile.Construct; var Tmp: TBitmap; pe: TPaletteEntry; Color: TColor; begin if not Assigned(FBitmap) then begin FBitmap := TBitmap.Create; try FBitmap.Width := Width; FBitmap.Height := Height; PatBlt(FBitmap.Canvas.Handle, 0, 0, Width, Height, Blackness); FBitmap.Canvas.Draw(0, 0, Self); Tmp := TBitmap.Create; try Tmp.Width := Width; Tmp.Height := Height; Tmp.PixelFormat := pf8Bit; {pick an odd color from the palette to represent the background color, one not likely in the metafile} GetPaletteEntries(Tmp.Palette, 115, 1, pe); Color := pe.peBlue shl 16 or pe.peGreen shl 8 or pe.peRed; Tmp.Canvas.Brush.Color := Color; Tmp.Canvas.FillRect(Rect(0, 0, Width, Height)); Tmp.Canvas.Draw(0, 0, Self); FMask := GetImageMask(Tmp, False, Color); finally Tmp.Free; end; except FreeAndNil(FBitmap); end; end; end; function ThtMetaFile.GetBitmap: TBitmap; begin Construct; Result := FBitmap; end; function ThtMetaFile.GetMask: TBitmap; begin Construct; Result := FMask; end; function ThtMetaFile.GetWhiteBGBitmap: TBitmap; begin if not Assigned(FWhiteBGBitmap) then begin FWhiteBGBitmap := TBitmap.Create; try FWhiteBGBitmap.Width := Width; FWhiteBGBitmap.Height := Height; PatBlt(FWhiteBGBitmap.Canvas.Handle, 0, 0, Width, Height, Whiteness); FWhiteBGBitmap.Canvas.Draw(0, 0, Self); except FreeAndNil(FWhiteBGBitmap); end; end; Result := FWhiteBGBitmap; end; destructor ThtMetaFile.Destroy; begin FreeAndNil(FBitmap); FreeAndNil(FMask); FreeAndNil(FWhiteBGBitmap); inherited; end; {$endif} function InSet(W: WideChar; S: SetOfChar): boolean; begin if Ord(W) > 255 then Result := False else Result := Char(W) in S; end; {----------------TCharCollection.GetAsString:} function TCharCollection.GetAsString: string; begin Result := Copy(FChars, 1, FCurrentIndex); end; function TCharCollection.GetSize: Integer; begin Result := FCurrentIndex; end; constructor TCharCollection.Create; begin inherited; SetLength(FChars, TokenLeng); GetMem(FIndices, TokenLeng*Sizeof(integer)); FCurrentIndex := 0; end; destructor TCharCollection.Destroy; begin FreeMem(FIndices); inherited; end; procedure TCharCollection.Add(C: Char; Index: Integer); begin if FCurrentIndex = Length(FChars) then begin SetLength(FChars, FCurrentIndex + 50); ReallocMem(FIndices, (FCurrentIndex+50)*Sizeof(integer)); end; Inc(FCurrentIndex); FIndices^[FCurrentIndex] := Index; FChars[FCurrentIndex] := C; end; procedure TCharCollection.Clear; begin FCurrentIndex := 0; FChars := ''; end; procedure TCharCollection.Concat(T: TCharCollection); var K: integer; begin K := FCurrentIndex + T.FCurrentIndex; if K >= Length(FChars) then begin SetLength(FChars, K + 50); ReallocMem(FIndices, (K+50)*Sizeof(integer)); end; Move(PChar(T.FChars)^, FChars[FCurrentIndex + 1], T.FCurrentIndex); Move(T.FIndices^[1], FIndices^[FCurrentIndex + 1], T.FCurrentIndex * Sizeof(Integer)); FCurrentIndex := K; end; {----------------TokenObj.Create} constructor TokenObj.Create; begin inherited; GetMem(C, TokenLeng * Sizeof(WideChar)); GetMem(I, TokenLeng*Sizeof(integer)); MaxIndex := TokenLeng; Leng := 0; St := ''; StringOK := True; end; destructor TokenObj.Destroy; begin FreeMem(I); FreeMem(C); inherited; end; procedure TokenObj.AddUnicodeChar(Ch: WideChar; Ind: integer); {Ch must be Unicode in this method} begin if Leng >= MaxIndex then begin ReallocMem(C, (MaxIndex + 50) * Sizeof(WideChar)); ReallocMem(I, (MaxIndex+50)*Sizeof(integer)); Inc(MaxIndex, 50); end; Inc(Leng); C^[Leng] := Ch; I^[Leng] := Ind; StringOK := False; end; procedure TokenObj.Clear; begin Leng := 0; St := ''; StringOK := True; end; function MultibyteToWideString(CodePage: integer; const S: string): WideString; var NewLen, Len: integer; begin Len := Length(S); {$ifdef Delphi6_Plus} if IsWin95 and (CodePage = CP_UTF8) then begin {Provide initial space. The resulting string will never be longer than the UTF-8 encoded string.} SetLength(Result, Len+1); {add 1 for #0 terminator} NewLen := UTF8ToUnicode(PWideChar(Result), Len+1, PChar(S), Len) - 1; {subtr 1 as don't want to count null terminator} end else {$endif} begin {Provide initial space. The resulting string will never be longer than the UTF-8 or multibyte encoded string.} SetLength(Result, 2 * Len); NewLen := MultiByteToWideChar(CodePage, 0, PChar(S), Len, PWideChar(Result), Len); if NewLen = 0 then { Invalid code page. Try default.} NewLen := MultiByteToWideChar(CP_ACP, 0, PChar(S), Len, PWideChar(Result), Len); end; SetLength(Result, NewLen); end; function WideStringToMultibyte(CodePage: integer; W: WideString): string; var NewLen, Len: integer; begin {$ifdef Delphi6_Plus} if CodePage = CP_UTF8 then {UTF-8 encoded string.} Result := UTF8Encode(W) else {$endif} begin Len := Length(W); SetLength(Result, 3*Len); NewLen := WideCharToMultiByte(CodePage, 0, PWideChar(W), Len, PChar(Result), 3*Len, Nil, Nil); if NewLen = 0 then { Invalid code page. Try default.} NewLen := WideCharToMultiByte(CP_ACP, 0, PWideChar(W), Len, PChar(Result), 3*Len, Nil, Nil); SetLength(Result, NewLen); end; end; function ByteNum(CodePage: integer; P: PChar): integer; var P1: PChar; begin if CodePage <> CP_UTF8 then begin P1 := CharNextEx(CodePage, P, 0); if Assigned(P1) then Result := P1 - P else Result := 0; end else case ord(P^) of {UTF-8} 0: Result := 0; 1..127: Result := 1; 192..223: Result := 2; 224..239: Result := 3; 240..247: Result := 4; else Result := 1; {error} end; end; procedure TokenObj.AddString(S: TCharCollection; CodePage: Integer); // Takes the given string S and converts it to Unicode using the given code page. // If we are on Windows 95 then CP_UTF8 (and CP_UTF7) are not supported. // We compensate for this by using a Delphi function. // Note: There are more code pages (including CP_UTF7), which are not supported // on all platforms. These are rather esoteric and therefore not considered here. var WS: WideString; I, J, N, Len, NewLen: Integer; begin Len := S.FCurrentIndex; {$ifdef Delphi6_Plus} if IsWin95 and (CodePage = CP_UTF8) then begin {Provide initial space. The resulting string will never be longer than the UTF-8 encoded string.} SetLength(WS, Len+1); {add 1 for #0 terminator} NewLen := UTF8ToUnicode(PWideChar(WS), Len+1, PChar(S.FChars), Len) - 1; {subtr 1 as don't want to count null terminator} end else {$endif} begin {Provide initial space. The resulting string will never be longer than the UTF-8 or multibyte encoded string.} SetLength(WS, 2 * Len); NewLen := MultiByteToWideChar(CodePage, 0, PChar(S.FChars), Len, PWideChar(WS), Len); if NewLen = 0 then { Invalid code page. Try default.} NewLen := MultiByteToWideChar(CP_ACP, 0, PChar(S.FChars), Len, PWideChar(WS), Len); end; {Store the wide string and character indices.} if Len = NewLen then {single byte character set or at least no multibyte conversion} for I := 1 to NewLen do AddUnicodeChar(WS[I], S.FIndices[I]) else begin {multibyte character set} J := 1; for I := 1 to NewLen do begin AddUnicodeChar(WS[I], S.FIndices[J]); {find index for start of next character} N := ByteNum(CodePage, @S.FChars[J]); if N > 0 then J := J + N else Break; end; end; end; procedure TokenObj.Concat(T: TokenObj); var K: integer; begin K := Leng + T.Leng; if K > MaxIndex then begin ReallocMem(C, (K + 50) * Sizeof(WideChar)); ReallocMem(I, (K+50)*Sizeof(integer)); MaxIndex := K+50; end; Move(T.C^, C^[Leng + 1], T.Leng * Sizeof(WideChar)); Move(T.I^, I^[Leng+1], T.Leng*Sizeof(integer)); Leng := K; StringOK := False; end; procedure TokenObj.Remove(N: integer); begin {remove a single character} if N <= Leng then begin Move(C^[N + 1], C^[N], (Leng - N) * Sizeof(WideChar)); Move(I^[N+1], I^[N], (Leng-N)*Sizeof(integer)); if StringOK then Delete(St, N, 1); Dec(Leng); end; end; procedure TokenObj.Replace(N: integer; Ch: WideChar); begin {replace a single character} if N <= Leng then begin C^[N] := Ch; if StringOK then St[N] := Ch; end; end; function TokenObj.GetString: WideString; begin if not StringOK then begin SetLength(St, Leng); Move(C^, St[1], SizeOf(WideChar) * Leng); StringOK := True; end; Result := St; end; {----------------BitmapToRegion} function BitmapToRegion(ABmp: TBitmap; XForm: PXForm; TransparentColor: TColor): HRGN; {Find a Region corresponding to the non-transparent area of a bitmap. Thanks to Felipe Machado. See http://www.delphi3000.com/ Minor modifications made.} const AllocUnit = 100; type PRectArray = ^TRectArray; TRectArray = Array[0..(MaxInt div SizeOf(TRect))-1] of TRect; var pr: PRectArray; // used to access the rects array of RgnData by index h: HRGN; // Handles to regions RgnData: PRgnData; // Pointer to structure RGNDATA used to create regions lr, lg, lb: Byte; // values for lowest and hightest trans. colors x,y, x0: Integer; // coordinates of current rect of visible pixels b: PByteArray; // used to easy the task of testing the byte pixels (R,G,B) ScanLinePtr: Pointer; // Pointer to current ScanLine being scanned ScanLineInc: Integer; // Offset to next bitmap scanline (can be negative) maxRects: Cardinal; // Number of rects to realloc memory by chunks of AllocUnit bmp: TBitmap; begin Result := 0; lr := GetRValue(TransparentColor); lg := GetGValue(TransparentColor); lb := GetBValue(TransparentColor); { ensures that the pixel format is 32-bits per pixel } bmp := TBitmap.Create; try bmp.Assign(ABmp); bmp.PixelFormat := pf32bit; { alloc initial region data } maxRects := AllocUnit; GetMem(RgnData,SizeOf(TRgnDataHeader) + (SizeOf(TRect) * maxRects)); FillChar(RgnData^, SizeOf(TRgnDataHeader) + (SizeOf(TRect) * maxRects), 0); try with RgnData^.rdh do begin dwSize := SizeOf(TRgnDataHeader); iType := RDH_RECTANGLES; nCount := 0; nRgnSize := 0; SetRect(rcBound, MAXLONG, MAXLONG, 0, 0); end; { scan each bitmap row - the orientation doesn't matter (Bottom-up or not) } {$IFNDEF LCL} //For now ScanLinePtr := bmp.ScanLine[0]; if bmp.Height > 1 then ScanLineInc := Integer(bmp.ScanLine[1]) - Integer(ScanLinePtr) else ScanLineInc := 0; {$ENDIF} for y := 0 to bmp.Height - 1 do begin x := 0; while x < bmp.Width do begin x0 := x; while x < bmp.Width do begin b := @PByteArray(ScanLinePtr)[x*SizeOf(TRGBQuad)]; // BGR-RGB: Windows 32bpp BMPs are made of BGRa quads (not RGBa) if (b[2] = lr) and (b[1] = lg) and (b[0] = lb) then Break; // pixel is transparent Inc(x); end; { test to see if we have a non-transparent area in the image } if x > x0 then begin { increase RgnData by AllocUnit rects if we exceeds maxRects } if RgnData^.rdh.nCount >= maxRects then begin Inc(maxRects,AllocUnit); ReallocMem(RgnData,SizeOf(TRgnDataHeader) + (SizeOf(TRect) * MaxRects)); pr := @RgnData^.Buffer; FillChar(pr^[maxRects-AllocUnit], AllocUnit*SizeOf(TRect), 0); end; { Add the rect (x0, y)-(x, y+1) as a new visible area in the region } pr := @RgnData^.Buffer; // Buffer is an array of rects with RgnData^.rdh do begin SetRect(pr[nCount], x0, y, x, y+1); { adjust the bound rectangle of the region if we are "out-of-bounds" } if x0 < rcBound.Left then rcBound.Left := x0; if y < rcBound.Top then rcBound.Top := y; if x > rcBound.Right then rcBound.Right := x; if y+1 > rcBound.Bottom then rcBound.Bottom := y+1; Inc(nCount); end; end; // if x > x0 { Need to create the region by muliple calls to ExtCreateRegion, 'cause } { it will fail on Windows 98 if the number of rectangles is too large } if RgnData^.rdh.nCount = 2000 then begin h := ExtCreateRegion(XForm, SizeOf(TRgnDataHeader) + (SizeOf(TRect) * maxRects), RgnData^); if Result > 0 then begin // Expand the current region CombineRgn(Result, Result, h, RGN_OR); DeleteObject(h); end else // First region, assign it to Result Result := h; RgnData^.rdh.nCount := 0; SetRect(RgnData^.rdh.rcBound, MAXLONG, MAXLONG, 0, 0); end; Inc(x); end; // scan every sample byte of the image {$IFNDEF FPC} Inc(Integer(ScanLinePtr), ScanLineInc); {$ELSE} Inc(ScanLinePtr, ScanLineInc); // LCL port: removed cast for 64-bits. {$ENDIF} end; { need to call ExCreateRegion one more time because we could have left } { a RgnData with less than 2000 rects, so it wasn't yet created/combined } if RgnData^.rdh.nCount > 0 then {LDB 0 Count causes exception and abort in Win98} h := ExtCreateRegion(XForm, SizeOf(TRgnDataHeader) + (SizeOf(TRect) * MaxRects), RgnData^) else h := 0; if Result > 0 then begin CombineRgn(Result, Result, h, RGN_OR); DeleteObject(h); end else Result := h; finally FreeMem(RgnData,SizeOf(TRgnDataHeader) + (SizeOf(TRect) * MaxRects)); end; finally bmp.Free; end; end; {----------------EnlargeImage} function EnlargeImage(Image: TGpObject; W, H: integer): TBitmap; {enlarge 1 pixel images for tiling. Returns a TBitmap regardless of Image type} var NewBitmap: TBitmap; begin Result := TBitmap.Create; if Image is TGpBitmap then NewBitmap := TGpBitmap(Image).GetTBitmap else NewBitmap := TBitmap(Image); Result.Assign(NewBitmap); if NewBitmap.Width = 1 then Result.Width := IntMin(100, W) else Result.Width := NewBitmap.Width; if NewBitmap.Height = 1 then Result.Height := IntMin(100, H) else Result.Height := NewBitmap.Height; Result.Canvas.StretchDraw(Rect(0,0,Result.Width, Result.Height), NewBitmap); if Image is TGpImage then NewBitmap.Free; end; {----------------PrintBitmap} procedure PrintBitmap(Canvas: TCanvas; X, Y, W, H: integer; BMHandle: HBitmap); {Y relative to top of display here} var OldPal: HPalette; DC: HDC; Info: PBitmapInfo; Image: AllocRec; ImageSize: DWord; InfoSize: DWord; begin if BMHandle = 0 then Exit; DC := Canvas.Handle; try {$IFNDEF LCL} GetDIBSizes(BMHandle, InfoSize, ImageSize); {$ENDIF} GetMem(Info, InfoSize); try Image := Allocate(ImageSize); OldPal := SelectPalette(DC, ThePalette, False); try {$IFNDEF LCL} GetDIB(BMHandle, ThePalette, Info^, Image.Ptr^); {$ENDIF} RealizePalette(DC); with Info^.bmiHeader do StretchDIBits(DC, X, Y, W, H, 0, 0, biWidth, biHeight, Image.Ptr, Info^, DIB_RGB_COLORS, SRCCOPY); finally DeAllocate(Image); SelectPalette(DC, OldPal, False); end; finally FreeMem(Info, InfoSize); end; except end; end; {----------------PrintBitmap1} procedure PrintBitmap1(Canvas: TCanvas; X, Y, W, H, YI, HI: integer; BMHandle: HBitmap); {Y relative to top of display here} var OldPal: HPalette; DC: HDC; Info: PBitmapInfo; Image: AllocRec; ImageSize: DWord; InfoSize: DWord; begin if BMHandle = 0 then Exit; DC := Canvas.Handle; try {$IFNDEF LCL} GetDIBSizes(BMHandle, InfoSize, ImageSize); {$ENDIF} GetMem(Info, InfoSize); try Image := Allocate(ImageSize); OldPal := SelectPalette(DC, ThePalette, False); try {$IFNDEF LCL} GetDIB(BMHandle, ThePalette, Info^, Image.Ptr^); {$ENDIF} RealizePalette(DC); with Info^.bmiHeader do StretchDIBits(DC, X, Y, biWidth, HI, 0, YI, biWidth, HI, Image.Ptr, Info^, DIB_RGB_COLORS, SRCCOPY); finally DeAllocate(Image); SelectPalette(DC, OldPal, False); end; finally FreeMem(Info, InfoSize); end; except end; end; {----------------PrintTransparentBitmap1} procedure PrintTransparentBitmap1(Canvas: TCanvas; X, Y, NewW, NewH: integer; Bitmap, Mask: TBitmap; YI, HI: integer); {Y relative to top of display here} {This routine prints transparently but only on a white background} {X, Y are point where upper left corner will be printed. NewW, NewH are the Width and Height of the output (possibly stretched) Vertically only a portion of the Bitmap, Mask may be printed starting at Y=YI in the bitmap and a height of HI } var OldPal: HPalette; DC: HDC; Info: PBitmapInfo; Image: AllocRec; ImageSize: DWord; InfoSize: DWord; Abitmap: TBitmap; begin ABitmap := TBitmap.Create; try Abitmap.Assign(Bitmap); ABitmap.Height := HI; SetBkColor(ABitmap.Canvas.Handle, clWhite); SetTextColor(ABitmap.Canvas.Handle, clBlack); BitBlt(ABitmap.Canvas.Handle, 0, 0, Bitmap.Width, HI, Bitmap.Canvas.Handle, 0, YI, SrcCopy); BitBlt(ABitmap.Canvas.Handle, 0, 0, Bitmap.Width, HI, Mask.Canvas.Handle, 0, YI, SRCPAINT); DC := Canvas.Handle; {$IFNDEF LCL} GetDIBSizes(ABitmap.Handle, InfoSize, ImageSize); {$ENDIF} GetMem(Info, InfoSize); try Image := Allocate(ImageSize); OldPal := SelectPalette(DC, ThePalette, False); try {$IFNDEF LCL} GetDIB(ABitmap.Handle, ThePalette, Info^, Image.Ptr^); {$ENDIF} RealizePalette(DC); with Info^.bmiHeader do StretchDIBits(DC, X, Y, NewW, NewH, 0, 0, biWidth, biHeight, Image.Ptr, Info^, DIB_RGB_COLORS, SRCCOPY); finally DeAllocate(Image); SelectPalette(DC, OldPal, False); end; finally FreeMem(Info, InfoSize); end; finally ABitmap.Free; end; end; {----------------PrintTransparentBitmap3} procedure PrintTransparentBitmap3(Canvas: TCanvas; X, Y, NewW, NewH: integer; Bitmap, Mask: TBitmap; YI, HI: integer); {Y relative to top of display here} {This routine prints transparently on complex background by printing through a clip region} {X, Y are point where upper left corner will be printed. NewW, NewH are the Width and Height of the output (possibly stretched) Vertically only a portion of the Bitmap, Mask may be printed starting at Y=YI in the bitmap and a height of HI } var OldPal: HPalette; DC: HDC; Info: PBitmapInfo; Image: AllocRec; ImageSize: DWord; InfoSize: DWord; hRgn, OldRgn: THandle; Rslt: integer; XForm: TXForm; SizeV, SizeW: TSize; HF, VF: double; ABitmap, AMask: TBitmap; BitmapCopy: boolean; begin {the following converts the black masked area in the image to white. This may look better in WPTools which currently doesn't handle the masking} if (Bitmap.Handle = 0) or (HI <= 0) or (Bitmap.Width <= 0) then Exit; BitmapCopy := Bitmap.Height <> HI; try if BitmapCopy then begin ABitmap := TBitmap.Create; AMask := TBitmap.Create; end else begin ABitmap := Bitmap; AMask := Mask; end; try if BitmapCopy then begin Abitmap.Assign(Bitmap); ABitmap.Height := HI; BitBlt(ABitmap.Canvas.Handle, 0, 0, Bitmap.Width, HI, Bitmap.Canvas.Handle, 0, YI, SrcCopy); AMask.Assign(Mask); AMask.Height := HI; BitBlt(AMask.Canvas.Handle, 0, 0, AMask.Width, HI, Mask.Canvas.Handle, 0, YI, SrcCopy); end; SetBkColor(ABitmap.Canvas.Handle, clWhite); SetTextColor(ABitmap.Canvas.Handle, clBlack); BitBlt(ABitmap.Canvas.Handle, 0, 0, Bitmap.Width, HI, AMask.Canvas.Handle, 0, 0, SRCPAINT); DC := Canvas.Handle; {calculate a transform for the clip region as it may be a different size than the mask and needs to be positioned on the canvas.} GetViewportExtEx(DC, SizeV); GetWindowExtEx(DC, SizeW); HF := (SizeV.cx/SizeW.cx); {Horizontal adjustment factor} VF := (SizeV.cy/SizeW.cy); {Vertical adjustment factor} XForm.eM11 := HF * (NewW/Bitmap.Width); XForm.eM12 := 0; XForm.eM21 := 0; XForm.eM22 := VF * (NewH/HI); XForm.edx := HF*X; XForm.edy := VF*Y; {Find the region for the white area of the Mask} hRgn := BitmapToRegion(AMask, @XForm, $FFFFFF); if hRgn <> 0 then {else nothing to output--this would be unusual} begin OldRgn := CreateRectRgn(0,0,1,1); {a valid region is needed for the next call} Rslt := GetClipRgn(DC, OldRgn); {save the Old clip region} try if Rslt = 1 then CombineRgn(hRgn, hRgn, OldRgn, RGN_AND); SelectClipRgn(DC, hRgn); {$IFNDEF LCL} GetDIBSizes(ABitmap.Handle, InfoSize, ImageSize); {$ENDIF} GetMem(Info, InfoSize); try Image := Allocate(ImageSize); OldPal := SelectPalette(DC, ThePalette, True); try {$IFNDEF LCL} GetDIB(ABitmap.Handle, ThePalette, Info^, Image.Ptr^); {$ENDIF} RealizePalette(DC); with Info^.bmiHeader do StretchDIBits(DC, X, Y, NewW, NewH, 0, 0, biWidth, biHeight, Image.Ptr, Info^, DIB_RGB_COLORS, SRCCOPY); finally DeAllocate(Image); SelectPalette(DC, OldPal, False); end; finally FreeMem(Info, InfoSize); end; finally if Rslt = 1 then SelectClipRgn(DC, OldRgn) else SelectClipRgn(DC, 0); DeleteObject(hRgn); DeleteObject(OldRgn); end; end; finally if BitmapCopy then begin ABitmap.Free; AMask.Free; end; end; except end; end; type BorderPointArray = array[0..3] of TPoint; function htStyles(P0, P1, P2, P3: BorderStyleType): htBorderStyleArray; begin Result[0] := P0; Result[1] := P1; Result[2] := P2; Result[3] := P3; end; procedure DrawGpImage(Handle: THandle; Image: TGpImage; DestX, DestY: integer); {Draws the entire image as specified at the point specified} var g: TGpGraphics; begin g := TGPGraphics.Create(Handle); try g.DrawImage(Image, DestX, DestY, Image.Width, Image.Height); except end; g.Free; end; procedure DrawGpImage(Handle: THandle; Image: TGpImage; DestX, DestY, SrcX, SrcY, SrcW, SrcH: integer); {Draw a portion of the image at DestX, DestY. No stretching} var g: TGpGraphics; begin g := TGPGraphics.Create(Handle); try g.DrawImage(Image, DestX, DestY, SrcX, SrcY, SrcW, SrcH); except end; g.Free; end; procedure StretchDrawGpImage(Handle: THandle; Image: TGpImage; DestX, DestY, DestW, DestH: integer); {Draws the entire image in the rectangle specified} var g: TGpGraphics; begin g := TGPGraphics.Create(Handle); try g.DrawImage(Image, DestX, DestY, DestW, DestH); except end; g.Free; end; procedure StretchPrintGpImageDirect(Handle: THandle; Image: TGpImage; DestX, DestY, DestW, DestH: integer; ScaleX, ScaleY: single); {Prints the entire image at the point specified with the height and width specified} var g: TGpGraphics; begin g := TGPGraphics.Create(Handle); try g.ScaleTransform(ScaleX, ScaleY); g.DrawImage(Image, DestX, DestY, DestW, DestH); except end; g.Free; end; procedure StretchPrintGpImageOnColor(Canvas: TCanvas; Image: TGpImage; DestX, DestY, DestW, DestH: integer; Color: TColor = clWhite); var g: TGpGraphics; bg: TBitmap; begin {Draw image on white background first, then print} bg := TBitmap.Create; bg.Width := TGPImage(Image).Width; bg.Height := TGPImage(Image).Height; bg.Canvas.Brush.Color := Color; bg.Canvas.FillRect(Rect(0, 0, bg.Width, bg.Height)); g := TGPGraphics.Create(bg.Canvas.Handle); g.DrawImage(TGPImage(Image),0,0, bg.Width, bg.Height); g.Free; Canvas.StretchDraw(Rect(DestX, DestY, DestX+DestW, DestY+DestH), bg); bg.Free; end; procedure PrintGpImageDirect(Handle: THandle; Image: TGpImage; DestX, DestY: integer; ScaleX, ScaleY: single); {Prints the entire image as specified at the point specified} var g: TGpGraphics; begin g := TGPGraphics.Create(Handle); try g.ScaleTransform(ScaleX, ScaleY); g.DrawImage(Image, DestX, DestY, Image.Width, Image.Height); except end; g.Free; end; function Points(P0, P1, P2, P3: TPoint): BorderPointArray; begin Result[0] := P0; Result[1] := P1; Result[2] := P2; Result[3] := P3; end; function htColors(C0, C1, C2, C3: TColor): htColorArray; begin Result[0] := C0; Result[1] := C1; Result[2] := C2; Result[3] := C3; end; procedure DrawOnePolygon(Canvas: TCanvas; P: BorderPointArray; Color: TColor; Side: integer; Printing: boolean); {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} 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)); var R: HRgn; OldWidth: integer; OldStyle: TPenStyle; OldColor: TColor; Thickness: integer; P1, P2: TPoint; I: SideArray; begin 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 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; C: htColorArray; S: htBorderStyleArray; BGround: TColor; Print: boolean); {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: BorderPointArray; I: integer; Color: TColor; MRect: TRect; lb: TLogBrush; Pn, OldPn: HPen; W, D: array[0..3] of integer; InPath: boolean; PenType, Start: integer; StyleSet: set of BorderStyleType; OuterRegion, InnerRegion: THandle; Brush: TBrush; function Darker(Color: TColor): TColor; {find a somewhat darker color for shading purposes} const F = 0.75; var Red, Green, Blue: Byte; begin if Color and $80000000 = $80000000 then Color := GetSysColor(Color and $FFFFFF) else Color := Color and $FFFFFF; Red := Color and $FF; Green := (Color and $FF00) shr 8; Blue := (Color and $FF0000) shr 16; Result := RGB(Round(F*Red), Round(F*Green), Round(F*Blue)); end; begin {Limit the borders to somewhat more than the screen size} ORect.Bottom := IntMin(ORect.Bottom, BotLim); ORect.Top := IntMax(ORect.Top, TopLim); IRect.Bottom := IntMin(IRect.Bottom, BotLim); IRect.Top := IntMax(IRect.Top, TopLim); {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; {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; {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 := 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 if S[I] in [bssSolid, bssInset, bssOutset] then begin Bnd[0] := PO[I]; Bnd[1] := PO[(I+1) Mod 4]; Bnd[2] := PI[(I+1) Mod 4]; Bnd[3] := PI[I]; Color := C[I] or PalRelative; case S[I] of bssSolid: DrawOnePolygon(Canvas, Bnd, Color, I, Print); bssInset: begin if I in [0,1] then Color := Darker(C[I]) or PalRelative; DrawOnePolygon(Canvas, Bnd, Color, I, Print); end; bssOutset: begin if (I in [2,3]) then Color := Darker(C[I]) or PalRelative; DrawOnePolygon(Canvas, Bnd, Color, I, Print); end; end; end else if S[I] in [bssRidge, bssGroove] then begin {ridge or groove} Color := C[I] or PalRelative; 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: begin if I in [0,1] then Color := Darker(C[I]) or PalRelative; DrawOnePolygon(Canvas, Bnd, Color, I, Print); end; bssRidge: begin if (I in [2,3]) then Color := Darker(C[I]) or PalRelative; DrawOnePolygon(Canvas, Bnd, Color, I, Print); end; end; Color := C[I] or PalRelative; 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: begin if I in [0,1] then Color := Darker(C[I]) or PalRelative; DrawOnePolygon(Canvas, Bnd, Color, I, Print); end; bssGroove: begin if (I in [2,3]) then Color := Darker(C[I]) or PalRelative; DrawOnePolygon(Canvas, Bnd, Color, I, Print); end; end; end else if S[I] = bssDouble then begin Color := C[I] 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 else if S[I] in [bssDashed, bssDotted] then begin if not InPath then begin lb.lbStyle := BS_SOLID; lb.lbColor := C[I] 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); {$IFNDEF LCL} Windows.movetoEx(Canvas.Handle, PM[I].x, PM[I].y, Nil); {$ELSE} LclIntf.movetoEx(Canvas.Handle, PM[I].x, PM[I].y, Nil); {$ENDIF} Start := I; InPath := True; end; {$IFNDEF LCL} Windows.LineTo(Canvas.Handle, PM[(I+1) mod 4].x, PM[(I+1) mod 4].y); {$ELSE} LclIntf.LineTo(Canvas.Handle, PM[(I+1) mod 4].x, PM[(I+1) mod 4].y); {$ENDIF} 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; finally if Pn <> 0 then begin SelectObject(Canvas.Handle, OldPn); DeleteObject(Pn); end; end; end; { TgpObject } function GetImageHeight(Image: TGpObject): integer; begin if Image is TBitmap then Result := TBitmap(Image).Height else if Image is TGpImage then Result := TGpImage(Image).Height else if Image is TGifImage then Result := TGifImage(Image).Height {$ifndef NoMetafile} else if Image is ThtMetaFile then Result := ThtMetaFile(Image).Height {$endif} else Raise(EGDIPlus.Create('Not a TBitmap, TGifImage, TMetafile, or TGpImage')); end; function GetImageWidth(Image: TGpObject): integer; begin if Image is TBitmap then Result := TBitmap(Image).Width else if Image is TGpImage then Result := TGpImage(Image).Width else if Image is TGifImage then Result := TGifImage(Image).Width {$ifndef NoMetafile} else if Image is ThtMetaFile then Result := ThtMetaFile(Image).Width {$endif} else Raise(EGDIPlus.Create('Not a TBitmap, TGifImage, TMetafile, or TGpImage')); end; initialization DC := GetDC(0); try ColorBits := GetDeviceCaps(DC, BitsPixel)*GetDeviceCaps(DC, Planes); if ColorBits <= 4 then ColorBits := 4 else if ColorBits <= 8 then ColorBits := 8 else ColorBits := 24; ThePalette := 0; if ColorBits = 8 then CalcPalette(DC); if ColorBits <= 8 then {use Palette Relative bit only when Palettes used} PalRelative := $2000000 else PalRelative := 0; finally ReleaseDC(0, DC); end; IsWin95 := (Win32Platform = VER_PLATFORM_WIN32_WINDOWS) and (Win32MinorVersion in [0..9]); IsWin32Platform := Win32Platform = VER_PLATFORM_WIN32_WINDOWS; {$ifdef UseElPack} UnicodeControls := True; {$endif} {$ifdef UseTNT} UnicodeControls := not IsWin32Platform; {$endif} {$IFDEF LCL} {$I htmlun2.lrs} {$ENDIF} DefBitMap := TBitmap.Create; ErrorBitMap := TBitmap.Create; ErrorBitMapMask := TBitmap.Create; {$IFNDEF LCL} DefBitMap.Handle := LoadBitmap(HInstance, MakeIntResource(DefaultBitmap)); ErrorBitMap.Handle := LoadBitmap(HInstance, MakeIntResource(ErrBitmap)); ErrorBitMapMask.Handle := LoadBitmap(HInstance, MakeIntResource(ErrBitmapMask)); Screen.Cursors[HandCursor] := LoadCursor(HInstance, MakeIntResource(Hand_Cursor)); Screen.Cursors[UpDownCursor] := LoadCursor(HInstance, 'UPDOWNCURSOR'); Screen.Cursors[UpOnlyCursor] := LoadCursor(HInstance, 'UPONLYCURSOR'); Screen.Cursors[DownOnlyCursor] := LoadCursor(HInstance, 'DOWNONLYCURSOR'); {$ELSE} DefBitMap.LoadFromLazarusResource('DefaultBitmap'); ErrorBitMap.LoadFromLazarusResource('ErrBitmap'); ErrorBitMapMask.LoadFromLazarusResource('ErrBitmapMask'); //Don't need since equal to crHandPoint (and jumps around on Carbon). //Screen.Cursors[HandCursor] := LoadCursorFromLazarusResource('Hand_Cursor'); Screen.Cursors[UpDownCursor] := LoadCursorFromLazarusResource('UPDOWNCURSOR'); Screen.Cursors[UpOnlyCursor] := LoadCursorFromLazarusResource('UPONLYCURSOR'); Screen.Cursors[DownOnlyCursor] := LoadCursorFromLazarusResource('DOWNONLYCURSOR'); {$ENDIF} WaitStream := TMemoryStream.Create; Finalization ThisExit; end.