UniscribeHelper.cpp [plain text]
#include "config.h"
#include "UniscribeHelper.h"
#include "Font.h"
#include "FontUtilsChromiumWin.h"
#include "HWndDC.h"
#include "PlatformContextSkia.h"
#include "SkiaFontWin.h"
#include "SkPoint.h"
#include <windows.h>
#include <wtf/Assertions.h>
namespace WebCore {
typedef HRESULT (WINAPI *ScriptItemizeOpenTypeFunc)(const WCHAR*, int, int,
const SCRIPT_CONTROL*,
const SCRIPT_STATE*,
SCRIPT_ITEM*,
OPENTYPE_TAG*, int*);
typedef HRESULT (WINAPI *ScriptShapeOpenTypeFunc)(HDC, SCRIPT_CACHE*,
SCRIPT_ANALYSIS*,
OPENTYPE_TAG, OPENTYPE_TAG,
int*, TEXTRANGE_PROPERTIES**,
int, const WCHAR*, int, int,
WORD*, SCRIPT_CHARPROP*,
WORD*, SCRIPT_GLYPHPROP*,
int*);
static ScriptItemizeOpenTypeFunc gScriptItemizeOpenTypeFunc = 0;
static ScriptShapeOpenTypeFunc gScriptShapeOpenTypeFunc = 0;
static bool gOpenTypeFunctionsLoaded = false;
static void loadOpenTypeFunctions()
{
HMODULE hModule = GetModuleHandle(L"usp10");
if (hModule) {
gScriptItemizeOpenTypeFunc = reinterpret_cast<ScriptItemizeOpenTypeFunc>(GetProcAddress(hModule, "ScriptItemizeOpenType"));
gScriptShapeOpenTypeFunc = reinterpret_cast<ScriptShapeOpenTypeFunc>(GetProcAddress(hModule, "ScriptShapeOpenType"));
}
if (!gScriptItemizeOpenTypeFunc || !gScriptShapeOpenTypeFunc) {
gScriptItemizeOpenTypeFunc = 0;
gScriptShapeOpenTypeFunc = 0;
}
gOpenTypeFunctionsLoaded = true;
}
static void setLogFontAndStyle(HFONT hfont, LOGFONT *logfont, int *style)
{
ASSERT(hfont && logfont);
if (!hfont || !logfont)
return;
GetObject(hfont, sizeof(LOGFONT), logfont);
logfont->lfWidth = 0;
logfont->lfEscapement = 0;
logfont->lfOrientation = 0;
logfont->lfCharSet = DEFAULT_CHARSET;
logfont->lfOutPrecision = OUT_TT_ONLY_PRECIS;
logfont->lfQuality = DEFAULT_QUALITY; logfont->lfPitchAndFamily = DEFAULT_PITCH | FF_DONTCARE;
if (style)
*style = getStyleFromLogfont(logfont);
}
HDC UniscribeHelper::m_cachedDC = 0;
static bool canUseGlyphIndex(const SCRIPT_ITEM& run)
{
return !run.a.fNoGlyphIndex;
}
UniscribeHelper::UniscribeHelper(const UChar* input,
int inputLength,
bool isRtl,
HFONT hfont,
SCRIPT_CACHE* scriptCache,
SCRIPT_FONTPROPERTIES* fontProperties,
WORD spaceGlyph)
: m_input(input)
, m_inputLength(inputLength)
, m_isRtl(isRtl)
, m_hfont(hfont)
, m_scriptCache(scriptCache)
, m_fontProperties(fontProperties)
, m_spaceGlyph(spaceGlyph)
, m_directionalOverride(false)
, m_inhibitLigate(false)
, m_letterSpacing(0)
, m_spaceWidth(0)
, m_wordSpacing(0)
, m_ascent(0)
, m_disableFontFallback(false)
{
m_logfont.lfFaceName[0] = 0;
if (!gOpenTypeFunctionsLoaded)
loadOpenTypeFunctions();
}
UniscribeHelper::~UniscribeHelper()
{
}
void UniscribeHelper::initWithOptionalLengthProtection(bool lengthProtection)
{
static const int kMaxInputLength = 65535;
if (m_inputLength == 0 || (lengthProtection && m_inputLength > kMaxInputLength))
return;
fillRuns();
fillShapes();
fillScreenOrder();
}
int UniscribeHelper::width() const
{
int width = 0;
for (int itemIndex = 0; itemIndex < static_cast<int>(m_runs.size()); itemIndex++)
width += advanceForItem(itemIndex);
return width;
}
void UniscribeHelper::justify(int additionalSpace)
{
int totalGlyphs = 0;
for (size_t run = 0; run < m_runs.size(); run++) {
int runIndex = m_screenOrder[run];
totalGlyphs += static_cast<int>(m_shapes[runIndex].glyphLength());
}
if (totalGlyphs == 0)
return;
Vector<SCRIPT_VISATTR, 64> visualAttributes;
visualAttributes.resize(totalGlyphs);
Vector<int, 64> advances;
advances.resize(totalGlyphs);
Vector<int, 64> justify;
justify.resize(totalGlyphs);
int destIndex = 0;
for (size_t run = 0; run < m_runs.size(); run++) {
int runIndex = m_screenOrder[run];
const Shaping& shaping = m_shapes[runIndex];
for (int i = 0; i < shaping.glyphLength(); i++, destIndex++) {
memcpy(&visualAttributes[destIndex], &shaping.m_visualAttributes[i],
sizeof(SCRIPT_VISATTR));
advances[destIndex] = shaping.m_advance[i];
}
}
int minKashida;
#if USE(SKIA_TEXT)
for (int i = 0; i < totalGlyphs; ++i) {
if (visualAttributes[i].uJustification == SCRIPT_JUSTIFY_ARABIC_KASHIDA)
visualAttributes[i].uJustification = SCRIPT_JUSTIFY_NONE;
}
minKashida = 0;
#else
minKashida = 1; #endif
ScriptJustify(&visualAttributes[0], &advances[0], totalGlyphs,
additionalSpace, minKashida, &justify[0]);
int globalGlyphIndex = 0;
for (size_t run = 0; run < m_runs.size(); run++) {
int runIndex = m_screenOrder[run];
Shaping& shaping = m_shapes[runIndex];
shaping.m_justify.resize(shaping.glyphLength());
for (int i = 0; i < shaping.glyphLength(); i++, globalGlyphIndex++)
shaping.m_justify[i] = justify[globalGlyphIndex];
}
}
int UniscribeHelper::characterToX(int offset) const
{
HRESULT hr;
ASSERT(offset <= m_inputLength);
int width = 0;
for (size_t screenIndex = 0; screenIndex < m_runs.size(); screenIndex++) {
int itemIndex = m_screenOrder[screenIndex];
const SCRIPT_ITEM& item = m_runs[itemIndex];
const Shaping& shaping = m_shapes[itemIndex];
int itemLength = shaping.charLength();
if (offset >= item.iCharPos && offset <= item.iCharPos + itemLength) {
int charLength = offset - item.iCharPos;
int curX = 0;
hr = ScriptCPtoX(charLength, FALSE, itemLength,
shaping.glyphLength(),
&shaping.m_logs[0], &shaping.m_visualAttributes[0],
shaping.effectiveAdvances(), &item.a, &curX);
if (FAILED(hr))
return 0;
width += curX + shaping.m_prePadding;
ASSERT(width >= 0);
return width;
}
width += advanceForItem(itemIndex);
}
ASSERT(width >= 0);
return width;
}
int UniscribeHelper::xToCharacter(int x) const
{
HRESULT hr;
for (size_t screenIndex = 0; screenIndex < m_runs.size(); screenIndex++) {
int itemIndex = m_screenOrder[screenIndex];
int itemAdvance = advanceForItem(itemIndex);
const Shaping& shaping = m_shapes[itemIndex];
int itemLength = shaping.charLength();
if (x <= itemAdvance && itemLength > 0) {
const SCRIPT_ITEM& item = m_runs[itemIndex];
x -= shaping.m_prePadding;
int charX = 0;
int trailing;
hr = ScriptXtoCP(x, itemLength, shaping.glyphLength(),
&shaping.m_logs[0], &shaping.m_visualAttributes[0],
shaping.effectiveAdvances(), &item.a, &charX,
&trailing);
return charX + item.iCharPos;
}
x -= itemAdvance;
}
return 0;
}
void UniscribeHelper::draw(GraphicsContext* graphicsContext,
HDC dc, int x, int y, int from, int to)
{
HGDIOBJ oldFont = 0;
int curX = x;
bool firstRun = true;
#if !USE(SKIA_TEXT)
bool useWindowsDrawing = windowsCanHandleTextDrawing(graphicsContext);
#endif
for (size_t screenIndex = 0; screenIndex < m_runs.size(); screenIndex++) {
int itemIndex = m_screenOrder[screenIndex];
const SCRIPT_ITEM& item = m_runs[itemIndex];
const Shaping& shaping = m_shapes[itemIndex];
int fromChar = from - item.iCharPos;
int toChar = to - item.iCharPos;
if (shaping.charLength() == 0 ||
fromChar >= shaping.charLength() || toChar <= 0) {
curX += advanceForItem(itemIndex);
continue;
}
int fromGlyph, afterGlyph;
if (item.a.fRTL) {
if (toChar >= shaping.charLength())
fromGlyph = 0;
else {
fromGlyph = shaping.m_logs[toChar - 1];
}
if (fromChar <= 0) {
afterGlyph = shaping.glyphLength();
} else {
afterGlyph = shaping.m_logs[fromChar - 1];
}
} else {
fromGlyph = shaping.m_logs[fromChar < 0 ? 0 : fromChar];
if (toChar >= shaping.charLength())
afterGlyph = shaping.glyphLength();
else
afterGlyph = shaping.m_logs[toChar];
}
const int* effectiveAdvances = shaping.effectiveAdvances();
int innerOffset = 0;
for (int i = 0; i < fromGlyph; i++)
innerOffset += effectiveAdvances[i];
int glyphCount = afterGlyph - fromGlyph;
if (fromGlyph >= 0 && glyphCount > 0) {
innerOffset += shaping.m_prePadding;
const int* justify = shaping.m_justify.size() == 0 ? 0 : &shaping.m_justify[fromGlyph];
#if USE(SKIA_TEXT)
const int* advances = shaping.m_justify.size() ?
&shaping.m_justify[fromGlyph]
: &shaping.m_advance[fromGlyph];
#else
if (useWindowsDrawing) {
if (firstRun) {
oldFont = SelectObject(dc, shaping.m_hfont);
firstRun = false;
} else
SelectObject(dc, shaping.m_hfont);
}
#endif
bool textOutOk = false;
for (int executions = 0; executions < 2; ++executions) {
#if USE(SKIA_TEXT)
SkPoint origin;
origin.fX = curX + + innerOffset;
origin.fY = y + m_ascent;
paintSkiaText(graphicsContext,
shaping.m_hfont,
glyphCount,
&shaping.m_glyphs[fromGlyph],
advances,
&shaping.m_offsets[fromGlyph],
&origin);
textOutOk = true;
#else
if (useWindowsDrawing) {
HRESULT hr = ScriptTextOut(dc, shaping.m_scriptCache,
curX + innerOffset,
y - shaping.m_ascentOffset,
0, 0, &item.a, 0, 0,
&shaping.m_glyphs[fromGlyph],
glyphCount,
&shaping.m_advance[fromGlyph],
justify,
&shaping.m_offsets[fromGlyph]);
textOutOk = (hr == S_OK);
} else {
SkPoint origin;
origin.fX = curX + + innerOffset;
origin.fY = y + m_ascent;
paintSkiaText(graphicsContext,
shaping.m_hfont,
glyphCount,
&shaping.m_glyphs[fromGlyph],
&shaping.m_advance[fromGlyph],
&shaping.m_offsets[fromGlyph],
&origin);
textOutOk = true;
}
#endif
if (!textOutOk && 0 == executions) {
tryToPreloadFont(shaping.m_hfont);
continue;
}
break;
}
}
curX += advanceForItem(itemIndex);
}
if (oldFont)
SelectObject(dc, oldFont);
}
WORD UniscribeHelper::firstGlyphForCharacter(int charOffset) const
{
for (int i = 0; i < static_cast<int>(m_runs.size()); i++) {
int firstChar = m_runs[i].iCharPos;
const Shaping& shaping = m_shapes[i];
int localOffset = charOffset - firstChar;
if (localOffset >= 0 && localOffset < shaping.charLength()) {
size_t glyphIndex = shaping.m_logs[localOffset];
if (glyphIndex >= shaping.m_glyphs.size()) {
ASSERT(shaping.m_glyphs.size() == 0);
return 0;
}
return shaping.m_glyphs[glyphIndex];
}
}
return 0;
}
void UniscribeHelper::fillRuns()
{
HRESULT hr;
m_runs.resize(cUniscribeHelperStackRuns);
m_scriptTags.resize(cUniscribeHelperStackRuns);
SCRIPT_STATE inputState;
inputState.uBidiLevel = m_isRtl;
inputState.fOverrideDirection = m_directionalOverride;
inputState.fInhibitSymSwap = false;
inputState.fCharShape = false; inputState.fDigitSubstitute = false; inputState.fInhibitLigate = m_inhibitLigate;
inputState.fDisplayZWG = false; inputState.fArabicNumContext = m_isRtl; inputState.fGcpClusters = false;
inputState.fReserved = 0;
inputState.fEngineReserved = 0;
static SCRIPT_CONTROL inputControl = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
while (true) {
int numberOfItems = 0;
if (gScriptItemizeOpenTypeFunc) {
hr = gScriptItemizeOpenTypeFunc(m_input, m_inputLength,
static_cast<int>(m_runs.size()) - 1,
&inputControl, &inputState,
&m_runs[0], &m_scriptTags[0],
&numberOfItems);
if (SUCCEEDED(hr)) {
for (int i = 0; i < numberOfItems; ++i) {
if (m_scriptTags[i] == SCRIPT_TAG_UNKNOWN) {
int j = 1;
while (i + j < numberOfItems && m_scriptTags[i + j] == SCRIPT_TAG_UNKNOWN)
++j;
if (--j) {
m_runs.remove(i + 1, j);
m_scriptTags.remove(i + 1, j);
numberOfItems -= j;
}
}
}
m_scriptTags.resize(numberOfItems);
}
} else {
hr = ScriptItemize(m_input, m_inputLength,
static_cast<int>(m_runs.size()) - 1,
&inputControl, &inputState, &m_runs[0],
&numberOfItems);
}
if (SUCCEEDED(hr)) {
m_runs.resize(numberOfItems);
break;
}
if (hr != E_OUTOFMEMORY) {
m_runs.resize(0);
break;
}
m_runs.resize(m_runs.size() * 2);
m_scriptTags.resize(m_runs.size());
}
}
bool UniscribeHelper::shape(const UChar* input,
int itemLength,
int numGlyphs,
SCRIPT_ITEM& run,
OPENTYPE_TAG scriptTag,
Shaping& shaping)
{
HFONT hfont = m_hfont;
SCRIPT_CACHE* scriptCache = m_scriptCache;
SCRIPT_FONTPROPERTIES* fontProperties = m_fontProperties;
Vector<SCRIPT_CHARPROP, cUniscribeHelperStackChars> charProps;
Vector<SCRIPT_GLYPHPROP, cUniscribeHelperStackChars> glyphProps;
int ascent = m_ascent;
WORD spaceGlyph = m_spaceGlyph;
HRESULT hr;
bool lastFallbackTried = m_disableFontFallback;
bool result;
int generatedGlyphs = 0;
resetFontIndex();
while (true) {
shaping.m_logs.resize(itemLength);
shaping.m_glyphs.resize(numGlyphs);
shaping.m_visualAttributes.resize(numGlyphs);
charProps.resize(itemLength);
glyphProps.resize(numGlyphs);
run.a.fNoGlyphIndex = FALSE;
#ifdef PURIFY
ZeroMemory(&shaping.m_glyphs[0],
sizeof(shaping.m_glyphs[0]) * shaping.m_glyphs.size());
#endif
if (m_cachedDC)
SelectObject(m_cachedDC, hfont);
if (gScriptShapeOpenTypeFunc) {
TEXTRANGE_PROPERTIES* rangeProps = m_featureRecords.size() ? &m_rangeProperties : 0;
hr = gScriptShapeOpenTypeFunc(m_cachedDC, scriptCache, &run.a,
scriptTag, 0, &itemLength,
&rangeProps, rangeProps ? 1 : 0,
input, itemLength, numGlyphs,
&shaping.m_logs[0], &charProps[0],
&shaping.m_glyphs[0], &glyphProps[0],
&generatedGlyphs);
} else {
hr = ScriptShape(m_cachedDC, scriptCache, input, itemLength,
numGlyphs, &run.a,
&shaping.m_glyphs[0], &shaping.m_logs[0],
&shaping.m_visualAttributes[0], &generatedGlyphs);
}
if (hr == E_PENDING && !m_cachedDC) {
EnsureCachedDCCreated();
continue;
}
if (hr == E_OUTOFMEMORY) {
numGlyphs *= 2;
continue;
}
if (SUCCEEDED(hr) && (lastFallbackTried || !containsMissingGlyphs(shaping, run, fontProperties) && canUseGlyphIndex(run)))
break;
if (!m_disableFontFallback &&
nextWinFontData(&hfont, &scriptCache, &fontProperties, &ascent)) {
continue;
} else if (!lastFallbackTried) {
lastFallbackTried = true;
if (!m_logfont.lfFaceName[0])
setLogFontAndStyle(m_hfont, &m_logfont, &m_style);
const UChar *family = getFallbackFamily(input, itemLength,
FontDescription::StandardFamily, 0, 0);
bool fontOk = getDerivedFontData(family, m_style, &m_logfont,
&ascent, &hfont, &scriptCache,
&spaceGlyph);
if (!fontOk) {
tryToPreloadFont(hfont);
fontOk = getDerivedFontData(family, m_style, &m_logfont,
&ascent, &hfont, &scriptCache,
&spaceGlyph);
ASSERT(fontOk);
}
ASSERT(hfont);
continue;
} else if (hr == USP_E_SCRIPT_NOT_IN_FONT) {
run.a.eScript = SCRIPT_UNDEFINED;
continue;
} else if (FAILED(hr)) {
generatedGlyphs = 0;
result = false;
goto cleanup;
}
}
shaping.m_hfont = hfont;
shaping.m_scriptCache = scriptCache;
shaping.m_spaceGlyph = spaceGlyph;
shaping.m_ascentOffset = m_ascent ? ascent - m_ascent : 0;
result = true;
cleanup:
shaping.m_glyphs.resize(generatedGlyphs);
shaping.m_visualAttributes.resize(generatedGlyphs);
if (gScriptShapeOpenTypeFunc) {
for (int i = 0; i < generatedGlyphs; ++i)
memcpy(&shaping.m_visualAttributes[i], &glyphProps[i].sva, sizeof(SCRIPT_VISATTR));
}
shaping.m_advance.resize(generatedGlyphs);
shaping.m_offsets.resize(generatedGlyphs);
if (!result)
shaping.m_logs.clear();
return result;
}
void UniscribeHelper::EnsureCachedDCCreated()
{
if (m_cachedDC)
return;
HWndDC screenDC(0);
m_cachedDC = ::CreateCompatibleDC(screenDC);
ASSERT(m_cachedDC);
}
void UniscribeHelper::fillShapes()
{
m_shapes.resize(m_runs.size());
for (size_t i = 0; i < m_runs.size(); i++) {
int startItem = m_runs[i].iCharPos;
int itemLength = m_inputLength - startItem;
if (i < m_runs.size() - 1)
itemLength = m_runs[i + 1].iCharPos - startItem;
int numGlyphs;
if (itemLength < cUniscribeHelperStackChars) {
numGlyphs = cUniscribeHelperStackChars;
} else {
numGlyphs = itemLength * 3 / 2 + 16;
}
Shaping& shaping = m_shapes[i];
if (!shape(&m_input[startItem], itemLength, numGlyphs, m_runs[i], m_scriptTags[i], shaping))
continue;
if (m_disableFontFallback)
continue;
EnsureCachedDCCreated();
SelectObject(m_cachedDC, shaping.m_hfont);
shaping.m_prePadding = 0;
if (FAILED(ScriptPlace(m_cachedDC, shaping.m_scriptCache,
&shaping.m_glyphs[0],
static_cast<int>(shaping.m_glyphs.size()),
&shaping.m_visualAttributes[0], &m_runs[i].a,
&shaping.m_advance[0], &shaping.m_offsets[0],
&shaping.m_abc))) {
m_runs.clear();
m_scriptTags.clear();
m_shapes.clear();
m_screenOrder.clear();
}
}
adjustSpaceAdvances();
if (m_letterSpacing != 0 || m_wordSpacing != 0)
applySpacing();
}
void UniscribeHelper::fillScreenOrder()
{
m_screenOrder.resize(m_runs.size());
if (m_isRtl) {
for (int i = 0; i < static_cast<int>(m_screenOrder.size()); i++)
m_screenOrder[static_cast<int>(m_screenOrder.size()) - i - 1] = i;
} else {
for (int i = 0; i < static_cast<int>(m_screenOrder.size()); i++)
m_screenOrder[i] = i;
}
}
void UniscribeHelper::adjustSpaceAdvances()
{
if (m_spaceWidth == 0)
return;
int spaceWidthWithoutLetterSpacing = m_spaceWidth - m_letterSpacing;
for (size_t run = 0; run < m_runs.size(); run++) {
Shaping& shaping = m_shapes[run];
for (int i = 0; i < shaping.charLength(); i++) {
UChar c = m_input[m_runs[run].iCharPos + i];
bool treatAsSpace = Font::treatAsSpace(c);
if (!treatAsSpace && !Font::treatAsZeroWidthSpaceInComplexScript(c))
continue;
int glyphIndex = shaping.m_logs[i];
int currentAdvance = shaping.m_advance[glyphIndex];
shaping.m_glyphs[glyphIndex] = shaping.m_spaceGlyph;
if (treatAsSpace) {
int diff = currentAdvance - spaceWidthWithoutLetterSpacing;
shaping.m_advance[glyphIndex] -= diff;
shaping.m_abc.abcB -= diff;
continue;
}
shaping.m_advance[glyphIndex] = 0;
shaping.m_abc.abcB -= currentAdvance;
shaping.m_offsets[glyphIndex].du = 0;
shaping.m_offsets[glyphIndex].dv = 0;
}
}
}
void UniscribeHelper::applySpacing()
{
for (size_t run = 0; run < m_runs.size(); run++) {
Shaping& shaping = m_shapes[run];
bool isRtl = m_runs[run].a.fRTL;
if (m_letterSpacing != 0) {
if (isRtl)
shaping.m_prePadding += m_letterSpacing;
for (int i = 0; i < shaping.glyphLength(); i++) {
if (shaping.m_visualAttributes[i].fClusterStart) {
if (isRtl) {
if (i != shaping.glyphLength() - 1) {
shaping.m_advance[i] += m_letterSpacing;
shaping.m_abc.abcB += m_letterSpacing;
}
} else {
shaping.m_advance[i] += m_letterSpacing;
shaping.m_abc.abcB += m_letterSpacing;
}
}
}
}
if (m_wordSpacing != 0) {
for (int i = 0; i < shaping.charLength(); i++) {
if (!Font::treatAsSpace(m_input[m_runs[run].iCharPos + i]))
continue;
int glyphIndex = shaping.m_logs[i];
if (isRtl) {
shaping.m_advance[glyphIndex] += m_wordSpacing;
shaping.m_abc.abcB += m_wordSpacing;
} else {
if (glyphIndex == 0)
shaping.m_prePadding += m_wordSpacing;
else {
shaping.m_advance[glyphIndex - 1] += m_wordSpacing;
shaping.m_abc.abcB += m_wordSpacing;
}
}
}
}
}
}
int UniscribeHelper::advanceForItem(int itemIndex) const
{
int accum = 0;
const Shaping& shaping = m_shapes[itemIndex];
if (shaping.m_justify.size() == 0) {
return shaping.m_abc.abcA + shaping.m_abc.abcB +
shaping.m_abc.abcC + shaping.m_prePadding;
}
int justification = 0;
for (size_t i = 0; i < shaping.m_justify.size(); i++)
justification += shaping.m_justify[i];
return shaping.m_prePadding + justification;
}
bool UniscribeHelper::containsMissingGlyphs(const Shaping& shaping,
const SCRIPT_ITEM& run,
const SCRIPT_FONTPROPERTIES* properties) const
{
for (int i = 0; i < shaping.charLength(); i++) {
UChar c = m_input[run.iCharPos + i];
if (Font::treatAsZeroWidthSpaceInComplexScript(c))
continue;
int glyphIndex = shaping.m_logs[i];
WORD glyph = shaping.m_glyphs[glyphIndex];
if (glyph == properties->wgDefault
|| (glyph == properties->wgInvalid && glyph != properties->wgBlank))
return true;
}
return false;
}
static OPENTYPE_TAG convertFeatureTag(const String& tag)
{
return ((tag[0] & 0xFF) | ((tag[1] & 0xFF) << 8) | ((tag[2] & 0xFF) << 16) | ((tag[3] & 0xFF) << 24));
}
void UniscribeHelper::setRangeProperties(const FontFeatureSettings* featureSettings)
{
if (!featureSettings || !featureSettings->size()) {
m_featureRecords.resize(0);
return;
}
m_featureRecords.resize(featureSettings->size());
for (unsigned i = 0; i < featureSettings->size(); ++i) {
m_featureRecords[i].lParameter = featureSettings->at(i).value();
m_featureRecords[i].tagFeature = convertFeatureTag(featureSettings->at(i).tag());
}
m_rangeProperties.potfRecords = &m_featureRecords[0];
m_rangeProperties.cotfRecords = m_featureRecords.size();
}
}