You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 

334 lines
9.3 KiB

// $Id$
#include <stdio.h>
#include <stdlib.h>
#include <kdebug.h>
#include <klocale.h>
#include <qapplication.h>
#include <qfile.h>
#include "dviwin.h"
#include "font.h"
#include "kdvi.h"
#include "xdvi.h"
extern void oops(QString message);
#define PK_PRE 247
#define PK_ID 89
#define PK_MAGIC (PK_PRE << 8) + PK_ID
#define GF_PRE 247
#define GF_ID 131
#define GF_MAGIC (GF_PRE << 8) + GF_ID
#define VF_PRE 247
#define VF_ID_BYTE 202
#define VF_MAGIC (VF_PRE << 8) + VF_ID_BYTE
macro::macro()
{
pos = 0L; /* address of first byte of macro */
end = 0L; /* address of last+1 byte */
dvi_adv = 0; /* DVI units to move reference point */
free_me = false;
}
macro::~macro()
{
if ((pos != 0L) && (free_me == true))
delete [] pos;
}
void font::fontNameReceiver(QString fname)
{
flags |= font::FONT_LOADED;
filename = fname;
#ifdef DEBUG_FONT
kdDebug() << "FONT NAME RECEIVED:" << filename << endl;
#endif
file = fopen(QFile::encodeName(filename), "r");
if (file == NULL) {
kdError() << i18n("Can't find font ") << fontname << "." << endl;
return;
}
set_char_p = &dviWindow::set_char;
int magic = two(file);
if (magic == PK_MAGIC) {
// Achtung! Read_PK_index kann auch schiefgehen!
read_PK_index();
set_char_p = &dviWindow::set_char;
} else
if (magic == GF_MAGIC)
oops(i18n("The GF format for font file %1 is no longer supported").arg(filename) );
else
if (magic == VF_MAGIC) {
read_VF_index();
set_char_p = &dviWindow::set_vf_char;
} else
oops(i18n("Cannot recognize format for font file %1").arg(filename) );
}
font::font(char *nfontname, float nfsize, long chk, Q_INT32 scale, double dconv, class fontPool *pool, float shrinkFact)
{
#ifdef DEBUG_FONT
kdDebug() << "constructing font " << nfontname << " at " << (int) (nfsize + 0.5) << " dpi" << endl;
#endif
shrinkFactor = shrinkFact;
font_pool = pool;
fontname = nfontname;
fsize = nfsize;
checksum = chk;
flags = font::FONT_IN_USE;
file = NULL;
filename = "";
dimconv = dconv;
scaled_size = scale;
glyphtable = 0;
macrotable = 0;
for(unsigned int i=0; i<max_num_of_chars_in_font; i++)
characterPixmaps[i] = 0;
// By default, this font contains only empty characters. After the
// font has been loaded, this function pointer will be replaced by
// another one.
set_char_p = &dviWindow::set_empty_char;
}
font::~font()
{
#ifdef DEBUG_FONT
kdDebug() << "discarding font " << fontname << " at " << (int)(fsize + 0.5) << " dpi" << endl;
#endif
if (fontname != 0)
delete [] fontname;
if (glyphtable != 0)
delete [] glyphtable;
if (macrotable != 0)
delete [] macrotable;
for(unsigned int i=0; i<max_num_of_chars_in_font; i++)
if (characterPixmaps[i])
delete characterPixmaps[i];
if (flags & FONT_LOADED) {
if (file != NULL)
fclose(file);
if (flags & FONT_VIRTUAL)
vf_table.clear();
}
}
void font::setShrinkFactor(float sf)
{
shrinkFactor = sf;
for(unsigned int i=0; i<max_num_of_chars_in_font; i++) {
if (characterPixmaps[i] != 0) {
delete characterPixmaps[i];
characterPixmaps[i] = 0;
}
}
}
/** mark_as_used marks the font, and all the fonts it referrs to, as
used, i.e. their FONT_IN_USE-flag is set. */
void font::mark_as_used(void)
{
#ifdef DEBUG_FONT
kdDebug() << "marking font " << fontname << " at " << (int) (fsize + 0.5) << " dpi" << endl;
#endif
if (flags & font::FONT_IN_USE)
return;
flags |= font::FONT_IN_USE;
// For virtual fonts, also go through the list of referred fonts
if (flags & font::FONT_VIRTUAL) {
QIntDictIterator<font> it(vf_table);
while( it.current() ) {
it.current()->flags |= font::FONT_IN_USE;
++it;
}
}
}
QPixmap font::characterPixmap(unsigned int ch)
{
if (ch > max_num_of_chars_in_font) {
kdError(4300) << "Tried to access character with number " << ch << endl;
return nullPixmap;
}
if (characterPixmaps[ch])
return *(characterPixmaps[ch]);
glyph *g = glyphptr(ch);
if (g == 0) {
return nullPixmap;
}
// Otherwise, we rescale the bitmap in order to produce the required
// pixmap. Rescaling a character, however, is an art that requires
// some explanation...
//
// If we would just divide the size of the character and the
// coordinates by the shrink factor, then the result would look
// quite ugly: due to the ineviatable rounding errors in the integer
// arithmetic, the characters would be displaced by up to a
// pixel. That doesn't sound much, but on low-resolution devices,
// such as a notebook screen, the effect would be a "dancing line"
// of characters, which looks really bad.
//
// The cure is the following procedure:
//
// (a) scale the hot point
//
// (b) fit the unshrunken bitmap into a bitmap which is even
// bigger. Use this to produce extra empty rows and columns at the
// borders. The proper choice of the border size will ensure that
// the hot point will fall exactly onto the coordinates which we
// calculated previously.
// Here the cheating starts ... on the screen many fonts look very
// light. We improve on the looks by lowering the shrink factor just
// when shrinking the characters. The position of the chars on the
// screen will not be affected, the chars are just slightly larger.
// Calculate the coordinates of the hot point in the shrunken
// bitmap
g->x2 = (int)(g->x/shrinkFactor);
g->y2 = (int)(g->y/shrinkFactor);
// Calculate the size of the target bitmap for the
int shrunk_width = g->x2 + (int)((g->bitmap.w-g->x) / shrinkFactor + 0.5) + 1;
int shrunk_height = g->y2 + (int)((g->bitmap.h-g->y) / shrinkFactor + 0.5) + 1;
// Now calculate the size of the white border. This is some sort
// of black magic. Don't modify unless you know what you are doing.
int pre_rows = (int)((1.0 + g->y2)*shrinkFactor + 0.5) - g->y - 1;
if (pre_rows < 0)
pre_rows = 0;
int post_rows = (int)(shrunk_height*shrinkFactor + 0.5) - g->bitmap.h;
if (post_rows < 0)
post_rows = 0;
int pre_cols = (int)((1.0 + g->x2)*shrinkFactor + 0.5) - g->x - 1;
if (pre_cols < 0)
pre_cols = 0;
int post_cols = (int)(shrunk_width*shrinkFactor + 0.5) - g->bitmap.w;
if (post_cols < 0)
post_cols = 0;
// Now shrinking may begin. Produce a QBitmap with the unshrunk
// character.
QBitmap bm(g->bitmap.bytes_wide*8, (int)g->bitmap.h, (const uchar *)(g->bitmap.bits), TRUE);
// ... turn it into a Pixmap (highly inefficient, please improve)
characterPixmaps[ch] = new QPixmap(g->bitmap.w+pre_cols+post_cols, g->bitmap.h+pre_rows+post_rows);
if ((characterPixmaps[ch] == 0) || (characterPixmaps[ch]->isNull())) {
kdError(4300) << "Could not properly allocate SmallChar in glyph::shrunkCharacter!" << endl;
if (characterPixmaps[ch] != 0)
delete characterPixmaps[ch];
characterPixmaps[ch] = 0;
return 0;
}
if (!bm.isNull()) {
QPainter paint(characterPixmaps[ch]);
paint.setBackgroundColor(Qt::white);
paint.setPen( Qt::black );
paint.fillRect(0,0,g->bitmap.w+pre_cols+post_cols, g->bitmap.h+pre_rows+post_rows, Qt::white);
paint.drawPixmap(pre_cols, pre_rows, bm);
paint.end();
} else
kdError(4300) << "Null Bitmap in glyph::shrunkCharacter encountered!" << endl;
// Generate an Image and shrink it to the proper size. By the
// documentation of smoothScale, the resulting Image will be
// 8-bit.
QImage im = characterPixmaps[ch]->convertToImage().smoothScale(shrunk_width, shrunk_height);
// Generate the alpha-channel. This again is highly inefficient.
// Would anybody please produce a faster routine?
QImage im32 = im.convertDepth(32);
im32.setAlphaBuffer(TRUE);
for(int y=0; y<im.height(); y++) {
QRgb *imag_scanline = (QRgb *)im32.scanLine(y);
for(int x=0; x<im.width(); x++) {
// Make White => Transparent
if ((0x00ffffff & *imag_scanline) == 0x00ffffff)
*imag_scanline &= 0x00ffffff;
else
*imag_scanline |= 0xff000000;
imag_scanline++; // Disgusting pointer arithmetic. Should be forbidden.
}
}
characterPixmaps[ch]->convertFromImage(im32,0);
characterPixmaps[ch]->setOptimization(QPixmap::BestOptim);
return *(characterPixmaps[ch]);
}
/** Returns a pointer to glyph number ch in the font, or NULL, if this
number does not exist. This function first reads the bitmap of the
character from the PK-file, if necessary */
glyph *font::glyphptr(unsigned int ch) {
// Paranoid checks
if (ch > max_num_of_chars_in_font) {
kdError(4300) << "Tried to access character with number " << ch << endl;
return 0;
}
if (glyphtable == 0) {
kdError(4300) << "Tried to access glyphptr() for non-character font!" << endl;
return 0;
}
struct glyph *g = glyphtable+ch;
if (g->bitmap.bits == NULL) {
if (g->addr == 0) {
kdError() << i18n("Character %1 not defined in font %2").arg(ch).arg(fontname) << endl;
g->addr = -1;
return NULL;
}
if (g->addr == -1)
return NULL; /* previously flagged missing char */
if (file == NULL) {
file = fopen(QFile::encodeName(filename), "r");
if (file == NULL) {
oops(i18n("Font file disappeared: %1").arg(filename) );
return NULL;
}
}
fseek(file, g->addr, 0);
read_PK_char(ch);
if (g->bitmap.bits == NULL) {
g->addr = -1;
return NULL;
}
}
return g;
}
#include "font.moc"