1/*
2 * Copyright (C) 1999 Lars Knoll (knoll@kde.org)
3 * Copyright (C) 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011 Apple Inc. All rights reserved.
4 * Copyright (C) 2013 Google Inc. All rights reserved.
5 *
6 * This library is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU Library General Public
8 * License as published by the Free Software Foundation; either
9 * version 2 of the License, or (at your option) any later version.
10 *
11 * This library is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14 * Library General Public License for more details.
15 *
16 * You should have received a copy of the GNU Library General Public License
17 * along with this library; see the file COPYING.LIB.  If not, write to
18 * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
19 * Boston, MA 02110-1301, USA.
20 *
21 */
22
23#include "config.h"
24#include "core/css/resolver/FontBuilder.h"
25
26#include "core/css/CSSCalculationValue.h"
27#include "core/css/CSSToLengthConversionData.h"
28#include "core/frame/LocalFrame.h"
29#include "core/frame/Settings.h"
30#include "core/rendering/RenderTheme.h"
31#include "core/rendering/RenderView.h"
32#include "core/rendering/TextAutosizer.h"
33#include "platform/fonts/FontDescription.h"
34#include "platform/text/LocaleToScriptMapping.h"
35
36namespace blink {
37
38// FIXME: This scoping class is a short-term fix to minimize the changes in
39// Font-constructing logic.
40class FontDescriptionChangeScope {
41    STACK_ALLOCATED();
42public:
43    FontDescriptionChangeScope(FontBuilder* fontBuilder)
44        : m_fontBuilder(fontBuilder)
45        , m_fontDescription(fontBuilder->m_style->fontDescription())
46    {
47    }
48
49    void reset() { m_fontDescription = FontDescription(); }
50    void set(const FontDescription& fontDescription) { m_fontDescription = fontDescription; }
51    FontDescription& fontDescription() { return m_fontDescription; }
52
53    ~FontDescriptionChangeScope()
54    {
55        m_fontBuilder->didChangeFontParameters(m_fontBuilder->m_style->setFontDescription(m_fontDescription));
56    }
57
58private:
59    RawPtrWillBeMember<FontBuilder> m_fontBuilder;
60    FontDescription m_fontDescription;
61};
62
63FontBuilder::FontBuilder()
64    : m_document(nullptr)
65    , m_style(0)
66    , m_fontDirty(false)
67{
68}
69
70void FontBuilder::initForStyleResolve(const Document& document, RenderStyle* style)
71{
72    ASSERT(document.frame());
73    m_document = &document;
74    m_style = style;
75    m_fontDirty = false;
76}
77
78inline static void setFontFamilyToStandard(FontDescription& fontDescription, const Document* document)
79{
80    if (!document || !document->settings())
81        return;
82
83    fontDescription.setGenericFamily(FontDescription::StandardFamily);
84    const AtomicString& standardFontFamily = document->settings()->genericFontFamilySettings().standard();
85    if (standardFontFamily.isEmpty())
86        return;
87
88    fontDescription.firstFamily().setFamily(standardFontFamily);
89    // FIXME: Why is this needed here?
90    fontDescription.firstFamily().appendFamily(nullptr);
91}
92
93void FontBuilder::setInitial(float effectiveZoom)
94{
95    ASSERT(m_document && m_document->settings());
96    if (!m_document || !m_document->settings())
97        return;
98
99    FontDescriptionChangeScope scope(this);
100
101    scope.reset();
102    setFontFamilyToStandard(scope.fontDescription(), m_document);
103    setSize(scope.fontDescription(), FontBuilder::initialSize());
104}
105
106void FontBuilder::inheritFrom(const FontDescription& fontDescription)
107{
108    FontDescriptionChangeScope scope(this);
109
110    scope.set(fontDescription);
111}
112
113void FontBuilder::didChangeFontParameters(bool changed)
114{
115    m_fontDirty |= changed;
116}
117
118void FontBuilder::fromSystemFont(CSSValueID valueId, float effectiveZoom)
119{
120    FontDescriptionChangeScope scope(this);
121
122    FontDescription fontDescription;
123    RenderTheme::theme().systemFont(valueId, fontDescription);
124
125    // Double-check and see if the theme did anything. If not, don't bother updating the font.
126    if (!fontDescription.isAbsoluteSize())
127        return;
128
129    // Make sure the rendering mode and printer font settings are updated.
130    const Settings* settings = m_document->settings();
131    ASSERT(settings); // If we're doing style resolution, this document should always be in a frame and thus have settings
132    if (!settings)
133        return;
134
135    // Handle the zoom factor.
136    fontDescription.setComputedSize(getComputedSizeFromSpecifiedSize(fontDescription, effectiveZoom, fontDescription.specifiedSize()));
137    scope.set(fontDescription);
138}
139
140void FontBuilder::setFontFamilyInitial()
141{
142    FontDescriptionChangeScope scope(this);
143
144    setFontFamilyToStandard(scope.fontDescription(), m_document);
145}
146
147void FontBuilder::setFontFamilyInherit(const FontDescription& parentFontDescription)
148{
149    FontDescriptionChangeScope scope(this);
150
151    scope.fontDescription().setGenericFamily(parentFontDescription.genericFamily());
152    scope.fontDescription().setFamily(parentFontDescription.family());
153}
154
155// FIXME: I am not convinced FontBuilder needs to know anything about CSSValues.
156void FontBuilder::setFontFamilyValue(CSSValue* value)
157{
158    FontDescriptionChangeScope scope(this);
159
160    if (!value->isValueList())
161        return;
162
163    FontFamily& firstFamily = scope.fontDescription().firstFamily();
164    FontFamily* currFamily = 0;
165
166    // Before mapping in a new font-family property, we should reset the generic family.
167    FixedPitchFontType oldFixedPitchFontType = scope.fontDescription().fixedPitchFontType();
168    scope.fontDescription().setGenericFamily(FontDescription::NoFamily);
169
170    for (CSSValueListIterator i = value; i.hasMore(); i.advance()) {
171        CSSValue* item = i.value();
172        if (!item->isPrimitiveValue())
173            continue;
174        CSSPrimitiveValue* contentValue = toCSSPrimitiveValue(item);
175        AtomicString face;
176        Settings* settings = m_document->settings();
177        if (contentValue->isString()) {
178            face = AtomicString(contentValue->getStringValue());
179        } else if (settings) {
180            switch (contentValue->getValueID()) {
181            case CSSValueWebkitBody:
182                face = settings->genericFontFamilySettings().standard();
183                break;
184            case CSSValueSerif:
185                face = FontFamilyNames::webkit_serif;
186                scope.fontDescription().setGenericFamily(FontDescription::SerifFamily);
187                break;
188            case CSSValueSansSerif:
189                face = FontFamilyNames::webkit_sans_serif;
190                scope.fontDescription().setGenericFamily(FontDescription::SansSerifFamily);
191                break;
192            case CSSValueCursive:
193                face = FontFamilyNames::webkit_cursive;
194                scope.fontDescription().setGenericFamily(FontDescription::CursiveFamily);
195                break;
196            case CSSValueFantasy:
197                face = FontFamilyNames::webkit_fantasy;
198                scope.fontDescription().setGenericFamily(FontDescription::FantasyFamily);
199                break;
200            case CSSValueMonospace:
201                face = FontFamilyNames::webkit_monospace;
202                scope.fontDescription().setGenericFamily(FontDescription::MonospaceFamily);
203                break;
204            case CSSValueWebkitPictograph:
205                face = FontFamilyNames::webkit_pictograph;
206                scope.fontDescription().setGenericFamily(FontDescription::PictographFamily);
207                break;
208            default:
209                break;
210            }
211        }
212
213        if (!face.isEmpty()) {
214            if (!currFamily) {
215                // Filling in the first family.
216                firstFamily.setFamily(face);
217                firstFamily.appendFamily(nullptr); // Remove any inherited family-fallback list.
218                currFamily = &firstFamily;
219            } else {
220                RefPtr<SharedFontFamily> newFamily = SharedFontFamily::create();
221                newFamily->setFamily(face);
222                currFamily->appendFamily(newFamily);
223                currFamily = newFamily.get();
224            }
225        }
226    }
227
228    // We can't call useFixedDefaultSize() until all new font families have been added
229    // If currFamily is non-zero then we set at least one family on this description.
230    if (!currFamily)
231        return;
232
233    if (scope.fontDescription().keywordSize() && scope.fontDescription().fixedPitchFontType() != oldFixedPitchFontType)
234        setSize(scope.fontDescription(), FontDescription::Size(scope.fontDescription().keywordSize(), 0.0f, false));
235}
236
237void FontBuilder::setWeight(FontWeight fontWeight)
238{
239    FontDescriptionChangeScope scope(this);
240
241    scope.fontDescription().setWeight(fontWeight);
242}
243
244void FontBuilder::setSize(const FontDescription::Size& size)
245{
246    FontDescriptionChangeScope scope(this);
247
248    setSize(scope.fontDescription(), size);
249}
250
251void FontBuilder::setStretch(FontStretch fontStretch)
252{
253    FontDescriptionChangeScope scope(this);
254
255    scope.fontDescription().setStretch(fontStretch);
256}
257
258void FontBuilder::setScript(const String& locale)
259{
260    FontDescriptionChangeScope scope(this);
261
262    scope.fontDescription().setLocale(locale);
263    scope.fontDescription().setScript(localeToScriptCodeForFontSelection(locale));
264}
265
266void FontBuilder::setStyle(FontStyle italic)
267{
268    FontDescriptionChangeScope scope(this);
269
270    scope.fontDescription().setStyle(italic);
271}
272
273void FontBuilder::setVariant(FontVariant smallCaps)
274{
275    FontDescriptionChangeScope scope(this);
276
277    scope.fontDescription().setVariant(smallCaps);
278}
279
280void FontBuilder::setVariantLigatures(const FontDescription::VariantLigatures& ligatures)
281{
282    FontDescriptionChangeScope scope(this);
283
284    scope.fontDescription().setVariantLigatures(ligatures);
285}
286
287void FontBuilder::setTextRendering(TextRenderingMode textRenderingMode)
288{
289    FontDescriptionChangeScope scope(this);
290
291    scope.fontDescription().setTextRendering(textRenderingMode);
292}
293
294void FontBuilder::setKerning(FontDescription::Kerning kerning)
295{
296    FontDescriptionChangeScope scope(this);
297
298    scope.fontDescription().setKerning(kerning);
299}
300
301void FontBuilder::setFontSmoothing(FontSmoothingMode foontSmoothingMode)
302{
303    FontDescriptionChangeScope scope(this);
304
305    scope.fontDescription().setFontSmoothing(foontSmoothingMode);
306}
307
308void FontBuilder::setFeatureSettings(PassRefPtr<FontFeatureSettings> settings)
309{
310    FontDescriptionChangeScope scope(this);
311
312    scope.fontDescription().setFeatureSettings(settings);
313}
314
315void FontBuilder::setSize(FontDescription& fontDescription, const FontDescription::Size& size)
316{
317    float specifiedSize = size.value;
318
319    if (!specifiedSize && size.keyword)
320        specifiedSize = FontSize::fontSizeForKeyword(m_document, size.keyword, fontDescription.fixedPitchFontType());
321
322    if (specifiedSize < 0)
323        return;
324
325    // Overly large font sizes will cause crashes on some platforms (such as Windows).
326    // Cap font size here to make sure that doesn't happen.
327    specifiedSize = std::min(maximumAllowedFontSize, specifiedSize);
328
329    fontDescription.setKeywordSize(size.keyword);
330    fontDescription.setSpecifiedSize(specifiedSize);
331    fontDescription.setIsAbsoluteSize(size.isAbsolute);
332}
333
334float FontBuilder::getComputedSizeFromSpecifiedSize(FontDescription& fontDescription, float effectiveZoom, float specifiedSize)
335{
336    float zoomFactor = effectiveZoom;
337    // FIXME: Why is this here!!!!?!
338    if (LocalFrame* frame = m_document->frame())
339        zoomFactor *= frame->textZoomFactor();
340
341    return FontSize::getComputedSizeFromSpecifiedSize(m_document, zoomFactor, fontDescription.isAbsoluteSize(), specifiedSize);
342}
343
344static void getFontAndGlyphOrientation(const RenderStyle* style, FontOrientation& fontOrientation, NonCJKGlyphOrientation& glyphOrientation)
345{
346    if (style->isHorizontalWritingMode()) {
347        fontOrientation = Horizontal;
348        glyphOrientation = NonCJKGlyphOrientationVerticalRight;
349        return;
350    }
351
352    switch (style->textOrientation()) {
353    case TextOrientationVerticalRight:
354        fontOrientation = Vertical;
355        glyphOrientation = NonCJKGlyphOrientationVerticalRight;
356        return;
357    case TextOrientationUpright:
358        fontOrientation = Vertical;
359        glyphOrientation = NonCJKGlyphOrientationUpright;
360        return;
361    case TextOrientationSideways:
362        if (style->writingMode() == LeftToRightWritingMode) {
363            // FIXME: This should map to sideways-left, which is not supported yet.
364            fontOrientation = Vertical;
365            glyphOrientation = NonCJKGlyphOrientationVerticalRight;
366            return;
367        }
368        fontOrientation = Horizontal;
369        glyphOrientation = NonCJKGlyphOrientationVerticalRight;
370        return;
371    case TextOrientationSidewaysRight:
372        fontOrientation = Horizontal;
373        glyphOrientation = NonCJKGlyphOrientationVerticalRight;
374        return;
375    default:
376        ASSERT_NOT_REACHED();
377        fontOrientation = Horizontal;
378        glyphOrientation = NonCJKGlyphOrientationVerticalRight;
379        return;
380    }
381}
382
383void FontBuilder::checkForOrientationChange(RenderStyle* style)
384{
385    FontOrientation fontOrientation;
386    NonCJKGlyphOrientation glyphOrientation;
387    getFontAndGlyphOrientation(style, fontOrientation, glyphOrientation);
388
389    FontDescriptionChangeScope scope(this);
390
391    if (scope.fontDescription().orientation() == fontOrientation && scope.fontDescription().nonCJKGlyphOrientation() == glyphOrientation)
392        return;
393
394    scope.fontDescription().setNonCJKGlyphOrientation(glyphOrientation);
395    scope.fontDescription().setOrientation(fontOrientation);
396}
397
398void FontBuilder::checkForGenericFamilyChange(RenderStyle* style, const RenderStyle* parentStyle)
399{
400    FontDescriptionChangeScope scope(this);
401
402    if (scope.fontDescription().isAbsoluteSize() || !parentStyle)
403        return;
404
405    const FontDescription& parentFontDescription = parentStyle->fontDescription();
406    if (scope.fontDescription().fixedPitchFontType() == parentFontDescription.fixedPitchFontType())
407        return;
408
409    // For now, lump all families but monospace together.
410    if (scope.fontDescription().genericFamily() != FontDescription::MonospaceFamily
411        && parentFontDescription.genericFamily() != FontDescription::MonospaceFamily)
412        return;
413
414    // We know the parent is monospace or the child is monospace, and that font
415    // size was unspecified. We want to scale our font size as appropriate.
416    // If the font uses a keyword size, then we refetch from the table rather than
417    // multiplying by our scale factor.
418    float size;
419    if (scope.fontDescription().keywordSize()) {
420        size = FontSize::fontSizeForKeyword(m_document, scope.fontDescription().keywordSize(), scope.fontDescription().fixedPitchFontType());
421    } else {
422        Settings* settings = m_document->settings();
423        float fixedScaleFactor = (settings && settings->defaultFixedFontSize() && settings->defaultFontSize())
424            ? static_cast<float>(settings->defaultFixedFontSize()) / settings->defaultFontSize()
425            : 1;
426        size = parentFontDescription.fixedPitchFontType() == FixedPitchFont ?
427            scope.fontDescription().specifiedSize() / fixedScaleFactor :
428            scope.fontDescription().specifiedSize() * fixedScaleFactor;
429    }
430
431    scope.fontDescription().setSpecifiedSize(size);
432    updateComputedSize(scope.fontDescription(), style);
433}
434
435void FontBuilder::updateComputedSize(RenderStyle* style, const RenderStyle* parentStyle)
436{
437    FontDescriptionChangeScope scope(this);
438    updateComputedSize(scope.fontDescription(), style);
439}
440
441void FontBuilder::updateComputedSize(FontDescription& fontDescription, RenderStyle* style)
442{
443    float computedSize = getComputedSizeFromSpecifiedSize(fontDescription, style->effectiveZoom(), fontDescription.specifiedSize());
444    float multiplier = style->textAutosizingMultiplier();
445    if (multiplier > 1)
446        computedSize = TextAutosizer::computeAutosizedFontSize(computedSize, multiplier);
447    fontDescription.setComputedSize(computedSize);
448}
449
450// FIXME: style param should come first
451void FontBuilder::createFont(PassRefPtrWillBeRawPtr<FontSelector> fontSelector, const RenderStyle* parentStyle, RenderStyle* style)
452{
453    if (!m_fontDirty)
454        return;
455
456    updateComputedSize(style, parentStyle);
457    checkForGenericFamilyChange(style, parentStyle);
458    checkForOrientationChange(style);
459    style->font().update(fontSelector);
460    m_fontDirty = false;
461}
462
463void FontBuilder::createFontForDocument(PassRefPtrWillBeRawPtr<FontSelector> fontSelector, RenderStyle* documentStyle)
464{
465    FontDescription fontDescription = FontDescription();
466    fontDescription.setLocale(documentStyle->locale());
467    fontDescription.setScript(localeToScriptCodeForFontSelection(documentStyle->locale()));
468
469    setFontFamilyToStandard(fontDescription, m_document);
470
471    setSize(fontDescription, FontDescription::Size(FontSize::initialKeywordSize(), 0.0f, false));
472    updateComputedSize(fontDescription, documentStyle);
473
474    FontOrientation fontOrientation;
475    NonCJKGlyphOrientation glyphOrientation;
476    getFontAndGlyphOrientation(documentStyle, fontOrientation, glyphOrientation);
477    fontDescription.setOrientation(fontOrientation);
478    fontDescription.setNonCJKGlyphOrientation(glyphOrientation);
479    documentStyle->setFontDescription(fontDescription);
480    documentStyle->font().update(fontSelector);
481}
482
483}
484