Implementation of BFont::Blocks

BFont::Blocks is now implemented in ServerFont, via a call through the
app_server.  It uses fontconfig to iterate through a charset of a font
and stores the defined blocks in a bitmap.

A new API was added, BFont::IncludesBlock, that will allow for arbitrary
testing of a given Unicode block. Since nothing is cached, searching
through an entire charset for a series of Unicode blocks can be quite
slow. In a given block there may be only 1 or 2 characters actually
defined so every character within a block needs to be checked until one
is found, which in a degenerate case will mean the entire block is
checked.

Signed-off-by: Axel Dörfler <axeld@pinc-software.de>
This commit is contained in:
dsizzle 2016-03-10 21:38:32 -08:00 committed by Axel Dörfler
parent b17d5d1c48
commit 9b6b158b88
11 changed files with 425 additions and 10 deletions

View File

@ -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 ] {

View File

@ -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 ;
}
}
}

View File

@ -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;
}

View File

@ -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

View File

@ -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,

View File

@ -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<uint16>(fFamilyID);
link.Attach<uint16>(fStyleID);
int32 status;
if (link.FlushWithReply(status) != B_OK
|| status != B_OK) {
return unicode_block(~0LL, ~0LL);
}
unicode_block blocksForFont;
link.Read<unicode_block>(&blocksForFont);
return blocksForFont;
}
bool
BFont::IncludesBlock(uint32 start, uint32 end) const
{
BPrivate::AppServerLink link;
link.StartMessage(AS_GET_HAS_UNICODE_BLOCK);
link.Attach<uint16>(fFamilyID);
link.Attach<uint16>(fStyleID);
link.Attach<uint32>(start);
link.Attach<uint32>(end);
int32 status;
if (link.FlushWithReply(status) != B_OK
|| status != B_OK) {
return false;
}
bool hasBlock;
link.Read<bool>(&hasBlock);
return hasBlock;
}

View File

@ -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++ ]

View File

@ -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);

View File

@ -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<uint16>(&familyID);
link.Read<uint16>(&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<unicode_block>(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<uint16>(&familyID);
link.Read<uint16>(&styleID);
link.Read<uint32>(&start);
link.Read<uint32>(&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<bool>(hasBlock);
} else
fLink.StartMessage(status);
fLink.Flush();
break;
}
case AS_GET_GLYPH_SHAPES:
{
FTRACE(("ServerApp %s: AS_GET_GLYPH_SHAPES\n", Signature()));

View File

@ -22,8 +22,16 @@
#include FT_GLYPH_H
#include FT_OUTLINE_H
#ifdef FONTCONFIG_ENABLED
#include <fontconfig.h>
#include <fcfreetype.h>
#endif // FONTCONFIG_ENABLED
#include <Shape.h>
#include <String.h>
#include <UnicodeBlockObjects.h>
#include <UTF8.h>
#include <agg_bounding_rect.h>
@ -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)

View File

@ -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;