1/*
2 * Copyright (C) 2012, 2013 Research In Motion Limited. All rights reserved.
3 *
4 * This library is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU Lesser General Public
6 * License as published by the Free Software Foundation; either
7 * version 2 of the License, or (at your option) any later version.
8 *
9 * This library is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
12 * Lesser General Public License for more details.
13 *
14 * You should have received a copy of the GNU Lesser General Public
15 * License along with this library; if not, write to the Free Software
16 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
17 */
18
19#include "config.h"
20#include "SimpleFontData.h"
21
22#include "FloatRect.h"
23#include "Font.h"
24#include "FontCache.h"
25#include "FontDescription.h"
26#include "ITypeUtils.h"
27
28#include <fs_api.h>
29#include <unicode/normlzr.h>
30
31namespace WebCore {
32
33static inline float FSFixedToFloat(FS_FIXED n) { return n / 65536.0; }
34
35#define openTypeTag(a, b, c, d) (FS_ULONG)((a << 24) | (b << 16) | (c << 8) | d)
36
37void SimpleFontData::platformInit()
38{
39    FS_FIXED ascender = 0;
40    FS_FIXED descender = 0;
41    FS_FIXED leading = 0;
42    FsAscDescLeadSource source;
43    FS_LONG result;
44    if (m_platformData.size() > 0) {
45        // FIXME: hack! FS_get_ascender_descender_leading() returns ERR_NO_CURRENT_SFNT when size is 0, even though we called FS_set_scale and m_platformData.font()->cur_sfnt is not 0
46        result = FS_get_ascender_descender_leading(m_platformData.font(), &ascender, &descender, &leading, &source);
47        ASSERT_UNUSED(result, result == SUCCESS);
48    }
49
50    m_fontMetrics.setAscent(FS_ROUND(ascender));
51    m_fontMetrics.setDescent(FS_ROUND(descender));
52    m_fontMetrics.setLineGap(iTypeFixedToFloat(leading));
53    m_fontMetrics.setLineSpacing(lroundf(m_fontMetrics.ascent()) + lroundf(m_fontMetrics.descent()) + lroundf(m_fontMetrics.lineGap()));
54
55    FONT_METRICS metrics;
56    result = FS_font_metrics(m_platformData.font(), &metrics);
57    ASSERT_UNUSED(result, result == SUCCESS);
58
59    // m_fontMetrics.setUnitsPerEm(FS_get_design_units(m_platformData().font()));
60    m_fontMetrics.setUnitsPerEm(metrics.unitsPerEm);
61
62    FS_USHORT xRange = metrics.font_bbox.xMax - metrics.font_bbox.xMin;
63    m_maxCharWidth = roundf((xRange * roundf(m_platformData.size())) / metrics.unitsPerEm);
64
65    TTF_OS2 os2;
66    if (FS_get_table_structure(m_platformData.font(), TAG_OS2, &os2) == SUCCESS && os2.sxHeight && os2.xAvgCharWidth) {
67        FS_USHORT yppem = m_platformData.font()->lpm;
68        m_fontMetrics.setXHeight(static_cast<float>(os2.sxHeight) * yppem / metrics.unitsPerEm);
69        m_avgCharWidth = static_cast<float>(os2.xAvgCharWidth) * yppem / metrics.unitsPerEm;
70    } else {
71        // HACK
72        m_fontMetrics.setXHeight(m_fontMetrics.ascent() * 0.56);
73        m_fontMetrics.setHasXHeight(false);
74        m_avgCharWidth = m_fontMetrics.xHeight();
75
76        GlyphPage* glyphPageZero = GlyphPageTreeNode::getRootChild(this, 0)->page();
77
78        if (glyphPageZero) {
79            static const UChar32 xChar = 'x';
80            const Glyph xGlyph = glyphPageZero->glyphDataForCharacter(xChar).glyph;
81
82            if (xGlyph) {
83                // In widthForGlyph(), xGlyph will be compared with
84                // m_zeroWidthSpaceGlyph, which isn't initialized yet here.
85                // Initialize it with zero to make sure widthForGlyph() returns
86                // the right width.
87                m_zeroWidthSpaceGlyph = 0;
88                m_avgCharWidth = widthForGlyph(xGlyph);
89            }
90        }
91    }
92
93    if (m_platformData.orientation() == Vertical && !isTextOrientationFallback())
94        m_hasVerticalGlyphs = FS_get_table(m_platformData.font(), openTypeTag('v', 'h', 'e', 'a'), TBL_QUERY, 0)
95            || FS_get_table(m_platformData.font(), openTypeTag('V', 'O', 'R', 'G'), TBL_QUERY, 0);
96}
97
98void SimpleFontData::platformCharWidthInit()
99{
100}
101
102void SimpleFontData::platformDestroy()
103{
104}
105
106PassRefPtr<SimpleFontData> SimpleFontData::platformCreateScaledFontData(const FontDescription& fontDescription, float scaleFactor) const
107{
108    const float scaledSize = lroundf(fontDescription.computedSize() * scaleFactor);
109    return adoptRef(new SimpleFontData(
110        FontPlatformData(m_platformData.font()->cur_lfnt->name,
111            scaledSize,
112            m_platformData.syntheticBold(),
113            m_platformData.syntheticOblique(),
114            m_platformData.orientation(),
115            m_platformData.widthVariant()),
116        isCustomFont(), false));
117}
118
119bool SimpleFontData::containsCharacters(const UChar* characters, int length) const
120{
121    int position = 0;
122
123    while (position < length) {
124        // FIXME: use shaper?
125        UChar32 character;
126        int nextPosition = position;
127        U16_NEXT(characters, nextPosition, length, character);
128
129        FS_USHORT glyph = FS_map_char(m_platformData.font(), static_cast<FS_ULONG>(character));
130        if (!glyph)
131            return false;
132
133        position = nextPosition;
134    }
135
136    return true;
137}
138
139void SimpleFontData::determinePitch()
140{
141    m_treatAsFixedPitch = m_platformData.isFixedPitch();
142}
143
144FloatRect SimpleFontData::platformBoundsForGlyph(Glyph glyph) const
145{
146    FS_GLYPHMAP* glyphmap = FS_get_glyphmap(m_platformData.font(), glyph, FS_MAP_DISTANCEFIELD | FS_MAP_GRAYMAP8);
147    if (!glyphmap)
148        return FloatRect();
149
150    FloatRect bounds(glyphmap->lo_x, glyphmap->height - glyphmap->hi_y, glyphmap->width, glyphmap->height);
151    FS_free_char(m_platformData.font(), glyphmap);
152
153    return bounds;
154}
155
156float SimpleFontData::platformWidthForGlyph(Glyph glyph) const
157{
158    FS_SHORT idx, idy;
159    FS_FIXED dx, dy;
160    FS_FIXED s00, s01, s10, s11;
161    bool needsFakeBoldReset = m_platformData.syntheticBold() && m_treatAsFixedPitch;
162
163    if (needsFakeBoldReset) {
164        FS_get_scale(m_platformData.font(), &s00, &s01, &s10, &s11);
165        FS_set_bold_pct(m_platformData.font(), 0);
166        FS_set_scale(m_platformData.font(), s00, s01, s10, s11);
167    }
168
169    if (FS_get_advance(m_platformData.font(), glyph, FS_MAP_DISTANCEFIELD | FS_MAP_GRAYMAP8, &idx, &idy, &dx, &dy) != SUCCESS)
170        dx = 0;
171
172    if (needsFakeBoldReset) {
173        FS_set_bold_pct(m_platformData.font(), ITYPEFAKEBOLDAMOUNT);
174        FS_set_scale(m_platformData.font(), s00, s01, s10, s11);
175    }
176
177    return iTypeFixedToFloat(dx);
178}
179
180bool SimpleFontData::canRenderCombiningCharacterSequence(const UChar* characters, size_t length) const
181{
182    if (!m_combiningCharacterSequenceSupport)
183        m_combiningCharacterSequenceSupport = adoptPtr(new HashMap<String, bool>);
184
185    WTF::HashMap<String, bool>::AddResult addResult = m_combiningCharacterSequenceSupport->add(String(characters, length), false);
186    if (!addResult.isNewEntry)
187        return addResult.iterator->value;
188
189    UErrorCode error = U_ZERO_ERROR;
190    Vector<UChar, 4> normalizedCharacters(length);
191    int32_t normalizedLength = unorm_normalize(characters, length, UNORM_NFC, UNORM_UNICODE_3_2, &normalizedCharacters[0], length, &error);
192    if (U_FAILURE(error))
193        return false;
194
195    int position = 0;
196    while (position < normalizedLength) {
197        UChar32 character;
198        int nextPosition = position;
199        U16_NEXT(normalizedCharacters, nextPosition, normalizedLength, character);
200
201        if (!u_hasBinaryProperty(character, UCHAR_DEFAULT_IGNORABLE_CODE_POINT)) {
202            FS_USHORT glyph = FS_map_char(m_platformData.font(), static_cast<FS_ULONG>(character));
203            if (!glyph)
204                return false;
205        }
206
207        position = nextPosition;
208    }
209
210    addResult.iterator->value = true;
211    return true;
212}
213
214} // namespace WebCore
215