/trunk/src/plfreetype.c
C | 1288 lines | 676 code | 177 blank | 435 comment | 150 complexity | bcf60cf4dcf815b0da87f397b9a0d62c MD5 | raw file
Possible License(s): LGPL-2.0, BSD-3-Clause-No-Nuclear-License-2014, Apache-2.0, GPL-2.0
- // $Id: plfreetype.c 12147 2012-01-22 00:39:02Z airwin $
- //
- // Copyright (C) 2002, 2004, 2005 Andrew Roach
- // Copyright (C) 2002 Maurice LeBrun
- // Copyright (C) 2002-2012 Alan W. Irwin
- // Copyright (C) 2003, 2004 Joao Cardoso
- // Copyright (C) 2003, 2004, 2005 Rafael Laboissiere
- // Copyright (C) 2004 Andrew Ross
- //
- // This file is part of PLplot.
- //
- // PLplot is free software; you can redistribute it and/or modify
- // it under the terms of the GNU Library General Public License as published
- // by the Free Software Foundation; either version 2 of the License, or
- // (at your option) any later version.
- //
- // PLplot is distributed in the hope that it will be useful,
- // but WITHOUT ANY WARRANTY; without even the implied warranty of
- // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- // GNU Library General Public License for more details.
- //
- // You should have received a copy of the GNU Library General Public License
- // along with PLplot; if not, write to the Free Software
- // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
- //
- //
- // Support routines for freetype font engine
- //
- // This file contains a series of support routines for drivers interested
- // in using freetype rendered fonts instead of plplot plotter fonts.
- // Freetype supports a gerth of font formats including TrueType, OpenType,
- // Adobe Type1, Type42 etc... the list seems almost endless. Any bitmap
- // driver should be able to use any of these freetype fonts from plplot if
- // these routines are properly initialised.
- //
- // Freetype support is not intended to be a "feature" of the common API,
- // but is implemented as a driver-specific optional extra invoked via the
- // -drvopt command line toggle. It is intended to be used in the context of
- // "PLESC_HAS_TEXT" for any bitmap drivers without native font support.
- // Implementing freetype in this manner minimise changes to the overall
- // API. Because of this approach, there is not a "wealth" of font options
- // available to the programmer. You can not do anything you can't do for a
- // normal freetype plotter font like boldface. You can do most of the
- // things that you can do with a plotter font however, like greek
- // characters superscripting, and selecting one of the four "pre-defined"
- // plplot font types. At present underlining and overlining are not
- // supported.
- //
- // To give the user some level of control over the fonts that are used,
- // environmental variables can be set to over-ride the definitions used by
- // the five default plplot fonts.
- //
- // The exact syntax for evoking freetype fonts is dependant on each
- // driver, but for the GD and GNUSVGA drivers I have followed the syntax of
- // the PS driver and use the command-line switch of "-drvopt text" to
- // activate the feature, and suggest other programmers do the same for
- // commonality.
- //
- // Both anti-aliased and monochrome font rendering is supported by these
- // routines. How these are evoked depends on the programmer, but with the
- // GD and GNUSVGA driver families I have used the command-line switch
- // "-drvopt smooth" to activate the feature; but, considering you also need
- // to turn freetype on, it would probably really be more like "-drvopt
- // text,smooth".
- //
- //
- #if !defined ( WIN32 ) || defined ( __GNUC__ )
- #include <unistd.h>
- #else
- #define F_OK 1
- #include <stdio.h>
- int access( char *filename, int flag )
- {
- FILE *infile;
- infile = fopen( filename, "r" );
- if ( infile != NULL )
- {
- fclose( infile );
- return 0;
- }
- else
- {
- return 1;
- }
- }
- #endif
- #define makeunixslash( b ) do { char *I; for ( I = b; *I != 0; *I++ ) if ( *I == '\\' ) *I = '/';} while ( 0 )
- #include "plDevs.h"
- #include "plplotP.h"
- #include "drivers.h"
- #ifdef HAVE_FREETYPE
- #include "plfreetype.h"
- #include "plfci-truetype.h"
- #define FT_Data _FT_Data_
- // Font lookup table that is constructed in plD_FreeType_init
- PLDLLIMPEXP_DATA( FCI_to_FontName_Table ) FontLookup[N_TrueTypeLookup];
- // TOP LEVEL DEFINES
- // Freetype lets you set the text size absolutely. It also takes into
- // account the DPI when doing so. So does plplot. Why, then, is it that the
- // size of the text drawn by plplot is bigger than the text drawn by
- // freetype when given IDENTICAL parameters ? Perhaps I am missing
- // something somewhere, but to fix this up we use TEXT_SCALING_FACTOR to
- // set a scaling factor to try and square things up a bit.
- //
- #define TEXT_SCALING_FACTOR .7
- // default size of temporary text buffer
- // If we wanted to be fancy we could add sizing, but this should be big enough
- #define NTEXT_ALLOC 1024
- //--------------------------------------------------------------------------
- // Some debugging macros
- //--------------------------------------------------------------------------
- #define Debug6( a, b, c, d, e, f ) do { if ( pls->debug ) { fprintf( stderr, a, b, c, d, e, f ); } } while ( 0 )
- // FUNCTION PROTOTYPES
- // Public prototypes, generally available to the API
- void plD_FreeType_init( PLStream *pls );
- void plD_render_freetype_text( PLStream *pls, EscText *args );
- void plD_FreeType_Destroy( PLStream *pls );
- void pl_set_extended_cmap0( PLStream *pls, int ncol0_width, int ncol0_org );
- void pl_RemakeFreeType_text_from_buffer( PLStream *pls );
- void plD_render_freetype_sym( PLStream *pls, EscText *args );
- // Private prototypes for use in this file only
- static void FT_PlotChar( PLStream *pls, FT_Data *FT, FT_GlyphSlot slot, int x, int y );
- static void FT_SetFace( PLStream *pls, PLUNICODE fci );
- static PLFLT CalculateIncrement( int bg, int fg, int levels );
- // These are never defined, maybe they will be used in the future?
- //
- // static void pl_save_FreeType_text_to_buffer (PLStream *pls, EscText *args);
- // static FT_ULong hershey_to_unicode (char in);
- //
- //
- static void FT_WriteStrW( PLStream *pls, const PLUNICODE *text, short len, int x, int y );
- static void FT_StrX_YW( PLStream *pls, const PLUNICODE *text, short len, int *xx, int *yy );
- //--------------------------------------------------------------------------
- // FT_StrX_YW()
- //
- // Returns the dimensions of the text box. It does this by fully parsing
- // the supplied text through the rendering engine. It does everything
- // but draw the text. This seems, to me, the easiest and most accurate
- // way of determining the text's dimensions. If/when caching is added,
- // the CPU hit for this "double processing" will be minimal.
- //--------------------------------------------------------------------------
- void
- FT_StrX_YW( PLStream *pls, const PLUNICODE *text, short len, int *xx, int *yy )
- {
- FT_Data *FT = (FT_Data *) pls->FT;
- short i = 0;
- FT_Vector akerning;
- int x = 0, y = 0;
- char esc;
- plgesc( &esc );
- //
- // Things seems to work better with this line than without it;
- // I guess because there is no vertical kerning or advancement for most
- // non-transformed fonts, so we need to define *something* for the y height,
- // and this is the best thing I could think of.
- //
- y -= (int) FT->face->size->metrics.height;
- // walk through the text character by character
- for ( i = 0; i < len; i++ )
- {
- if ( ( text[i] == (PLUNICODE) esc ) && ( text[i - 1] != (PLUNICODE) esc ) )
- {
- if ( text[i + 1] == (PLUNICODE) esc )
- continue;
- switch ( text[i + 1] )
- {
- case 'u': // super script
- case 'd': // subscript
- case 'U':
- case 'D':
- i++;
- break;
- }
- }
- else if ( text[i] & PL_FCI_MARK )
- {
- // FCI in text stream; change font accordingly.
- FT_SetFace( pls, text[i] );
- }
- else
- {
- // see if we have kerning for the particular character pair
- if ( ( i > 0 ) && FT_HAS_KERNING( FT->face ) )
- {
- FT_Get_Kerning( FT->face,
- text[i - 1],
- text[i],
- ft_kerning_default,
- &akerning );
- x += (int) ( akerning.x >> 6 ); // add (or subtract) the kerning
- }
- //
- // Next we load the char. This also draws the char, transforms it, and
- // converts it to a bitmap. At present this is a bit wasteful, but
- // if/when I add cache support, then this data won't go to waste.
- // Since there is no sense in going to the trouble of doing anti-aliasing
- // calculations since we aren't REALLY plotting anything, we will render
- // this as monochrome since it is probably marginally quicker. If/when
- // cache support is added, naturally this will have to change.
- //
- FT_Load_Char( FT->face, text[i], FT_LOAD_MONOCHROME + FT_LOAD_RENDER );
- //
- // Add in the "advancement" needed to position the cursor for the next
- // character. Unless the text is transformed, "y" will always be zero.
- // Y is negative because freetype does things upside down
- //
- x += (int) ( FT->face->glyph->advance.x );
- y -= (int) ( FT->face->glyph->advance.y );
- }
- }
- //
- // Convert from unit of 1/64 of a pixel to pixels, and do it real fast with
- // a bitwise shift (mind you, any decent compiler SHOULD optimise /64 this way
- // anyway...)
- //
- // (RL, on 2005-01-23) Removed the shift bellow to avoid truncation errors
- // later.
- //yy=y>> 6;
- //xx=x>> 6;
- //
- *yy = y;
- *xx = x;
- }
- //--------------------------------------------------------------------------
- // FT_WriteStrW()
- //
- // Writes a string of FT text at the current cursor location.
- // most of the code here is identical to "FT_StrX_Y" and I will probably
- // collapse the two into some more efficient code eventually.
- //--------------------------------------------------------------------------
- void
- FT_WriteStrW( PLStream *pls, const PLUNICODE *text, short len, int x, int y )
- {
- FT_Data *FT = (FT_Data *) pls->FT;
- short i = 0, last_char = -1;
- FT_Vector akerning, adjust;
- char esc;
- plgesc( &esc );
- //
- // Adjust for the descender - make sure the font is nice and centred
- // vertically. Freetype assumes we have a base-line, but plplot thinks of
- // centre-lines, so that's why we have to do this. Since this is one of our
- // own adjustments, rather than a freetype one, we have to run it through
- // the transform matrix manually.
- //
- // For some odd reason, this works best if we triple the
- // descender's height and then adjust the height later on...
- // Don't ask me why, 'cause I don't know. But it does seem to work.
- //
- // I really wish I knew *why* it worked better though...
- //
- // y-=FT->face->descender >> 6;
- //
- #ifdef DODGIE_DECENDER_HACK
- adjust.y = ( FT->face->descender >> 6 ) * 3;
- #else
- adjust.y = ( FT->face->descender >> 6 );
- #endif
- // (RL) adjust.y is zeroed below,, making the code above (around
- // DODGIE_DECENDER_HACK) completely useless. This is necessary for
- // getting the vertical alignment of text right, which is coped with
- // in function plD_render_freetype_text now.
- //
- adjust.x = 0;
- adjust.y = 0;
- FT_Vector_Transform( &adjust, &FT->matrix );
- x += (int) adjust.x;
- y -= (int) adjust.y;
- // (RL, on 2005-01-25) The computation of cumulated glyph width within
- // the text is done now with full precision, using 26.6 Freetype
- // arithmetics. We should then shift the x and y variables by 6 bits,
- // as below. Inside the character for loop, all operations regarding
- // x and y will be done in 26.6 mode and these variables will be
- // converted to integers when passed to FT_PlotChar. Notrice that we
- // are using ROUND and float division instead of ">> 6" now. This
- // minimizes truncation errors.
- //
- x <<= 6;
- y <<= 6;
- // walk through the text character by character
- for ( i = 0; i < len; i++ )
- {
- if ( ( text[i] == (PLUNICODE) esc ) && ( text[i - 1] != (PLUNICODE) esc ) )
- {
- if ( text[i + 1] == (PLUNICODE) esc )
- continue;
- switch ( text[i + 1] )
- {
- //
- // We run the OFFSET for the super-script and sub-script through the
- // transformation matrix so we can calculate nice and easy the required
- // offset no matter what's happened rotation wise. Everything else, like
- // kerning and advancing from character to character is transformed
- // automatically by freetype, but since the superscript/subscript is a
- // feature of plplot, and not freetype, we have to make allowances.
- //
- case 'u': // super script
- case 'U': // super script
- adjust.y = FT->face->size->metrics.height / 2;
- adjust.x = 0;
- FT_Vector_Transform( &adjust, &FT->matrix );
- x += (int) adjust.x;
- y -= (int) adjust.y;
- i++;
- break;
- case 'd': // subscript
- case 'D': // subscript
- adjust.y = -FT->face->size->metrics.height / 2;
- adjust.x = 0;
- FT_Vector_Transform( &adjust, &FT->matrix );
- x += (int) adjust.x;
- y -= (int) adjust.y;
- i++;
- break;
- }
- }
- else if ( text[i] & PL_FCI_MARK )
- {
- // FCI in text stream; change font accordingly.
- FT_SetFace( pls, text[i] );
- FT = (FT_Data *) pls->FT;
- FT_Set_Transform( FT->face, &FT->matrix, &FT->pos );
- }
- else
- {
- // see if we have kerning for the particular character pair
- if ( ( last_char != -1 ) && ( i > 0 ) && FT_HAS_KERNING( FT->face ) )
- {
- FT_Get_Kerning( FT->face,
- text[last_char],
- text[i],
- ft_kerning_default, &akerning );
- x += (int) akerning.x; // add (or subtract) the kerning
- y -= (int) akerning.y; // Do I need this in case of rotation ?
- }
- FT_Load_Char( FT->face, text[i], ( FT->smooth_text == 0 ) ? FT_LOAD_MONOCHROME + FT_LOAD_RENDER : FT_LOAD_RENDER | FT_LOAD_FORCE_AUTOHINT );
- FT_PlotChar( pls, FT, FT->face->glyph,
- ROUND( x / 64.0 ), ROUND( y / 64.0 ) ); // render the text
- x += (int) FT->face->glyph->advance.x;
- y -= (int) FT->face->glyph->advance.y;
- last_char = i;
- }
- } // end for
- }
- //--------------------------------------------------------------------------
- // FT_PlotChar()
- //
- // Plots an individual character. I know some of this stuff, like colour
- // could be parsed from plstream, but it was just quicker this way.
- //--------------------------------------------------------------------------
- void
- FT_PlotChar( PLStream *pls, FT_Data *FT, FT_GlyphSlot slot,
- int x, int y )
- {
- unsigned char bittest;
- short i, k, j;
- int n = slot->bitmap.pitch;
- int current_pixel_colour;
- int R, G, B;
- PLFLT alpha_a;
- //PLFLT alpha_b;
- int xx;
- short imin, imax, kmin, kmax;
- // Corners of the clipping rectangle
- PLINT clipxmin, clipymin, clipxmax, clipymax, tmp;
- PLINT clpxmi, clpxma, clpymi, clpyma;
- // Convert clipping box into normal coordinates
- clipxmin = pls->clpxmi;
- clipxmax = pls->clpxma;
- clipymin = pls->clpymi;
- clipymax = pls->clpyma;
- if ( plsc->difilt )
- {
- difilt( &clipxmin, &clipymin, 1, &clpxmi, &clpxma, &clpymi, &clpyma );
- difilt( &clipxmax, &clipymax, 1, &clpxmi, &clpxma, &clpymi, &clpyma );
- }
- if ( FT->scale != 0.0 ) // scale was set
- {
- clipxmin = (PLINT) ( clipxmin / FT->scale );
- clipxmax = (PLINT) ( clipxmax / FT->scale );
- if ( FT->invert_y == 1 )
- {
- clipymin = (PLINT) ( FT->ymax - ( clipymin / FT->scale ) );
- clipymax = (PLINT) ( FT->ymax - ( clipymax / FT->scale ) );
- }
- else
- {
- clipymin = (PLINT) ( clipymin / FT->scale );
- clipymax = (PLINT) ( clipymax / FT->scale );
- }
- }
- else
- {
- clipxmin = (PLINT) ( clipxmin / FT->scalex );
- clipxmax = (PLINT) ( clipxmax / FT->scalex );
- if ( FT->invert_y == 1 )
- {
- clipymin = (PLINT) ( FT->ymax - ( clipymin / FT->scaley ) );
- clipymax = (PLINT) ( FT->ymax - ( clipymax / FT->scaley ) );
- }
- else
- {
- clipymin = (PLINT) ( clipymin / FT->scaley );
- clipymax = (PLINT) ( clipymax / FT->scaley );
- }
- }
- if ( clipxmin > clipxmax )
- {
- tmp = clipxmax;
- clipxmax = clipxmin;
- clipxmin = tmp;
- }
- if ( clipymin > clipymax )
- {
- tmp = clipymax;
- clipymax = clipymin;
- clipymin = tmp;
- }
- // Comment this out as it fails for cases where we want to plot text
- // in the background font, i.e. example 24.
- //
- //if ((slot->bitmap.pixel_mode==ft_pixel_mode_mono)||(pls->icol0==0)) {
- if ( slot->bitmap.pixel_mode == ft_pixel_mode_mono )
- {
- x += slot->bitmap_left;
- y -= slot->bitmap_top;
- imin = (short) MAX( 0, clipymin - y );
- imax = (short) MIN( slot->bitmap.rows, clipymax - y );
- for ( i = imin; i < imax; i++ )
- {
- for ( k = 0; k < n; k++ )
- {
- bittest = 128;
- for ( j = 0; j < 8; j++ )
- {
- if ( ( bittest & (unsigned char) slot->bitmap.buffer[( i * n ) + k] ) == bittest )
- {
- xx = x + ( k * 8 ) + j;
- if ( ( xx >= clipxmin ) && ( xx <= clipxmax ) )
- FT->pixel( pls, xx, y + i );
- }
- bittest >>= 1;
- }
- }
- }
- }
- // this is the anti-aliased stuff
- else
- {
- x += slot->bitmap_left;
- y -= slot->bitmap_top;
- imin = (short) MAX( 0, clipymin - y );
- imax = (short) MIN( slot->bitmap.rows, clipymax - y );
- kmin = (short) MAX( 0, clipxmin - x );
- kmax = (short) MIN( slot->bitmap.width, clipxmax - x );
- for ( i = imin; i < imax; i++ )
- {
- for ( k = kmin; k < kmax; k++ )
- {
- FT->shade = ( slot->bitmap.buffer[( i * slot->bitmap.width ) + k] );
- if ( FT->shade > 0 )
- {
- if ( ( FT->BLENDED_ANTIALIASING == 1 ) && ( FT->read_pixel != NULL ) )
- // The New anti-aliasing technique
- {
- if ( FT->shade == 255 )
- {
- FT->pixel( pls, x + k, y + i );
- }
- else
- {
- current_pixel_colour = FT->read_pixel( pls, x + k, y + i );
- G = GetGValue( current_pixel_colour );
- R = GetRValue( current_pixel_colour );
- B = GetBValue( current_pixel_colour );
- alpha_a = (float) FT->shade / 255.0;
- // alpha_b=1.0-alpha_a;
- // R=(plsc->curcolor.r*alpha_a)+(R*alpha_b);
- // G=(plsc->curcolor.g*alpha_a)+(G*alpha_b);
- // B=(plsc->curcolor.b*alpha_a)+(B*alpha_b);
- //
- // This next bit of code is, I *think*, computationally
- // more efficient than the bit above. It results in
- // an indistinguishable plot, but file sizes are different
- // suggesting subtle variations doubtless caused by rounding
- // and/or floating point conversions. Questions are - which is
- // better ? Which is more "correct" ? Does it make a difference ?
- // Is one faster than the other so that you'd ever notice ?
- //
- R = (int) ( ( ( plsc->curcolor.r - R ) * alpha_a ) + R );
- G = (int) ( ( ( plsc->curcolor.g - G ) * alpha_a ) + G );
- B = (int) ( ( ( plsc->curcolor.b - B ) * alpha_a ) + B );
- FT->set_pixel( pls, x + k, y + i, RGB( R > 255 ? 255 : R, G > 255 ? 255 : G, B > 255 ? 255 : B ) );
- }
- }
- else // The old anti-aliasing technique
- {
- FT->col_idx = FT->ncol0_width - ( ( FT->ncol0_width * FT->shade ) / 255 );
- FT->last_icol0 = pls->icol0;
- plcol0( pls->icol0 + ( FT->col_idx * ( FT->ncol0_org - 1 ) ) );
- FT->pixel( pls, x + k, y + i );
- plcol0( FT->last_icol0 );
- }
- }
- }
- }
- }
- }
- //--------------------------------------------------------------------------
- // plD_FreeType_init()
- //
- // Allocates memory to Freetype structure
- // Initialises the freetype library.
- // Initialises freetype structure
- //--------------------------------------------------------------------------
- void plD_FreeType_init( PLStream *pls )
- {
- FT_Data *FT;
- char *a;
- // font paths and file names can be long so leave generous (1024) room
- char font_dir[PLPLOT_MAX_PATH];
- // N.B. must be in exactly same order as TrueTypeLookup
- const char *env_font_names[N_TrueTypeLookup] = {
- "PLPLOT_FREETYPE_SANS_FONT",
- "PLPLOT_FREETYPE_SERIF_FONT",
- "PLPLOT_FREETYPE_MONO_FONT",
- "PLPLOT_FREETYPE_SCRIPT_FONT",
- "PLPLOT_FREETYPE_SYMBOL_FONT",
- "PLPLOT_FREETYPE_SANS_ITALIC_FONT",
- "PLPLOT_FREETYPE_SERIF_ITALIC_FONT",
- "PLPLOT_FREETYPE_MONO_ITALIC_FONT",
- "PLPLOT_FREETYPE_SCRIPT_ITALIC_FONT",
- "PLPLOT_FREETYPE_SYMBOL_ITALIC_FONT",
- "PLPLOT_FREETYPE_SANS_OBLIQUE_FONT",
- "PLPLOT_FREETYPE_SERIF_OBLIQUE_FONT",
- "PLPLOT_FREETYPE_MONO_OBLIQUE_FONT",
- "PLPLOT_FREETYPE_SCRIPT_OBLIQUE_FONT",
- "PLPLOT_FREETYPE_SYMBOL_OBLIQUE_FONT",
- "PLPLOT_FREETYPE_SANS_BOLD_FONT",
- "PLPLOT_FREETYPE_SERIF_BOLD_FONT",
- "PLPLOT_FREETYPE_MONO_BOLD_FONT",
- "PLPLOT_FREETYPE_SCRIPT_BOLD_FONT",
- "PLPLOT_FREETYPE_SYMBOL_BOLD_FONT",
- "PLPLOT_FREETYPE_SANS_BOLD_ITALIC_FONT",
- "PLPLOT_FREETYPE_SERIF_BOLD_ITALIC_FONT",
- "PLPLOT_FREETYPE_MONO_BOLD_ITALIC_FONT",
- "PLPLOT_FREETYPE_SCRIPT_BOLD_ITALIC_FONT",
- "PLPLOT_FREETYPE_SYMBOL_BOLD_ITALIC_FONT",
- "PLPLOT_FREETYPE_SANS_BOLD_OBLIQUE_FONT",
- "PLPLOT_FREETYPE_SERIF_BOLD_OBLIQUE_FONT",
- "PLPLOT_FREETYPE_MONO_BOLD_OBLIQUE_FONT",
- "PLPLOT_FREETYPE_SCRIPT_BOLD_OBLIQUE_FONT",
- "PLPLOT_FREETYPE_SYMBOL_BOLD_OBLIQUE_FONT"
- };
- short i;
- #if defined ( MSDOS ) || defined ( WIN32 )
- static char *default_font_names[] = { "arial.ttf", "times.ttf", "timesi.ttf", "arial.ttf",
- "symbol.ttf" };
- char WINDIR_PATH[PLPLOT_MAX_PATH];
- char *b;
- b = getenv( "WINDIR" );
- strncpy( WINDIR_PATH, b, PLPLOT_MAX_PATH - 1 );
- WINDIR_PATH[PLPLOT_MAX_PATH - 1] = '\0';
- #else
- const char *default_unix_font_dir = PL_FREETYPE_FONT_DIR;
- #endif
- if ( pls->FT )
- {
- plwarn( "Freetype seems already to have been initialised!" );
- return;
- }
- if ( ( pls->FT = calloc( 1, (size_t) sizeof ( FT_Data ) ) ) == NULL )
- plexit( "Could not allocate memory for Freetype" );
- FT = (FT_Data *) pls->FT;
- if ( ( FT->textbuf = calloc( NTEXT_ALLOC, 1 ) ) == NULL )
- plexit( "Could not allocate memory for Freetype text buffer" );
- if ( FT_Init_FreeType( &FT->library ) )
- plexit( "Could not initialise Freetype library" );
- // set to an impossible value for an FCI
- FT->fci = PL_FCI_IMPOSSIBLE;
- #if defined ( MSDOS ) || defined ( WIN32 )
- //
- // Work out if we have Win95+ or Win3.?... sort of.
- // Actually, this just tries to find the place where the fonts live by looking
- // for arial, which should be on all windows machines.
- // At present, it only looks in two places, on one drive. I might change this
- // soon.
- //
- if ( WINDIR_PATH == NULL )
- {
- if ( access( "c:\\windows\\fonts\\arial.ttf", F_OK ) == 0 )
- {
- strcpy( font_dir, "c:/windows/fonts/" );
- }
- else if ( access( "c:\\windows\\system\\arial.ttf", F_OK ) == 0 )
- {
- strcpy( font_dir, "c:/windows/system/" );
- }
- else
- plwarn( "Could not find font path; I sure hope you have defined fonts manually !" );
- }
- else
- {
- strncat( WINDIR_PATH, "\\fonts\\arial.ttf", PLPLOT_MAX_PATH - 1 - strlen( WINDIR_PATH ) );
- if ( access( WINDIR_PATH, F_OK ) == 0 )
- {
- b = strrchr( WINDIR_PATH, '\\' );
- b++;
- *b = 0;
- makeunixslash( WINDIR_PATH );
- strcpy( font_dir, WINDIR_PATH );
- }
- else
- plwarn( "Could not find font path; I sure hope you have defined fonts manually !" );
- }
- if ( pls->debug )
- fprintf( stderr, "%s\n", font_dir );
- #else
- //
- // For Unix systems, we will set the font path up a little differently in
- // that the configured PL_FREETYPE_FONT_DIR has been set as the default path,
- // but the user can override this by setting the environmental variable
- // "PLPLOT_FREETYPE_FONT_DIR" to something else.
- // NOTE WELL - the trailing slash must be added for now !
- //
- if ( ( a = getenv( "PLPLOT_FREETYPE_FONT_DIR" ) ) != NULL )
- strncpy( font_dir, a, PLPLOT_MAX_PATH - 1 );
- else
- strncpy( font_dir, default_unix_font_dir, PLPLOT_MAX_PATH - 1 );
- font_dir[PLPLOT_MAX_PATH - 1] = '\0';
- #endif
- //
- // The driver looks for N_TrueTypeLookup environmental variables
- // where the path and name of these fonts can be OPTIONALLY set,
- // overriding the configured default values.
- //
- for ( i = 0; i < N_TrueTypeLookup; i++ )
- {
- if ( ( a = getenv( env_font_names[i] ) ) != NULL )
- {
- //
- // Work out if we have been given an absolute path to a font name, or just
- // a font name sans-path. To do this we will look for a directory separator
- // character, which means some system specific junk. DJGPP is all wise, and
- // understands both Unix and DOS conventions. DOS only knows DOS, and
- // I assume everything else knows Unix-speak. (Why Bill, didn't you just
- // pay the extra 15c and get a REAL separator???)
- //
- #ifdef MSDOS
- if ( a[1] == ':' ) // check for MS-DOS absolute path
- #else
- if ( ( a[0] == '/' ) || ( a[0] == '~' ) ) // check for unix abs path
- #endif
- strncpy( FT->font_name[i], a, PLPLOT_MAX_PATH - 1 );
- else
- {
- strncpy( FT->font_name[i], font_dir, PLPLOT_MAX_PATH - 1 );
- strncat( FT->font_name[i], a, PLPLOT_MAX_PATH - 1 - strlen( FT->font_name[i] ) );
- }
- }
- else
- {
- strncpy( FT->font_name[i], font_dir, PLPLOT_MAX_PATH - 1 );
- strncat( FT->font_name[i], (const char *) TrueTypeLookup[i].pfont, PLPLOT_MAX_PATH - 1 - strlen( FT->font_name[i] ) );
- }
- FT->font_name[i][PLPLOT_MAX_PATH - 1] = '\0';
- {
- FILE *infile;
- if ( ( infile = fopen( FT->font_name[i], "r" ) ) == NULL )
- {
- char msgbuf[1024];
- snprintf( msgbuf, 1024,
- "plD_FreeType_init: Could not find the freetype compatible font:\n %s",
- FT->font_name[i] );
- plwarn( msgbuf );
- }
- else
- {
- fclose( infile );
- }
- }
- FontLookup[i].fci = TrueTypeLookup[i].fci;
- FontLookup[i].pfont = (unsigned char *) FT->font_name[i];
- }
- //
- // Next, we check to see if -drvopt has been used on the command line to
- // over-ride any settings
- //
- }
- //--------------------------------------------------------------------------
- // FT_SetFace( PLStream *pls, PLUNICODE fci )
- //
- // Sets up the font face and size
- //--------------------------------------------------------------------------
- void FT_SetFace( PLStream *pls, PLUNICODE fci )
- {
- FT_Data *FT = (FT_Data *) pls->FT;
- double font_size = pls->chrht * 72 / 25.4; // font_size in points, chrht is in mm
- // save a copy of character height and resolution
- FT->chrht = pls->chrht;
- FT->xdpi = pls->xdpi;
- FT->ydpi = pls->ydpi;
- if ( fci != FT->fci )
- {
- const char *font_name = plP_FCI2FontName( fci, FontLookup, N_TrueTypeLookup );
- if ( font_name == NULL )
- {
- if ( FT->fci == PL_FCI_IMPOSSIBLE )
- plexit( "FT_SetFace: Bad FCI and no previous valid font to fall back on" );
- else
- plwarn( "FT_SetFace: Bad FCI. Falling back to previous font." );
- }
- else
- {
- FT->fci = fci;
- if ( FT->face != NULL )
- {
- FT_Done_Face( FT->face );
- FT->face = NULL;
- }
- if ( FT->face == NULL )
- {
- if ( FT_New_Face( FT->library, font_name, 0, &FT->face ) )
- plexit( "FT_SetFace: Error loading a font in freetype" );
- }
- }
- }
- FT_Set_Char_Size( FT->face, 0,
- (FT_F26Dot6) ( font_size * 64 / TEXT_SCALING_FACTOR ), (FT_UInt) pls->xdpi,
- (FT_UInt) pls->ydpi );
- }
- //--------------------------------------------------------------------------
- // plD_render_freetype_text()
- //
- // Transforms the font
- // calculates real-world bitmap coordinates from plplot ones
- // renders text using freetype
- //--------------------------------------------------------------------------
- void plD_render_freetype_text( PLStream *pls, EscText *args )
- {
- FT_Data *FT = (FT_Data *) pls->FT;
- int x, y;
- int w = 0, h = 0;
- PLFLT *t = args->xform;
- FT_Matrix matrix;
- PLFLT angle = PI * pls->diorot / 2;
- //
- // Used later in a commented out section (See Rotate The Page), if that
- // section will never be used again, remove these as well.
- // PLINT clxmin, clxmax, clymin, clymax;
- //
- PLFLT Sin_A, Cos_A;
- FT_Vector adjust;
- PLUNICODE fci;
- FT_Fixed height;
- PLFLT height_factor;
- if ( ( args->string != NULL ) || ( args->unicode_array_len > 0 ) )
- {
- //
- // Work out if either the font size, the font face or the
- // resolution has changed.
- // If either has, then we will reload the font face.
- //
- plgfci( &fci );
- if ( ( FT->fci != fci ) || ( FT->chrht != pls->chrht ) || ( FT->xdpi != pls->xdpi ) || ( FT->ydpi != pls->ydpi ) )
- FT_SetFace( pls, fci );
- // this will help work out underlining and overlining
- Debug6( "%s %d %d %d %d\n", "plD_render_freetype_text:",
- FT->face->underline_position >> 6,
- FT->face->descender >> 6,
- FT->face->ascender >> 6,
- ( ( FT->face->underline_position * -1 ) + FT->face->ascender ) >> 6 );
- //
- // Now we work out how long the text is (for justification etc...) and how
- // high the text is. This is done on UN-TRANSFORMED text, since we will
- // apply our own transformations on it later, so it's necessary for us
- // to to turn all transformations off first, before calling the function
- // that calculates the text size.
- //
- FT->matrix.xx = 0x10000;
- FT->matrix.xy = 0x00000;
- FT->matrix.yx = 0x00000;
- FT->matrix.yy = 0x10000;
- FT_Vector_Transform( &FT->pos, &FT->matrix );
- FT_Set_Transform( FT->face, &FT->matrix, &FT->pos );
- FT_StrX_YW( pls, args->unicode_array, (short) args->unicode_array_len, &w, &h );
- //
- // Set up the transformation Matrix
- //
- // Fortunately this is almost identical to plplot's own transformation matrix;
- // you have NO idea how much effort that saves ! Some params are in a
- // different order, and Freetype wants integers whereas plplot likes floats,
- // but such differences are quite trivial.
- //
- // For some odd reason, this needs to be set a different way for DJGPP. Why ?
- // I wish I knew.
- //
- // (RL, on 2005-01-21) The height_factor variable is introduced below.
- // It is used here and farther below when computing the vertical
- // adjustment. The rationale for its introduction is as follow: up to
- // now, the text produced with Hershey fonts was systematically taller
- // than the same text produced with TT fonts, and tha by a factor of
- // around 1.125 (I discovered this empirically). This corresponds
- // roughly to the ratio between total height and the ascender of some
- // TT faces. Hence the computation below. Remember that descender is
- // always a negative quantity.
- //
- height_factor = (PLFLT) ( FT->face->ascender - FT->face->descender )
- / FT->face->ascender;
- height = (FT_Fixed) ( 0x10000 * height_factor );
- #ifdef DJGPP
- FT->matrix.xx = (FT_Fixed) ( (PLFLT) height * t[0] );
- FT->matrix.xy = (FT_Fixed) ( (PLFLT) height * t[2] );
- FT->matrix.yx = (FT_Fixed) ( (PLFLT) height * t[1] );
- FT->matrix.yy = (FT_Fixed) ( (PLFLT) height * t[3] );
- #else
- FT->matrix.xx = (FT_Fixed) ( (PLFLT) height * t[0] );
- FT->matrix.xy = (FT_Fixed) ( (PLFLT) height * t[1] );
- FT->matrix.yx = (FT_Fixed) ( (PLFLT) height * t[2] );
- FT->matrix.yy = (FT_Fixed) ( (PLFLT) height * t[3] );
- #endif
- // Rotate the Font
- //
- // If the page has been rotated using -ori, this is where we rotate the
- // font to point in the right direction. To make things nice and easy, we
- // will use freetypes matrix math stuff to do this for us.
- //
- Cos_A = cos( angle );
- Sin_A = sin( angle );
- matrix.xx = (FT_Fixed) ( (PLFLT) 0x10000 * Cos_A );
- #ifdef DJGPP
- matrix.xy = (FT_Fixed) ( (PLFLT) 0x10000 * Sin_A * -1.0 );
- matrix.yx = (FT_Fixed) ( (PLFLT) 0x10000 * Sin_A );
- #else
- matrix.xy = (FT_Fixed) ( (PLFLT) 0x10000 * Sin_A );
- matrix.yx = (FT_Fixed) ( (PLFLT) 0x10000 * Sin_A * -1.0 );
- #endif
- matrix.yy = (FT_Fixed) ( (PLFLT) 0x10000 * Cos_A );
- FT_Matrix_Multiply( &matrix, &FT->matrix );
- // Calculate a Vector from the matrix
- //
- // This is closely related to the "transform matrix".
- // The matrix is used for rendering the glyph, while the vector is used for
- // calculating offsets of the text box, so we need both. Why ? I dunno, but
- // we have to live with it, and it works...
- //
- FT_Vector_Transform( &FT->pos, &FT->matrix );
- // Transform the font face
- //
- // This is where our matrix transformation is calculated for the font face.
- // This is only done once for each unique transformation since it is "sticky"
- // within the font. Font rendering is done later, using the supplied matrix,
- // but invisibly to us from here on. I don't believe the vector is used, but
- // it is asked for.
- //
- FT_Set_Transform( FT->face, &FT->matrix, &FT->pos );
- // Rotate the Page
- //
- // If the page has been rotated using -ori, this is we recalculate the
- // reference point for the text using plplot functions.
- //
- // difilt(&args->x, &args->y, 1, &clxmin, &clxmax, &clymin, &clymax);
- //
- // Convert into normal coordinates from virtual coordinates
- //
- if ( FT->scale != 0.0 ) // scale was set
- {
- x = (int) ( args->x / FT->scale );
- if ( FT->invert_y == 1 )
- y = (int) ( FT->ymax - ( args->y / FT->scale ) );
- else
- y = (int) ( args->y / FT->scale );
- }
- else
- {
- x = (int) ( args->x / FT->scalex );
- if ( FT->invert_y == 1 )
- y = (int) ( FT->ymax - ( args->y / FT->scaley ) );
- else
- y = (int) ( args->y / FT->scaley );
- }
- // Adjust for the justification and character height
- //
- // Eeeksss... this wasn't a nice bit of code to work out, let me tell you.
- // I could not work out an entirely satisfactory solution that made
- // logical sense, so came up with an "illogical" one as well.
- // The logical one works fine for text in the normal "portrait"
- // orientation, and does so for reasons you might expect it to work; But
- // for all other orientations, the text's base line is either a little
- // high, or a little low. This is because of the way the base-line pos
- // is calculated from the decender height. The "dodgie" way of calculating
- // the position is to use the character height here, then adjust for the
- // decender height by a three-fold factor later on. That approach seems to
- // work a little better for rotated pages, but why it should be so, I
- // don't understand. You can compile in or out which way you want it by
- // defining "DODGIE_DECENDER_HACK".
- //
- // note: the logic of the page rotation coming up next is that we pump in
- // the justification factor and then use freetype to rotate and transform
- // the values, which we then use to change the plotting location.
- //
- #ifdef DODGIE_DECENDER_HACK
- adjust.y = h;
- #else
- adjust.y = 0;
- #endif
- // (RL, on 2005-01-24) The code below uses floating point and division
- // operations instead of integer shift used before. This is slower but
- // gives accurate placement of text in plots.
- //
- // (RL, on 2005-01-21) The hack below is intended to align single
- // glyphs being generated via plpoin. The way to detect this
- // situation is completely hackish, I must admit, by checking whether the
- // length of the Unicode array is equal 2 and whether the first
- // character is actually a font-changing command to font number 4 (for
- // symbols). This is ugly because it depends on definitions set
- // elsewhere, but it works.
- //
- // The computation of the vertical and horizontal adjustments are
- // based on the bouding box of the glyph being loaded (since there is
- // only one glyph in the string in this case, we are okay here).
- //
- if ( ( args->unicode_array_len == 2 )
- && ( args->unicode_array[0] == ( PL_FCI_MARK | 0x004 ) ) )
- {
- adjust.x = (FT_Pos) ( args->just * ROUND( (PLFLT) FT->face->glyph->metrics.width / 64.0 ) );
- adjust.y = (FT_Pos) ROUND( (PLFLT) FT->face->glyph->metrics.height / 128.0 );
- }
- else
- {
- // (RL, on 2005-01-21) The vertical adjustment is set below, making
- // the DODGIE conditional moot. I use the value of h as return by FT_StrX_YW,
- // which should correspond to the total height of the text being
- // drawn. Freetype aligns text around the baseline, while PLplot
- // aligns to the center of the ascender portion. We must then adjust
- // by half of the ascender and this is why there is a division by
- // height_factor below.
- //
- adjust.y = (FT_Pos)
- ROUND( (PLFLT) FT->face->size->metrics.height / height_factor / 128.0 );
- adjust.x = (FT_Pos) ( args->just * ROUND( w / 64.0 ) );
- }
- FT_Vector_Transform( &adjust, &FT->matrix ); // was /&matrix); - was I using the wrong matrix all this time ?
- x -= (int) adjust.x;
- y += (int) adjust.y;
- FT_WriteStrW( pls, args->unicode_array, (short) args->unicode_array_len, x, y ); // write it out
- }
- else
- {
- plD_render_freetype_sym( pls, args );
- }
- }
- //--------------------------------------------------------------------------
- // plD_FreeType_Destroy()
- //
- // Restores cmap0 if it had been modifed for anti-aliasing
- // closes the freetype library.
- // Deallocates memory to the Freetype structure
- //--------------------------------------------------------------------------
- void plD_FreeType_Destroy( PLStream *pls )
- {
- FT_Data *FT = (FT_Data *) pls->FT;
- //extern int FT_Done_Library( FT_Library library );
- if ( FT )
- {
- if ( ( FT->smooth_text == 1 ) && ( FT->BLENDED_ANTIALIASING == 0 ) )
- plscmap0n( FT->ncol0_org );
- if ( FT->textbuf )
- free( FT->textbuf );
- FT_Done_Library( FT->library );
- free( pls->FT );
- pls->FT = NULL;
- }
- }
- //--------------------------------------------------------------------------
- // PLFLT CalculateIncrement( int bg, int fg, int levels)
- //
- // Takes the value of the foreground, and the background, and when
- // given the number of desired steps, calculates how much to incriment
- // a value to transition from fg to bg.
- // This function only does it for one colour channel at a time.
- //--------------------------------------------------------------------------
- static PLFLT CalculateIncrement( int bg, int fg, int levels )
- {
- PLFLT ret = 0;
- if ( levels > 1 )
- {
- if ( fg > bg )
- ret = ( ( fg + 1 ) - bg ) / levels;
- else if ( fg < bg )
- ret = ( ( ( fg - 1 ) - bg ) / levels );
- }
- return ( ret );
- }
- //--------------------------------------------------------------------------
- // void pl_set_extended_cmap0(PLStream *pls, int ncol0_width, int ncol0_org)
- //
- // ncol0_width - how many greyscale levels to accolate to each CMAP0 entry
- // ncol0_org - the originl number of CMAP0 entries.
- //
- // This function calcualtes and sets an extended CMAP0 entry for the
- // driver. It is assumed that the caller has checked to make sure there is
- // room for extending CMAP0 already.
- //
- // NOTES
- // We don't bother calculating an entry for CMAP[0], the background.
- // It is assumed the caller has already expanded the size of CMAP[0]
- //--------------------------------------------------------------------------
- void pl_set_extended_cmap0( PLStream *pls, int ncol0_width, int ncol0_org )
- {
- int i, j, k;
- int r, g, b;
- PLFLT r_inc, g_inc, b_inc;
- for ( i = 1; i < ncol0_org; i++ )
- {
- r = pls->cmap0[i].r;
- g = pls->cmap0[i].g;
- b = pls->cmap0[i].b;
- r_inc = CalculateIncrement( pls->cmap0[0].r, r, ncol0_width );
- g_inc = CalculateIncrement( pls->cmap0[0].g, g, ncol0_width );
- b_inc = CalculateIncrement( pls->cmap0[0].b, b, ncol0_width );
- for ( j = 0, k = ncol0_org + i - 1; j < ncol0_width; j++, k += ( ncol0_org - 1 ) )
- {
- r -= (int) r_inc;
- g -= (int) g_inc;
- b -= (int) b_inc;
- if ( ( r < 0 ) || ( g < 0 ) || ( b < 0 ) )
- plscol0( k, 0, 0, 0 );
- else
- plscol0( k, ( r > 0xff ? 0xff : r ), ( g > 0xff ? 0xff : g ), ( b > 0xff ? 0xff : b ) );
- }
- }
- }
- //--------------------------------------------------------------------------
- // plD_render_freetype_sym( PLStream *pls, EscText *args )
- // PLStream *pls - pointer to plot stream
- // EscText *args - pointer to standard "string" object.
- //
- // This function is a simple rendering function which draws a single
- // character at a time. The function is an alternative to the text
- // functions which are considerably, and needlessly, more complicated
- // than what we need here.
- //--------------------------------------------------------------------------
- void plD_render_freetype_sym( PLStream *pls, EscText *args )
- {
- FT_Data *FT = (FT_Data *) pls->FT;
- int x, y;
- FT_Vector adjust;
- PLUNICODE fci;
- if ( FT->scale != 0.0 ) // scale was set
- {
- x = (int) ( args->x / FT->scale );
- if ( FT->invert_y == 1 )
- y = (int) ( FT->ymax - ( args->y / FT->scale ) );
- else
- y = (int) ( args->y / FT->scale );
- }
- else
- {
- x = (int) ( args->x / FT->scalex );
- if ( FT->invert_y == 1 )
- y = (int) ( FT->ymax - ( args->y / FT->scaley ) );
- else
- y = (int) ( args->y / FT->scaley );
- }
- //
- // Adjust for the descender - make sure the font is nice and centred
- // vertically. Freetype assumes we have a base-line, but plplot thinks of
- // centre-lines, so that's why we have to do this. Since this is one of our
- // own adjustments, rather than a freetype one, we have to run it through
- // the transform matrix manually.
- //
- // For some odd reason, this works best if we triple the
- // descender's height and then adjust the height later on...
- // Don't ask me why, 'cause I don't know. But it does seem to work.
- //
- // I really wish I knew *why* it worked better though...
- //
- // y-=FT->face->descender >> 6;
- //
- #ifdef DODGIE_DECENDER_HACK
- adjust.y = ( FT->face->descender >> 6 ) * 3;
- #else
- adjust.y = ( FT->face->descender >> 6 );
- #endif
- adjust.x = 0;
- FT_Vector_Transform( &adjust, &FT->matrix );
- x += (int) adjust.x;
- y -= (int) adjust.y;
- plgfci( &fci );
- FT_SetFace( pls, fci );
- FT = (FT_Data *) pls->FT;
- FT_Set_Transform( FT->face, &FT->matrix, &FT->pos );
- FT_Load_Char( FT->face, args->unicode_char, ( FT->smooth_text == 0 ) ? FT_LOAD_MONOCHROME + FT_LOAD_RENDER : FT_LOAD_RENDER | FT_LOAD_FORCE_AUTOHINT );
- //
- // Now we have to try and componsate for the fact that the freetype glyphs are left
- // justified, and plplot's glyphs are centred. To do this, we will just work out the
- // advancment, halve it, and take it away from the x position. This wont be 100%
- // accurate because "spacing" is factored into the right hand side of the glyph,
- // but it is as good a way as I can think of.
- //
- x -= (int) ( ( FT->face->glyph->advance.x >> 6 ) / 2 );
- FT_PlotChar( pls, FT, FT->face->glyph, x, y ); // render the text
- }
- #else
- int
- plfreetype()
- {
- return 0;
- }
- #endif