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
46namespace blink {
47
48// This is the largest VDMX table which we'll try to load and parse.
49static const size_t maxVDMXTableSize = 1024 * 1024; // 1 MB
50
51void 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
187void SimpleFontData::platformCharWidthInit()
188{
189    // charwidths are set in platformInit.
190}
191
192void SimpleFontData::platformDestroy()
193{
194}
195
196PassRefPtr<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
202void SimpleFontData::determinePitch()
203{
204    m_treatAsFixedPitch = platformData().isFixedPitch();
205}
206
207static 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
222FloatRect 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
237float 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
255bool 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
281bool 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