#include "TGFont.h"
#include "TGClient.h"
#include "THashTable.h"
#include "TVirtualX.h"
#include "TObjString.h"
#include "TGWidget.h"
#include <errno.h>
#include "Riostream.h"
#include "TROOT.h"
#include "TError.h"
#include "TMath.h"
ClassImp(TGFont)
ClassImp(TGFontPool)
ClassImp(TGTextLayout)
#define FONT_FAMILY 0
#define FONT_SIZE 1
#define FONT_WEIGHT 2
#define FONT_SLANT 3
#define FONT_UNDERLINE 4
#define FONT_OVERSTRIKE 5
#define FONT_NUMFIELDS 6
#define XLFD_FOUNDRY 0
#define XLFD_FAMILY 1
#define XLFD_WEIGHT 2
#define XLFD_SLANT 3
#define XLFD_SETWIDTH 4
#define XLFD_ADD_STYLE 5
#define XLFD_PIXEL_SIZE 6
#define XLFD_POINT_SIZE 7
#define XLFD_RESOLUTION_X 8
#define XLFD_RESOLUTION_Y 9
#define XLFD_SPACING 10
#define XLFD_AVERAGE_WIDTH 11
#define XLFD_REGISTRY 12
#define XLFD_ENCODING 13
#define XLFD_NUMFIELDS 14 // Number of fields in XLFD.
struct LayoutChunk_t {
const char *fStart;
Int_t fNumChars;
Int_t fNumDisplayChars;
Int_t fX;
Int_t fY;
Int_t fTotalWidth;
Int_t fDisplayWidth;
};
struct XLFDAttributes_t {
FontAttributes_t fFA;
const char *fFoundry;
Int_t fSlant;
Int_t fSetwidth;
Int_t fCharset;
Int_t fEncoding;
};
class TNamedFont : public TObjString, public TRefCnt {
public:
Int_t fDeletePending;
FontAttributes_t fFA;
};
enum EFontSpacing { kFontProportional = 0,
kFontFixed = 1,
kFontMono = 1,
kFontCharcell = 2 };
enum EFontSetWidth { kFontSWNormal = 0,
kFontSWCondence = 1,
kFontSWExpand = 2,
kFontSWUnknown = 3 };
enum EFontCharset { kFontCSNormal = 0,
kFontCSSymbol = 1,
kFontCSOther = 2 };
enum ECharType { kCharNormal, kCharReplace, kCharSkip };
struct FontStateMap_t { Int_t fNumKey; char *fStrKey; };
static FontStateMap_t gWeightMap[] = {
{ kFontWeightNormal, "normal" },
{ kFontWeightBold, "bold" },
{ kFontWeightUnknown, 0 }
};
static FontStateMap_t gSlantMap[] = {
{ kFontSlantRoman, "roman" },
{ kFontSlantItalic, "italic" },
{ kFontSlantUnknown, 0 }
};
static FontStateMap_t gUnderlineMap[] = {
{ 1, "underline" },
{ 0, 0 }
};
static FontStateMap_t gOverstrikeMap[] = {
{ 1, "overstrike" },
{ 0, 0 }
};
static FontStateMap_t gXlfdgWeightMap[] = {
{ kFontWeightNormal, "normal" },
{ kFontWeightNormal, "medium" },
{ kFontWeightNormal, "book" },
{ kFontWeightNormal, "light" },
{ kFontWeightBold, "bold" },
{ kFontWeightBold, "demi" },
{ kFontWeightBold, "demibold" },
{ kFontWeightNormal, 0 }
};
static FontStateMap_t gXlfdSlantMap[] = {
{ kFontSlantRoman, "r" },
{ kFontSlantItalic, "i" },
{ kFontSlantOblique, "o" },
{ kFontSlantRoman, 0 }
};
static FontStateMap_t gXlfdSetwidthMap[] = {
{ kFontSWNormal, "normal" },
{ kFontSWCondence, "narrow" },
{ kFontSWCondence, "semicondensed" },
{ kFontSWCondence, "condensed" },
{ kFontSWUnknown, 0 }
};
static FontStateMap_t gXlfdCharsetMap[] = {
{ kFontCSNormal, "iso8859" },
{ kFontCSSymbol, "adobe" },
{ kFontCSSymbol, "sun" },
{ kFontCSOther, 0 }
};
static char gHexChars[] = "0123456789abcdefxtnvr\\";
static char gMapChars[] = {
0, 0, 0, 0, 0, 0, 0, 'a', 'b', 't', 'n', 'v', 'f', 'r', 0
};
static int GetControlCharSubst(int c, char buf[4]);
void FontAttributes_t::Init()
{
fFamily = 0;
fPointsize = 0;
fWeight = kFontWeightNormal;
fSlant = kFontSlantRoman;
fUnderline = 0;
fOverstrike = 0;
}
TGFont::~TGFont()
{
if (fFontStruct) {
gVirtualX->DeleteFont(fFontStruct);
}
}
void TGFont::GetFontMetrics(FontMetrics_t *m) const
{
if (!m) {
Error("GetFontMetrics", "argument may not be 0");
return;
}
*m = fFM;
m->fLinespace = fFM.fAscent + fFM.fDescent;
}
FontStruct_t TGFont::operator()() const
{
return fFontStruct;
}
void TGFont::Print(Option_t *option) const
{
TString opt = option;
if ((opt == "full") && fNamedHash) {
Printf("TGFont: %s, %s, %s, ref cnt = %u",
fNamedHash->GetName(),
fFM.fFixed ? "fixed" : "prop", References());
} else {
Printf("TGFont: %s, %s, ref cnt = %u", fName.Data(),
fFM.fFixed ? "fixed" : "prop", References());
}
}
Int_t TGFont::PostscriptFontName(TString *dst) const
{
const char *family;
TString weightString;
TString slantString;
char *src, *dest;
Int_t upper, len;
len = dst->Length();
family = fFA.fFamily;
if (strncasecmp(family, "itc ", 4) == 0) {
family = family + 4;
}
if ((strcasecmp(family, "Arial") == 0)
|| (strcasecmp(family, "Geneva") == 0)) {
family = "Helvetica";
} else if ((strcasecmp(family, "Times New Roman") == 0)
|| (strcasecmp(family, "New York") == 0)) {
family = "Times";
} else if ((strcasecmp(family, "Courier New") == 0)
|| (strcasecmp(family, "Monaco") == 0)) {
family = "Courier";
} else if (strcasecmp(family, "AvantGarde") == 0) {
family = "AvantGarde";
} else if (strcasecmp(family, "ZapfChancery") == 0) {
family = "ZapfChancery";
} else if (strcasecmp(family, "ZapfDingbats") == 0) {
family = "ZapfDingbats";
} else {
dst->Append(family);
src = dest = (char*)dst->Data() + len;
upper = 1;
for (; *src != '\0'; src++, dest++) {
while (isspace(UChar_t(*src))) {
src++;
upper = 1;
}
*dest = *src;
if ((upper != 0) && (islower(UChar_t(*src)))) {
*dest = toupper(UChar_t(*src));
}
upper = 0;
}
*dest = '\0';
family = (char *) dst->Data() + len;
}
if (family != (char *) dst->Data() + len) {
dst->Append(family);
family = (char *) dst->Data() + len;
}
if (strcasecmp(family, "NewCenturySchoolbook") == 0) {
dst->Append("NewCenturySchlbk");
family = (char *) dst->Data() + len;
}
weightString = "";
if (fFA.fWeight == kFontWeightNormal) {
if (strcmp(family, "Bookman") == 0) {
weightString = "Light";
} else if (strcmp(family, "AvantGarde") == 0) {
weightString = "Book";
} else if (strcmp(family, "ZapfChancery") == 0) {
weightString = "Medium";
}
} else {
if ((strcmp(family, "Bookman") == 0)
|| (strcmp(family, "AvantGarde") == 0)) {
weightString = "Demi";
} else {
weightString = "Bold";
}
}
slantString = "";
if (fFA.fSlant == kFontSlantRoman) {
;
} else {
if ((strcmp(family, "Helvetica") == 0)
|| (strcmp(family, "Courier") == 0)
|| (strcmp(family, "AvantGarde") == 0)) {
slantString = "Oblique";
} else {
slantString = "Italic";
}
}
if ((slantString.IsNull()) && (weightString.IsNull())) {
if ((strcmp(family, "Times") == 0)
|| (strcmp(family, "NewCenturySchlbk") == 0)
|| (strcmp(family, "Palatino") == 0)) {
dst->Append("-Roman");
}
} else {
dst->Append("-");
if (!weightString.IsNull()) dst->Append(weightString);
if (!slantString.IsNull()) dst->Append(slantString);
}
return fFA.fPointsize;
}
Int_t TGFont::MeasureChars(const char *source, Int_t numChars, Int_t maxLength,
Int_t flags, Int_t *length) const
{
const char *p;
const char *term;
Int_t termX;
Int_t curX;
Int_t newX;
Int_t c, sawNonSpace;
if (!numChars) {
*length = 0;
return 0;
}
if (maxLength <= 0) {
maxLength = INT_MAX;
}
newX = curX = termX = 0;
p = term = source;
sawNonSpace = !isspace(UChar_t(*p));
for (c = UChar_t(*p);;) {
newX += fWidths[c];
if (newX > maxLength) {
break;
}
curX = newX;
numChars--;
p++;
if (!numChars) {
term = p;
termX = curX;
break;
}
c = UChar_t(*p);
if (isspace(c)) {
if (sawNonSpace) {
term = p;
termX = curX;
sawNonSpace = 0;
}
} else {
sawNonSpace = 1;
}
}
if ((flags & kTextPartialOK) && (numChars > 0) && (curX < maxLength)) {
numChars--;
curX = newX;
p++;
}
if ((flags & kTextAtLeastOne) && (term == source) && (numChars > 0)) {
term = p;
termX = curX;
if (term == source) {
term++;
termX = newX;
}
} else if ((numChars == 0) || !(flags & kTextWholeWords)) {
term = p;
termX = curX;
}
*length = termX;
return term - source;
}
Int_t TGFont::TextWidth(const char *string, Int_t numChars) const
{
Int_t width;
if (numChars < 0) {
numChars = strlen(string);
}
MeasureChars(string, numChars, 0, 0, &width);
return width;
}
Int_t TGFont::XTextWidth(const char *string, Int_t numChars) const
{
int width;
if (numChars < 0) {
numChars = strlen(string);
}
width = gVirtualX->TextWidth(fFontStruct, string, numChars);
return width;
}
void TGFont::UnderlineChars(Drawable_t dst, GContext_t gc,
const char *string, Int_t x, Int_t y,
Int_t firstChar, Int_t lastChar) const
{
Int_t startX, endX;
MeasureChars(string, firstChar, 0, 0, &startX);
MeasureChars(string, lastChar, 0, 0, &endX);
gVirtualX->FillRectangle(dst, gc, x + startX, y + fUnderlinePos,
(UInt_t) (endX - startX),
(UInt_t) fUnderlineHeight);
}
TGTextLayout *TGFont::ComputeTextLayout(const char *string, Int_t numChars,
Int_t wrapLength, Int_t justify, Int_t flags,
UInt_t *width, UInt_t *height) const
{
const char *start, *end, *special;
Int_t n, y=0, charsThisChunk, maxChunks;
Int_t baseline, h, curX, newX, maxWidth;
TGTextLayout *layout;
LayoutChunk_t *chunk;
#define MAX_LINES 50
Int_t staticLineLengths[MAX_LINES];
Int_t *lineLengths;
Int_t maxLines, curLine, layoutHeight;
lineLengths = staticLineLengths;
maxLines = MAX_LINES;
h = fFM.fAscent + fFM.fDescent;
if (numChars < 0) {
numChars = strlen(string);
}
maxChunks = 0;
layout = new TGTextLayout;
layout->fFont = this;
layout->fString = string;
layout->fNumChunks = 0;
layout->fChunks = 0;
baseline = fFM.fAscent;
maxWidth = 0;
curX = 0;
end = string + numChars;
special = string;
flags &= kTextIgnoreTabs | kTextIgnoreNewlines;
flags |= kTextWholeWords | kTextAtLeastOne;
curLine = 0;
for (start = string; start < end;) {
if (start >= special) {
for (special = start; special < end; special++) {
if (!(flags & kTextIgnoreNewlines)) {
if ((*special == '\n') || (*special == '\r')) {
break;
}
}
if (!(flags & kTextIgnoreTabs)) {
if (*special == '\t') {
break;
}
}
}
}
chunk = 0;
if (start < special) {
charsThisChunk = MeasureChars(start, special - start,
wrapLength - curX, flags, &newX);
newX += curX;
flags &= ~kTextAtLeastOne;
if (charsThisChunk > 0) {
chunk = NewChunk(layout, &maxChunks, start,
charsThisChunk, curX, newX, baseline);
start += charsThisChunk;
curX = newX;
}
}
if ((start == special) && (special < end)) {
chunk = 0;
if (*special == '\t') {
newX = curX + fTabWidth;
newX -= newX % fTabWidth;
NewChunk(layout, &maxChunks, start, 1, curX, newX, baseline)->fNumDisplayChars = -1;
start++;
if ((start < end) && ((wrapLength <= 0) || (newX <= wrapLength))) {
curX = newX;
flags &= ~kTextAtLeastOne;
continue;
}
} else {
NewChunk(layout, &maxChunks, start, 1, curX, 1000000000, baseline)->fNumDisplayChars = -1;
start++;
goto wrapLine;
}
}
while ((start < end) && isspace(UChar_t(*start))) {
if (!(flags & kTextIgnoreNewlines)) {
if ((*start == '\n') || (*start == '\r')) {
break;
}
}
if (!(flags & kTextIgnoreTabs)) {
if (*start == '\t') {
break;
}
}
start++;
}
if (chunk) {
charsThisChunk = start - (chunk->fStart + chunk->fNumChars);
if (charsThisChunk > 0) {
chunk->fNumChars += MeasureChars(chunk->fStart + chunk->fNumChars,
charsThisChunk, 0, 0, &chunk->fTotalWidth);
chunk->fTotalWidth += curX;
}
}
wrapLine:
flags |= kTextAtLeastOne;
if (curX > maxWidth) {
maxWidth = curX;
}
if (curLine >= maxLines) {
int *newLengths;
newLengths = new int[2 * maxLines];
memcpy((void *) newLengths, lineLengths, maxLines * sizeof (int));
if (lineLengths != staticLineLengths) {
delete[] lineLengths;
}
lineLengths = newLengths;
maxLines *= 2;
}
lineLengths[curLine] = curX;
curLine++;
curX = 0;
baseline += h;
}
if ((layout->fNumChunks > 0) && ((flags & kTextIgnoreNewlines) == 0)) {
if (layout->fChunks[layout->fNumChunks - 1].fStart[0] == '\n') {
chunk = NewChunk(layout, &maxChunks, start, 0, curX, 1000000000, baseline);
chunk->fNumDisplayChars = -1;
baseline += h;
}
}
curLine = 0;
chunk = layout->fChunks;
if (chunk) y = chunk->fY;
for (n = 0; n < layout->fNumChunks; n++) {
int extra;
if (chunk->fY != y) {
curLine++;
y = chunk->fY;
}
extra = maxWidth - lineLengths[curLine];
if (justify == kTextCenterX) {
chunk->fX += extra / 2;
} else if (justify == kTextRight) {
chunk->fX += extra;
}
++chunk;
}
layout->fWidth = maxWidth;
layoutHeight = baseline - fFM.fAscent;
if (layout->fNumChunks == 0) {
layoutHeight = h;
layout->fNumChunks = 1;
layout->fChunks = new LayoutChunk_t[1];
layout->fChunks[0].fStart = string;
layout->fChunks[0].fNumChars = 0;
layout->fChunks[0].fNumDisplayChars = -1;
layout->fChunks[0].fX = 0;
layout->fChunks[0].fY = fFM.fAscent;
layout->fChunks[0].fTotalWidth = 0;
layout->fChunks[0].fDisplayWidth = 0;
}
if (width) {
*width = layout->fWidth;
}
if (height) {
*height = layoutHeight;
}
if (lineLengths != staticLineLengths) {
delete[] lineLengths;
}
return layout;
}
TGTextLayout::~TGTextLayout()
{
if (fChunks) {
delete[] fChunks;
}
}
void TGTextLayout::DrawText(Drawable_t dst, GContext_t gc,
Int_t x, Int_t y, Int_t firstChar, Int_t lastChar) const
{
Int_t i, numDisplayChars, drawX;
LayoutChunk_t *chunk;
if (lastChar < 0) lastChar = 100000000;
chunk = fChunks;
for (i = 0; i < fNumChunks; i++) {
numDisplayChars = chunk->fNumDisplayChars;
if ((numDisplayChars > 0) && (firstChar < numDisplayChars)) {
if (firstChar <= 0) {
drawX = 0;
firstChar = 0;
} else {
fFont->MeasureChars(chunk->fStart, firstChar, 0, 0, &drawX);
}
if (lastChar < numDisplayChars) numDisplayChars = lastChar;
fFont->DrawChars(dst, gc, chunk->fStart + firstChar, numDisplayChars - firstChar,
x + chunk->fX + drawX, y + chunk->fY);
}
firstChar -= chunk->fNumChars;
lastChar -= chunk->fNumChars;
if (lastChar <= 0) break;
chunk++;
}
}
void TGTextLayout::UnderlineChar(Drawable_t dst, GContext_t gc,
Int_t x, Int_t y, Int_t underline) const
{
int xx, yy, width, height;
if ((CharBbox(underline, &xx, &yy, &width, &height) != 0)
&& (width != 0)) {
gVirtualX->FillRectangle(dst, gc, x + xx,
y + yy + fFont->fFM.fAscent + fFont->fUnderlinePos,
(UInt_t) width, (UInt_t) fFont->fUnderlineHeight);
}
}
Int_t TGTextLayout::PointToChar(Int_t x, Int_t y) const
{
LayoutChunk_t *chunk, *last;
Int_t i, n, dummy, baseline, pos;
if (y < 0) {
return 0;
}
last = chunk = fChunks;
for (i = 0; i < fNumChunks; i++) {
baseline = chunk->fY;
if (y < baseline + fFont->fFM.fDescent) {
if (x < chunk->fX) {
return (chunk->fStart - fString);
}
if (x >= fWidth) {
x = INT_MAX;
}
last = chunk;
while ((i < fNumChunks) && (chunk->fY == baseline)) {
if (x < chunk->fX + chunk->fTotalWidth) {
if (chunk->fNumDisplayChars < 0) {
return (chunk->fStart - fString);
}
n = fFont->MeasureChars(chunk->fStart, chunk->fNumChars,
x + 1 - chunk->fX, kTextPartialOK, &dummy);
return ((chunk->fStart + n - 1) - fString);
}
last = chunk;
chunk++;
i++;
}
pos = (last->fStart + last->fNumChars) - fString;
if (i < fNumChunks) pos--;
return pos;
}
last = chunk;
chunk++;
}
return ((last->fStart + last->fNumChars) - fString);
}
Int_t TGTextLayout::CharBbox(Int_t index, Int_t *x, Int_t *y, Int_t *w, Int_t *h) const
{
LayoutChunk_t *chunk;
Int_t i, xx, ww;
if (index < 0) {
return 0;
}
chunk = fChunks;
for (i = 0; i < fNumChunks; i++) {
if (chunk->fNumDisplayChars < 0) {
if (!index) {
xx = chunk->fX;
ww = chunk->fTotalWidth;
goto check;
}
} else if (index < chunk->fNumChars) {
if (x) {
fFont->MeasureChars(chunk->fStart, index, 0, 0, &xx);
xx += chunk->fX;
}
if (w) {
fFont->MeasureChars(chunk->fStart + index, 1, 0, 0, &ww);
}
goto check;
}
index -= chunk->fNumChars;
chunk++;
}
if (!index) {
chunk--;
xx = chunk->fX + chunk->fTotalWidth;
ww = 0;
} else {
return 0;
}
check:
if (y) {
*y = chunk->fY - fFont->fFM.fAscent;
}
if (h) {
*h = fFont->fFM.fAscent + fFont->fFM.fDescent;
}
if (xx > fWidth) {
xx = fWidth;
}
if (x) {
*x = xx;
}
if (w) {
if (xx + ww > fWidth) {
ww = fWidth - xx;
}
*w = ww;
}
return 1;
}
Int_t TGTextLayout::DistanceToText(Int_t x, Int_t y) const
{
Int_t i, x1, x2, y1, y2, xDiff, yDiff, dist, minDist, ascent, descent;
LayoutChunk_t *chunk;
ascent = fFont->fFM.fAscent;
descent = fFont->fFM.fDescent;
minDist = 0;
chunk = fChunks;
for (i = 0; i < fNumChunks; i++) {
if (chunk->fStart[0] == '\n') {
chunk++;
continue;
}
x1 = chunk->fX;
y1 = chunk->fY - ascent;
x2 = chunk->fX + chunk->fDisplayWidth;
y2 = chunk->fY + descent;
if (x < x1) {
xDiff = x1 - x;
} else if (x >= x2) {
xDiff = x - x2 + 1;
} else {
xDiff = 0;
}
if (y < y1) {
yDiff = y1 - y;
} else if (y >= y2) {
yDiff = y - y2 + 1;
} else {
yDiff = 0;
}
if ((xDiff == 0) && (yDiff == 0)) {
return 0;
}
dist = (int) TMath::Hypot((Double_t) xDiff, (Double_t) yDiff);
if ((dist < minDist) || !minDist) {
minDist = dist;
}
chunk++;
}
return minDist;
}
Int_t TGTextLayout::IntersectText(Int_t x, Int_t y, Int_t w, Int_t h) const
{
Int_t result, i, x1, y1, x2, y2;
LayoutChunk_t *chunk;
Int_t left, top, right, bottom;
chunk = fChunks;
left = x;
top = y;
right = x + w;
bottom = y + h;
result = 0;
for (i = 0; i < fNumChunks; i++) {
if (chunk->fStart[0] == '\n') {
chunk++;
continue;
}
x1 = chunk->fX;
y1 = chunk->fY - fFont->fFM.fAscent;
x2 = chunk->fX + chunk->fDisplayWidth;
y2 = chunk->fY + fFont->fFM.fDescent;
if ((right < x1) || (left >= x2) || (bottom < y1) || (top >= y2)) {
if (result == 1) {
return 0;
}
result = -1;
} else if ((x1 < left) || (x2 >= right) || (y1 < top) || (y2 >= bottom)) {
return 0;
} else if (result == -1) {
return 0;
} else {
result = 1;
}
chunk++;
}
return result;
}
void TGTextLayout::ToPostscript(TString *result) const
{
#define MAXUSE 128
char buf[MAXUSE + 10];
LayoutChunk_t *chunk;
Int_t i, j, used, c, baseline;
chunk = fChunks;
baseline = chunk->fY;
used = 0;
buf[used++] = '(';
for (i = 0; i < fNumChunks; i++) {
if (baseline != chunk->fY) {
buf[used++] = ')';
buf[used++] = '\n';
buf[used++] = '(';
baseline = chunk->fY;
}
if (chunk->fNumDisplayChars <= 0) {
if (chunk->fStart[0] == '\t') {
buf[used++] = '\\';
buf[used++] = 't';
}
} else {
for (j = 0; j < chunk->fNumDisplayChars; j++) {
c = UChar_t(chunk->fStart[j]);
if ((c == '(') || (c == ')') || (c == '\\') || (c < 0x20) || (c >= UChar_t(0x7f))) {
sprintf(buf + used, "\\%03o", c);
used += 4;
} else {
buf[used++] = c;
}
if (used >= MAXUSE) {
buf[used] = '\0';
result->Append(buf);
used = 0;
}
}
}
if (used >= MAXUSE) {
buf[used] = '\0';
result->Append(buf);
used = 0;
}
chunk++;
}
buf[used++] = ')';
buf[used++] = '\n';
buf[used] = '\0';
result->Append(buf);
}
LayoutChunk_t *TGFont::NewChunk(TGTextLayout *layout, Int_t *maxPtr,
const char *start, Int_t numChars,
Int_t curX, Int_t newX, Int_t y) const
{
LayoutChunk_t *chunk;
Int_t i, maxChunks;
maxChunks = *maxPtr;
if (layout->fNumChunks == maxChunks) {
if (maxChunks == 0) {
maxChunks = 1;
} else {
maxChunks *= 2;
}
chunk = new LayoutChunk_t[maxChunks];
if (layout->fNumChunks > 0) {
for (i=0; i<layout->fNumChunks; ++i) chunk[i] = layout->fChunks[i];
delete[] layout->fChunks;
}
layout->fChunks = chunk;
*maxPtr = maxChunks;
}
chunk = &layout->fChunks[layout->fNumChunks];
chunk->fStart = start;
chunk->fNumChars = numChars;
chunk->fNumDisplayChars = numChars;
chunk->fX = curX;
chunk->fY = y;
chunk->fTotalWidth = newX - curX;
chunk->fDisplayWidth = newX - curX;
layout->fNumChunks++;
return chunk;
}
void TGFont::DrawCharsExp(Drawable_t dst, GContext_t gc,
const char *source, Int_t numChars,
Int_t x, Int_t y) const
{
const char *p;
Int_t i, type;
char buf[4];
p = source;
for (i = 0; i < numChars; i++) {
type = fTypes[UChar_t(*p)];
if (type != kCharNormal) {
DrawChars(dst, gc, source, p - source, x, y);
x += gVirtualX->TextWidth(fFontStruct, source, p - source);
if (type == kCharReplace) {
DrawChars(dst, gc, buf, GetControlCharSubst(UChar_t(*p), buf), x, y);
x += fWidths[UChar_t(*p)];
}
source = p + 1;
}
p++;
}
DrawChars(dst, gc, source, p - source, x, y);
}
void TGFont::DrawChars(Drawable_t dst, GContext_t gc,
const char *source, Int_t numChars,
Int_t x, Int_t y) const
{
Int_t max_width = gVirtualX->TextWidth(fFontStruct, "@", 1);
if ((x + (max_width * numChars) > 0x7fff)) {
int length;
numChars = MeasureChars(source, numChars, 0x7fff - x, 0, &length);
}
gVirtualX->DrawString(dst, gc, x, y, source, numChars);
if (fFA.fUnderline != 0) {
gVirtualX->FillRectangle(dst, gc, x, y + fUnderlinePos,
(UInt_t) gVirtualX->TextWidth(fFontStruct, source, numChars),
(UInt_t) fBarHeight);
}
if (fFA.fOverstrike != 0) {
y -= fFM.fDescent + fFM.fAscent / 10;
gVirtualX->FillRectangle(dst, gc, x, y,
(UInt_t) gVirtualX->TextWidth(fFontStruct, source, numChars),
(UInt_t) fBarHeight);
}
}
TGFontPool::TGFontPool(TGClient *client)
{
fClient = client;
fList = new THashTable(50);
fList->SetOwner();
fNamedTable = new THashTable(50);
fNamedTable->SetOwner();
fUidTable = new THashTable(50);
fUidTable->SetOwner();
}
TGFontPool::~TGFontPool()
{
delete fList;
}
TGFont *TGFontPool::GetFont(const char *font, Bool_t fixedDefault)
{
if (!font || !*font) {
Error("GetFont", "argument may not be 0 or empty");
return 0;
}
TGFont *f = (TGFont*)fList->FindObject(font);
if (f) {
f->AddReference();
return f;
}
TNamedFont *nf = (TNamedFont*)fNamedTable->FindObject(font);
if (nf) {
nf->AddReference();
f = GetFontFromAttributes(&nf->fFA, 0);
} else {
Int_t errsav = gErrorIgnoreLevel;
gErrorIgnoreLevel = kFatal;
f = GetNativeFont(font, fixedDefault);
gErrorIgnoreLevel = errsav;
if (!f) {
FontAttributes_t fa;
if (!ParseFontName(font, &fa)) {
return 0;
}
f = GetFontFromAttributes(&fa, 0);
}
}
fList->Add(f);
f->SetRefCount(1);
f->fNamedHash = nf;
f->MeasureChars("0", 1, 0, 0, &f->fTabWidth);
if (!f->fTabWidth) {
f->fTabWidth = f->fFM.fMaxWidth;
}
f->fTabWidth *= 8;
if (!f->fTabWidth) {
f->fTabWidth = 1;
}
Int_t descent = f->fFM.fDescent;
f->fUnderlinePos = descent/2;
f->fUnderlineHeight = f->fFA.fPointsize/10;
if (!f->fUnderlineHeight) {
f->fUnderlineHeight = 1;
}
if (f->fUnderlinePos + f->fUnderlineHeight > descent) {
f->fUnderlineHeight = descent - f->fUnderlinePos;
if (!f->fUnderlineHeight) {
f->fUnderlinePos--;
f->fUnderlineHeight = 1;
}
}
return f;
}
TGFont *TGFontPool::GetFont(const TGFont *font)
{
TGFont *f = (TGFont*)fList->FindObject(font);
if (f) {
f->AddReference();
return f;
}
return 0;
}
TGFont *TGFontPool::GetFont(FontStruct_t fs)
{
TGFont *f = FindFont(fs);
if (f) {
f->AddReference();
return f;
}
static int i = 0;
f = new TGFont(Form("unknown-%d", i));
f->fFontStruct = fs;
f->fFontH = gVirtualX->GetFontHandle(fs);
gVirtualX->GetFontProperties(fs, f->fFM.fAscent, f->fFM.fDescent);
f->fFM.fLinespace = f->fFM.fAscent + f->fFM.fDescent;
f->fFM.fMaxWidth = gVirtualX->TextWidth(fs, "@", 1);
f->fFM.fFixed = (f->fFM.fMaxWidth == gVirtualX->TextWidth(fs, "i", 1)) ? kTRUE : kFALSE;
fList->Add(f);
i++;
return f;
}
TGFont *TGFontPool::GetFont(const char *family, Int_t ptsize, Int_t weight, Int_t slant)
{
char *s;
TString tmp;
tmp.Form("%s %d", family, ptsize);
s = FindStateString(gWeightMap, weight);
if (s) {
tmp += " ";
tmp + s;
}
s = FindStateString(gSlantMap, slant);
if (s) {
tmp += " ";
tmp += s;
}
return GetFont(tmp.Data());
}
void TGFontPool::FreeFont(const TGFont *font)
{
TGFont *f = (TGFont*) fList->FindObject(font);
if (f) {
if (f->RemoveReference() == 0) {
if (font->fNamedHash) {
TNamedFont *nf = (TNamedFont *) font->fNamedHash;
if ((nf->RemoveReference() == 0) && (nf->fDeletePending != 0)) {
fNamedTable->Remove(nf);
delete nf;
}
}
fList->Remove(f);
delete font;
}
}
}
TGFont *TGFontPool::FindFont(FontStruct_t font) const
{
TIter next(fList);
TGFont *f = 0;
while ((f = (TGFont*) next())) {
if (f->fFontStruct == font) {
return f;
}
}
return 0;
}
TGFont *TGFontPool::FindFontByHandle(FontH_t font) const
{
TIter next(fList);
TGFont *f = 0;
while ((f = (TGFont*) next())) {
if (f->fFontH == font) {
return f;
}
}
return 0;
}
const char *TGFontPool::GetUid(const char *string)
{
TObjString *obj = 0;
obj = (TObjString*)fUidTable->FindObject(string);
if (!obj) {
obj = new TObjString(string);
fUidTable->Add(obj);
}
return (const char *)obj->GetName();
}
char **TGFontPool::GetAttributeInfo(const FontAttributes_t *fa)
{
Int_t i, num;
const char *str = 0;
char **result = new char*[FONT_NUMFIELDS];
for (i = 0; i < FONT_NUMFIELDS; ++i) {
str = 0;
num = 0;
switch (i) {
case FONT_FAMILY:
str = fa->fFamily;
if (!str) str = "";
break;
case FONT_SIZE:
num = fa->fPointsize;
break;
case FONT_WEIGHT:
str = FindStateString(gWeightMap, fa->fWeight);
break;
case FONT_SLANT:
str = FindStateString(gSlantMap, fa->fSlant);
break;
case FONT_UNDERLINE:
num = fa->fUnderline;
break;
case FONT_OVERSTRIKE:
num = fa->fOverstrike;
break;
}
if (str) {
result[i] = new char[strlen(str)+1];
strcpy(result[i], str);
} else {
result[i] = new char[20];
sprintf(result[i], "%d", num);
}
}
return result;
}
void TGFontPool::FreeAttributeInfo(char **info)
{
Int_t i;
if (info) {
for (i = 0; i < FONT_NUMFIELDS; ++i) {
if (info[i]) {
delete[] info[i];
}
}
delete[] info;
}
}
void TGFontPool::Print(Option_t *opt) const
{
fList->Print(opt);
}
void TGFont::SavePrimitive(ostream &out, Option_t * )
{
char quote = '"';
if (gROOT->ClassSaved(TGFont::Class())) {
out << endl;
} else {
out << endl;
out << " TGFont *ufont; // will reflect user font changes" << endl;
}
out << " ufont = gClient->GetFont(" << quote << GetName() << quote << ");" << endl;
}
static char *GetToken(char *str)
{
static char *p = 0;
char *retp;
if (str) p = str;
if (!p) {
return 0;
}
if (!*p) {
return 0;
}
while (*p && ((*p == ' ') || (*p == '\t'))) {
++p;
}
if (!*p) {
return 0;
}
if (*p == '"') {
retp = ++p;
if (!*p) {
return 0;
}
while (*p && (*p != '"')) {
++p;
}
if (*p == '"') {
*p++ = '\0';
}
} else {
retp = p;
while (*p && (*p != ' ') && (*p != '\t')) {
++p;
}
if (*p) {
*p++ = '\0';
}
}
return retp;
}
Bool_t TGFontPool::ParseFontName(const char *string, FontAttributes_t *fa)
{
char *s;
int n, result;
XLFDAttributes_t xa;
char *str = new char[strlen(string)+1];
strcpy(str, string);
if (*str == '-' || *str == '*') {
xa.fFA = *fa;
result = ParseXLFD(str, &xa);
if (result) {
*fa = xa.fFA;
delete[] str;
return kTRUE;
}
}
s = GetToken(str);
if (!s) {
delete[] str;
return kFALSE;
}
fa->fFamily = GetUid(s);
s = GetToken(0);
if (s) {
char *end;
fa->fPointsize = strtol(s, &end, 0);
if ((errno == ERANGE) || (end == s)) {
return kFALSE;
}
}
while ((s = GetToken(0))) {
n = FindStateNum(gWeightMap, s);
if (n != kFontWeightUnknown) {
fa->fWeight = n;
continue;
}
n = FindStateNum(gSlantMap, s);
if (n != kFontSlantUnknown) {
fa->fSlant = n;
continue;
}
n = FindStateNum(gUnderlineMap, s);
if (n) {
fa->fUnderline = n;
continue;
}
n = FindStateNum(gOverstrikeMap, s);
if (n) {
fa->fOverstrike = n;
continue;
}
delete[] str;
return kFALSE;
}
delete[] str;
return kTRUE;
}
Bool_t TGFontPool::ParseXLFD(const char *string, XLFDAttributes_t *xa)
{
char *src;
const char *str;
int i, j;
char *field[XLFD_NUMFIELDS + 2];
TString ds("");
memset(field, '\0', sizeof (field));
str = string;
if (*str == '-') str++;
ds.Append((char *) str);
src = (char*)ds.Data();
field[0] = src;
for (i = 0; *src != '\0'; src++) {
if (isupper(UChar_t(*src))) {
*src = tolower(UChar_t(*src));
}
if (*src == '-') {
i++;
if (i > XLFD_NUMFIELDS) {
break;
}
*src = '\0';
field[i] = src + 1;
}
}
if ((i > XLFD_ADD_STYLE) && (FieldSpecified(field[XLFD_ADD_STYLE]))) {
if (atoi(field[XLFD_ADD_STYLE]) != 0) {
for (j = XLFD_NUMFIELDS - 1; j >= XLFD_ADD_STYLE; j--) {
field[j + 1] = field[j];
}
field[XLFD_ADD_STYLE] = 0;
i++;
}
}
if (i < XLFD_FAMILY) {
return kFALSE;
}
if (FieldSpecified(field[XLFD_FOUNDRY])) {
xa->fFoundry = GetUid(field[XLFD_FOUNDRY]);
}
if (FieldSpecified(field[XLFD_FAMILY])) {
xa->fFA.fFamily = GetUid(field[XLFD_FAMILY]);
}
if (FieldSpecified(field[XLFD_WEIGHT])) {
xa->fFA.fWeight = FindStateNum(gXlfdgWeightMap, field[XLFD_WEIGHT]);
}
if (FieldSpecified(field[XLFD_SLANT])) {
xa->fSlant = FindStateNum(gXlfdSlantMap, field[XLFD_SLANT]);
if (xa->fSlant == kFontSlantRoman) {
xa->fFA.fSlant = kFontSlantRoman;
} else {
xa->fFA.fSlant = kFontSlantItalic;
}
}
if (FieldSpecified(field[XLFD_SETWIDTH])) {
xa->fSetwidth = FindStateNum(gXlfdSetwidthMap, field[XLFD_SETWIDTH]);
}
if (FieldSpecified(field[XLFD_POINT_SIZE])) {
if (field[XLFD_POINT_SIZE][0] == '[') {
xa->fFA.fPointsize = atoi(field[XLFD_POINT_SIZE] + 1);
} else {
char *end;
xa->fFA.fPointsize = strtol(field[XLFD_POINT_SIZE], &end, 0);
if (errno == ERANGE || end == field[XLFD_POINT_SIZE]) {
return kFALSE;
}
xa->fFA.fPointsize /= 10;
}
}
if (FieldSpecified(field[XLFD_PIXEL_SIZE])) {
if (field[XLFD_PIXEL_SIZE][0] == '[') {
xa->fFA.fPointsize = atoi(field[XLFD_PIXEL_SIZE] + 1);
} else {
char *end;
xa->fFA.fPointsize = strtol(field[XLFD_PIXEL_SIZE], &end, 0);
if (errno == ERANGE || end == field[XLFD_PIXEL_SIZE]) {
return kFALSE;
}
}
}
xa->fFA.fPointsize = -xa->fFA.fPointsize;
if (FieldSpecified(field[XLFD_REGISTRY])) {
xa->fCharset = FindStateNum(gXlfdCharsetMap, field[XLFD_REGISTRY]);
}
if (FieldSpecified(field[XLFD_ENCODING])) {
xa->fEncoding = atoi(field[XLFD_ENCODING]);
}
return kTRUE;
}
Int_t TGFontPool::FindStateNum(const FontStateMap_t *map, const char *strKey)
{
const FontStateMap_t *m;
if (!map->fStrKey) {
return 0;
}
for (m = map; m->fStrKey != 0; m++) {
if (strcasecmp(strKey, m->fStrKey) == 0) {
return m->fNumKey;
}
}
return m->fNumKey;
}
char *TGFontPool::FindStateString(const FontStateMap_t *map, Int_t numKey)
{
for ( ; map->fStrKey != 0; map++) {
if (numKey == map->fNumKey) return map->fStrKey;
}
return 0;
}
Bool_t TGFontPool::FieldSpecified(const char *field)
{
char ch;
if (!field) {
return kFALSE;
}
ch = field[0];
return (ch != '*' && ch != '?');
}
const char *TGFontPool::NameOfFont(TGFont *font)
{
return font->GetName();
}
char **TGFontPool::GetFontFamilies()
{
Int_t i, numNames;
char *family, *end, *p;
THashTable familyTable(100);
familyTable.SetOwner();
char **nameList;
char **dst;
nameList = gVirtualX->ListFonts("*", 10000, numNames);
for (i = 0; i < numNames; i++) {
if (nameList[i][0] != '-') {
continue;
}
family = strchr(nameList[i] + 1, '-');
if (!family) {
continue;
}
family++;
end = strchr(family, '-');
if (!end) {
continue;
}
*end = '\0';
for (p = family; *p != '\0'; p++) {
if (isupper(UChar_t(*p))) {
*p = tolower(UChar_t(*p));
}
}
if (!familyTable.FindObject(family)) {
familyTable.Add(new TObjString(family));
}
}
UInt_t entries = familyTable.GetEntries();
dst = new char*[entries+1];
TIter next(&familyTable);
i = 0;
TObject *obj;
while ((obj = next())) {
dst[i] = StrDup(obj->GetName());
i++;
}
dst[i] = 0;
gVirtualX->FreeFontNames(nameList);
return dst;
}
void TGFontPool::FreeFontFamilies(char **f)
{
Int_t i;
if (!f) return;
for (i = 0; f[i] != 0; ++i) {
delete[] f[i];
}
delete[] f;
}
TGFont *TGFontPool::GetFontFromAttributes(FontAttributes_t *fa, TGFont *fontPtr)
{
Int_t numNames, score, i, scaleable, pixelsize, xaPixelsize;
Int_t bestIdx, bestScore, bestScaleableIdx, bestScaleableScore;
XLFDAttributes_t xa;
TString buf;
char **nameList;
TGFont *font;
FontStruct_t fontStruct;
const char *fmt, *family;
family = fa->fFamily;
if (!family) {
family = "*";
}
pixelsize = -fa->fPointsize;
if (pixelsize < 0) {
double d;
d = -pixelsize * 25.4/72;
Int_t xx; Int_t yy; UInt_t ww; UInt_t hh;
gVirtualX->GetWindowSize(gVirtualX->GetDefaultRootWindow(), xx, yy, ww, hh);
d *= ww;
d /= gVirtualX->ScreenWidthMM();
d += 0.5;
pixelsize = (int) d;
}
fontStruct = 0;
fmt = "-*-%.240s-*-*-*-*-*-*-*-*-*-*-*-*";
buf.Form(fmt, family);
nameList = gVirtualX->ListFonts(buf.Data(), 32768, numNames);
if (!numNames) {
buf.Format(fmt, "fixed");
nameList = gVirtualX->ListFonts(buf.Data(), 32768, numNames);
if (!numNames) {
getsystem:
fontStruct = gVirtualX->LoadQueryFont("fixed");
if (!fontStruct) {
fontStruct = gVirtualX->LoadQueryFont("*");
if (!fontStruct) {
return 0;
}
}
goto end;
}
}
bestIdx = 0;
bestScore = kMaxInt;
bestScaleableIdx = 0;
bestScaleableScore = kMaxInt;
for (i = 0; i < numNames; i++) {
score = 0;
scaleable = 0;
if (!ParseXLFD(nameList[i], &xa)) {
continue;
}
xaPixelsize = -xa.fFA.fPointsize;
if (strcasecmp(xa.fFoundry, "adobe") != 0) {
score += 3000;
}
if (!xa.fFA.fPointsize) {
score += 10;
scaleable = 1;
} else {
if (xaPixelsize > pixelsize) {
score += (xaPixelsize - pixelsize) * 120;
} else {
score += (pixelsize - xaPixelsize) * 100;
}
}
score += TMath::Abs(xa.fFA.fWeight - fa->fWeight) * 30;
score += TMath::Abs(xa.fFA.fSlant - fa->fSlant) * 25;
if (xa.fSlant == kFontSlantOblique) {
}
if (xa.fSetwidth != kFontSWNormal) {
score += 2000;
}
if (xa.fCharset == kFontCSOther) {
score += 11000;
}
if ((xa.fCharset == kFontCSNormal) && (xa.fEncoding != 1)) {
score += 8000;
}
if (scaleable) {
if (score < bestScaleableScore) {
bestScaleableIdx = i;
bestScaleableScore = score;
}
} else {
if (score < bestScore) {
bestIdx = i;
bestScore = score;
}
}
if (!score) {
break;
}
}
fontStruct = 0;
if (bestScaleableScore < bestScore) {
char *str, *rest;
tryscale:
str = nameList[bestScaleableIdx];
for (i = 0; i < XLFD_PIXEL_SIZE - 1; i++) {
str = strchr(str + 1, '-');
}
rest = str;
for (i = XLFD_PIXEL_SIZE - 1; i < XLFD_REGISTRY; i++) {
rest = strchr(rest + 1, '-');
}
*str = '\0';
buf.Form("%.240s-*-%d-*-*-*-*-*%s", nameList[bestScaleableIdx], pixelsize, rest);
*str = '-';
fontStruct = gVirtualX->LoadQueryFont(buf.Data());
bestScaleableScore = kMaxInt;
}
if (!fontStruct) {
buf = nameList[bestIdx];
fontStruct = gVirtualX->LoadQueryFont(buf.Data());
if (!fontStruct) {
if (bestScaleableScore < kMaxInt) {
goto tryscale;
} else {
gVirtualX->FreeFontNames(nameList);
goto getsystem;
}
}
}
gVirtualX->FreeFontNames(nameList);
end:
font = MakeFont(fontPtr, fontStruct, buf);
font->fFA.fUnderline = fa->fUnderline;
font->fFA.fOverstrike = fa->fOverstrike;
return font;
}
TGFont *TGFontPool::GetNativeFont(const char *name, Bool_t fixedDefault)
{
FontStruct_t fontStruct;
fixedDefault = fixedDefault && ((*name == '-') || (*name == '*'));
fontStruct = fClient->GetFontByName(name, fixedDefault);
if (!fontStruct) {
return 0;
}
return MakeFont(0, fontStruct, name);
}
TGFont *TGFontPool::MakeFont(TGFont *font, FontStruct_t fontStruct,
const char *fontName)
{
TGFont *newFont;
Int_t i, width, firstChar, lastChar, n, replaceOK;
char *p;
char buf[4];
XLFDAttributes_t xa;
if (font) {
gVirtualX->FreeFontStruct(font->fFontStruct);
newFont = font;
} else {
newFont = new TGFont(fontName);
}
xa.fFA.Init();
if (!ParseXLFD(fontName, &xa)) {
newFont->fFA.Init();
newFont->fFA.fFamily = GetUid(fontName);
} else {
newFont->fFA = xa.fFA;
}
if (newFont->fFA.fPointsize < 0) {
double d;
Int_t xx; Int_t yy; UInt_t ww; UInt_t hh;
gVirtualX->GetWindowSize(gVirtualX->GetDefaultRootWindow(), xx, yy, ww, hh);
d = -newFont->fFA.fPointsize * 72/25.4;
d *= gVirtualX->ScreenWidthMM();
d /= ww;
d += 0.5;
newFont->fFA.fPointsize = (int) d;
}
Int_t ascent;
Int_t descent;
gVirtualX->GetFontProperties(fontStruct, ascent, descent);
newFont->fFM.fAscent = ascent;
newFont->fFM.fDescent = descent;
newFont->fFM.fLinespace = ascent + descent;
newFont->fFM.fMaxWidth = gVirtualX->TextWidth(fontStruct, "@", 1);
newFont->fFM.fFixed = kTRUE;
newFont->fFontStruct = fontStruct;
newFont->fFontH = gVirtualX->GetFontHandle(fontStruct);
firstChar = 0x20;
lastChar = 0xff;
for (i = 0; i < 256; i++) {
if ((i == 0177) || (i < firstChar) || (i > lastChar)) {
newFont->fTypes[i] = kCharReplace;
} else {
newFont->fTypes[i] = kCharNormal;
}
}
char ch[2] = {0, 0};
width = 0;
for (i = 0; i < 256; i++) {
if (newFont->fTypes[i] != kCharNormal) {
n = 0;
} else {
ch[0] = i;
n = gVirtualX->TextWidth(fontStruct, ch, 1);
}
newFont->fWidths[i] = n;
if (n) {
if (!width) {
width = n;
} else if (width != n) {
newFont->fFM.fFixed = kFALSE;
}
}
}
replaceOK = kTRUE;
for (p = gHexChars; *p != '\0'; p++) {
if ((UChar_t(*p) < firstChar) || (UChar_t(*p) > lastChar)) {
replaceOK = kFALSE;
break;
}
}
for (i = 0; i < 256; i++) {
if (newFont->fTypes[i] == kCharReplace) {
if (replaceOK) {
n = GetControlCharSubst(i, buf);
for (; --n >= 0;) {
newFont->fWidths[i] += newFont->fWidths[UChar_t(buf[n])];
}
} else {
newFont->fTypes[i] = kCharSkip;
}
}
}
newFont->fUnderlinePos = descent >> 1;
newFont->fBarHeight = newFont->fWidths[(int)'I']/3;
if (newFont->fBarHeight == 0) {
newFont->fBarHeight = 1;
}
if (newFont->fUnderlinePos + newFont->fBarHeight > descent) {
newFont->fBarHeight = descent - newFont->fUnderlinePos;
if (!newFont->fBarHeight) {
newFont->fUnderlinePos--;
newFont->fBarHeight = 1;
}
}
return newFont;
}
static Int_t GetControlCharSubst(Int_t c, char buf[4])
{
buf[0] = '\\';
if (((UInt_t)c < sizeof(gMapChars)) && (gMapChars[c] != 0)) {
buf[1] = gMapChars[c];
return 2;
} else {
buf[1] = 'x';
buf[2] = gHexChars[(c >> 4) & 0xf];
buf[3] = gHexChars[c & 0xf];
return 4;
}
}
This page has been automatically generated. If you have any comments or suggestions about the page layout send a mail to ROOT support, or contact the developers with any questions or problems regarding ROOT.