mirror of
https://github.com/flutter/flutter.git
synced 2026-02-20 02:29:02 +08:00
This caused us to lose our gn check certification. :( Turns out gn check was just ignoring all the header paths it didn't understand and so gn check passing for sky wasn't meaning much. I tried to straighten out some of the mess in this CL, but its going to take several more rounds of massaging before gn check passes again. On the bright side (almost) all of our headers are absolute now. Turns out my script (attached to the bug) didn't notice ../ includes but I'll fix that in the next patch. R=abarth@chromium.org BUG=435361 Review URL: https://codereview.chromium.org/746023002
259 lines
10 KiB
C++
259 lines
10 KiB
C++
/*
|
|
* Copyright (C) 2003, 2006, 2008, 2009, 2010, 2011 Apple Inc. All rights reserved.
|
|
* Copyright (C) 2008 Holger Hans Peter Freyther
|
|
* Copyright (C) 2014 Google Inc. All rights reserved.
|
|
*
|
|
* This library is free software; you can redistribute it and/or
|
|
* modify it under the terms of the GNU Library General Public
|
|
* License as published by the Free Software Foundation; either
|
|
* version 2 of the License, or (at your option) any later version.
|
|
*
|
|
* This library is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
* Library General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU Library General Public License
|
|
* along with this library; see the file COPYING.LIB. If not, write to
|
|
* the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
|
|
* Boston, MA 02110-1301, USA.
|
|
*
|
|
*/
|
|
|
|
#include "sky/engine/config.h"
|
|
#include "sky/engine/platform/fonts/WidthIterator.h"
|
|
|
|
#include "sky/engine/platform/fonts/Character.h"
|
|
#include "sky/engine/platform/fonts/Font.h"
|
|
#include "sky/engine/platform/fonts/FontPlatformFeatures.h"
|
|
#include "sky/engine/platform/fonts/GlyphBuffer.h"
|
|
#include "sky/engine/platform/fonts/Latin1TextIterator.h"
|
|
#include "sky/engine/platform/fonts/SimpleFontData.h"
|
|
#include "sky/engine/platform/text/SurrogatePairAwareTextIterator.h"
|
|
#include "sky/engine/wtf/MathExtras.h"
|
|
|
|
using namespace WTF;
|
|
using namespace Unicode;
|
|
|
|
namespace blink {
|
|
|
|
WidthIterator::WidthIterator(const Font* font, const TextRun& run, HashSet<const SimpleFontData*>* fallbackFonts, bool accountForGlyphBounds, bool forTextEmphasis)
|
|
: m_font(font)
|
|
, m_run(run)
|
|
, m_currentCharacter(0)
|
|
, m_runWidthSoFar(0)
|
|
, m_isAfterExpansion(!run.allowsLeadingExpansion())
|
|
, m_fallbackFonts(fallbackFonts)
|
|
, m_maxGlyphBoundingBoxY(std::numeric_limits<float>::min())
|
|
, m_minGlyphBoundingBoxY(std::numeric_limits<float>::max())
|
|
, m_firstGlyphOverflow(0)
|
|
, m_lastGlyphOverflow(0)
|
|
, m_accountForGlyphBounds(accountForGlyphBounds)
|
|
, m_forTextEmphasis(forTextEmphasis)
|
|
{
|
|
// If the padding is non-zero, count the number of spaces in the run
|
|
// and divide that by the padding for per space addition.
|
|
m_expansion = m_run.expansion();
|
|
if (!m_expansion)
|
|
m_expansionPerOpportunity = 0;
|
|
else {
|
|
bool isAfterExpansion = m_isAfterExpansion;
|
|
unsigned expansionOpportunityCount = m_run.is8Bit() ? Character::expansionOpportunityCount(m_run.characters8(), m_run.length(), m_run.direction(), isAfterExpansion) : Character::expansionOpportunityCount(m_run.characters16(), m_run.length(), m_run.direction(), isAfterExpansion);
|
|
if (isAfterExpansion && !m_run.allowsTrailingExpansion())
|
|
expansionOpportunityCount--;
|
|
|
|
if (!expansionOpportunityCount)
|
|
m_expansionPerOpportunity = 0;
|
|
else
|
|
m_expansionPerOpportunity = m_expansion / expansionOpportunityCount;
|
|
}
|
|
}
|
|
|
|
GlyphData WidthIterator::glyphDataForCharacter(CharacterData& charData)
|
|
{
|
|
ASSERT(m_font);
|
|
return m_font->glyphDataForCharacter(charData.character, m_run.rtl());
|
|
}
|
|
|
|
float WidthIterator::characterWidth(UChar32 character, const GlyphData& glyphData) const
|
|
{
|
|
const SimpleFontData* fontData = glyphData.fontData;
|
|
ASSERT(fontData);
|
|
|
|
if (UNLIKELY(character == '\t' && m_run.allowTabs()))
|
|
return m_font->tabWidth(*fontData, m_run.tabSize(), m_run.xPos() + m_runWidthSoFar);
|
|
|
|
float width = fontData->widthForGlyph(glyphData.glyph);
|
|
|
|
// SVG uses horizontalGlyphStretch(), when textLength is used to stretch/squeeze text.
|
|
if (UNLIKELY(m_run.horizontalGlyphStretch() != 1))
|
|
width *= m_run.horizontalGlyphStretch();
|
|
|
|
return width;
|
|
}
|
|
|
|
void WidthIterator::cacheFallbackFont(UChar32 character, const SimpleFontData* fontData,
|
|
const SimpleFontData* primaryFont)
|
|
{
|
|
if (fontData == primaryFont)
|
|
return;
|
|
|
|
// FIXME: This does a little extra work that could be avoided if
|
|
// glyphDataForCharacter() returned whether it chose to use a small caps font.
|
|
if (m_font->fontDescription().variant() == FontVariantNormal || character == toUpper(character)) {
|
|
m_fallbackFonts->add(fontData);
|
|
} else {
|
|
ASSERT(m_font->fontDescription().variant() == FontVariantSmallCaps);
|
|
const GlyphData uppercaseGlyphData = m_font->glyphDataForCharacter(toUpper(character),
|
|
m_run.rtl());
|
|
if (uppercaseGlyphData.fontData != primaryFont)
|
|
m_fallbackFonts->add(uppercaseGlyphData.fontData);
|
|
}
|
|
}
|
|
|
|
float WidthIterator::adjustSpacing(float width, const CharacterData& charData,
|
|
const SimpleFontData& fontData, GlyphBuffer* glyphBuffer)
|
|
{
|
|
// Account for letter-spacing.
|
|
if (width)
|
|
width += m_font->fontDescription().letterSpacing();
|
|
|
|
static bool expandAroundIdeographs = FontPlatformFeatures::canExpandAroundIdeographsInComplexText();
|
|
bool treatAsSpace = Character::treatAsSpace(charData.character);
|
|
if (treatAsSpace || (expandAroundIdeographs && Character::isCJKIdeographOrSymbol(charData.character))) {
|
|
// Distribute the run's total expansion evenly over all expansion opportunities in the run.
|
|
if (m_expansion) {
|
|
if (!treatAsSpace && !m_isAfterExpansion) {
|
|
// Take the expansion opportunity before this ideograph.
|
|
m_expansion -= m_expansionPerOpportunity;
|
|
float expansionAtThisOpportunity = m_expansionPerOpportunity;
|
|
m_runWidthSoFar += expansionAtThisOpportunity;
|
|
if (glyphBuffer) {
|
|
if (glyphBuffer->isEmpty()) {
|
|
if (m_forTextEmphasis)
|
|
glyphBuffer->add(fontData.zeroWidthSpaceGlyph(), &fontData, m_expansionPerOpportunity);
|
|
else
|
|
glyphBuffer->add(fontData.spaceGlyph(), &fontData, expansionAtThisOpportunity);
|
|
} else {
|
|
glyphBuffer->expandLastAdvance(expansionAtThisOpportunity);
|
|
}
|
|
}
|
|
}
|
|
if (m_run.allowsTrailingExpansion()
|
|
|| (m_run.ltr() && charData.characterOffset + charData.clusterLength < static_cast<size_t>(m_run.length()))
|
|
|| (m_run.rtl() && charData.characterOffset)) {
|
|
m_expansion -= m_expansionPerOpportunity;
|
|
width += m_expansionPerOpportunity;
|
|
m_isAfterExpansion = true;
|
|
}
|
|
} else {
|
|
m_isAfterExpansion = false;
|
|
}
|
|
|
|
// Account for word spacing.
|
|
// We apply additional space between "words" by adding width to the space character.
|
|
if (treatAsSpace && (charData.character != '\t' || !m_run.allowTabs())
|
|
&& (charData.characterOffset || charData.character == noBreakSpace)
|
|
&& m_font->fontDescription().wordSpacing()) {
|
|
width += m_font->fontDescription().wordSpacing();
|
|
}
|
|
} else {
|
|
m_isAfterExpansion = false;
|
|
}
|
|
|
|
return width;
|
|
}
|
|
|
|
void WidthIterator::updateGlyphBounds(const GlyphData& glyphData, float width, bool firstCharacter)
|
|
{
|
|
ASSERT(glyphData.fontData);
|
|
FloatRect bounds = glyphData.fontData->boundsForGlyph(glyphData.glyph);
|
|
|
|
if (firstCharacter)
|
|
m_firstGlyphOverflow = std::max<float>(0, -bounds.x());
|
|
m_lastGlyphOverflow = std::max<float>(0, bounds.maxX() - width);
|
|
m_maxGlyphBoundingBoxY = std::max(m_maxGlyphBoundingBoxY, bounds.maxY());
|
|
m_minGlyphBoundingBoxY = std::min(m_minGlyphBoundingBoxY, bounds.y());
|
|
}
|
|
|
|
template <typename TextIterator>
|
|
unsigned WidthIterator::advanceInternal(TextIterator& textIterator, GlyphBuffer* glyphBuffer)
|
|
{
|
|
bool hasExtraSpacing = (m_font->fontDescription().letterSpacing() || m_font->fontDescription().wordSpacing() || m_expansion)
|
|
&& !m_run.spacingDisabled();
|
|
|
|
const SimpleFontData* primaryFont = m_font->primaryFont();
|
|
const SimpleFontData* lastFontData = primaryFont;
|
|
|
|
CharacterData charData;
|
|
while (textIterator.consume(charData.character, charData.clusterLength)) {
|
|
charData.characterOffset = textIterator.currentCharacter();
|
|
|
|
const GlyphData glyphData = glyphDataForCharacter(charData);
|
|
Glyph glyph = glyphData.glyph;
|
|
const SimpleFontData* fontData = glyphData.fontData;
|
|
ASSERT(fontData);
|
|
|
|
// Now that we have a glyph and font data, get its width.
|
|
float width = characterWidth(charData.character, glyphData);
|
|
|
|
if (m_fallbackFonts && lastFontData != fontData && width) {
|
|
lastFontData = fontData;
|
|
cacheFallbackFont(charData.character, fontData, primaryFont);
|
|
}
|
|
|
|
if (hasExtraSpacing)
|
|
width = adjustSpacing(width, charData, *fontData, glyphBuffer);
|
|
|
|
if (m_accountForGlyphBounds)
|
|
updateGlyphBounds(glyphData, width, !charData.characterOffset);
|
|
|
|
if (m_forTextEmphasis && !Character::canReceiveTextEmphasis(charData.character))
|
|
glyph = 0;
|
|
|
|
// Advance past the character we just dealt with.
|
|
textIterator.advance(charData.clusterLength);
|
|
m_runWidthSoFar += width;
|
|
|
|
if (glyphBuffer)
|
|
glyphBuffer->add(glyph, fontData, width);
|
|
}
|
|
|
|
unsigned consumedCharacters = textIterator.currentCharacter() - m_currentCharacter;
|
|
m_currentCharacter = textIterator.currentCharacter();
|
|
|
|
return consumedCharacters;
|
|
}
|
|
|
|
unsigned WidthIterator::advance(int offset, GlyphBuffer* glyphBuffer)
|
|
{
|
|
int length = m_run.length();
|
|
|
|
if (offset > length)
|
|
offset = length;
|
|
|
|
if (m_currentCharacter >= static_cast<unsigned>(offset))
|
|
return 0;
|
|
|
|
if (m_run.is8Bit()) {
|
|
Latin1TextIterator textIterator(m_run.data8(m_currentCharacter), m_currentCharacter, offset, length);
|
|
return advanceInternal(textIterator, glyphBuffer);
|
|
}
|
|
|
|
SurrogatePairAwareTextIterator textIterator(m_run.data16(m_currentCharacter), m_currentCharacter, offset, length);
|
|
return advanceInternal(textIterator, glyphBuffer);
|
|
}
|
|
|
|
bool WidthIterator::advanceOneCharacter(float& width)
|
|
{
|
|
float initialWidth = m_runWidthSoFar;
|
|
|
|
if (!advance(m_currentCharacter + 1))
|
|
return false;
|
|
|
|
width = m_runWidthSoFar - initialWidth;
|
|
return true;
|
|
}
|
|
|
|
} // namespace blink
|