blob: 14ed101500fb49662f8a67551636e08c0f81c511 [file] [log] [blame]
// Copyright 2014 PDFium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com
#include "xfa/fde/css/fde_cssdeclaration.h"
#include "core/fxcrt/fx_ext.h"
#include "third_party/base/ptr_util.h"
CFDE_CSSDeclaration::CFDE_CSSDeclaration() {}
CFDE_CSSDeclaration::~CFDE_CSSDeclaration() {}
CFDE_CSSValue* CFDE_CSSDeclaration::GetProperty(FDE_CSSProperty eProperty,
bool& bImportant) const {
for (const auto& p : properties_) {
if (p->eProperty == eProperty) {
bImportant = p->bImportant;
return p->pValue.Get();
}
}
return nullptr;
}
const FX_WCHAR* CFDE_CSSDeclaration::CopyToLocal(
const FDE_CSSPropertyArgs* pArgs,
const FX_WCHAR* pszValue,
int32_t iValueLen) {
ASSERT(iValueLen > 0);
std::unordered_map<uint32_t, FX_WCHAR*>* pCache = pArgs->pStringCache;
uint32_t key = 0;
if (pCache) {
key = FX_HashCode_GetW(CFX_WideStringC(pszValue, iValueLen), false);
auto it = pCache->find(key);
if (it != pCache->end())
return it->second;
}
FX_WCHAR* psz = FX_Alloc(FX_WCHAR, iValueLen + 1);
FXSYS_wcsncpy(psz, pszValue, iValueLen);
psz[iValueLen] = '\0';
if (pCache)
(*pCache)[key] = psz;
return psz;
}
CFX_RetainPtr<CFDE_CSSPrimitiveValue> CFDE_CSSDeclaration::NewNumberValue(
FDE_CSSPrimitiveType eUnit,
FX_FLOAT fValue) const {
if (eUnit == FDE_CSSPrimitiveType::Number && FXSYS_fabs(fValue) < 0.001f)
fValue = 0.0f;
return pdfium::MakeRetain<CFDE_CSSPrimitiveValue>(eUnit, fValue);
}
CFX_RetainPtr<CFDE_CSSPrimitiveValue> CFDE_CSSDeclaration::NewEnumValue(
FDE_CSSPropertyValue eValue) const {
return pdfium::MakeRetain<CFDE_CSSPrimitiveValue>(eValue);
}
void CFDE_CSSDeclaration::AddPropertyHolder(FDE_CSSProperty eProperty,
CFX_RetainPtr<CFDE_CSSValue> pValue,
bool bImportant) {
auto pHolder = pdfium::MakeUnique<FDE_CSSPropertyHolder>();
pHolder->bImportant = bImportant;
pHolder->eProperty = eProperty;
pHolder->pValue = pValue;
properties_.push_back(std::move(pHolder));
}
void CFDE_CSSDeclaration::AddProperty(const FDE_CSSPropertyArgs* pArgs,
const FX_WCHAR* pszValue,
int32_t iValueLen) {
ASSERT(iValueLen > 0);
bool bImportant = false;
if (iValueLen >= 10 && pszValue[iValueLen - 10] == '!' &&
FXSYS_wcsnicmp(L"important", pszValue + iValueLen - 9, 9) == 0) {
if ((iValueLen -= 10) == 0)
return;
bImportant = true;
}
const uint32_t dwType = pArgs->pProperty->dwType;
switch (dwType & 0x0F) {
case FDE_CSSVALUETYPE_Primitive: {
static const uint32_t g_ValueGuessOrder[] = {
FDE_CSSVALUETYPE_MaybeNumber, FDE_CSSVALUETYPE_MaybeEnum,
FDE_CSSVALUETYPE_MaybeColor, FDE_CSSVALUETYPE_MaybeURI,
FDE_CSSVALUETYPE_MaybeFunction, FDE_CSSVALUETYPE_MaybeString,
};
static const int32_t g_ValueGuessCount =
sizeof(g_ValueGuessOrder) / sizeof(uint32_t);
for (int32_t i = 0; i < g_ValueGuessCount; ++i) {
const uint32_t dwMatch = dwType & g_ValueGuessOrder[i];
if (dwMatch == 0) {
continue;
}
CFX_RetainPtr<CFDE_CSSValue> pCSSValue;
switch (dwMatch) {
case FDE_CSSVALUETYPE_MaybeFunction:
pCSSValue = ParseFunction(pArgs, pszValue, iValueLen);
break;
case FDE_CSSVALUETYPE_MaybeNumber:
pCSSValue = ParseNumber(pArgs, pszValue, iValueLen);
break;
case FDE_CSSVALUETYPE_MaybeEnum:
pCSSValue = ParseEnum(pArgs, pszValue, iValueLen);
break;
case FDE_CSSVALUETYPE_MaybeColor:
pCSSValue = ParseColor(pArgs, pszValue, iValueLen);
break;
case FDE_CSSVALUETYPE_MaybeURI:
pCSSValue = ParseURI(pArgs, pszValue, iValueLen);
break;
case FDE_CSSVALUETYPE_MaybeString:
pCSSValue = ParseString(pArgs, pszValue, iValueLen);
break;
default:
break;
}
if (pCSSValue) {
AddPropertyHolder(pArgs->pProperty->eName, pCSSValue, bImportant);
return;
}
if (FDE_IsOnlyValue(dwType, g_ValueGuessOrder[i]))
return;
}
break;
}
case FDE_CSSVALUETYPE_Shorthand: {
CFX_RetainPtr<CFDE_CSSValue> pWidth;
switch (pArgs->pProperty->eName) {
case FDE_CSSProperty::Font:
ParseFontProperty(pArgs, pszValue, iValueLen, bImportant);
return;
case FDE_CSSProperty::Border:
if (ParseBorderProperty(pszValue, iValueLen, pWidth)) {
AddPropertyHolder(FDE_CSSProperty::BorderLeftWidth, pWidth,
bImportant);
AddPropertyHolder(FDE_CSSProperty::BorderTopWidth, pWidth,
bImportant);
AddPropertyHolder(FDE_CSSProperty::BorderRightWidth, pWidth,
bImportant);
AddPropertyHolder(FDE_CSSProperty::BorderBottomWidth, pWidth,
bImportant);
return;
}
break;
case FDE_CSSProperty::BorderLeft:
if (ParseBorderProperty(pszValue, iValueLen, pWidth)) {
AddPropertyHolder(FDE_CSSProperty::BorderLeftWidth, pWidth,
bImportant);
return;
}
break;
case FDE_CSSProperty::BorderTop:
if (ParseBorderProperty(pszValue, iValueLen, pWidth)) {
AddPropertyHolder(FDE_CSSProperty::BorderTopWidth, pWidth,
bImportant);
return;
}
break;
case FDE_CSSProperty::BorderRight:
if (ParseBorderProperty(pszValue, iValueLen, pWidth)) {
AddPropertyHolder(FDE_CSSProperty::BorderRightWidth, pWidth,
bImportant);
return;
}
break;
case FDE_CSSProperty::BorderBottom:
if (ParseBorderProperty(pszValue, iValueLen, pWidth)) {
AddPropertyHolder(FDE_CSSProperty::BorderBottomWidth, pWidth,
bImportant);
return;
}
break;
default:
break;
}
} break;
case FDE_CSSVALUETYPE_List:
ParseValueListProperty(pArgs, pszValue, iValueLen, bImportant);
return;
default:
ASSERT(false);
break;
}
}
void CFDE_CSSDeclaration::AddProperty(const FDE_CSSPropertyArgs* pArgs,
const FX_WCHAR* pszName,
int32_t iNameLen,
const FX_WCHAR* pszValue,
int32_t iValueLen) {
auto pProperty = pdfium::MakeUnique<FDE_CSSCustomProperty>();
pProperty->pwsName = CopyToLocal(pArgs, pszName, iNameLen);
pProperty->pwsValue = CopyToLocal(pArgs, pszValue, iValueLen);
custom_properties_.push_back(std::move(pProperty));
}
CFX_RetainPtr<CFDE_CSSValue> CFDE_CSSDeclaration::ParseNumber(
const FDE_CSSPropertyArgs* pArgs,
const FX_WCHAR* pszValue,
int32_t iValueLen) {
FX_FLOAT fValue;
FDE_CSSPrimitiveType eUnit;
if (!FDE_ParseCSSNumber(pszValue, iValueLen, fValue, eUnit))
return nullptr;
return NewNumberValue(eUnit, fValue);
}
CFX_RetainPtr<CFDE_CSSValue> CFDE_CSSDeclaration::ParseEnum(
const FDE_CSSPropertyArgs* pArgs,
const FX_WCHAR* pszValue,
int32_t iValueLen) {
const FDE_CSSPropertyValueTable* pValue =
FDE_GetCSSPropertyValueByName(CFX_WideStringC(pszValue, iValueLen));
return pValue ? NewEnumValue(pValue->eName) : nullptr;
}
CFX_RetainPtr<CFDE_CSSValue> CFDE_CSSDeclaration::ParseColor(
const FDE_CSSPropertyArgs* pArgs,
const FX_WCHAR* pszValue,
int32_t iValueLen) {
FX_ARGB dwColor;
if (!FDE_ParseCSSColor(pszValue, iValueLen, dwColor))
return nullptr;
return pdfium::MakeRetain<CFDE_CSSPrimitiveValue>(dwColor);
}
CFX_RetainPtr<CFDE_CSSValue> CFDE_CSSDeclaration::ParseURI(
const FDE_CSSPropertyArgs* pArgs,
const FX_WCHAR* pszValue,
int32_t iValueLen) {
int32_t iOffset;
if (!FDE_ParseCSSURI(pszValue, &iOffset, &iValueLen))
return nullptr;
if (iValueLen <= 0)
return nullptr;
pszValue = CopyToLocal(pArgs, pszValue + iOffset, iValueLen);
return pszValue ? pdfium::MakeRetain<CFDE_CSSPrimitiveValue>(
FDE_CSSPrimitiveType::URI, pszValue)
: nullptr;
}
CFX_RetainPtr<CFDE_CSSValue> CFDE_CSSDeclaration::ParseString(
const FDE_CSSPropertyArgs* pArgs,
const FX_WCHAR* pszValue,
int32_t iValueLen) {
int32_t iOffset;
if (!FDE_ParseCSSString(pszValue, iValueLen, &iOffset, &iValueLen))
return nullptr;
if (iValueLen <= 0)
return nullptr;
pszValue = CopyToLocal(pArgs, pszValue + iOffset, iValueLen);
return pszValue ? pdfium::MakeRetain<CFDE_CSSPrimitiveValue>(
FDE_CSSPrimitiveType::String, pszValue)
: nullptr;
}
CFX_RetainPtr<CFDE_CSSValue> CFDE_CSSDeclaration::ParseFunction(
const FDE_CSSPropertyArgs* pArgs,
const FX_WCHAR* pszValue,
int32_t iValueLen) {
if (pszValue[iValueLen - 1] != ')')
return nullptr;
int32_t iStartBracket = 0;
while (pszValue[iStartBracket] != '(') {
if (iStartBracket >= iValueLen)
return nullptr;
iStartBracket++;
}
if (iStartBracket == 0)
return nullptr;
const FX_WCHAR* pszFuncName = CopyToLocal(pArgs, pszValue, iStartBracket);
pszValue += (iStartBracket + 1);
iValueLen -= (iStartBracket + 2);
std::vector<CFX_RetainPtr<CFDE_CSSValue>> argumentArr;
CFDE_CSSValueListParser parser(pszValue, iValueLen, ',');
FDE_CSSPrimitiveType ePrimitiveType;
while (parser.NextValue(ePrimitiveType, pszValue, iValueLen)) {
switch (ePrimitiveType) {
case FDE_CSSPrimitiveType::String: {
const FDE_CSSPropertyValueTable* pPropertyValue =
FDE_GetCSSPropertyValueByName(CFX_WideStringC(pszValue, iValueLen));
if (pPropertyValue) {
argumentArr.push_back(NewEnumValue(pPropertyValue->eName));
continue;
}
auto pFunctionValue = ParseFunction(pArgs, pszValue, iValueLen);
if (pFunctionValue) {
argumentArr.push_back(pFunctionValue);
continue;
}
argumentArr.push_back(pdfium::MakeRetain<CFDE_CSSPrimitiveValue>(
FDE_CSSPrimitiveType::String,
CopyToLocal(pArgs, pszValue, iValueLen)));
break;
}
case FDE_CSSPrimitiveType::Number: {
FX_FLOAT fValue;
if (FDE_ParseCSSNumber(pszValue, iValueLen, fValue, ePrimitiveType))
argumentArr.push_back(NewNumberValue(ePrimitiveType, fValue));
break;
}
default:
argumentArr.push_back(pdfium::MakeRetain<CFDE_CSSPrimitiveValue>(
FDE_CSSPrimitiveType::String,
CopyToLocal(pArgs, pszValue, iValueLen)));
break;
}
}
auto pArgumentList = pdfium::MakeRetain<CFDE_CSSValueList>(argumentArr);
auto pFunction =
pdfium::MakeUnique<CFDE_CSSFunction>(pszFuncName, pArgumentList);
return pdfium::MakeRetain<CFDE_CSSPrimitiveValue>(std::move(pFunction));
}
void CFDE_CSSDeclaration::ParseValueListProperty(
const FDE_CSSPropertyArgs* pArgs,
const FX_WCHAR* pszValue,
int32_t iValueLen,
bool bImportant) {
FX_WCHAR separator =
(pArgs->pProperty->eName == FDE_CSSProperty::FontFamily) ? ',' : ' ';
CFDE_CSSValueListParser parser(pszValue, iValueLen, separator);
const uint32_t dwType = pArgs->pProperty->dwType;
FDE_CSSPrimitiveType eType;
std::vector<CFX_RetainPtr<CFDE_CSSValue>> list;
while (parser.NextValue(eType, pszValue, iValueLen)) {
switch (eType) {
case FDE_CSSPrimitiveType::Number:
if (dwType & FDE_CSSVALUETYPE_MaybeNumber) {
FX_FLOAT fValue;
if (FDE_ParseCSSNumber(pszValue, iValueLen, fValue, eType))
list.push_back(NewNumberValue(eType, fValue));
}
break;
case FDE_CSSPrimitiveType::String:
if (dwType & FDE_CSSVALUETYPE_MaybeColor) {
FX_ARGB dwColor;
if (FDE_ParseCSSColor(pszValue, iValueLen, dwColor)) {
list.push_back(pdfium::MakeRetain<CFDE_CSSPrimitiveValue>(dwColor));
continue;
}
}
if (dwType & FDE_CSSVALUETYPE_MaybeEnum) {
const FDE_CSSPropertyValueTable* pValue =
FDE_GetCSSPropertyValueByName(
CFX_WideStringC(pszValue, iValueLen));
if (pValue) {
list.push_back(NewEnumValue(pValue->eName));
continue;
}
}
if (dwType & FDE_CSSVALUETYPE_MaybeString) {
pszValue = CopyToLocal(pArgs, pszValue, iValueLen);
list.push_back(pdfium::MakeRetain<CFDE_CSSPrimitiveValue>(
FDE_CSSPrimitiveType::String, pszValue));
}
break;
case FDE_CSSPrimitiveType::RGB:
if (dwType & FDE_CSSVALUETYPE_MaybeColor) {
FX_ARGB dwColor;
if (FDE_ParseCSSColor(pszValue, iValueLen, dwColor)) {
list.push_back(pdfium::MakeRetain<CFDE_CSSPrimitiveValue>(dwColor));
}
}
break;
default:
break;
}
}
if (list.empty())
return;
switch (pArgs->pProperty->eName) {
case FDE_CSSProperty::BorderWidth:
Add4ValuesProperty(list, bImportant, FDE_CSSProperty::BorderLeftWidth,
FDE_CSSProperty::BorderTopWidth,
FDE_CSSProperty::BorderRightWidth,
FDE_CSSProperty::BorderBottomWidth);
return;
case FDE_CSSProperty::Margin:
Add4ValuesProperty(list, bImportant, FDE_CSSProperty::MarginLeft,
FDE_CSSProperty::MarginTop,
FDE_CSSProperty::MarginRight,
FDE_CSSProperty::MarginBottom);
return;
case FDE_CSSProperty::Padding:
Add4ValuesProperty(list, bImportant, FDE_CSSProperty::PaddingLeft,
FDE_CSSProperty::PaddingTop,
FDE_CSSProperty::PaddingRight,
FDE_CSSProperty::PaddingBottom);
return;
default: {
auto pList = pdfium::MakeRetain<CFDE_CSSValueList>(list);
AddPropertyHolder(pArgs->pProperty->eName, pList, bImportant);
return;
}
}
}
void CFDE_CSSDeclaration::Add4ValuesProperty(
const std::vector<CFX_RetainPtr<CFDE_CSSValue>>& list,
bool bImportant,
FDE_CSSProperty eLeft,
FDE_CSSProperty eTop,
FDE_CSSProperty eRight,
FDE_CSSProperty eBottom) {
switch (list.size()) {
case 1:
AddPropertyHolder(eLeft, list[0], bImportant);
AddPropertyHolder(eTop, list[0], bImportant);
AddPropertyHolder(eRight, list[0], bImportant);
AddPropertyHolder(eBottom, list[0], bImportant);
return;
case 2:
AddPropertyHolder(eLeft, list[1], bImportant);
AddPropertyHolder(eTop, list[0], bImportant);
AddPropertyHolder(eRight, list[1], bImportant);
AddPropertyHolder(eBottom, list[0], bImportant);
return;
case 3:
AddPropertyHolder(eLeft, list[1], bImportant);
AddPropertyHolder(eTop, list[0], bImportant);
AddPropertyHolder(eRight, list[1], bImportant);
AddPropertyHolder(eBottom, list[2], bImportant);
return;
case 4:
AddPropertyHolder(eLeft, list[3], bImportant);
AddPropertyHolder(eTop, list[0], bImportant);
AddPropertyHolder(eRight, list[1], bImportant);
AddPropertyHolder(eBottom, list[2], bImportant);
return;
default:
break;
}
}
bool CFDE_CSSDeclaration::ParseBorderProperty(
const FX_WCHAR* pszValue,
int32_t iValueLen,
CFX_RetainPtr<CFDE_CSSValue>& pWidth) const {
pWidth.Reset(nullptr);
CFDE_CSSValueListParser parser(pszValue, iValueLen, ' ');
FDE_CSSPrimitiveType eType;
while (parser.NextValue(eType, pszValue, iValueLen)) {
switch (eType) {
case FDE_CSSPrimitiveType::Number: {
if (pWidth)
continue;
FX_FLOAT fValue;
if (FDE_ParseCSSNumber(pszValue, iValueLen, fValue, eType))
pWidth = NewNumberValue(eType, fValue);
break;
}
case FDE_CSSPrimitiveType::String: {
const FDE_CSSCOLORTABLE* pColorItem =
FDE_GetCSSColorByName(CFX_WideStringC(pszValue, iValueLen));
if (pColorItem)
continue;
const FDE_CSSPropertyValueTable* pValue =
FDE_GetCSSPropertyValueByName(CFX_WideStringC(pszValue, iValueLen));
if (!pValue)
continue;
switch (pValue->eName) {
case FDE_CSSPropertyValue::Thin:
case FDE_CSSPropertyValue::Thick:
case FDE_CSSPropertyValue::Medium:
if (!pWidth)
pWidth = NewEnumValue(pValue->eName);
break;
default:
break;
}
break;
}
default:
break;
}
}
if (!pWidth)
pWidth = NewNumberValue(FDE_CSSPrimitiveType::Number, 0.0f);
return true;
}
void CFDE_CSSDeclaration::ParseFontProperty(const FDE_CSSPropertyArgs* pArgs,
const FX_WCHAR* pszValue,
int32_t iValueLen,
bool bImportant) {
CFDE_CSSValueListParser parser(pszValue, iValueLen, '/');
CFX_RetainPtr<CFDE_CSSPrimitiveValue> pStyle;
CFX_RetainPtr<CFDE_CSSPrimitiveValue> pVariant;
CFX_RetainPtr<CFDE_CSSPrimitiveValue> pWeight;
CFX_RetainPtr<CFDE_CSSPrimitiveValue> pFontSize;
CFX_RetainPtr<CFDE_CSSPrimitiveValue> pLineHeight;
std::vector<CFX_RetainPtr<CFDE_CSSValue>> familyList;
FDE_CSSPrimitiveType eType;
while (parser.NextValue(eType, pszValue, iValueLen)) {
switch (eType) {
case FDE_CSSPrimitiveType::String: {
const FDE_CSSPropertyValueTable* pValue =
FDE_GetCSSPropertyValueByName(CFX_WideStringC(pszValue, iValueLen));
if (pValue) {
switch (pValue->eName) {
case FDE_CSSPropertyValue::XxSmall:
case FDE_CSSPropertyValue::XSmall:
case FDE_CSSPropertyValue::Small:
case FDE_CSSPropertyValue::Medium:
case FDE_CSSPropertyValue::Large:
case FDE_CSSPropertyValue::XLarge:
case FDE_CSSPropertyValue::XxLarge:
case FDE_CSSPropertyValue::Smaller:
case FDE_CSSPropertyValue::Larger:
if (!pFontSize)
pFontSize = NewEnumValue(pValue->eName);
continue;
case FDE_CSSPropertyValue::Bold:
case FDE_CSSPropertyValue::Bolder:
case FDE_CSSPropertyValue::Lighter:
if (!pWeight)
pWeight = NewEnumValue(pValue->eName);
continue;
case FDE_CSSPropertyValue::Italic:
case FDE_CSSPropertyValue::Oblique:
if (!pStyle)
pStyle = NewEnumValue(pValue->eName);
continue;
case FDE_CSSPropertyValue::SmallCaps:
if (!pVariant)
pVariant = NewEnumValue(pValue->eName);
continue;
case FDE_CSSPropertyValue::Normal:
if (!pStyle)
pStyle = NewEnumValue(pValue->eName);
else if (!pVariant)
pVariant = NewEnumValue(pValue->eName);
else if (!pWeight)
pWeight = NewEnumValue(pValue->eName);
else if (!pFontSize)
pFontSize = NewEnumValue(pValue->eName);
else if (!pLineHeight)
pLineHeight = NewEnumValue(pValue->eName);
continue;
default:
break;
}
}
if (pFontSize) {
familyList.push_back(pdfium::MakeRetain<CFDE_CSSPrimitiveValue>(
eType, CopyToLocal(pArgs, pszValue, iValueLen)));
}
parser.m_Separator = ',';
break;
}
case FDE_CSSPrimitiveType::Number: {
FX_FLOAT fValue;
if (!FDE_ParseCSSNumber(pszValue, iValueLen, fValue, eType))
break;
if (eType == FDE_CSSPrimitiveType::Number) {
switch ((int32_t)fValue) {
case 100:
case 200:
case 300:
case 400:
case 500:
case 600:
case 700:
case 800:
case 900:
if (!pWeight)
pWeight = NewNumberValue(FDE_CSSPrimitiveType::Number, fValue);
continue;
}
}
if (!pFontSize)
pFontSize = NewNumberValue(eType, fValue);
else if (!pLineHeight)
pLineHeight = NewNumberValue(eType, fValue);
break;
}
default:
break;
}
}
if (!pStyle)
pStyle = NewEnumValue(FDE_CSSPropertyValue::Normal);
if (!pVariant)
pVariant = NewEnumValue(FDE_CSSPropertyValue::Normal);
if (!pWeight)
pWeight = NewEnumValue(FDE_CSSPropertyValue::Normal);
if (!pFontSize)
pFontSize = NewEnumValue(FDE_CSSPropertyValue::Medium);
if (!pLineHeight)
pLineHeight = NewEnumValue(FDE_CSSPropertyValue::Normal);
AddPropertyHolder(FDE_CSSProperty::FontStyle, pStyle, bImportant);
AddPropertyHolder(FDE_CSSProperty::FontVariant, pVariant, bImportant);
AddPropertyHolder(FDE_CSSProperty::FontWeight, pWeight, bImportant);
AddPropertyHolder(FDE_CSSProperty::FontSize, pFontSize, bImportant);
AddPropertyHolder(FDE_CSSProperty::LineHeight, pLineHeight, bImportant);
if (!familyList.empty()) {
auto pList = pdfium::MakeRetain<CFDE_CSSValueList>(familyList);
AddPropertyHolder(FDE_CSSProperty::FontFamily, pList, bImportant);
}
}
size_t CFDE_CSSDeclaration::PropertyCountForTesting() const {
return properties_.size();
}
FDE_CSSPropertyHolder::FDE_CSSPropertyHolder() {}
FDE_CSSPropertyHolder::~FDE_CSSPropertyHolder() {}