update Changes for FT2 thread safety
[imager.git] / font.c
CommitLineData
92bda632 1#include "imager.h"
fa16b6c6 2#include "imrender.h"
02d1d628
AMH
3
4#include <sys/types.h>
5#include <sys/stat.h>
6#include <fcntl.h>
7
8#include <stdio.h>
9#include <stdlib.h>
10
d03fd5a4
TC
11#ifdef HAVE_LIBT1
12#endif
13
f75c1aeb 14
02d1d628
AMH
15/*
16=head1 NAME
17
d03fd5a4 18font.c - implements font handling functions for t1 and truetype fonts
02d1d628
AMH
19
20=head1 SYNOPSIS
21
d03fd5a4
TC
22 i_init_fonts();
23
24 #ifdef HAVE_LIBT1
25 fontnum = i_t1_new(path_to_pfb, path_to_afm);
26 i_t1_bbox(fontnum, points, "foo", 3, i_img_dim cords[BOUNDING_BOX_COUNT]);
27 rc = i_t1_destroy(fontnum);
28 #endif
29
30 #ifdef HAVE_LIBTT
02d1d628 31 handle = i_tt_new(path_to_ttf);
eeaa33fd 32 rc = i_tt_bbox(handle, points, "foo", 3, int cords[6], utf8);
02d1d628
AMH
33 i_tt_destroy(handle);
34
35 // and much more
36
37=head1 DESCRIPTION
38
d03fd5a4
TC
39font.c implements font creation, rendering, bounding box functions and
40more for Imager.
02d1d628
AMH
41
42=head1 FUNCTION REFERENCE
43
44Some of these functions are internal.
45
b8c2033e 46=over
02d1d628
AMH
47
48=cut
49
50*/
51
02d1d628 52
4f68b48f 53/* Truetype font support */
d03fd5a4
TC
54#ifdef HAVE_LIBTT
55
d93d5c10 56/* These are enabled by default when configuring Freetype 1.x
3799c4d1
TC
57 I haven't a clue how to reliably detect it at compile time.
58
59 We need a compilation probe in Makefile.PL
60*/
61#define FTXPOST 1
d93d5c10 62#define FTXERR18 1
3799c4d1 63
4f68b48f
TC
64#include <freetype.h>
65#define TT_CHC 5
02d1d628 66
3799c4d1
TC
67#ifdef FTXPOST
68#include <ftxpost.h>
69#endif
70
d93d5c10
TC
71#ifdef FTXERR18
72#include <ftxerr18.h>
73#endif
74
a90b253d
TC
75/* some versions of FT1.x don't seem to define this - it's font defined
76 so it won't change */
77#ifndef TT_MS_LANGID_ENGLISH_GENERAL
78#define TT_MS_LANGID_ENGLISH_GENERAL 0x0409
79#endif
80
4f68b48f
TC
81/* convert a code point into an index in the glyph cache */
82#define TT_HASH(x) ((x) & 0xFF)
02d1d628 83
4f68b48f
TC
84typedef struct i_glyph_entry_ {
85 TT_Glyph glyph;
86 unsigned long ch;
87} i_tt_glyph_entry;
02d1d628 88
4f68b48f 89#define TT_NOCHAR (~0UL)
02d1d628 90
4f68b48f
TC
91struct TT_Instancehandle_ {
92 TT_Instance instance;
93 TT_Instance_Metrics imetrics;
94 TT_Glyph_Metrics gmetrics[256];
95 i_tt_glyph_entry glyphs[256];
96 int smooth;
4f68b48f 97 int order;
8d14daab 98 i_img_dim ptsize;
4f68b48f 99};
02d1d628 100
4f68b48f 101typedef struct TT_Instancehandle_ TT_Instancehandle;
02d1d628 102
4f68b48f
TC
103struct TT_Fonthandle_ {
104 TT_Face face;
105 TT_Face_Properties properties;
106 TT_Instancehandle instanceh[TT_CHC];
107 TT_CharMap char_map;
3799c4d1
TC
108#ifdef FTXPOST
109 int loaded_names;
110 TT_Error load_cond;
111#endif
4f68b48f 112};
02d1d628
AMH
113
114/* Defines */
115
116#define USTRCT(x) ((x).z)
117#define TT_VALID( handle ) ( ( handle ).z != NULL )
118
d93d5c10 119static void i_tt_push_error(TT_Error rc);
02d1d628
AMH
120
121/* Prototypes */
122
8d14daab
TC
123static int i_tt_get_instance( TT_Fonthandle *handle, i_img_dim points, int smooth );
124static void i_tt_init_raster_map( TT_Raster_Map* bit, i_img_dim width, i_img_dim height, int smooth );
02d1d628
AMH
125static void i_tt_done_raster_map( TT_Raster_Map *bit );
126static void i_tt_clear_raster_map( TT_Raster_Map* bit );
8d14daab 127static void i_tt_blit_or( TT_Raster_Map *dst, TT_Raster_Map *src,i_img_dim x_off, i_img_dim y_off );
4f68b48f
TC
128static int i_tt_get_glyph( TT_Fonthandle *handle, int inst, unsigned long j );
129static void
130i_tt_render_glyph( TT_Glyph glyph, TT_Glyph_Metrics* gmetrics,
131 TT_Raster_Map *bit, TT_Raster_Map *small_bit,
8d14daab 132 i_img_dim x_off, i_img_dim y_off, int smooth );
4f68b48f
TC
133static int
134i_tt_render_all_glyphs( TT_Fonthandle *handle, int inst, TT_Raster_Map *bit,
8d14daab 135 TT_Raster_Map *small_bit, i_img_dim cords[6],
718b8c97 136 char const* txt, size_t len, int smooth, int utf8 );
8d14daab
TC
137static void i_tt_dump_raster_map2( i_img* im, TT_Raster_Map* bit, i_img_dim xb, i_img_dim yb, const i_color *cl, int smooth );
138static void i_tt_dump_raster_map_channel( i_img* im, TT_Raster_Map* bit, i_img_dim xb, i_img_dim yb, int channel, int smooth );
4f68b48f 139static int
8d14daab
TC
140i_tt_rasterize( TT_Fonthandle *handle, TT_Raster_Map *bit, i_img_dim cords[6],
141 double points, char const* txt, size_t len, int smooth, int utf8 );
142static undef_int i_tt_bbox_inst( TT_Fonthandle *handle, int inst ,const char *txt, size_t len, i_img_dim cords[6], int utf8 );
02d1d628
AMH
143
144
145/* static globals needed */
146
d03fd5a4
TC
147static int TT_initialized = 0;
148static TT_Engine engine;
02d1d628
AMH
149static int LTT_dpi = 72; /* FIXME: this ought to be a part of the call interface */
150static int LTT_hinted = 1; /* FIXME: this too */
151
152
153/*
154 * FreeType interface
155 */
156
157
158/*
159=item init_tt()
160
d03fd5a4 161Initializes the freetype font rendering engine
02d1d628
AMH
162
163=cut
164*/
165
d03fd5a4 166static undef_int
d1555273 167i_init_tt(void) {
02d1d628 168 TT_Error error;
95b2bff4
TC
169 TT_Byte palette[] = { 0, 64, 127, 191, 255 };
170
d1555273
TC
171 i_clear_error();
172
02d1d628 173 mm_log((1,"init_tt()\n"));
d03fd5a4 174 error = TT_Init_FreeType( &engine );
02d1d628 175 if ( error ){
8d14daab
TC
176 mm_log((1,"Initialization of freetype failed, code = 0x%x\n",
177 (unsigned)error));
d1555273
TC
178 i_tt_push_error(error);
179 i_push_error(0, "Could not initialize freetype 1.x");
d03fd5a4 180 return(1);
02d1d628 181 }
3799c4d1
TC
182
183#ifdef FTXPOST
d03fd5a4 184 error = TT_Init_Post_Extension( engine );
3799c4d1 185 if (error) {
8d14daab
TC
186 mm_log((1, "Initialization of Post extension failed = 0x%x\n",
187 (unsigned)error));
d1555273
TC
188
189 i_tt_push_error(error);
190 i_push_error(0, "Could not initialize FT 1.x POST extension");
d03fd5a4 191 return 1;
3799c4d1
TC
192 }
193#endif
194
d03fd5a4 195 error = TT_Set_Raster_Gray_Palette(engine, palette);
95b2bff4 196 if (error) {
8d14daab
TC
197 mm_log((1, "Initialization of gray levels failed = 0x%x\n",
198 (unsigned)error));
d1555273
TC
199 i_tt_push_error(error);
200 i_push_error(0, "Could not initialize FT 1.x POST extension");
d03fd5a4 201 return 1;
95b2bff4
TC
202 }
203
d03fd5a4 204 TT_initialized = 1;
db65c8df 205
d03fd5a4 206 return(0);
02d1d628
AMH
207}
208
209
210/*
211=item i_tt_get_instance(handle, points, smooth)
212
213Finds a points+smooth instance or if one doesn't exist in the cache
214allocates room and returns its cache entry
215
216 fontname - path to the font to load
217 handle - handle to the font.
218 points - points of the requested font
219 smooth - boolean (True: antialias on, False: antialias is off)
220
221=cut
222*/
223
224static
225int
8d14daab 226i_tt_get_instance( TT_Fonthandle *handle, i_img_dim points, int smooth ) {
02d1d628
AMH
227 int i,idx;
228 TT_Error error;
229
8d14daab
TC
230 mm_log((1,"i_tt_get_instance(handle %p, points %" i_DF ", smooth %d)\n",
231 handle, i_DFc(points), smooth));
02d1d628
AMH
232
233 if (smooth == -1) { /* Smooth doesn't matter for this search */
4f68b48f
TC
234 for(i=0;i<TT_CHC;i++) {
235 if (handle->instanceh[i].ptsize==points) {
236 mm_log((1,"i_tt_get_instance: in cache - (non selective smoothing search) returning %d\n",i));
237 return i;
238 }
02d1d628
AMH
239 }
240 smooth=1; /* We will be adding a font - add it as smooth then */
241 } else { /* Smooth doesn't matter for this search */
4f68b48f
TC
242 for(i=0;i<TT_CHC;i++) {
243 if (handle->instanceh[i].ptsize == points
244 && handle->instanceh[i].smooth == smooth) {
245 mm_log((1,"i_tt_get_instance: in cache returning %d\n",i));
246 return i;
247 }
02d1d628
AMH
248 }
249 }
250
251 /* Found the instance in the cache - return the cache index */
252
4f68b48f
TC
253 for(idx=0;idx<TT_CHC;idx++) {
254 if (!(handle->instanceh[idx].order)) break; /* find the lru item */
255 }
02d1d628
AMH
256
257 mm_log((1,"i_tt_get_instance: lru item is %d\n",idx));
8d14daab 258 mm_log((1,"i_tt_get_instance: lru pointer %p\n",
4f68b48f 259 USTRCT(handle->instanceh[idx].instance) ));
02d1d628
AMH
260
261 if ( USTRCT(handle->instanceh[idx].instance) ) {
262 mm_log((1,"i_tt_get_instance: freeing lru item from cache %d\n",idx));
93d0372f 263
4f68b48f 264 /* Free cached glyphs */
93d0372f 265 for(i=0;i<256;i++)
4f68b48f
TC
266 if ( USTRCT(handle->instanceh[idx].glyphs[i].glyph) )
267 TT_Done_Glyph( handle->instanceh[idx].glyphs[i].glyph );
93d0372f 268
4f68b48f
TC
269 for(i=0;i<256;i++) {
270 handle->instanceh[idx].glyphs[i].ch = TT_NOCHAR;
271 USTRCT(handle->instanceh[idx].glyphs[i].glyph)=NULL;
272 }
273
274 /* Free instance if needed */
275 TT_Done_Instance( handle->instanceh[idx].instance );
02d1d628
AMH
276 }
277
278 /* create and initialize instance */
279 /* FIXME: probably a memory leak on fail */
280
281 (void) (( error = TT_New_Instance( handle->face, &handle->instanceh[idx].instance ) ) ||
282 ( error = TT_Set_Instance_Resolutions( handle->instanceh[idx].instance, LTT_dpi, LTT_dpi ) ) ||
283 ( error = TT_Set_Instance_CharSize( handle->instanceh[idx].instance, points*64 ) ) );
284
285 if ( error ) {
8d14daab
TC
286 mm_log((1, "Could not create and initialize instance: error %x.\n",
287 (unsigned)error ));
02d1d628
AMH
288 return -1;
289 }
290
291 /* Now that the instance should the inplace we need to lower all of the
292 ru counts and put `this' one with the highest entry */
293
294 for(i=0;i<TT_CHC;i++) handle->instanceh[i].order--;
295
296 handle->instanceh[idx].order=TT_CHC-1;
297 handle->instanceh[idx].ptsize=points;
298 handle->instanceh[idx].smooth=smooth;
299 TT_Get_Instance_Metrics( handle->instanceh[idx].instance, &(handle->instanceh[idx].imetrics) );
300
4f68b48f
TC
301 /* Zero the memory for the glyph storage so they are not thought as
302 cached if they haven't been cached since this new font was loaded */
02d1d628 303
4f68b48f
TC
304 for(i=0;i<256;i++) {
305 handle->instanceh[idx].glyphs[i].ch = TT_NOCHAR;
306 USTRCT(handle->instanceh[idx].glyphs[i].glyph)=NULL;
307 }
02d1d628
AMH
308
309 return idx;
310}
311
312
313/*
314=item i_tt_new(fontname)
315
316Creates a new font handle object, finds a character map and initialise the
317the font handle's cache
318
319 fontname - path to the font to load
320
321=cut
322*/
323
324TT_Fonthandle*
97ac0a96 325i_tt_new(const char *fontname) {
02d1d628
AMH
326 TT_Error error;
327 TT_Fonthandle *handle;
328 unsigned short i,n;
329 unsigned short platform,encoding;
d93d5c10 330
d03fd5a4 331 if (!TT_initialized && i_init_tt()) {
d1555273
TC
332 i_push_error(0, "Could not initialize FT1 engine");
333 return NULL;
334 }
335
d93d5c10 336 i_clear_error();
02d1d628
AMH
337
338 mm_log((1,"i_tt_new(fontname '%s')\n",fontname));
339
340 /* allocate memory for the structure */
341
f0960b14 342 handle = mymalloc( sizeof(TT_Fonthandle) ); /* checked 5Nov05 tonyc */
02d1d628
AMH
343
344 /* load the typeface */
d03fd5a4 345 error = TT_Open_Face( engine, fontname, &handle->face );
02d1d628 346 if ( error ) {
4f68b48f
TC
347 if ( error == TT_Err_Could_Not_Open_File ) {
348 mm_log((1, "Could not find/open %s.\n", fontname ));
349 }
350 else {
351 mm_log((1, "Error while opening %s, error code = 0x%x.\n",fontname,
8d14daab 352 (unsigned)error ));
4f68b48f 353 }
d93d5c10 354 i_tt_push_error(error);
02d1d628
AMH
355 return NULL;
356 }
357
358 TT_Get_Face_Properties( handle->face, &(handle->properties) );
4f68b48f 359
02d1d628 360 /* First, look for a Unicode charmap */
02d1d628
AMH
361 n = handle->properties.num_CharMaps;
362 USTRCT( handle->char_map )=NULL; /* Invalidate character map */
363
364 for ( i = 0; i < n; i++ ) {
365 TT_Get_CharMap_ID( handle->face, i, &platform, &encoding );
4f68b48f
TC
366 if ( (platform == 3 && encoding == 1 )
367 || (platform == 0 && encoding == 0 ) ) {
368 mm_log((2,"i_tt_new - found char map platform %u encoding %u\n",
369 platform, encoding));
02d1d628
AMH
370 TT_Get_CharMap( handle->face, i, &(handle->char_map) );
371 break;
372 }
373 }
27e79497
TC
374 if (!USTRCT(handle->char_map) && n != 0) {
375 /* just use the first one */
376 TT_Get_CharMap( handle->face, 0, &(handle->char_map));
377 }
02d1d628
AMH
378
379 /* Zero the pointsizes - and ordering */
380
381 for(i=0;i<TT_CHC;i++) {
382 USTRCT(handle->instanceh[i].instance)=NULL;
383 handle->instanceh[i].order=i;
384 handle->instanceh[i].ptsize=0;
385 handle->instanceh[i].smooth=-1;
386 }
387
3799c4d1
TC
388#ifdef FTXPOST
389 handle->loaded_names = 0;
390#endif
391
8d14daab 392 mm_log((1,"i_tt_new <- %p\n",handle));
02d1d628
AMH
393 return handle;
394}
395
396
397
398/*
399