Files
core/ASCOfficeHtmlFile_SaxHtmlParser/HTMLReaderLib/LiteHTMLAttributes.h

2031 lines
59 KiB
C++
Raw Blame History

#pragma once
//#include "LiteHTMLCommon.h"
#include "LiteHTMLEntityResolver.h"
#pragma warning(push, 4)
#pragma warning (disable : 4290) // C++ Exception Specification ignored
#pragma warning (disable : 4127) // Disable warning C4127: conditional expression is constant
#pragma warning (disable : 4239) // Disable warning C4239: nonstandard extension used : 'initializing' : conversion from 'CLiteHTMLAttributes' to 'CLiteHTMLAttributes &'
class CLiteHTMLAttributes; // forward declaration
/**
* CLiteHTMLElemAttr
*/
class CLiteHTMLElemAttr
{
// Friends
friend class CLiteHTMLAttributes;
// Constructors
public:
CLiteHTMLElemAttr(LPCTSTR lpszAttribName = NULL,
LPCTSTR lpszAttribValue = NULL)
{
Init();
m_strAttrName = lpszAttribName;
m_strAttrValue = lpszAttribValue;
}
CLiteHTMLElemAttr(const CLiteHTMLElemAttr &rSource)
{
Init();
m_strAttrName = rSource.m_strAttrName;
m_strAttrValue = rSource.m_strAttrValue;
}
// Initialization Helpers
private:
static void Init(void)
{
if (_namedColors.GetCount())
return;
/** 28 system colors */
_namedColors[_T("activeborder")] = (COLORREF)0x8000000A;
_namedColors[_T("activecaption")] = (COLORREF)0x80000002;
_namedColors[_T("appworkspace")] = (COLORREF)0x8000000C;
_namedColors[_T("background")] = (COLORREF)0x80000001;
_namedColors[_T("buttonface")] = (COLORREF)0x8000000F;
_namedColors[_T("buttonhighlight")] = (COLORREF)0x80000014;
_namedColors[_T("buttonshadow")] = (COLORREF)0x80000010;
_namedColors[_T("buttontext")] = (COLORREF)0x80000012;
_namedColors[_T("captiontext")] = (COLORREF)0x80000009;
_namedColors[_T("graytext")] = (COLORREF)0x80000011;
_namedColors[_T("highlight")] = (COLORREF)0x8000000D;
_namedColors[_T("highlighttext")] = (COLORREF)0x8000000E;
_namedColors[_T("inactiveborder")] = (COLORREF)0x8000000B;
_namedColors[_T("inactivecaption")] = (COLORREF)0x80000003;
_namedColors[_T("inactivecaptiontext")] = (COLORREF)0x80000013;
_namedColors[_T("infobackground")] = (COLORREF)0x80000018;
_namedColors[_T("infotext")] = (COLORREF)0x80000017;
_namedColors[_T("menu")] = (COLORREF)0x80000004;
_namedColors[_T("menutext")] = (COLORREF)0x80000007;
_namedColors[_T("scrollbar")] = (COLORREF)0x80000000;
_namedColors[_T("threeddarkshadow")] = (COLORREF)0x80000015;
_namedColors[_T("threedface")] = (COLORREF)0x8000000F;
_namedColors[_T("threedhighlight")] = (COLORREF)0x80000014;
_namedColors[_T("threedlightshadow")] = (COLORREF)0x80000016;
_namedColors[_T("threedshadow")] = (COLORREF)0x80000010;
_namedColors[_T("window")] = (COLORREF)0x80000005;
_namedColors[_T("windowframe")] = (COLORREF)0x80000006;
_namedColors[_T("windowtext")] = (COLORREF)0x80000008;
/** 16 basic colors */
_namedColors[_T("black")] = RGB(0x00, 0x00, 0x00);
_namedColors[_T("gray")] = RGB(0x80, 0x80, 0x80);
_namedColors[_T("silver")] = RGB(0xC0, 0xC0, 0xC0);
_namedColors[_T("white")] = RGB(0xFF, 0xFF, 0xFF);
_namedColors[_T("yellow")] = RGB(0xFF, 0xFF, 0x00);
_namedColors[_T("olive")] = RGB(0x80, 0x80, 0x00);
_namedColors[_T("red")] = RGB(0xFF, 0x00, 0x00);
_namedColors[_T("maroon")] = RGB(0x80, 0x00, 0x00);
_namedColors[_T("fuchsia")] = RGB(0xFF, 0x00, 0xFF);
_namedColors[_T("purple")] = RGB(0x80, 0x00, 0x80);
_namedColors[_T("blue")] = RGB(0x00, 0x00, 0xFF);
_namedColors[_T("navy")] = RGB(0x00, 0x00, 0x80);
_namedColors[_T("aqua")] = RGB(0x00, 0xFF, 0xFF);
_namedColors[_T("teal")] = RGB(0x00, 0x80, 0x80);
_namedColors[_T("lime")] = RGB(0x00, 0xFF, 0x00);
_namedColors[_T("green")] = RGB(0x00, 0x80, 0xFF);
/** additional named colors */
_namedColors[_T("darkolivegreen")] = RGB(0x55, 0x6B, 0x2F);
_namedColors[_T("olivedrab")] = RGB(0x6B, 0x8E, 0x23);
_namedColors[_T("yellowgreen")] = RGB(0x9A, 0xCD, 0x32);
_namedColors[_T("lawngreen")] = RGB(0x7C, 0xFC, 0x00);
_namedColors[_T("chartreuse")] = RGB(0x7F, 0xFF, 0x00);
_namedColors[_T("greenyellow")] = RGB(0xAD, 0xFF, 0x2F);
_namedColors[_T("palegreen")] = RGB(0x98, 0xFB, 0x98);
_namedColors[_T("lightgreen")] = RGB(0x90, 0xEE, 0x90);
_namedColors[_T("darkgreen")] = RGB(0x00, 0x64, 0x00);
_namedColors[_T("forestgreen")] = RGB(0x22, 0x8B, 0x22);
_namedColors[_T("seagreen")] = RGB(0x2E, 0x8B, 0x57);
_namedColors[_T("mediumseagreen")] = RGB(0x3C, 0xB3, 0x71);
_namedColors[_T("limegreen")] = RGB(0x32, 0xCD, 0x32);
_namedColors[_T("darkseagreen")] = RGB(0x8F, 0xBC, 0x8B);
_namedColors[_T("springgreen")] = RGB(0x00, 0xFF, 0x7F);
_namedColors[_T("mediumspringgreen")] = RGB(0x00, 0xFA, 0x99);
_namedColors[_T("darkslategray")] = RGB(0x2F, 0x4F, 0x4F);
_namedColors[_T("darkcyan")] = RGB(0x00, 0x8B, 0x8B);
_namedColors[_T("cadetblue")] = RGB(0x5F, 0x9E, 0xA0);
_namedColors[_T("lightseagreen")] = RGB(0x20, 0xB2, 0xAA);
_namedColors[_T("mediumaquamarine")] = RGB(0x66, 0xCD, 0xAA);
_namedColors[_T("turquoise")] = RGB(0x40, 0xE0, 0xD0);
_namedColors[_T("aquamarine")] = RGB(0x7F, 0xFF, 0xD4);
_namedColors[_T("paleturquoise")] = RGB(0xAF, 0xEE, 0xEE);
_namedColors[_T("slategray")] = RGB(0x70, 0x80, 0x90);
_namedColors[_T("lightslategray")] = RGB(0x77, 0x88, 0x99);
_namedColors[_T("steelblue")] = RGB(0x46, 0x82, 0xB4);
_namedColors[_T("deepskyblue")] = RGB(0x00, 0xBF, 0xFF);
_namedColors[_T("darkturquoise")] = RGB(0x00, 0xCE, 0xD1);
_namedColors[_T("mediumturquoise")] = RGB(0x48, 0xD1, 0xCC);
_namedColors[_T("powderblue")] = RGB(0xB0, 0xE0, 0xE6);
_namedColors[_T("lightcyan")] = RGB(0xE0, 0xFF, 0xFF);
_namedColors[_T("darkblue")] = RGB(0x00, 0x00, 0x8B);
_namedColors[_T("mediumblue")] = RGB(0x00, 0x00, 0xCD);
_namedColors[_T("royalblue")] = RGB(0x41, 0x69, 0xe1);
_namedColors[_T("dodgerblue")] = RGB(0x1E, 0x90, 0xFF);
_namedColors[_T("cornflowerblue")] = RGB(0x64, 0x95, 0xED);
_namedColors[_T("skyblue")] = RGB(0x87, 0xCE, 0xEB);
_namedColors[_T("lightskyblue")] = RGB(0x87, 0xCE, 0xFA);
_namedColors[_T("lightblue")] = RGB(0xAD, 0xD8, 0xE6);
_namedColors[_T("midnightblue")] = RGB(0x19, 0x19, 0x70);
_namedColors[_T("darkslateblue")] = RGB(0x48, 0x3D, 0x8B);
_namedColors[_T("blueviolet")] = RGB(0x8A, 0x2B, 0xE2);
_namedColors[_T("slateblue")] = RGB(0x6A, 0x5A, 0xCD);
_namedColors[_T("mediumslateblue")] = RGB(0x7B, 0x68, 0xEE);
_namedColors[_T("mediumpurple")] = RGB(0x93, 0x70, 0xDB);
_namedColors[_T("lightsteelblue")] = RGB(0xB0, 0xC4, 0xDE);
_namedColors[_T("lavender")] = RGB(0xE6, 0xE6, 0xFA);
_namedColors[_T("indigo")] = RGB(0x4B, 0x00, 0x82);
_namedColors[_T("darkviolet")] = RGB(0x94, 0x00, 0xD3);
_namedColors[_T("darkorchid")] = RGB(0x99, 0x32, 0xCC);
_namedColors[_T("mediumorchid")] = RGB(0xBA, 0x55, 0xD3);
_namedColors[_T("orchid")] = RGB(0xDA, 0x70, 0xD6);
_namedColors[_T("violet")] = RGB(0xEE, 0x82, 0xEE);
_namedColors[_T("plum")] = RGB(0xDD, 0xA0, 0xDD);
_namedColors[_T("thistle")] = RGB(0xD8, 0xDF, 0xD8);
_namedColors[_T("darkmagenta")] = RGB(0x8B, 0x00, 0x8B);
_namedColors[_T("mediumvioletred")] = RGB(0xC7, 0x15, 0x85);
_namedColors[_T("deeppink")] = RGB(0xFF, 0x14, 0x93);
_namedColors[_T("palmvioletred")] = RGB(0xDB, 0x70, 0x93);
_namedColors[_T("hotpink")] = RGB(0xFF, 0x69, 0xB4);
_namedColors[_T("lightpink")] = RGB(0xFF, 0xB6, 0xC1);
_namedColors[_T("pink")] = RGB(0xFF, 0xC0, 0xCB);
_namedColors[_T("mistyrose")] = RGB(0xFF, 0xE4, 0xE1);
_namedColors[_T("brown")] = RGB(0xA5, 0x2A, 0x2A);
_namedColors[_T("indianred")] = RGB(0xCD, 0x5C, 0x5C);
_namedColors[_T("rosybrown")] = RGB(0xBC, 0x8F, 0x8F);
_namedColors[_T("salmon")] = RGB(0xFA, 0x80, 0x72);
_namedColors[_T("lightcoral")] = RGB(0xF0, 0x80, 0x80);
_namedColors[_T("darksalmon")] = RGB(0xE9, 0x96, 0x7A);
_namedColors[_T("lightsalmon")] = RGB(0xFF, 0xA0, 0x7A);
_namedColors[_T("peachpuff")] = RGB(0xFF, 0xDA, 0xB9);
_namedColors[_T("darkred")] = RGB(0x8B, 0x00, 0x00);
_namedColors[_T("firebrick")] = RGB(0xB2, 0x22, 0x22);
_namedColors[_T("crimson")] = RGB(0xDC, 0x14, 0x3C);
_namedColors[_T("orangered")] = RGB(0xFF, 0x45, 0x00);
_namedColors[_T("tomato")] = RGB(0xFF, 0x63, 0x47);
_namedColors[_T("coral")] = RGB(0xFF, 0x7F, 0x50);
_namedColors[_T("wheat")] = RGB(0xF5, 0xDE, 0xB3);
_namedColors[_T("papayawhip")] = RGB(0xFF, 0xEF, 0xD5);
_namedColors[_T("sienna")] = RGB(0xA0, 0x52, 0x2D);
_namedColors[_T("chocolate")] = RGB(0xD2, 0x69, 0x1E);
_namedColors[_T("darkorange")] = RGB(0xFF, 0x8C, 0x00);
_namedColors[_T("sandybrown")] = RGB(0xF4, 0xA4, 0x60);
_namedColors[_T("orange")] = RGB(0xFF, 0xA5, 0x00);
_namedColors[_T("navajowhite")] = RGB(0xFF, 0xDE, 0xAD);
_namedColors[_T("moccasin")] = RGB(0xFF, 0xE4, 0xB5);
_namedColors[_T("saddlebrown")] = RGB(0x8B, 0x45, 0x13);
_namedColors[_T("peru")] = RGB(0xCD, 0x85, 0x3F);
_namedColors[_T("burlywood")] = RGB(0xDE, 0xB8, 0x87);
_namedColors[_T("tan")] = RGB(0xD2, 0xB4, 0x8C);
_namedColors[_T("bisque")] = RGB(0xFF, 0xE4, 0xC4);
_namedColors[_T("blanchedalmond")] = RGB(0xFF, 0xEB, 0xCD);
_namedColors[_T("antiquewhite")] = RGB(0xFA, 0xEB, 0xD7);
_namedColors[_T("darkgoldenrod")] = RGB(0xB8, 0x86, 0x0B);
_namedColors[_T("goldenrod")] = RGB(0xDA, 0xA5, 0x20);
_namedColors[_T("darkkhaki")] = RGB(0xBD, 0xB7, 0x6B);
_namedColors[_T("gold")] = RGB(0xFF, 0xD7, 0x00);
_namedColors[_T("khaki")] = RGB(0xF0, 0xE6, 0x8C);
_namedColors[_T("palegoldenrod")] = RGB(0xEE, 0xE8, 0xAA);
_namedColors[_T("lemonchiffon")] = RGB(0xFF, 0xFA, 0xCD);
_namedColors[_T("beige")] = RGB(0xF5, 0xF5, 0xDC);
_namedColors[_T("lightgoldenrodyellow")]= RGB(0xFA, 0xFA, 0xD2);
_namedColors[_T("lightyellow")] = RGB(0xFF, 0xFF, 0xE0);
_namedColors[_T("ivory")] = RGB(0xFF, 0xFF, 0x00);
_namedColors[_T("cornsilk")] = RGB(0xFF, 0xF8, 0xDC);
_namedColors[_T("oldlace")] = RGB(0xFD, 0xF5, 0xE6);
_namedColors[_T("florawhite")] = RGB(0xFF, 0xFA, 0xF0);
_namedColors[_T("honeydew")] = RGB(0xF0, 0xFF, 0xF0);
_namedColors[_T("mintcream")] = RGB(0xF5, 0xFF, 0xFA);
_namedColors[_T("azure")] = RGB(0xF0, 0xFF, 0xFF);
_namedColors[_T("ghostwhite")] = RGB(0xF8, 0xF8, 0xFF);
_namedColors[_T("linen")] = RGB(0xFA, 0xF0, 0xE6);
_namedColors[_T("seashell")] = RGB(0xFF, 0xF5, 0xEE);
_namedColors[_T("snow")] = RGB(0xFF, 0xFA, 0xFA);
_namedColors[_T("dimgray")] = RGB(0x69, 0x69, 0x69);
_namedColors[_T("darkgray")] = RGB(0xA9, 0xA9, 0xA9);
_namedColors[_T("lightgray")] = RGB(0xD3, 0xD3, 0xD3);
_namedColors[_T("gainsboro")] = RGB(0xDC, 0xDC, 0xDC);
_namedColors[_T("whitesmoke")] = RGB(0xF5, 0xF5, 0xF5);
_namedColors[_T("ghostwhite")] = RGB(0xF8, 0xF8, 0xFF);
_namedColors[_T("aliceblue")] = RGB(0xF0, 0xF8, 0xFF);
}
// Attributes
public:
/**
* @return name of this CLiteHTMLElemAttr
*/
CString getName(void) const
{ return (m_strAttrName); }
/**
* @return value of this CLiteHTMLElemAttr
*/
CString getValue(void) const
{ return (m_strAttrValue); }
/**
* Determines if the attribute value is a named color value
* @return true if attribute value is a named color, otherwise, false
*/
static bool isNamedColorValue (const CString &sValue)
{
if ( (sValue.GetLength()) && (::_istalpha(sValue[0])) )
{
COLORREF crTemp = _clrInvalid;
CString strKey(sValue);
strKey.MakeLower();
if (_namedColors.Lookup(strKey, crTemp))
return (true);
}
return (false);
}
bool isNamedColorValue(void) const
{
return isNamedColorValue (m_strAttrValue);
}
/**
* Determines if the attribute value is a named system color value
* @return true if value is a named system color, false otherwise
*/
static bool isSysColorValue (const CString &sValue)
{
if ( (sValue.GetLength()) && (::_istalpha(sValue[0])) )
{
COLORREF crTemp = _clrInvalid;
CString strKey (sValue);
strKey.MakeLower();
if (_namedColors.Lookup(strKey, crTemp))
return (crTemp >= 0x80000000 && crTemp <= 0x80000018);
}
return (false);
}
bool isSysColorValue(void) const
{
return isSysColorValue (m_strAttrValue);
}
/**
* Determines if the attribute value is a color value in
* hexadecimal format
* @return true if attribute value is a color value, otherwise, false
*/
static bool isHexColorValue (const CString &sValue)
{
// zero-length attribute value?
if (sValue.IsEmpty())
return (false);
if (sValue[0] == _T('#'))
{
if (sValue.GetLength() > 1)
{
for (int i = 1; i < sValue.GetLength(); i++)
{
if (!::_istxdigit(sValue[i]))
return (false);
}
return (true);
}
}
return (false);
}
bool isHexColorValue(void) const
{
return isHexColorValue (m_strAttrValue);
}
/**
* Determines if the attribute value contains a color reference
* @return true, if attribute value is color value, false otherwise.
*/
bool isColorValue(void) const
{ return (isNamedColorValue() || isHexColorValue()); }
/**
* Returns the color value of the attribute
* @return a COLORREF representing the color
*/
static COLORREF getColorValue (const CString& aValue)
{
COLORREF crTemp = _clrInvalid;
if (isNamedColorValue(aValue))
{
CString strKey(aValue);
strKey.MakeLower();
if (_namedColors.Lookup(strKey, crTemp))
{
// is this a system named color value?
if (crTemp >= 0x80000000 && crTemp <= 0x80000018)
crTemp = ::GetSysColor(crTemp & 0x7FFFFFFF);
}
}
else if (isHexColorValue(aValue))
crTemp = ::_tcstoul(aValue.Mid(1), NULL, 16);
return (crTemp);
}
COLORREF getColorValue(void) const
{
return getColorValue (m_strAttrValue);
}
/**
* Returns the RGB value of the attribute in hexadecimal format
* @return hexadecimal string representing the color value
*/
static CString getColorHexValue (const CString& aValue)
{
CString strColorHex;
if (isHexColorValue (aValue))
strColorHex = aValue;//aValue.Mid(1);
else
{
COLORREF crTemp = getColorValue(aValue);
if (crTemp != _clrInvalid)
strColorHex.Format(_T("#%02x%02x%02x"), crTemp & 0xff, (crTemp >> 8) & 0xff, (crTemp >> 16) & 0xff);
}
return (strColorHex);
}
CString getColorHexValue(void) const
{
return getColorHexValue (m_strAttrValue);
}
/**
* Checks to see if the attribute contains a percent value
* @return true if value is a percent value, otherwise, false
*/
bool isPercentValue(void) const
{ return (m_strAttrValue.Right(1) == _T("%") ? true : false); }
/**
* Returns a percent value of the attribute
* @return percentage value
*/
unsigned short getPercentValue(unsigned short max = _percentMax) const
{
ATLASSERT(max > 0);
if (!isPercentValue()) return (0);
unsigned short percentVal = (unsigned short)((short)*this);
return ((percentVal > max ? max : percentVal));
}
/**
* Parses a length value from the attribute/value
* and identifies its length unit also
*
* @param rUnit - this will receive the type of the length unit
*
* @return an integer value of the attribute
*/
enum LengthUnitsEnum { em, ex, px, per, in, cm, mm, pt, pc };
short getLengthValue(LengthUnitsEnum &rUnit) const
{
static const TCHAR _szUnits[][4] =
{
/** relative length units */
_T("em"), _T("ex"), _T("px"), _T("%"),
/** absolute length units */
_T("in"), _T("cm"), _T("mm"), _T("pt"), _T("pc")
};
if (m_strAttrValue.IsEmpty())
return (0);
int i;
for (i = 0; i < sizeof(_szUnits)/sizeof(_szUnits[0]); i++)
{
if (m_strAttrValue.Right(::lstrlen(_szUnits[i])). \
CompareNoCase(_szUnits[i]) == 0)
{
rUnit = (LengthUnitsEnum)i;
break;
}
}
if (i == sizeof(_szUnits)/sizeof(_szUnits[0]))
return (0);
return (*this);
}
// Operators
public:
/**
* Converts attribute value to bool
* @return true or false
*/
operator bool() const
{
if (!m_strAttrValue.CompareNoCase(_T("true")))
return (true);
if (!m_strAttrValue.CompareNoCase(_T("false")))
return (false);
return (((short)*this ? true : false));
}
/**
* Converts attribute value to BYTE (unsigned char)
* @return the left-most character of attribute value
*/
operator BYTE() const
{ return ((BYTE)(m_strAttrValue.GetLength() ? m_strAttrValue[0] : 0)); }
/**
* Converts attribute value to double
* @return 0.00 on failure, otherwise, a numeric value
*/
operator double() const
{ return (::_tcstod(m_strAttrValue, NULL)); }
/**
* Converts attribute value to signed short int
* @return 0 on failure, otherwise, an integer value
*/
operator short() const
{ return ((short)::_ttoi(m_strAttrValue)); }
/**
* @return attribute value
*/
operator LPCTSTR() const
{ return (m_strAttrValue); }
// Private Operations
public:
/**
* Sets the value of an attribute. Takes care of the following:
* 1. Ignores leading and trailing white-space characters
* 2. Replaces character entities with appropriate characters.
* 3. Ignores line feeds (LF).
* 4. Replaces each carriage-return (CR) or tab with a single space.
*
* @param lpszValue - new attribute value
*
*/
void putValue(LPCTSTR lpszValue)
{
ATLASSERT(AtlIsValidString(lpszValue));
m_strAttrValue = lpszValue;
// ignore leading white-spaces
m_strAttrValue.TrimLeft();
// ignore trailing white-spaces
m_strAttrValue.TrimRight();
// ignore line feeds
m_strAttrValue.Remove(_T('\n'));
// replace tab and carriage-return with a single space
m_strAttrValue.Replace(_T('\r'), _T(' '));
m_strAttrValue.Replace(_T('\t'), _T(' '));
/** resolve entity reference(s) */
/*
int iCurPos = -1, iParseLen = 0;
TCHAR chSubst = 0;
do
{
if ((iCurPos = m_strAttrValue.Find(_T('&'), ++iCurPos)) == -1)
break;
iParseLen = CLiteHTMLEntityResolver::resolveEntity(m_strAttrValue.Mid(iCurPos), chSubst);
if (iParseLen)
{
m_strAttrValue.Replace
(
m_strAttrValue.Mid(iCurPos, iParseLen),
CString(chSubst)
);
}
}
while (true);
*/
}
// Parsing Helpers
public:
// parses an attribute/value pair from the given string
UINT parseFromStr(LPCTSTR lpszString);
// Data Members
public:
static const COLORREF _clrInvalid; // an invalid color
static const unsigned short _percentMax; // maximum allowable percentage value
private:
typedef CAtlMap<CString, COLORREF> CNamedColors;
static CNamedColors _namedColors; // collection of named colors
CString m_strAttrName, // attribute name
m_strAttrValue; // attribute value
};
/**
* CLiteHTMLElemAttr::parseFromStr
*
* @param lpszString - string to parse
*
* @return number of TCHARs successfully parsed
*/
inline UINT CLiteHTMLElemAttr::parseFromStr(LPCTSTR lpszString)
{
ATLASSERT(AtlIsValidString(lpszString));
LPCTSTR lpszBegin = lpszString;
LPCTSTR lpszEnd;
TCHAR ch = 0;
// skip leading white-space characters
while (::_istspace(*lpszBegin))
lpszBegin = ::_tcsinc(lpszBegin);
// name doesn't begin with an alphabet?
if (!::_istalpha(*lpszBegin))
return (0U);
lpszEnd = lpszBegin;
do
{
// attribute name may contain letters (a-z, A-Z), digits (0-9),
// underscores '_', hyphen '-', colons ':', and periods '.'
if ( (!::_istalnum(*lpszEnd)) &&
(*lpszEnd != _T('-')) && (*lpszEnd != _T(':')) &&
(*lpszEnd != _T('_')) && (*lpszEnd != _T('.')) )
{
ATLASSERT(lpszEnd != lpszBegin);
// only white-space characters, a null-character, an
// equal-sign, a greater-than symbol, or a forward-slash
// can act as the separator between an attribute and its
// value
if (*lpszEnd == NULL ||
*lpszEnd == _T('=') ||
::_istspace(*lpszEnd) ||
*lpszEnd == _T('>') || *lpszEnd == _T('/'))
{
break;
}
return (0U); // any other character will fail parsing process
}
lpszEnd = ::_tcsinc(lpszEnd);
} while (true);
// extract attribute name
CString strAttrName(lpszBegin, int (lpszEnd - lpszBegin));
// skip spaces after attribute name
if (::_istspace(*lpszEnd))
{
lpszEnd = ::_tcsinc(lpszEnd);
}
if (*lpszEnd != _T('='))
{
m_strAttrName = strAttrName;
m_strAttrValue.Empty();
return (UINT) (lpszEnd - lpszString);
}
else
{
// skip white-space characters after equal-sign
// and the equal-sign itself
do {
lpszEnd = ::_tcsinc(lpszEnd);
} while (::_istspace(*lpszEnd));
lpszBegin = lpszEnd;
ch = *lpszEnd;
// is attribute value wrapped in quotes?
if (ch == _T('\'') || ch == _T('\"'))
{
lpszBegin = ::_tcsinc(lpszBegin); // skip quote symbol
do
{
lpszEnd = ::_tcsinc(lpszEnd);
}
// Loop until we find the same quote character that
// was used at the starting of the attribute value.
// Anything within these quotes is considered valid!
// NOTE that the entity references are resolved later.
while (*lpszEnd != NULL && *lpszEnd != ch);
}
// open attribute value i.e. not wrapped in quotes?
else
{
do
{
lpszEnd = ::_tcsinc(lpszEnd);
}
// loop until we find a tag ending delimeter or any
// white-space character, or until we reach at the
// end of the string buffer
while (*lpszEnd != NULL && !::_istspace(*lpszEnd) &&
!(*lpszEnd == _T('/') && *(lpszEnd + 1) != NULL && *(lpszEnd + 1) == _T('>')) // open atrtibute as ' type=text/javascript ' - '/' is not end of tag
&& *lpszEnd != _T('>'));
}
m_strAttrName = strAttrName;
if (lpszEnd == lpszBegin) // empty attribute value?
m_strAttrValue.Empty();
else
// use putValue() instead of direct assignment;
// this will automatically normalize data before
// assigning according to the specs and will
// also resolve entity references!!!
putValue(CString(lpszBegin, int (lpszEnd - lpszBegin)));
// calculate and return the count of characters successfully parsed
return (UINT (lpszEnd - lpszString) +
(ch == _T('\'') || ch == _T('\"') ? 1 : 0) );
}
return (0U);
}
/**
* CLiteHTMLAttributes
*/
class CLiteHTMLAttributes
{
// Construction/Destruction
public:
CLiteHTMLAttributes()
: m_parrAttrib(NULL)
{ }
/**
* @param bCopy - if true, this CLiteHTMLAttributes makes a copy
* of the encapsulated pointer. if false, this constructor takes
* ownership of the encapsulated pointer.
*
*/
CLiteHTMLAttributes(CLiteHTMLAttributes &rSource, bool bCopy = false)
: m_parrAttrib(NULL)
{
if (!bCopy)
{
m_parrAttrib = rSource.m_parrAttrib;
rSource.m_parrAttrib = NULL;
}
else
{
const int nElemCount = rSource.getCount();
if (nElemCount)
{
if ((m_parrAttrib = new CElemAttrArray) == NULL)
ATLTRACE2 ("Out of memory\n");
CLiteHTMLElemAttr *pItem = NULL;
m_parrAttrib->SetCount(nElemCount);
/** DEEP COPY BEGIN */
for (int iElem = 0; iElem < nElemCount; iElem++)
{
if ((pItem = new CLiteHTMLElemAttr(rSource[iElem])) == NULL)
{
removeAll();
ATLTRACE2 ("Out of memory\n");
return;
}
(*m_parrAttrib)[iElem] = pItem;
pItem = NULL;
}
/** DEEP COPY END */
}
}
}
virtual ~CLiteHTMLAttributes()
{ removeAll(); }
// Initialization
public:
// parses attribute/value pairs from the given string
UINT parseFromStr(LPCTSTR lpszString, long aLength);
// Attributes
public:
/**
* Returns the count of CLiteHTMLElemAttr items in this collection
* @return number of items
*/
int getCount(void) const
{
if (m_parrAttrib != NULL)
return (int) (m_parrAttrib->GetCount());
return (0);
}
/**
* Look up the index of an attribute given its name.
* If more than one attribute with the same name exist,
* this will return the index of the first match.
*
* @param lpszAttributeName - name of the attribute
*
* @return zero-based index of an attribute, or -1 if not found
*/
int getIndexFromName(LPCTSTR lpszAttributeName) const
{
ATLASSERT(AtlIsValidString(lpszAttributeName));
CLiteHTMLElemAttr *pItem = NULL;
for (int iElem = 0; iElem < getCount(); iElem++)
{
if ((pItem = (*m_parrAttrib)[iElem]) == NULL) // just in case
continue;
// perform a CASE-INSENSITIVE search
if (pItem->m_strAttrName.CompareNoCase(lpszAttributeName) == 0)
return (iElem);
}
return (-1);
}
/**
* Returns a CLiteHTMLElemAttr object given an attribute's index
*
* @return CLiteHTMLElemAttr containing the name and value of an attribute
*/
CLiteHTMLElemAttr operator[](int nIndex) const
{
if (!(nIndex >= 0 && nIndex < getCount()))
{
//ATLASSERT(FALSE);
return (CLiteHTMLElemAttr());
}
return ( *((*m_parrAttrib)[nIndex]) );
}
/**
* Returns a CLiteHTMLElemAttr object given an attribute name
*
* @return CLiteHTMLElemAttr containing the name and value of an attribute
*/
CLiteHTMLElemAttr operator[](LPCTSTR lpszIndex) const
{
ATLASSERT(AtlIsValidString(lpszIndex));
return ((*this)[getIndexFromName(lpszIndex)]);
}
/**
* Returns a CLiteHTMLElemAttr object given an attribute's index
*
* @return CLiteHTMLElemAttr containing the name and value of an attribute
* @since 1.0
* r
*/
CLiteHTMLElemAttr getAttribute(int nIndex) const
{ return ((*this)[nIndex]); }
/**
* Returns a CLiteHTMLElemAttr object given an attribute name
*
* @return CLiteHTMLElemAttr containing the name and value of an attribute
* @since 1.0
*/
CLiteHTMLElemAttr getAttribute(LPCTSTR lpszIndex) const
{
ATLASSERT(AtlIsValidString(lpszIndex));
return ((*this)[getIndexFromName(lpszIndex)]);
}
BOOL getRawAttribute(int nIndex, CLiteHTMLElemAttr** pAttr)
{
if (NULL == pAttr)
return FALSE;
if (nIndex < 0 || nIndex >= getCount())
{
*pAttr = NULL;
return FALSE;
}
*pAttr = m_parrAttrib->GetAt (nIndex);
return TRUE;
}
BOOL getRawAttribute(LPCTSTR sIndex, CLiteHTMLElemAttr** pAttr)
{
if (NULL == pAttr)
return FALSE;
int nIndex = getIndexFromName(sIndex);
if (nIndex < 0 || nIndex >= getCount())
{
*pAttr = NULL;
return FALSE;
}
*pAttr = m_parrAttrib->GetAt (nIndex);
return TRUE;
}
/**
* Returns the name of an attribute given its index
*
* @return name of an attribute
* @since 1.0
*/
CString getName(int nIndex) const
{ return ((*this)[nIndex].m_strAttrName); }
/**
* Returns the value of an attribute given its index
*
* @return value of an attribute
* @since 1.0
*/
CString getValue(int nIndex) const
{ return ((*this)[nIndex].m_strAttrValue); }
/**
* Returns the value of an attribute given its name
*
* @return value of an attribute
*/
CString getValueFromName(LPCTSTR lpszAttributeName) const
{ return ((*this)[lpszAttributeName].m_strAttrValue); }
// Operations
public:
/**
* Adds a new CLiteHTMLElemAttr item to the collection
*
* @param lpszName - attribute name (serves as the key to the item)
* @param lpszValue - attribute value
* @param bReplaceOld - If an item with the same name as specified
* by lpszName already exists in the collection, this
* parameter is used to determine whether to replace the
* existing item or add a new one
*
* @return pointer to a CLiteHTMLElemAttr
* @since 1.0
*/
CLiteHTMLElemAttr* addAttribute(LPCTSTR lpszName, LPCTSTR lpszValue)
{
ATLASSERT(AtlIsValidString(lpszName));
ATLASSERT(AtlIsValidString(lpszValue));
CLiteHTMLElemAttr *pItem = new CLiteHTMLElemAttr(lpszName, lpszValue);
if (pItem != NULL)
{
if (m_parrAttrib == NULL)
{
if ((m_parrAttrib = new CElemAttrArray) == NULL)
{
SAFE_DELETE_POINTER(pItem);
ATLTRACE2("(Error) CLiteHTMLAttributes::addAttribute: Out of memory.\n");
return (NULL);
}
}
ATLVERIFY(m_parrAttrib->Add(pItem) >= 0);
}
return (pItem);
}
/**
* Removes an CLiteHTMLElemAttr item from the collection
*
* @param lpszName - attribute to remove
*
* @return true if successful, false otherwise
* @since 1.0
*/
bool removeAttribute(int nIndex)
{
if (!(nIndex >= 0 && nIndex < getCount()))
return (false);
CLiteHTMLElemAttr *pItem = NULL;
ATLVERIFY(((*m_parrAttrib)[nIndex]) != NULL);
SAFE_DELETE_POINTER(pItem);
return (true);
}
/**
* Removes all CLiteHTMLElemAttr items from the collection
* @return true if successful, false otherwise
* @since 1.0
*/
bool removeAll(void)
{
CLiteHTMLElemAttr *pItem = NULL;
for (int iElem = 0; iElem < getCount(); iElem++)
{
ATLVERIFY((pItem = (*m_parrAttrib)[iElem]) != NULL);
SAFE_DELETE_POINTER(pItem);
}
SAFE_DELETE_POINTER(m_parrAttrib);
return (true);
}
// Data Members
private:
typedef ATL::CAtlArray <CLiteHTMLElemAttr*> CElemAttrArray;
CElemAttrArray *m_parrAttrib; // array of attributes/value pairs
};
/**
* CLiteHTMLAttributes::parseFromStr
*
* @param lpszString - string to parse. It can contain pairs such as:
*
* 1. NAME
* 2. NAME=VALUE
* 3. NAME='VALUE'
* 4. NAME="VALUE"
*
* NAME consist of letters, digits, underscores,
* colons, hyphens, and periods
*
* NOTE that white-spaces between NAME and equal-sign AND
* equal-sign and VALUE is allowed.
*
* @return number of TCHARs successfully parsed
* @since 1.0
*/
inline UINT CLiteHTMLAttributes::parseFromStr(LPCTSTR lpszString, long aLength)
{
ATLASSERT(AtlIsValidString(lpszString));
CElemAttrArray *pcoll = NULL;
CLiteHTMLElemAttr oElemAttr;
const UINT nStrLen = (UINT) (-1 == aLength) ? ::_tcslen(lpszString) : aLength;
UINT nRetVal = 0U,
nTemp = 0U;
do
{
// try to parse an attribute/value
// pair from the rest of the string
if (!(nTemp = oElemAttr.parseFromStr(&lpszString[nRetVal])))
{
if (!nRetVal)
goto LError;
break;
}
// collection has not been instantiated until now?
if (pcoll == NULL)
{
// instantiate now
if ((pcoll = new CElemAttrArray) == NULL)
// out of memory?
{
ATLTRACE2("(Error) CLiteHTMLAttributes::parseFromStr: Out of memory.\n");
goto LError;
}
}
// add attribute/value pair to collection
if (pcoll->Add(new CLiteHTMLElemAttr(oElemAttr)) < 0)
goto LError;
// advance seek pointer
nRetVal += nTemp;
}
// do we still have something in the buffer to parse?
while (nRetVal < nStrLen);
// collection was never instantiated?
if (pcoll == NULL)
goto LError;
// collection is empty?
if (0 == pcoll->GetCount())
goto LError;
// current collection could not be emptied?
if (!removeAll())
goto LError;
m_parrAttrib = pcoll;
pcoll = NULL;
goto LCleanExit; // success!
LError:
SAFE_DELETE_POINTER(pcoll);
nRetVal = 0U;
LCleanExit:
return (nRetVal);
}
/**
* CLiteHTMLElemStyleAttr
style string has form: 'name1=value1 value2 value3; name2 = value4;'
*/
class CLiteHTMLElemStyleAttr
{
private:
typedef CAtlMap<CString, CString> CStyleMap;
CStyleMap m_aStyleAttrs;
double m_dParentFontSize;
protected:
BOOL ParseMarginTag (CString &aValues)
{
CAtlArray<CString> arrValues;
SpaceSeparatedValuesToArray (aValues, arrValues);
const int nArrayLength = arrValues.GetCount ();
if (nArrayLength < 1 || nArrayLength > 4)
return FALSE;
switch (nArrayLength)
{
case 1:
{
CString sValue = arrValues[0];
m_aStyleAttrs.SetAt (_T("margin-top"), sValue);
m_aStyleAttrs.SetAt (_T("margin-left"), sValue);
m_aStyleAttrs.SetAt (_T("margin-right"), sValue);
m_aStyleAttrs.SetAt (_T("margin-bottom"), sValue);
}
break;
case 2:
{
CString sValueTop = arrValues[0];
CString sValueLeft = arrValues[1];
m_aStyleAttrs.SetAt (_T("margin-top"), sValueTop);
m_aStyleAttrs.SetAt (_T("margin-left"), sValueLeft);
m_aStyleAttrs.SetAt (_T("margin-right"), sValueLeft);
m_aStyleAttrs.SetAt (_T("margin-bottom"), sValueTop);
}
break;
case 3:
{
CString sValueTop = arrValues[0];
CString sValueLeft = arrValues[1];
CString sValueBottom = arrValues[2];
m_aStyleAttrs.SetAt (_T("margin-top"), sValueTop);
m_aStyleAttrs.SetAt (_T("margin-left"), sValueLeft);
m_aStyleAttrs.SetAt (_T("margin-right"), sValueLeft);
m_aStyleAttrs.SetAt (_T("margin-bottom"), sValueBottom);
}
break;
case 4:
{
CString sValueTop = arrValues[0];
CString sValueRight = arrValues[1];
CString sValueBottom = arrValues[2];
CString sValueLeft = arrValues[3];
m_aStyleAttrs.SetAt (_T("margin-top"), sValueTop);
m_aStyleAttrs.SetAt (_T("margin-left"), sValueLeft);
m_aStyleAttrs.SetAt (_T("margin-right"), sValueRight);
m_aStyleAttrs.SetAt (_T("margin-bottom"), sValueBottom);
}
break;
}
return TRUE;
}
BOOL ParsePaddingTag (CString &aValues)
{
CAtlArray<CString> arrValues;
SpaceSeparatedValuesToArray (aValues, arrValues);
const int nArrayLength = arrValues.GetCount ();
if (nArrayLength < 1 || nArrayLength > 4)
return FALSE;
switch (nArrayLength)
{
case 1:
{
CString sValue = arrValues[0];
m_aStyleAttrs.SetAt (_T("padding-top"), sValue);
m_aStyleAttrs.SetAt (_T("padding-left"), sValue);
m_aStyleAttrs.SetAt (_T("padding-right"), sValue);
m_aStyleAttrs.SetAt (_T("padding-bottom"), sValue);
}
break;
case 2:
{
CString sValueTop = arrValues[0];
CString sValueLeft = arrValues[1];
m_aStyleAttrs.SetAt (_T("padding-top"), sValueTop);
m_aStyleAttrs.SetAt (_T("padding-left"), sValueLeft);
m_aStyleAttrs.SetAt (_T("padding-right"), sValueLeft);
m_aStyleAttrs.SetAt (_T("padding-bottom"), sValueTop);
}
break;
case 3:
{
CString sValueTop = arrValues[0];
CString sValueLeft = arrValues[1];
CString sValueBottom = arrValues[2];
m_aStyleAttrs.SetAt (_T("padding-top"), sValueTop);
m_aStyleAttrs.SetAt (_T("padding-left"), sValueLeft);
m_aStyleAttrs.SetAt (_T("padding-right"), sValueLeft);
m_aStyleAttrs.SetAt (_T("padding-bottom"), sValueBottom);
}
break;
case 4:
{
CString sValueTop = arrValues[0];
CString sValueRight = arrValues[1];
CString sValueBottom = arrValues[2];
CString sValueLeft = arrValues[3];
m_aStyleAttrs.SetAt (_T("padding-top"), sValueTop);
m_aStyleAttrs.SetAt (_T("padding-left"), sValueLeft);
m_aStyleAttrs.SetAt (_T("padding-right"), sValueRight);
m_aStyleAttrs.SetAt (_T("padding-bottom"), sValueBottom);
}
break;
}
return TRUE;
}
BOOL ParseBorderColorTag (CString &aValues)
{
CAtlArray<CString> arrValues;
SpaceSeparatedValuesToArray (aValues, arrValues);
const int nArrayLength = arrValues.GetCount ();
if (nArrayLength < 1 || nArrayLength > 4)
return FALSE;
switch (nArrayLength)
{
case 1:
{
CString sValue = arrValues[0];
CString sNewValue = CLiteHTMLElemAttr::getColorHexValue (sValue);
if (sNewValue.IsEmpty())
sNewValue = sValue;
m_aStyleAttrs.SetAt (_T("border-left-color"), sNewValue);
m_aStyleAttrs.SetAt (_T("border-top-color"), sNewValue);
m_aStyleAttrs.SetAt (_T("border-right-color"), sNewValue);
m_aStyleAttrs.SetAt (_T("border-bottom-color"), sNewValue);
}
break;
case 2:
{
CString sValueTop = arrValues[0];
CString sNewValueTop = CLiteHTMLElemAttr::getColorHexValue (sValueTop);
if (sNewValueTop.IsEmpty())
sNewValueTop = sValueTop;
CString sValueLeft = arrValues[1];
CString sNewValueLeft = CLiteHTMLElemAttr::getColorHexValue (sValueLeft);
if (sNewValueLeft.IsEmpty())
sNewValueLeft = sValueLeft;
m_aStyleAttrs.SetAt (_T("border-left-color"), sNewValueLeft);
m_aStyleAttrs.SetAt (_T("border-top-color"), sNewValueTop);
m_aStyleAttrs.SetAt (_T("border-right-color"), sNewValueLeft);
m_aStyleAttrs.SetAt (_T("border-bottom-color"), sNewValueTop);
}
break;
case 3:
{
CString sValueTop = arrValues[0];
CString sNewValueTop = CLiteHTMLElemAttr::getColorHexValue (sValueTop);
if (sNewValueTop.IsEmpty())
sNewValueTop = sValueTop;
CString sValueLeft = arrValues[1];
CString sNewValueLeft = CLiteHTMLElemAttr::getColorHexValue (sValueLeft);
if (sNewValueLeft.IsEmpty())
sNewValueLeft = sValueLeft;
CString sValueBottom = arrValues[2];
CString sNewValueBottom = CLiteHTMLElemAttr::getColorHexValue (sValueBottom);
if (sNewValueBottom.IsEmpty())
sNewValueBottom = sValueBottom;
m_aStyleAttrs.SetAt (_T("border-left-color"), sNewValueLeft);
m_aStyleAttrs.SetAt (_T("border-top-color"), sNewValueTop);
m_aStyleAttrs.SetAt (_T("border-right-color"), sNewValueLeft);
m_aStyleAttrs.SetAt (_T("border-bottom-color"), sNewValueBottom);
}
break;
case 4:
{
CString sValueTop = arrValues[0];
CString sNewValueTop = CLiteHTMLElemAttr::getColorHexValue (sValueTop);
if (sNewValueTop.IsEmpty())
sNewValueTop = sValueTop;
CString sValueRight = arrValues[1];
CString sNewValueRight = CLiteHTMLElemAttr::getColorHexValue (sValueRight);
if (sNewValueRight.IsEmpty())
sNewValueRight = sValueRight;
CString sValueBottom = arrValues[2];
CString sNewValueBottom = CLiteHTMLElemAttr::getColorHexValue (sValueBottom);
if (sNewValueBottom.IsEmpty())
sNewValueBottom = sValueBottom;
CString sValueLeft = arrValues[3];
CString sNewValueLeft = CLiteHTMLElemAttr::getColorHexValue (sValueLeft);
if (sNewValueLeft.IsEmpty())
sNewValueLeft = sValueLeft;
m_aStyleAttrs.SetAt (_T("border-left-color"), sNewValueLeft);
m_aStyleAttrs.SetAt (_T("border-top-color"), sNewValueTop);
m_aStyleAttrs.SetAt (_T("border-right-color"), sNewValueRight);
m_aStyleAttrs.SetAt (_T("border-bottom-color"), sNewValueBottom);
}
break;
}
return TRUE;
}
BOOL ParseBackgroundTag (CString &aValues)
{
// example: body {background:#ffffff url('img_tree.png') no-repeat right top;}
// background-color
// background-image
// background-repeat
// background-attachment
// background-position
CAtlArray<CString> arrValues;
SpaceSeparatedValuesToArray (aValues, arrValues);
const int nArrayLength = arrValues.GetCount ();
int nCurrentElement = 0;
if (nCurrentElement >= nArrayLength)
return FALSE;
CString sCurrentElement = arrValues[nCurrentElement].MakeLower();
if (0 != sCurrentElement.Find (_T("url")) && (sCurrentElement != _T("none"))) // if first element is not color, but image
{
m_aStyleAttrs.SetAt (_T("background-color"), arrValues[nCurrentElement]);
++nCurrentElement;
}
if (nCurrentElement >= nArrayLength)
return TRUE;
sCurrentElement = arrValues[nCurrentElement].MakeLower();
if (0 == sCurrentElement.Find (_T("url")))
{
m_aStyleAttrs.SetAt (_T("background-image"), arrValues[nCurrentElement]);
++nCurrentElement;
}
if (nCurrentElement >= nArrayLength)
return TRUE;
sCurrentElement = arrValues[nCurrentElement].MakeLower();
if (sCurrentElement == _T("repeat-x") || sCurrentElement == _T("repeat-y") || sCurrentElement == _T("no-repeat"))
{
m_aStyleAttrs.SetAt (_T("background-repeat"), arrValues[nCurrentElement]);
++nCurrentElement;
}
if (nCurrentElement >= nArrayLength)
return TRUE;
sCurrentElement = arrValues[nCurrentElement].MakeLower();
if (sCurrentElement == _T("fixed") || sCurrentElement == _T("scroll") || sCurrentElement == _T("inherit") || sCurrentElement == _T("local"))
{
m_aStyleAttrs.SetAt (_T("background-attachment"), arrValues[nCurrentElement]);
++nCurrentElement;
}
if (nCurrentElement >= nArrayLength)
return TRUE;
sCurrentElement = arrValues[nCurrentElement].MakeLower();
m_aStyleAttrs.SetAt (_T("background-position"), arrValues[nCurrentElement]);
return TRUE;
}
BOOL ParseFontTag (CString &aValues)
{
// example: font:italic bold 12px/30px Georgia, serif;
// example: font:15px arial,sans-serif;
// font-style
// font-variant
// font-weight
// font-size/line-height
// font-family
CAtlArray<CString> arrValues;
SpaceSeparatedValuesToArray (aValues, arrValues);
const int nArrayLength = arrValues.GetCount ();
int nCurrentElement = 0;
// font-style
if ((nCurrentElement < nArrayLength)
&& (
0 == arrValues[nCurrentElement].CompareNoCase (_T("normal"))
|| 0 == arrValues[nCurrentElement].CompareNoCase (_T("italic"))
|| 0 == arrValues[nCurrentElement].CompareNoCase (_T("oblique"))
|| 0 == arrValues[nCurrentElement].CompareNoCase (_T("inherit"))
)
)
{
m_aStyleAttrs.SetAt (_T("font-style"), arrValues[nCurrentElement]);
++nCurrentElement;
}
// font-variant
if ((nCurrentElement < nArrayLength)
&& (
0 == arrValues[nCurrentElement].CompareNoCase (_T("normal"))
|| 0 == arrValues[nCurrentElement].CompareNoCase (_T("small-caps"))
|| 0 == arrValues[nCurrentElement].CompareNoCase (_T("inherit"))
)
)
{
m_aStyleAttrs.SetAt (_T("font-variant"), arrValues[nCurrentElement]);
++nCurrentElement;
}
// font-weight
if ((nCurrentElement < nArrayLength)
&& (
0 == arrValues[nCurrentElement].CompareNoCase (_T("normal"))
|| 0 == arrValues[nCurrentElement].CompareNoCase (_T("bold"))
|| 0 == arrValues[nCurrentElement].CompareNoCase (_T("bolder"))
|| 0 == arrValues[nCurrentElement].CompareNoCase (_T("lighter"))
|| 0 == arrValues[nCurrentElement].CompareNoCase (_T("100"))
|| 0 == arrValues[nCurrentElement].CompareNoCase (_T("200"))
|| 0 == arrValues[nCurrentElement].CompareNoCase (_T("300"))
|| 0 == arrValues[nCurrentElement].CompareNoCase (_T("400"))
|| 0 == arrValues[nCurrentElement].CompareNoCase (_T("500"))
|| 0 == arrValues[nCurrentElement].CompareNoCase (_T("600"))
|| 0 == arrValues[nCurrentElement].CompareNoCase (_T("700"))
|| 0 == arrValues[nCurrentElement].CompareNoCase (_T("800"))
|| 0 == arrValues[nCurrentElement].CompareNoCase (_T("900"))
|| 0 == arrValues[nCurrentElement].CompareNoCase (_T("inherit"))
)
)
{
m_aStyleAttrs.SetAt (_T("font-weight"), arrValues[nCurrentElement]);
++nCurrentElement;
}
// font-size/line-height (required)
if (nCurrentElement < nArrayLength)
{
// find 'line-height'
const CString sFontSizeLineHeight = arrValues[nCurrentElement];
int nLineHeightPos = sFontSizeLineHeight.Find('/');
if (-1 != nLineHeightPos)
{
const CString sLineHeight = sFontSizeLineHeight.Mid (nLineHeightPos + 1);
const CString sFontSize = sFontSizeLineHeight.Mid (0, nLineHeightPos);
if (!sLineHeight.IsEmpty())
m_aStyleAttrs.SetAt (_T("line-height"), sLineHeight);
if (!sFontSize.IsEmpty())
m_aStyleAttrs.SetAt (_T("font-size"), sLineHeight);
}
else
{
m_aStyleAttrs.SetAt (_T("font-size"), sFontSizeLineHeight);
}
++nCurrentElement;
}
// font-family (required)
if (nCurrentElement < nArrayLength)
{
//example: font-family:"Times New Roman",Georgia,Serif;
CString sFontFamily = arrValues[nCurrentElement];
++nCurrentElement;
for (; nCurrentElement < nArrayLength; ++nCurrentElement)
{
sFontFamily += _T(" ");
sFontFamily += arrValues[nCurrentElement];
}
if (!sFontFamily.IsEmpty())
m_aStyleAttrs.SetAt (_T("font-family"), sFontFamily);
}
return TRUE;
}
BOOL ParsePair (CString &sPair)
{
// an example of pair: "color:blue"
BOOL bRes = TRUE;
const int nValuePos = sPair.Find(':');
if (-1 == nValuePos)
return FALSE;
CString sName = sPair.Mid (0, nValuePos);
sName.Remove (' ');
CString sValue = sPair.Mid (nValuePos + 1);
sValue = sValue.Trim (' ');
// check some attributes and generate multiple attributes
if (0 == sName.CompareNoCase(_T("background")))
{
// parse background
ParseBackgroundTag (sValue);
}
if (0 == sName.CompareNoCase(_T("border-color")))
{
// parse border color
ParseBorderColorTag (sValue);
}
else if (0 == sName.CompareNoCase(_T("font")))
{
// parse font
ParseFontTag (sValue);
}
else if (0 == sName.CompareNoCase(_T("font-size")))
{
m_aStyleAttrs.SetAt(sName, sValue);
}
else if (0 == sName.CompareNoCase(_T("margin")))
{
ParseMarginTag (sValue);
}
else if (0 == sName.CompareNoCase(_T("padding")))
{
ParsePaddingTag (sValue);
}
else if (0 == sName.CompareNoCase(_T("line-height")))
{
ConvertValueToPx (sValue);
m_aStyleAttrs.SetAt(sName, sValue);
}
else if (
0 == sName.CompareNoCase(_T("color"))
|| 0 == sName.CompareNoCase (_T("border-left-color"))
|| 0 == sName.CompareNoCase (_T("border-right-color"))
|| 0 == sName.CompareNoCase (_T("border-top-color"))
|| 0 == sName.CompareNoCase (_T("border-bottom-color"))
|| 0 == sName.CompareNoCase (_T("background-color"))
)
{
CString sNewValue = CLiteHTMLElemAttr::getColorHexValue (sValue);
if (sNewValue.IsEmpty())
m_aStyleAttrs.SetAt(sName, sValue);
else
m_aStyleAttrs.SetAt(sName, sNewValue);
}
else
{
m_aStyleAttrs.SetAt(sName, sValue);
/*
// we can't convert all params to pixels - it may be in (%), but not depends from font-size of parent
CAtlArray<CString> aArray;
SpaceSeparatedValuesToArray (sValue, aArray);
CString sResultValue, sFormattedValue;
for (int nValue = 0; nValue < aArray.GetCount() - 1; nValue++)
{
sFormattedValue = aArray[nValue];
ConvertValueToPx (sFormattedValue);
sResultValue += sFormattedValue;
sResultValue += ' ';
}
sFormattedValue = aArray[aArray.GetCount() - 1];
ConvertValueToPx (sFormattedValue);
sResultValue += sFormattedValue;
m_aStyleAttrs.SetAt(sName, sResultValue);
*/
}
return bRes;
}
BOOL ParseStyleAttributes (const CString &aStyleString)
{
BOOL bRes = TRUE;
// an example of style string: "color:blue;text-align: center"
// find pair
int nPairPos = 0;
while (true)
{
const int nPos = aStyleString.Find(';', nPairPos);
if (-1 == nPos)
{
// last attribute
CString sPair = aStyleString.Mid (nPairPos);
bRes &= ParsePair (sPair);
break;
}
else
{
CString sPair = aStyleString.Mid (nPairPos, nPos - nPairPos);
nPairPos = nPos + 1; // points to start of pair (skip ';' character)
bRes &= ParsePair (sPair);
}
}
return bRes;
}
BOOL ParseAttributes (CLiteHTMLAttributes* pParent)
{
const int nAttrsCount = pParent->getCount();
for (int nAttr = 0; nAttr < nAttrsCount; nAttr++)
{
CString sName = pParent->getName(nAttr);
if (0 == sName.CompareNoCase(_T("style"))) // parse 'style'
{
// found style attribute
CString sValue = pParent->getValue (nAttr);
sValue.Remove('\n');
sValue.Remove('\r');
sValue.Remove('!');
sValue.Replace (_T("important"), _T(""));
return ParseStyleAttributes (sValue);
}
}
return FALSE;
}
BOOL SetDefaultValues ()
{
// set default values here
m_dParentFontSize = 16.0;
return TRUE;
}
BOOL AddFrom (const CStyleMap &aStyleAttrs)
{
POSITION pPos = aStyleAttrs.GetStartPosition ();
while (NULL != pPos)
{
const CStyleMap::CPair *pPair = aStyleAttrs.GetAt (pPos);
m_aStyleAttrs.SetAt (pPair->m_key, pPair->m_value);
aStyleAttrs.GetNext (pPos);
}
return TRUE;
}
BOOL CopyFrom (const CStyleMap &aStyleAttrs)
{
m_aStyleAttrs.RemoveAll ();
return AddFrom (aStyleAttrs);
}
public:
static CString GetStyleString (const CLiteHTMLAttributes &oAttrs)
{
const int nAttrsCount = oAttrs.getCount();
CString sValue;
for (int nAttr = 0; nAttr < nAttrsCount; nAttr++)
{
CString sName = oAttrs.getName(nAttr);
//CStringA sAttr; sAttr = sName; ATLTRACE2 ("Attr: %s\n", sAttr);
if (0 == sName.CompareNoCase(_T("style"))) // parse 'style'
{
// found style attribute
CString sTempValue = oAttrs.getValue (nAttr);
sTempValue.Remove('\n');
sTempValue.Remove('\r');
//sTempValue.Remove('!');
//sTempValue.Replace (_T("important"), _T(""));
sValue += sTempValue;
}
}
return sValue;
}
static CString GetStyleString (const CLiteHTMLAttributes *pAttrs)
{
if (NULL == pAttrs)
return _T("");
return GetStyleString (*pAttrs);
}
// convert size(width, height, max-width) 'auto' and '%' values to 'pt'-value (returns TRUE if converted)
static BOOL SizeValueToPt (CString& sValue, const int nDefaultValue)
{
BOOL bRes = FALSE;
if (0 == sValue.CompareNoCase(_T("auto")))
{
const double dValue = 72.0 / 96.0 * nDefaultValue;
sValue.Format( _T("%.2fpt"), dValue);
bRes = TRUE;
}
if (!bRes && (-1 != sValue.Find('%')))
{
sValue.Remove ('%');
const int nPerc = _ttoi (sValue.GetBuffer());
sValue.ReleaseBuffer();
sValue.Format( _T("%.2fpt"), double (nDefaultValue * 72.0 / 96.0 * (nPerc / 100.0)) );
bRes = TRUE;
}
return bRes;
}
static double ConvertFontSizeToPx (CString &sValue, const double dEmValue = 16.0)
{
double dValue (0);
// absolute
if (0 == sValue.CompareNoCase(_T("xx-small")))
{
dValue = 9.0;
sValue.Format( _T("%.3fpx"), dValue);
return dValue;
}
else if (0 == sValue.CompareNoCase(_T("x-small")))
{
dValue = 10.0;
sValue.Format( _T("%.3fpx"), dValue);
return dValue;
}
else if (0 == sValue.CompareNoCase(_T("small")))
{
dValue = 13.0;
sValue.Format( _T("%.3fpx"), dValue);
return dValue;
}
else if (0 == sValue.CompareNoCase(_T("medium"))) // default
{
dValue = 16.0;
sValue.Format( _T("%.3fpx"), dValue);
return dValue;
}
else if (0 == sValue.CompareNoCase(_T("large")))
{
dValue = 18.0;
sValue.Format( _T("%.3fpx"), dValue);
return dValue;
}
else if (0 == sValue.CompareNoCase(_T("x-large")))
{
dValue = 24.0;
sValue.Format( _T("%.3fpx"), dValue);
return dValue;
}
else if (0 == sValue.CompareNoCase(_T("xx-large")))
{
dValue = 32.0;
sValue.Format( _T("%.3fpx"), dValue);
return dValue;
}
else if (0 == sValue.CompareNoCase(_T("larger")))
{
dValue = dEmValue * 1.2;
sValue.Format( _T("%.3fpx"), dValue);
return dValue;
}
else if (0 == sValue.CompareNoCase(_T("smaller")))
{
dValue = dEmValue * 0.8;
sValue.Format( _T("%.3fpx"), dValue);
return dValue;
}
else if (0 == sValue.CompareNoCase(_T("inherit")))
{
sValue.Format( _T("%.3fpx"), dEmValue);
return dEmValue;
}
return ConvertValueToPx (sValue, dEmValue);
}
static double ConvertValueToPx (CString &sValue, const double dEmValue = 16.0)
{
// dEmValue - <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD> <20><><EFBFBD><EFBFBD>, <20><><EFBFBD>, <20><><EFBFBD> <20><><EFBFBD><EFBFBD> <20><><EFBFBD><EFBFBD><EFBFBD>, 1em
int nPos = -1;
double dValue = dEmValue;
if ( -1 != ( nPos = sValue.Find( _T("in") ) ) )
{
dValue = _tstof( sValue.Mid( 0, nPos ) );
dValue *= (2.54 / 0.0264);
sValue.Format( _T("%.3fpx"), dValue );
}
else if ( -1 != ( nPos = sValue.Find( _T("cm") ) ) )
{
dValue = _tstof( sValue.Mid( 0, nPos ) );
dValue /= 0.0264;
sValue.Format( _T("%.3fpx"), dValue );
}
else if ( -1 != ( nPos = sValue.Find( _T("mm") ) ) )
{
dValue = _tstof( sValue.Mid( 0, nPos ) );
dValue /= 0.00264;
sValue.Format( _T("%.3fpx"), dValue );
}
else if ( -1 != ( nPos = sValue.Find( _T("em") ) ) )
{
dValue = _tstof( sValue.Mid( 0, nPos ) );
dValue *= dEmValue;
sValue.Format( _T("%.3fpx"), dValue );
}
else if ( -1 != ( nPos = sValue.Find( _T("ex") ) ) )
{
// TO DO: <20><><EFBFBD><EFBFBD> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD> <20><> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD> (<28><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD> 'em')
}
else if ( -1 != ( nPos = sValue.Find( _T("pt") ) ) )
{
dValue = _tstof( sValue.Mid( 0, nPos ) );
dValue *= (2.54 / 72 / 0.0264);
sValue.Format( _T("%.3fpx"), dValue );
}
else if ( -1 != ( nPos = sValue.Find( _T("pc") ) ) )
{
dValue = _tstof( sValue.Mid( 0, nPos ) );
dValue *= (2.54 / 6 / 0.0264);
sValue.Format( _T("%.3fpx"), dValue );
}
else if ( -1 != ( nPos = sValue.Find( _T("%") ) ) )
{
dValue = _tstof( sValue.Mid( 0, nPos ) );
dValue *= (dEmValue / 100.0);
sValue.Format( _T("%.3fpx"), dValue );
}
else if ( -1 != ( nPos = sValue.Find( _T("px") ) ) )
{
dValue = _tstof( sValue.Mid( 0, nPos ) );
sValue.Format( _T("%.3fpx"), dValue );
}
return dValue;
}
// parse symbol-separated values to array
static void SymbolSeparatedValuesToArray (TCHAR aSymbol, const CString& sValue, CAtlArray<CString>& aValues, int nStartPos = 0)
{
int nPosStart = nStartPos; // start of value
while (true)
{
const int nPosEnd = sValue.Find(aSymbol, nPosStart); // symbol
if (-1 == nPosEnd)
{
// end of string
aValues.Add (sValue.Mid (nPosStart));
break;
}
else
{
// space found
aValues.Add(sValue.Mid (nPosStart, nPosEnd - nPosStart));
nPosStart = nPosEnd + 1; // skip symbol and continue search
}
}
}
// parse space-separated values to array
static void SpaceSeparatedValuesToArray (const CString& sValue, CAtlArray<CString>& aValues)
{
int nPosStart = 0; // start of value
while (nPosStart < sValue.GetLength() && sValue[nPosStart] == ' ')
{
++nPosStart; // spit spaces
}
SymbolSeparatedValuesToArray (' ', sValue, aValues, nPosStart);
}
public:
CLiteHTMLElemStyleAttr (CLiteHTMLAttributes &pParent)
{
SetDefaultValues ();
ParseAttributes (&pParent);
}
CLiteHTMLElemStyleAttr (const CString &sStyle)
{
SetDefaultValues ();
ParseStyleAttributes (sStyle);
}
// copy ctor
CLiteHTMLElemStyleAttr (CLiteHTMLElemStyleAttr &aObj)
{
CopyFrom (aObj.m_aStyleAttrs);
}
CLiteHTMLElemStyleAttr& operator = (const CLiteHTMLElemStyleAttr& aObj)
{
if (&aObj != this)
{
CopyFrom (aObj.m_aStyleAttrs);
m_dParentFontSize = aObj.m_dParentFontSize;
}
return *this;
}
CLiteHTMLElemStyleAttr& operator + (const CLiteHTMLElemStyleAttr& aObj)
{
if (&aObj != this)
{
AddFrom (aObj.m_aStyleAttrs);
}
return *this;
}
size_t getCount () const
{
return m_aStyleAttrs.GetCount();
}
CString getAttribute (CString sName) const
{
const CAtlMap<CString, CString>::CPair *pPair = m_aStyleAttrs.Lookup (sName);
return (NULL == pPair) ? _T("") : pPair->m_value;
}
void setAttribute (CString sName, CString sValue)
{
m_aStyleAttrs.SetAt (sName, sValue);
}
CString ToString () const
{
CString sRes = _T("");
POSITION pPos = m_aStyleAttrs.GetStartPosition ();
while (NULL != pPos)
{
const CAtlMap<CString, CString>::CPair *pPair = m_aStyleAttrs.GetAt (pPos);
sRes += pPair->m_key + _T(":") + pPair->m_value + _T(";");
m_aStyleAttrs.GetNext (pPos);
}
return sRes;
}
BOOL AddStyleString (const CString aStyle)
{
return ParseStyleAttributes (aStyle);
}
void SetParentFontSize (double aParentFontSize)
{
m_dParentFontSize = aParentFontSize;
}
BOOL ConvertAttributesToPx (const double dEmValue = 16.0)
{
// rework font attributes
CString sFontFamily = getAttribute(_T("font-family"));
CString sFontSize = getAttribute(_T("font-size"));
CString sFontStyle = getAttribute(_T("font-style"));
CString sFontVariant = getAttribute(_T("font-variant"));
CString sFontWeight = getAttribute(_T("font-weight"));
if (sFontFamily.IsEmpty())
{
//setAttribute (_T("font-family"), _T("inherit"));
}
if (sFontSize.IsEmpty())
{
sFontSize.Format (_T("%.3fpx"), dEmValue);
setAttribute (_T("font-size"), sFontSize);
}
if (sFontStyle.IsEmpty())
{
//setAttribute (_T("font-style"), _T("inherit"));
}
if (sFontVariant.IsEmpty())
{
//setAttribute (_T("font-variant"), _T("inherit"));
}
if (sFontWeight.IsEmpty())
{
//setAttribute (_T("font-weight"), _T("inherit"));
}
// translate to px
POSITION pPos = m_aStyleAttrs.GetStartPosition ();
while (NULL != pPos)
{
CAtlMap<CString, CString>::CPair *pPair = m_aStyleAttrs.GetAt (pPos);
if (_T("font-size") == pPair->m_key
/*
|| _T("margin-top") == pPair->m_key
|| _T("margin-bottom") == pPair->m_key
|| _T("margin-left") == pPair->m_key
|| _T("margin-right") == pPair->m_key*/
|| _T("text-indent") == pPair->m_key)
{
ConvertValueToPx (pPair->m_value, dEmValue);
}
m_aStyleAttrs.GetNext (pPos);
}
return TRUE;
}
// helpers
// margins
BOOL FillMargins ()
{
CString sMarginLeft = getAttribute(_T("margin-left"));
CString sMarginRight = getAttribute(_T("margin-right"));
CString sMarginTop = getAttribute(_T("margin-top"));
CString sMarginBottom = getAttribute(_T("margin-bottom"));
CString sMargin = getAttribute(_T("margin"));
CAtlArray<CString> aMargins;
if (!sMargin.IsEmpty())
{
// parse spece-splitted values
SpaceSeparatedValuesToArray (sMargin, aMargins);
}
else
return FALSE;
switch (aMargins.GetCount())
{
case 0:
{
ATLTRACE2 ("margin string is empty\n");
}
break;
break;
case 1:
{
if (sMarginTop.IsEmpty())
sMarginTop = aMargins[0];
if (sMarginRight.IsEmpty())
sMarginRight = sMarginTop;
if (sMarginBottom.IsEmpty())
sMarginBottom = sMarginTop;
if (sMarginLeft.IsEmpty())
sMarginLeft = sMarginTop;
}
break;
case 2:
{
if (sMarginTop.IsEmpty())
sMarginTop = aMargins[0];
if (sMarginRight.IsEmpty())
sMarginRight = sMarginTop;
if (sMarginBottom.IsEmpty())
sMarginBottom = aMargins[1];
if (sMarginLeft.IsEmpty())
sMarginLeft = sMarginRight;
}
case 3:
{
if (sMarginTop.IsEmpty())
sMarginTop = aMargins[0];
if (sMarginRight.IsEmpty())
sMarginRight = aMargins[1];
if (sMarginBottom.IsEmpty())
sMarginBottom = aMargins[2];
if (sMarginLeft.IsEmpty())
sMarginLeft = sMarginRight;
}
break;
case 4: // 4 and more?
default:
{
if (sMarginTop.IsEmpty())
sMarginTop = aMargins[0];
if (sMarginRight.IsEmpty())
sMarginRight = aMargins[1];
if (sMarginBottom.IsEmpty())
sMarginBottom = aMargins[2];
if (sMarginLeft.IsEmpty())
sMarginLeft = aMargins[3];
}
break;
}
if (0 != aMargins.GetCount())
{
SizeValueToPt (sMarginTop, 0);
SizeValueToPt (sMarginRight, 0);
SizeValueToPt (sMarginBottom, 0);
SizeValueToPt (sMarginLeft, 0);
setAttribute (_T("margin-top"), sMarginTop);
setAttribute (_T("margin-right"), sMarginRight);
setAttribute (_T("margin-bottom"), sMarginBottom);
setAttribute (_T("margin-left"), sMarginLeft);
}
return TRUE;
}
};
#pragma warning (default : 4290)
#pragma warning (default : 4127)
#pragma warning (default : 4239) // Disable warning C4239: nonstandard extension used : 'initializing' : conversion from 'CLiteHTMLAttributes' to 'CLiteHTMLAttributes &'
#pragma warning(pop)