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