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