
git-svn-id: https://svn.code.sf.net/p/lazarus-ccr/svn@1418 8e941d3f-bd1b-0410-a28a-d453659cc2b4
4345 lines
110 KiB
ObjectPascal
Executable File
4345 lines
110 KiB
ObjectPascal
Executable File
{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.
|
|
|
|
|
|
|