// XFontImp.cc for FbTk fluxbox toolkit
// Copyright (c) 2002 - 2006 Henrik Kinnunen (fluxgen at fluxbox dot org)
//
// Permission is hereby granted, free of charge, to any person obtaining a
// copy of this software and associated documentation files (the "Software"),
// to deal in the Software without restriction, including without limitation
// the rights to use, copy, modify, merge, publish, distribute, sublicense,
// and/or sell copies of the Software, and to permit persons to whom the
// Software is furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
// DEALINGS IN THE SOFTWARE.
// $Id: XFontImp.cc 4945 2007-06-29 17:25:24Z markt $
#include "XFontImp.hh"
#include "App.hh"
#include "GContext.hh"
#include "FbPixmap.hh"
#include "I18n.hh"
#include <X11/Xutil.h>
#include <iostream>
#include <new>
#ifdef HAVE_CSTDIO
#include <cstdio>
#else
#include <stdio.h>
#endif
#ifdef HAVE_CSTRING
#include <cstring>
#else
#include <string.h>
#endif
#include <algorithm>
using std::cerr;
using std::endl;
using std::string;
using std::nothrow;
namespace FbTk {
XFontImp::XFontImp(const char *fontname):m_fontstruct(0) {
for (int i = ROT0; i <= ROT270; ++i) {
m_rotfonts[i] = 0;
m_rotfonts_loaded[i] = false;
}
if (fontname != 0)
load(fontname);
}
XFontImp::~XFontImp() {
if (m_fontstruct != 0)
XFreeFont(App::instance()->display(), m_fontstruct);
for (int i = ROT0; i <= ROT270; ++i)
if (m_rotfonts[i] != 0)
freeRotFont(m_rotfonts[i]);
}
int XFontImp::ascent() const {
if (m_fontstruct == 0)
return 0;
return m_fontstruct->ascent;
}
bool XFontImp::load(const string &fontname) {
XFontStruct *font = XLoadQueryFont(App::instance()->display(), fontname.c_str());
if (font == 0)
return false;
if (m_fontstruct != 0) // free old font struct, if any
XFreeFont(App::instance()->display(), m_fontstruct);
m_fontstruct = font; //set new font
for (int i = ROT0; i <= ROT270; ++i) {
m_rotfonts_loaded[i] = false;
if (m_rotfonts[i] != 0) {
freeRotFont(m_rotfonts[i]);
m_rotfonts[i] = 0;
}
}
return true;
}
void XFontImp::drawText(const FbDrawable &w, int screen, GC gc, const FbString &text, size_t len, int x, int y, FbTk::Orientation orient) {
if (m_fontstruct == 0)
return;
// use roated font functions?
if (orient != ROT0 && validOrientation(orient)) {
drawRotText(w.drawable(), screen, gc, text, len, x, y, orient);
return;
}
string localestr = text;
localestr.erase(len, string::npos);
localestr = FbStringUtil::FbStrToLocale(localestr);
XSetFont(w.display(), gc, m_fontstruct->fid);
XDrawString(w.display(), w.drawable(), gc, x, y, localestr.data(), localestr.size());
}
unsigned int XFontImp::textWidth(const FbString &text, unsigned int size) const {
if (text.empty() || m_fontstruct == 0)
return 0;
string localestr = text;
localestr.erase(size, string::npos);
localestr = FbStringUtil::FbStrToLocale(localestr);
return XTextWidth(m_fontstruct, localestr.data(), localestr.size());
}
unsigned int XFontImp::height() const {
if (m_fontstruct == 0)
return 0;
return m_fontstruct->ascent + m_fontstruct->descent;
}
void XFontImp::rotate(FbTk::Orientation orient) {
//we must have a font loaded before we rotate
if (m_fontstruct == 0 || m_fontstruct->per_char == 0 || orient == ROT0)
return;
_FB_USES_NLS;
// X system default vars
Display *dpy = App::instance()->display();
Window rootwin = DefaultRootWindow(dpy);
int screen = DefaultScreen(dpy);
char text[3];
int ichar, i, j, index, boxlen = 60;
int vert_w, vert_h, vert_len, bit_w, bit_h, bit_len;
int min_char, max_char;
unsigned char *vertdata, *bitdata;
int ascent, descent, lbearing, rbearing;
// create the depth 1 canvas bitmap
FbTk::FbPixmap canvas(rootwin, boxlen, boxlen, 1);
// create graphic context for our canvas
FbTk::GContext font_gc(canvas);
font_gc.setBackground(None);
font_gc.setFont(m_fontstruct->fid);
// allocate space for rotated font
m_rotfonts[orient] = new(nothrow) XRotFontStruct;
XRotFontStruct *rotfont = m_rotfonts[orient];
if (rotfont == 0) {
cerr<<"RotFont: "<<_FBTK_CONSOLETEXT(Error, OutOfMemory, "Out of memory", "Something couldn't allocate memory")<<endl;
return;
}
// determine which characters are defined in font
min_char = m_fontstruct->min_char_or_byte2;
max_char = m_fontstruct->max_char_or_byte2;
// we only want printable chars
if (min_char<32)
min_char = 32;
if (max_char>126)
max_char = 126;
/* some overall font data ... */
rotfont->min_char = min_char;
rotfont->max_char = max_char;
rotfont->max_ascent = m_fontstruct->max_bounds.ascent;
rotfont->max_descent = m_fontstruct->max_bounds.descent;
rotfont->height = rotfont->max_ascent + rotfont->max_descent;
// font needs rotation
// loop through each character
for (ichar = min_char; ichar <= max_char; ichar++) {
index = ichar - m_fontstruct->min_char_or_byte2;
// per char dimensions ...
ascent = rotfont->per_char[ichar-32].ascent = m_fontstruct->per_char[index].ascent;
descent = rotfont->per_char[ichar-32].descent = m_fontstruct->per_char[index].descent;
lbearing = rotfont->per_char[ichar-32].lbearing = m_fontstruct->per_char[index].lbearing;
rbearing = rotfont->per_char[ichar-32].rbearing = m_fontstruct->per_char[index].rbearing;
rotfont->per_char[ichar-32].width = m_fontstruct->per_char[index].width;
// some space chars have zero body, but a bitmap can't have
if (!ascent && !descent)
ascent = rotfont->per_char[ichar-32].ascent = 1;
if (!lbearing && !rbearing)
rbearing = rotfont->per_char[ichar-32].rbearing = 1;
// glyph width and height when vertical
vert_w = rbearing - lbearing;
vert_h = ascent + descent;
// width in bytes
vert_len = (vert_w-1)/8+1;
font_gc.setForeground(None);
canvas.fillRectangle(font_gc.gc(),
0, 0,
boxlen, boxlen);
// draw the character centre top right on canvas
sprintf(text, "%c", ichar);
font_gc.setForeground(1);
XDrawImageString(dpy, canvas.drawable(), font_gc.gc(),
boxlen/2 - lbearing,
boxlen/2 - descent, text, 1);
// reserve memory for first XImage
vertdata = (unsigned char *)calloc((unsigned)(vert_len * vert_h), 1);
XImage *I1 = XCreateImage(dpy, DefaultVisual(dpy, screen),
1, XYBitmap,
0, (char *)vertdata,
vert_w, vert_h, 8, 0);
if (I1 == None) {
cerr << "RotFont: " << _FBTK_CONSOLETEXT(Error, CreateXImage,
"Can't create XImage",
"XCreateImage failed for some reason")
<< "." << endl;
free(vertdata);
delete rotfont;
m_rotfonts[orient] = 0;
return;
}
I1->byte_order = I1->bitmap_bit_order = MSBFirst;
// extract character from canvas
XGetSubImage(dpy, canvas.drawable(),
boxlen/2, boxlen/2 - vert_h,
vert_w, vert_h, 1, XYPixmap, I1, 0, 0);
I1->format = XYBitmap;
// width, height of rotated character
if (orient == ROT180) {
bit_w = vert_w;
bit_h = vert_h;
} else {
bit_w = vert_h;
bit_h = vert_w;
}
// width in bytes
bit_len = (bit_w-1)/8 + 1;
rotfont->per_char[ichar-32].glyph.bit_w = bit_w;
rotfont->per_char[ichar-32].glyph.bit_h = bit_h;
// reserve memory for the rotated image
bitdata = (unsigned char *)calloc((unsigned)(bit_h * bit_len), 1);
// create the image
XImage *I2 = XCreateImage(dpy, DefaultVisual(dpy, screen), 1, XYBitmap, 0,
(char *)bitdata, bit_w, bit_h, 8, 0);
if (I2 == None) {
cerr << "XFontImp: " <<_FBTK_CONSOLETEXT(Error, CreateXImage,
"Can't create XImage",
"XCreateImage failed for some reason")
<< "." << endl;
free(bitdata);
delete rotfont;
m_rotfonts[orient] = 0;
return;
}
I2->byte_order = I2->bitmap_bit_order = MSBFirst;
// map vertical data to rotated character
for (j = 0; j < bit_h; j++) {
for (i = 0; i < bit_w; i++) {
char val = 0;
if (orient == ROT270) {
val = vertdata[i*vert_len + (vert_w-j-1)/8] &
(128>>((vert_w-j-1)%8));
} else if (orient == ROT180) {
val = vertdata[(vert_h-j-1)*vert_len +
(vert_w-i-1)/8] & (128>>((vert_w-i-1)%8));
} else {
val = vertdata[(vert_h-i-1)*vert_len + j/8] &
(128>>(j%8));
}
if (val) {
bitdata[j*bit_len + i/8] = bitdata[j*bit_len + i/8] |
(128>>(i%8));
}
}
}
// create this character's bitmap
rotfont->per_char[ichar-32].glyph.bm =
XCreatePixmap(dpy, rootwin, bit_w, bit_h, 1);
// put the image into the bitmap
XPutImage(dpy, rotfont->per_char[ichar-32].glyph.bm,
font_gc.gc(), I2, 0, 0, 0, 0, bit_w, bit_h);
// free the image and data
XDestroyImage(I1);
XDestroyImage(I2);
}
}
void XFontImp::freeRotFont(XRotFontStruct *rotfont) {
// loop through each character and free its pixmap
for (int ichar = rotfont->min_char - 32;
ichar <= rotfont->max_char - 32; ++ichar) {
XFreePixmap(App::instance()->display(), rotfont->per_char[ichar].glyph.bm);
}
delete rotfont;
rotfont = 0;
}
void XFontImp::drawRotText(Drawable w, int screen, GC gc, const FbString &text, size_t len, int x, int y, FbTk::Orientation orient) const {
Display *dpy = App::instance()->display();
static GC my_gc = 0;
int xp, yp, ichar;
XRotFontStruct *rotfont = m_rotfonts[orient];
if (text.empty() || len<1)
return;
if (my_gc == 0)
my_gc = XCreateGC(dpy, w, 0, 0);
XCopyGC(dpy, gc, GCForeground|GCBackground, my_gc);
// vertical or upside down
XSetFillStyle(dpy, my_gc, FillStippled);
string localestr = text;
localestr.erase(len, string::npos);
localestr = FbStringUtil::FbStrToLocale(localestr);
const char *ctext = localestr.data();
len = localestr.size();
// loop through each character in texting
for (size_t i = 0; i<len; i++) {
ichar = ctext[i]-32;
// make sure it's a printing character
if (ichar >= 0 && ichar<95) {
// suitable offset
if (orient == ROT270) {
xp = x-rotfont->per_char[ichar].ascent;
yp = y-rotfont->per_char[ichar].rbearing;
} else if (orient == ROT180) {
xp = x-rotfont->per_char[ichar].rbearing;
yp = y-rotfont->per_char[ichar].descent+1;
} else { // ROT90
xp = x-rotfont->per_char[ichar].descent;
yp = y+rotfont->per_char[ichar].lbearing;
}
// draw the glyph
XSetStipple(dpy, my_gc, rotfont->per_char[ichar].glyph.bm);
XSetTSOrigin(dpy, my_gc, xp, yp);
XFillRectangle(dpy, w, my_gc, xp, yp,
rotfont->per_char[ichar].glyph.bit_w,
rotfont->per_char[ichar].glyph.bit_h);
// advance position
if (orient == ROT270)
y -= rotfont->per_char[ichar].width;
else if (orient == ROT180)
x -= rotfont->per_char[ichar].width;
else
y += rotfont->per_char[ichar].width;
}
}
}
bool XFontImp::validOrientation(FbTk::Orientation orient) {
if (orient == ROT0 || m_rotfonts[orient])
return true;
if (m_rotfonts_loaded[orient])
return false; // load must have failed
m_rotfonts_loaded[orient] = true;
rotate(orient);
return m_rotfonts[orient] != 0;
}
};
syntax highlighted by Code2HTML, v. 0.9.1