
git-svn-id: https://svn.code.sf.net/p/lazarus-ccr/svn@4378 8e941d3f-bd1b-0410-a28a-d453659cc2b4
2730 lines
77 KiB
ObjectPascal
2730 lines
77 KiB
ObjectPascal
{Version 9.45}
|
|
{*********************************************************}
|
|
{* STYLEUN.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 StyleUn;
|
|
|
|
interface
|
|
|
|
uses
|
|
SysUtils, Classes,
|
|
{$IFNDEF LCL}
|
|
Windows, Messages,
|
|
{$ELSE}
|
|
LclIntf, LMessages, Types, LclType, HtmlMisc,
|
|
{$ENDIF}
|
|
Graphics, Controls, Forms, Dialogs;
|
|
|
|
const
|
|
IntNull = -12345678;
|
|
Auto = -12348765;
|
|
AutoParagraph = -12348766;
|
|
ParagraphSpace = 14; {default spacing between paragraphs, etc.}
|
|
{$ifdef Delphi6_Plus}
|
|
varInt = [varInteger, varByte, varSmallInt, varShortInt, varWord, varLongWord];
|
|
{$else}
|
|
varInt = [varInteger];
|
|
{$endif}
|
|
EastEurope8859_2 = 31; {for 8859-2}
|
|
CRLF = #$D#$A;
|
|
|
|
type
|
|
AlignmentType = (ANone, ATop, AMiddle, ABaseline, ABottom, ALeft, ARight, AJustify, ASub, ASuper);
|
|
BorderStyleType = (bssNone, bssSolid, bssInset, bssOutset, bssGroove, bssRidge,
|
|
bssDashed, bssDotted, bssDouble);
|
|
ListBulletType = (lbBlank, lbCircle, lbDecimal, lbDisc, lbLowerAlpha, lbLowerRoman,
|
|
lbNone, lbSquare, lbUpperAlpha, lbUpperRoman);
|
|
ClearAttrType = (clrNone, clLeft, clRight, clAll);
|
|
PositionType = (posStatic, posAbsolute, posRelative);
|
|
VisibilityType = (viInherit, viHidden, viVisible);
|
|
TextTransformType = (txNone, txUpper, txLower, txCaps);
|
|
PositionRec = record
|
|
PosType: (pTop, pCenter, pBottom, pLeft, pRight, PPercent, pDim);
|
|
Value: integer;
|
|
RepeatD: boolean;
|
|
Fixed: boolean;
|
|
end;
|
|
PtPositionRec = array[1..2] of PositionRec;
|
|
|
|
{$ifdef Ver90}
|
|
TFontCharSet = integer; {dummy for Delphi 2}
|
|
{$endif}
|
|
|
|
ThtFontInfo = class
|
|
iName: string;
|
|
iSize: double;
|
|
iStyle: TFontStyles;
|
|
iColor: TColor;
|
|
ibgColor: TColor;
|
|
iCharSet: TFontCharSet;
|
|
iCharExtra: Variant;
|
|
end;
|
|
|
|
FIIndex = (LFont, VFont, HLFont, HVFont);
|
|
TFontInfoArray = class
|
|
Ar: array[LFont..HVFont] of ThtFontInfo;
|
|
constructor Create;
|
|
destructor Destroy; override;
|
|
procedure Assign(Source: TFontInfoArray);
|
|
end;
|
|
|
|
SetOfChar = Set of Char;
|
|
|
|
TMyFont = class(TFont)
|
|
public
|
|
bgColor: TColor;
|
|
tmHeight, tmDescent, tmExternalLeading, tmAveCharWidth,
|
|
tmMaxCharWidth, tmCharset: integer;
|
|
CharExtra: integer;
|
|
procedure Assign(Source: TPersistent); override;
|
|
procedure AssignToCanvas(Canvas: TCanvas);
|
|
destructor Destroy; override;
|
|
constructor Create;
|
|
end;
|
|
|
|
PropIndices = (
|
|
FontFamily, FontSize, FontStyle, FontWeight, TextAlign, TextDecoration,
|
|
LetterSpacing, BorderStyle, Color, BackgroundColor, BorderColor,
|
|
MarginTop, MarginRight, MarginBottom, MarginLeft,
|
|
PaddingTop, PaddingRight, PaddingBottom, PaddingLeft,
|
|
BorderTopWidth, BorderRightWidth, BorderBottomWidth, BorderLeftWidth,
|
|
BorderTopColor, BorderRightColor, BorderBottomColor, BorderLeftColor,
|
|
BorderTopStyle, BorderRightStyle, BorderBottomStyle, BorderLeftStyle,
|
|
Width, Height, TopPos, BottomPos, RightPos, LeftPos, Visibility,
|
|
LineHeight, BackgroundImage, BackgroundPosition,
|
|
BackgroundRepeat, BackgroundAttachment, VerticalAlign, Position, ZIndex,
|
|
ListStyleType, ListStyleImage, Float, Clear, TextIndent,
|
|
PageBreakBefore, PageBreakAfter, PageBreakInside, TextTransform,
|
|
WordWrap, FontVariant, BorderCollapse, OverFlow, Display);
|
|
|
|
TVMarginArray = array[BackgroundColor..LeftPos] of Variant;
|
|
TMarginArray = array[BackgroundColor..LeftPos] of integer;
|
|
|
|
TStyleList = class;
|
|
|
|
TProperties = class(TObject)
|
|
private
|
|
TheFont: TMyFont;
|
|
InLink: boolean;
|
|
DefFontname: string;
|
|
procedure AddPropertyByIndex(Index: PropIndices; PropValue: string);
|
|
procedure GetSingleFontInfo(var Font: ThtFontInfo);
|
|
procedure CalcLinkFontInfo(Styles: TStyleList; I: integer);
|
|
procedure CombineX(Styles: TStyleList;
|
|
const Tag, AClass, AnID, PSeudo, ATitle: string; AProp: TProperties);
|
|
public
|
|
PropTag, PropClass, PropID, PropPseudo, PropTitle: string;
|
|
PropStyle: TProperties;
|
|
FontBG: TColor;
|
|
CharSet: TFontCharSet;
|
|
CodePage: integer;
|
|
EmSize, ExSize: integer; {# pixels for Em and Ex dimensions}
|
|
Props: array[Low(PropIndices)..High(PropIndices)] of Variant;
|
|
Originals: array[Low(PropIndices)..High(PropIndices)] of boolean;
|
|
FIArray: TFontInfoArray;
|
|
ID: integer;
|
|
|
|
constructor Create;
|
|
destructor Destroy; override;
|
|
procedure Copy(Source: TProperties);
|
|
procedure CopyDefault(Source: TProperties);
|
|
procedure Inherit(Tag: string; Source: TProperties);
|
|
procedure Assign(const Item: Variant; Index: PropIndices);
|
|
procedure AssignCharSet(CS: TFontCharset);
|
|
procedure AssignUTF8;
|
|
procedure Combine(Styles: TStyleList;
|
|
const Tag, AClass, AnID, Pseudo, ATitle: string; AProp: TProperties);
|
|
procedure Update(Source: TProperties; Styles: TStyleList; I: integer);
|
|
function GetFont: TMyFont;
|
|
procedure GetFontInfo(AFI: TFontInfoArray);
|
|
procedure GetVMarginArray(var MArray: TVMarginArray);
|
|
function GetBackgroundImage(var Image: string): boolean;
|
|
procedure GetBackgroundPos(EmSize, ExSize: integer; var P: PtPositionRec);
|
|
function GetLineHeight(NewHeight:integer): integer;
|
|
function GetTextIndent(var PC: boolean): integer;
|
|
function GetTextTransform: TextTransformType;
|
|
function GetFontVariant: string;
|
|
procedure GetPageBreaks(var Before, After, Intact: boolean);
|
|
function GetVertAlign(var Align: AlignmentType): boolean;
|
|
function GetFloat(var Align: AlignmentType): boolean;
|
|
function GetClear(var Clr: ClearAttrType): boolean;
|
|
function GetOriginalForegroundColor: TColor;
|
|
function GetBackgroundColor: TColor;
|
|
function GetBorderStyle: BorderStyleType;
|
|
function BorderStyleNotBlank: boolean;
|
|
function GetListStyleType: ListBulletType;
|
|
function GetListStyleImage: string;
|
|
function GetPosition: PositionType;
|
|
function GetVisibility: VisibilityType;
|
|
function GetZIndex: integer;
|
|
function DisplayNone: boolean;
|
|
function Collapse: boolean;
|
|
procedure SetFontBG;
|
|
procedure AddPropertyByName(const PropName, PropValue: string);
|
|
function IsOverflowHidden: boolean;
|
|
end;
|
|
|
|
TStyleList = Class(TStringList)
|
|
private
|
|
MasterList: TObject;
|
|
SeqNo: integer;
|
|
public
|
|
DefProp: TProperties;
|
|
constructor Create(AMasterList: TObject);
|
|
destructor Destroy; override;
|
|
procedure Clear; override;
|
|
function GetSeqNo: string;
|
|
procedure Initialize(const FontName, PreFontName: string;
|
|
PointSize: integer; AColor, AHotspot, AVisitedColor, AActiveColor: TColor;
|
|
LinkUnderline: boolean; ACharSet: TFontCharSet; MarginHeight, MarginWidth: integer);
|
|
procedure AddModifyProp(const Selector, Prop, Value: string);
|
|
function AddObject(const S: string; AObject: TObject): Integer; override;
|
|
function AddDuplicate(const Tag: string; Prop: TProperties): TProperties;
|
|
procedure ModifyLinkColor(Psuedo: string; AColor: TColor);
|
|
{$ifdef Quirk}
|
|
procedure FixupTableColor(BodyProp: TProperties);
|
|
{$endif}
|
|
end;
|
|
|
|
const
|
|
PropWords: array[Low(PropIndices)..High(PropIndices)] of string =
|
|
('font-family', 'font-size', 'font-style', 'font-weight', 'text-align',
|
|
'text-decoration', 'letter-spacing', 'border-style', 'color', 'background-color',
|
|
'border-color',
|
|
'margin-top', 'margin-right', 'margin-bottom', 'margin-left',
|
|
'padding-top', 'padding-right','padding-bottom', 'padding-left',
|
|
'border-top-width', 'border-right-width','border-bottom-width', 'border-left-width',
|
|
'border-top-color', 'border-right-color','border-bottom-color', 'border-left-color',
|
|
'border-top-style', 'border-right-style','border-bottom-style', 'border-left-style',
|
|
'width', 'height', 'top', 'bottom', 'right', 'left', 'visibility',
|
|
'line-height', 'background-image', 'background-position',
|
|
'background-repeat', 'background-attachment', 'vertical-align', 'position', 'z-index',
|
|
'list-style-type', 'list-style-image', 'float', 'clear', 'text-indent',
|
|
'page-break-before', 'page-break-after', 'page-break-inside', 'text-transform',
|
|
'word-wrap', 'font-variant', 'border-collapse', 'overflow', 'display');
|
|
|
|
procedure ConvMargArray(const VM: TVMarginArray; BaseWidth, BaseHeight, EmSize,
|
|
ExSize: integer; BStyle: BorderStyleType; var AutoCount: integer;
|
|
var M: TMarginArray);
|
|
|
|
procedure ConvVertMargins(const VM: TVMarginArray;
|
|
BaseHeight, EmSize, ExSize: Integer;
|
|
var M: TMarginArray; var TopAuto, BottomAuto: boolean);
|
|
|
|
procedure ConvMargArrayForCellPadding(const VM: TVMarginArray; EmSize,
|
|
ExSize: Integer; var M: TMarginArray);
|
|
|
|
procedure ConvInlineMargArray(const VM: TVMarginArray; BaseWidth, BaseHeight, EmSize,
|
|
ExSize: Integer; {BStyle: BorderStyleType;} var M: TMarginArray);
|
|
|
|
function ColorFromString(S: string; NeedPound: boolean; var Color: TColor): boolean;
|
|
|
|
function ReadURL(Item: Variant): string;
|
|
|
|
function ReadFontName(S: string): string;
|
|
|
|
function AlignmentFromString(S: string): AlignmentType;
|
|
|
|
{$ifndef Ver130}
|
|
{$ifndef Delphi6_Plus}
|
|
procedure FreeAndNil(var Obj);
|
|
{$endif}
|
|
{$endif}
|
|
|
|
implementation
|
|
|
|
uses
|
|
{$ifdef Delphi6_Plus}
|
|
Variants,
|
|
{$endif}
|
|
htmlsubs, htmlun2, readhtml;
|
|
|
|
var
|
|
DefPointSize: double;
|
|
|
|
{$ifndef Ver130}
|
|
{$ifndef Delphi6_Plus}
|
|
procedure FreeAndNil(var Obj);
|
|
var
|
|
P: TObject;
|
|
begin
|
|
P := TObject(Obj);
|
|
TObject(Obj) := nil; {clear the reference before destroying the object}
|
|
P.Free;
|
|
end;
|
|
{$endif}
|
|
{$endif}
|
|
|
|
{----------------AlignmentFromString}
|
|
function AlignmentFromString(S: string): AlignmentType;
|
|
begin
|
|
S := LowerCase(S);
|
|
if S = 'top' then Result := ATop
|
|
else if (S = 'middle') or (S = 'absmiddle') or (S = 'center') then Result := AMiddle
|
|
else if S = 'left' then Result := ALeft
|
|
else if S = 'right' then Result := ARight
|
|
else if (S = 'bottom') then Result := ABottom
|
|
else if (S = 'baseline') then Result := ABaseline
|
|
else if (S = 'justify') then Result := AJustify
|
|
else Result := ANone;
|
|
end;
|
|
|
|
function FontSizeConv(const Str: string; OldSize: double): double; forward;
|
|
function LengthConv(const Str: string; Relative: boolean; Base, EmSize, ExSize,
|
|
Default: integer): integer; forward;
|
|
|
|
function FindPropIndex(const PropWord:string; var PropIndex: PropIndices): boolean;
|
|
var
|
|
I: PropIndices;
|
|
begin
|
|
Result := True;
|
|
for I := Low(PropIndices) to High(PropIndices)do
|
|
if PropWord = PropWords[I] then
|
|
begin
|
|
PropIndex := I;
|
|
Exit;
|
|
end;
|
|
Result := False;
|
|
end;
|
|
|
|
{----------------ReadURL}
|
|
function ReadURL(Item: Variant): string;
|
|
var
|
|
I: integer;
|
|
S: string;
|
|
begin
|
|
Result := '';
|
|
if VarType(Item) = VarString then
|
|
begin
|
|
S := Item;
|
|
I := Pos('url(', S);
|
|
if I > 0 then
|
|
begin
|
|
S := System.Copy(S, 5, Length(S));
|
|
I := Pos(')', S);
|
|
if I > 0 then
|
|
S := System.Copy(S, 1, I-1);
|
|
if Length(S) > 2 then
|
|
if S[1] in ['''', '"'] then
|
|
begin
|
|
Delete(S, Length(S), 1);
|
|
Delete(S, 1, 1);
|
|
end;
|
|
Result := S;
|
|
end;
|
|
end;
|
|
end;
|
|
|
|
{----------------TMyFont.Assign}
|
|
procedure TMyFont.Assign(Source: TPersistent);
|
|
begin
|
|
if Source is TMyFont then
|
|
begin
|
|
bgColor := TMyFont(Source).bgColor;
|
|
tmHeight := TMyFont(Source).tmHeight;
|
|
tmDescent := TMyFont(Source).tmDescent;
|
|
tmExternalLeading := TMyFont(Source).tmExternalLeading;
|
|
tmAveCharWidth := TMyFont(Source).tmAveCharWidth;
|
|
tmMaxCharWidth := TMyFont(Source).tmMaxCharWidth;
|
|
tmCharset := TMyFont(Source).tmCharset;
|
|
CharExtra := TMyFont(Source).CharExtra;
|
|
end;
|
|
inherited Assign(Source);
|
|
end;
|
|
|
|
procedure TMyFont.AssignToCanvas(Canvas: TCanvas);
|
|
begin
|
|
Canvas.Font := Self;
|
|
SetTextCharacterExtra(Canvas.Handle, CharExtra);
|
|
end;
|
|
|
|
destructor TMyFont.destroy;
|
|
begin
|
|
inherited;
|
|
end;
|
|
|
|
constructor TmyFont.Create;
|
|
begin
|
|
inherited;
|
|
end;
|
|
|
|
var
|
|
Sequence: integer;
|
|
|
|
{----------------TProperties.Create}
|
|
constructor TProperties.Create;
|
|
var
|
|
I: PropIndices;
|
|
begin
|
|
inherited Create;
|
|
ID := Sequence;
|
|
Inc(Sequence);
|
|
FontBG := clNone;
|
|
for I := MarginTop to LeftPos do
|
|
Props[I] := IntNull;
|
|
Props[ZIndex] := 0;
|
|
end;
|
|
|
|
destructor TProperties.Destroy;
|
|
begin
|
|
TheFont.Free;
|
|
FIArray.Free;
|
|
inherited;
|
|
end;
|
|
|
|
{----------------TProperties.Copy}
|
|
procedure TProperties.Copy(Source: TProperties);
|
|
var
|
|
I: PropIndices;
|
|
begin
|
|
for I := Low(I) to High(I) do
|
|
Props[I] := Source.Props[I];
|
|
end;
|
|
|
|
{----------------TProperties.CopyDefault}
|
|
procedure TProperties.CopyDefault(Source: TProperties);
|
|
var
|
|
I: PropIndices;
|
|
begin
|
|
for I := Low(I) to High(I) do
|
|
Props[I] := Source.Props[I];
|
|
AssignCharSet(Source.CharSet);
|
|
DefFontname := Source.DefFontname;
|
|
PropTag := 'default';
|
|
end;
|
|
|
|
procedure TProperties.Inherit(Tag: string; Source: TProperties);
|
|
{copy the properties that are inheritable}
|
|
var
|
|
I: PropIndices;
|
|
Span, HBF: boolean;
|
|
begin
|
|
Span := Source.PropTag = 'span';
|
|
HBF := (Source.PropTag = 'thead') or (Source.PropTag = 'tbody')
|
|
or (Source.PropTag = 'tfoot');
|
|
for I := Low(I) to High(I) do
|
|
if Span and (I <> BorderStyle) then {Borderstyle already acted on}
|
|
Props[I] := Source.Props[I]
|
|
else if HBF then
|
|
begin
|
|
Props[I] := Source.Props[I]; {tr gets them all}
|
|
Originals[I] := Source.Originals[I];
|
|
end
|
|
else if (I = WordWrap) and (Tag = 'table') then {table doesn't inherit word wrap}
|
|
Props[WordWrap] := 'normal'
|
|
else
|
|
case I of
|
|
MarginTop..LeftPos:
|
|
Props[I] := IntNull;
|
|
BackgroundColor, BorderColor, BorderStyle,
|
|
Clear, Float, BackgroundImage, BackgroundPosition, BackgroundRepeat, BackgroundAttachment,
|
|
Position, PageBreakBefore, PageBreakAfter, PageBreakInside, BorderCollapse,
|
|
OverFlow, Display:
|
|
; {do nothing}
|
|
else
|
|
Props[I] := Source.Props[I];
|
|
end;
|
|
DefFontname := Source.DefFontname;
|
|
FontBG := Source.FontBG;
|
|
CharSet := Source.CharSet;
|
|
CodePage := Source.CodePage;
|
|
PropTitle := Source.PropTitle;
|
|
InLink := Source.InLink;
|
|
if InLink then
|
|
begin
|
|
if not Assigned(FIArray) then
|
|
FIArray := TFontInfoArray.Create;
|
|
FIArray.Assign(Source.FIArray);
|
|
end;
|
|
|
|
EmSize := Source.EmSize; {actually this is calculated later }
|
|
ExSize := EmSize div 2; {apparently correlates with what browsers are doing}
|
|
end;
|
|
|
|
{----------------TProperties.Update}
|
|
procedure TProperties.Update(Source: TProperties; Styles: TStyleList; I: integer);
|
|
{Change the inherited properties for this item to those of Source}
|
|
var
|
|
Index: PropIndices;
|
|
begin
|
|
for Index := Low(Index) to High(Index) do
|
|
if not Originals[Index] then
|
|
Props[Index] := Source.Props[Index];
|
|
TheFont.Free; {may no longer be good}
|
|
TheFont := Nil;
|
|
if Assigned(FIArray) then
|
|
if Source.Inlink then
|
|
FIArray.Assign(Source.FIArray)
|
|
else if PropPseudo = 'link' then {an <a href> tag}
|
|
CalcLinkFontInfo(Styles, I)
|
|
else
|
|
begin {an <a href> tag has been removed}
|
|
FIArray.Free;
|
|
FIArray := Nil;
|
|
Inlink := False;
|
|
end;
|
|
end;
|
|
|
|
{----------------TProperties.Assign}
|
|
procedure TProperties.Assign(const Item: Variant; Index: PropIndices);
|
|
{Assignment should be made in order of importance as the first one in
|
|
predominates}
|
|
var
|
|
I: FIIndex;
|
|
begin
|
|
if not Originals[Index] then
|
|
begin
|
|
Props[Index] := Item;
|
|
Originals[Index] := True;
|
|
if InLink then
|
|
case Index of
|
|
Color:
|
|
for I := LFont to HVFont do
|
|
FIArray.Ar[I].iColor := Item;
|
|
FontSize:
|
|
for I := LFont to HVFont do
|
|
FIArray.Ar[I].iSize := Item;
|
|
FontFamily:
|
|
for I := LFont to HVFont do
|
|
FIArray.Ar[I].iName := Item;
|
|
end;
|
|
end;
|
|
end;
|
|
|
|
function TProperties.GetBackgroundImage(var Image: string): boolean;
|
|
begin
|
|
if (VarType(Props[BackgroundImage]) = VarString) then
|
|
if (Props[BackgroundImage] = 'none') then
|
|
begin
|
|
Image := '';
|
|
Result := True;
|
|
end
|
|
else
|
|
begin
|
|
Image := ReadUrl(Props[BackgroundImage]);
|
|
Result := Image <> '';
|
|
end
|
|
else Result := False;
|
|
end;
|
|
|
|
procedure TProperties.AssignCharSet(CS: TFontCharset);
|
|
const
|
|
{EastEurope8859_2 = 31; }
|
|
SetValues: array[1..20] of integer =
|
|
(ANSI_CHARSET, DEFAULT_CHARSET, SYMBOL_CHARSET, MAC_CHARSET, SHIFTJIS_CHARSET,
|
|
HANGEUL_CHARSET, JOHAB_CHARSET, GB2312_CHARSET, CHINESEBIG5_CHARSET,
|
|
GREEK_CHARSET, TURKISH_CHARSET, VIETNAMESE_CHARSET, HEBREW_CHARSET,
|
|
ARABIC_CHARSET, BALTIC_CHARSET, RUSSIAN_CHARSET, THAI_CHARSET,
|
|
EASTEUROPE_CHARSET, OEM_CHARSET, EastEurope8859_2);
|
|
{ SetValues: array[1..19] of integer =
|
|
(0, 1, 2, 77, 128, 129, 130, 134, 136, 161, 162, 163, 177, 178,
|
|
186, 204, 222, 238, 255); }
|
|
CodePages: array[1..20] of integer =
|
|
(1252, CP_ACP, 0, CP_MACCP, 932, 949, 1361, 936, 950, 1253, 1254, 1258, 1255, 1256, 1257, 1251,
|
|
874, 1250, CP_OEMCP, 28592); {28592 for 8859-2, east european}
|
|
var
|
|
I: integer;
|
|
Save: THandle;
|
|
tm : TTextmetric;
|
|
DC: HDC;
|
|
Font: TFont;
|
|
IX: FIIndex;
|
|
begin
|
|
if CS = EastEurope8859_2 then
|
|
begin
|
|
CharSet := EASTEUROPE_CHARSET;
|
|
CodePage := 28592;
|
|
if Assigned(FIArray) then
|
|
for IX := LFont to HVFont do
|
|
FIArray.Ar[IX].iCharset := CharSet;
|
|
Exit;
|
|
end;
|
|
{the following makes sure the CharSet is available. It also translates
|
|
"Default_CharSet" into the actual local character set}
|
|
Font := TFont.Create;
|
|
Font.Name := '';
|
|
Font.CharSet := CS;
|
|
DC := GetDC(0);
|
|
try
|
|
Save := SelectObject(DC, Font.Handle);
|
|
GetTextMetrics(DC, tm);
|
|
if CS <> Default_Charset then {leave default as is}
|
|
CharSet := tm.tmCharSet
|
|
else CharSet := Default_CharSet;
|
|
if Assigned(FIArray) then
|
|
for IX := LFont to HVFont do
|
|
FIArray.Ar[IX].iCharset := CharSet;
|
|
SelectObject(DC, Save);
|
|
Finally
|
|
ReleaseDC(0, DC);
|
|
Font.Free;
|
|
end;
|
|
|
|
for I := 1 to 19 do
|
|
if SetValues[I] = tm.tmCharSet then
|
|
begin
|
|
CodePage := CodePages[I];
|
|
break;
|
|
end;
|
|
end;
|
|
|
|
procedure TProperties.AssignUTF8;
|
|
{Called by DoMeta in Readhtml.pas to make the properties using UTF-8 for conversions.}
|
|
begin
|
|
CodePage := CP_UTF8;
|
|
Charset := ANSI_CHARSET;
|
|
end;
|
|
|
|
{----------------TProperties.GetBackgroundPos}
|
|
procedure TProperties.GetBackgroundPos(EmSize, ExSize: integer; var P: PtPositionRec);
|
|
var
|
|
S: array[1..2] of string;
|
|
Tmp: string;
|
|
I, N, XY: integer;
|
|
begin
|
|
if (VarType(Props[BackgroundPosition]) <> VarString) then
|
|
begin
|
|
P[1].PosType := pDim;
|
|
P[1].Value := 0;
|
|
P[2] := P[1];
|
|
end
|
|
else
|
|
begin
|
|
Tmp := Trim(Props[BackgroundPosition]);
|
|
N := Pos(' ', Tmp);
|
|
if N > 0 then
|
|
begin
|
|
S[1] := System.Copy(Tmp, 1, N-1);
|
|
S[2] := Trim(system.Copy(Tmp, N+1, 255));
|
|
N := 2;
|
|
end
|
|
else
|
|
begin
|
|
S[1] := Tmp;
|
|
N := 1;
|
|
end;
|
|
I := 1;
|
|
XY := 1; {X}
|
|
while I <= N do
|
|
begin
|
|
P[XY].PosType := pDim;
|
|
if S[I] = 'center' then
|
|
P[XY].PosType := pCenter
|
|
else if Pos('%', S[I]) > 0 then
|
|
P[XY].PosType := pPercent
|
|
else if S[I] = 'left' then
|
|
begin
|
|
if XY = 2 then {entered in reverse direction}
|
|
P[2] := P[1];
|
|
P[1].PosType := pLeft;
|
|
end
|
|
else if S[I] = 'right' then
|
|
begin
|
|
if XY = 2 then
|
|
P[2] := P[1];
|
|
P[1].PosType := pRight;
|
|
end
|
|
else if S[I] = 'top' then
|
|
begin
|
|
P[2].PosType := pTop;
|
|
if XY = 1 then
|
|
Dec(XY); {read next one into X}
|
|
end
|
|
else if S[I] = 'bottom' then
|
|
begin
|
|
P[2].PosType := pBottom;
|
|
if XY = 1 then
|
|
Dec(XY);
|
|
end;
|
|
if P[XY].PosType in [pDim, pPercent] then
|
|
begin
|
|
P[XY].Value := LengthConv(S[I], False, 100, EmSize, ExSize, 0);
|
|
end;
|
|
Inc(I);
|
|
Inc(XY);
|
|
end;
|
|
if N = 1 then
|
|
if XY = 2 then
|
|
P[2].PosType := pCenter
|
|
else P[1].PosType := pCenter; {single entry but it was a Y}
|
|
end;
|
|
P[1].RepeatD := True;
|
|
P[2].RepeatD := True;
|
|
if (VarType(Props[BackgroundRepeat]) = VarString) then
|
|
begin
|
|
Tmp := Trim(Props[BackgroundRepeat]);
|
|
if Tmp = 'no-repeat' then
|
|
begin
|
|
P[1].RepeatD := False;
|
|
P[2].RepeatD := False;
|
|
end
|
|
else if Tmp = 'repeat-x' then
|
|
P[2].RepeatD := False
|
|
else if Tmp = 'repeat-y' then
|
|
P[1].RepeatD := False;
|
|
end;
|
|
P[1].Fixed := False;
|
|
if (VarType(Props[BackgroundAttachment]) = VarString) and
|
|
(Trim(Props[BackgroundAttachment]) = 'fixed') then
|
|
P[1].Fixed := True;
|
|
P[2].Fixed := P[1].Fixed;
|
|
end;
|
|
|
|
function TProperties.GetVertAlign(var Align: AlignmentType): boolean;
|
|
{note: 'top' should have a catagory of its own}
|
|
var
|
|
S: string;
|
|
begin
|
|
if (VarType(Props[VerticalAlign]) = VarString) then
|
|
begin
|
|
Result := True;
|
|
S := Props[VerticalAlign];
|
|
if (S = 'top') or (S = 'text-top') then Align := ATop
|
|
else if S = 'middle' then Align := AMiddle
|
|
else if S = 'baseline' then Align := ABaseline
|
|
else if (S = 'bottom') then Align := ABottom
|
|
else if (S = 'sub') then Align := ASub
|
|
else if (S = 'super') then Align := ASuper
|
|
else Result := False;
|
|
end
|
|
else Result := False;
|
|
end;
|
|
|
|
function TProperties.IsOverflowHidden: boolean;
|
|
begin
|
|
Result := (VarType(Props[OverFlow]) = VarString) and (Props[OverFlow] = 'hidden');
|
|
end;
|
|
|
|
function TProperties.GetFloat(var Align: AlignmentType): boolean;
|
|
var
|
|
S: string;
|
|
begin
|
|
if (VarType(Props[Float]) = VarString) then
|
|
begin
|
|
Result := True;
|
|
S := Props[Float];
|
|
if (S = 'left') then Align := ALeft
|
|
else if S = 'right' then Align := ARight
|
|
else if S = 'none' then Align := ANone
|
|
else Result := False;
|
|
end
|
|
else Result := False;
|
|
end;
|
|
|
|
function TProperties.GetClear(var Clr: ClearAttrType): boolean;
|
|
var
|
|
S: string;
|
|
begin
|
|
if (VarType(Props[Clear]) = VarString) then
|
|
begin
|
|
Result := True;
|
|
S := Props[Clear];
|
|
if (S = 'left') then Clr := clLeft
|
|
else if S = 'right' then Clr := clRight
|
|
else if S = 'both' then Clr := clAll
|
|
else if S = 'none' then Clr := clrNone
|
|
else Result := False;
|
|
Props[Clear] := Unassigned; {allow only one read}
|
|
end
|
|
else Result := False;
|
|
end;
|
|
|
|
function TProperties.GetListStyleType: ListBulletType;
|
|
const
|
|
S: array[Low(ListBulletType)..High(ListBulletType)] of string =
|
|
('blank', 'circle', 'decimal', 'disc', 'lower-alpha', 'lower-roman',
|
|
'none', 'square', 'upper-alpha', 'upper-roman');
|
|
var
|
|
I: ListBulletType;
|
|
|
|
begin
|
|
if VarType(Props[ListStyleType]) = VarString then
|
|
for I := Low(ListBulletType) to High(ListBulletType) do
|
|
if S[I] = Props[ListStyleType] then
|
|
begin
|
|
Result := I;
|
|
Exit;
|
|
end;
|
|
Result := lbBlank;
|
|
end;
|
|
|
|
function TProperties.GetListStyleImage: string;
|
|
begin
|
|
Result := ReadURL(Props[ListStyleImage])
|
|
end;
|
|
|
|
function TProperties.GetPosition: PositionType;
|
|
begin
|
|
Result := posStatic;
|
|
if VarType(Props[Position]) = VarString then
|
|
begin
|
|
if Props[Position] = 'absolute' then
|
|
Result := posAbsolute
|
|
else if Props[Position] = 'relative' then
|
|
Result := posRelative;
|
|
end;
|
|
end;
|
|
|
|
function TProperties.GetVisibility: VisibilityType;
|
|
begin
|
|
Result := viVisible;
|
|
if VarType(Props[Visibility]) in varInt then
|
|
if Props[Visibility] = viHidden then
|
|
Result := viHidden;
|
|
end;
|
|
|
|
function TProperties.GetZIndex: integer;
|
|
begin
|
|
Result := 0;
|
|
if VarType(Props[ZIndex]) in VarInt then
|
|
Result := Props[ZIndex]
|
|
else if VarType(Props[ZIndex]) = VarString then
|
|
Result := StrToIntDef(Props[ZIndex], 0);
|
|
end;
|
|
|
|
function TProperties.DisplayNone: boolean;
|
|
begin
|
|
Result := (VarType(Props[Display]) = VarString) and (Props[Display] = 'none');
|
|
end;
|
|
|
|
function TProperties.Collapse: boolean;
|
|
begin
|
|
Result := (VarType(Props[BorderCollapse]) = VarString) and (Props[BorderCollapse] = 'collapse');
|
|
end;
|
|
|
|
function TProperties.GetLineHeight(NewHeight:integer): integer;
|
|
var
|
|
V: double;
|
|
Code: integer;
|
|
begin
|
|
if VarType(Props[LineHeight]) = varString then
|
|
begin
|
|
Val(Props[LineHeight], V, Code);
|
|
if Code = 0 then {a numerical entry with no 'em', '%', etc. Use the new font height}
|
|
Result := Round(V*NewHeight)
|
|
else
|
|
{note: 'normal' yields -1 in the next statement}
|
|
Result := LengthConv(Props[LineHeight], True, EmSize, EmSize, ExSize, -1);
|
|
end
|
|
else Result := -1;
|
|
end;
|
|
|
|
function TProperties.GetTextIndent(var PC: boolean): integer;
|
|
var
|
|
I: integer;
|
|
begin
|
|
PC := False;
|
|
if VarType(Props[TextIndent]) = varString then
|
|
begin
|
|
I := Pos('%', Props[TextIndent]);
|
|
if I > 0 then
|
|
begin
|
|
PC := True; {return value in percent}
|
|
Result := LengthConv(Props[TextIndent], True, 100, 0, 0, 0);
|
|
end
|
|
else
|
|
Result := LengthConv(Props[TextIndent], False, 0, EmSize, EmSize, 0);
|
|
end
|
|
else Result := 0;
|
|
end;
|
|
|
|
function TProperties.GetTextTransform: TextTransformType;
|
|
begin
|
|
try
|
|
if VarType(Props[TextTransform]) in VarInt then
|
|
Result := Props[TextTransform]
|
|
else Result := txNone;
|
|
except
|
|
Result := txNone;
|
|
end;
|
|
end;
|
|
|
|
function TProperties.GetFontVariant: string;
|
|
begin
|
|
try
|
|
if VarType(Props[FontVariant]) = varString then
|
|
Result := Props[FontVariant]
|
|
else Result := 'normal';
|
|
except
|
|
Result := 'normal';
|
|
end;
|
|
end;
|
|
|
|
procedure TProperties.GetPageBreaks(var Before, After, Intact: boolean);
|
|
begin
|
|
Before := (VarType(Props[PageBreakBefore]) = varString) and (Props[PageBreakBefore] = 'always');
|
|
After := (VarType(Props[PageBreakAfter]) = varString) and (Props[PageBreakAfter] = 'always');
|
|
Intact := (VarType(Props[PageBreakInside]) = varString) and (Props[PageBreakInside] = 'avoid');
|
|
end;
|
|
|
|
function TProperties.GetBackgroundColor: TColor;
|
|
begin
|
|
if (VarType(Props[BackgroundColor]) in varInt) and Originals[BackgroundColor] then
|
|
{Originals to prevent fonts from getting inherited background color}
|
|
Result := Props[BackgroundColor]
|
|
else Result := clNone;
|
|
end;
|
|
|
|
function TProperties.GetOriginalForegroundColor: TColor;
|
|
begin {return a color only if it hasn't been inherited}
|
|
if (VarType(Props[Color]) in varInt) and Originals[Color] then
|
|
Result := Props[Color]
|
|
else Result := clNone;
|
|
end;
|
|
|
|
function BorderStyleFromString(const S: string): BorderStyleType;
|
|
const
|
|
Ar: array[1..9] of string = ('none', 'solid', 'inset', 'outset','groove', 'ridge',
|
|
'dashed', 'dotted', 'double');
|
|
Ar1: array[1..9] of BorderStyleType = (bssNone, bssSolid, bssInset, bssOutset, bssGroove, bssRidge,
|
|
bssDashed, bssDotted, bssDouble);
|
|
var
|
|
I: integer;
|
|
begin
|
|
Result := bssNone;
|
|
for I := 1 to 9 do
|
|
if S = Ar[I] then
|
|
begin
|
|
Result := Ar1[I];
|
|
break;
|
|
end;
|
|
end;
|
|
|
|
function TProperties.GetBorderStyle: BorderStyleType;
|
|
begin
|
|
Result := bssNone;
|
|
if VarType(Props[BorderStyle]) = VarString then
|
|
Result := BorderStyleFromString(Props[BorderStyle]);
|
|
end;
|
|
|
|
function TProperties.BorderStyleNotBlank: boolean;
|
|
{was a border of some type (including bssNone) requested?}
|
|
begin
|
|
Result := VarType(Props[BorderStyle]) = VarString;
|
|
end;
|
|
|
|
procedure TProperties.SetFontBG;
|
|
{called for font tags like <b>, <small>, etc. Sets the font background color.}
|
|
begin
|
|
if (VarType(Props[BackgroundColor]) in varInt) and Originals[BackgroundColor] then
|
|
FontBG := Props[BackgroundColor];
|
|
end;
|
|
|
|
procedure ConvVertMargins(const VM: TVMarginArray;
|
|
BaseHeight, EmSize, ExSize: Integer;
|
|
var M: TMarginArray; var TopAuto, BottomAuto: boolean);
|
|
|
|
function Convert(V: Variant; var IsAutoParagraph: boolean): integer;
|
|
begin
|
|
IsAutoParagraph := False;
|
|
if VarType(V) = VarString then
|
|
Result := LengthConv(V, False, BaseHeight, EmSize, ExSize, 0) {Auto will be 0}
|
|
else if VarType(V) in varInt then
|
|
begin
|
|
if V = IntNull then
|
|
Result := 0
|
|
else if V = AutoParagraph then
|
|
begin
|
|
Result := ParagraphSpace;
|
|
IsAutoParagraph := True;
|
|
end
|
|
else Result := V;
|
|
end
|
|
else Result := 0;
|
|
end;
|
|
|
|
begin
|
|
M[MarginTop] := Convert(VM[MarginTop], TopAuto);
|
|
M[MarginBottom] := Convert(VM[MarginBottom], BottomAuto);
|
|
end;
|
|
|
|
{----------------ConvMargArray}
|
|
procedure ConvMargArray(const VM: TVMarginArray; BaseWidth, BaseHeight, EmSize,
|
|
ExSize: Integer; BStyle: BorderStyleType; var AutoCount: integer;
|
|
var M: TMarginArray);
|
|
{This routine does not do MarginTop and MarginBottom as they are done by ConvVertMargins}
|
|
var
|
|
I: PropIndices;
|
|
Base: integer;
|
|
begin
|
|
AutoCount := 0; {count of 'auto's in width items}
|
|
for I := Low(VM) to High(VM) do
|
|
begin
|
|
case I of
|
|
Height, TopPos:
|
|
Base := BaseHeight
|
|
else Base := BaseWidth;
|
|
end;
|
|
case I of
|
|
BackgroundColor, BorderColor:
|
|
begin
|
|
if VarType(VM[I]) <= VarNull then
|
|
M[I] := clNone
|
|
else M[I] := VM[I];
|
|
end;
|
|
BorderTopWidth..BorderLeftWidth:
|
|
begin
|
|
if VM[PropIndices(Ord(BorderTopStyle) + (Ord(I)-Ord(BorderTopWidth)))] = bssNone then
|
|
M[I] := 0
|
|
else
|
|
begin
|
|
if VarType(VM[I]) = VarString then
|
|
begin
|
|
if VM[I] = 'thin' then
|
|
M[I] := 2
|
|
else if VM[I] = 'medium' then
|
|
M[I] := 4
|
|
else if VM[I] = 'thick' then
|
|
M[I] := 6
|
|
else
|
|
M[I] := LengthConv(VM[I], False, BaseWidth, EmSize, ExSize, 4); {Auto will be 4}
|
|
end
|
|
else if (VarType(VM[I]) in varInt) then
|
|
begin
|
|
if (VM[I] = IntNull) then
|
|
M[I] := 4
|
|
else M[I] := VM[I];
|
|
end;
|
|
end;
|
|
end;
|
|
Height, PaddingTop..PaddingLeft:
|
|
begin
|
|
if VarType(VM[I]) = VarString then
|
|
begin
|
|
M[I] := LengthConv(VM[I], False, Base, EmSize, ExSize, 0); {Auto will be 0}
|
|
if (I = Height) and (Pos('%', VM[I]) > 0) then {include border in % heights}
|
|
M[I] := M[I] - M[BorderTopWidth] - M[BorderBottomWidth] - M[PaddingTop] - M[PaddingBottom];
|
|
end
|
|
else if VarType(VM[I]) in varInt then
|
|
begin
|
|
if VM[I] = IntNull then
|
|
M[I] := 0
|
|
else M[I] := VM[I];
|
|
end
|
|
else M[I] := 0;
|
|
end;
|
|
TopPos, RightPos, BottomPos, LeftPos:
|
|
begin
|
|
if VarType(VM[I]) = VarString then
|
|
M[I] := LengthConv(VM[I], False, Base, EmSize, ExSize, Auto) {Auto will be Auto}
|
|
else if VarType(VM[I]) in varInt then
|
|
begin
|
|
if VM[I] = IntNull then
|
|
M[I] := Auto
|
|
else M[I] := VM[I];
|
|
end
|
|
else M[I] := Auto;
|
|
end;
|
|
MarginLeft, MarginRight:
|
|
begin
|
|
if VarType(VM[I]) = VarString then
|
|
begin
|
|
if VM[I] = 'auto' then
|
|
begin
|
|
M[I] := Auto;
|
|
Inc(AutoCount);
|
|
end
|
|
else M[I] := LengthConv(VM[I], False, BaseWidth, EmSize, ExSize, 0);
|
|
end
|
|
else if VarType(VM[I]) in varInt then
|
|
begin
|
|
if VM[I] = IntNull then
|
|
M[I] := 0
|
|
else M[I] := VM[I];
|
|
end
|
|
else M[I] := 0;
|
|
end;
|
|
Width:
|
|
begin
|
|
if VarType(VM[I]) = VarString then
|
|
M[I] := LengthConv(VM[I], False, BaseWidth, EmSize, ExSize, Auto)
|
|
else if VarType(VM[I]) in varInt then
|
|
begin
|
|
if VM[I] = IntNull then
|
|
M[I] := Auto
|
|
else M[I] := VM[I];
|
|
end
|
|
else M[I] := Auto;
|
|
if M[I] = Auto then
|
|
Inc(AutoCount);
|
|
end;
|
|
MarginTop, MarginBottom: ; {do nothing}
|
|
else
|
|
begin
|
|
if VarType(VM[I]) = VarString then
|
|
M[I] := LengthConv(VM[I], False, BaseWidth, EmSize, ExSize, 0)
|
|
else if VarType(VM[I]) in varInt then
|
|
begin
|
|
if VM[I] = IntNull then
|
|
M[I] := 0
|
|
else M[I] := VM[I];
|
|
end
|
|
else M[I] := 0;
|
|
end;
|
|
end;
|
|
end;
|
|
end;
|
|
|
|
procedure ConvMargArrayForCellPadding(const VM: TVMarginArray; EmSize,
|
|
ExSize: Integer; var M: TMarginArray);
|
|
{Return negative for no entry or percent entry}
|
|
var
|
|
I: PropIndices;
|
|
begin
|
|
for I := PaddingTop to PaddingLeft do
|
|
if VarType(VM[I]) = VarString then
|
|
M[I] := LengthConv(VM[I], False, -100, EmSize, ExSize, 0) {Auto will be 0}
|
|
else if VarType(VM[I]) in varInt then
|
|
begin
|
|
if VM[I] = IntNull then
|
|
M[I] := -1
|
|
else M[I] := VM[I];
|
|
end
|
|
else M[I] := -1;
|
|
end;
|
|
|
|
{----------------ConvInlineMargArray}
|
|
procedure ConvInlineMargArray(const VM: TVMarginArray; BaseWidth, BaseHeight, EmSize,
|
|
ExSize: Integer; {BStyle: BorderStyleType;} var M: TMarginArray);
|
|
{currently for images, form controls. BaseWidth/Height and BStyle currently not supported}
|
|
var
|
|
I: PropIndices;
|
|
begin
|
|
for I := Low(VM) to High(VM) do
|
|
case I of
|
|
Height, Width:
|
|
begin
|
|
if VarType(VM[I]) = VarString then
|
|
M[I] := LengthConv(VM[I], False, BaseWidth, EmSize, ExSize, Auto) {Auto will be Auto}
|
|
else if VarType(VM[I]) in varInt then
|
|
begin
|
|
if VM[I] = IntNull then
|
|
M[I] := IntNull
|
|
else M[I] := VM[I];
|
|
end
|
|
else M[I] := IntNull;
|
|
end;
|
|
MarginLeft, MarginRight, MarginTop, MarginBottom:
|
|
begin
|
|
if VarType(VM[I]) = VarString then
|
|
M[I] := LengthConv(VM[I], False, BaseWidth, EmSize, ExSize, 0) {auto is 0}
|
|
else if VarType(VM[I]) in varInt then
|
|
begin
|
|
if VM[I] = IntNull then
|
|
M[I] := IntNull
|
|
else M[I] := VM[I];
|
|
end
|
|
else M[I] := IntNull;
|
|
end;
|
|
BorderTopWidth..BorderLeftWidth:
|
|
begin
|
|
if VM[PropIndices(Ord(BorderTopStyle) + (Ord(I)-Ord(BorderTopWidth)))] = bssNone then
|
|
M[I] := 0
|
|
else
|
|
begin
|
|
if VarType(VM[I]) = VarString then
|
|
begin
|
|
if VM[I] = 'thin' then
|
|
M[I] := 2
|
|
else if VM[I] = 'medium' then
|
|
M[I] := 4
|
|
else if VM[I] = 'thick' then
|
|
M[I] := 6
|
|
else
|
|
M[I] := LengthConv(VM[I], False, BaseWidth, EmSize, ExSize, 4); {Auto will be 4}
|
|
end
|
|
else if (VarType(VM[I]) in varInt) then
|
|
begin
|
|
if (VM[I] = IntNull) then
|
|
M[I] := 4
|
|
else M[I] := VM[I];
|
|
end;
|
|
end;
|
|
end;
|
|
else
|
|
; {remaining items unsupported/unused}
|
|
end;
|
|
end;
|
|
|
|
{----------------TProperties.Combine}
|
|
procedure TProperties.Combine(Styles: TStyleList;
|
|
const Tag, AClass, AnID, PSeudo, ATitle: string; AProp: TProperties);
|
|
{When called, this TProperties contains the inherited properties. Here we
|
|
add the ones relevant to this item. AProp are TProperties gleaned from the
|
|
Style= attribute. AClass may be a multiple class like class="ab.cd"}
|
|
var
|
|
BClass, S: string;
|
|
I: integer;
|
|
begin
|
|
BClass := Trim(AClass);
|
|
I := Pos('.', BClass);
|
|
if I <= 0 then
|
|
CombineX(Styles, Tag, BClass, AnID, PSeudo, '', AProp) {0 or 1 Class}
|
|
else
|
|
begin {more than one class}
|
|
repeat
|
|
S := System.Copy(BClass, 1, I-1);
|
|
CombineX(Styles, Tag, S, AnID, PSeudo, '', Nil);
|
|
Delete(BClass, 1, I);
|
|
BClass := Trim(BClass);
|
|
I := Pos('.', BClass);
|
|
until I <= 0;
|
|
CombineX(Styles, Tag, BClass, AnID, PSeudo, '', AProp);
|
|
CombineX(Styles, Tag, AClass, AnID, PSeudo, '', AProp);
|
|
end;
|
|
PropTag := Tag;
|
|
PropClass := AClass;
|
|
PropID := AnID;
|
|
PropPseudo := Pseudo;
|
|
PropStyle := AProp;
|
|
if ATitle <> '' then
|
|
PropTitle := ATitle;
|
|
if PSeudo = 'link' then
|
|
begin
|
|
if not Assigned(FIArray) then
|
|
FIArray := TFontInfoArray.Create;
|
|
CalcLinkFontInfo(Styles, PropStack.Count-1);
|
|
InLink := True;
|
|
end;
|
|
end;
|
|
|
|
{----------------TProperties.CombineX}
|
|
procedure TProperties.CombineX(Styles: TStyleList;
|
|
const Tag, AClass, AnID, PSeudo, ATitle: string; AProp: TProperties);
|
|
{When called, this TProperties contains the inherited properties. Here we
|
|
add the ones relevant to this item. AProp are TProperties gleaned from the
|
|
Style= attribute.}
|
|
var
|
|
OldSize: double;
|
|
IX: integer;
|
|
NoHoverVisited: boolean;
|
|
|
|
procedure Merge(Source: TProperties);
|
|
var
|
|
Index: PropIndices;
|
|
I: FIIndex;
|
|
Wt: integer;
|
|
S1: string;
|
|
begin
|
|
for Index := Low(Index) to High(PropIndices) do
|
|
if (VarType(Source.Props[Index]) <> varEmpty) and (Vartype(Source.Props[Index]) <> varNull) then
|
|
case Index of
|
|
MarginTop..LeftPos:
|
|
if VarType(Source.Props[Index]) = VarString then
|
|
begin
|
|
Props[Index] := Source.Props[Index];
|
|
Originals[Index] := True;
|
|
end
|
|
else if Source.Props[Index] <> IntNull then
|
|
begin
|
|
Props[Index] := Source.Props[Index];
|
|
Originals[Index] := True;
|
|
end;
|
|
FontFamily, FontSize, FontStyle, FontWeight, Color, BackgroundColor,
|
|
TextDecoration, LetterSpacing:
|
|
begin
|
|
Originals[Index] := True;
|
|
Props[Index] := Source.Props[Index];
|
|
if InLink then
|
|
for I := LFont to HVFont do
|
|
with FIArray.Ar[I] do
|
|
case Index of
|
|
FontFamily:
|
|
begin
|
|
S1 := ReadFontName(Props[FontFamily]);
|
|
if S1 <> '' then
|
|
iName := S1;
|
|
end;
|
|
FontSize:
|
|
iSize := FontSizeConv(Props[FontSize], iSize);
|
|
Color: iColor := Props[Color];
|
|
BackgroundColor: ibgColor := Props[BackgroundColor];
|
|
FontStyle:
|
|
if (Props[FontStyle] = 'italic') or (Props[FontStyle] = 'oblique') then
|
|
iStyle := iStyle + [fsItalic]
|
|
else if Props[FontStyle] = 'normal' then
|
|
iStyle := iStyle - [fsItalic];
|
|
FontWeight:
|
|
if Pos('bold', Props[FontWeight]) > 0 then
|
|
iStyle := iStyle + [fsBold]
|
|
else if Pos('normal', Props[FontWeight]) > 0 then
|
|
iStyle := iStyle - [fsBold]
|
|
else
|
|
begin
|
|
Wt := StrToIntDef(Props[FontWeight], 0);
|
|
if Wt >= 600 then
|
|
iStyle := iStyle + [fsBold];
|
|
end;
|
|
TextDecoration:
|
|
if Props[TextDecoration] = 'underline' then
|
|
iStyle := iStyle + [fsUnderline]
|
|
else if Props[TextDecoration] = 'line-through' then
|
|
iStyle := iStyle + [fsStrikeOut]
|
|
else if Props[TextDecoration] = 'none' then
|
|
iStyle := iStyle - [fsStrikeOut, fsUnderline];
|
|
LetterSpacing:
|
|
iCharExtra := Props[LetterSpacing];
|
|
end;
|
|
end
|
|
else
|
|
begin
|
|
Props[Index] := Source.Props[Index];
|
|
Originals[Index] := True; {it's defined for this item, not inherited}
|
|
end;
|
|
end;
|
|
end;
|
|
|
|
function CheckForContextual(I: integer): boolean;
|
|
{process contextual selectors}
|
|
var
|
|
J, K, N: integer;
|
|
A: array[1..10] of record
|
|
Tg, Cl, ID, PS: string;
|
|
gt: boolean;
|
|
end;
|
|
MustMatchParent: Boolean;
|
|
|
|
procedure Split(S: string);
|
|
var
|
|
I, J: integer;
|
|
begin
|
|
N := 1; {N is number of selectors in contextual string}
|
|
I := Pos(' ', S);
|
|
while (I > 0) and (N < 10) do
|
|
begin
|
|
A[N].Tg := System.Copy(S, 1, I-1);
|
|
Delete(S, 1, I);
|
|
S := Trim(S);
|
|
Inc(N);
|
|
I := Pos(' ', S);
|
|
end;
|
|
A[N].Tg := S;
|
|
if (N >= 2) and (Length(A[2].Tg) > 0) then
|
|
repeat
|
|
Delete(A[2].Tg, 1, 1); {remove the sort digit}
|
|
until (length(A[2].Tg)=0) or not (A[2].Tg[1] in ['0'..'9']);
|
|
for I := 1 to N do
|
|
begin
|
|
J := Pos('>', A[I].Tg);
|
|
if I > 1 then
|
|
A[I-1].gt := J > 0;
|
|
if J > 0 then
|
|
Delete(A[I].Tg, J, 1);
|
|
J := Pos(':', A[I].Tg);
|
|
if J > 0 then
|
|
begin
|
|
A[I].PS := System.Copy(A[I].Tg, J+1, Length(A[I].Tg));
|
|
A[I].Tg := System.Copy(A[I].Tg, 1, J-1);
|
|
end
|
|
else A[I].PS := '';
|
|
J := Pos('#', A[I].Tg);
|
|
if J > 0 then
|
|
begin
|
|
A[I].ID := System.Copy(A[I].Tg, J+1, Length(A[I].Tg));
|
|
A[I].Tg := System.Copy(A[I].Tg, 1, J-1);
|
|
end
|
|
else A[I].ID := '';
|
|
J := Pos('.', A[I].Tg);
|
|
if J > 0 then
|
|
begin
|
|
A[I].Cl := System.Copy(A[I].Tg, J+1, Length(A[I].Tg));
|
|
A[I].Tg := System.Copy(A[I].Tg, 1, J-1);
|
|
end
|
|
else A[I].Cl := '';
|
|
end;
|
|
end;
|
|
|
|
function PartOf(const S1, S2: string): boolean;
|
|
{see if all classes in S1 are present in S2. Classes are separated by '.'}
|
|
var
|
|
SL1, SL2: TStringList;
|
|
J, X: integer;
|
|
|
|
function FormStringList(S: string): TStringList;
|
|
{construct a TStringList from classes in string S}
|
|
var
|
|
I: integer;
|
|
begin
|
|
Result := TStringList.Create;
|
|
Result.Sorted := True;
|
|
I := Pos('.', S);
|
|
while I >= 1 do
|
|
begin
|
|
Result.Add(System.Copy(S, 1, I-1));
|
|
Delete(S, 1, I);
|
|
I := Pos('.', S);
|
|
end;
|
|
Result.Add(S);
|
|
end;
|
|
|
|
begin {PartOf}
|
|
SL1 := FormStringList(S1);
|
|
try
|
|
SL2 := FormStringList(S2);
|
|
try
|
|
Result := True; {assume all will be found}
|
|
for J := 0 to SL1.Count-1 do
|
|
If not SL2.Find(SL1[J], X) then
|
|
begin
|
|
Result := False; {one is missing, return False}
|
|
Break;
|
|
end;
|
|
finally
|
|
SL2.Free;
|
|
end;
|
|
finally
|
|
SL1.Free;
|
|
end;
|
|
end;
|
|
|
|
begin
|
|
Result := False;
|
|
Split(Styles[I]); //split contextual selectors into parts in array A
|
|
if (A[1].Tg <> Tag) and (A[1].Cl <> AClass) and (A[1].PS <> PSeudo) then
|
|
Exit
|
|
else Result := True;
|
|
if (N > 1) //it's a contextual selector. N is count of selectors
|
|
and ((A[1].Tg = Tag) or (A[1].Tg = ''))
|
|
and ((A[1].Cl = AClass) or (A[1].Cl = ''))
|
|
and ((A[1].ID = AnID) or (A[1].ID = ''))
|
|
and ((A[1].PS = PSeudo) or (A[1].PS = '') and (PSeudo = 'link')) then
|
|
begin //look thru the stack to see if this contextual selector is appropriate
|
|
K := 2; //K is selector index in the sequence
|
|
J := PropStack.Count-2; // start on stack item below this one
|
|
MustMatchParent := A[1].gt;
|
|
while (K <= N) and (J >= 1) do
|
|
begin
|
|
with PropStack[J] do
|
|
if ((A[K].Tg = PropTag) or (A[K].Tg = ''))
|
|
and ((A[K].Cl = PropClass) or (A[K].Cl = '') or PartOf(A[K].Cl, PropClass))
|
|
and ((A[K].ID = PropID) or (A[K].ID = ''))
|
|
and ((A[K].PS = PropPseudo) or (A[K].PS = '')) then
|
|
begin
|
|
if K = N then //all parts of contextual selector match
|
|
Merge(Styles.Objects[I] as TProperties);
|
|
MustMatchParent := A[K].gt;
|
|
Inc(K);
|
|
end
|
|
else if MustMatchParent then
|
|
Break; {Didn't match}
|
|
Dec(J);
|
|
end;
|
|
end
|
|
end;
|
|
|
|
procedure MergeItems(Const Item: string);
|
|
{look up items in the Style list. If found, merge them in this TProperties.
|
|
Items may be duplicated in which case the last has priority. Items may be
|
|
simple tags like 'p', 'blockquote', 'em', etc or they may be more complex
|
|
like p.class, em#id, a.class:link, etc}
|
|
var
|
|
X: integer;
|
|
begin
|
|
if Styles.Find(Item, X) then
|
|
begin
|
|
Merge(Styles.Objects[X] as TProperties);
|
|
Inc(X);
|
|
while (X < Styles.Count) and (Styles[X] = Item) do
|
|
begin //duplicates, last one has highest priority
|
|
Merge(Styles.Objects[X] as TProperties);
|
|
Inc(X);
|
|
end;
|
|
end;
|
|
end;
|
|
|
|
begin
|
|
{$ifdef Quirk}
|
|
if (Tag = 'td') or (Tag = 'th') then
|
|
OldSize := DefPointSize else
|
|
{$endif}
|
|
if (VarType(Props[FontSize]) = VarDouble) and (Props[FontSize] > 0.0) then {should be true}
|
|
OldSize := Props[FontSize]
|
|
else OldSize := DefPointSize;
|
|
|
|
{Some hover and visited items adequately taken care of when link processed}
|
|
NoHoverVisited := (Pseudo = '') or ((Pseudo <> 'hover') and (Pseudo <> 'visited'));
|
|
|
|
// in the following, lowest priority on top, highest towards bottom.
|
|
|
|
if (Tag = 'a') and (Pseudo <> '') then
|
|
MergeItems('::'+Pseudo); {default Pseudo definition}
|
|
|
|
if NoHoverVisited then
|
|
MergeItems(Tag);
|
|
|
|
if Pseudo <> '' then
|
|
MergeItems(':'+Pseudo);
|
|
|
|
if (AClass <> '') and NoHoverVisited then
|
|
MergeItems('.'+AClass);
|
|
|
|
if (AClass <> '') and NoHoverVisited then
|
|
MergeItems(Tag+'.'+AClass);
|
|
|
|
if Pseudo <> '' then
|
|
MergeItems(Tag+':'+Pseudo);
|
|
|
|
if (AClass <> '') and (PSeudo <> '') then
|
|
MergeItems('.'+AClass+':'+Pseudo);
|
|
|
|
if (AClass <> '') and (Pseudo <> '') then
|
|
MergeItems(Tag+'.'+AClass+':'+Pseudo);
|
|
|
|
if AnID <> '' then
|
|
begin
|
|
MergeItems('#'+AnID);
|
|
MergeItems(Tag+'#'+AnID);
|
|
if (AClass <> '') then
|
|
MergeItems('.'+AClass+'#'+AnID);
|
|
if (Pseudo <> '') then
|
|
begin
|
|
MergeItems('#'+AnID+':'+Pseudo);
|
|
MergeItems(Tag+'#'+AnID+':'+Pseudo);
|
|
end;
|
|
if (AClass <> '') then
|
|
MergeItems(Tag+'.'+AClass+'#'+AnID);
|
|
MergeItems('.'+AClass+'#'+AnID+':'+Pseudo);
|
|
MergeItems(Tag+'.'+AClass+'#'+AnID+':'+Pseudo);
|
|
end;
|
|
|
|
{process the entries in Styles to see if they are contextual selectors}
|
|
Styles.Find(Tag, IX); //place to start
|
|
while (IX < Styles.Count) and (Pos(Tag, Styles[IX]) = 1) and CheckForContextual(IX) do
|
|
Inc(IX);
|
|
|
|
Styles.Find('.'+AClass, IX); //place to start
|
|
while (IX < Styles.Count) and (Pos('.'+AClass, Styles[IX]) = 1) and CheckForContextual(IX) do
|
|
Inc(IX);
|
|
|
|
Styles.Find(':'+PSeudo, IX); //place to start
|
|
while (IX < Styles.Count) and (Pos(':'+PSeudo, Styles[IX]) = 1) and CheckForContextual(IX) do
|
|
Inc(IX);
|
|
|
|
If Assigned(AProp) then //the Style= attribute
|
|
Merge(AProp);
|
|
|
|
if not ((VarType(Props[FontSize]) = VarDouble) or
|
|
(VarType(Props[FontSize]) in varInt)) then {if still a string, hasn't been converted}
|
|
Props[FontSize] := FontSizeConv(Props[FontSize], OldSize);
|
|
end;
|
|
|
|
function TProperties.GetFont: TMyFont;
|
|
var
|
|
Font: ThtFontInfo;
|
|
Save: THandle;
|
|
SaveCharSet: TFontCharSet;
|
|
tm : TTextmetric;
|
|
DC: HDC;
|
|
V: Variant;
|
|
begin {call only if all things valid}
|
|
if not Assigned(TheFont) then
|
|
begin
|
|
Font := ThtFontInfo.Create;
|
|
try
|
|
GetSingleFontInfo(Font);
|
|
TheFont := TMyFont.Create;
|
|
with TheFont, Font do
|
|
begin
|
|
Name := iName;
|
|
Height := -Round(iSize * Screen.PixelsPerInch / 72);
|
|
Style := iStyle;
|
|
bgColor := ibgColor;
|
|
Color := iColor;
|
|
Charset := iCharSet;
|
|
V := iCharExtra;
|
|
end;
|
|
finally
|
|
Font.Free;
|
|
end;
|
|
{if this is a Symbol charset, then keep it that way. To check the font's real
|
|
charset, use Default_Charset}
|
|
SaveCharSet := TheFont.CharSet;
|
|
TheFont.CharSet := Default_Charset;
|
|
DC := GetDC(0);
|
|
try
|
|
Save := SelectObject(DC, TheFont.Handle);
|
|
try
|
|
GetTextMetrics(DC, tm);
|
|
finally
|
|
SelectObject(DC, Save);
|
|
end;
|
|
if tm.tmCharset = Symbol_Charset then
|
|
TheFont.Charset := Symbol_CharSet
|
|
else
|
|
TheFont.Charset := SaveCharSet;
|
|
{now get the info on the finalized font}
|
|
if TheFont.Charset <> Default_Charset then {else already have the textmetrics}
|
|
begin
|
|
Save := SelectObject(DC, TheFont.Handle);
|
|
try
|
|
GetTextMetrics(DC, tm);
|
|
finally
|
|
SelectObject(DC, Save);
|
|
end;
|
|
end;
|
|
finally
|
|
ReleaseDC(0, DC);
|
|
end;
|
|
{calculate EmSize with current font rather than inherited}
|
|
EmSize := tm.tmHeight-tm.tmInternalLeading;
|
|
ExSize := EmSize div 2; {apparently correlates with what browsers are doing}
|
|
TheFont.tmHeight := tm.tmHeight;
|
|
TheFont.tmDescent := tm.tmDescent;
|
|
TheFont.tmExternalLeading := tm.tmExternalLeading;
|
|
TheFont.tmMaxCharWidth := tm.tmMaxCharWidth;
|
|
TheFont.tmAveCharWidth := tm.tmAveCharWidth;
|
|
TheFont.tmCharset := tm.tmCharset;
|
|
if VarType(V) in VarInt then
|
|
TheFont.CharExtra := V
|
|
else if VarType(V) = VarString then
|
|
if V = 'normal' then
|
|
TheFont.CharExtra := 0
|
|
else TheFont.CharExtra := LengthConv(V, False, EmSize, EmSize, ExSize, 0)
|
|
else TheFont.CharExtra := 0;
|
|
end;
|
|
Result := TMyFont.Create;
|
|
Result.Assign(TheFont);
|
|
end;
|
|
|
|
{----------------ReadFontName}
|
|
function ReadFontName(S: string): string;
|
|
const
|
|
AMax = 5;
|
|
var
|
|
S1: string;
|
|
Done: boolean;
|
|
|
|
function NextFontName: string;
|
|
const
|
|
Generic1: array[1..AMax] of string = ('serif', 'monospace', 'sans-serif', 'cursive', 'Helvetica');
|
|
Generic2: array[1..AMax] of string = ('Times New Roman', 'Courier New', 'Arial', 'Lucida Handwriting', 'Arial');
|
|
var
|
|
I: integer;
|
|
begin
|
|
I := Pos(',', S); {read up to the comma}
|
|
if I > 0 then
|
|
begin
|
|
Result := Trim(System.Copy(S, 1, I-1));
|
|
Delete(S, 1, I);
|
|
end
|
|
else
|
|
begin {last item}
|
|
Result := Trim(S);
|
|
S := '';
|
|
end;
|
|
for I := 1 to AMax do
|
|
if CompareText(Result, Generic1[I]) = 0 then
|
|
begin
|
|
Result := Generic2[I];
|
|
break;
|
|
end;
|
|
if (Result <> '') and (Result[Length(Result)] in ['"', '''']) then
|
|
SetLength(Result, Length(Result)-1);
|
|
if (Result <> '') and (Result[1] in ['"', '''']) then
|
|
Delete(Result, 1, 1);
|
|
end;
|
|
|
|
begin
|
|
Done := False;
|
|
S1 := NextFontName;
|
|
while (S1 <> '') and not Done do
|
|
begin
|
|
{$IFDEF LCL} //Generic2 fonts won't be in Screen.Fonts with GTK2, so make
|
|
// sure first font for family is selected.
|
|
if Result = '' then
|
|
Result := S1;
|
|
{$ENDIF}
|
|
Done := Screen.Fonts.IndexOf(S1) >= 0;
|
|
if Done then
|
|
Result := S1
|
|
else S1 := NextFontName;
|
|
end;
|
|
end;
|
|
|
|
{----------------TProperties.GetSingleFontInfo}
|
|
procedure TProperties.GetSingleFontInfo(var Font: ThtFontInfo);
|
|
const
|
|
AMax = 5;
|
|
var
|
|
S, S1: string;
|
|
Done: boolean;
|
|
Wt: integer;
|
|
Style: TFontStyles;
|
|
|
|
function NextFontName: string;
|
|
const
|
|
Generic1: array[1..AMax] of string = ('serif', 'monospace', 'sans-serif', 'cursive', 'Helvetica');
|
|
Generic2: array[1..AMax] of string = ('Times New Roman', 'Courier New', 'Arial', 'Lucida Handwriting', 'Arial');
|
|
var
|
|
I: integer;
|
|
begin
|
|
I := Pos(',', S); {read up to the comma}
|
|
if I > 0 then
|
|
begin
|
|
Result := Trim(System.Copy(S, 1, I-1));
|
|
Delete(S, 1, I);
|
|
end
|
|
else
|
|
begin {last item}
|
|
Result := Trim(S);
|
|
S := '';
|
|
end;
|
|
for I := 1 to AMax do
|
|
if CompareText(Result, Generic1[I]) = 0 then
|
|
begin
|
|
Result := Generic2[I];
|
|
break;
|
|
end;
|
|
if (Result <> '') and (Result[Length(Result)] in ['"', '''']) then
|
|
SetLength(Result, Length(Result)-1);
|
|
if (Result <> '') and (Result[1] in ['"', '''']) then
|
|
Delete(Result, 1, 1);
|
|
end;
|
|
|
|
begin {call only if all things valid}
|
|
Font.ibgColor := FontBG;
|
|
Font.iColor := Props[Color];
|
|
Style := [];
|
|
if Pos('bold', Props[FontWeight]) > 0 then
|
|
Style := [fsBold]
|
|
else
|
|
begin
|
|
Wt := StrToIntDef(Props[FontWeight], 0);
|
|
if Wt >= 600 then
|
|
Style := [fsBold];
|
|
end;
|
|
if (Props[FontStyle] = 'italic') or (Props[FontStyle] = 'oblique') then
|
|
Style := Style + [fsItalic];
|
|
if Props[TextDecoration] = 'underline' then
|
|
Style := Style + [fsUnderline]
|
|
else if Props[TextDecoration] = 'line-through' then
|
|
Style := Style + [fsStrikeOut];
|
|
Font.iStyle := Style;
|
|
Font.iSize := Props[FontSize];
|
|
Font.iCharset := CharSet;
|
|
Font.iCharExtra := Props[LetterSpacing];
|
|
|
|
Done := False;
|
|
S := Props[FontFamily];
|
|
S1 := NextFontName;
|
|
while (S1 <> '') and not Done do
|
|
begin
|
|
{$IFDEF LCL} //Generic2 fonts won't be in Screen.Fonts with GTK2, so make
|
|
// sure first font for family is selected.
|
|
if Font.iName = '' then
|
|
Font.iName := S1;
|
|
{$ENDIF}
|
|
Done := Screen.Fonts.IndexOf(S1) >= 0;
|
|
if Done then
|
|
begin
|
|
Font.iName := S1;
|
|
end
|
|
else S1 := NextFontName;
|
|
end;
|
|
if Font.iName = '' then
|
|
Font.iName := DefFontname;
|
|
end;
|
|
|
|
procedure TProperties.CalcLinkFontInfo(Styles: TStyleList; I: integer);
|
|
{I is index in PropStack for this item}
|
|
procedure InsertNewProp(N: integer; const Pseudo: string);
|
|
begin
|
|
PropStack.Insert(N, TProperties.Create);
|
|
PropStack[N].Inherit('', PropStack[N-1]);
|
|
PropStack[N].Combine(Styles, PropTag, PropClass, PropID, Pseudo, PropTitle, PropStyle);
|
|
end;
|
|
|
|
begin
|
|
PropStack[I].SetFontBG;
|
|
GetSingleFontInfo(FIArray.Ar[LFont]);
|
|
InsertNewProp(I+1, 'visited');
|
|
PropStack[I+1].SetFontBG;
|
|
PropStack[I+1].GetSingleFontInfo(FIArray.Ar[VFont]);
|
|
InsertNewProp(I+2, 'hover');
|
|
PropStack[I+2].SetFontBG;
|
|
PropStack[I+2].GetSingleFontInfo(FIArray.Ar[HVFont]);
|
|
PropStack.Delete(I+2);
|
|
PropStack.Delete(I+1);
|
|
InsertNewProp(I+1, 'hover');
|
|
PropStack[I+1].SetFontBG;
|
|
PropStack[I+1].GetSingleFontInfo(FIArray.Ar[HLFont]);
|
|
PropStack.Delete(I+1);
|
|
end;
|
|
|
|
procedure TProperties.GetFontInfo(AFI: TFontInfoArray);
|
|
begin
|
|
AFI.Assign(FIArray);
|
|
end;
|
|
|
|
procedure TProperties.GetVMarginArray(var MArray: TVMarginArray);
|
|
var
|
|
I: PropIndices;
|
|
begin
|
|
for I := Low(Marray) to High(MArray) do
|
|
case I of
|
|
BorderTopStyle..BorderLeftStyle:
|
|
if VarType(Props[I]) = VarString then
|
|
MArray[I] := BorderStyleFromString(Props[I])
|
|
else MArray[I] := bssNone;
|
|
else
|
|
MArray[I] := Props[I];
|
|
end;
|
|
end;
|
|
|
|
procedure TProperties.AddPropertyByIndex(Index: PropIndices; PropValue: string);
|
|
var
|
|
NewColor: TColor;
|
|
begin
|
|
case Index of
|
|
BorderColor:
|
|
if ColorFromString(PropValue, False, NewColor) then
|
|
begin
|
|
Props[BorderColor] := NewColor;
|
|
Props[BorderLeftColor] := NewColor;
|
|
Props[BorderTopColor] := NewColor;
|
|
Props[BorderRightColor] := NewColor;
|
|
Props[BorderBottomColor] := NewColor;
|
|
end;
|
|
BorderTopColor .. BorderLeftColor:
|
|
if ColorFromString(PropValue, False, NewColor) then
|
|
Props[Index] := NewColor;
|
|
Color, BackgroundColor:
|
|
if ColorFromString(PropValue, False, NewColor) then
|
|
Props[Index] := NewColor
|
|
else if Index = Color then
|
|
Props[Index] := clBlack
|
|
else Props[Index] := clNone;
|
|
MarginTop..BorderLeftWidth, Width..LeftPos:
|
|
Props[Index] := PropValue;
|
|
FontSize:
|
|
Props[FontSize] := PropValue;
|
|
Visibility:
|
|
begin
|
|
if PropValue = 'visible' then
|
|
Props[Visibility] := viVisible
|
|
else if PropValue = 'hidden' then
|
|
Props[Visibility] := viHidden;
|
|
end;
|
|
TextTransform:
|
|
begin
|
|
if PropValue = 'uppercase' then
|
|
Props[TextTransform] := txUpper
|
|
else if PropValue = 'lowercase' then
|
|
Props[TextTransform] := txLower
|
|
else Props[TextTransform] := txNone;
|
|
end;
|
|
WordWrap:
|
|
if PropValue = 'break-word' then
|
|
Props[WordWrap] := PropValue
|
|
else Props[WordWrap] := 'normal';
|
|
FontVariant:
|
|
if PropValue = 'small-caps' then
|
|
Props[FontVariant] := PropValue
|
|
else if PropValue = 'normal' then
|
|
Props[FontVariant] := 'normal';
|
|
BorderTopStyle..BorderLeftStyle:
|
|
begin
|
|
if PropValue <> 'none' then
|
|
Props[BorderStyle] := PropValue;
|
|
Props[Index] := PropValue;
|
|
end;
|
|
BorderStyle:
|
|
begin
|
|
Props[BorderStyle] := PropValue;
|
|
Props[BorderTopStyle] := PropValue;
|
|
Props[BorderRightStyle] := PropValue;
|
|
Props[BorderBottomStyle] := PropValue;
|
|
Props[BorderLeftStyle] := PropValue;
|
|
end;
|
|
else
|
|
Props[Index] := PropValue;
|
|
end;
|
|
end;
|
|
|
|
procedure TProperties.AddPropertyByName(const PropName, PropValue: string);
|
|
var
|
|
Index: PropIndices;
|
|
begin
|
|
if FindPropIndex(PropName, Index) then
|
|
AddPropertyByIndex(Index, PropValue);
|
|
end;
|
|
|
|
constructor TStyleList.Create(AMasterList: TObject);
|
|
begin
|
|
inherited Create;
|
|
MasterList := AMasterList;
|
|
Sorted := True;
|
|
Duplicates := dupAccept;
|
|
SeqNo := 10;
|
|
end;
|
|
|
|
destructor TStyleList.Destroy;
|
|
begin
|
|
Clear;
|
|
inherited Destroy;
|
|
end;
|
|
|
|
procedure TStyleList.Clear;
|
|
var
|
|
I: integer;
|
|
begin
|
|
for I := 0 to Count-1 do
|
|
TProperties(Objects[I]).Free;
|
|
SeqNo := 10;
|
|
inherited;
|
|
end;
|
|
|
|
function TStyleList.GetSeqNo: string;
|
|
begin {used to help sort contextual items by entry sequence}
|
|
Result := IntToStr(SeqNo);
|
|
Inc(SeqNo);
|
|
end;
|
|
|
|
{----------------TStyleList.AddModifyProp}
|
|
procedure TStyleList.AddModifyProp(const Selector, Prop, Value: string);
|
|
{strings are all lowercase here}
|
|
var
|
|
I: integer;
|
|
PropIndex: PropIndices;
|
|
Propty: TProperties;
|
|
NewColor: TColor;
|
|
NewProp: boolean;
|
|
begin
|
|
if FindPropIndex(Prop, PropIndex) then
|
|
begin
|
|
if not Find(Selector, I) then
|
|
begin
|
|
NewProp := True;
|
|
Propty := TProperties.Create {newly created property}
|
|
end
|
|
else
|
|
begin
|
|
Propty := TProperties(Objects[I]); {modify existing property}
|
|
NewProp := False;
|
|
end;
|
|
case PropIndex of
|
|
Color:
|
|
if ColorFromString(Value, False, NewColor) then
|
|
begin
|
|
if Selector = ':link' then
|
|
begin {changed the defaults to be the same as link}
|
|
ModifyLinkColor('hover', NewColor);
|
|
ModifyLinkColor('visited', NewColor);
|
|
end
|
|
else if Selector = ':visited' then
|
|
ModifyLinkColor('hover', NewColor);
|
|
Propty.Props[PropIndex] := NewColor;
|
|
end;
|
|
BorderColor:
|
|
if ColorFromString(Value, False, NewColor) then
|
|
begin
|
|
Propty.Props[BorderColor] := NewColor;
|
|
Propty.Props[BorderLeftColor] := NewColor;
|
|
Propty.Props[BorderTopColor] := NewColor;
|
|
Propty.Props[BorderRightColor] := NewColor;
|
|
Propty.Props[BorderBottomColor] := NewColor;
|
|
end;
|
|
BorderTopColor .. BorderLeftColor:
|
|
if ColorFromString(Value, False, NewColor) then
|
|
Propty.Props[PropIndex] := NewColor;
|
|
BackgroundColor:
|
|
if ColorFromString(Value, False, NewColor) then
|
|
Propty.Props[PropIndex] := NewColor
|
|
else Propty.Props[PropIndex] := clNone;
|
|
Visibility:
|
|
begin
|
|
if Value = 'visible' then
|
|
Propty.Props[Visibility] := viVisible
|
|
else if Value = 'hidden' then
|
|
Propty.Props[Visibility] := viHidden;
|
|
end;
|
|
TextTransform:
|
|
begin
|
|
if Value = 'uppercase' then
|
|
Propty.Props[TextTransform] := txUpper
|
|
else if Value = 'lowercase' then
|
|
Propty.Props[TextTransform] := txLower
|
|
else Propty.Props[TextTransform] := txNone;
|
|
end;
|
|
WordWrap:
|
|
if Value = 'break-word' then
|
|
Propty.Props[WordWrap] := Value
|
|
else Propty.Props[WordWrap] := 'normal';
|
|
FontVariant:
|
|
if Value = 'small-caps' then
|
|
Propty.Props[FontVariant] := Value
|
|
else if Value = 'normal' then
|
|
Propty.Props[FontVariant] := 'normal';
|
|
BorderTopStyle..BorderLeftStyle:
|
|
begin
|
|
if Value <> 'none' then
|
|
Propty.Props[BorderStyle] := Value;
|
|
Propty.Props[PropIndex] := Value;
|
|
end;
|
|
BorderStyle:
|
|
begin
|
|
Propty.Props[BorderStyle] := Value;
|
|
Propty.Props[BorderTopStyle] := Value;
|
|
Propty.Props[BorderRightStyle] := Value;
|
|
Propty.Props[BorderBottomStyle] := Value;
|
|
Propty.Props[BorderLeftStyle] := Value;
|
|
end;
|
|
LineHeight:
|
|
Propty.Props[PropIndex] := Value;
|
|
else
|
|
Propty.Props[PropIndex] := Value;
|
|
end;
|
|
if NewProp then
|
|
AddObject(Selector, Propty); {it's a newly created property}
|
|
if Pos(':hover', Selector) > 0 then
|
|
TSectionList(MasterList).LinksActive := True;
|
|
if Selector = 'a' then
|
|
begin
|
|
AddModifyProp('::link', Prop, Value); {also applies to ::link}
|
|
end;
|
|
{$ifdef Quirk}
|
|
if (Selector = 'body') and (PropIndex = Color) then
|
|
FixupTableColor(Propty);
|
|
{$endif}
|
|
end;
|
|
end;
|
|
|
|
{$ifdef Quirk}
|
|
procedure TStyleList.FixupTableColor(BodyProp: TProperties);
|
|
{if Quirk is set, make sure that the table color is defined the same as the
|
|
body color}
|
|
var
|
|
Propty1: TProperties;
|
|
I: integer;
|
|
begin
|
|
if Find('td', I) then
|
|
begin
|
|
Propty1 := TProperties(Objects[I]);
|
|
Propty1.Props[Color] := BodyProp.Props[Color];
|
|
end;
|
|
if Find('th', I) then
|
|
begin
|
|
Propty1 := TProperties(Objects[I]);
|
|
Propty1.Props[Color] := BodyProp.Props[Color];
|
|
end;
|
|
end;
|
|
{$endif}
|
|
|
|
function TStyleList.AddObject(const S: string; AObject: TObject): Integer;
|
|
begin
|
|
Result := inherited AddObject(S, AObject);
|
|
TProperties(AObject).PropTag := S;
|
|
end;
|
|
|
|
function TStyleList.AddDuplicate(const Tag: string; Prop: TProperties): TProperties;
|
|
begin
|
|
Result := TProperties.Create;
|
|
Result.Copy(Prop);
|
|
AddObject(Tag, Result);
|
|
end;
|
|
|
|
procedure TStyleList.ModifyLinkColor(Psuedo: string; AColor: TColor);
|
|
var
|
|
I: integer;
|
|
begin
|
|
if Find('::'+Psuedo, I) then {the defaults}
|
|
with TProperties(Objects[I]) do
|
|
Props[Color] := AColor;
|
|
end;
|
|
|
|
procedure TStyleList.Initialize(const FontName, PreFontName: string;
|
|
PointSize: integer; AColor, AHotspot, AVisitedColor, AActiveColor: TColor;
|
|
LinkUnderline: boolean; ACharSet: TFontCharSet; MarginHeight, MarginWidth: integer);
|
|
type
|
|
ListTypes = (ul, ol, menu, dir, dl, dd, blockquote);
|
|
const
|
|
ListStr: array[Low(ListTypes)..High(ListTypes)] of string =
|
|
('ul', 'ol', 'menu', 'dir', 'dl', 'dd', 'blockquote');
|
|
var
|
|
HIndex: integer;
|
|
Properties: TProperties;
|
|
J: ListTypes;
|
|
F: double;
|
|
|
|
begin
|
|
Clear;
|
|
DefPointSize := PointSize;
|
|
|
|
Properties := TProperties.Create;
|
|
with Properties do
|
|
begin
|
|
DefFontname := FontName;
|
|
Props[FontFamily] := FontName;
|
|
Props[FontSize] := PointSize*1.0;
|
|
Props[FontStyle] := 'none';
|
|
Props[FontWeight] := 'normal';
|
|
Props[TextAlign] := 'left';
|
|
Props[TextDecoration] := 'none';
|
|
Props[TextTransform] := txNone;
|
|
Props[WordWrap] := 'normal';
|
|
Props[FontVariant] := 'normal';
|
|
Props[Color] := AColor;
|
|
Props[MarginTop] := MarginHeight;
|
|
Props[MarginBottom] := MarginHeight;
|
|
Props[MarginLeft] := MarginWidth;
|
|
Props[MarginRight] := MarginWidth;
|
|
Props[Visibility] := viVisible;
|
|
Props[LetterSpacing] := 0;
|
|
CharSet := ACharSet;
|
|
end;
|
|
AddObject('default', Properties);
|
|
DefProp := Properties;
|
|
|
|
{$ifdef Quirk}
|
|
Properties := TProperties.Create;
|
|
with Properties do
|
|
begin
|
|
Props[FontSize] := PointSize*1.0;
|
|
Props[FontStyle] := 'none';
|
|
Props[FontWeight] := 'normal';
|
|
Props[Color] := AColor;
|
|
end;
|
|
AddObject('td', Properties);
|
|
Properties := AddDuplicate('th', Properties);
|
|
Properties.Props[FontWeight] := 'bold';
|
|
{$endif}
|
|
|
|
Properties := TProperties.Create;
|
|
with Properties do
|
|
begin
|
|
Props[Color] := AHotSpot or PalRelative;
|
|
if LinkUnderline then
|
|
Props[TextDecoration] := 'underline'
|
|
else
|
|
Props[TextDecoration] := 'none';
|
|
end;
|
|
AddObject('::link', Properties);
|
|
|
|
Properties := TProperties.Create;
|
|
with Properties do
|
|
Props[Color] := AVisitedColor or PalRelative;
|
|
AddObject('::visited', Properties);
|
|
|
|
Properties := TProperties.Create;
|
|
with Properties do
|
|
Props[Color] := AActiveColor or PalRelative;
|
|
AddObject('::hover', Properties);
|
|
|
|
Properties := TProperties.Create;
|
|
AddObject('null', Properties);
|
|
|
|
Properties := TProperties.Create;
|
|
with Properties do
|
|
begin
|
|
Props[FontFamily] := PreFontName;
|
|
Props[FontSize] := PointSize*10.0 / 12.0;
|
|
Props[FontStyle] := 'none';
|
|
Props[FontWeight] := 'normal';
|
|
Props[TextDecoration] := 'none';
|
|
end;
|
|
AddObject('pre', Properties);
|
|
|
|
Properties := TProperties.Create;
|
|
with Properties do
|
|
begin
|
|
Props[MarginTop] := AutoParagraph;
|
|
Props[MarginBottom] := AutoParagraph;
|
|
end;
|
|
AddObject('p', Properties);
|
|
|
|
Properties := TProperties.Create;
|
|
with Properties do
|
|
begin
|
|
Props[MarginTop] := 0;
|
|
end;
|
|
AddObject('p 11pre', Properties);
|
|
|
|
for J := Low(ListTypes) to High(ListTypes) do
|
|
begin
|
|
Properties := TProperties.Create;
|
|
with Properties do
|
|
begin
|
|
Props[PaddingLeft] := 35;
|
|
Props[MarginTop] := 0;
|
|
Props[MarginBottom] := 0;
|
|
case J of
|
|
ol, ul, menu, dir:
|
|
begin
|
|
Props[ListStyleType] := 'blank';
|
|
Props[MarginTop] := AutoParagraph;
|
|
Props[MarginBottom] := AutoParagraph;
|
|
end;
|
|
dl: begin
|
|
Props[MarginLeft] := 0;
|
|
Props[PaddingLeft] := 0;
|
|
Props[ListStyleType] := 'none';
|
|
end;
|
|
blockquote:
|
|
begin
|
|
Props[MarginTop] := AutoParagraph;
|
|
Props[MarginBottom] := ParagraphSpace;
|
|
end;
|
|
|
|
dd: ;
|
|
end;
|
|
end;
|
|
AddObject(ListStr[J], Properties);
|
|
end;
|
|
|
|
Properties := TProperties.Create;
|
|
with Properties do
|
|
begin
|
|
Props[FontFamily] := PrefontName;
|
|
Props[FontSize] := '0.833em'; {10.0 / 12.0;}
|
|
end;
|
|
AddObject('code', Properties);
|
|
AddDuplicate('tt', Properties);
|
|
AddDuplicate('kbd', Properties);
|
|
AddDuplicate('samp', Properties);
|
|
|
|
Properties := TProperties.Create;
|
|
with Properties do
|
|
begin
|
|
Props[FontWeight] := 'bold';
|
|
end;
|
|
AddObject('b', Properties);
|
|
AddDuplicate('strong', Properties);
|
|
{$ifndef Quirk}
|
|
AddDuplicate('th', Properties);
|
|
{$endif}
|
|
|
|
Properties := TProperties.Create;
|
|
with Properties do
|
|
begin
|
|
Props[FontSize] := '75%';
|
|
Props[VerticalAlign] := 'super';
|
|
end;
|
|
AddObject('sup', Properties);
|
|
|
|
Properties := TProperties.Create;
|
|
with Properties do
|
|
begin
|
|
Props[FontSize] := '75%';
|
|
Props[VerticalAlign] := 'sub';
|
|
end;
|
|
AddObject('sub', Properties);
|
|
|
|
Properties := TProperties.Create;
|
|
with Properties do
|
|
begin
|
|
Props[FontSize] := '1.25em';
|
|
end;
|
|
AddObject('big', Properties);
|
|
|
|
Properties := TProperties.Create;
|
|
with Properties do
|
|
begin
|
|
Props[FontSize] := '0.75em';
|
|
end;
|
|
AddObject('small', Properties);
|
|
|
|
Properties := TProperties.Create;
|
|
with Properties do
|
|
begin
|
|
Props[TextAlign] := 'none';
|
|
end;
|
|
AddObject('table', Properties);
|
|
|
|
Properties := TProperties.Create;
|
|
with Properties do
|
|
begin
|
|
Props[FontStyle] := 'italic';
|
|
end;
|
|
AddObject('i', Properties);
|
|
AddDuplicate('em', Properties);
|
|
AddDuplicate('cite', Properties);
|
|
AddDuplicate('var', Properties);
|
|
|
|
AddDuplicate('address', Properties);
|
|
|
|
Properties := TProperties.Create;
|
|
with Properties do
|
|
begin
|
|
Props[TextDecoration] := 'underline';
|
|
end;
|
|
AddObject('u', Properties);
|
|
|
|
Properties := TProperties.Create;
|
|
with Properties do
|
|
begin
|
|
Props[TextDecoration] := 'line-through';
|
|
end;
|
|
AddObject('s', Properties);
|
|
AddDuplicate('strike', Properties);
|
|
|
|
Properties := TProperties.Create;
|
|
with Properties do
|
|
begin
|
|
Props[TextAlign] := 'center';
|
|
end;
|
|
AddObject('center', Properties);
|
|
AddDuplicate('caption', Properties);
|
|
|
|
Properties := TProperties.Create;
|
|
with Properties do
|
|
begin
|
|
Props[FontFamily] := 'Arial Unicode MS, Arial';
|
|
Props[FontSize] := '10pt';
|
|
Props[FontStyle] := 'none';
|
|
Props[FontWeight] := 'normal';
|
|
Props[TextAlign] := 'left';
|
|
Props[TextDecoration] := 'none';
|
|
Props[Color] := AColor;
|
|
end;
|
|
AddObject('input', Properties);
|
|
AddDuplicate('select', Properties);
|
|
|
|
Properties := AddDuplicate('textarea', Properties);
|
|
if IsWin32Platform then
|
|
Properties.Props[FontFamily] := 'Arial Unicode MS, Arial'
|
|
else
|
|
Properties.Props[FontFamily] := PreFontName;
|
|
|
|
Properties := TProperties.Create;
|
|
with Properties do
|
|
begin
|
|
Props[MarginLeft] := 0;
|
|
Props[MarginRight] := 0;
|
|
Props[MarginTop] := 10;
|
|
Props[MarginBottom] := 10;
|
|
end;
|
|
AddObject('hr', Properties);
|
|
|
|
for HIndex := 1 to 6 do
|
|
begin
|
|
Properties := TProperties.Create;
|
|
F := PointSize / 12.0;
|
|
with Properties do
|
|
begin
|
|
case HIndex of
|
|
1: Props[FontSize] := 24.0*F;
|
|
2: Props[FontSize] := 18.0*F;
|
|
3: Props[FontSize] := 14.0*F;
|
|
4: Props[FontSize] := 12.0*F;
|
|
5: Props[FontSize] := 10.0*F;
|
|
6: Props[FontSize] := 8.0*F;
|
|
end;
|
|
Props[MarginTop] := 19;
|
|
Props[MarginBottom] := Props[MarginTop];
|
|
Props[FontWeight] := 'bold';
|
|
end;
|
|
AddObject('h'+IntToStr(HIndex), Properties);
|
|
end;
|
|
end;
|
|
|
|
const
|
|
NumColors = 177; //176;
|
|
Colors: array[1..NumColors] of string[20] = ('transparent',
|
|
'black', 'maroon', 'green', 'olive', 'navy', 'purple', 'teal', 'gray',
|
|
'silver', 'red', 'lime', 'yellow', 'blue', 'fuchsia', 'aqua', 'white',
|
|
'aliceblue', 'antiquewhite', 'aquamarine', 'azure', 'beige',
|
|
'bisque', 'blanchedalmond', 'blueviolet', 'brown', 'burlywood',
|
|
'cadetblue', 'chartreuse', 'chocolate', 'coral', 'cornflowerblue',
|
|
'cornsilk', 'crimson', 'cyan', 'darkblue', 'darkcyan',
|
|
'darkgoldenrod', 'darkgray', 'darkgreen', 'darkkhaki', 'darkmagenta',
|
|
'darkolivegreen', 'darkorange', 'darkorchid', 'darkred', 'darksalmon',
|
|
'darkseagreen', 'darkslateblue', 'darkslategray', 'darkturquoise', 'darkviolet',
|
|
'deeppink', 'deepskyblue', 'dimgray', 'dodgerblue', 'firebrick',
|
|
'floralwhite', 'forestgreen', 'gainsboro', 'ghostwhite', 'gold',
|
|
'goldenrod', 'greenyellow', 'honeydew', 'hotpink', 'indianred',
|
|
'indigo', 'ivory', 'khaki', 'lavender', 'lavenderblush',
|
|
'lawngreen', 'lemonchiffon', 'lightblue', 'lightcoral', 'lightcyan',
|
|
'lightgoldenrodyellow', 'lightgreen', 'lightgray', 'lightpink', 'lightsalmon',
|
|
'lightseagreen', 'lightskyblue', 'lightslategray', 'lightsteelblue', 'lightyellow',
|
|
'limegreen', 'linen', 'magenta', 'mediumaquamarine', 'mediumblue',
|
|
'mediumorchid', 'mediumpurple', 'mediumseagreen', 'mediumslateblue', 'mediumspringgreen',
|
|
'mediumturquoise', 'mediumvioletred', 'midnightblue', 'mintcream', 'mistyrose',
|
|
'moccasin', 'navajowhite', 'oldlace', 'olivedrab', 'orange',
|
|
'orangered', 'orchid', 'palegoldenrod', 'palegreen', 'paleturquoise',
|
|
'palevioletred', 'papayawhip', 'peachpuff', 'peru', 'pink',
|
|
'plum', 'powderblue', 'rosybrown', 'royalblue', 'saddlebrown',
|
|
'salmon', 'sandybrown', 'seagreen', 'seashell', 'sienna',
|
|
'skyblue', 'slateblue', 'slategray', 'snow', 'springgreen',
|
|
'steelblue', 'tan', 'thistle', 'tomato', 'turquoise',
|
|
'violet', 'wheat', 'whitesmoke', 'yellowgreen',
|
|
|
|
'darkyellow',
|
|
|
|
'grey', 'darkgrey', 'darkslategrey', 'dimgrey', 'lightgrey', 'lightslategrey', 'slategrey',
|
|
'background', 'activecaption', 'inactivecaption', 'menu', 'window',
|
|
'windowframe', 'menutext', 'windowtext', 'captiontext', 'activeborder',
|
|
'inactiveborder', 'appworkSpace', 'highlight', 'hightlighttext', 'buttonface',
|
|
'buttonshadow', 'graytext', 'buttontext', 'inactivecaptiontext', 'buttonhighlight',
|
|
'threeddarkshadow', 'threedlightshadow', 'infotext', 'infobackground', 'scrollbar',
|
|
'threedface', 'threedhighlight', 'threedshadow');
|
|
|
|
ColorValues: array[1..NumColors] of TColor = (clNone,
|
|
clBLACK, clMAROON, clGREEN, clOLIVE, clNAVY, clPURPLE, clTEAL, clGRAY,
|
|
clSILVER, clRED, clLIME, clYELLOW, clBLUE, clFUCHSIA, clAQUA, clWHITE,
|
|
$FFF8F0, $D7EBFA, $D4FF7F, $FFFFF0, $DCF5F5,
|
|
$C4E4FF, $CDEBFF, $E22B8A, $2A2AA5, $87B8DE,
|
|
$A09E5F, $00FF7F, $1E69D2, $507FFF, $ED9564,
|
|
$DCF8FF, $3614DC, $FFFF00, $8B0000, $8B8B00,
|
|
$0B86B8, $A9A9A9, $006400, $6BB7BD, $8B008B,
|
|
$2F6B55, $008CFF, $CC3299, $00008B, $7A96E9,
|
|
$8FBC8F, $8B3D48, $4F4F2F, $D1CE00, $D30094,
|
|
$9314FF, $FFBF00, $696969, $FF901E, $2222B2,
|
|
$F0FAFF, $228B22, $DCDCDC, $FFF8F8, $00D7FF,
|
|
$20A5DA, $2FFFAD, $F0FFF0, $B469FF, $5C5CCD,
|
|
$82004B, $F0FFFF, $8CE6F0, $FAE6E6, $F5F0FF,
|
|
$00FC7C, $CDFAFF, $E6D8AD, $8080F0, $FFFFE0,
|
|
$D2FAFA, $90EE90, $D3D3D3, $C1B6FF, $7AA0FF,
|
|
$AAB220, $FACE87, $998877, $DEC4B0, $E0FFFF,
|
|
$32CD32, $E6F0FA, $FF00FF, $AACD66, $CD0000,
|
|
$D355BA, $DB7093, $71B33C, $EE687B, $9AFA00,
|
|
$CCD148, $8515C7, $701919, $FAFFF5, $E1E4FF,
|
|
$B5E4FF, $ADDEFF, $E6F5FD, $238E6B, $00A5FF,
|
|
$0045FF, $D670DA, $AAE8EE, $98FB98, $EEEEAF,
|
|
$9370DB, $D5EFFF, $B9DAFF, $3F85CD, $CBC0FF,
|
|
$DDA0DD, $E6E0B0, $8F8FBC, $E16941, $13458B,
|
|
$7280FA, $60A4F4, $578B2E, $EEF5FF, $2D52A0,
|
|
$EBCE87, $CD5A6A, $908070, $FAFAFF, $7FFF00,
|
|
$B48246, $8CB4D2, $D8BFD8, $4763FF, $D0E040,
|
|
$EE82EE, $B3DEF5, $F5F5F5, $32CD9A,
|
|
|
|
$008080, // added
|
|
|
|
clgray, $A9A9A9, $4F4F2F, $696969, $D3D3D3, $998877, $908070,
|
|
clBackground, clActiveCaption, clInactiveCaption, clMenu, clWindow,
|
|
clWindowFrame, clMenuText, clWindowText, clCaptionText, clActiveBorder,
|
|
clInactiveBorder, clAppWorkSpace, clHighlight, clHighlightText, clBtnFace,
|
|
clBtnShadow, clGrayText, clBtnText, clInactiveCaptionText, clBtnHighlight,
|
|
cl3DDkShadow, clBtnHighlight, clInfoText, clInfoBk, clScrollBar,
|
|
clBtnFace, cl3DLight, clBtnShadow);
|
|
|
|
var
|
|
ColorStrings: TStringList;
|
|
|
|
procedure SortColors;
|
|
var
|
|
I: integer;
|
|
begin
|
|
// Put the Colors into a sorted StringList for faster access.
|
|
if ColorStrings = nil then
|
|
begin
|
|
ColorStrings := TStringList.Create;
|
|
with ColorStrings do
|
|
begin
|
|
for I := 1 to NumColors do
|
|
ColorStrings.AddObject(Colors[I], Pointer(ColorValues[I]));
|
|
Sort;
|
|
end;
|
|
end;
|
|
end;
|
|
|
|
{Translate StyleSheet color string to Color. If NeedPound is true, a '#' sign
|
|
is required to preceed a hexidecimal value.}
|
|
function ColorFromString(S: string; NeedPound: boolean; var Color: TColor): boolean;
|
|
const
|
|
LastS: string = '?&%@';
|
|
LastColor: TColor = 0;
|
|
var
|
|
I, Rd, Bl: integer;
|
|
S1: string;
|
|
|
|
function FindRGBColor(S: string): boolean;
|
|
type
|
|
Colors = (red, green, blue);
|
|
var
|
|
A: array[red..blue] of string;
|
|
C: array[red..blue] of integer;
|
|
I, J: integer;
|
|
K: Colors;
|
|
begin
|
|
I := Pos('(', S);
|
|
J := Pos(')', S);
|
|
if (I > 0) and (J > 0) then
|
|
begin
|
|
S := copy(S, 1, J-1);
|
|
S := Trim(Copy(S, I+1, 255));
|
|
for K := Red to Green do
|
|
begin
|
|
I := Pos(',', S);
|
|
A[K] := Trim(copy(S, 1, I-1));
|
|
S := Trim(Copy(S, I+1, 255));
|
|
end;
|
|
A[Blue] := S;
|
|
for K := Red to Blue do
|
|
begin
|
|
I := Pos('%', A[K]);
|
|
if I > 0 then
|
|
begin
|
|
Delete(A[K], I, 1);
|
|
try
|
|
C[K] := Round(StrToFloat(A[K]) * 2.55);
|
|
except
|
|
C[K] := 0;
|
|
end;
|
|
end
|
|
else
|
|
C[K] := StrToIntDef(A[K], 0);
|
|
C[K] := IntMax(0, IntMin(255, C[K]));
|
|
end;
|
|
Color := (C[Blue] shl 16) or (C[Green] shl 8) or C[Red];
|
|
Result := True;
|
|
end
|
|
else Result := False;
|
|
end;
|
|
|
|
begin
|
|
if S = '' then
|
|
begin
|
|
Result := False;
|
|
Exit;
|
|
end;
|
|
S := Lowercase(Trim(S));
|
|
if S = LastS then
|
|
begin {inquiries often come in pairs, this saves some recomputing}
|
|
Color := LastColor;
|
|
Result := True;
|
|
Exit;
|
|
end;
|
|
if (Length(S) > 0) and (S[1] <> '#') and (Pos('rgb(', S) = 0) then
|
|
if ColorStrings.Find(S, I) then
|
|
begin
|
|
Color := TColor(ColorStrings.Objects[I]);
|
|
Result := True;
|
|
LastS := S;
|
|
LastColor := Color;
|
|
Exit;
|
|
end;
|
|
S1 := S;
|
|
I := Pos('rgb', S);
|
|
if (I > 0) then
|
|
Result := FindRGBColor(Copy(S, I+3, 255))
|
|
else
|
|
begin
|
|
try
|
|
I := Pos('#', S);
|
|
if I > 0 then
|
|
while I > 0 do {sometimes multiple ##}
|
|
begin
|
|
Delete(S, 1, I);
|
|
I := Pos('#', S);
|
|
end
|
|
else if NeedPound then
|
|
begin
|
|
Result := False;
|
|
Exit;
|
|
end;
|
|
S := Trim(S);
|
|
if Length(S) <= 3 then
|
|
for I := Length(S) downto 1 do
|
|
Insert(S[I], S, I); {double each character}
|
|
Color := StrToInt('$'+S); {but bytes are backwards!}
|
|
Rd := Color and $FF;
|
|
Bl := Color and $FF0000;
|
|
Color := (Color and $00FF00) + (Rd shl 16) + (Bl shr 16) or PalRelative;
|
|
Result := True;
|
|
except
|
|
Result := False;
|
|
end;
|
|
end;
|
|
if Result then
|
|
begin
|
|
LastS := S1;
|
|
LastColor := Color;
|
|
end;
|
|
end;
|
|
|
|
{ TFontInfoArray }
|
|
|
|
constructor TFontInfoArray.Create;
|
|
var
|
|
I: FIIndex;
|
|
begin
|
|
inherited Create;
|
|
for I := LFont to HVFont do
|
|
Ar[I] := ThtFontInfo.Create;
|
|
end;
|
|
|
|
destructor TFontInfoArray.Destroy;
|
|
var
|
|
I: FIIndex;
|
|
begin
|
|
for I := LFont to HVFont do
|
|
Ar[I].Free;
|
|
inherited;
|
|
end;
|
|
|
|
procedure TFontInfoArray.Assign(Source: TFontInfoArray);
|
|
var
|
|
I: FIIndex;
|
|
begin
|
|
for I := LFont to HVFont do
|
|
begin
|
|
Ar[I].iName := Source.Ar[I].iName;
|
|
Ar[I].iSize := Source.Ar[I].iSize;
|
|
Ar[I].iStyle := Source.Ar[I].iStyle;
|
|
Ar[I].iColor := Source.Ar[I].iColor;
|
|
Ar[I].ibgColor := Source.Ar[I].ibgColor;
|
|
Ar[I].iCharSet := Source.Ar[I].iCharSet;
|
|
Ar[I].iCharExtra := Source.Ar[I].iCharExtra;
|
|
end;
|
|
end;
|
|
|
|
{$HINTS OFF}
|
|
{----------------FontSizeConv}
|
|
function FontSizeConv(const Str: string; OldSize: double): double;
|
|
{given a font-size string, return the point size}
|
|
var
|
|
V, PPI: double;
|
|
S1, S2: string;
|
|
I, J: integer;
|
|
begin
|
|
PPI := Screen.PixelsPerInch;
|
|
Val(Str, V, I);
|
|
J := Pos('e', Str); {'e' would be legal for Val but not for us}
|
|
if (J <> 0) and (I > J) then
|
|
I := J;
|
|
if I > 0 then
|
|
begin
|
|
S1 := Copy(Str, 1, I-1);
|
|
S2 := Trim(Copy(Str, I, Length(Str)-I+1));
|
|
end
|
|
else
|
|
begin
|
|
S1 := Str;
|
|
S2 := '';
|
|
end;
|
|
{S1 has the number, S2 the units}
|
|
Val(S1, V, I);
|
|
if S2 = 'in' then
|
|
V := 72.0*V
|
|
else if S2 = 'cm' then
|
|
V := 72.0*V/2.54
|
|
else if S2 = 'mm' then
|
|
V := 72.0*V/25.4
|
|
else if S2 = 'pt' then
|
|
else if S2 = 'px' then
|
|
V := V*72.0/PPI
|
|
else if S2 = 'pc' then
|
|
V := V*12.0
|
|
else if S2 = 'em' then
|
|
V := V*OldSize
|
|
else if S2 = 'ex' then
|
|
V := V*OldSize/2.0 {1/2 of em}
|
|
else if S2 = '%' then
|
|
V := V*OldSize/100.0
|
|
else if S2 = '' then
|
|
V := V*72.0/PPI {pixels by default}
|
|
else if S2 = 'smaller' then
|
|
V := 0.75*OldSize
|
|
else if S2 = 'larger' then
|
|
V := 1.25*OldSize
|
|
else if S2 = 'xx-small' then
|
|
V := DefPointSize/1.5
|
|
else if S2 = 'x-small' then
|
|
V := DefPointSize/1.2
|
|
else if S2 = 'small' then
|
|
V := DefPointSize
|
|
else if S2 = 'medium' then
|
|
V := DefPointSize*1.2
|
|
else if S2 = 'large' then
|
|
V := DefPointSize*1.5
|
|
else if S2 = 'x-large' then
|
|
V := DefPointSize*2.0
|
|
else if S2 = 'xx-large' then
|
|
V := DefPointSize*3.0
|
|
else V := DefPointSize; {error, return 12pt}
|
|
Result := V;
|
|
end;
|
|
|
|
{----------------LengthConv}
|
|
function LengthConv(const Str: string; Relative: boolean; Base, EmSize, ExSize,
|
|
Default: integer): integer;
|
|
{given a length string, return the appropriate pixel value. Base is the
|
|
base value for percentage. EmSize, ExSize for units relative to the font.
|
|
Relative makes a numerical entry relative to Base.
|
|
Default returned if no match.}
|
|
var
|
|
V, PPI: double;
|
|
S1, S2: string;
|
|
I, J: integer;
|
|
begin
|
|
PPI := Screen.PixelsPerInch;
|
|
Val(Str, V, I);
|
|
J := Pos('e', Str); {'e' would be legal for Val but not for us}
|
|
if (J <> 0) and (I > J) then
|
|
I := J;
|
|
if I > 0 then
|
|
begin
|
|
S1 := Copy(Str, 1, I-1);
|
|
S2 := Trim(Copy(Str, I, Length(Str)-I+1));
|
|
end
|
|
else
|
|
begin
|
|
S1 := Str;
|
|
S2 := '';
|
|
end;
|
|
{S1 has the number, S2 the units}
|
|
Val(S1, V, I);
|
|
if S2 = 'in' then
|
|
V := PPI*V
|
|
else if S2 = 'cm' then
|
|
V := PPI*V/2.54
|
|
else if S2 = 'mm' then
|
|
V := PPI*V/25.4
|
|
else if S2 = 'pt' then
|
|
V := V*PPI/72.0
|
|
else if S2 = 'px' then
|
|
else if S2 = 'pc' then
|
|
V := V*PPI/6.0
|
|
else if S2 = 'em' then
|
|
V := V*EmSize
|
|
else if S2 = 'ex' then
|
|
V := V*ExSize
|
|
else if S2 = '%' then
|
|
V := V*Base/100.0
|
|
else if S2 = '' then
|
|
if Relative then
|
|
V := V*Base {relative}
|
|
else
|
|
V := V {same as pixels, at least for margins}
|
|
else V := Default;
|
|
Result := Round(V);
|
|
end;
|
|
|
|
Initialization
|
|
|
|
SortColors;
|
|
|
|
Finalization
|
|
|
|
ColorStrings.Free;
|
|
|
|
end.
|
|
|
|
|