Home | History | Annotate | Download | only in skia
      1 /*
      2  * Copyright (c) 2008, 2009, Google Inc. All rights reserved.
      3  *
      4  * Redistribution and use in source and binary forms, with or without
      5  * modification, are permitted provided that the following conditions are
      6  * met:
      7  *
      8  *     * Redistributions of source code must retain the above copyright
      9  * notice, this list of conditions and the following disclaimer.
     10  *     * Redistributions in binary form must reproduce the above
     11  * copyright notice, this list of conditions and the following disclaimer
     12  * in the documentation and/or other materials provided with the
     13  * distribution.
     14  *     * Neither the name of Google Inc. nor the names of its
     15  * contributors may be used to endorse or promote products derived from
     16  * this software without specific prior written permission.
     17  *
     18  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
     19  * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
     20  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
     21  * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
     22  * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
     23  * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
     24  * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
     25  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
     26  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
     27  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
     28  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
     29  */
     30 
     31 #include "config.h"
     32 #include "platform/fonts/SimpleFontData.h"
     33 
     34 #include <unicode/normlzr.h>
     35 #include "SkPaint.h"
     36 #include "SkPath.h"
     37 #include "SkTypeface.h"
     38 #include "SkTypes.h"
     39 #include "SkUtils.h"
     40 #include "platform/fonts/FontDescription.h"
     41 #include "platform/fonts/GlyphPage.h"
     42 #include "platform/fonts/VDMXParser.h"
     43 #include "platform/geometry/FloatRect.h"
     44 #include "wtf/unicode/Unicode.h"
     45 
     46 namespace blink {
     47 
     48 // This is the largest VDMX table which we'll try to load and parse.
     49 static const size_t maxVDMXTableSize = 1024 * 1024; // 1 MB
     50 
     51 void SimpleFontData::platformInit()
     52 {
     53     if (!m_platformData.size()) {
     54         m_fontMetrics.reset();
     55         m_avgCharWidth = 0;
     56         m_maxCharWidth = 0;
     57         return;
     58     }
     59 
     60     SkPaint paint;
     61     SkPaint::FontMetrics metrics;
     62 
     63     m_platformData.setupPaint(&paint);
     64     paint.getFontMetrics(&metrics);
     65     SkTypeface* face = paint.getTypeface();
     66     ASSERT(face);
     67 
     68     int vdmxAscent = 0, vdmxDescent = 0;
     69     bool isVDMXValid = false;
     70 
     71 #if OS(LINUX) || OS(ANDROID)
     72     // Manually digging up VDMX metrics is only applicable when bytecode hinting using FreeType.
     73     // With GDI, the metrics will already have taken this into account (as needed).
     74     // With DirectWrite or CoreText, no bytecode hinting is ever done.
     75     // This code should be pushed into FreeType (hinted font metrics).
     76     static const uint32_t vdmxTag = SkSetFourByteTag('V', 'D', 'M', 'X');
     77     int pixelSize = m_platformData.size() + 0.5;
     78     if (!paint.isAutohinted()
     79         &&    (paint.getHinting() == SkPaint::kFull_Hinting
     80             || paint.getHinting() == SkPaint::kNormal_Hinting))
     81     {
     82         size_t vdmxSize = face->getTableSize(vdmxTag);
     83         if (vdmxSize && vdmxSize < maxVDMXTableSize) {
     84             uint8_t* vdmxTable = (uint8_t*) fastMalloc(vdmxSize);
     85             if (vdmxTable
     86                 && face->getTableData(vdmxTag, 0, vdmxSize, vdmxTable) == vdmxSize
     87                 && parseVDMX(&vdmxAscent, &vdmxDescent, vdmxTable, vdmxSize, pixelSize))
     88                 isVDMXValid = true;
     89             fastFree(vdmxTable);
     90         }
     91     }
     92 #endif
     93 
     94     float ascent;
     95     float descent;
     96 
     97     // Beware those who step here: This code is designed to match Win32 font
     98     // metrics *exactly* (except the adjustment of ascent/descent on Linux/Android).
     99     if (isVDMXValid) {
    100         ascent = vdmxAscent;
    101         descent = -vdmxDescent;
    102     } else {
    103         ascent = SkScalarRoundToInt(-metrics.fAscent);
    104         descent = SkScalarRoundToInt(metrics.fDescent);
    105 #if OS(LINUX) || OS(ANDROID)
    106         // When subpixel positioning is enabled, if the descent is rounded down, the descent part
    107         // of the glyph may be truncated when displayed in a 'overflow: hidden' container.
    108         // To avoid that, borrow 1 unit from the ascent when possible.
    109         // FIXME: This can be removed if sub-pixel ascent/descent is supported.
    110         if (platformData().fontRenderStyle().useSubpixelPositioning && descent < SkScalarToFloat(metrics.fDescent) && ascent >= 1) {
    111             ++descent;
    112             --ascent;
    113         }
    114 #endif
    115     }
    116 
    117     m_fontMetrics.setAscent(ascent);
    118     m_fontMetrics.setDescent(descent);
    119 
    120     float xHeight;
    121     if (metrics.fXHeight) {
    122         xHeight = metrics.fXHeight;
    123         m_fontMetrics.setXHeight(xHeight);
    124     } else {
    125         xHeight = ascent * 0.56; // Best guess from Windows font metrics.
    126         m_fontMetrics.setXHeight(xHeight);
    127         m_fontMetrics.setHasXHeight(false);
    128     }
    129 
    130     float lineGap = SkScalarToFloat(metrics.fLeading);
    131     m_fontMetrics.setLineGap(lineGap);
    132     m_fontMetrics.setLineSpacing(lroundf(ascent) + lroundf(descent) + lroundf(lineGap));
    133 
    134     if (platformData().orientation() == Vertical && !isTextOrientationFallback()) {
    135         static const uint32_t vheaTag = SkSetFourByteTag('v', 'h', 'e', 'a');
    136         static const uint32_t vorgTag = SkSetFourByteTag('V', 'O', 'R', 'G');
    137         size_t vheaSize = face->getTableSize(vheaTag);
    138         size_t vorgSize = face->getTableSize(vorgTag);
    139         if ((vheaSize > 0) || (vorgSize > 0))
    140             m_hasVerticalGlyphs = true;
    141     }
    142 
    143     // In WebKit/WebCore/platform/graphics/SimpleFontData.cpp, m_spaceWidth is
    144     // calculated for us, but we need to calculate m_maxCharWidth and
    145     // m_avgCharWidth in order for text entry widgets to be sized correctly.
    146 #if OS(WIN)
    147     m_maxCharWidth = SkScalarRoundToInt(metrics.fMaxCharWidth);
    148 
    149     // Older version of the DirectWrite API doesn't implement support for max
    150     // char width. Fall back on a multiple of the ascent. This is entirely
    151     // arbitrary but comes pretty close to the expected value in most cases.
    152     if (m_maxCharWidth < 1)
    153         m_maxCharWidth = ascent * 2;
    154 #else
    155     // FIXME: This seems incorrect and should probably use fMaxCharWidth as
    156     // the code path above.
    157     SkScalar xRange = metrics.fXMax - metrics.fXMin;
    158     m_maxCharWidth = SkScalarRoundToInt(xRange * SkScalarRoundToInt(m_platformData.size()));
    159 #endif
    160 
    161     if (metrics.fAvgCharWidth)
    162         m_avgCharWidth = SkScalarRoundToInt(metrics.fAvgCharWidth);
    163     else {
    164         m_avgCharWidth = xHeight;
    165 
    166         GlyphPage* glyphPageZero = GlyphPageTreeNode::getRootChild(this, 0)->page();
    167 
    168         if (glyphPageZero) {
    169             static const UChar32 xChar = 'x';
    170             const Glyph xGlyph = glyphPageZero->glyphForCharacter(xChar);
    171 
    172             if (xGlyph) {
    173                 // In widthForGlyph(), xGlyph will be compared with
    174                 // m_zeroWidthSpaceGlyph, which isn't initialized yet here.
    175                 // Initialize it with zero to make sure widthForGlyph() returns
    176                 // the right width.
    177                 m_zeroWidthSpaceGlyph = 0;
    178                 m_avgCharWidth = widthForGlyph(xGlyph);
    179             }
    180         }
    181     }
    182 
    183     if (int unitsPerEm = face->getUnitsPerEm())
    184         m_fontMetrics.setUnitsPerEm(unitsPerEm);
    185 }
    186 
    187 void SimpleFontData::platformCharWidthInit()
    188 {
    189     // charwidths are set in platformInit.
    190 }
    191 
    192 void SimpleFontData::platformDestroy()
    193 {
    194 }
    195 
    196 PassRefPtr<SimpleFontData> SimpleFontData::platformCreateScaledFontData(const FontDescription& fontDescription, float scaleFactor) const
    197 {
    198     const float scaledSize = lroundf(fontDescription.computedSize() * scaleFactor);
    199     return SimpleFontData::create(FontPlatformData(m_platformData, scaledSize), isCustomFont() ? CustomFontData::create() : nullptr);
    200 }
    201 
    202 void SimpleFontData::determinePitch()
    203 {
    204     m_treatAsFixedPitch = platformData().isFixedPitch();
    205 }
    206 
    207 static inline void getSkiaBoundsForGlyph(SkPaint& paint, Glyph glyph, SkRect& bounds)
    208 {
    209     paint.setTextEncoding(SkPaint::kGlyphID_TextEncoding);
    210 
    211     SkPath path;
    212     paint.getTextPath(&glyph, sizeof(glyph), 0, 0, &path);
    213     bounds = path.getBounds();
    214 
    215     if (!paint.isSubpixelText()) {
    216         SkIRect ir;
    217         bounds.round(&ir);
    218         bounds.set(ir);
    219     }
    220 }
    221 
    222 FloatRect SimpleFontData::platformBoundsForGlyph(Glyph glyph) const
    223 {
    224     if (!m_platformData.size())
    225         return FloatRect();
    226 
    227     SkASSERT(sizeof(glyph) == 2); // compile-time assert
    228 
    229     SkPaint paint;
    230     m_platformData.setupPaint(&paint);
    231 
    232     SkRect bounds;
    233     getSkiaBoundsForGlyph(paint, glyph, bounds);
    234     return FloatRect(bounds);
    235 }
    236 
    237 float SimpleFontData::platformWidthForGlyph(Glyph glyph) const
    238 {
    239     if (!m_platformData.size())
    240         return 0;
    241 
    242     SkASSERT(sizeof(glyph) == 2); // compile-time assert
    243 
    244     SkPaint paint;
    245 
    246     m_platformData.setupPaint(&paint);
    247 
    248     paint.setTextEncoding(SkPaint::kGlyphID_TextEncoding);
    249     SkScalar width = paint.measureText(&glyph, 2);
    250     if (!paint.isSubpixelText())
    251         width = SkScalarRoundToInt(width);
    252     return SkScalarToFloat(width);
    253 }
    254 
    255 bool SimpleFontData::canRenderCombiningCharacterSequence(const UChar* characters, size_t length) const
    256 {
    257     if (!m_combiningCharacterSequenceSupport)
    258         m_combiningCharacterSequenceSupport = adoptPtr(new HashMap<String, bool>);
    259 
    260     WTF::HashMap<String, bool>::AddResult addResult = m_combiningCharacterSequenceSupport->add(String(characters, length), false);
    261     if (!addResult.isNewEntry)
    262         return addResult.storedValue->value;
    263 
    264     UErrorCode error = U_ZERO_ERROR;
    265     Vector<UChar, 4> normalizedCharacters(length);
    266     int32_t normalizedLength = unorm_normalize(characters, length, UNORM_NFC, UNORM_UNICODE_3_2, &normalizedCharacters[0], length, &error);
    267     // Can't render if we have an error or no composition occurred.
    268     if (U_FAILURE(error) || (static_cast<size_t>(normalizedLength) == length))
    269         return false;
    270 
    271     SkPaint paint;
    272     m_platformData.setupPaint(&paint);
    273     paint.setTextEncoding(SkPaint::kUTF16_TextEncoding);
    274     if (paint.textToGlyphs(&normalizedCharacters[0], normalizedLength * 2, 0)) {
    275         addResult.storedValue->value = true;
    276         return true;
    277     }
    278     return false;
    279 }
    280 
    281 bool SimpleFontData::fillGlyphPage(GlyphPage* pageToFill, unsigned offset, unsigned length, UChar* buffer, unsigned bufferLength) const
    282 {
    283     if (SkUTF16_IsHighSurrogate(buffer[bufferLength-1])) {
    284         SkDebugf("%s last char is high-surrogate", __FUNCTION__);
    285         return false;
    286     }
    287 
    288     SkAutoSTMalloc<GlyphPage::size, uint16_t> glyphStorage(length);
    289 
    290     uint16_t* glyphs = glyphStorage.get();
    291     SkTypeface* typeface = platformData().typeface();
    292     typeface->charsToGlyphs(buffer, SkTypeface::kUTF16_Encoding, glyphs, length);
    293 
    294     bool haveGlyphs = false;
    295     for (unsigned i = 0; i < length; i++) {
    296         if (glyphs[i]) {
    297             pageToFill->setGlyphDataForIndex(offset + i, glyphs[i], this);
    298             haveGlyphs = true;
    299         }
    300     }
    301 
    302     return haveGlyphs;
    303 }
    304 
    305 } // namespace blink
    306