#include "ServerFont.h"
#include "Angle.h"
#include "AppFontManager.h"
#include "GlyphLayoutEngine.h"
#include "GlobalFontManager.h"
#include "truncate_string.h"
#include "utf8_functions.h"
#include FT_FREETYPE_H
#include FT_GLYPH_H
#include FT_OUTLINE_H
#ifdef FONTCONFIG_ENABLED
#include <fontconfig.h>
#include <fcfreetype.h>
#endif
#include <Shape.h>
#include <String.h>
#include <UnicodeBlockObjects.h>
#include <UTF8.h>
#include <agg_bounding_rect.h>
#include <stdio.h>
#include <string.h>
inline BPoint
VectorToPoint(const FT_Vector *vector)
{
BPoint result;
result.x = float(vector->x) / 64;
result.y = -float(vector->y) / 64;
return result;
}
int
MoveToFunc(const FT_Vector *to, void *user)
{
((BShape *)user)->MoveTo(VectorToPoint(to));
return 0;
}
int
LineToFunc(const FT_Vector *to, void *user)
{
((BShape *)user)->LineTo(VectorToPoint(to));
return 0;
}
int
ConicToFunc(const FT_Vector *control, const FT_Vector *to, void *user)
{
BPoint controls[3];
controls[0] = VectorToPoint(control);
controls[1] = controls[0];
controls[2] = VectorToPoint(to);
((BShape *)user)->BezierTo(controls);
return 0;
}
int
CubicToFunc(const FT_Vector *control1, const FT_Vector *control2, const FT_Vector *to, void *user)
{
BPoint controls[3];
controls[0] = VectorToPoint(control1);
controls[1] = VectorToPoint(control2);
controls[2] = VectorToPoint(to);
((BShape *)user)->BezierTo(controls);
return 0;
}
inline bool
is_white_space(uint32 charCode)
{
switch (charCode) {
case 0x0009:
case 0x000b:
case 0x000c:
case 0x0020:
case 0x00a0:
case 0x000a:
case 0x000d:
case 0x2028:
case 0x2029:
return true;
}
return false;
}
ServerFont::ServerFont(FontStyle& style, float size, float rotation,
float shear, float falseBoldWidth, uint16 flags, uint8 spacing)
:
fStyle(&style, false),
fSize(size),
fRotation(rotation),
fShear(shear),
fFalseBoldWidth(falseBoldWidth),
fBounds(0, 0, 0, 0),
fFlags(flags),
fSpacing(spacing),
fDirection(style.Direction()),
fFace(style.Face()),
fEncoding(B_UNICODE_UTF8)
{
}
ServerFont::ServerFont()
:
fStyle(NULL)
{
*this = *gFontManager->DefaultPlainFont();
}
ServerFont::ServerFont(const ServerFont &font)
:
fStyle(NULL)
{
*this = font;
}
ServerFont::~ServerFont()
{
}
ServerFont&
ServerFont::operator=(const ServerFont& font)
{
fSize = font.fSize;
fRotation = font.fRotation;
fShear = font.fShear;
fFalseBoldWidth = font.fFalseBoldWidth;
fFlags = font.fFlags;
fSpacing = font.fSpacing;
fEncoding = font.fEncoding;
fBounds = font.fBounds;
SetStyle(font.fStyle);
fFace = font.fFace;
return *this;
}
bool
ServerFont::operator==(const ServerFont& other) const
{
if (GetFamilyAndStyle() != other.GetFamilyAndStyle())
return false;
return fSize == other.fSize && fRotation == other.fRotation
&& fShear == other.fShear && fFalseBoldWidth == other.fFalseBoldWidth
&& fFlags == other.fFlags && fSpacing == other.fSpacing
&& fEncoding == other.fEncoding && fBounds == other.fBounds
&& fDirection == other.fDirection && fFace == other.fFace;
}
int32
ServerFont::CountTuned()
{
return fStyle->TunedCount();
}
font_file_format
ServerFont::FileFormat()
{
return fStyle->FileFormat();
}
const char*
ServerFont::Style() const
{
return fStyle->Name();
}
const char*
ServerFont::Family() const
{
return fStyle->Family()->Name();
}
void
ServerFont::SetStyle(FontStyle* style)
{
if (style && style != fStyle) {
fStyle.SetTo(style, false);
fFace = fStyle->PreservedFace(fFace);
fDirection = fStyle->Direction();
fBounds.Set(0, -1, 0, -1);
}
}
status_t
ServerFont::SetFamilyAndStyle(uint16 familyID, uint16 styleID,
AppFontManager* fontManager)
{
BReference<FontStyle> style;
if (gFontManager->Lock()) {
style.SetTo(gFontManager->GetStyle(familyID, styleID), false);
gFontManager->Unlock();
}
if (style == NULL) {
if (fontManager != NULL && fontManager->Lock()) {
style.SetTo(fontManager->GetStyle(familyID, styleID), false);
fontManager->Unlock();
}
if (style == NULL)
return B_ERROR;
}
SetStyle(style);
fBounds.Set(0, -1, 0, -1);
return B_OK;
}
status_t
ServerFont::SetFamilyAndStyle(uint32 fontID, AppFontManager* fontManager)
{
uint16 style = fontID & 0xFFFF;
uint16 family = (fontID & 0xFFFF0000) >> 16;
return SetFamilyAndStyle(family, style, fontManager);
}
void
ServerFont::SetSize(float value)
{
fSize = value;
fBounds.Set(0, -1, 0, -1);
}
status_t
ServerFont::SetFace(uint16 face)
{
if (fStyle->PreservedFace(face) == face) {
fFace = face;
return B_OK;
}
BReference <FontStyle> style;
uint16 familyID = FamilyID();
if (gFontManager->Lock()) {
int32 count = gFontManager->CountStyles(familyID);
for (int32 i = 0; i < count; i++) {
style.SetTo(gFontManager->GetStyleByIndex(familyID, i), false);
if (style == NULL)
break;
if (style->PreservedFace(face) == face)
break;
else
style = NULL;
}
gFontManager->Unlock();
}
if (!style)
return B_ERROR;
fFace = face;
SetStyle(style);
fBounds.Set(0, -1, 0, -1);
return B_OK;
}
uint32
ServerFont::GetFamilyAndStyle() const
{
if (fStyle == NULL || fStyle->Family() == NULL)
return 0;
return (FamilyID() << 16) | StyleID();
}
FT_Face
ServerFont::GetTransformedFace(bool rotate, bool shear) const
{
fStyle->Lock();
FT_Face face = fStyle->FreeTypeFace();
if (!face) {
fStyle->Unlock();
return NULL;
}
FT_Set_Char_Size(face, 0, int32(fSize * 64), 72, 72);
if ((rotate && fRotation != 0) || (shear && fShear != 90)) {
FT_Matrix rmatrix, smatrix;
Angle rotationAngle(fRotation);
rmatrix.xx = (FT_Fixed)( rotationAngle.Cosine() * 0x10000);
rmatrix.xy = (FT_Fixed)(-rotationAngle.Sine() * 0x10000);
rmatrix.yx = (FT_Fixed)( rotationAngle.Sine() * 0x10000);
rmatrix.yy = (FT_Fixed)( rotationAngle.Cosine() * 0x10000);
Angle shearAngle(fShear);
smatrix.xx = (FT_Fixed)(0x10000);
smatrix.xy = (FT_Fixed)(-shearAngle.Cosine() * 0x10000);
smatrix.yx = (FT_Fixed)(0);
smatrix.yy = (FT_Fixed)(0x10000);
FT_Matrix_Multiply(&rmatrix, &smatrix);
FT_Set_Transform(face, &smatrix, NULL);
}
return face;
}
void
ServerFont::PutTransformedFace(FT_Face face) const
{
FT_Set_Transform(face, NULL, NULL);
fStyle->Unlock();
}
status_t
ServerFont::GetGlyphShapes(const char charArray[], int32 numChars,
BShape* shapeArray[]) const
{
if (!charArray || numChars <= 0 || !shapeArray)
return B_BAD_DATA;
FT_Face face = GetTransformedFace(true, true);
if (!face)
return B_ERROR;
FT_Outline_Funcs funcs;
funcs.move_to = MoveToFunc;
funcs.line_to = LineToFunc;
funcs.conic_to = ConicToFunc;
funcs.cubic_to = CubicToFunc;
funcs.shift = 0;
funcs.delta = 0;
const char* string = charArray;
for (int i = 0; i < numChars; i++) {
shapeArray[i] = new (std::nothrow) BShape();
if (shapeArray[i] == NULL) {
PutTransformedFace(face);
return B_NO_MEMORY;
}
FT_Load_Char(face, UTF8ToCharCode(&string), FT_LOAD_NO_BITMAP);
FT_Outline outline = face->glyph->outline;
FT_Outline_Decompose(&outline, &funcs, shapeArray[i]);
shapeArray[i]->Close();
}
PutTransformedFace(face);
return B_OK;
}
#ifdef FONTCONFIG_ENABLED
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;
}
static
void
ParseFcMap(FcChar32 charMap[], FcChar32 baseCodePoint, unicode_block& blocksForMap)
{
uint32 block = 0;
const uint8 BITS_PER_BLOCK = 32;
uint32 currentCodePoint = 0;
if (baseCodePoint > kUnicodeBlockMap[kNumUnicodeBlockRanges-1].end)
return;
for (int i = 0; i < FC_CHARSET_MAP_SIZE; ++i) {
FcChar32 curMapBlock = charMap[i];
int32 rangeStart = -1;
int32 startBlock = -1;
int32 endBlock = -1;
uint32 startPoint = 0;
currentCodePoint = baseCodePoint + block;
for (int bit = 0; bit < BITS_PER_BLOCK; ++bit) {
if (curMapBlock == 0 && startBlock < 0)
break;
if ((curMapBlock & 0x1) != 0 && rangeStart < 0) {
rangeStart = bit;
startPoint = currentCodePoint + rangeStart;
startBlock = FindBlockForCodepoint(startPoint, 0);
if (startBlock >= 0) {
blocksForMap = blocksForMap
| kUnicodeBlockMap[startBlock].block;
}
} else if (rangeStart >= 0 && startBlock >= 0) {
uint32 endPoint = currentCodePoint + (bit - 1);
endBlock = FindBlockForCodepoint(endPoint,
startBlock);
++startBlock;
while (startBlock <= endBlock) {
blocksForMap = blocksForMap
| kUnicodeBlockMap[startBlock].block;
++startBlock;
}
startBlock = -1;
endBlock = -1;
rangeStart = -1;
}
curMapBlock >>= 1;
}
if (rangeStart >= 0 && startBlock >= 0) {
uint32 endPoint = currentCodePoint + BITS_PER_BLOCK - 1;
endBlock = FindBlockForCodepoint(endPoint,
startBlock);
++startBlock;
while (startBlock <= endBlock) {
blocksForMap = blocksForMap
| kUnicodeBlockMap[startBlock].block;
++startBlock;
}
}
block += BITS_PER_BLOCK;
}
}
#endif
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) {
PutTransformedFace(face);
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);
}
FcCharSetDestroy(charSet);
PutTransformedFace(face);
#endif
return B_OK;
}
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) {
PutTransformedFace(face);
return B_ERROR;
}
uint32 curCodePoint = start;
while (curCodePoint <= end && hasBlock == false) {
if (FcCharSetHasChar(charSet, (FcChar32)curCodePoint) == FcTrue) {
hasBlock = true;
break;
}
++curCodePoint;
}
FcCharSetDestroy(charSet);
PutTransformedFace(face);
#endif
return B_OK;
}
status_t
ServerFont::GetHasGlyphs(const char* string, int32 numBytes, int32 numChars, bool* hasArray,
bool useFallbacks) const
{
if (string == NULL || numBytes <= 0 || numChars <= 0 || hasArray == NULL)
return B_BAD_DATA;
FontCacheEntry* entry = NULL;
FontCacheReference cacheReference;
BObjectList<FontCacheReference, true> fallbacks(21);
entry = GlyphLayoutEngine::FontCacheEntryFor(*this, false);
if (entry == NULL)
return B_ERROR;
cacheReference.SetTo(entry);
uint32 charCode;
int32 charIndex = 0;
const char* start = string;
while (charIndex < numChars && (charCode = UTF8ToCharCode(&string)) != 0) {
hasArray[charIndex] = entry->CanCreateGlyph(charCode);
if (hasArray[charIndex] == false && useFallbacks) {
if (fallbacks.IsEmpty())
GlyphLayoutEngine::PopulateFallbacks(fallbacks, *this, false);
if (GlyphLayoutEngine::GetFallbackReference(fallbacks, charCode) != NULL)
hasArray[charIndex] = true;
}
charIndex++;
if (string - start + 1 > numBytes)
break;
}
return B_OK;
}
class EdgesConsumer {
public:
EdgesConsumer(edge_info* edges, float size)
:
fEdges(edges),
fSize(size)
{
}
bool NeedsVector() { return false; }
void Start() {}
void Finish(double x, double y) {}
void ConsumeEmptyGlyph(int32 index, uint32 charCode, double x, double y)
{
fEdges[index].left = 0.0;
fEdges[index].right = 0.0;
}
bool ConsumeGlyph(int32 index, uint32 charCode, const GlyphCache* glyph,
FontCacheEntry* entry, double x, double y, double advanceX,
double advanceY)
{
fEdges[index].left = glyph->inset_left / fSize;
fEdges[index].right = glyph->inset_right / fSize;
return true;
}
private:
edge_info* fEdges;
float fSize;
};
status_t
ServerFont::GetEdges(const char* string, int32 numBytes, int32 numChars,
edge_info* edges) const
{
if (string == NULL || numBytes <= 0 || numChars <= 0 || edges == NULL)
return B_BAD_DATA;
EdgesConsumer consumer(edges, fSize);
if (GlyphLayoutEngine::LayoutGlyphs(consumer, *this, string, numBytes,
numChars, NULL, fSpacing)) {
return B_OK;
}
return B_ERROR;
}
class BPointEscapementConsumer {
public:
BPointEscapementConsumer(BPoint* escapements, BPoint* offsets, float size)
:
fEscapements(escapements),
fOffsets(offsets),
fSize(size)
{
}
bool NeedsVector() { return false; }
void Start() {}
void Finish(double x, double y) {}
void ConsumeEmptyGlyph(int32 index, uint32 charCode, double x, double y)
{
_Set(index, 0, 0);
}
bool ConsumeGlyph(int32 index, uint32 charCode, const GlyphCache* glyph,
FontCacheEntry* entry, double x, double y, double advanceX,
double advanceY)
{
return _Set(index, advanceX, advanceY);
}
private:
inline bool _Set(int32 index, double x, double y)
{
fEscapements[index].x = x / fSize;
fEscapements[index].y = y / fSize;
if (fOffsets) {
fOffsets[index].x = 0;
fOffsets[index].y = 0;
}
return true;
}
BPoint* fEscapements;
BPoint* fOffsets;
float fSize;
};
status_t
ServerFont::GetEscapements(const char* string, int32 numBytes, int32 numChars,
escapement_delta delta, BPoint escapementArray[],
BPoint offsetArray[]) const
{
if (string == NULL || numBytes <= 0 || numChars <= 0
|| escapementArray == NULL) {
return B_BAD_DATA;
}
BPointEscapementConsumer consumer(escapementArray, offsetArray, fSize);
if (GlyphLayoutEngine::LayoutGlyphs(consumer, *this, string, numBytes,
numChars, &delta, fSpacing)) {
return B_OK;
}
return B_ERROR;
}
class WidthEscapementConsumer {
public:
WidthEscapementConsumer(float* widths, float size)
:
fWidths(widths),
fSize(size)
{
}
bool NeedsVector() { return false; }
void Start() {}
void Finish(double x, double y) {}
void ConsumeEmptyGlyph(int32 index, uint32 charCode, double x, double y)
{
fWidths[index] = 0.0;
}
bool ConsumeGlyph(int32 index, uint32 charCode, const GlyphCache* glyph,
FontCacheEntry* entry, double x, double y, double advanceX,
double advanceY)
{
fWidths[index] = advanceX / fSize;
return true;
}
private:
float* fWidths;
float fSize;
};
status_t
ServerFont::GetEscapements(const char* string, int32 numBytes, int32 numChars,
escapement_delta delta, float widthArray[]) const
{
if (string == NULL || numBytes <= 0 || numChars <= 0 || widthArray == NULL)
return B_BAD_DATA;
WidthEscapementConsumer consumer(widthArray, fSize);
if (GlyphLayoutEngine::LayoutGlyphs(consumer, *this, string, numBytes,
numChars, &delta, fSpacing)) {
return B_OK;
}
return B_ERROR;
}
class BoundingBoxConsumer {
public:
BoundingBoxConsumer(Transformable& transform, BRect* rectArray,
bool asString)
:
rectArray(rectArray),
stringBoundingBox(INT32_MAX, INT32_MAX, INT32_MIN, INT32_MIN),
fAsString(asString),
fCurves(fPathAdaptor),
fContour(fCurves),
fTransformedOutline(fCurves, transform),
fTransformedContourOutline(fContour, transform),
fTransform(transform)
{
}
bool NeedsVector() { return false; }
void Start() {}
void Finish(double x, double y) {}
void ConsumeEmptyGlyph(int32 index, uint32 charCode, double x, double y) {}
bool ConsumeGlyph(int32 index, uint32 charCode, const GlyphCache* glyph,
FontCacheEntry* entry, double x, double y, double advanceX,
double advanceY)
{
if (glyph->data_type != glyph_data_outline) {
const agg::rect_i& r = glyph->bounds;
if (fAsString) {
if (rectArray) {
rectArray[index].left = r.x1 + x;
rectArray[index].top = r.y1 + y;
rectArray[index].right = r.x2 + x + 1;
rectArray[index].bottom = r.y2 + y + 1;
} else {
stringBoundingBox = stringBoundingBox
| BRect(r.x1 + x, r.y1 + y,
r.x2 + x + 1, r.y2 + y + 1);
}
} else {
rectArray[index].left = r.x1;
rectArray[index].top = r.y1;
rectArray[index].right = r.x2 + 1;
rectArray[index].bottom = r.y2 + 1;
}
} else {
if (fAsString) {
entry->InitAdaptors(glyph, x, y,
fMonoAdaptor, fGray8Adaptor, fPathAdaptor);
} else {
entry->InitAdaptors(glyph, 0, 0,
fMonoAdaptor, fGray8Adaptor, fPathAdaptor);
}
double left = 0.0;
double top = 0.0;
double right = -1.0;
double bottom = -1.0;
uint32 pathID[1];
pathID[0] = 0;
agg::bounding_rect(fTransformedOutline, pathID, 0, 1,
&left, &top, &right, &bottom);
if (rectArray) {
rectArray[index] = BRect(left, top, right, bottom);
} else {
stringBoundingBox = stringBoundingBox
| BRect(left, top, right, bottom);
}
}
return true;
}
BRect* rectArray;
BRect stringBoundingBox;
private:
bool fAsString;
FontCacheEntry::GlyphPathAdapter fPathAdaptor;
FontCacheEntry::GlyphGray8Adapter fGray8Adaptor;
FontCacheEntry::GlyphMonoAdapter fMonoAdaptor;
FontCacheEntry::CurveConverter fCurves;
FontCacheEntry::ContourConverter fContour;
FontCacheEntry::TransformedOutline fTransformedOutline;
FontCacheEntry::TransformedContourOutline fTransformedContourOutline;
Transformable& fTransform;
};
status_t
ServerFont::GetBoundingBoxes(const char* string, int32 numBytes, int32 numChars,
BRect rectArray[], bool stringEscapement, font_metric_mode mode,
escapement_delta delta, bool asString)
{
if (string == NULL || numBytes <= 0 || numChars <= 0 || rectArray == NULL)
return B_BAD_DATA;
Transformable transform(EmbeddedTransformation());
BoundingBoxConsumer consumer(transform, rectArray, asString);
if (GlyphLayoutEngine::LayoutGlyphs(consumer, *this, string, numBytes,
numChars, stringEscapement ? &delta : NULL, fSpacing)) {
return B_OK;
}
return B_ERROR;
}
status_t
ServerFont::GetBoundingBoxesForStrings(char *charArray[], size_t lengthArray[],
int32 numStrings, BRect rectArray[], font_metric_mode mode,
const escapement_delta deltaArray[])
{
if (charArray == NULL || lengthArray == NULL || numStrings <= 0
|| rectArray == NULL || deltaArray == NULL) {
return B_BAD_DATA;
}
Transformable transform(EmbeddedTransformation());
for (int32 i = 0; i < numStrings; i++) {
size_t numBytes = lengthArray[i];
const char* string = charArray[i];
escapement_delta delta = deltaArray[i];
BoundingBoxConsumer consumer(transform, NULL, true);
if (!GlyphLayoutEngine::LayoutGlyphs(consumer, *this, string, numBytes,
INT32_MAX, &delta, fSpacing)) {
return B_ERROR;
}
rectArray[i] = consumer.stringBoundingBox;
}
return B_OK;
}
class StringWidthConsumer {
public:
StringWidthConsumer()
:
width(0.0)
{
}
bool NeedsVector() { return false; }
void Start() {}
void Finish(double x, double y) { width = x; }
void ConsumeEmptyGlyph(int32 index, uint32 charCode, double x, double y) {}
bool ConsumeGlyph(int32 index, uint32 charCode, const GlyphCache* glyph,
FontCacheEntry* entry, double x, double y, double advanceX,
double advanceY)
{
return true;
}
float width;
};
float
ServerFont::StringWidth(const char *string, int32 numBytes,
const escapement_delta* deltaArray) const
{
if (!string || numBytes <= 0)
return 0.0;
StringWidthConsumer consumer;
if (!GlyphLayoutEngine::LayoutGlyphs(consumer, *this, string, numBytes,
INT32_MAX, deltaArray, fSpacing)) {
return 0.0;
}
return consumer.width;
}
BRect
ServerFont::BoundingBox()
{
FT_Face face = fStyle->FreeTypeFace();
if (fBounds.IsValid() &&
fBounds.IntegerWidth() > 0 &&
fBounds.IntegerHeight() > 0)
return fBounds;
if (IsScalable()) {
FT_BBox bounds = face->bbox;
fBounds.left = (float)bounds.xMin / (float)face->units_per_EM;
fBounds.right = (float)bounds.xMax / (float)face->units_per_EM;
fBounds.top = (float)bounds.yMin / (float)face->units_per_EM;
fBounds.bottom = (float)bounds.yMax / (float)face->units_per_EM;
float scaledWidth = fBounds.Width() * fSize;
float scaledHeight = fBounds.Height() * fSize;
fBounds.InsetBy((fBounds.Width() - scaledWidth) / 2.f,
(fBounds.Height() - scaledHeight) / 2.f);
} else {
float pixelSize = fSize * 64.f;
float minDelta = abs(face->available_sizes[0].size - pixelSize);
float width = face->available_sizes[0].x_ppem;
float height = face->available_sizes[0].y_ppem;
for (int i = 1; i < face->num_fixed_sizes; ++i) {
float delta = abs(face->available_sizes[i].size - pixelSize);
if (delta < minDelta) {
width = face->available_sizes[i].x_ppem;
height = face->available_sizes[i].y_ppem;
}
}
fBounds.top = 0;
fBounds.left = 0;
fBounds.right = width / 64.f;
fBounds.bottom = height / 64.f;
}
return fBounds;
}
void
ServerFont::GetHeight(font_height& height) const
{
fStyle->GetHeight(fSize, height);
}
void
ServerFont::TruncateString(BString* inOut, uint32 mode, float width) const
{
if (!inOut)
return;
float ellipsisWidth = StringWidth(B_UTF8_ELLIPSIS, strlen(B_UTF8_ELLIPSIS));
int32 numChars = inOut->CountChars();
float* escapementArray = new (std::nothrow) float[numChars];
if (escapementArray == NULL)
return;
static escapement_delta delta = (escapement_delta){ 0.0, 0.0 };
if (GetEscapements(inOut->String(), inOut->Length(), numChars, delta,
escapementArray) == B_OK) {
truncate_string(*inOut, mode, width, escapementArray, fSize,
ellipsisWidth, numChars);
}
delete[] escapementArray;
}
Transformable
ServerFont::EmbeddedTransformation() const
{
Transformable transform;
transform.ShearBy(B_ORIGIN, (90.0 - fShear) * M_PI / 180.0, 0.0);
transform.RotateBy(B_ORIGIN, -fRotation * M_PI / 180.0);
return transform;
}
void
ServerFont::SetFontData(FT_Byte* location, uint32 size)
{
if (fStyle != NULL)
fStyle->SetFontData(location, size);
}