diff --git a/build/jam/BuildFeatures b/build/jam/BuildFeatures index c59f9e7be1..0815b68455 100644 --- a/build/jam/BuildFeatures +++ b/build/jam/BuildFeatures @@ -303,6 +303,21 @@ if [ IsPackageAvailable freetype_devel ] { Echo "Freetype support not available on $(TARGET_PACKAGING_ARCH)" ; } +# fontconfig +if [ IsPackageAvailable fontconfig_devel ] { + ExtractBuildFeatureArchives fontconfig : + file: base fontconfig + runtime: lib + file: devel fontconfig_devel + depends: base + library: $(developLibDir)/libfontconfig.so + headers: $(developHeadersDir) $(developHeadersDir)/fontconfig + ; + + EnableBuildFeatures fontconfig ; +} else { + Echo "fontconfig support not available on $(TARGET_PACKAGING_ARCH)" ; +} # Gutenprint if [ IsPackageAvailable gutenprint_devel ] { diff --git a/build/jam/OptionalPackages b/build/jam/OptionalPackages index 513c2bef48..c0e1b2c421 100644 --- a/build/jam/OptionalPackages +++ b/build/jam/OptionalPackages @@ -100,8 +100,8 @@ if [ IsOptionalHaikuImagePackageAdded Development ] { local architectureObject ; for architectureObject in [ MultiArchSubDirSetup ] { on $(architectureObject) { - AddHaikuImagePackages curl_devel ffmpeg_devel freetype_devel - glu_devel jpeg_devel libpng16_devel zlib_devel ; + AddHaikuImagePackages curl_devel ffmpeg_devel fontconfig_devel + freetype_devel glu_devel jpeg_devel libpng16_devel zlib_devel ; } } } diff --git a/headers/os/interface/Font.h b/headers/os/interface/Font.h index eb7d59c674..5277950a0d 100644 --- a/headers/os/interface/Font.h +++ b/headers/os/interface/Font.h @@ -1,5 +1,5 @@ /* - * Copyright 2005-2015, Haiku, Inc. All rights reserved. + * Copyright 2005-2016, Haiku, Inc. All rights reserved. * Distributed under the terms of the MIT License. */ #ifndef _FONT_H_ @@ -120,6 +120,15 @@ private: }; +struct unicode_block_range { + uint32 start; + uint32 end; + const unicode_block& block; + + uint32 Count() const { return end + 1 - start; } +}; + + struct edge_info { float left; float right; @@ -201,6 +210,7 @@ public: bool IsFullAndHalfFixed() const; BRect BoundingBox() const; unicode_block Blocks() const; + bool IncludesBlock(uint32 start, uint32 end) const; font_file_format FileFormat() const; int32 CountTuned() const; @@ -371,6 +381,7 @@ unicode_block::operator=(const unicode_block& block) { fData[0] = block.fData[0]; fData[1] = block.fData[1]; + return *this; } diff --git a/headers/os/interface/UnicodeBlockObjects.h b/headers/os/interface/UnicodeBlockObjects.h index ed5061e378..2ea87c5a91 100644 --- a/headers/os/interface/UnicodeBlockObjects.h +++ b/headers/os/interface/UnicodeBlockObjects.h @@ -1,5 +1,5 @@ /* - * Copyright 2001-2009, Haiku, Inc. All rights reserved. + * Copyright 2001-2016, Haiku, Inc. All rights reserved. * Distributed under the terms of the MIT License. */ #ifndef _UNICODEBLOCKOBJECTS_H @@ -10,6 +10,8 @@ // Unicode block list with their unicode encoding range +// +// Original BeOS-compatible blocks const unicode_block B_BASIC_LATIN_BLOCK( /* 0000 - 007F */ 0x0000000000000000LL, 0x0000000000000001LL); const unicode_block B_LATIN1_SUPPLEMENT_BLOCK( /* 0080 - 00FF */ 0x0000000000000000LL, 0x0000000000000002LL); const unicode_block B_LATIN_EXTENDED_A_BLOCK( /* 0100 - 017F */ 0x0000000000000000LL, 0x0000000000000004LL); @@ -82,4 +84,80 @@ const unicode_block B_HALFWIDTH_AND_FULLWIDTH_FORMS_BLOCK( /* FF00 - FFEF */ 0 const unicode_block B_SPECIALS_BLOCK( /* FEFF and FFF0 - FFFF */ 0x0000000000000020LL, 0x0000000000000000LL); const unicode_block B_TIBETAN_BLOCK( /* 0F00 - 0FBF */ 0x0000000000000040LL, 0x0000000000000000LL); + +const unicode_block_range kUnicodeBlockMap[] = { + {0x0000, 0x007f, B_BASIC_LATIN_BLOCK }, + {0x0080, 0x00ff, B_LATIN1_SUPPLEMENT_BLOCK }, + {0x0100, 0x017f, B_LATIN_EXTENDED_A_BLOCK }, + {0x0180, 0x024f, B_LATIN_EXTENDED_B_BLOCK }, + {0x0250, 0x02af, B_IPA_EXTENSIONS_BLOCK }, + {0x02b0, 0x02ff, B_SPACING_MODIFIER_LETTERS_BLOCK }, + {0x0300, 0x036f, B_COMBINING_DIACRITICAL_MARKS_BLOCK }, + {0x0370, 0x03cf, B_BASIC_GREEK_BLOCK }, + {0x03d0, 0x03ff, B_GREEK_SYMBOLS_AND_COPTIC_BLOCK }, + {0x0400, 0x04ff, B_CYRILLIC_BLOCK }, + {0x0530, 0x058f, B_ARMENIAN_BLOCK }, + {0x0590, 0x05cf, B_BASIC_HEBREW_BLOCK }, + {0x05d0, 0x05ff, B_HEBREW_EXTENDED_BLOCK }, + {0x0600, 0x0670, B_BASIC_ARABIC_BLOCK }, + {0x0671, 0x06ff, B_ARABIC_EXTENDED_BLOCK }, + {0x0900, 0x097f, B_DEVANAGARI_BLOCK }, + {0x0980, 0x09ff, B_BENGALI_BLOCK }, + {0x0a00, 0x0a7f, B_GURMUKHI_BLOCK }, + {0x0a80, 0x0aff, B_GUJARATI_BLOCK }, + {0x0b00, 0x0b7f, B_ORIYA_BLOCK }, + {0x0b80, 0x0bff, B_TAMIL_BLOCK }, + {0x0c00, 0x0c7f, B_TELUGU_BLOCK }, + {0x0c80, 0x0cff, B_KANNADA_BLOCK}, + {0x0d00, 0x0d7f, B_MALAYALAM_BLOCK}, + {0x0e00, 0x0e7f, B_THAI_BLOCK}, + {0x0e80, 0x0eff, B_LAO_BLOCK}, + {0x0f00, 0x0fff, B_TIBETAN_BLOCK}, + {0x10a0, 0x10ff, B_BASIC_GEORGIAN_BLOCK}, + {0x1100, 0x11ff, B_HANGUL_JAMO_BLOCK}, + {0x1e00, 0x1eff, B_LATIN_EXTENDED_ADDITIONAL_BLOCK}, + {0x1f00, 0x1fff, B_GREEK_EXTENDED_BLOCK}, + {0x2000, 0x206f, B_GENERAL_PUNCTUATION_BLOCK}, + {0x2070, 0x209f, B_SUPERSCRIPTS_AND_SUBSCRIPTS_BLOCK}, + {0x20a0, 0x20cf, B_CURRENCY_SYMBOLS_BLOCK}, + {0x20d0, 0x20ff, B_COMBINING_MARKS_FOR_SYMBOLS_BLOCK}, + {0x2100, 0x214f, B_LETTERLIKE_SYMBOLS_BLOCK}, + {0x2150, 0x218f, B_NUMBER_FORMS_BLOCK}, + {0x2190, 0x21ff, B_ARROWS_BLOCK}, + {0x2200, 0x22ff, B_MATHEMATICAL_OPERATORS_BLOCK}, + {0x2300, 0x23ff, B_MISCELLANEOUS_TECHNICAL_BLOCK}, + {0x2400, 0x243f, B_CONTROL_PICTURES_BLOCK}, + {0x2440, 0x245f, B_OPTICAL_CHARACTER_RECOGNITION_BLOCK}, + {0x2460, 0x24ff, B_ENCLOSED_ALPHANUMERICS_BLOCK}, + {0x2500, 0x257f, B_BOX_DRAWING_BLOCK}, + {0x2580, 0x259f, B_BLOCK_ELEMENTS_BLOCK}, + {0x25a0, 0x25ff, B_GEOMETRIC_SHAPES_BLOCK}, + {0x2600, 0x26ff, B_MISCELLANEOUS_SYMBOLS_BLOCK}, + {0x2700, 0x27bf, B_DINGBATS_BLOCK}, + {0x3000, 0x303f, B_CJK_SYMBOLS_AND_PUNCTUATION_BLOCK}, + {0x3040, 0x309f, B_HIRAGANA_BLOCK}, + {0x30a0, 0x30ff, B_KATAKANA_BLOCK}, + {0x3100, 0x312f, B_BOPOMOFO_BLOCK}, + {0x3130, 0x318f, B_HANGUL_COMPATIBILITY_JAMO_BLOCK}, + {0x3190, 0x319f, B_CJK_MISCELLANEOUS_BLOCK}, + {0x3200, 0x32ff, B_ENCLOSED_CJK_LETTERS_AND_MONTHS_BLOCK}, + {0x3300, 0x33ff, B_CJK_COMPATIBILITY_BLOCK}, + {0x4e00, 0x9fff, B_CJK_UNIFIED_IDEOGRAPHS_BLOCK}, + {0xd800, 0xdb7f, B_HIGH_SURROGATES_BLOCK}, + {0xdc00, 0xdfff, B_LOW_SURROGATES_BLOCK}, + {0xe000, 0xf8ff, B_PRIVATE_USE_AREA_BLOCK}, + {0xf900, 0xfaff, B_CJK_COMPATIBILITY_IDEOGRAPHS_BLOCK}, + {0xfb00, 0xfb4f, B_ALPHABETIC_PRESENTATION_FORMS_BLOCK}, + {0xfb50, 0xfdff, B_ARABIC_PRESENTATION_FORMS_A_BLOCK}, + {0xfe20, 0xfe2f, B_COMBINING_HALF_MARKS_BLOCK}, + {0xfe30, 0xfe4f, B_CJK_COMPATIBILITY_FORMS_BLOCK}, + {0xfe50, 0xfe6f, B_SMALL_FORM_VARIANTS_BLOCK}, + {0xfe70, 0xfeff, B_ARABIC_PRESENTATION_FORMS_B_BLOCK}, + {0xff00, 0xffef, B_HALFWIDTH_AND_FULLWIDTH_FORMS_BLOCK}, + {0xfff0, 0xffff, B_SPECIALS_BLOCK} +}; + +const uint32 kNumUnicodeBlockRanges + = sizeof(kUnicodeBlockMap) / sizeof(kUnicodeBlockMap[0]); + #endif // _UNICODEBLOCKOBJECTS_H diff --git a/headers/private/app/ServerProtocol.h b/headers/private/app/ServerProtocol.h index 73b0a7cfac..580bb1728f 100644 --- a/headers/private/app/ServerProtocol.h +++ b/headers/private/app/ServerProtocol.h @@ -1,5 +1,5 @@ /* - * Copyright 2001-2015, Haiku. + * Copyright 2001-2016, Haiku. * Distributed under the terms of the MIT License. * * Authors: @@ -143,6 +143,8 @@ enum { AS_GET_HAS_GLYPHS, AS_GET_GLYPH_SHAPES, AS_GET_TRUNCATED_STRINGS, + AS_GET_UNICODE_BLOCKS, + AS_GET_HAS_UNICODE_BLOCK, // Screen methods AS_VALID_SCREEN_ID, diff --git a/src/kits/interface/Font.cpp b/src/kits/interface/Font.cpp index 804307fe54..5e65bbc695 100644 --- a/src/kits/interface/Font.cpp +++ b/src/kits/interface/Font.cpp @@ -839,8 +839,43 @@ BFont::BoundingBox() const unicode_block BFont::Blocks() const { - // TODO: Add Block support - return unicode_block(~0LL, ~0LL); + BPrivate::AppServerLink link; + link.StartMessage(AS_GET_UNICODE_BLOCKS); + link.Attach(fFamilyID); + link.Attach(fStyleID); + + int32 status; + if (link.FlushWithReply(status) != B_OK + || status != B_OK) { + return unicode_block(~0LL, ~0LL); + } + + unicode_block blocksForFont; + link.Read(&blocksForFont); + + return blocksForFont; +} + +bool +BFont::IncludesBlock(uint32 start, uint32 end) const +{ + BPrivate::AppServerLink link; + link.StartMessage(AS_GET_HAS_UNICODE_BLOCK); + link.Attach(fFamilyID); + link.Attach(fStyleID); + link.Attach(start); + link.Attach(end); + + int32 status; + if (link.FlushWithReply(status) != B_OK + || status != B_OK) { + return false; + } + + bool hasBlock; + link.Read(&hasBlock); + + return hasBlock; } diff --git a/src/servers/app/Jamfile b/src/servers/app/Jamfile index 8f567abe2e..04c4afa65e 100644 --- a/src/servers/app/Jamfile +++ b/src/servers/app/Jamfile @@ -32,14 +32,26 @@ local font_src = ; UseBuildFeatureHeaders freetype ; -Includes [ FGristFiles AppServer.cpp BitmapManager.cpp Canvas.cpp +if [ FIsBuildFeatureEnabled fontconfig ] { + SubDirC++Flags -DFONTCONFIG_ENABLED ; + UseBuildFeatureHeaders fontconfig ; + Includes [ FGristFiles AppServer.cpp BitmapManager.cpp Canvas.cpp + ClientMemoryAllocator.cpp Desktop.cpp DesktopSettings.cpp + DrawState.cpp DrawingEngine.cpp Layer.cpp PictureBoundingBoxPlayer.cpp + ServerApp.cpp ServerBitmap.cpp ServerCursor.cpp ServerFont.cpp + ServerPicture.cpp ServerWindow.cpp View.cpp Window.cpp WorkspacesView.cpp + $(decorator_src) $(font_src) ] + : [ BuildFeatureAttribute freetype : headers ] + [ BuildFeatureAttribute fontconfig : headers ] ; +} else { + Includes [ FGristFiles AppServer.cpp BitmapManager.cpp Canvas.cpp ClientMemoryAllocator.cpp Desktop.cpp DesktopSettings.cpp DrawState.cpp DrawingEngine.cpp Layer.cpp PictureBoundingBoxPlayer.cpp ServerApp.cpp ServerBitmap.cpp ServerCursor.cpp ServerFont.cpp ServerPicture.cpp ServerWindow.cpp View.cpp Window.cpp WorkspacesView.cpp $(decorator_src) $(font_src) ] : [ BuildFeatureAttribute freetype : headers ] ; - +} local BROKEN_64 = ; if $(TARGET_ARCH) != x86_64 { @@ -103,6 +115,7 @@ Server app_server : libaslocal.a $(BROKEN_64)libasremote.a $(BROKEN_64)libashtml5.a libasdrawing.a libpainter.a libagg.a [ BuildFeatureAttribute freetype : library ] + [ BuildFeatureAttribute fontconfig : library ] libstackandtile.a liblinprog.a libtextencoding.so shared [ TargetLibstdc++ ] diff --git a/src/servers/app/ProfileMessageSupport.cpp b/src/servers/app/ProfileMessageSupport.cpp index f48f3bdfea..4f331c2e85 100644 --- a/src/servers/app/ProfileMessageSupport.cpp +++ b/src/servers/app/ProfileMessageSupport.cpp @@ -1,5 +1,5 @@ /* - * Copyright 2007-2009, Haiku Inc. All rights reserved. + * Copyright 2007-2016, Haiku Inc. All rights reserved. * Distributed under the terms of the MIT License. * * Authors: @@ -125,6 +125,8 @@ string_for_message_code(uint32 code, BString& string) CODE(AS_GET_HAS_GLYPHS); CODE(AS_GET_GLYPH_SHAPES); CODE(AS_GET_TRUNCATED_STRINGS); + CODE(AS_GET_UNICODE_BLOCKS); + CODE(AS_GET_HAS_UNICODE_BLOCK); // Screen methods CODE(AS_VALID_SCREEN_ID); diff --git a/src/servers/app/ServerApp.cpp b/src/servers/app/ServerApp.cpp index d3258e5985..b9b25b6610 100644 --- a/src/servers/app/ServerApp.cpp +++ b/src/servers/app/ServerApp.cpp @@ -2028,6 +2028,71 @@ ServerApp::_DispatchMessage(int32 code, BPrivate::LinkReceiver& link) break; } + case AS_GET_UNICODE_BLOCKS: + { + FTRACE(("ServerApp %s: AS_GET_UNICODE_BLOCKS\n", Signature())); + + // Attached Data: + // 1) uint16 family ID + // 2) uint16 style ID + + // Returns: + // 1) unicode_block - bitfield of Unicode blocks in font + + uint16 familyID, styleID; + link.Read(&familyID); + link.Read(&styleID); + + ServerFont font; + status_t status = font.SetFamilyAndStyle(familyID, styleID); + if (status == B_OK) { + unicode_block blocksForFont; + font.GetUnicodeBlocks(blocksForFont); + + fLink.StartMessage(B_OK); + fLink.Attach(blocksForFont); + } else + fLink.StartMessage(status); + + fLink.Flush(); + break; + } + + case AS_GET_HAS_UNICODE_BLOCK: + { + FTRACE(("ServerApp %s: AS_INCLUDES_UNICODE_BLOCK\n", Signature())); + + // Attached Data: + // 1) uint16 family ID + // 2) uint16 style ID + // 3 uint32 start of unicode block + // 4) uint32 end of unicode block + + // Returns: + // 1) bool - whether or not font includes specified block range + + uint16 familyID, styleID; + uint32 start, end; + link.Read(&familyID); + link.Read(&styleID); + link.Read(&start); + link.Read(&end); + + ServerFont font; + status_t status = font.SetFamilyAndStyle(familyID, styleID); + if (status == B_OK) { + bool hasBlock; + + status = font.IncludesUnicodeBlock(start, end, hasBlock); + fLink.StartMessage(status); + fLink.Attach(hasBlock); + } else + fLink.StartMessage(status); + + fLink.Flush(); + break; + } + case AS_GET_GLYPH_SHAPES: { FTRACE(("ServerApp %s: AS_GET_GLYPH_SHAPES\n", Signature())); diff --git a/src/servers/app/ServerFont.cpp b/src/servers/app/ServerFont.cpp index d84068c1e1..74dd8f94a0 100644 --- a/src/servers/app/ServerFont.cpp +++ b/src/servers/app/ServerFont.cpp @@ -22,8 +22,16 @@ #include FT_GLYPH_H #include FT_OUTLINE_H +#ifdef FONTCONFIG_ENABLED + +#include +#include + +#endif // FONTCONFIG_ENABLED + #include #include +#include #include #include @@ -437,6 +445,190 @@ ServerFont::GetGlyphShapes(const char charArray[], int32 numChars, } +#ifdef FONTCONFIG_ENABLED + +/*! + \brief For a given codepoint, do a binary search of the defined unicode + blocks to figure out which one contains the codepoint. + \param codePoint is the point to find + \param startGuess is the starting point for the binary search (default 0) +*/ +static +int32 +FindBlockForCodepoint(uint32 codePoint, uint32 startGuess) +{ + uint32 min = 0; + uint32 max = kNumUnicodeBlockRanges; + uint32 guess = (max + min) / 2; + + if (startGuess > 0) + guess = startGuess; + + if (codePoint > kUnicodeBlockMap[max-1].end) + return -1; + + while ((max >= min) && (guess < kNumUnicodeBlockRanges)) { + uint32 start = kUnicodeBlockMap[guess].start; + uint32 end = kUnicodeBlockMap[guess].end; + + if (start <= codePoint && end >= codePoint) + return guess; + + if (end < codePoint) { + min = guess + 1; + } else { + max = guess - 1; + } + + guess = (max + min) / 2; + } + + return -1; +} + +/*! + \brief parses charmap from fontconfig. See fontconfig docs for FcCharSetFirstPage + and FcCharSetNextPage for details on format. + \param charMap is a fontconfig character map + \param baseCodePoint is the base codepoint returned by fontconfig + \param blocksForMap is a unicode_block to store the bitmap of contained blocks +*/ +static +void +ParseFcMap(FcChar32 charMap[], FcChar32 baseCodePoint, unicode_block& blocksForMap) +{ + for (int i = 0; i < FC_CHARSET_MAP_SIZE; ++i) { + FcChar32 curMapBlock = charMap[i]; + uint32 rangeStart = 0; + uint32 block = 0; + + for (int bit = 0; bit < 32; ++bit) { + uint32 startPoint = 0; + int32 foundStartBlock = -1; + int32 foundEndBlock = -1; + + if ((curMapBlock & 0x8) != 0) { + if (rangeStart == 0) { + rangeStart = bit; + startPoint = baseCodePoint + block + (rangeStart); + foundStartBlock = FindBlockForCodepoint(startPoint, 0); + if (foundStartBlock >= 0) { + blocksForMap = blocksForMap + | kUnicodeBlockMap[foundStartBlock].block; + } + } + } else if (rangeStart > 0 && foundStartBlock > 0) { + // when we find an empty bit, that's the end of the range + uint32 endPoint = baseCodePoint + block + (bit - 1); + + foundEndBlock = FindBlockForCodepoint(endPoint, + foundStartBlock); + // start the binary search at the block where we found the + // start codepoint to ideally find the end in the same + // block. + ++foundStartBlock; + + while (foundStartBlock <= foundEndBlock) { + // if the starting codepoint is found in a different block + // than the ending codepoint, we should add all the blocks + // inbetween. + blocksForMap = blocksForMap + | kUnicodeBlockMap[foundStartBlock].block; + ++foundStartBlock; + } + + foundStartBlock = -1; + foundEndBlock = -1; + rangeStart = 0; + } else { + foundStartBlock = -1; + rangeStart = 0; + } + + curMapBlock >>= 1; + } + + block += 32; + } +} + +#endif // FONTCONFIG_ENABLED + + +/*! + \brief Gets a bitmap that indicates which Unicode blocks are in the font. + \param unicode_block to store bitmap in + \return B_OK; bitmap will be empty if something went wrong +*/ +status_t +ServerFont::GetUnicodeBlocks(unicode_block& blocksForFont) +{ + blocksForFont = unicode_block(); + +#ifdef FONTCONFIG_ENABLED + FT_Face face = GetTransformedFace(true, true); + if (face == NULL) + return B_ERROR; + + FcCharSet *charSet = FcFreeTypeCharSet(face, NULL); + if (charSet == NULL) + return B_ERROR; + + FcChar32 charMap[FC_CHARSET_MAP_SIZE]; + FcChar32 next = 0; + FcChar32 baseCodePoint = FcCharSetFirstPage(charSet, charMap, &next); + + while ((baseCodePoint != FC_CHARSET_DONE) && (next != FC_CHARSET_DONE)) { + ParseFcMap(charMap, baseCodePoint, blocksForFont); + baseCodePoint = FcCharSetNextPage(charSet, charMap, &next); + } + +#endif // FONTCONFIG_ENABLED + + return B_OK; +} + +/*! + \brief Checks if a unicode block specified by a start and end point is defined + in the current font + \param start of unicode block + \param end of unicode block + \param hasBlock boolean to store whether the font contains the specified block + \return B_OK; hasBlock will be false if something goes wrong +*/ +status_t +ServerFont::IncludesUnicodeBlock(uint32 start, uint32 end, bool& hasBlock) +{ + hasBlock = false; + +#ifdef FONTCONFIG_ENABLED + FT_Face face = GetTransformedFace(true, true); + if (face == NULL) + return B_ERROR; + + FcCharSet *charSet = FcFreeTypeCharSet(face, NULL); + if (charSet == NULL) + return B_ERROR; + + uint32 curCodePoint = start; + + while (curCodePoint <= end && hasBlock == false) { + // loop through range; if any character in the range is in the charset + // then the block is represented. + if (FcCharSetHasChar(charSet, (FcChar32)curCodePoint) == FcTrue) { + hasBlock = true; + break; + } + + ++curCodePoint; + } + +#endif // FONTCONFIG_ENABLED + + return B_OK; +} + + class HasGlyphsConsumer { public: HasGlyphsConsumer(bool* hasArray) diff --git a/src/servers/app/ServerFont.h b/src/servers/app/ServerFont.h index b04a7e310a..8d63cbfef5 100644 --- a/src/servers/app/ServerFont.h +++ b/src/servers/app/ServerFont.h @@ -163,6 +163,8 @@ class ServerFont { float width) const; Transformable EmbeddedTransformation() const; + status_t GetUnicodeBlocks(unicode_block &blocksForFont); + status_t IncludesUnicodeBlock(uint32 start, uint32 end, bool &hasBlock); protected: friend class FontStyle;