diff --git a/Libraries/LibTTF/Cmap.cpp b/Libraries/LibTTF/Cmap.cpp index afc94180d0..bef847599c 100644 --- a/Libraries/LibTTF/Cmap.cpp +++ b/Libraries/LibTTF/Cmap.cpp @@ -24,7 +24,8 @@ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -#include "Font.h" +#include "Cmap.h" +#include namespace TTF { @@ -32,7 +33,7 @@ extern u16 be_u16(const u8* ptr); extern u32 be_u32(const u8* ptr); extern i16 be_i16(const u8* ptr); -Font::Cmap::Subtable::Platform Font::Cmap::Subtable::platform_id() const +Cmap::Subtable::Platform Cmap::Subtable::platform_id() const { switch (m_raw_platform_id) { case 0: return Platform::Unicode; @@ -43,7 +44,7 @@ Font::Cmap::Subtable::Platform Font::Cmap::Subtable::platform_id() const } } -Font::Cmap::Subtable::Format Font::Cmap::Subtable::format() const +Cmap::Subtable::Format Cmap::Subtable::format() const { switch (be_u16(m_slice.offset_pointer(0))) { case 0: return Format::ByteEncoding; @@ -59,12 +60,12 @@ Font::Cmap::Subtable::Format Font::Cmap::Subtable::format() const } } -u32 Font::Cmap::num_subtables() const +u32 Cmap::num_subtables() const { return be_u16(m_slice.offset_pointer((u32) Offsets::NumTables)); } -Optional Font::Cmap::subtable(u32 index) const +Optional Cmap::subtable(u32 index) const { if (index >= num_subtables()) { return {}; @@ -79,7 +80,7 @@ Optional Font::Cmap::subtable(u32 index) const } // FIXME: This only handles formats 4 (SegmentToDelta) and 12 (SegmentedCoverage) for now. -u32 Font::Cmap::Subtable::glyph_id_for_codepoint(u32 codepoint) const +u32 Cmap::Subtable::glyph_id_for_codepoint(u32 codepoint) const { switch (format()) { case Format::SegmentToDelta: @@ -91,7 +92,7 @@ u32 Font::Cmap::Subtable::glyph_id_for_codepoint(u32 codepoint) const } } -u32 Font::Cmap::Subtable::glyph_id_for_codepoint_table_4(u32 codepoint) const +u32 Cmap::Subtable::glyph_id_for_codepoint_table_4(u32 codepoint) const { u32 segcount_x2 = be_u16(m_slice.offset_pointer((u32) Table4Offsets::SegCountX2)); if (m_slice.size() < segcount_x2 * (u32) Table4Sizes::NonConstMultiplier + (u32) Table4Sizes::Constant) { @@ -118,7 +119,7 @@ u32 Font::Cmap::Subtable::glyph_id_for_codepoint_table_4(u32 codepoint) const return 0; } -u32 Font::Cmap::Subtable::glyph_id_for_codepoint_table_12(u32 codepoint) const +u32 Cmap::Subtable::glyph_id_for_codepoint_table_12(u32 codepoint) const { u32 num_groups = be_u32(m_slice.offset_pointer((u32) Table12Offsets::NumGroups)); ASSERT(m_slice.size() >= (u32) Table12Sizes::Header + (u32) Table12Sizes::Record * num_groups); @@ -137,7 +138,7 @@ u32 Font::Cmap::Subtable::glyph_id_for_codepoint_table_12(u32 codepoint) const return 0; } -u32 Font::Cmap::glyph_id_for_codepoint(u32 codepoint) const +u32 Cmap::glyph_id_for_codepoint(u32 codepoint) const { auto opt_subtable = subtable(m_active_index); if (!opt_subtable.has_value()) { diff --git a/Libraries/LibTTF/Cmap.h b/Libraries/LibTTF/Cmap.h new file mode 100644 index 0000000000..83dd48f88e --- /dev/null +++ b/Libraries/LibTTF/Cmap.h @@ -0,0 +1,131 @@ +/* + * Copyright (c) 2020, Srimanta Barua + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * 2. 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. + * + * 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 HOLDER 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. + */ + +#pragma once + +#include + +namespace TTF { + +class Cmap { +public: + class Subtable { + public: + enum class Platform { + Unicode = 0, + Macintosh = 1, + Windows = 3, + Custom = 4, + }; + enum class Format { + ByteEncoding = 0, + HighByte = 2, + SegmentToDelta = 4, + TrimmedTable = 6, + Mixed16And32 = 8, + TrimmedArray = 10, + SegmentedCoverage = 12, + ManyToOneRange = 13, + UnicodeVariationSequences = 14, + }; + enum class WindowsEncoding { + UnicodeBMP = 1, + UnicodeFullRepertoire = 10, + }; + + Subtable(const ByteBuffer& slice, u16 platform_id, u16 encoding_id) + : m_slice(slice) + , m_raw_platform_id(platform_id) + , m_encoding_id(encoding_id) + { + } + // Returns 0 if glyph not found. This corresponds to the "missing glyph" + u32 glyph_id_for_codepoint(u32 codepoint) const; + Platform platform_id() const; + u16 encoding_id() const { return m_encoding_id; } + Format format() const; + + private: + enum class Table4Offsets { + SegCountX2 = 6, + EndConstBase = 14, + StartConstBase = 16, + DeltaConstBase = 16, + RangeConstBase = 16, + GlyphOffsetConstBase = 16, + }; + enum class Table4Sizes { + Constant = 16, + NonConstMultiplier = 4, + }; + enum class Table12Offsets { + NumGroups = 12, + Record_StartCode = 16, + Record_EndCode = 20, + Record_StartGlyph = 24, + }; + enum class Table12Sizes { + Header = 16, + Record = 12, + }; + + u32 glyph_id_for_codepoint_table_4(u32 codepoint) const; + u32 glyph_id_for_codepoint_table_12(u32 codepoint) const; + + ByteBuffer m_slice; + u16 m_raw_platform_id; + u16 m_encoding_id; + }; + + Cmap() {} + Cmap(const ByteBuffer& slice) + : m_slice(slice) + { + ASSERT(m_slice.size() > (size_t) Sizes::TableHeader); + } + + u32 num_subtables() const; + Optional subtable(u32 index) const; + void set_active_index(u32 index) { m_active_index = index; } + // Returns 0 if glyph not found. This corresponds to the "missing glyph" + u32 glyph_id_for_codepoint(u32 codepoint) const; + +private: + enum class Offsets { + NumTables = 2, + EncodingRecord_EncodingID = 2, + EncodingRecord_Offset = 4, + }; + enum class Sizes { + TableHeader = 4, + EncodingRecord = 8, + }; + + ByteBuffer m_slice; + u32 m_active_index { UINT32_MAX }; +}; + +} \ No newline at end of file diff --git a/Libraries/LibTTF/Font.cpp b/Libraries/LibTTF/Font.cpp index 293388ba89..dc3157c0de 100644 --- a/Libraries/LibTTF/Font.cpp +++ b/Libraries/LibTTF/Font.cpp @@ -24,7 +24,10 @@ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ +#include "Cmap.h" #include "Font.h" +#include "Glyf.h" +#include "Tables.h" #include #include #include @@ -59,37 +62,37 @@ u32 tag_from_str(const char *str) return be_u32((const u8*) str); } -u16 Font::Head::units_per_em() const +u16 Head::units_per_em() const { return be_u16(m_slice.offset_pointer((u32) Offsets::UnitsPerEM)); } -i16 Font::Head::xmin() const +i16 Head::xmin() const { return be_i16(m_slice.offset_pointer((u32) Offsets::XMin)); } -i16 Font::Head::ymin() const +i16 Head::ymin() const { return be_i16(m_slice.offset_pointer((u32) Offsets::YMin)); } -i16 Font::Head::xmax() const +i16 Head::xmax() const { return be_i16(m_slice.offset_pointer((u32) Offsets::XMax)); } -i16 Font::Head::ymax() const +i16 Head::ymax() const { return be_i16(m_slice.offset_pointer((u32) Offsets::YMax)); } -u16 Font::Head::lowest_recommended_ppem() const +u16 Head::lowest_recommended_ppem() const { return be_u16(m_slice.offset_pointer((u32) Offsets::LowestRecPPEM)); } -Font::IndexToLocFormat Font::Head::index_to_loc_format() const +IndexToLocFormat Head::index_to_loc_format() const { i16 raw = be_i16(m_slice.offset_pointer((u32) Offsets::IndexToLocFormat)); switch (raw) { @@ -102,32 +105,32 @@ Font::IndexToLocFormat Font::Head::index_to_loc_format() const } } -i16 Font::Hhea::ascender() const +i16 Hhea::ascender() const { return be_i16(m_slice.offset_pointer((u32) Offsets::Ascender)); } -i16 Font::Hhea::descender() const +i16 Hhea::descender() const { return be_i16(m_slice.offset_pointer((u32) Offsets::Descender)); } -i16 Font::Hhea::line_gap() const +i16 Hhea::line_gap() const { return be_i16(m_slice.offset_pointer((u32) Offsets::LineGap)); } -u16 Font::Hhea::advance_width_max() const +u16 Hhea::advance_width_max() const { return be_u16(m_slice.offset_pointer((u32) Offsets::AdvanceWidthMax)); } -u16 Font::Hhea::number_of_h_metrics() const +u16 Hhea::number_of_h_metrics() const { return be_u16(m_slice.offset_pointer((u32) Offsets::NumberOfHMetrics)); } -Font::GlyphHorizontalMetrics Font::Hmtx::get_glyph_horizontal_metrics(u32 glyph_id) const +GlyphHorizontalMetrics Hmtx::get_glyph_horizontal_metrics(u32 glyph_id) const { ASSERT(glyph_id < m_num_glyphs); if (glyph_id < m_number_of_h_metrics) { @@ -148,7 +151,7 @@ Font::GlyphHorizontalMetrics Font::Hmtx::get_glyph_horizontal_metrics(u32 glyph_ }; } -u16 Font::Maxp::num_glyphs() const +u16 Maxp::num_glyphs() const { return be_u16(m_slice.offset_pointer((u32) Offsets::NumGlyphs)); } @@ -244,13 +247,14 @@ Font::Font(ByteBuffer&& buffer, u32 offset) ASSERT(glyf_slice.has_value()); // Load the tables. - m_head = Head(head_slice.value()); - m_hhea = Hhea(hhea_slice.value()); - m_maxp = Maxp(maxp_slice.value()); - m_hmtx = Hmtx(hmtx_slice.value(), m_maxp.num_glyphs(), m_hhea.number_of_h_metrics()); + m_head_slice = head_slice.value(); + m_hhea_slice = hhea_slice.value(); + m_maxp_slice = maxp_slice.value(); + m_hmtx_slice = hmtx_slice.value(); + m_loca_slice = loca_slice.value(); + m_glyf_slice = glyf_slice.value(); + m_cmap = Cmap(cmap_slice.value()); - m_loca = Loca(loca_slice.value(), m_maxp.num_glyphs(), m_head.index_to_loc_format()); - m_glyf = Glyf(glyf_slice.value()); // Select cmap table. FIXME: Do this better. Right now, just looks for platform "Windows" // and corresponding encoding "Unicode full repertoire", or failing that, "Unicode BMP" @@ -275,10 +279,11 @@ Font::Font(ByteBuffer&& buffer, u32 offset) ScaledFontMetrics Font::metrics(float x_scale, float y_scale) const { - auto ascender = m_hhea.ascender() * y_scale; - auto descender = m_hhea.descender() * y_scale; - auto line_gap = m_hhea.line_gap() * y_scale; - auto advance_width_max = m_hhea.advance_width_max() * x_scale; + Hhea hhea(m_hhea_slice); + auto ascender = hhea.ascender() * y_scale; + auto descender = hhea.descender() * y_scale; + auto line_gap = hhea.line_gap() * y_scale; + auto advance_width_max = hhea.advance_width_max() * x_scale; return ScaledFontMetrics { .ascender = (int) roundf(ascender), .descender = (int) roundf(descender), @@ -287,14 +292,22 @@ ScaledFontMetrics Font::metrics(float x_scale, float y_scale) const }; } +// FIXME: "loca" and "glyf" are not available for CFF fonts. ScaledGlyphMetrics Font::glyph_metrics(u32 glyph_id, float x_scale, float y_scale) const { - if (glyph_id >= m_maxp.num_glyphs()) { + u32 num_glyphs = glyph_count(); + u16 number_of_h_metrics = Hhea(m_hhea_slice).number_of_h_metrics(); + auto index_to_loc_format = Head(m_head_slice).index_to_loc_format(); + Hmtx hmtx(m_hmtx_slice, num_glyphs, number_of_h_metrics); + Loca loca(m_loca_slice, num_glyphs, index_to_loc_format); + Glyf glyf(m_glyf_slice); + + if (glyph_id >= num_glyphs) { glyph_id = 0; } - auto horizontal_metrics = m_hmtx.get_glyph_horizontal_metrics(glyph_id); - auto glyph_offset = m_loca.get_glyph_offset(glyph_id); - auto glyph = m_glyf.glyph(glyph_offset); + auto horizontal_metrics = hmtx.get_glyph_horizontal_metrics(glyph_id); + auto glyph_offset = loca.get_glyph_offset(glyph_id); + auto glyph = glyf.glyph(glyph_offset); int ascender = glyph.ascender(); int descender = glyph.descender(); return ScaledGlyphMetrics { @@ -308,20 +321,35 @@ ScaledGlyphMetrics Font::glyph_metrics(u32 glyph_id, float x_scale, float y_scal // FIXME: "loca" and "glyf" are not available for CFF fonts. RefPtr Font::raster_glyph(u32 glyph_id, float x_scale, float y_scale) const { - if (glyph_id >= m_maxp.num_glyphs()) { + u32 num_glyphs = glyph_count(); + auto index_to_loc_format = Head(m_head_slice).index_to_loc_format(); + Loca loca(m_loca_slice, num_glyphs, index_to_loc_format); + Glyf glyf(m_glyf_slice); + + if (glyph_id >= num_glyphs) { glyph_id = 0; } - auto glyph_offset = m_loca.get_glyph_offset(glyph_id); - auto glyph = m_glyf.glyph(glyph_offset); + auto glyph_offset = loca.get_glyph_offset(glyph_id); + auto glyph = glyf.glyph(glyph_offset); return glyph.raster(x_scale, y_scale, [&](u16 glyph_id) { - if (glyph_id >= m_maxp.num_glyphs()) { + if (glyph_id >= num_glyphs) { glyph_id = 0; } - auto glyph_offset = m_loca.get_glyph_offset(glyph_id); - return m_glyf.glyph(glyph_offset); + auto glyph_offset = loca.get_glyph_offset(glyph_id); + return glyf.glyph(glyph_offset); }); } +u32 Font::glyph_count() const +{ + return Maxp(m_maxp_slice).num_glyphs(); +} + +u16 Font::units_per_em() const +{ + return Head(m_head_slice).units_per_em(); +} + int ScaledFont::width(const StringView& string) const { Utf8View utf8 { string }; diff --git a/Libraries/LibTTF/Font.h b/Libraries/LibTTF/Font.h index e1d798610c..cf34c43ea8 100644 --- a/Libraries/LibTTF/Font.h +++ b/Libraries/LibTTF/Font.h @@ -26,16 +26,13 @@ #pragma once +#include "Cmap.h" #include -#include #include -#include #include #include -#include #include #include -#include #define POINTS_PER_INCH 72.0f #define DEFAULT_DPI 96 @@ -85,376 +82,20 @@ private: ScaledFontMetrics metrics(float x_scale, float y_scale) const; ScaledGlyphMetrics glyph_metrics(u32 glyph_id, float x_scale, float y_scale) const; RefPtr raster_glyph(u32 glyph_id, float x_scale, float y_scale) const; - u32 glyph_count() const { return m_maxp.num_glyphs(); } - - class Rasterizer { - public: - Rasterizer(Gfx::IntSize); - void draw_path(Gfx::Path&); - RefPtr accumulate(); - - private: - void draw_line(Gfx::FloatPoint, Gfx::FloatPoint); - - Gfx::IntSize m_size; - FixedArray m_data; - }; - - enum class IndexToLocFormat { - Offset16, - Offset32, - }; - - class Head { - public: - Head() {} - Head(const ByteBuffer& slice) - : m_slice(slice) - { - ASSERT(m_slice.size() >= (size_t) Sizes::Table); - } - u16 units_per_em() const; - i16 xmin() const; - i16 ymin() const; - i16 xmax() const; - i16 ymax() const; - u16 lowest_recommended_ppem() const; - IndexToLocFormat index_to_loc_format() const; - - private: - enum class Offsets { - UnitsPerEM = 18, - XMin = 36, - YMin = 38, - XMax = 40, - YMax = 42, - LowestRecPPEM = 46, - IndexToLocFormat = 50, - }; - enum class Sizes { - Table = 54, - }; - - ByteBuffer m_slice; - }; - - class Hhea { - public: - Hhea() {} - Hhea(const ByteBuffer& slice) - : m_slice(slice) - { - ASSERT(m_slice.size() >= (size_t) Sizes::Table); - } - i16 ascender() const; - i16 descender() const; - i16 line_gap() const; - u16 advance_width_max() const; - u16 number_of_h_metrics() const; - - private: - enum class Offsets { - Ascender = 4, - Descender = 6, - LineGap = 8, - AdvanceWidthMax = 10, - NumberOfHMetrics = 34, - }; - enum class Sizes { - Table = 36, - }; - - ByteBuffer m_slice; - }; - - class Maxp { - public: - Maxp() {} - Maxp(const ByteBuffer& slice) - : m_slice(slice) - { - ASSERT(m_slice.size() >= (size_t) Sizes::TableV0p5); - } - u16 num_glyphs() const; - - private: - enum class Offsets { - NumGlyphs = 4 - }; - enum class Sizes { - TableV0p5 = 6, - }; - - ByteBuffer m_slice; - }; - - struct GlyphHorizontalMetrics { - u16 advance_width; - i16 left_side_bearing; - }; - - class Hmtx { - public: - Hmtx() {} - Hmtx(const ByteBuffer& slice, u32 num_glyphs, u32 number_of_h_metrics) - : m_slice(slice) - , m_num_glyphs(num_glyphs) - , m_number_of_h_metrics(number_of_h_metrics) - { - ASSERT(m_slice.size() >= number_of_h_metrics * (u32) Sizes::LongHorMetric + (num_glyphs - number_of_h_metrics) * (u32) Sizes::LeftSideBearing); - } - GlyphHorizontalMetrics get_glyph_horizontal_metrics(u32 glyph_id) const; - - private: - enum class Sizes { - LongHorMetric = 4, - LeftSideBearing = 2 - }; - - ByteBuffer m_slice; - u32 m_num_glyphs; - u32 m_number_of_h_metrics; - }; - - class Cmap { - public: - class Subtable { - public: - enum class Platform { - Unicode = 0, - Macintosh = 1, - Windows = 3, - Custom = 4, - }; - enum class Format { - ByteEncoding = 0, - HighByte = 2, - SegmentToDelta = 4, - TrimmedTable = 6, - Mixed16And32 = 8, - TrimmedArray = 10, - SegmentedCoverage = 12, - ManyToOneRange = 13, - UnicodeVariationSequences = 14, - }; - enum class WindowsEncoding { - UnicodeBMP = 1, - UnicodeFullRepertoire = 10, - }; - - Subtable(const ByteBuffer& slice, u16 platform_id, u16 encoding_id) - : m_slice(slice) - , m_raw_platform_id(platform_id) - , m_encoding_id(encoding_id) - { - } - // Returns 0 if glyph not found. This corresponds to the "missing glyph" - u32 glyph_id_for_codepoint(u32 codepoint) const; - Platform platform_id() const; - u16 encoding_id() const { return m_encoding_id; } - Format format() const; - - private: - enum class Table4Offsets { - SegCountX2 = 6, - EndConstBase = 14, - StartConstBase = 16, - DeltaConstBase = 16, - RangeConstBase = 16, - GlyphOffsetConstBase = 16, - }; - enum class Table4Sizes { - Constant = 16, - NonConstMultiplier = 4, - }; - enum class Table12Offsets { - NumGroups = 12, - Record_StartCode = 16, - Record_EndCode = 20, - Record_StartGlyph = 24, - }; - enum class Table12Sizes { - Header = 16, - Record = 12, - }; - - u32 glyph_id_for_codepoint_table_4(u32 codepoint) const; - u32 glyph_id_for_codepoint_table_12(u32 codepoint) const; - - ByteBuffer m_slice; - u16 m_raw_platform_id; - u16 m_encoding_id; - }; - - Cmap() {} - Cmap(const ByteBuffer& slice) - : m_slice(slice) - { - ASSERT(m_slice.size() > (size_t) Sizes::TableHeader); - } - - u32 num_subtables() const; - Optional subtable(u32 index) const; - void set_active_index(u32 index) { m_active_index = index; } - // Returns 0 if glyph not found. This corresponds to the "missing glyph" - u32 glyph_id_for_codepoint(u32 codepoint) const; - - private: - enum class Offsets { - NumTables = 2, - EncodingRecord_EncodingID = 2, - EncodingRecord_Offset = 4, - }; - enum class Sizes { - TableHeader = 4, - EncodingRecord = 8, - }; - - ByteBuffer m_slice; - u32 m_active_index { UINT32_MAX }; - }; - - class Loca { - public: - Loca() {} - Loca(const ByteBuffer& slice, u32 num_glyphs, IndexToLocFormat index_to_loc_format) - : m_slice(slice) - , m_num_glyphs(num_glyphs) - , m_index_to_loc_format(index_to_loc_format) - { - switch (m_index_to_loc_format) { - case IndexToLocFormat::Offset16: - ASSERT(m_slice.size() >= m_num_glyphs * 2); - break; - case IndexToLocFormat::Offset32: - ASSERT(m_slice.size() >= m_num_glyphs * 4); - break; - } - } - u32 get_glyph_offset(u32 glyph_id) const; - - private: - ByteBuffer m_slice; - u32 m_num_glyphs; - IndexToLocFormat m_index_to_loc_format; - }; - - class Glyf { - public: - class Glyph { - public: - Glyph(const ByteBuffer& slice, i16 xmin, i16 ymin, i16 xmax, i16 ymax, i16 num_contours = -1) - : m_xmin(xmin) - , m_ymin(ymin) - , m_xmax(xmax) - , m_ymax(ymax) - , m_num_contours(num_contours) - , m_slice(move(slice)) - { - if (m_num_contours >= 0) { - m_type = Type::Simple; - } - } - template - RefPtr raster(float x_scale, float y_scale, GlyphCb glyph_callback) const - { - switch (m_type) { - case Type::Simple: - return raster_simple(x_scale, y_scale); - case Type::Composite: - return raster_composite(x_scale, y_scale, glyph_callback); - } - ASSERT_NOT_REACHED(); - } - int ascender() const { return m_ymax; } - int descender() const { return m_ymin; } - - private: - enum class Type { - Simple, - Composite, - }; - - class ComponentIterator { - public: - struct Item { - u16 glyph_id; - Gfx::AffineTransform affine; - }; - - ComponentIterator(const ByteBuffer& slice) - : m_slice(slice) - { - } - Optional next(); - - private: - ByteBuffer m_slice; - bool m_has_more { true }; - u32 m_offset { 0 }; - }; - - void raster_inner(Rasterizer&, Gfx::AffineTransform&) const; - RefPtr raster_simple(float x_scale, float y_scale) const; - template - RefPtr raster_composite(float x_scale, float y_scale, GlyphCb glyph_callback) const - { - u32 width = (u32) (ceil((m_xmax - m_xmin) * x_scale)) + 1; - u32 height = (u32) (ceil((m_ymax - m_ymin) * y_scale)) + 1; - Rasterizer rasterizer(Gfx::IntSize(width, height)); - auto affine = Gfx::AffineTransform().scale(x_scale, -y_scale).translate(-m_xmin, -m_ymax); - ComponentIterator component_iterator(m_slice); - while (true) { - auto opt_item = component_iterator.next(); - if (!opt_item.has_value()) { - break; - } - auto item = opt_item.value(); - auto affine_here = affine * item.affine; - auto glyph = glyph_callback(item.glyph_id); - glyph.raster_inner(rasterizer, affine_here); - } - return rasterizer.accumulate(); - } - - Type m_type { Type::Composite }; - i16 m_xmin; - i16 m_ymin; - i16 m_xmax; - i16 m_ymax; - i16 m_num_contours { -1 }; - ByteBuffer m_slice; - }; - - Glyf() {} - Glyf(const ByteBuffer& slice) - : m_slice(move(slice)) - { - } - Glyph glyph(u32 offset) const; - - private: - enum class Offsets { - XMin = 2, - YMin = 4, - XMax = 6, - YMax = 8, - }; - enum class Sizes { - GlyphHeader = 10, - }; - - ByteBuffer m_slice; - }; + u32 glyph_count() const; + u16 units_per_em() const; + // This owns the font data ByteBuffer m_buffer; - Head m_head; - Hhea m_hhea; - Maxp m_maxp; - Hmtx m_hmtx; + // These are all non-owning slices + ByteBuffer m_head_slice; + ByteBuffer m_hhea_slice; + ByteBuffer m_maxp_slice; + ByteBuffer m_hmtx_slice; + ByteBuffer m_loca_slice; + ByteBuffer m_glyf_slice; + // These are stateful wrappers around tables Cmap m_cmap; - Loca m_loca; - Glyf m_glyf; friend ScaledFont; }; @@ -464,7 +105,7 @@ public: ScaledFont(RefPtr font, float point_width, float point_height, unsigned dpi_x = DEFAULT_DPI, unsigned dpi_y = DEFAULT_DPI) : m_font(font) { - float units_per_em = m_font->m_head.units_per_em(); + float units_per_em = m_font->units_per_em(); m_x_scale = (point_width * dpi_x) / (POINTS_PER_INCH * units_per_em); m_y_scale = (point_height * dpi_y) / (POINTS_PER_INCH * units_per_em); } diff --git a/Libraries/LibTTF/Glyf.cpp b/Libraries/LibTTF/Glyf.cpp index 7e6b703fcf..f04d62422f 100644 --- a/Libraries/LibTTF/Glyf.cpp +++ b/Libraries/LibTTF/Glyf.cpp @@ -24,7 +24,7 @@ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -#include "Font.h" +#include "Glyf.h" #include #include @@ -147,7 +147,7 @@ private: Gfx::AffineTransform m_affine; }; -Optional Font::Glyf::Glyph::ComponentIterator::next() { +Optional Glyf::Glyph::ComponentIterator::next() { if (!m_has_more) { return {}; } @@ -208,7 +208,7 @@ Optional Font::Glyf::Glyph::Componen }; } -Font::Rasterizer::Rasterizer(Gfx::IntSize size) +Rasterizer::Rasterizer(Gfx::IntSize size) : m_size(size) , m_data(m_size.width() * m_size.height()) { @@ -217,14 +217,14 @@ Font::Rasterizer::Rasterizer(Gfx::IntSize size) } } -void Font::Rasterizer::draw_path(Gfx::Path& path) +void Rasterizer::draw_path(Gfx::Path& path) { for (auto& line : path.split_lines()) { draw_line(line.from, line.to); } } -RefPtr Font::Rasterizer::accumulate() +RefPtr Rasterizer::accumulate() { auto bitmap = Gfx::Bitmap::create(Gfx::BitmapFormat::RGBA32, m_size); Color base_color = Color::from_rgb(0xffffff); @@ -246,7 +246,7 @@ RefPtr Font::Rasterizer::accumulate() return bitmap; } -void Font::Rasterizer::draw_line(Gfx::FloatPoint p0, Gfx::FloatPoint p1) +void Rasterizer::draw_line(Gfx::FloatPoint p0, Gfx::FloatPoint p1) { // FIXME: Shift x and y according to dy/dx if (p0.x() < 0.0) { p0.set_x(roundf(p0.x())); } @@ -254,7 +254,6 @@ void Font::Rasterizer::draw_line(Gfx::FloatPoint p0, Gfx::FloatPoint p1) if (p1.x() < 0.0) { p1.set_x(roundf(p1.x())); } if (p1.y() < 0.0) { p1.set_y(roundf(p1.y())); } - dbg() << "m_size: " << m_size << " | p0: (" << p0.x() << ", " << p0.y() << ") | p1: (" << p1.x() << ", " << p1.y() << ")"; ASSERT(p0.x() >= 0.0 && p0.y() >= 0.0 && p0.x() <= m_size.width() && p0.y() <= m_size.height()); ASSERT(p1.x() >= 0.0 && p1.y() >= 0.0 && p1.x() <= m_size.width() && p1.y() <= m_size.height()); @@ -319,7 +318,7 @@ void Font::Rasterizer::draw_line(Gfx::FloatPoint p0, Gfx::FloatPoint p1) } } -u32 Font::Loca::get_glyph_offset(u32 glyph_id) const +u32 Loca::get_glyph_offset(u32 glyph_id) const { ASSERT(glyph_id < m_num_glyphs); switch (m_index_to_loc_format) { @@ -363,7 +362,7 @@ static void get_ttglyph_offsets(const ByteBuffer& slice, u32 num_points, u32 fla *y_offset = *x_offset + x_size; } -void Font::Glyf::Glyph::raster_inner(Rasterizer& rasterizer, Gfx::AffineTransform& affine) const +void Glyf::Glyph::raster_inner(Rasterizer& rasterizer, Gfx::AffineTransform& affine) const { // Get offets for flags, x, and y. u16 num_points = be_u16(m_slice.offset_pointer((m_num_contours - 1) * 2)) + 1; @@ -461,7 +460,7 @@ void Font::Glyf::Glyph::raster_inner(Rasterizer& rasterizer, Gfx::AffineTransfor rasterizer.draw_path(path); } -RefPtr Font::Glyf::Glyph::raster_simple(float x_scale, float y_scale) const +RefPtr Glyf::Glyph::raster_simple(float x_scale, float y_scale) const { u32 width = (u32) (ceil((m_xmax - m_xmin) * x_scale)) + 2; u32 height = (u32) (ceil((m_ymax - m_ymin) * y_scale)) + 2; @@ -471,7 +470,7 @@ RefPtr Font::Glyf::Glyph::raster_simple(float x_scale, float y_scal return rasterizer.accumulate(); } -Font::Glyf::Glyph Font::Glyf::glyph(u32 offset) const +Glyf::Glyph Glyf::glyph(u32 offset) const { ASSERT(m_slice.size() >= offset + (u32) Sizes::GlyphHeader); i16 num_contours = be_i16(m_slice.offset_pointer(offset)); diff --git a/Libraries/LibTTF/Glyf.h b/Libraries/LibTTF/Glyf.h new file mode 100644 index 0000000000..db1d6cb699 --- /dev/null +++ b/Libraries/LibTTF/Glyf.h @@ -0,0 +1,182 @@ +/* + * Copyright (c) 2020, Srimanta Barua + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * 2. 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. + * + * 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 HOLDER 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. + */ + +#pragma once + +#include "Tables.h" +#include +#include +#include +#include +#include + +namespace TTF { + +class Rasterizer { +public: + Rasterizer(Gfx::IntSize); + void draw_path(Gfx::Path&); + RefPtr accumulate(); + +private: + void draw_line(Gfx::FloatPoint, Gfx::FloatPoint); + + Gfx::IntSize m_size; + FixedArray m_data; +}; + +class Loca { +public: + Loca(const ByteBuffer& slice, u32 num_glyphs, IndexToLocFormat index_to_loc_format) + : m_slice(slice) + , m_num_glyphs(num_glyphs) + , m_index_to_loc_format(index_to_loc_format) + { + switch (m_index_to_loc_format) { + case IndexToLocFormat::Offset16: + ASSERT(m_slice.size() >= m_num_glyphs * 2); + break; + case IndexToLocFormat::Offset32: + ASSERT(m_slice.size() >= m_num_glyphs * 4); + break; + } + } + u32 get_glyph_offset(u32 glyph_id) const; + +private: + ByteBuffer m_slice; + u32 m_num_glyphs; + IndexToLocFormat m_index_to_loc_format; +}; + +class Glyf { +public: + class Glyph { + public: + Glyph(const ByteBuffer& slice, i16 xmin, i16 ymin, i16 xmax, i16 ymax, i16 num_contours = -1) + : m_xmin(xmin) + , m_ymin(ymin) + , m_xmax(xmax) + , m_ymax(ymax) + , m_num_contours(num_contours) + , m_slice(slice) + { + if (m_num_contours >= 0) { + m_type = Type::Simple; + } + } + template + RefPtr raster(float x_scale, float y_scale, GlyphCb glyph_callback) const + { + switch (m_type) { + case Type::Simple: + return raster_simple(x_scale, y_scale); + case Type::Composite: + return raster_composite(x_scale, y_scale, glyph_callback); + } + ASSERT_NOT_REACHED(); + } + int ascender() const { return m_ymax; } + int descender() const { return m_ymin; } + + private: + enum class Type { + Simple, + Composite, + }; + + class ComponentIterator { + public: + struct Item { + u16 glyph_id; + Gfx::AffineTransform affine; + }; + + ComponentIterator(const ByteBuffer& slice) + : m_slice(slice) + { + } + Optional next(); + + private: + ByteBuffer m_slice; + bool m_has_more { true }; + u32 m_offset { 0 }; + }; + + void raster_inner(Rasterizer&, Gfx::AffineTransform&) const; + RefPtr raster_simple(float x_scale, float y_scale) const; + template + RefPtr raster_composite(float x_scale, float y_scale, GlyphCb glyph_callback) const + { + u32 width = (u32) (ceil((m_xmax - m_xmin) * x_scale)) + 1; + u32 height = (u32) (ceil((m_ymax - m_ymin) * y_scale)) + 1; + Rasterizer rasterizer(Gfx::IntSize(width, height)); + auto affine = Gfx::AffineTransform().scale(x_scale, -y_scale).translate(-m_xmin, -m_ymax); + ComponentIterator component_iterator(m_slice); + while (true) { + auto opt_item = component_iterator.next(); + if (!opt_item.has_value()) { + break; + } + auto item = opt_item.value(); + auto affine_here = affine * item.affine; + auto glyph = glyph_callback(item.glyph_id); + glyph.raster_inner(rasterizer, affine_here); + } + return rasterizer.accumulate(); + } + + Type m_type { Type::Composite }; + i16 m_xmin; + i16 m_ymin; + i16 m_xmax; + i16 m_ymax; + i16 m_num_contours { -1 }; + ByteBuffer m_slice; + }; + + Glyf(const ByteBuffer& slice) + : m_slice(slice) + { + } + Glyph glyph(u32 offset) const; + +private: + enum class Offsets { + XMin = 2, + YMin = 4, + XMax = 6, + YMax = 8, + }; + enum class Sizes { + GlyphHeader = 10, + }; + + ByteBuffer m_slice; +}; + +} \ No newline at end of file diff --git a/Libraries/LibTTF/Tables.h b/Libraries/LibTTF/Tables.h new file mode 100644 index 0000000000..583f5358b4 --- /dev/null +++ b/Libraries/LibTTF/Tables.h @@ -0,0 +1,145 @@ +/* + * Copyright (c) 2020, Srimanta Barua + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * 2. 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. + * + * 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 HOLDER 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. + */ + +#pragma once + +#include + +namespace TTF { + +enum class IndexToLocFormat { + Offset16, + Offset32, +}; + +class Head { +public: + Head(const ByteBuffer& slice) + : m_slice(slice) + { + ASSERT(m_slice.size() >= (size_t) Sizes::Table); + } + u16 units_per_em() const; + i16 xmin() const; + i16 ymin() const; + i16 xmax() const; + i16 ymax() const; + u16 lowest_recommended_ppem() const; + IndexToLocFormat index_to_loc_format() const; + +private: + enum class Offsets { + UnitsPerEM = 18, + XMin = 36, + YMin = 38, + XMax = 40, + YMax = 42, + LowestRecPPEM = 46, + IndexToLocFormat = 50, + }; + enum class Sizes { + Table = 54, + }; + + ByteBuffer m_slice; +}; + +class Hhea { +public: + Hhea(const ByteBuffer& slice) + : m_slice(slice) + { + ASSERT(m_slice.size() >= (size_t) Sizes::Table); + } + i16 ascender() const; + i16 descender() const; + i16 line_gap() const; + u16 advance_width_max() const; + u16 number_of_h_metrics() const; + +private: + enum class Offsets { + Ascender = 4, + Descender = 6, + LineGap = 8, + AdvanceWidthMax = 10, + NumberOfHMetrics = 34, + }; + enum class Sizes { + Table = 36, + }; + + ByteBuffer m_slice; +}; + +class Maxp { +public: + Maxp(const ByteBuffer& slice) + : m_slice(slice) + { + ASSERT(m_slice.size() >= (size_t) Sizes::TableV0p5); + } + u16 num_glyphs() const; + +private: + enum class Offsets { + NumGlyphs = 4 + }; + enum class Sizes { + TableV0p5 = 6, + }; + + ByteBuffer m_slice; +}; + +struct GlyphHorizontalMetrics { + u16 advance_width; + i16 left_side_bearing; +}; + +class Hmtx { +public: + Hmtx(const ByteBuffer& slice, u32 num_glyphs, u32 number_of_h_metrics) + : m_slice(slice) + , m_num_glyphs(num_glyphs) + , m_number_of_h_metrics(number_of_h_metrics) + { + ASSERT(m_slice.size() >= number_of_h_metrics * (u32) Sizes::LongHorMetric + (num_glyphs - number_of_h_metrics) * (u32) Sizes::LeftSideBearing); + } + GlyphHorizontalMetrics get_glyph_horizontal_metrics(u32 glyph_id) const; + +private: + enum class Sizes { + LongHorMetric = 4, + LeftSideBearing = 2 + }; + + ByteBuffer m_slice; + u32 m_num_glyphs; + u32 m_number_of_h_metrics; +}; + +} \ No newline at end of file