15c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)/*
25c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) * Copyright (C) 2003, 2006, 2008, 2009, 2010, 2011 Apple Inc. All rights reserved.
35c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) * Copyright (C) 2008 Holger Hans Peter Freyther
45c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) *
55c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) * This library is free software; you can redistribute it and/or
65c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) * modify it under the terms of the GNU Library General Public
75c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) * License as published by the Free Software Foundation; either
85c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) * version 2 of the License, or (at your option) any later version.
95c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) *
105c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) * This library is distributed in the hope that it will be useful,
115c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) * but WITHOUT ANY WARRANTY; without even the implied warranty of
125c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
135c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) * Library General Public License for more details.
145c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) *
155c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) * You should have received a copy of the GNU Library General Public License
165c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) * along with this library; see the file COPYING.LIB.  If not, write to
175c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
185c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) * Boston, MA 02110-1301, USA.
195c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) *
205c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) */
215c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)
225c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)#include "config.h"
2353e740f4a82e17f3ae59772501622dc354e42336Torne (Richard Coles)#include "core/platform/graphics/WidthIterator.h"
245c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)
2553e740f4a82e17f3ae59772501622dc354e42336Torne (Richard Coles)#include "core/platform/graphics/Font.h"
2653e740f4a82e17f3ae59772501622dc354e42336Torne (Richard Coles)#include "core/platform/graphics/GlyphBuffer.h"
2753e740f4a82e17f3ae59772501622dc354e42336Torne (Richard Coles)#include "core/platform/graphics/Latin1TextIterator.h"
2853e740f4a82e17f3ae59772501622dc354e42336Torne (Richard Coles)#include "core/platform/graphics/SimpleFontData.h"
2953e740f4a82e17f3ae59772501622dc354e42336Torne (Richard Coles)#include "core/platform/graphics/SurrogatePairAwareTextIterator.h"
307757ec2eadfa2dd8ac2aeed0a4399e9b07ec38cbBen Murdoch#include "wtf/MathExtras.h"
315c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)
325c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)using namespace WTF;
335c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)using namespace Unicode;
345c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)using namespace std;
355c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)
365c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)namespace WebCore {
375c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)
385c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)WidthIterator::WidthIterator(const Font* font, const TextRun& run, HashSet<const SimpleFontData*>* fallbackFonts, bool accountForGlyphBounds, bool forTextEmphasis)
395c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)    : m_font(font)
405c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)    , m_run(run)
415c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)    , m_currentCharacter(0)
425c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)    , m_runWidthSoFar(0)
435c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)    , m_isAfterExpansion(!run.allowsLeadingExpansion())
445c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)    , m_finalRoundingWidth(0)
455c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)    , m_typesettingFeatures(font->typesettingFeatures())
465c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)    , m_fallbackFonts(fallbackFonts)
475c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)    , m_accountForGlyphBounds(accountForGlyphBounds)
485c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)    , m_maxGlyphBoundingBoxY(numeric_limits<float>::min())
495c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)    , m_minGlyphBoundingBoxY(numeric_limits<float>::max())
505c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)    , m_firstGlyphOverflow(0)
515c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)    , m_lastGlyphOverflow(0)
525c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)    , m_forTextEmphasis(forTextEmphasis)
535c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles){
545c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)    // If the padding is non-zero, count the number of spaces in the run
555c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)    // and divide that by the padding for per space addition.
565c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)    m_expansion = m_run.expansion();
575c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)    if (!m_expansion)
585c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)        m_expansionPerOpportunity = 0;
595c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)    else {
605c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)        bool isAfterExpansion = m_isAfterExpansion;
615c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)        unsigned expansionOpportunityCount = m_run.is8Bit() ? Font::expansionOpportunityCount(m_run.characters8(), m_run.length(), m_run.ltr() ? LTR : RTL, isAfterExpansion) : Font::expansionOpportunityCount(m_run.characters16(), m_run.length(), m_run.ltr() ? LTR : RTL, isAfterExpansion);
625c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)        if (isAfterExpansion && !m_run.allowsTrailingExpansion())
635c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)            expansionOpportunityCount--;
645c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)
655c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)        if (!expansionOpportunityCount)
665c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)            m_expansionPerOpportunity = 0;
675c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)        else
685c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)            m_expansionPerOpportunity = m_expansion / expansionOpportunityCount;
695c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)    }
705c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)}
715c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)
725c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)GlyphData WidthIterator::glyphDataForCharacter(UChar32 character, bool mirror, int currentCharacter, unsigned& advanceLength)
735c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles){
745c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)    ASSERT(m_font);
755c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)
765c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)#if ENABLE(SVG_FONTS)
775c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)    if (TextRun::RenderingContext* renderingContext = m_run.renderingContext())
785c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)        return renderingContext->glyphDataForCharacter(*m_font, m_run, *this, character, mirror, currentCharacter, advanceLength);
795c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)#else
805c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)    UNUSED_PARAM(currentCharacter);
815c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)    UNUSED_PARAM(advanceLength);
825c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)#endif
835c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)
845c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)    return m_font->glyphDataForCharacter(character, mirror);
855c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)}
865c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)
875c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)struct OriginalAdvancesForCharacterTreatedAsSpace {
885c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)public:
895c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)    OriginalAdvancesForCharacterTreatedAsSpace(bool isSpace, float advanceBefore, float advanceAt)
905c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)        : characterIsSpace(isSpace)
915c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)        , advanceBeforeCharacter(advanceBefore)
925c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)        , advanceAtCharacter(advanceAt)
935c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)    {
945c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)    }
955c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)
965c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)    bool characterIsSpace;
975c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)    float advanceBeforeCharacter;
985c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)    float advanceAtCharacter;
995c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)};
1005c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)
1015c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)typedef Vector<pair<int, OriginalAdvancesForCharacterTreatedAsSpace>, 64> CharactersTreatedAsSpace;
1025c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)
1035c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)static inline float applyFontTransforms(GlyphBuffer* glyphBuffer, bool ltr, int& lastGlyphCount, const SimpleFontData* fontData, TypesettingFeatures typesettingFeatures, CharactersTreatedAsSpace& charactersTreatedAsSpace)
1045c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles){
1055c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)    ASSERT(typesettingFeatures & (Kerning | Ligatures));
1065c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)
1075c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)    if (!glyphBuffer)
1085c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)        return 0;
1095c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)
1105c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)    int glyphBufferSize = glyphBuffer->size();
1115c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)    if (glyphBuffer->size() <= lastGlyphCount + 1)
1125c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)        return 0;
1135c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)
1145c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)    GlyphBufferAdvance* advances = glyphBuffer->advances(0);
1155c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)    float widthDifference = 0;
1165c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)    for (int i = lastGlyphCount; i < glyphBufferSize; ++i)
1175c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)        widthDifference -= advances[i].width();
1185c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)
1195c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)    if (!ltr)
1205c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)        glyphBuffer->reverse(lastGlyphCount, glyphBufferSize - lastGlyphCount);
1215c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)
1225c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)    fontData->applyTransforms(glyphBuffer->glyphs(lastGlyphCount), advances + lastGlyphCount, glyphBufferSize - lastGlyphCount, typesettingFeatures);
1235c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)
1245c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)    if (!ltr)
1255c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)        glyphBuffer->reverse(lastGlyphCount, glyphBufferSize - lastGlyphCount);
1265c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)
1275c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)    for (size_t i = 0; i < charactersTreatedAsSpace.size(); ++i) {
1285c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)        int spaceOffset = charactersTreatedAsSpace[i].first;
1295c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)        const OriginalAdvancesForCharacterTreatedAsSpace& originalAdvances = charactersTreatedAsSpace[i].second;
1305c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)        if (spaceOffset && !originalAdvances.characterIsSpace)
1315c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)            glyphBuffer->advances(spaceOffset - 1)->setWidth(originalAdvances.advanceBeforeCharacter);
1325c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)        glyphBuffer->advances(spaceOffset)->setWidth(originalAdvances.advanceAtCharacter);
1335c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)    }
1345c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)    charactersTreatedAsSpace.clear();
1355c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)
1365c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)    for (int i = lastGlyphCount; i < glyphBufferSize; ++i)
1375c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)        widthDifference += advances[i].width();
1385c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)
1395c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)    lastGlyphCount = glyphBufferSize;
1405c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)    return widthDifference;
1415c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)}
1425c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)
1435c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)template <typename TextIterator>
1445c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)inline unsigned WidthIterator::advanceInternal(TextIterator& textIterator, GlyphBuffer* glyphBuffer)
1455c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles){
1465c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)    bool rtl = m_run.rtl();
1475c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)    bool hasExtraSpacing = (m_font->letterSpacing() || m_font->wordSpacing() || m_expansion) && !m_run.spacingDisabled();
1485c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)
1495c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)    float widthSinceLastRounding = m_runWidthSoFar;
1505c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)    m_runWidthSoFar = floorf(m_runWidthSoFar);
1515c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)    widthSinceLastRounding -= m_runWidthSoFar;
1525c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)
1535c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)    float lastRoundingWidth = m_finalRoundingWidth;
1545c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)    FloatRect bounds;
1555c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)
1565c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)    const SimpleFontData* primaryFont = m_font->primaryFont();
1575c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)    const SimpleFontData* lastFontData = primaryFont;
1585c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)    int lastGlyphCount = glyphBuffer ? glyphBuffer->size() : 0;
1595c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)
1605c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)    UChar32 character = 0;
1615c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)    unsigned clusterLength = 0;
1625c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)    CharactersTreatedAsSpace charactersTreatedAsSpace;
1635c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)    while (textIterator.consume(character, clusterLength)) {
1645c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)        unsigned advanceLength = clusterLength;
1655c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)        const GlyphData& glyphData = glyphDataForCharacter(character, rtl, textIterator.currentCharacter(), advanceLength);
1665c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)        Glyph glyph = glyphData.glyph;
1675c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)        const SimpleFontData* fontData = glyphData.fontData;
1685c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)
1695c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)        ASSERT(fontData);
1705c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)
1715c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)        // Now that we have a glyph and font data, get its width.
1725c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)        float width;
1735c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)        if (character == '\t' && m_run.allowTabs())
1745c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)            width = m_font->tabWidth(*fontData, m_run.tabSize(), m_run.xPos() + m_runWidthSoFar + widthSinceLastRounding);
1755c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)        else {
1765c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)            width = fontData->widthForGlyph(glyph);
1775c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)
1785c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)            // SVG uses horizontalGlyphStretch(), when textLength is used to stretch/squeeze text.
1795c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)            width *= m_run.horizontalGlyphStretch();
1805c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)
1815c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)            // We special case spaces in two ways when applying word rounding.
1825c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)            // First, we round spaces to an adjusted width in all fonts.
1835c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)            // Second, in fixed-pitch fonts we ensure that all characters that
1845c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)            // match the width of the space character have the same width as the space character.
1855c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)            if (m_run.applyWordRounding() && width == fontData->spaceWidth() && (fontData->pitch() == FixedPitch || glyph == fontData->spaceGlyph()))
1865c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)                width = fontData->adjustedSpaceWidth();
1875c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)        }
1885c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)
1895c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)        if (fontData != lastFontData && width) {
1905c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)            if (shouldApplyFontTransforms())
1915c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)                m_runWidthSoFar += applyFontTransforms(glyphBuffer, m_run.ltr(), lastGlyphCount, lastFontData, m_typesettingFeatures, charactersTreatedAsSpace);
1925c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)
1935c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)            lastFontData = fontData;
1945c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)            if (m_fallbackFonts && fontData != primaryFont) {
1955c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)                // FIXME: This does a little extra work that could be avoided if
1965c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)                // glyphDataForCharacter() returned whether it chose to use a small caps font.
1975c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)                if (!m_font->isSmallCaps() || character == toUpper(character))
1985c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)                    m_fallbackFonts->add(fontData);
1995c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)                else {
2005c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)                    const GlyphData& uppercaseGlyphData = m_font->glyphDataForCharacter(toUpper(character), rtl);
2015c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)                    if (uppercaseGlyphData.fontData != primaryFont)
2025c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)                        m_fallbackFonts->add(uppercaseGlyphData.fontData);
2035c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)                }
2045c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)            }
2055c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)        }
2065c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)
2075c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)        if (hasExtraSpacing) {
2085c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)            // Account for letter-spacing.
2095c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)            if (width && m_font->letterSpacing())
2105c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)                width += m_font->letterSpacing();
2115c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)
2125c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)            static bool expandAroundIdeographs = Font::canExpandAroundIdeographsInComplexText();
2135c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)            bool treatAsSpace = Font::treatAsSpace(character);
2145c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)            if (treatAsSpace || (expandAroundIdeographs && Font::isCJKIdeographOrSymbol(character))) {
2155c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)                // Distribute the run's total expansion evenly over all expansion opportunities in the run.
2165c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)                if (m_expansion) {
2175c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)                    float previousExpansion = m_expansion;
2185c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)                    if (!treatAsSpace && !m_isAfterExpansion) {
2195c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)                        // Take the expansion opportunity before this ideograph.
2205c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)                        m_expansion -= m_expansionPerOpportunity;
2215c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)                        float expansionAtThisOpportunity = !m_run.applyWordRounding() ? m_expansionPerOpportunity : roundf(previousExpansion) - roundf(m_expansion);
2225c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)                        m_runWidthSoFar += expansionAtThisOpportunity;
2235c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)                        if (glyphBuffer) {
224926b001d589ce2f10facb93dd4b87578ea35a855Torne (Richard Coles)                            if (glyphBuffer->isEmpty()) {
225926b001d589ce2f10facb93dd4b87578ea35a855Torne (Richard Coles)                                if (m_forTextEmphasis)
226926b001d589ce2f10facb93dd4b87578ea35a855Torne (Richard Coles)                                    glyphBuffer->add(fontData->zeroWidthSpaceGlyph(), fontData, m_expansionPerOpportunity);
227926b001d589ce2f10facb93dd4b87578ea35a855Torne (Richard Coles)                                else
228926b001d589ce2f10facb93dd4b87578ea35a855Torne (Richard Coles)                                    glyphBuffer->add(fontData->spaceGlyph(), fontData, expansionAtThisOpportunity);
229926b001d589ce2f10facb93dd4b87578ea35a855Torne (Richard Coles)                            } else
2305c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)                                glyphBuffer->expandLastAdvance(expansionAtThisOpportunity);
2315c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)                        }
2325c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)                        previousExpansion = m_expansion;
2335c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)                    }
2345c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)                    if (m_run.allowsTrailingExpansion() || (m_run.ltr() && textIterator.currentCharacter() + advanceLength < static_cast<size_t>(m_run.length()))
2355c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)                        || (m_run.rtl() && textIterator.currentCharacter())) {
2365c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)                        m_expansion -= m_expansionPerOpportunity;
2375c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)                        width += !m_run.applyWordRounding() ? m_expansionPerOpportunity : roundf(previousExpansion) - roundf(m_expansion);
2385c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)                        m_isAfterExpansion = true;
2395c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)                    }
2405c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)                } else
2415c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)                    m_isAfterExpansion = false;
2425c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)
2435c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)                // Account for word spacing.
2445c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)                // We apply additional space between "words" by adding width to the space character.
245926b001d589ce2f10facb93dd4b87578ea35a855Torne (Richard Coles)                if (treatAsSpace && (character != '\t' || !m_run.allowTabs()) && (textIterator.currentCharacter() || character == noBreakSpace) && m_font->wordSpacing())
2465c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)                    width += m_font->wordSpacing();
2475c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)            } else
2485c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)                m_isAfterExpansion = false;
2495c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)        }
2505c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)
2515c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)        if (shouldApplyFontTransforms() && glyphBuffer && Font::treatAsSpace(character))
2525c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)            charactersTreatedAsSpace.append(make_pair(glyphBuffer->size(),
2535c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)                OriginalAdvancesForCharacterTreatedAsSpace(character == ' ', glyphBuffer->size() ? glyphBuffer->advanceAt(glyphBuffer->size() - 1) : 0, width)));
2545c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)
2555c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)        if (m_accountForGlyphBounds) {
2565c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)            bounds = fontData->boundsForGlyph(glyph);
2575c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)            if (!textIterator.currentCharacter())
2585c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)                m_firstGlyphOverflow = max<float>(0, -bounds.x());
2595c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)        }
2605c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)
2615c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)        if (m_forTextEmphasis && !Font::canReceiveTextEmphasis(character))
2625c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)            glyph = 0;
2635c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)
2645c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)        // Advance past the character we just dealt with.
2655c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)        textIterator.advance(advanceLength);
2665c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)
2675c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)        float oldWidth = width;
2685c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)
2695c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)        // Force characters that are used to determine word boundaries for the rounding hack
2705c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)        // to be integer width, so following words will start on an integer boundary.
2715c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)        if (m_run.applyWordRounding() && Font::isRoundingHackCharacter(character)) {
2725c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)            width = ceilf(width);
2735c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)
2745c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)            // Since widthSinceLastRounding can lose precision if we include measurements for
2755c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)            // preceding whitespace, we bypass it here.
2765c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)            m_runWidthSoFar += width;
2775c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)
2785c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)            // Since this is a rounding hack character, we should have reset this sum on the previous
2795c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)            // iteration.
2805c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)            ASSERT(!widthSinceLastRounding);
2815c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)        } else {
2825c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)            // Check to see if the next character is a "rounding hack character", if so, adjust
2835c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)            // width so that the total run width will be on an integer boundary.
2845c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)            if ((m_run.applyWordRounding() && textIterator.currentCharacter() < m_run.length() && Font::isRoundingHackCharacter(*(textIterator.characters())))
2855c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)                || (m_run.applyRunRounding() && textIterator.currentCharacter() >= m_run.length())) {
2865c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)                float totalWidth = widthSinceLastRounding + width;
2875c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)                widthSinceLastRounding = ceilf(totalWidth);
2885c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)                width += widthSinceLastRounding - totalWidth;
2895c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)                m_runWidthSoFar += widthSinceLastRounding;
2905c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)                widthSinceLastRounding = 0;
2915c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)            } else
2925c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)                widthSinceLastRounding += width;
2935c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)        }
2945c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)
2955c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)        if (glyphBuffer)
2965c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)            glyphBuffer->add(glyph, fontData, (rtl ? oldWidth + lastRoundingWidth : width));
2975c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)
2985c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)        lastRoundingWidth = width - oldWidth;
2995c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)
3005c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)        if (m_accountForGlyphBounds) {
3015c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)            m_maxGlyphBoundingBoxY = max(m_maxGlyphBoundingBoxY, bounds.maxY());
3025c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)            m_minGlyphBoundingBoxY = min(m_minGlyphBoundingBoxY, bounds.y());
3035c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)            m_lastGlyphOverflow = max<float>(0, bounds.maxX() - width);
3045c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)        }
3055c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)    }
3065c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)
3075c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)    if (shouldApplyFontTransforms())
3085c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)        m_runWidthSoFar += applyFontTransforms(glyphBuffer, m_run.ltr(), lastGlyphCount, lastFontData, m_typesettingFeatures, charactersTreatedAsSpace);
3095c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)
3105c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)    unsigned consumedCharacters = textIterator.currentCharacter() - m_currentCharacter;
3115c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)    m_currentCharacter = textIterator.currentCharacter();
3125c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)    m_runWidthSoFar += widthSinceLastRounding;
3135c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)    m_finalRoundingWidth = lastRoundingWidth;
3145c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)    return consumedCharacters;
3155c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)}
3165c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)
3175c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)unsigned WidthIterator::advance(int offset, GlyphBuffer* glyphBuffer)
3185c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles){
3195c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)    int length = m_run.length();
3205c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)
3215c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)    if (offset > length)
3225c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)        offset = length;
3235c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)
3245c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)    if (m_currentCharacter >= static_cast<unsigned>(offset))
3255c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)        return 0;
3265c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)
3275c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)    if (m_run.is8Bit()) {
3285c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)        Latin1TextIterator textIterator(m_run.data8(m_currentCharacter), m_currentCharacter, offset, length);
3295c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)        return advanceInternal(textIterator, glyphBuffer);
3305c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)    }
3315c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)
3325c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)    SurrogatePairAwareTextIterator textIterator(m_run.data16(m_currentCharacter), m_currentCharacter, offset, length);
3335c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)    return advanceInternal(textIterator, glyphBuffer);
3345c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)}
3355c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)
3365c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)bool WidthIterator::advanceOneCharacter(float& width, GlyphBuffer& glyphBuffer)
3375c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles){
3385c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)    int oldSize = glyphBuffer.size();
3395c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)    advance(m_currentCharacter + 1, &glyphBuffer);
3405c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)    float w = 0;
3415c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)    for (int i = oldSize; i < glyphBuffer.size(); ++i)
3425c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)        w += glyphBuffer.advanceAt(i);
3435c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)    width = w;
3445c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)    return glyphBuffer.size() > oldSize;
3455c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)}
3465c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)
3475c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)}
348