PLplot: plfreetype.c Source File

PLplot 5.10.0

- Main Page
- Namespaces
- Classes
- Files

- File List
- File Members

All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Macros
plfreetype.c
Go to the documentation of this file.
1 // $Id: plfreetype.c 12661 2013-11-05 16:24:11Z airwin $
2 //
3 // Copyright (C) 2002, 2004, 2005 Andrew Roach
4 // Copyright (C) 2002 Maurice LeBrun
5 // Copyright (C) 2002-2012 Alan W. Irwin
6 // Copyright (C) 2003, 2004 Joao Cardoso
7 // Copyright (C) 2003, 2004, 2005 Rafael Laboissiere
8 // Copyright (C) 2004 Andrew Ross
9 //
10 // This file is part of PLplot.
11 //
12 // PLplot is free software; you can redistribute it and/or modify
13 // it under the terms of the GNU Library General Public License as published
14 // by the Free Software Foundation; either version 2 of the License, or
15 // (at your option) any later version.
16 //
17 // PLplot is distributed in the hope that it will be useful,
18 // but WITHOUT ANY WARRANTY; without even the implied warranty of
19 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
20 // GNU Library General Public License for more details.
21 //
22 // You should have received a copy of the GNU Library General Public License
23 // along with PLplot; if not, write to the Free Software
24 // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
25 //
26 //
27 // Support routines for freetype font engine
28 //
29 // This file contains a series of support routines for drivers interested
30 // in using freetype rendered fonts instead of plplot plotter fonts.
31 // Freetype supports a gerth of font formats including TrueType, OpenType,
32 // Adobe Type1, Type42 etc... the list seems almost endless. Any bitmap
33 // driver should be able to use any of these freetype fonts from plplot if
34 // these routines are properly initialised.
35 //
36 // Freetype support is not intended to be a "feature" of the common API,
37 // but is implemented as a driver-specific optional extra invoked via the
38 // -drvopt command line toggle. It is intended to be used in the context of
39 // "PLESC_HAS_TEXT" for any bitmap drivers without native font support.
40 // Implementing freetype in this manner minimise changes to the overall
41 // API. Because of this approach, there is not a "wealth" of font options
42 // available to the programmer. You can not do anything you can't do for a
43 // normal freetype plotter font like boldface. You can do most of the
44 // things that you can do with a plotter font however, like greek
45 // characters superscripting, and selecting one of the four "pre-defined"
46 // plplot font types. At present underlining and overlining are not
47 // supported.
48 //
49 // To give the user some level of control over the fonts that are used,
50 // environmental variables can be set to over-ride the definitions used by
51 // the five default plplot fonts.
52 //
53 // The exact syntax for evoking freetype fonts is dependant on each
54 // driver, but for the GD and GNUSVGA drivers I have followed the syntax of
55 // the PS driver and use the command-line switch of "-drvopt text" to
56 // activate the feature, and suggest other programmers do the same for
57 // commonality.
58 //
59 // Both anti-aliased and monochrome font rendering is supported by these
60 // routines. How these are evoked depends on the programmer, but with the
61 // GD and GNUSVGA driver families I have used the command-line switch
62 // "-drvopt smooth" to activate the feature; but, considering you also need
63 // to turn freetype on, it would probably really be more like "-drvopt
64 // text,smooth".
65 //
66 //
67
68 #if !defined ( WIN32 ) || defined ( __GNUC__ )
69 #include <unistd.h>
70 #else
71 #define F_OK 1
72 #include <stdio.h>
73 int access( char *filename, int flag )
74 {
75 FILE *infile;
76 infile = fopen( filename, "r" );
77 if ( infile != NULL )
78 {
79 fclose( infile );
80 return 0;
81 }
82 else
83 {
84 return 1;
85 }
86 }
87 #endif
88
89 #define makeunixslash( b ) do { char *I; for ( I = b; *I != 0; *I++ ) if ( *I == '\\' ) *I = '/';} while ( 0 )
90
91 #include "plDevs.h"
92 #include "plplotP.h"
93 #include "drivers.h"
94 #ifdef PL_HAVE_FREETYPE
95 #include "plfreetype.h"
96 #include "plfci-truetype.h"
97
98 #define FT_Data _FT_Data_
99
100 // Font lookup table that is constructed in plD_FreeType_init
101 PLDLLIMPEXP_DATA( FCI_to_FontName_Table ) FontLookup[N_TrueTypeLookup];
102 // TOP LEVEL DEFINES
103
104 // Freetype lets you set the text size absolutely. It also takes into
105 // account the DPI when doing so. So does plplot. Why, then, is it that the
106 // size of the text drawn by plplot is bigger than the text drawn by
107 // freetype when given IDENTICAL parameters ? Perhaps I am missing
108 // something somewhere, but to fix this up we use TEXT_SCALING_FACTOR to
109 // set a scaling factor to try and square things up a bit.
110 //
111
112 #define TEXT_SCALING_FACTOR .7
113
114 // default size of temporary text buffer
115 // If we wanted to be fancy we could add sizing, but this should be big enough
116
117 #define NTEXT_ALLOC 1024
118
119 //--------------------------------------------------------------------------
120 // Some debugging macros
121 //--------------------------------------------------------------------------
122
123 #define Debug6( a, b, c, d, e, f ) do { if ( pls->debug ) { fprintf( stderr, a, b, c, d, e, f ); } } while ( 0 )
124
125
126 // FUNCTION PROTOTYPES
127
128 // Public prototypes, generally available to the API
129
130 void plD_FreeType_init( PLStream *pls );
131 void plD_render_freetype_text( PLStream *pls, EscText *args );
132 void plD_FreeType_Destroy( PLStream *pls );
133 void pl_set_extended_cmap0( PLStream *pls, int ncol0_width, int ncol0_org );
134 void pl_RemakeFreeType_text_from_buffer( PLStream *pls );
135 void plD_render_freetype_sym( PLStream *pls, EscText *args );
136
137 // Private prototypes for use in this file only
138
139 static void FT_PlotChar( PLStream *pls, FT_Data *FT, FT_GlyphSlot slot, int x, int y );
140 static void FT_SetFace( PLStream *pls, PLUNICODE fci );
141 static PLFLT CalculateIncrement( int bg, int fg, int levels );
142
143 // These are never defined, maybe they will be used in the future?
144 //
145 // static void pl_save_FreeType_text_to_buffer (PLStream *pls, EscText *args);
146 // static FT_ULong hershey_to_unicode (char in);
147 //
148 //
149
150 static void FT_WriteStrW( PLStream *pls, const PLUNICODE *text, short len, int x, int y );
151 static void FT_StrX_YW( PLStream *pls, const PLUNICODE *text, short len, int *xx, int *yy, int *overyy, int *underyy );
152
153 //--------------------------------------------------------------------------
154 // FT_StrX_YW()
155 //
156 // Returns the dimensions of the text box. It does this by fully parsing
157 // the supplied text through the rendering engine. It does everything
158 // but draw the text. This seems, to me, the easiest and most accurate
159 // way of determining the text's dimensions. If/when caching is added,
160 // the CPU hit for this "double processing" will be minimal.
161 //--------------------------------------------------------------------------
162
163 void
164 FT_StrX_YW( PLStream *pls, const PLUNICODE *text, short len, int *xx, int *yy, int *overyy, int *underyy )
165 {
166 FT_Data *FT = (FT_Data *) pls->FT;
167 short i = 0;
168 FT_Vector akerning, adjust;
169 int x = 0, y = 0, startingy;
170 char esc;
171
172 plgesc( &esc );
173
174 //
175 // Things seems to work better with this line than without it;
176 // I guess because there is no vertical kerning or advancement for most
177 // non-transformed fonts, so we need to define *something* for the y height,
178 // and this is the best thing I could think of.
179 //
180
181 y -= (int) FT->face->size->metrics.height;
182 startingy = y;
183 *yy = y; //note height is negative!
184 *overyy = 0;
185 *underyy = 0;
186 adjust.x = 0;
187 adjust.y = 0;
188
189 // walk through the text character by character
190 for ( i = 0; i < len; i++ )
191 {
192 if ( ( text[i] == (PLUNICODE) esc ) && ( text[i - 1] != (PLUNICODE) esc ) )
193 {
194 if ( text[i + 1] == (PLUNICODE) esc )
195 continue;
196
197 switch ( text[i + 1] )
198 {
199 case 'u': // super script
200 case 'U': // super script
201 adjust.y = FT->face->size->metrics.height / 2;
202 adjust.x = 0;
203 FT_Vector_Transform( &adjust, &FT->matrix );
204 x += (int) adjust.x;
205 y -= (int) adjust.y;
206 //calculate excess height from superscripts, this will need changing if scale of sub/superscripts changes
207 *overyy = y - startingy < *overyy ? y - startingy : *overyy;
208 i++;
209 break;
210
211 case 'd': // subscript
212 case 'D': // subscript
213 adjust.y = -FT->face->size->metrics.height / 2;
214 adjust.x = 0;
215 FT_Vector_Transform( &adjust, &FT->matrix );
216 x += (int) adjust.x;
217 y -= (int) adjust.y;
218 //calculate excess depth from subscripts, this will need changing if scale of sub/superscripts changes
219 *underyy = startingy - y < *underyy ? startingy - y : *underyy;
220 i++;
221 break;
222 }
223 }
224 else if ( text[i] & PL_FCI_MARK )
225 {
226 // FCI in text stream; change font accordingly.
227 FT_SetFace( pls, text[i] );
228 *yy = (int) ( FT->face->size->metrics.height > -*yy ? -FT->face->size->metrics.height : *yy );
229 }
230 else
231 {
232 // see if we have kerning for the particular character pair
233 if ( ( i > 0 ) && FT_HAS_KERNING( FT->face ) )
234 {
235 FT_Get_Kerning( FT->face,
236 text[i - 1],
237 text[i],
238 ft_kerning_default,
239 &akerning );
240 x += (int) ( akerning.x >> 6 ); // add (or subtract) the kerning
241 }
242
243 //
244 // Next we load the char. This also draws the char, transforms it, and
245 // converts it to a bitmap. At present this is a bit wasteful, but
246 // if/when I add cache support, then this data won't go to waste.
247 // Since there is no sense in going to the trouble of doing anti-aliasing
248 // calculations since we aren't REALLY plotting anything, we will render
249 // this as monochrome since it is probably marginally quicker. If/when
250 // cache support is added, naturally this will have to change.
251 //
252
253 FT_Load_Char( FT->face, text[i], FT_LOAD_MONOCHROME + FT_LOAD_RENDER );
254
255 //
256 // Add in the "advancement" needed to position the cursor for the next
257 // character. Unless the text is transformed, "y" will always be zero.
258 // Y is negative because freetype does things upside down
259 //
260
261 x += (int) ( FT->face->glyph->advance.x );
262 y -= (int) ( FT->face->glyph->advance.y );
263 }
264 }
265
266 //
267 // Convert from unit of 1/64 of a pixel to pixels, and do it real fast with
268 // a bitwise shift (mind you, any decent compiler SHOULD optimise /64 this way
269 // anyway...)
270 //
271
272 // (RL, on 2005-01-23) Removed the shift bellow to avoid truncation errors
273 // later.
274 //yy=y>> 6;
275 //xx=x>> 6;
276 //
277 *xx = x;
278 }
279
280 //--------------------------------------------------------------------------
281 // FT_WriteStrW()
282 //
283 // Writes a string of FT text at the current cursor location.
284 // most of the code here is identical to "FT_StrX_Y" and I will probably
285 // collapse the two into some more efficient code eventually.
286 //--------------------------------------------------------------------------
287
288 void
289 FT_WriteStrW( PLStream *pls, const PLUNICODE *text, short len, int x, int y )
290 {
291 FT_Data *FT = (FT_Data *) pls->FT;
292 short i = 0, last_char = -1;
293 FT_Vector akerning, adjust;
294 char esc;
295
296 plgesc( &esc );
297
298
299 //
300 // Adjust for the descender - make sure the font is nice and centred
301 // vertically. Freetype assumes we have a base-line, but plplot thinks of
302 // centre-lines, so that's why we have to do this. Since this is one of our
303 // own adjustments, rather than a freetype one, we have to run it through
304 // the transform matrix manually.
305 //
306 // For some odd reason, this works best if we triple the
307 // descender's height and then adjust the height later on...
308 // Don't ask me why, 'cause I don't know. But it does seem to work.
309 //
310 // I really wish I knew *why* it worked better though...
311 //
312 // y-=FT->face->descender >> 6;
313 //
314
315 #ifdef DODGIE_DECENDER_HACK
316 adjust.y = ( FT->face->descender >> 6 ) * 3;
317 #else
318 adjust.y = ( FT->face->descender >> 6 );
319 #endif
320
321 // (RL) adjust.y is zeroed below,, making the code above (around
322 // DODGIE_DECENDER_HACK) completely useless. This is necessary for
323 // getting the vertical alignment of text right, which is coped with
324 // in function plD_render_freetype_text now.
325 //
326
327 adjust.x = 0;
328 adjust.y = 0;
329 FT_Vector_Transform( &adjust, &FT->matrix );
330 x += (int) adjust.x;
331 y -= (int) adjust.y;
332
333 // (RL, on 2005-01-25) The computation of cumulated glyph width within
334 // the text is done now with full precision, using 26.6 Freetype
335 // arithmetics. We should then shift the x and y variables by 6 bits,
336 // as below. Inside the character for loop, all operations regarding
337 // x and y will be done in 26.6 mode and these variables will be
338 // converted to integers when passed to FT_PlotChar. Notrice that we
339 // are using ROUND and float division instead of ">> 6" now. This
340 // minimizes truncation errors.
341 //
342
343 x <<= 6;
344 y <<= 6;
345
346 // walk through the text character by character
347
348 for ( i = 0; i < len; i++ )
349 {
350 if ( ( text[i] == (PLUNICODE) esc ) && ( text[i - 1] != (PLUNICODE) esc ) )
351 {
352 if ( text[i + 1] == (PLUNICODE) esc )
353 continue;
354
355 switch ( text[i + 1] )
356 {
357 //
358 // We run the OFFSET for the super-script and sub-script through the
359 // transformation matrix so we can calculate nice and easy the required
360 // offset no matter what's happened rotation wise. Everything else, like
361 // kerning and advancing from character to character is transformed
362 // automatically by freetype, but since the superscript/subscript is a
363 // feature of plplot, and not freetype, we have to make allowances.
364 //
365
366 case 'u': // super script
367 case 'U': // super script
368 adjust.y = FT->face->size->metrics.height / 2;
369 adjust.x = 0;
370 FT_Vector_Transform( &adjust, &FT->matrix );
371 x += (int) adjust.x;
372 y -= (int) adjust.y;
373 i++;
374 break;
375
376 case 'd': // subscript
377 case 'D': // subscript
378 adjust.y = -FT->face->size->metrics.height / 2;
379 adjust.x = 0;
380 FT_Vector_Transform( &adjust, &FT->matrix );
381 x += (int) adjust.x;
382 y -= (int) adjust.y;
383 i++;
384 break;
385 }
386 }
387 else if ( text[i] & PL_FCI_MARK )
388 {
389 // FCI in text stream; change font accordingly.
390 FT_SetFace( pls, text[i] );
391 FT = (FT_Data *) pls->FT;
392 FT_Set_Transform( FT->face, &FT->matrix, &FT->pos );
393 }
394 else
395 {
396 // see if we have kerning for the particular character pair
397 if ( ( last_char != -1 ) && ( i > 0 ) && FT_HAS_KERNING( FT->face ) )
398 {
399 FT_Get_Kerning( FT->face,
400 text[last_char],
401 text[i],
402 ft_kerning_default, &akerning );
403 x += (int) akerning.x; // add (or subtract) the kerning
404 y -= (int) akerning.y; // Do I need this in case of rotation ?
405 }
406
407
408 FT_Load_Char( FT->face, text[i], ( FT->smooth_text == 0 ) ? FT_LOAD_MONOCHROME + FT_LOAD_RENDER : FT_LOAD_RENDER | FT_LOAD_FORCE_AUTOHINT );
409 FT_PlotChar( pls, FT, FT->face->glyph,
410 ROUND( x / 64.0 ), ROUND( y / 64.0 ) ); // render the text
411
412 x += (int) FT->face->glyph->advance.x;
413 y -= (int) FT->face->glyph->advance.y;
414
415 last_char = i;
416 }
417 } // end for
418 }
419
420 //--------------------------------------------------------------------------
421 // FT_PlotChar()
422 //
423 // Plots an individual character. I know some of this stuff, like colour
424 // could be parsed from plstream, but it was just quicker this way.
425 //--------------------------------------------------------------------------
426
427 void
428 FT_PlotChar( PLStream *pls, FT_Data *FT, FT_GlyphSlot slot,
429 int x, int y )
430 {
431 unsigned char bittest;
432 short i, k, j;
433 int n = slot->bitmap.pitch;
434 int current_pixel_colour;
435 int R, G, B;
436 PLFLT alpha_a;
437 //PLFLT alpha_b;
438 int xx;
439 short imin, imax, kmin, kmax;
440
441 // Corners of the clipping rectangle
442 PLINT clipxmin, clipymin, clipxmax, clipymax, tmp;
443 PLINT clpxmi, clpxma, clpymi, clpyma;
444
445 // Convert clipping box into normal coordinates
446 clipxmin = pls->clpxmi;
447 clipxmax = pls->clpxma;
448 clipymin = pls->clpymi;
449 clipymax = pls->clpyma;
450
451 if ( plsc->difilt )
452 {
453 difilt( &clipxmin, &clipymin, 1, &clpxmi, &clpxma, &clpymi, &clpyma );
454 difilt( &clipxmax, &clipymax, 1, &clpxmi, &clpxma, &clpymi, &clpyma );
455 }
456
457
458 if ( FT->scale != 0.0 ) // scale was set
459 {
460 clipxmin = (PLINT) ( clipxmin / FT->scale );
461 clipxmax = (PLINT) ( clipxmax / FT->scale );
462 if ( FT->invert_y == 1 )
463 {
464 clipymin = (PLINT) ( FT->ymax - ( clipymin / FT->scale ) );
465 clipymax = (PLINT) ( FT->ymax - ( clipymax / FT->scale ) );
466 }
467 else
468 {
469 clipymin = (PLINT) ( clipymin / FT->scale );
470 clipymax = (PLINT) ( clipymax / FT->scale );
471 }
472 }
473 else
474 {
475 clipxmin = (PLINT) ( clipxmin / FT->scalex );
476 clipxmax = (PLINT) ( clipxmax / FT->scalex );
477
478 if ( FT->invert_y == 1 )
479 {
480 clipymin = (PLINT) ( FT->ymax - ( clipymin / FT->scaley ) );
481 clipymax = (PLINT) ( FT->ymax - ( clipymax / FT->scaley ) );
482 }
483 else
484 {
485 clipymin = (PLINT) ( clipymin / FT->scaley );
486 clipymax = (PLINT) ( clipymax / FT->scaley );
487 }
488 }
489 if ( clipxmin > clipxmax )
490 {
491 tmp = clipxmax;
492 clipxmax = clipxmin;
493 clipxmin = tmp;
494 }
495 if ( clipymin > clipymax )
496 {
497 tmp = clipymax;
498 clipymax = clipymin;
499 clipymin = tmp;
500 }
501
502 // Comment this out as it fails for cases where we want to plot text
503 // in the background font, i.e. example 24.
504 //
505 //if ((slot->bitmap.pixel_mode==ft_pixel_mode_mono)||(pls->icol0==0)) {
506 if ( slot->bitmap.pixel_mode == ft_pixel_mode_mono )
507 {
508 x += slot->bitmap_left;
509 y -= slot->bitmap_top;
510
511 imin = (short) MAX( 0, clipymin - y );
512 imax = (short) MIN( slot->bitmap.rows, clipymax - y );
513 for ( i = imin; i < imax; i++ )
514 {
515 for ( k = 0; k < n; k++ )
516 {
517 bittest = 128;
518 for ( j = 0; j < 8; j++ )
519 {
520 if ( ( bittest & (unsigned char) slot->bitmap.buffer[( i * n ) + k] ) == bittest )
521 {
522 xx = x + ( k * 8 ) + j;
523 if ( ( xx >= clipxmin ) && ( xx <= clipxmax ) )
524 FT->pixel( pls, xx, y + i );
525 }
526 bittest >>= 1;
527 }
528 }
529 }
530 }
531
532 // this is the anti-aliased stuff
533
534 else
535 {
536 x += slot->bitmap_left;
537 y -= slot->bitmap_top;
538
539 imin = (short) MAX( 0, clipymin - y );
540 imax = (short) MIN( slot->bitmap.rows, clipymax - y );
541 kmin = (short) MAX( 0, clipxmin - x );
542 kmax = (short) MIN( slot->bitmap.width, clipxmax - x );
543 for ( i = imin; i < imax; i++ )
544 {
545 for ( k = kmin; k < kmax; k++ )
546 {
547 FT->shade = ( slot->bitmap.buffer[( i * slot->bitmap.width ) + k] );
548 if ( FT->shade > 0 )
549 {
550 if ( ( FT->BLENDED_ANTIALIASING == 1 ) && ( FT->read_pixel != NULL ) )
551 // The New anti-aliasing technique
552 {
553 if ( FT->shade == 255 )
554 {
555 FT->pixel( pls, x + k, y + i );
556 }
557 else
558 {
559 current_pixel_colour = FT->read_pixel( pls, x + k, y + i );
560
561 G = GetGValue( current_pixel_colour );
562 R = GetRValue( current_pixel_colour );
563 B = GetBValue( current_pixel_colour );
564 alpha_a = (float) FT->shade / 255.0;
565
566 // alpha_b=1.0-alpha_a;
567 // R=(plsc->curcolor.r*alpha_a)+(R*alpha_b);
568 // G=(plsc->curcolor.g*alpha_a)+(G*alpha_b);
569 // B=(plsc->curcolor.b*alpha_a)+(B*alpha_b);
570 //
571
572 // This next bit of code is, I *think*, computationally
573 // more efficient than the bit above. It results in
574 // an indistinguishable plot, but file sizes are different
575 // suggesting subtle variations doubtless caused by rounding
576 // and/or floating point conversions. Questions are - which is
577 // better ? Which is more "correct" ? Does it make a difference ?
578 // Is one faster than the other so that you'd ever notice ?
579 //
580
581 R = (int) ( ( ( plsc->curcolor.r - R ) * alpha_a ) + R );
582 G = (int) ( ( ( plsc->curcolor.g - G ) * alpha_a ) + G );
583 B = (int) ( ( ( plsc->curcolor.b - B ) * alpha_a ) + B );
584
585 FT->set_pixel( pls, x + k, y + i, RGB( R > 255 ? 255 : R, G > 255 ? 255 : G, B > 255 ? 255 : B ) );
586 }
587 }
588 else // The old anti-aliasing technique
589 {
590 FT->col_idx = FT->ncol0_width - ( ( FT->ncol0_width * FT->shade ) / 255 );
591 FT->last_icol0 = pls->icol0;
592 plcol0( pls->icol0 + ( FT->col_idx * ( FT->ncol0_org - 1 ) ) );
593 FT->pixel( pls, x + k, y + i );
594 plcol0( FT->last_icol0 );
595 }
596 }
597 }
598 }
599 }
600 }
601
602 //--------------------------------------------------------------------------
603 // plD_FreeType_init()
604 //
605 // Allocates memory to Freetype structure
606 // Initialises the freetype library.
607 // Initialises freetype structure
608 //--------------------------------------------------------------------------
609
610 void plD_FreeType_init( PLStream *pls )
611 {
612 FT_Data *FT;
613 char *a;
614 // font paths and file names can be long so leave generous (1024) room
615 char font_dir[PLPLOT_MAX_PATH];
616 // N.B. must be in exactly same order as TrueTypeLookup
617 const char *env_font_names[N_TrueTypeLookup] = {
618 "PLPLOT_FREETYPE_SANS_FONT",
619 "PLPLOT_FREETYPE_SERIF_FONT",
620 "PLPLOT_FREETYPE_MONO_FONT",
621 "PLPLOT_FREETYPE_SCRIPT_FONT",
622 "PLPLOT_FREETYPE_SYMBOL_FONT",
623 "PLPLOT_FREETYPE_SANS_ITALIC_FONT",
624 "PLPLOT_FREETYPE_SERIF_ITALIC_FONT",
625 "PLPLOT_FREETYPE_MONO_ITALIC_FONT",
626 "PLPLOT_FREETYPE_SCRIPT_ITALIC_FONT",
627 "PLPLOT_FREETYPE_SYMBOL_ITALIC_FONT",
628 "PLPLOT_FREETYPE_SANS_OBLIQUE_FONT",
629 "PLPLOT_FREETYPE_SERIF_OBLIQUE_FONT",
630 "PLPLOT_FREETYPE_MONO_OBLIQUE_FONT",
631 "PLPLOT_FREETYPE_SCRIPT_OBLIQUE_FONT",
632 "PLPLOT_FREETYPE_SYMBOL_OBLIQUE_FONT",
633 "PLPLOT_FREETYPE_SANS_BOLD_FONT",
634 "PLPLOT_FREETYPE_SERIF_BOLD_FONT",
635 "PLPLOT_FREETYPE_MONO_BOLD_FONT",
636 "PLPLOT_FREETYPE_SCRIPT_BOLD_FONT",
637 "PLPLOT_FREETYPE_SYMBOL_BOLD_FONT",
638 "PLPLOT_FREETYPE_SANS_BOLD_ITALIC_FONT",
639 "PLPLOT_FREETYPE_SERIF_BOLD_ITALIC_FONT",
640 "PLPLOT_FREETYPE_MONO_BOLD_ITALIC_FONT",
641 "PLPLOT_FREETYPE_SCRIPT_BOLD_ITALIC_FONT",
642 "PLPLOT_FREETYPE_SYMBOL_BOLD_ITALIC_FONT",
643 "PLPLOT_FREETYPE_SANS_BOLD_OBLIQUE_FONT",
644 "PLPLOT_FREETYPE_SERIF_BOLD_OBLIQUE_FONT",
645 "PLPLOT_FREETYPE_MONO_BOLD_OBLIQUE_FONT",
646 "PLPLOT_FREETYPE_SCRIPT_BOLD_OBLIQUE_FONT",
647 "PLPLOT_FREETYPE_SYMBOL_BOLD_OBLIQUE_FONT"
648 };
649 short i;
650
651 #if defined ( MSDOS ) || defined ( WIN32 )
652 static char *default_font_names[] = { "arial.ttf", "times.ttf", "timesi.ttf", "arial.ttf",
653 "symbol.ttf" };
654 char WINDIR_PATH[PLPLOT_MAX_PATH];
655 char *b;
656 b = getenv( "WINDIR" );
657 strncpy( WINDIR_PATH, b, PLPLOT_MAX_PATH - 1 );
658 WINDIR_PATH[PLPLOT_MAX_PATH - 1] = '\0';
659 #else
660 const char *default_unix_font_dir = PL_FREETYPE_FONT_DIR;
661 #endif
662
663
664 if ( pls->FT )
665 {
666 plwarn( "Freetype seems already to have been initialised!" );
667 return;
668 }
669
670 if ( ( pls->FT = calloc( 1, (size_t) sizeof ( FT_Data ) ) ) == NULL )
671 plexit( "Could not allocate memory for Freetype" );
672
673 FT = (FT_Data *) pls->FT;
674
675 if ( ( FT->textbuf = calloc( NTEXT_ALLOC, 1 ) ) == NULL )
676 plexit( "Could not allocate memory for Freetype text buffer" );
677
678 if ( FT_Init_FreeType( &FT->library ) )
679 plexit( "Could not initialise Freetype library" );
680
681 // set to an impossible value for an FCI
682 FT->fci = PL_FCI_IMPOSSIBLE;
683
684 #if defined ( MSDOS ) || defined ( WIN32 )
685
686 // First check for a user customised location and if
687 // the fonts aren't found there try the default Windows
688 // locations
689 if ( ( a = getenv( "PLPLOT_FREETYPE_FONT_DIR" ) ) != NULL )
690 strncpy( font_dir, a, PLPLOT_MAX_PATH - 1 );
691 else if ( strlen( PL_FREETYPE_FONT_DIR ) > 0 )
692 strncpy( font_dir, PL_FREETYPE_FONT_DIR, PLPLOT_MAX_PATH - 1 );
693 else if ( WINDIR_PATH == NULL )
694 {
695 //try to guess the font location by looking for arial font on C:
696 if ( access( "c:\\windows\\fonts\\arial.ttf", F_OK ) == 0 )
697 {
698 strcpy( font_dir, "c:/windows/fonts/" );
699 }
700 else if ( access( "c:\\windows\\system\\arial.ttf", F_OK ) == 0 )
701 {
702 strcpy( font_dir, "c:/windows/system/" );
703 }
704 else
705 plwarn( "Could not find font path; I sure hope you have defined fonts manually !" );
706 }
707 else
708 {
709 //Try to guess the font location by looking for Arial font in the Windows Path
710 strncat( WINDIR_PATH, "\\fonts\\arial.ttf", PLPLOT_MAX_PATH - 1 - strlen( WINDIR_PATH ) );
711 if ( access( WINDIR_PATH, F_OK ) == 0 )
712 {
713 b = strrchr( WINDIR_PATH, '\\' );
714 b++;
715 *b = 0;
716 makeunixslash( WINDIR_PATH );
717 strcpy( font_dir, WINDIR_PATH );
718 }
719 else
720 plwarn( "Could not find font path; I sure hope you have defined fonts manually !" );
721 }
722 font_dir[PLPLOT_MAX_PATH - 1] = '\0';
723
724 if ( pls->debug )
725 fprintf( stderr, "%s\n", font_dir );
726 #else
727
728 //
729 // For Unix systems, we will set the font path up a little differently in
730 // that the configured PL_FREETYPE_FONT_DIR has been set as the default path,
731 // but the user can override this by setting the environmental variable
732 // "PLPLOT_FREETYPE_FONT_DIR" to something else.
733 // NOTE WELL - the trailing slash must be added for now !
734 //
735
736 if ( ( a = getenv( "PLPLOT_FREETYPE_FONT_DIR" ) ) != NULL )
737 strncpy( font_dir, a, PLPLOT_MAX_PATH - 1 );
738 else
739 strncpy( font_dir, default_unix_font_dir, PLPLOT_MAX_PATH - 1 );
740
741 font_dir[PLPLOT_MAX_PATH - 1] = '\0';
742 #endif
743
744 //
745 // The driver looks for N_TrueTypeLookup environmental variables
746 // where the path and name of these fonts can be OPTIONALLY set,
747 // overriding the configured default values.
748 //
749
750 for ( i = 0; i < N_TrueTypeLookup; i++ )
751 {
752 if ( ( a = getenv( env_font_names[i] ) ) != NULL )
753 {
754 //
755 // Work out if we have been given an absolute path to a font name, or just
756 // a font name sans-path. To do this we will look for a directory separator
757 // character, which means some system specific junk. DJGPP is all wise, and
758 // understands both Unix and DOS conventions. DOS only knows DOS, and
759 // I assume everything else knows Unix-speak. (Why Bill, didn't you just
760 // pay the extra 15c and get a REAL separator???)
761 //
762
763 #ifdef MSDOS
764 if ( a[1] == ':' ) // check for MS-DOS absolute path
765 #else
766 if ( ( a[0] == '/' ) || ( a[0] == '~' ) ) // check for unix abs path
767 #endif
768 strncpy( FT->font_name[i], a, PLPLOT_MAX_PATH - 1 );
769
770 else
771 {
772 strncpy( FT->font_name[i], font_dir, PLPLOT_MAX_PATH - 1 );
773 strncat( FT->font_name[i], a, PLPLOT_MAX_PATH - 1 - strlen( FT->font_name[i] ) );
774 }
775 }
776 else
777 {
778 strncpy( FT->font_name[i], font_dir, PLPLOT_MAX_PATH - 1 );
779 strncat( FT->font_name[i], (const char *) TrueTypeLookup[i].pfont, PLPLOT_MAX_PATH - 1 - strlen( FT->font_name[i] ) );
780 }
781 FT->font_name[i][PLPLOT_MAX_PATH - 1] = '\0';
782
783 {
784 FILE *infile;
785 if ( ( infile = fopen( FT->font_name[i], "r" ) ) == NULL )
786 {
787 char msgbuf[1024];
788 snprintf( msgbuf, 1024,
789 "plD_FreeType_init: Could not find the freetype compatible font:\n %s",
790 FT->font_name[i] );
791 plwarn( msgbuf );
792 }
793 else
794 {
795 fclose( infile );
796 }
797 }
798 FontLookup[i].fci = TrueTypeLookup[i].fci;
799 if ( FT->font_name[i][0] == '\0' )
800 FontLookup[i].pfont = NULL;
801 else
802 FontLookup[i].pfont = (unsigned char *) FT->font_name[i];
803 }
804 //
805 // Next, we check to see if -drvopt has been used on the command line to
806 // over-ride any settings
807 //
808 }
809
810
811 //--------------------------------------------------------------------------
812 // FT_SetFace( PLStream *pls, PLUNICODE fci )
813 //
814 // Sets up the font face and size
815 //--------------------------------------------------------------------------
816
817 void FT_SetFace( PLStream *pls, PLUNICODE fci )
818 {
819 FT_Data *FT = (FT_Data *) pls->FT;
820 double font_size = pls->chrht * 72 / 25.4; // font_size in points, chrht is in mm
821
822 // save a copy of character height and resolution
823 FT->chrht = pls->chrht;
824 FT->xdpi = pls->xdpi;
825 FT->ydpi = pls->ydpi;
826
827 if ( fci != FT->fci )
828 {
829 const char *font_name = plP_FCI2FontName( fci, FontLookup, N_TrueTypeLookup );
830 if ( font_name == NULL )
831 {
832 if ( FT->fci == PL_FCI_IMPOSSIBLE )
833 plexit( "FT_SetFace: Bad FCI and no previous valid font to fall back on" );
834 else
835 plwarn( "FT_SetFace: Bad FCI. Falling back to previous font." );
836 }
837 else
838 {
839 FT->fci = fci;
840
841 if ( FT->face != NULL )
842 {
843 FT_Done_Face( FT->face );
844 FT->face = NULL;
845 }
846
847 if ( FT->face == NULL )
848 {
849 if ( FT_New_Face( FT->library, font_name, 0, &FT->face ) )
850 plexit( "FT_SetFace: Error loading a font in freetype" );
851 }
852
853 //check if the charmap was loaded correctly - freetype only checks for a unicode charmap
854 //if it is not set then use the first found charmap in the font
855 if ( FT->face->charmap == NULL )
856 FT_Select_Charmap( FT->face, FT->face->charmaps[0]->encoding );
857 }
858 }
859 FT_Set_Char_Size( FT->face, 0,
860 (FT_F26Dot6) ( font_size * 64 / TEXT_SCALING_FACTOR ), (FT_UInt) pls->xdpi,
861 (FT_UInt) pls->ydpi );
862 }
863
864 //--------------------------------------------------------------------------
865 // plD_render_freetype_text()
866 //
867 // Transforms the font
868 // calculates real-world bitmap coordinates from plplot ones
869 // renders text using freetype
870 //--------------------------------------------------------------------------
871
872 void plD_render_freetype_text( PLStream *pls, EscText *args )
873 {
874 FT_Data *FT = (FT_Data *) pls->FT;
875 int x, y;
876 int w = 0, h = 0, overh = 0, underh = 0;
877 PLFLT *t = args->xform;
878 FT_Matrix matrix;
879 PLFLT angle = PI * pls->diorot / 2;
880 PLUNICODE *line = args->unicode_array;
881 int linelen;
882 int prevlineheights = 0;
883
884 // Used later in a commented out section (See Rotate The Page), if that
885 // section will never be used again, remove these as well.
886 // PLINT clxmin, clxmax, clymin, clymax;
887 //
888 PLFLT Sin_A, Cos_A;
889 FT_Vector adjust;
890 PLUNICODE fci;
891 FT_Fixed height;
892 PLFLT height_factor;
893
894 if ( ( args->unicode_array_len > 0 ) )
895 {
896 //
897 // Work out if either the font size, the font face or the
898 // resolution has changed.
899 // If either has, then we will reload the font face.
900 //
901 plgfci( &fci );
902 if ( ( FT->fci != fci ) || ( FT->chrht != pls->chrht ) || ( FT->xdpi != pls->xdpi ) || ( FT->ydpi != pls->ydpi ) )
903 FT_SetFace( pls, fci );
904
905
906 // this will help work out underlining and overlining
907
908 Debug6( "%s %d %d %d %d\n", "plD_render_freetype_text:",
909 FT->face->underline_position >> 6,
910 FT->face->descender >> 6,
911 FT->face->ascender >> 6,
912 ( ( FT->face->underline_position * -1 ) + FT->face->ascender ) >> 6 );
913
914
915
916 //
917 // Split the text into lines based on the newline character
918 //
919 while ( line < args->unicode_array + args->unicode_array_len )
920 {
921 linelen = 0;
922 while ( line[linelen] != '\n' && line + linelen < args->unicode_array + args->unicode_array_len )
923 ++linelen;
924
925 //
926 // Now we work out how long the text is (for justification etc...) and how
927 // high the text is. This is done on UN-TRANSFORMED text, since we will
928 // apply our own transformations on it later, so it's necessary for us
929 // to to turn all transformations off first, before calling the function
930 // that calculates the text size.
931 //
932
933 FT->matrix.xx = 0x10000;
934 FT->matrix.xy = 0x00000;
935 FT->matrix.yx = 0x00000;
936 FT->matrix.yy = 0x10000;
937
938 FT_Vector_Transform( &FT->pos, &FT->matrix );
939 FT_Set_Transform( FT->face, &FT->matrix, &FT->pos );
940
941 FT_StrX_YW( pls, line, (short) linelen, &w, &h, &overh, &underh );
942
943 //
944 // Set up the transformation Matrix
945 //
946 // Fortunately this is almost identical to plplot's own transformation matrix;
947 // you have NO idea how much effort that saves ! Some params are in a
948 // different order, and Freetype wants integers whereas plplot likes floats,
949 // but such differences are quite trivial.
950 //
951 // For some odd reason, this needs to be set a different way for DJGPP. Why ?
952 // I wish I knew.
953 //
954
955 // (RL, on 2005-01-21) The height_factor variable is introduced below.
956 // It is used here and farther below when computing the vertical
957 // adjustment. The rationale for its introduction is as follow: up to
958 // now, the text produced with Hershey fonts was systematically taller
959 // than the same text produced with TT fonts, and tha by a factor of
960 // around 1.125 (I discovered this empirically). This corresponds
961 // roughly to the ratio between total height and the ascender of some
962 // TT faces. Hence the computation below. Remember that descender is
963 // always a negative quantity.
964 //
965
966 height_factor = (PLFLT) ( FT->face->ascender - FT->face->descender )
967 / FT->face->ascender;
968 height = (FT_Fixed) ( 0x10000 * height_factor );
969
970 #ifdef DJGPP
971 FT->matrix.xx = (FT_Fixed) ( (PLFLT) height * t[0] );
972 FT->matrix.xy = (FT_Fixed) ( (PLFLT) height * t[2] );
973 FT->matrix.yx = (FT_Fixed) ( (PLFLT) height * t[1] );
974 FT->matrix.yy = (FT_Fixed) ( (PLFLT) height * t[3] );
975 #else
976 FT->matrix.xx = (FT_Fixed) ( (PLFLT) height * t[0] );
977 FT->matrix.xy = (FT_Fixed) ( (PLFLT) height * t[1] );
978 FT->matrix.yx = (FT_Fixed) ( (PLFLT) height * t[2] );
979 FT->matrix.yy = (FT_Fixed) ( (PLFLT) height * t[3] );
980 #endif
981
982
983 // Rotate the Font
984 //
985 // If the page has been rotated using -ori, this is where we rotate the
986 // font to point in the right direction. To make things nice and easy, we
987 // will use freetypes matrix math stuff to do this for us.
988 //
989
990 Cos_A = cos( angle );
991 Sin_A = sin( angle );
992
993 matrix.xx = (FT_Fixed) ( (PLFLT) 0x10000 * Cos_A );
994
995 #ifdef DJGPP
996 matrix.xy = (FT_Fixed) ( (PLFLT) 0x10000 * Sin_A * -1.0 );
997 matrix.yx = (FT_Fixed) ( (PLFLT) 0x10000 * Sin_A );
998 #else
999 matrix.xy = (FT_Fixed) ( (PLFLT) 0x10000 * Sin_A );
1000 matrix.yx = (FT_Fixed) ( (PLFLT) 0x10000 * Sin_A * -1.0 );
1001 #endif
1002
1003 matrix.yy = (FT_Fixed) ( (PLFLT) 0x10000 * Cos_A );
1004
1005 FT_Matrix_Multiply( &matrix, &FT->matrix );
1006
1007
1008 // Calculate a Vector from the matrix
1009 //
1010 // This is closely related to the "transform matrix".
1011 // The matrix is used for rendering the glyph, while the vector is used for
1012 // calculating offsets of the text box, so we need both. Why ? I dunno, but
1013 // we have to live with it, and it works...
1014 //
1015
1016
1017 FT_Vector_Transform( &FT->pos, &FT->matrix );
1018
1019
1020 // Transform the font face
1021 //
1022 // This is where our matrix transformation is calculated for the font face.
1023 // This is only done once for each unique transformation since it is "sticky"
1024 // within the font. Font rendering is done later, using the supplied matrix,
1025 // but invisibly to us from here on. I don't believe the vector is used, but
1026 // it is asked for.
1027 //
1028
1029 FT_Set_Transform( FT->face, &FT->matrix, &FT->pos );
1030
1031
1032 // Rotate the Page
1033 //
1034 // If the page has been rotated using -ori, this is we recalculate the
1035 // reference point for the text using plplot functions.
1036 //
1037
1038 // difilt(&args->x, &args->y, 1, &clxmin, &clxmax, &clymin, &clymax);
1039
1040
1041 //
1042 // Convert into normal coordinates from virtual coordinates
1043 //
1044
1045 if ( FT->scale != 0.0 ) // scale was set
1046 {
1047 x = (int) ( args->x / FT->scale );
1048
1049 if ( FT->invert_y == 1 )
1050 y = (int) ( FT->ymax - ( args->y / FT->scale ) );
1051 else
1052 y = (int) ( args->y / FT->scale );
1053 }
1054 else
1055 {
1056 x = (int) ( args->x / FT->scalex );
1057
1058 if ( FT->invert_y == 1 )
1059 y = (int) ( FT->ymax - ( args->y / FT->scaley ) );
1060 else
1061 y = (int) ( args->y / FT->scaley );
1062 }
1063
1064 // Adjust for the justification and character height
1065 //
1066 // Eeeksss... this wasn't a nice bit of code to work out, let me tell you.
1067 // I could not work out an entirely satisfactory solution that made
1068 // logical sense, so came up with an "illogical" one as well.
1069 // The logical one works fine for text in the normal "portrait"
1070 // orientation, and does so for reasons you might expect it to work; But
1071 // for all other orientations, the text's base line is either a little
1072 // high, or a little low. This is because of the way the base-line pos
1073 // is calculated from the decender height. The "dodgie" way of calculating
1074 // the position is to use the character height here, then adjust for the
1075 // decender height by a three-fold factor later on. That approach seems to
1076 // work a little better for rotated pages, but why it should be so, I
1077 // don't understand. You can compile in or out which way you want it by
1078 // defining "DODGIE_DECENDER_HACK".
1079 //
1080 // note: the logic of the page rotation coming up next is that we pump in
1081 // the justification factor and then use freetype to rotate and transform
1082 // the values, which we then use to change the plotting location.
1083 //
1084
1085
1086 #ifdef DODGIE_DECENDER_HACK
1087 adjust.y = h;
1088 #else
1089 adjust.y = 0;
1090 #endif
1091
1092 // (RL, on 2005-01-24) The code below uses floating point and division
1093 // operations instead of integer shift used before. This is slower but
1094 // gives accurate placement of text in plots.
1095 //
1096
1097 // (RL, on 2005-01-21) The hack below is intended to align single
1098 // glyphs being generated via plpoin. The way to detect this
1099 // situation is completely hackish, I must admit, by checking whether the
1100 // length of the Unicode array is equal 2 and whether the first
1101 // character is actually a font-changing command to font number 4 (for
1102 // symbols). This is ugly because it depends on definitions set
1103 // elsewhere, but it works.
1104 //
1105 // The computation of the vertical and horizontal adjustments are
1106 // based on the bouding box of the glyph being loaded (since there is
1107 // only one glyph in the string in this case, we are okay here).
1108 //
1109
1110 if ( ( args->unicode_array_len == 2 )
1111 && ( args->unicode_array[0] == ( PL_FCI_MARK | 0x004 ) ) )
1112 {
1113 adjust.x = (FT_Pos) ( args->just * ROUND( (PLFLT) FT->face->glyph->metrics.width / 64.0 ) );
1114 adjust.y = (FT_Pos) ROUND( (PLFLT) FT->face->glyph->metrics.height / 128.0 );
1115 }
1116 else
1117 {
1118 // (RL, on 2005-01-21) The vertical adjustment is set below, making
1119 // the DODGIE conditional moot. I use the value of h as return by FT_StrX_YW,
1120 // which should correspond to the total height of the text being
1121 // drawn. Freetype aligns text around the baseline, while PLplot
1122 // aligns to the center of the ascender portion. We must then adjust
1123 // by half of the ascender and this is why there is a division by
1124 // height_factor below.
1125 //
1126
1127 adjust.y = (FT_Pos)
1128 ROUND( (PLFLT) FT->face->size->metrics.height / height_factor / 128.0 - ( prevlineheights + overh ) / 64.0 );
1129 adjust.x = (FT_Pos) ( args->just * ROUND( w / 64.0 ) );
1130 }
1131
1132 FT_Vector_Transform( &adjust, &FT->matrix ); // was /&matrix); - was I using the wrong matrix all this time ?
1133
1134 x -= (int) adjust.x;
1135 y += (int) adjust.y;
1136
1137 FT_WriteStrW( pls, line, (short) linelen, x, y ); // write it out
1138
1139 //
1140 // Move to the next line
1141 //
1142 line += linelen + 1;
1143 prevlineheights += h + overh + underh;
1144 }
1145 }
1146 else
1147 {
1148 plD_render_freetype_sym( pls, args );
1149 }
1150 }
1151
1152 //--------------------------------------------------------------------------
1153 // plD_FreeType_Destroy()
1154 //
1155 // Restores cmap0 if it had been modifed for anti-aliasing
1156 // closes the freetype library.
1157 // Deallocates memory to the Freetype structure
1158 //--------------------------------------------------------------------------
1159
1160 void plD_FreeType_Destroy( PLStream *pls )
1161 {
1162 FT_Data *FT = (FT_Data *) pls->FT;
1163 //extern int FT_Done_Library( FT_Library library );
1164
1165 if ( FT )
1166 {
1167 if ( ( FT->smooth_text == 1 ) && ( FT->BLENDED_ANTIALIASING == 0 ) )
1168 plscmap0n( FT->ncol0_org );
1169 if ( FT->textbuf )
1170 free( FT->textbuf );
1171 FT_Done_Library( FT->library );
1172 free( pls->FT );
1173 pls->FT = NULL;
1174 }
1175 }
1176
1177 //--------------------------------------------------------------------------
1178 // PLFLT CalculateIncrement( int bg, int fg, int levels)
1179 //
1180 // Takes the value of the foreground, and the background, and when
1181 // given the number of desired steps, calculates how much to incriment
1182 // a value to transition from fg to bg.
1183 // This function only does it for one colour channel at a time.
1184 //--------------------------------------------------------------------------
1185
1186 static PLFLT CalculateIncrement( int bg, int fg, int levels )
1187 {
1188 PLFLT ret = 0;
1189
1190 if ( levels > 1 )
1191 {
1192 if ( fg > bg )
1193 ret = ( ( fg + 1 ) - bg ) / levels;
1194 else if ( fg < bg )
1195 ret = ( ( ( fg - 1 ) - bg ) / levels );
1196 }
1197 return ( ret );
1198 }
1199
1200 //--------------------------------------------------------------------------
1201 // void pl_set_extended_cmap0(PLStream *pls, int ncol0_width, int ncol0_org)
1202 //
1203 // ncol0_width - how many greyscale levels to accolate to each CMAP0 entry
1204 // ncol0_org - the originl number of CMAP0 entries.
1205 //
1206 // This function calcualtes and sets an extended CMAP0 entry for the
1207 // driver. It is assumed that the caller has checked to make sure there is
1208 // room for extending CMAP0 already.
1209 //
1210 // NOTES
1211 // We don't bother calculating an entry for CMAP[0], the background.
1212 // It is assumed the caller has already expanded the size of CMAP[0]
1213 //--------------------------------------------------------------------------
1214
1215 void pl_set_extended_cmap0( PLStream *pls, int ncol0_width, int ncol0_org )
1216 {
1217 int i, j, k;
1218 int r, g, b;
1219 PLFLT r_inc, g_inc, b_inc;
1220
1221 for ( i = 1; i < ncol0_org; i++ )
1222 {
1223 r = pls->cmap0[i].r;
1224 g = pls->cmap0[i].g;
1225 b = pls->cmap0[i].b;
1226
1227 r_inc = CalculateIncrement( pls->cmap0[0].r, r, ncol0_width );
1228 g_inc = CalculateIncrement( pls->cmap0[0].g, g, ncol0_width );
1229 b_inc = CalculateIncrement( pls->cmap0[0].b, b, ncol0_width );
1230
1231 for ( j = 0, k = ncol0_org + i - 1; j < ncol0_width; j++, k += ( ncol0_org - 1 ) )
1232 {
1233 r -= (int) r_inc;
1234 g -= (int) g_inc;
1235 b -= (int) b_inc;
1236 if ( ( r < 0 ) || ( g < 0 ) || ( b < 0 ) )
1237 plscol0( k, 0, 0, 0 );
1238 else
1239 plscol0( k, ( r > 0xff ? 0xff : r ), ( g > 0xff ? 0xff : g ), ( b > 0xff ? 0xff : b ) );
1240 }
1241 }
1242 }
1243
1244
1245 //--------------------------------------------------------------------------
1246 // plD_render_freetype_sym( PLStream *pls, EscText *args )
1247 // PLStream *pls - pointer to plot stream
1248 // EscText *args - pointer to standard "string" object.
1249 //
1250 // This function is a simple rendering function which draws a single
1251 // character at a time. The function is an alternative to the text
1252 // functions which are considerably, and needlessly, more complicated
1253 // than what we need here.
1254 //--------------------------------------------------------------------------
1255
1256
1257 void plD_render_freetype_sym( PLStream *pls, EscText *args )
1258 {
1259 FT_Data *FT = (FT_Data *) pls->FT;
1260 int x, y;
1261 FT_Vector adjust;
1262 PLUNICODE fci;
1263
1264 if ( FT->scale != 0.0 ) // scale was set
1265 {
1266 x = (int) ( args->x / FT->scale );
1267
1268 if ( FT->invert_y == 1 )
1269 y = (int) ( FT->ymax - ( args->y / FT->scale ) );
1270 else
1271 y = (int) ( args->y / FT->scale );
1272 }
1273 else
1274 {
1275 x = (int) ( args->x / FT->scalex );
1276
1277 if ( FT->invert_y == 1 )
1278 y = (int) ( FT->ymax - ( args->y / FT->scaley ) );
1279 else
1280 y = (int) ( args->y / FT->scaley );
1281 }
1282
1283
1284 //
1285 // Adjust for the descender - make sure the font is nice and centred
1286 // vertically. Freetype assumes we have a base-line, but plplot thinks of
1287 // centre-lines, so that's why we have to do this. Since this is one of our
1288 // own adjustments, rather than a freetype one, we have to run it through
1289 // the transform matrix manually.
1290 //
1291 // For some odd reason, this works best if we triple the
1292 // descender's height and then adjust the height later on...
1293 // Don't ask me why, 'cause I don't know. But it does seem to work.
1294 //
1295 // I really wish I knew *why* it worked better though...
1296 //
1297 // y-=FT->face->descender >> 6;
1298 //
1299
1300 #ifdef DODGIE_DECENDER_HACK
1301 adjust.y = ( FT->face->descender >> 6 ) * 3;
1302 #else
1303 adjust.y = ( FT->face->descender >> 6 );
1304 #endif
1305
1306 adjust.x = 0;
1307 FT_Vector_Transform( &adjust, &FT->matrix );
1308 x += (int) adjust.x;
1309 y -= (int) adjust.y;
1310
1311 plgfci( &fci );
1312 FT_SetFace( pls, fci );
1313
1314 FT = (FT_Data *) pls->FT;
1315 FT_Set_Transform( FT->face, &FT->matrix, &FT->pos );
1316
1317 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 );
1318
1319 //
1320 // Now we have to try and componsate for the fact that the freetype glyphs are left
1321 // justified, and plplot's glyphs are centred. To do this, we will just work out the
1322 // advancment, halve it, and take it away from the x position. This wont be 100%
1323 // accurate because "spacing" is factored into the right hand side of the glyph,
1324 // but it is as good a way as I can think of.
1325 //
1326
1327 x -= (int) ( ( FT->face->glyph->advance.x >> 6 ) / 2 );
1328 FT_PlotChar( pls, FT, FT->face->glyph, x, y ); // render the text
1329 }
1330
1331
1332
1333
1334 #else
1335 int
1336 plfreetype()
1337 {
1338 return 0;
1339 }
1340
1341 #endif