Home | History | Annotate | Download | only in graphics
      1 /*
      2  * Copyright (C) 2005, 2008, 2010 Apple Inc. All rights reserved.
      3  * Copyright (C) 2006 Alexey Proskuryakov
      4  *
      5  * Redistribution and use in source and binary forms, with or without
      6  * modification, are permitted provided that the following conditions
      7  * are met:
      8  *
      9  * 1.  Redistributions of source code must retain the above copyright
     10  *     notice, this list of conditions and the following disclaimer.
     11  * 2.  Redistributions in binary form must reproduce the above copyright
     12  *     notice, this list of conditions and the following disclaimer in the
     13  *     documentation and/or other materials provided with the distribution.
     14  * 3.  Neither the name of Apple Computer, Inc. ("Apple") nor the names of
     15  *     its contributors may be used to endorse or promote products derived
     16  *     from this software without specific prior written permission.
     17  *
     18  * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY
     19  * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
     20  * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
     21  * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY
     22  * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
     23  * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
     24  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
     25  * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
     26  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
     27  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
     28  */
     29 
     30 #include "config.h"
     31 #include "SimpleFontData.h"
     32 
     33 #include "Font.h"
     34 #include "FontCache.h"
     35 
     36 #if ENABLE(SVG_FONTS)
     37 #include "SVGFontData.h"
     38 #include "SVGFontElement.h"
     39 #include "SVGFontFaceElement.h"
     40 #include "SVGGlyphElement.h"
     41 #endif
     42 
     43 #include <wtf/MathExtras.h>
     44 #include <wtf/UnusedParam.h>
     45 
     46 using namespace std;
     47 
     48 namespace WebCore {
     49 
     50 SimpleFontData::SimpleFontData(const FontPlatformData& platformData, bool isCustomFont, bool isLoading, bool isTextOrientationFallback)
     51     : m_maxCharWidth(-1)
     52     , m_avgCharWidth(-1)
     53     , m_platformData(platformData)
     54     , m_treatAsFixedPitch(false)
     55     , m_isCustomFont(isCustomFont)
     56     , m_isLoading(isLoading)
     57     , m_isTextOrientationFallback(isTextOrientationFallback)
     58     , m_isBrokenIdeographFallback(false)
     59     , m_hasVerticalGlyphs(false)
     60 {
     61     platformInit();
     62     platformGlyphInit();
     63     platformCharWidthInit();
     64 }
     65 
     66 #if ENABLE(SVG_FONTS)
     67 SimpleFontData::SimpleFontData(PassOwnPtr<SVGFontData> svgFontData, int size, bool syntheticBold, bool syntheticItalic)
     68     : m_platformData(FontPlatformData(size, syntheticBold, syntheticItalic))
     69     , m_treatAsFixedPitch(false)
     70     , m_svgFontData(svgFontData)
     71     , m_isCustomFont(true)
     72     , m_isLoading(false)
     73     , m_isTextOrientationFallback(false)
     74     , m_isBrokenIdeographFallback(false)
     75     , m_hasVerticalGlyphs(false)
     76 {
     77     SVGFontFaceElement* svgFontFaceElement = m_svgFontData->svgFontFaceElement();
     78     unsigned unitsPerEm = svgFontFaceElement->unitsPerEm();
     79 
     80     float scale = size;
     81     if (unitsPerEm)
     82         scale /= unitsPerEm;
     83 
     84     float xHeight = svgFontFaceElement->xHeight() * scale;
     85     float ascent = svgFontFaceElement->ascent() * scale;
     86     float descent = svgFontFaceElement->descent() * scale;
     87     float lineGap = 0.1f * size;
     88 
     89     SVGFontElement* associatedFontElement = svgFontFaceElement->associatedFontElement();
     90     if (!xHeight) {
     91         // Fallback if x_heightAttr is not specified for the font element.
     92         Vector<SVGGlyphIdentifier> letterXGlyphs;
     93         associatedFontElement->getGlyphIdentifiersForString(String("x", 1), letterXGlyphs);
     94         xHeight = letterXGlyphs.isEmpty() ? 2 * ascent / 3 : letterXGlyphs.first().horizontalAdvanceX * scale;
     95     }
     96 
     97     m_fontMetrics.setUnitsPerEm(unitsPerEm);
     98     m_fontMetrics.setAscent(ascent);
     99     m_fontMetrics.setDescent(descent);
    100     m_fontMetrics.setLineGap(lineGap);
    101     m_fontMetrics.setLineSpacing(roundf(ascent) + roundf(descent) + roundf(lineGap));
    102     m_fontMetrics.setXHeight(xHeight);
    103 
    104     Vector<SVGGlyphIdentifier> spaceGlyphs;
    105     associatedFontElement->getGlyphIdentifiersForString(String(" ", 1), spaceGlyphs);
    106     m_spaceWidth = spaceGlyphs.isEmpty() ? xHeight : spaceGlyphs.first().horizontalAdvanceX * scale;
    107 
    108     Vector<SVGGlyphIdentifier> numeralZeroGlyphs;
    109     associatedFontElement->getGlyphIdentifiersForString(String("0", 1), numeralZeroGlyphs);
    110     m_avgCharWidth = numeralZeroGlyphs.isEmpty() ? m_spaceWidth : numeralZeroGlyphs.first().horizontalAdvanceX * scale;
    111 
    112     Vector<SVGGlyphIdentifier> letterWGlyphs;
    113     associatedFontElement->getGlyphIdentifiersForString(String("W", 1), letterWGlyphs);
    114     m_maxCharWidth = letterWGlyphs.isEmpty() ? ascent : letterWGlyphs.first().horizontalAdvanceX * scale;
    115 
    116     // FIXME: is there a way we can get the space glyph from the SVGGlyphIdentifier above?
    117     m_spaceGlyph = 0;
    118     m_zeroWidthSpaceGlyph = 0;
    119     determinePitch();
    120     m_missingGlyphData.fontData = this;
    121     m_missingGlyphData.glyph = 0;
    122 }
    123 #endif
    124 
    125 #if !PLATFORM(QT)
    126 // Estimates of avgCharWidth and maxCharWidth for platforms that don't support accessing these values from the font.
    127 void SimpleFontData::initCharWidths()
    128 {
    129     GlyphPage* glyphPageZero = GlyphPageTreeNode::getRootChild(this, 0)->page();
    130 
    131     // Treat the width of a '0' as the avgCharWidth.
    132     if (m_avgCharWidth <= 0.f && glyphPageZero) {
    133         static const UChar32 digitZeroChar = '0';
    134         Glyph digitZeroGlyph = glyphPageZero->glyphDataForCharacter(digitZeroChar).glyph;
    135         if (digitZeroGlyph)
    136             m_avgCharWidth = widthForGlyph(digitZeroGlyph);
    137     }
    138 
    139     // If we can't retrieve the width of a '0', fall back to the x height.
    140     if (m_avgCharWidth <= 0.f)
    141         m_avgCharWidth = m_fontMetrics.xHeight();
    142 
    143     if (m_maxCharWidth <= 0.f)
    144         m_maxCharWidth = max(m_avgCharWidth, m_fontMetrics.floatAscent());
    145 }
    146 
    147 void SimpleFontData::platformGlyphInit()
    148 {
    149     GlyphPage* glyphPageZero = GlyphPageTreeNode::getRootChild(this, 0)->page();
    150     if (!glyphPageZero) {
    151         LOG_ERROR("Failed to get glyph page zero.");
    152         m_spaceGlyph = 0;
    153         m_spaceWidth = 0;
    154         determinePitch();
    155         m_zeroWidthSpaceGlyph = 0;
    156         m_missingGlyphData.fontData = this;
    157         m_missingGlyphData.glyph = 0;
    158         return;
    159     }
    160 
    161     m_zeroWidthSpaceGlyph = glyphPageZero->glyphDataForCharacter(0).glyph;
    162 
    163     // Nasty hack to determine if we should round or ceil space widths.
    164     // If the font is monospace or fake monospace we ceil to ensure that
    165     // every character and the space are the same width.  Otherwise we round.
    166     m_spaceGlyph = glyphPageZero->glyphDataForCharacter(' ').glyph;
    167     float width = widthForGlyph(m_spaceGlyph);
    168     m_spaceWidth = width;
    169     determinePitch();
    170 
    171     // Force the glyph for ZERO WIDTH SPACE to have zero width, unless it is shared with SPACE.
    172     // Helvetica is an example of a non-zero width ZERO WIDTH SPACE glyph.
    173     // See <http://bugs.webkit.org/show_bug.cgi?id=13178>
    174     // Ask for the glyph for 0 to avoid paging in ZERO WIDTH SPACE. Control characters, including 0,
    175     // are mapped to the ZERO WIDTH SPACE glyph.
    176     if (m_zeroWidthSpaceGlyph == m_spaceGlyph) {
    177         m_zeroWidthSpaceGlyph = 0;
    178         LOG_ERROR("Font maps SPACE and ZERO WIDTH SPACE to the same glyph. Glyph width will not be overridden.");
    179     }
    180 
    181     m_missingGlyphData.fontData = this;
    182     m_missingGlyphData.glyph = 0;
    183 }
    184 #endif
    185 
    186 SimpleFontData::~SimpleFontData()
    187 {
    188 #if ENABLE(SVG_FONTS)
    189     if (!m_svgFontData || !m_svgFontData->svgFontFaceElement())
    190 #endif
    191         platformDestroy();
    192 
    193     if (!isCustomFont())
    194         GlyphPageTreeNode::pruneTreeFontData(this);
    195 }
    196 
    197 const SimpleFontData* SimpleFontData::fontDataForCharacter(UChar32) const
    198 {
    199     return this;
    200 }
    201 
    202 bool SimpleFontData::isSegmented() const
    203 {
    204     return false;
    205 }
    206 
    207 SimpleFontData* SimpleFontData::verticalRightOrientationFontData() const
    208 {
    209     if (!m_derivedFontData)
    210         m_derivedFontData = DerivedFontData::create(isCustomFont());
    211     if (!m_derivedFontData->verticalRightOrientation) {
    212         FontPlatformData verticalRightPlatformData(m_platformData);
    213         verticalRightPlatformData.setOrientation(Horizontal);
    214         m_derivedFontData->verticalRightOrientation = new SimpleFontData(verticalRightPlatformData, isCustomFont(), false, true);
    215     }
    216     return m_derivedFontData->verticalRightOrientation.get();
    217 }
    218 
    219 SimpleFontData* SimpleFontData::uprightOrientationFontData() const
    220 {
    221     if (!m_derivedFontData)
    222         m_derivedFontData = DerivedFontData::create(isCustomFont());
    223     if (!m_derivedFontData->uprightOrientation)
    224         m_derivedFontData->uprightOrientation = new SimpleFontData(m_platformData, isCustomFont(), false, true);
    225     return m_derivedFontData->uprightOrientation.get();
    226 }
    227 
    228 SimpleFontData* SimpleFontData::brokenIdeographFontData() const
    229 {
    230     if (!m_derivedFontData)
    231         m_derivedFontData = DerivedFontData::create(isCustomFont());
    232     if (!m_derivedFontData->brokenIdeograph) {
    233         m_derivedFontData->brokenIdeograph = new SimpleFontData(m_platformData, isCustomFont(), false);
    234         m_derivedFontData->brokenIdeograph->m_isBrokenIdeographFallback = true;
    235     }
    236     return m_derivedFontData->brokenIdeograph.get();
    237 }
    238 
    239 #ifndef NDEBUG
    240 String SimpleFontData::description() const
    241 {
    242     if (isSVGFont())
    243         return "[SVG font]";
    244     if (isCustomFont())
    245         return "[custom font]";
    246 
    247     return platformData().description();
    248 }
    249 #endif
    250 
    251 PassOwnPtr<SimpleFontData::DerivedFontData> SimpleFontData::DerivedFontData::create(bool forCustomFont)
    252 {
    253     return adoptPtr(new DerivedFontData(forCustomFont));
    254 }
    255 
    256 SimpleFontData::DerivedFontData::~DerivedFontData()
    257 {
    258     if (!forCustomFont)
    259         return;
    260 
    261     if (smallCaps)
    262         GlyphPageTreeNode::pruneTreeCustomFontData(smallCaps.get());
    263     if (emphasisMark)
    264         GlyphPageTreeNode::pruneTreeCustomFontData(emphasisMark.get());
    265     if (brokenIdeograph)
    266         GlyphPageTreeNode::pruneTreeCustomFontData(brokenIdeograph.get());
    267     if (verticalRightOrientation)
    268         GlyphPageTreeNode::pruneTreeCustomFontData(verticalRightOrientation.get());
    269     if (uprightOrientation)
    270         GlyphPageTreeNode::pruneTreeCustomFontData(uprightOrientation.get());
    271 }
    272 
    273 } // namespace WebCore
    274