blob: f94a17b3d1c7001b6aeb96e2c159af12c4f5c1e1 [file] [log] [blame]
/*
* Copyright (C) 2012 Google Inc. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are
* met:
*
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above
* copyright notice, this list of conditions and the following disclaimer
* in the documentation and/or other materials provided with the
* distribution.
* * Neither the name of Google Inc. nor the names of its
* contributors may be used to endorse or promote products derived from
* this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#ifndef HarfBuzzShaper_h
#define HarfBuzzShaper_h
#include "platform/fonts/FontDescription.h"
#include "platform/fonts/SmallCapsIterator.h"
#include "platform/fonts/shaping/ShapeResult.h"
#include "platform/fonts/shaping/Shaper.h"
#include "platform/geometry/FloatPoint.h"
#include "platform/geometry/FloatRect.h"
#include "platform/text/TextRun.h"
#include "wtf/Allocator.h"
#include "wtf/Deque.h"
#include "wtf/HashSet.h"
#include "wtf/Vector.h"
#include "wtf/text/CharacterNames.h"
#include <hb.h>
#include <memory>
#include <unicode/uscript.h>
namespace blink {
class Font;
class GlyphBuffer;
class SimpleFontData;
class HarfBuzzShaper;
class UnicodeRangeSet;
// Shaping text runs is split into several stages: Run segmentation, shaping the
// initial segment, identify shaped and non-shaped sequences of the shaping
// result, and processing sub-runs by trying to shape them with a fallback font
// until the last resort font is reached.
//
// If caps formatting is requested, an additional lowercase/uppercase
// segmentation stage is required. In this stage, OpenType features in the font
// are matched against the requested formatting and formatting is synthesized as
// required by the CSS Level 3 Fonts Module.
//
// Going through one example - for simplicity without caps formatting - to
// illustrate the process: The following is a run of vertical text to be
// shaped. After run segmentation in RunSegmenter it is split into 4
// segments. The segments indicated by the segementation results showing the
// script, orientation information and small caps handling of the individual
// segment. The Japanese text at the beginning has script "Hiragana", does not
// need rotation when laid out vertically and does not need uppercasing when
// small caps is requested.
//
// 0 い
// 1 ろ
// 2 は USCRIPT_HIRAGANA,
// OrientationIterator::OrientationKeep,
// SmallCapsIterator::SmallCapsSameCase
//
// 3 a
// 4 ̄ (Combining Macron)
// 5 a
// 6 A USCRIPT_LATIN,
// OrientationIterator::OrientationRotateSideways,
// SmallCapsIterator::SmallCapsUppercaseNeeded
//
// 7 い
// 8 ろ
// 9 は USCRIPT_HIRAGANA,
// OrientationIterator::OrientationKeep,
// SmallCapsIterator::SmallCapsSameCase
//
//
// Let's assume the CSS for this text run is as follows:
// font-family: "Heiti SC", Tinos, sans-serif;
// where Tinos is a web font, defined as a composite font, with two sub ranges,
// one for Latin U+00-U+FF and one unrestricted unicode-range.
//
// FontFallbackIterator provides the shaper with Heiti SC, then Tinos of the
// restricted unicode-range, then the unrestricted full unicode-range Tinos,
// then a system sans-serif.
//
// The initial segment 0-2 to the shaper, together with the segmentation
// properties and the initial Heiti SC font. Characters 0-2 are shaped
// successfully with Heiti SC. The next segment, 3-5 is passed to the shaper.
// The shaper attempts to shape it with Heiti SC, which fails for the Combining
// Macron. So the shaping result for this segment would look similar to this.
//
// Glyphpos: 0 1 2 3
// Cluster: 0 0 2 3
// Glyph: a x a A (where x is .notdef)
//
// Now in the extractShapeResults() step we notice that there is more work to
// do, since Heiti SC does not have a glyph for the Combining Macron combined
// with an a. So, this cluster together with a Todo item for switching to the
// next font is put into HolesQueue.
//
// After shaping the initial segment, the remaining items in the HolesQueue are
// processed, picking them from the head of the queue. So, first, the next font
// is requested from the FontFallbackIterator. In this case, Tinos (for the
// range U+00-U+FF) comes back. Shaping using this font, assuming it is
// subsetted, fails again since there is no combining mark available. This
// triggers requesting yet another font. This time, the Tinos font for the full
// range. With this, shaping succeeds with the following HarfBuzz result:
//
// Glyphpos 0 1 2 3
// Cluster: 0 0 2 3
// Glyph: a ̄ a A (with glyph coordinates placing the ̄ above the first a)
//
// Now this sub run is successfully processed and can be appended to
// ShapeResult. A new ShapeResult::RunInfo is created. The logic in
// insertRunIntoShapeResult then takes care of merging the shape result into
// the right position the vector of RunInfos in ShapeResult.
//
// Shaping then continues analogously for the remaining Hiragana Japanese
// sub-run, and the result is inserted into ShapeResult as well.
class PLATFORM_EXPORT HarfBuzzShaper final : public Shaper {
public:
HarfBuzzShaper(const Font*, const TextRun&);
PassRefPtr<ShapeResult> shapeResult();
~HarfBuzzShaper() {}
enum HolesQueueItemAction { HolesQueueNextFont, HolesQueueRange };
struct HolesQueueItem {
DISALLOW_NEW_EXCEPT_PLACEMENT_NEW();
HolesQueueItemAction m_action;
unsigned m_startIndex;
unsigned m_numCharacters;
HolesQueueItem(HolesQueueItemAction action, unsigned start, unsigned num)
: m_action(action), m_startIndex(start), m_numCharacters(num){};
};
protected:
using FeaturesVector = Vector<hb_feature_t, 6>;
class CapsFeatureSettingsScopedOverlay final {
STACK_ALLOCATED()
public:
CapsFeatureSettingsScopedOverlay(FeaturesVector&,
FontDescription::FontVariantCaps);
CapsFeatureSettingsScopedOverlay() = delete;
~CapsFeatureSettingsScopedOverlay();
private:
void overlayCapsFeatures(FontDescription::FontVariantCaps);
void prependCounting(const hb_feature_t&);
FeaturesVector& m_features;
size_t m_countFeatures;
};
private:
void setFontFeatures();
void appendToHolesQueue(HolesQueueItemAction,
unsigned startIndex,
unsigned numCharacters);
void prependHolesQueue(HolesQueueItemAction,
unsigned startIndex,
unsigned numCharacters);
void splitUntilNextCaseChange(HolesQueueItem& currentQueueItem,
SmallCapsIterator::SmallCapsBehavior&);
inline bool shapeRange(hb_buffer_t* harfBuzzBuffer,
unsigned startIndex,
unsigned numCharacters,
const SimpleFontData* currentFont,
PassRefPtr<UnicodeRangeSet> currentFontRangeSet,
UScriptCode currentRunScript,
hb_language_t);
bool extractShapeResults(hb_buffer_t* harfBuzzBuffer,
ShapeResult*,
bool& fontCycleQueued,
const HolesQueueItem& currentQueueItem,
const SimpleFontData* currentFont,
UScriptCode currentRunScript,
bool isLastResort);
bool collectFallbackHintChars(Vector<UChar32>& hint);
void insertRunIntoShapeResult(
ShapeResult*,
std::unique_ptr<ShapeResult::RunInfo> runToInsert,
unsigned startGlyph,
unsigned numGlyphs,
hb_buffer_t*);
std::unique_ptr<UChar[]> m_normalizedBuffer;
unsigned m_normalizedBufferLength;
FeaturesVector m_features;
Deque<HolesQueueItem> m_holesQueue;
};
} // namespace blink
#endif // HarfBuzzShaper_h