Commit | Line | Data |
---|---|---|
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 | ||
18 | font.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 | ||
39 | font.c implements font creation, rendering, bounding box functions and | |
40 | more for Imager. | |
41 | ||
42 | =head1 FUNCTION REFERENCE | |
43 | ||
44 | Some 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 |
84 | typedef 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 |
91 | struct 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 | 101 | typedef struct TT_Instancehandle_ TT_Instancehandle; |
02d1d628 | 102 | |
4f68b48f TC |
103 | struct 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 | 119 | static void i_tt_push_error(TT_Error rc); |
02d1d628 AMH |
120 | |
121 | /* Prototypes */ | |
122 | ||
123 | static int i_tt_get_instance( TT_Fonthandle *handle, int points, int smooth ); | |
124 | static void i_tt_init_raster_map( TT_Raster_Map* bit, int width, int height, int smooth ); | |
125 | static void i_tt_done_raster_map( TT_Raster_Map *bit ); | |
126 | static void i_tt_clear_raster_map( TT_Raster_Map* bit ); | |
127 | static void i_tt_blit_or( TT_Raster_Map *dst, TT_Raster_Map *src,int x_off, int y_off ); | |
4f68b48f TC |
128 | static int i_tt_get_glyph( TT_Fonthandle *handle, int inst, unsigned long j ); |
129 | static void | |
130 | i_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 ); | |
133 | static int | |
134 | i_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 | 137 | static void i_tt_dump_raster_map2( i_img* im, TT_Raster_Map* bit, int xb, int yb, const i_color *cl, int smooth ); |
02d1d628 | 138 | static void i_tt_dump_raster_map_channel( i_img* im, TT_Raster_Map* bit, int xb, int yb, int channel, int smooth ); |
4f68b48f TC |
139 | static int |
140 | i_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 ); |
142 | static 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 | 147 | static int TT_initialized = 0; |
02d1d628 AMH |
148 | static TT_Engine engine; |
149 | static int LTT_dpi = 72; /* FIXME: this ought to be a part of the call interface */ | |
150 | static int LTT_hinted = 1; /* FIXME: this too */ | |
151 | ||
152 | ||
153 | /* | |
154 | * FreeType interface | |
155 | */ | |
156 | ||
157 | ||
158 | /* | |
159 | =item init_tt() | |
160 | ||
161 | Initializes the freetype font rendering engine | |
162 | ||
163 | =cut | |
164 | */ | |
165 | ||
166 | undef_int | |
d1555273 | 167 | i_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 | ||
210 | Finds a points+smooth instance or if one doesn't exist in the cache | |
211 | allocates 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 | ||
221 | static | |
222 | int | |
223 | i_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 | ||
312 | Creates a new font handle object, finds a character map and initialise the | |
313 | the font handle's cache | |
314 | ||
315 | fontname - path to the font to load | |
316 | ||
317 | =cut | |
318 | */ | |
319 | ||
320 | TT_Fonthandle* | |
97ac0a96 | 321 | i_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 |