move t1lib font support to a separate module
[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
f75c1aeb 11#ifdef HAVE_LIBT1
f75c1aeb
TC
12#endif
13
14
02d1d628
AMH
15/*
16=head1 NAME
17
18font.c - implements font handling functions for t1 and truetype fonts
19
20=head1 SYNOPSIS
21
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, int cords[6]);
27 rc = i_t1_destroy(fontnum);
28 #endif
29
30 #ifdef HAVE_LIBTT
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
39font.c implements font creation, rendering, bounding box functions and
40more for Imager.
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 */
4f68b48f 54#ifdef HAVE_LIBTT
02d1d628 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;
97 int ptsize;
98 int order;
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
123static int i_tt_get_instance( TT_Fonthandle *handle, int points, int smooth );
124static void i_tt_init_raster_map( TT_Raster_Map* bit, int width, int height, int smooth );
125static void i_tt_done_raster_map( TT_Raster_Map *bit );
126static void i_tt_clear_raster_map( TT_Raster_Map* bit );
127static void i_tt_blit_or( TT_Raster_Map *dst, TT_Raster_Map *src,int x_off, int 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,
132 int x_off, int y_off, int smooth );
133static int
134i_tt_render_all_glyphs( TT_Fonthandle *handle, int inst, TT_Raster_Map *bit,
135 TT_Raster_Map *small_bit, int cords[6],
718b8c97 136 char const* txt, size_t len, int smooth, int utf8 );
97ac0a96 137static void i_tt_dump_raster_map2( i_img* im, TT_Raster_Map* bit, int xb, int yb, const i_color *cl, int smooth );
02d1d628 138static void i_tt_dump_raster_map_channel( i_img* im, TT_Raster_Map* bit, int xb, int yb, int channel, int smooth );
4f68b48f
TC
139static int
140i_tt_rasterize( TT_Fonthandle *handle, TT_Raster_Map *bit, int cords[6],
718b8c97
TC
141 float 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, int cords[6], int utf8 );
02d1d628
AMH
143
144
145/* static globals needed */
146
d1555273 147static int TT_initialized = 0;
02d1d628
AMH
148static TT_Engine engine;
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
161Initializes the freetype font rendering engine
162
163=cut
164*/
165
166undef_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
AMH
173 mm_log((1,"init_tt()\n"));
174 error = TT_Init_FreeType( &engine );
175 if ( error ){
176 mm_log((1,"Initialization of freetype failed, code = 0x%x\n",error));
d1555273
TC
177 i_tt_push_error(error);
178 i_push_error(0, "Could not initialize freetype 1.x");
02d1d628
AMH
179 return(1);
180 }
3799c4d1
TC
181
182#ifdef FTXPOST
183 error = TT_Init_Post_Extension( engine );
184 if (error) {
185 mm_log((1, "Initialization of Post extension failed = 0x%x\n", error));
d1555273
TC
186
187 i_tt_push_error(error);
188 i_push_error(0, "Could not initialize FT 1.x POST extension");
3799c4d1
TC
189 return 1;
190 }
191#endif
192
95b2bff4
TC
193 error = TT_Set_Raster_Gray_Palette(engine, palette);
194 if (error) {
195 mm_log((1, "Initialization of gray levels failed = 0x%x\n", error));
d1555273
TC
196 i_tt_push_error(error);
197 i_push_error(0, "Could not initialize FT 1.x POST extension");
95b2bff4
TC
198 return 1;
199 }
200
d1555273
TC
201 TT_initialized = 1;
202
02d1d628
AMH
203 return(0);
204}
205
206
207/*
208=item i_tt_get_instance(handle, points, smooth)
209
210Finds a points+smooth instance or if one doesn't exist in the cache
211allocates room and returns its cache entry
212
213 fontname - path to the font to load
214 handle - handle to the font.
215 points - points of the requested font
216 smooth - boolean (True: antialias on, False: antialias is off)
217
218=cut
219*/
220
221static
222int
223i_tt_get_instance( TT_Fonthandle *handle, int points, int smooth ) {
224 int i,idx;
225 TT_Error error;
226
4f68b48f
TC
227 mm_log((1,"i_tt_get_instance(handle 0x%X, points %d, smooth %d)\n",
228 handle,points,smooth));
02d1d628
AMH
229
230 if (smooth == -1) { /* Smooth doesn't matter for this search */
4f68b48f
TC
231 for(i=0;i<TT_CHC;i++) {
232 if (handle->instanceh[i].ptsize==points) {
233 mm_log((1,"i_tt_get_instance: in cache - (non selective smoothing search) returning %d\n",i));
234 return i;
235 }
02d1d628
AMH
236 }
237 smooth=1; /* We will be adding a font - add it as smooth then */
238 } else { /* Smooth doesn't matter for this search */
4f68b48f
TC
239 for(i=0;i<TT_CHC;i++) {
240 if (handle->instanceh[i].ptsize == points
241 && handle->instanceh[i].smooth == smooth) {
242 mm_log((1,"i_tt_get_instance: in cache returning %d\n",i));
243 return i;
244 }
02d1d628
AMH
245 }
246 }
247
248 /* Found the instance in the cache - return the cache index */
249
4f68b48f
TC
250 for(idx=0;idx<TT_CHC;idx++) {
251 if (!(handle->instanceh[idx].order)) break; /* find the lru item */
252 }
02d1d628
AMH
253
254 mm_log((1,"i_tt_get_instance: lru item is %d\n",idx));
4f68b48f
TC
255 mm_log((1,"i_tt_get_instance: lru pointer 0x%X\n",
256 USTRCT(handle->instanceh[idx].instance) ));
02d1d628
AMH
257
258 if ( USTRCT(handle->instanceh[idx].instance) ) {
259 mm_log((1,"i_tt_get_instance: freeing lru item from cache %d\n",idx));
93d0372f 260
4f68b48f 261 /* Free cached glyphs */
93d0372f 262 for(i=0;i<256;i++)
4f68b48f
TC
263 if ( USTRCT(handle->instanceh[idx].glyphs[i].glyph) )
264 TT_Done_Glyph( handle->instanceh[idx].glyphs[i].glyph );
93d0372f 265
4f68b48f
TC
266 for(i=0;i<256;i++) {
267 handle->instanceh[idx].glyphs[i].ch = TT_NOCHAR;
268 USTRCT(handle->instanceh[idx].glyphs[i].glyph)=NULL;
269 }
270
271 /* Free instance if needed */
272 TT_Done_Instance( handle->instanceh[idx].instance );
02d1d628
AMH
273 }
274
275 /* create and initialize instance */
276 /* FIXME: probably a memory leak on fail */
277
278 (void) (( error = TT_New_Instance( handle->face, &handle->instanceh[idx].instance ) ) ||
279 ( error = TT_Set_Instance_Resolutions( handle->instanceh[idx].instance, LTT_dpi, LTT_dpi ) ) ||
280 ( error = TT_Set_Instance_CharSize( handle->instanceh[idx].instance, points*64 ) ) );
281
282 if ( error ) {
283 mm_log((1, "Could not create and initialize instance: error 0x%x.\n",error ));
284 return -1;
285 }
286
287 /* Now that the instance should the inplace we need to lower all of the
288 ru counts and put `this' one with the highest entry */
289
290 for(i=0;i<TT_CHC;i++) handle->instanceh[i].order--;
291
292 handle->instanceh[idx].order=TT_CHC-1;
293 handle->instanceh[idx].ptsize=points;
294 handle->instanceh[idx].smooth=smooth;
295 TT_Get_Instance_Metrics( handle->instanceh[idx].instance, &(handle->instanceh[idx].imetrics) );
296
4f68b48f
TC
297 /* Zero the memory for the glyph storage so they are not thought as
298 cached if they haven't been cached since this new font was loaded */
02d1d628 299
4f68b48f
TC
300 for(i=0;i<256;i++) {
301 handle->instanceh[idx].glyphs[i].ch = TT_NOCHAR;
302 USTRCT(handle->instanceh[idx].glyphs[i].glyph)=NULL;
303 }
02d1d628
AMH
304
305 return idx;
306}
307
308
309/*
310=item i_tt_new(fontname)
311
312Creates a new font handle object, finds a character map and initialise the
313the font handle's cache
314
315 fontname - path to the font to load
316
317=cut
318*/
319
320TT_Fonthandle*
97ac0a96 321i_tt_new(const char *fontname) {
02d1d628
AMH
322 TT_Error error;
323 TT_Fonthandle *handle;
324 unsigned short i,n;
325 unsigned short platform,encoding;
d93d5c10 326
d1555273
TC
327 if (!TT_initialized && i_init_tt()) {
328 i_push_error(0, "Could not initialize FT1 engine");
329 return NULL;
330 }
331
d93d5c10 332 i_clear_error();
02d1d628
AMH
333
334 mm_log((1,"i_tt_new(fontname '%s')\n",fontname));
335
336 /* allocate memory for the structure */
337
f0960b14 338 handle = mymalloc( sizeof(TT_Fonthandle) ); /* checked 5Nov05 tonyc */
02d1d628
AMH
339
340 /* load the typeface */
341 error = TT_Open_Face( engine, fontname, &handle->face );
342 if ( error ) {
4f68b48f
TC
343 if ( error == TT_Err_Could_Not_Open_File ) {
344 mm_log((1, "Could not find/open %s.\n", fontname ));
345 }
346 else {
347 mm_log((1, "Error while opening %s, error code = 0x%x.\n",fontname,
348 error ));
349 }
d93d5c10 350 i_tt_push_error(error);
02d1d628
AMH
351 return NULL;
352 }
353
354 TT_Get_Face_Properties( handle->face, &(handle->properties) );
4f68b48f 355
02d1d628 356 /* First, look for a Unicode charmap */
02d1d628
AMH
357 n = handle->properties.num_CharMaps;
358 USTRCT( handle->char_map )=NULL; /* Invalidate character map */
359
360 for ( i = 0; i < n; i++ ) {
361 TT_Get_CharMap_ID( handle->face, i, &platform, &encoding );
4f68b48f
TC
362 if ( (platform == 3 && encoding == 1 )
363 || (platform == 0 && encoding == 0 ) ) {
364 mm_log((2,"i_tt_new - found char map platform %u encoding %u\n",
365 platform, encoding));
02d1d628
AMH
366 TT_Get_CharMap( handle->face, i, &(handle->char_map) );
367 break;
368 }
369 }
27e79497
TC
370 if (!USTRCT(handle->char_map) && n != 0) {
371 /* just use the first one */
372 TT_Get_CharMap( handle->face, 0, &(handle->char_map));
373 }
02d1d628
AMH
374
375 /* Zero the pointsizes - and ordering */
376
377 for(i=0;i<TT_CHC;i++) {
378 USTRCT(handle->instanceh[i].instance)=NULL;
379 handle->instanceh[i].order=i;
380 handle->instanceh[i].ptsize=0;
381 handle->instanceh[i].smooth=-1;
382 }
383
3799c4d1
TC
384#ifdef FTXPOST
385 handle->loaded_names = 0;
386#endif
387
02d1d628
AMH
388 mm_log((1,"i_tt_new <- 0x%X\n",handle));
389 return handle;
390}
391
392
393
394/*
395