1 /* font.c
2 Copyright (C) 2007-2020 Mark Tyler and Dmitry Groshev
3
4 This file is part of mtPaint.
5
6 mtPaint is free software; you can redistribute it and/or modify
7 it under the terms of the GNU General Public License as published by
8 the Free Software Foundation; either version 3 of the License, or
9 (at your option) any later version.
10
11 mtPaint is distributed in the hope that it will be useful,
12 but WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 GNU General Public License for more details.
15
16 You should have received a copy of the GNU General Public License
17 along with mtPaint in the file COPYING.
18 */
19
20 #ifdef U_FREETYPE
21
22 #include "global.h"
23 #undef _
24 #define _(X) X
25
26 #include "mygtk.h"
27 #include "memory.h"
28 #include "vcode.h"
29 #include "png.h"
30 #include "mainwindow.h"
31 #include "viewer.h"
32 #include "canvas.h"
33 #include "inifile.h"
34 #include "font.h"
35
36 #include <iconv.h>
37 #include <ft2build.h>
38 #include FT_FREETYPE_H
39
40 #if (GTK_MAJOR_VERSION == 1) && defined(U_NLS)
41 #include <langinfo.h>
42 #endif
43
44
45
46
47
48 /*
49 -----------------------------------------------------------------
50 | Definitions & Structs |
51 -----------------------------------------------------------------
52 */
53
54 #define MT_TEXT_MONO 1 /* Force mono rendering */
55 #define MT_TEXT_ROTATE_NN 2 /* Use nearest neighbour rotation on bitmap fonts */
56 #define MT_TEXT_OBLIQUE 4 /* Apply Oblique matrix transformation to scalable fonts */
57
58 #define FONT_INDEX_FILENAME ".mtpaint_fonts"
59
60 #define TX_MAX_DIRS 100
61
62 /* Persistent settings */
63 int font_obl, font_bmsize, font_size;
64 int font_dirs;
65 int ft_setdpi;
66
67
68 typedef struct filenameNODE filenameNODE;
69 struct filenameNODE
70 {
71 char *filename; // Filename of font
72 int face_index; // Face index within font file
73 filenameNODE *next; // Pointer to next filename, NULL=no more
74 };
75
76 typedef struct sizeNODE sizeNODE;
77 struct sizeNODE
78 {
79 int size; // Font size. 0=Scalable
80 filenameNODE *filename; // Pointer to first filename
81 sizeNODE *next; // Pointer to next size, NULL=no more
82 };
83
84 typedef struct styleNODE styleNODE;
85 struct styleNODE
86 {
87 char *style_name; // Style name
88 sizeNODE *size; // Pointer to first size of this font
89 styleNODE *next; // Pointer to next style, NULL=no more
90 };
91
92 typedef struct fontNODE fontNODE;
93 struct fontNODE
94 {
95 char *font_name; // Font name
96 int directory; // Which directory is this font in?
97 styleNODE *style; // Pointer to first style of this font
98 fontNODE *next; // Pointer to next font family, NULL=no more
99 };
100
101
102 typedef struct {
103 fontNODE *fn;
104 int dir, bm, name;
105 } fontname_cc;
106
107 typedef struct {
108 styleNODE *sn;
109 int name;
110 } fontstyle_cc;
111
112 typedef struct {
113 sizeNODE *zn;
114 int what;
115 int n;
116 } fontsize_cc;
117
118 typedef struct {
119 filenameNODE *fn;
120 int name, face;
121 } fontfile_cc;
122
123 typedef struct {
124 int idx, dir;
125 } dir_cc;
126
127 typedef struct {
128 char *text; // !!! widget-owned memory
129 int img, idx, script;
130 int fontname, nfontnames, fnsort;
131 int fontstyle, nfontstyles;
132 int lfontsize, nlfontsizes;
133 int fontfile, nfontfiles;
134 int dir, ndirs;
135 int fontsize;
136 int bkg[3], angle, dpi[3], spacing;
137 int preview_whc[3];
138 int lock;
139 unsigned char *preview_rgb;
140 memx2 fnmmem, fstmem, fszmem, ffnmem, dirmem;
141 fontname_cc *fontnames;
142 fontstyle_cc *fontstyles;
143 fontsize_cc *fontsizes;
144 fontfile_cc *fontfiles;
145 dir_cc *dirs;
146 void **preview_area;
147 void **obl_c, **size_spin, **add_b, **book;
148 void **fname_l, **fstyle_l, **fsize_l, **ffile_l, **dir_l;
149 char dirp[PATHBUF];
150 } font_dd;
151
152 static wjmem *font_mem;
153 static fontNODE *global_font_node;
154 static char *font_text;
155
156
157 /*
158 -----------------------------------------------------------------
159 | FreeType Rendering Code |
160 -----------------------------------------------------------------
161 */
162
ft_draw_bitmap(unsigned char * mem,int w,FT_Bitmap * bitmap,FT_Int x,FT_Int y,int ppb)163 static void ft_draw_bitmap(
164 unsigned char *mem,
165 int w,
166 FT_Bitmap *bitmap,
167 FT_Int x,
168 FT_Int y,
169 int ppb
170 )
171 {
172 unsigned char *dest = mem + y * w + x, *src = bitmap->buffer;
173 int i, j;
174
175 for (j = 0; j < bitmap->rows; j++)
176 {
177 if (ppb == 1) // 8 bits per pixel greyscale
178 {
179 // memcpy(dest, src, bitmap->width);
180 for (i = 0; i < bitmap->width; i++) dest[i] |= src[i];
181 }
182 else if (ppb == 8) // 1 bit per pixel mono
183 {
184 for (i = 0; i < bitmap->width; i++) dest[i] |=
185 (((int)src[i >> 3] >> (~i & 7)) & 1) * 255;
186 }
187 dest += w; src += bitmap->pitch;
188 }
189 }
190
extend(int * rxy,int x0,int y0,int x1,int y1)191 static inline void extend(int *rxy, int x0, int y0, int x1, int y1)
192 {
193 if (x0 < rxy[0]) rxy[0] = x0;
194 if (y0 < rxy[1]) rxy[1] = y0;
195 if (x1 > rxy[2]) rxy[2] = x1;
196 if (y1 > rxy[3]) rxy[3] = y1;
197 }
198
199
200 /*
201 Render text to a new chunk of memory. NULL return = failure, otherwise points to memory.
202 int characters required to print unicode strings correctly.
203 */
mt_text_render(char * text,int characters,char * filename,char * encoding,double size,int face_index,double angle,int flags,int * width,int * height)204 static unsigned char *mt_text_render(
205 char *text,
206 int characters,
207 char *filename,
208 char *encoding,
209 double size,
210 int face_index,
211 double angle,
212 int flags,
213 int *width,
214 int *height
215 )
216 {
217 unsigned char *mem = NULL;
218 #ifdef WIN32
219 const char *txtp1;
220 #else
221 char *txtp1;
222 #endif
223 char *txtp2;
224 double ca, sa, angle_r = angle / 180 * M_PI;
225 int bx, by, bw, bh, bits, ppb, fix_w, fix_h, scalable;
226 int Y1, Y2, X1, X2, ll, line, xflag;
227 int spc, spcx, spcy;
228 int pass, lines = 0;
229 #define MAX_LW 16
230 int tx0, tx1, lwa[MAX_LW * 2], *lw, *lw0 = lwa;
231 int dpi = ft_setdpi ? font_dpi : sys_dpi;
232 int minxy[4] = { MAX_WIDTH, MAX_HEIGHT, -MAX_WIDTH, -MAX_HEIGHT };
233 size_t s, ssize1 = characters, ssize2 = characters * 4 + 5;
234 iconv_t cd;
235 FT_Library library;
236 FT_Face face;
237 FT_Matrix matrix;
238 FT_Vector pen, uninit_(pen0);
239 FT_Error error;
240 FT_UInt glyph_index;
241 FT_Int32 unichar, *txt2, *tmp2;
242 FT_Int32 load_flags = FT_LOAD_RENDER | FT_LOAD_FORCE_AUTOHINT;
243
244 //printf("\n%s %i %s %s %f %i %f %i\n", text, characters, filename, encoding, size, face_index, angle, flags);
245
246 if ( flags & MT_TEXT_MONO ) load_flags |= FT_LOAD_TARGET_MONO;
247
248 if (characters < 1) return NULL;
249
250 error = FT_Init_FreeType( &library );
251 if (error) return NULL;
252
253 error = FT_New_Face( library, filename, face_index, &face );
254 if (error) goto fail2;
255
256 scalable = FT_IS_SCALABLE(face);
257
258 if (!scalable)
259 {
260 ca = 1.0; sa = 0.0; // Transform, if any, is for later
261
262 /* !!! Linux .pcf fonts require requested height to match ppem rounded up;
263 * Windows .fon fonts, conversely, require width & height. So we try both - WJ */
264 fix_w = face->available_sizes[0].width;
265 fix_h = face->available_sizes[0].height;
266 error = FT_Set_Pixel_Sizes(face, fix_w, fix_h);
267 if (error)
268 {
269 fix_w = (face->available_sizes[0].x_ppem + 32) >> 6;
270 fix_h = (face->available_sizes[0].y_ppem + 32) >> 6;
271 error = FT_Set_Pixel_Sizes(face, fix_w, fix_h);
272 }
273 if (error) goto fail1;
274
275 // !!! FNT fonts have special support in FreeType - maybe use it?
276 Y1 = face->size->metrics.ascender;
277 Y2 = face->size->metrics.descender;
278 }
279 else
280 {
281 ca = cos(angle_r); sa = sin(angle_r);
282 /* Ignore embedded bitmaps if transforming the font */
283 if (angle_r) load_flags |= FT_LOAD_NO_BITMAP;
284
285 matrix.yy = matrix.xx = (FT_Fixed)(ca * 0x10000L);
286 matrix.xy = -(matrix.yx = (FT_Fixed)(sa * 0x10000L));
287 if (flags & MT_TEXT_OBLIQUE)
288 {
289 matrix.xy = (FT_Fixed)((0.25 * ca - sa) * 0x10000L);
290 matrix.yy = (FT_Fixed)((0.25 * sa + ca) * 0x10000L);
291 load_flags |= FT_LOAD_NO_BITMAP;
292 }
293
294 error = FT_Set_Char_Size(face, size * 64, 0, dpi, 0);
295 if (error) goto fail1;
296
297 Y1 = FT_MulFix(face->ascender, face->size->metrics.y_scale);
298 Y2 = FT_MulFix(face->descender, face->size->metrics.y_scale);
299 }
300 spc = font_spacing * 0.64;
301 spcx = font_spacing * 0.64 * ca;
302 spcy = font_spacing * 0.64 * sa;
303
304 txt2 = calloc(1, ssize2 + 4);
305 if (!txt2) goto fail1;
306
307 txtp1 = text;
308 txtp2 = (char *)txt2;
309
310 /* !!! To handle non-Unicode fonts properly, is just too costly;
311 * instead we map 'em to ISO 8859-1 and hope for the best - WJ */
312 if (FT_Select_Charmap(face, FT_ENCODING_UNICODE))
313 FT_Set_Charmap(face, face->charmaps[0]); // Fallback
314
315 /* Convert input string to UTF-32, using native byte order */
316 #if G_BYTE_ORDER == G_LITTLE_ENDIAN
317 cd = iconv_open("UTF-32LE", encoding);
318 #else /* G_BYTE_ORDER == G_BIG_ENDIAN */
319 cd = iconv_open("UTF-32BE", encoding);
320 #endif
321
322 if ( cd == (iconv_t)(-1) ) goto fail0;
323
324 s = iconv(cd, &txtp1, &ssize1, &txtp2, &ssize2);
325 iconv_close(cd);
326 if (s == (size_t)(-1)) goto fail0;
327 characters = (txtp2 - (char *)txt2) / sizeof(*txt2); // Converted length
328 txt2[characters] = 0x0A; // Final newline
329 txt2[characters + 1] = 0; // Terminate the line
330
331 for (tmp2 = txt2; *tmp2; tmp2++) lines += *tmp2 == 0x0A;
332 /* For line boundaries */
333 if (lines > MAX_LW) lw0 = malloc(lines * sizeof(int) * 2);
334 memset(lw0, 0, lines * sizeof(int) * 2);
335
336 xflag = X1 = X2 = 0;
337 for (pass = -1; pass <= 1; pass++)
338 {
339 pen.x = pen.y = 0;
340 ll = 0;
341 line = -1;
342
343 for (tmp2 = txt2 , unichar = 0x0A; unichar; unichar = *tmp2++)
344 {
345 if (unichar == 0x0A) // Newline
346 {
347 lw = lw0 + ++line * 2;
348 tx0 = 0;
349 ll = 0; // Reset horizontal index
350 if (pass < 0) continue;
351 tx1 = (Y1 - Y2) * line;
352 pen.x = tx1 * sa + lw[0] * ca;
353 pen.y = tx1 * -ca + lw[0] * sa;
354 continue;
355 }
356
357 // Apply spacing
358 if (ll) pen.x += spcx , pen.y += spcy;
359
360 glyph_index = FT_Get_Char_Index(face, unichar);
361
362 if (scalable) // Cannot rotate fixed fonts
363 FT_Set_Transform(face, &matrix, &pen);
364
365 error = FT_Load_Glyph( face, glyph_index, load_flags );
366 if ( error ) continue;
367
368 if (pass < 0) // Calculating line bounds
369 {
370 if (lw[0] > tx0) lw[0] = tx0;
371 tx0 += face->glyph->metrics.horiAdvance;
372 if (lw[1] < tx0) lw[1] = tx0;
373 tx0 += spc;
374 continue;
375 }
376
377 // Remember boundaries
378 tx0 = lw[0] + face->glyph->metrics.horiBearingX;
379 if (!ll++)
380 {
381 if (!xflag++) X1 = X2 = tx0; // First glyph
382 pen0 = pen;
383 }
384 tx0 += (pen.x - pen0.x) * ca + (pen.y - pen0.y) * sa;
385 if (X1 > tx0) X1 = tx0;
386 tx0 += face->glyph->metrics.width - 64;
387 if (X2 < tx0) X2 = tx0;
388
389 switch (face->glyph->bitmap.pixel_mode)
390 {
391 case FT_PIXEL_MODE_GRAY: ppb = 1; break;
392 case FT_PIXEL_MODE_GRAY2: ppb = 2; break;
393 case FT_PIXEL_MODE_GRAY4: ppb = 4; break;
394 case FT_PIXEL_MODE_MONO: ppb = 8; break;
395 default: continue; // Unsupported mode
396 }
397
398 bx = face->glyph->bitmap_left;
399 by = -face->glyph->bitmap_top;
400 bw = face->glyph->bitmap.width;
401 bh = face->glyph->bitmap.rows;
402 bits = bw && bh;
403
404 // Bitmap glyphs don't get offset by FreeType
405 if (!scalable || (bits && !face->glyph->outline.n_points))
406 {
407 bx += pen.x >> 6;
408 by -= pen.y >> 6;
409 }
410 pen.x += face->glyph->advance.x;
411 pen.y += face->glyph->advance.y;
412
413 // Remember bitmap bounds
414 if (!mem && bits)
415 extend(minxy, bx, by, bx + bw - 1, by + bh - 1);
416
417 // Draw bitmap onto clipboard memory in pass 1
418 if (mem) ft_draw_bitmap(mem, *width, &face->glyph->bitmap,
419 bx - minxy[0], by - minxy[1], ppb);
420 }
421
422 if (pass < 0) // Calculate line offsets
423 {
424 lw = lw0;
425 for (tx0 = tx1 = 0 , line = lines; line-- > 0; )
426 {
427 if (tx0 > lw[0]) tx0 = lw[0];
428 if (tx1 < lw[1]) tx1 = lw[1];
429 lw += 2;
430 }
431 lw = lw0;
432 for (line = lines; line-- > 0; )
433 {
434 lw[0] = !font_align ? tx0 - lw[0] : // Left
435 font_align == 1 ? // Center
436 (tx0 + tx1 - lw[0] - lw[1]) / 2 :
437 tx1 - lw[1]; // Right
438 lw += 2;
439 }
440 continue;
441 }
442
443 if (mem) break; // Done
444
445 /* Adjust bounds for full-height rectangle; ignore possible
446 * rounding issues, for their effect is purely visual - WJ */
447 by = Y1 + (Y2 - Y1) * line;
448 while (TRUE)
449 {
450 bx = X1;
451 while (TRUE)
452 {
453 int Ax, Ay;
454
455 Ax = bx * ca - by * sa;
456 if (Ax < 0) Ax = -(-Ax / 64);
457 else Ax = (Ax + 63) / 64;
458 if (Ax < minxy[0]) minxy[0] = Ax;
459 if (Ax > minxy[2]) minxy[2] = Ax;
460
461 Ay = by * ca + bx * sa;
462 if (Ay < 0) Ay = (63 - Ay) / 64;
463 else Ay = -(Ay / 64);
464 if (Ay < minxy[1]) minxy[1] = Ay;
465 if (Ay > minxy[3]) minxy[3] = Ay;
466
467 if (bx == X2) break;
468 bx = X2;
469 }
470 if (by == Y1) break; // Done
471 by = Y1;
472 }
473
474 // Set up new clipboard
475 mem = calloc(1, (*width = minxy[2] - minxy[0] + 1) *
476 (*height = minxy[3] - minxy[1] + 1));
477 if (!mem) break; // Allocation failed so bail out
478 }
479
480 if (mem && !scalable && (angle!=0 || size>=2) ) // Rotate/Scale the bitmap font
481 {
482 chanlist old_img = {NULL, NULL, NULL, NULL}, new_img = {NULL, NULL, NULL, NULL};
483 int nw, nh, ow = *width, oh = *height, ch = CHN_MASK,
484 smooth = FALSE, // FALSE=nearest neighbour, TRUE=smooth
485 scale = size // Scale factor
486 ;
487
488 if ( scale >= 2 ) // Scale the bitmap up
489 {
490 nw = ow*scale;
491 nh = oh*scale;
492 old_img[ch] = mem;
493 new_img[ch] = calloc( 1, nw*nh );
494 if ( new_img[ch] )
495 {
496 if ( !mem_image_scale_real(old_img, ow, oh, 1, new_img, nw, nh,
497 0, FALSE, FALSE) )
498 {
499 mem = new_img[ch];
500 free( old_img[ch] ); // Scaling succeeded
501 *width = nw;
502 *height = nh;
503 ow = nw;
504 oh = nh;
505 }
506 else
507 {
508 free( new_img[ch] ); // Scaling failed
509 }
510 }
511 }
512
513 if ( angle != 0 )
514 {
515 mem_rotate_geometry(ow, oh, angle, &nw, &nh);
516
517 if ( !(flags & MT_TEXT_ROTATE_NN) ) // Smooth rotation requested
518 smooth = TRUE;
519
520 old_img[ch] = mem;
521 new_img[ch] = calloc( 1, nw*nh );
522
523 if ( new_img[ch] )
524 {
525 //printf("old = %i,%i new = %i,%i\n", ow, oh, nw, nh);
526
527 mem_rotate_free_real(old_img, new_img, ow, oh,
528 nw, nh, 1, -angle, smooth, FALSE, FALSE, TRUE);
529
530 mem = new_img[ch];
531 *width = nw;
532 *height = nh;
533 free( old_img[ch] );
534 }
535 }
536 }
537 if (lw0 != lwa) free(lw0);
538
539 fail0:
540 free(txt2);
541 fail1:
542 FT_Done_Face(face);
543 fail2:
544 FT_Done_FreeType(library);
545
546 return mem;
547 }
548
549
550 /*
551 -----------------------------------------------------------------
552 | Font Indexing Code |
553 -----------------------------------------------------------------
554 */
555
556 #define SIZE_SHIFT 10
557 #define MAXLEN 256
558 #define SLOT_FONT 0
559 #define SLOT_DIR 1
560 #define SLOT_STYLE 2
561 #define SLOT_SIZE 3
562 #define SLOT_FILENAME 4
563 #define SLOT_TOT 5
564
565
566
trim_tab(char * buf,char * txt)567 static void trim_tab( char *buf, char *txt)
568 {
569 char *st;
570
571 buf[0] = 0;
572 if (txt) strncpy0(buf, txt, MAXLEN);
573 for ( st=buf; st[0]!=0; st++ ) if ( st[0]=='\t' ) st[0]=' ';
574 if ( buf[0] == 0 ) snprintf(buf, MAXLEN, "_None");
575 }
576
577 typedef struct statchain statchain;
578 struct statchain {
579 statchain *p;
580 struct stat buf;
581 };
582
font_dir_search(FT_Library * ft_lib,int dirnum,FILE * fp,char * dir,statchain * cc)583 static void font_dir_search(FT_Library *ft_lib, int dirnum, FILE *fp, char *dir,
584 statchain *cc)
585 { // Search given directory for font files - recursively traverse directories
586 statchain sc = { cc };
587 FT_Face face;
588 DIR *dp;
589 struct dirent *ep;
590 char full_name[PATHBUF], tmp[2][MAXLEN];
591 int face_index;
592
593
594 dp = opendir(dir);
595 if (!dp) return;
596
597 while ( (ep = readdir(dp)) )
598 {
599 file_in_dir(full_name, dir, ep->d_name, PATHBUF);
600
601 if (stat(full_name, &sc.buf) < 0) continue; // Get file details
602
603 #ifdef WIN32
604 if (S_ISDIR(sc.buf.st_mode))
605 #else
606 if ((ep->d_type == DT_DIR) || S_ISDIR(sc.buf.st_mode))
607 #endif
608 { // Subdirectory so recurse
609 if (!strcmp(ep->d_name, ".") || !strcmp(ep->d_name, ".."))
610 continue;
611 /* If no inode number, assume it's Windows and just hope
612 * for the best: symlink loops do exist in Windows 7+, but
613 * I know of no simple approach for avoiding them - WJ */
614 if (sc.buf.st_ino)
615 {
616 for (cc = sc.p; cc; cc = cc->p)
617 if ((sc.buf.st_dev == cc->buf.st_dev) &&
618 (sc.buf.st_ino == cc->buf.st_ino)) break;
619 if (cc) continue; // Directory loop
620 }
621 font_dir_search( ft_lib, dirnum, fp, full_name, &sc );
622 continue;
623 }
624 // File so see if its a font
625 for ( face_index = 0;
626 !FT_New_Face( *ft_lib, full_name, face_index, &face );
627 face_index++ )
628 {
629 int size_type = 0;
630
631 if (!FT_IS_SCALABLE(face)) size_type =
632 face->available_sizes[0].height +
633 (face->available_sizes[0].width << SIZE_SHIFT) +
634 (face_index << (SIZE_SHIFT * 2));
635
636 // I use a tab character as a field delimeter, so replace any in the strings with a space
637
638 trim_tab( tmp[0], face->family_name );
639 trim_tab( tmp[1], face->style_name );
640
641 fprintf(fp, "%s\t%i\t%s\t%i\t%s\n",
642 tmp[0], dirnum, tmp[1], size_type, full_name);
643
644 if ( (face_index+1) >= face->num_faces )
645 {
646 FT_Done_Face(face);
647 break;
648 }
649 FT_Done_Face(face);
650 }
651 }
652 closedir(dp);
653 }
654
font_index_create(char * filename,char ** dir_in)655 static void font_index_create(char *filename, char **dir_in)
656 { // dir_in points to NULL terminated sequence of directories to search for fonts
657 statchain sc = { NULL };
658 FT_Library library;
659 int i;
660 FILE *fp;
661
662
663 if (FT_Init_FreeType(&library)) return;
664
665 if ((fp = fopen(filename, "w")))
666 {
667 for (i = 0; dir_in[i]; i++)
668 {
669 #ifdef WIN32
670 /* With old MinGW, stat() fails if dirname has path
671 * separator on end, so cut it off before call */
672 char *s;
673 int l = strlen(dir_in[i]);
674 if (!l--) continue;
675 s = strdup(dir_in[i]);
676 if ((s[l] == '\\') || (s[l] == '/')) s[l] = '\0';
677 l = stat(s, &sc.buf);
678 free(s);
679 if (l < 0) continue;
680 #else
681 if (stat(dir_in[i], &sc.buf) < 0) continue;
682 #endif
683 font_dir_search(&library, i, fp, dir_in[i], &sc);
684 }
685 fclose(fp);
686 }
687
688 FT_Done_FreeType(library);
689 }
690
691
font_mem_clear()692 static void font_mem_clear() // Empty whole structure from memory
693 {
694 free(font_text); font_text = NULL;
695 wjmemfree(font_mem); font_mem = NULL;
696 global_font_node = NULL;
697 }
698
699 #define newNODE(X) wjmalloc(font_mem, sizeof(X), ALIGNOF(X))
700
font_mem_add(char * font,int dirn,char * style,int fsize,char * filename)701 static int font_mem_add(char *font, int dirn, char *style, int fsize,
702 char *filename)
703 {// Add new font data to memory structure. Returns TRUE if successful.
704 int bm_index;
705 fontNODE *fo;
706 styleNODE *st;
707 sizeNODE *ze;
708 filenameNODE *fl;
709
710
711 bm_index = fsize >> SIZE_SHIFT * 2;
712 fsize &= (1 << SIZE_SHIFT * 2) - 1;
713
714 for (fo = global_font_node; fo; fo = fo->next)
715 {
716 if (!strcmp(fo->font_name, font) && (fo->directory == dirn))
717 break; // Font family+directory already exists
718 }
719
720 if (!fo) // Set up new structure as no match currently exists
721 {
722 fo = newNODE(fontNODE);
723 if (!fo) return (FALSE); // Memory failure
724 fo->next = global_font_node;
725 global_font_node = fo;
726
727 fo->directory = dirn;
728 fo->font_name = font;
729 /*
730 Its more efficient to load the newest font family/style/size as the new head because
731 when checking subsequent new items, its more likely that the next match will be the
732 head (or near it). If you add the new item to the end of the list you are ensuring
733 that wasted searches will happen as the more likely match will be at the end.
734 MT 22-8-2007
735 */
736 }
737
738 for (st = fo->style; st; st = st->next)
739 {
740 if (!strcmp(st->style_name, style))
741 break; // Font style already exists
742 }
743
744 if (!st) // Set up new structure as no match currently exists
745 {
746 st = newNODE(styleNODE);
747 if (!st) return (FALSE); // Memory failure
748 st->next = fo->style;
749 fo->style = st; // New head style
750
751 st->style_name = style;
752 }
753
754 for (ze = st->size; ze; ze = ze->next)
755 {
756 if ( ze->size == fsize ) break; // Font size already exists
757 }
758
759 if (!ze) // Set up new structure
760 {
761 ze = newNODE(sizeNODE);
762 if (!ze) return (FALSE); // Memory failure
763 ze->next = st->size;
764 st->size = ze; // New head size
765 ze->size = fsize;
766 }
767
768 /*
769 Always create a new filename node. If any filenames are duplicates we don't care.
770 If the user is stupid enough to pass dupicates then they must have their stupidity
771 shown to them in glorious technicolour so they don't do it again! ;-)
772 MT 24-8-2007
773 */
774 fl = newNODE(filenameNODE);
775 if (!fl) return (FALSE); // Memory failure
776
777 fl->next = ze->filename; // Old first filename (maybe NULL)
778 ze->filename = fl; // This is the new first filename
779
780 fl->filename = filename;
781 fl->face_index = bm_index;
782
783 return (TRUE);
784 }
785
font_index_load(char * filename)786 static void font_index_load(char *filename)
787 {
788 char *buf, *tmp, *tail, *slots[SLOT_TOT];
789 int i, dir, size;
790
791
792 font_mem = wjmemnew(0, 0);
793 font_text = slurp_file(filename, 1);
794 if (!font_mem || !font_text)
795 {
796 font_mem_clear();
797 return;
798 }
799
800 for (buf = font_text + 1; *buf; buf = tmp)
801 {
802 buf += strspn(buf, "\r\n");
803 if (!*buf) break;
804 tmp = buf + strcspn(buf, "\r\n");
805 if (*tmp) *tmp++ = 0;
806 for (i = 0; i < SLOT_TOT; i++)
807 {
808 slots[i] = buf;
809 buf += strcspn(buf, "\t");
810 if (*buf) *buf++ = 0;
811 }
812 dir = strtol(slots[SLOT_DIR], &tail, 10);
813 if (*tail) break;
814 size = strtol(slots[SLOT_SIZE], &tail, 10);
815 if (*tail) break;
816 if (!font_mem_add(slots[0], dir, slots[2], size, slots[4]))
817 { // Memory failure
818 font_mem_clear();
819 return;
820 }
821 }
822 }
823
824 #if 0
825 static void font_index_display(struct fontNODE *head)
826 {
827 int families=0, styles=0, sizes=0, filenames=0;
828 fontNODE *fo = head;
829 styleNODE *st;
830 sizeNODE *ze;
831 filenameNODE *fl;
832 size_t nspace = 0, sspace = 0;
833
834
835 while (fo)
836 {
837 printf("%s (%i)\n", fo->font_name, fo->directory);
838 nspace += strlen(fo->font_name) + 1;
839 sspace += sizeof(*fo);
840 families ++;
841 st = fo->style;
842 while (st)
843 {
844 printf("\t%s\n", st->style_name);
845 nspace += strlen(st->style_name) + 1;
846 sspace += sizeof(*st);
847 styles ++;
848 ze = st->size;
849 while (ze)
850 {
851 printf("\t\t%i x %i\n", ze->size % (1<<SIZE_SHIFT),
852 (ze->size >> SIZE_SHIFT) % (1<<SIZE_SHIFT)
853 );
854 sspace += sizeof(*ze);
855 sizes ++;
856 fl = ze->filename;
857 while (fl)
858 {
859 printf("\t\t\t%3i %s\n", fl->face_index, fl->filename);
860 nspace += strlen(fl->filename) + 1;
861 sspace += sizeof(*fl);
862 filenames++;
863 fl = fl->next;
864 }
865 ze = ze->next;
866 }
867 st = st->next;
868 }
869 fo = fo->next;
870 }
871 printf("\nMemory Used\t%'zu + %'zu (%.1fK)\n"
872 "Font Families\t%i\nFont Styles\t%i\nFont Sizes\t%i\nFont Filenames\t%i\n\n",
873 nspace, sspace, (double)(nspace + sspace) / 1024,
874 families, styles, sizes, filenames);
875 }
876 #endif
877
878 /*
879 -----------------------------------------------------------------
880 | GTK+ Front End Code |
881 -----------------------------------------------------------------
882 */
883
884
render_to_1bpp(int * w,int * h)885 static unsigned char *render_to_1bpp(int *w, int *h)
886 {
887 double angle = 0;
888 unsigned char *text_1bpp;
889 int flags = 0, size = 1;
890
891
892 size = inifile_get_gboolean("fontTypeBitmap", TRUE) ?
893 font_bmsize : font_size;
894
895 if ((mem_img_bpp == 1) || !font_aa)
896 {
897 flags |= MT_TEXT_MONO;
898 flags |= MT_TEXT_ROTATE_NN; // RGB image without AA = nearest neighbour rotation
899 }
900 if (font_obl) flags |= MT_TEXT_OBLIQUE;
901 if (font_r) angle = font_angle / 100.0;
902
903 text_1bpp = mt_text_render(
904 inifile_get( "textString", "" ),
905 strlen( inifile_get( "textString", "" ) ),
906 inifile_get( "lastTextFilename", "" ),
907
908 #if GTK_MAJOR_VERSION == 1
909 #ifdef U_NLS
910 nl_langinfo(CODESET),
911 // this only works on international version of mtPaint, as setlocale is needed
912 #else
913 "ISO-8859-1", // Non-international verson so it must be this
914 #endif
915 #else /* if GTK_MAJOR_VERSION >= 2 */
916 "UTF-8",
917 #endif
918
919 size,
920 inifile_get_gint32( "lastTextFace", 0 ),
921 angle, flags, w, h );
922
923 return text_1bpp;
924 }
925
926 /* Re-render the preview text and update it */
font_preview_update(font_dd * dt)927 static void font_preview_update(font_dd *dt)
928 {
929 unsigned char *text_1bpp;
930 int w=1, h=1;
931
932
933 if (script_cmds) return; // Not visible anyway
934 if (dt->preview_rgb) // Remove old rendering
935 {
936 free(dt->preview_rgb);
937 dt->preview_rgb = NULL;
938 }
939
940 text_1bpp = render_to_1bpp(&w, &h);
941
942 if ( text_1bpp )
943 {
944 dt->preview_rgb = calloc( 1, 3*w*h );
945 if (dt->preview_rgb)
946 {
947 int i, j = w*h;
948 unsigned char *src = text_1bpp, *dest = dt->preview_rgb;
949
950 for ( i=0; i<j; i++ )
951 {
952 dest[0] = dest[1] = dest[2] = *src++ ^ 255;
953 dest += 3;
954 }
955
956 dt->preview_whc[0] = w;
957 dt->preview_whc[1] = h;
958 }
959 free( text_1bpp );
960 //printf("font preview update %i x %i\n", w, h);
961 }
962
963 cmd_reset(dt->preview_area, dt);
964 // Show the world the fruits of my hard labour! ;-)
965 }
966
967
968
font_gui_create_index(char * filename)969 static void font_gui_create_index(char *filename) // Create index file with settings from ~/.mtpaint
970 {
971 char buf[128], *dirs[TX_MAX_DIRS + 1];
972 int i;
973
974
975 memset(dirs, 0, sizeof(dirs));
976 for (i = 0; i < font_dirs; i++)
977 {
978 snprintf(buf, 128, "font_dir%i", i);
979 dirs[i] = inifile_get( buf, "" );
980 }
981
982 progress_init(_("Creating Font Index"), 0);
983 font_index_create( filename, dirs );
984 progress_end();
985 }
986
987
ft_render_text()988 void ft_render_text() // FreeType equivalent of render_text()
989 {
990 unsigned char *text_1bpp;
991 int w=1, h=1;
992
993
994 text_1bpp = render_to_1bpp(&w, &h);
995
996 if (text_1bpp && make_text_clipboard(text_1bpp, w, h, 1))
997 text_paste = TEXT_PASTE_FT;
998 else text_paste = TEXT_PASTE_NONE;
999 }
1000
store_values(font_dd * dt)1001 static void store_values(font_dd *dt)
1002 {
1003 inifile_set("textString", dt->text);
1004 if (inifile_get_gboolean( "fontTypeBitmap", TRUE))
1005 font_bmsize = dt->fontsize;
1006 else font_size = dt->fontsize;
1007 if (mem_channel == CHN_IMAGE)
1008 {
1009 if (!script_cmds || (font_bk = dt->bkg[0] >= 0))
1010 font_bkg = dt->bkg[0];
1011 }
1012 if (!script_cmds || (font_r = !!dt->angle))
1013 font_angle = dt->angle;
1014 if (!script_cmds || (ft_setdpi = !!dt->dpi[0]))
1015 font_dpi = dt->dpi[0];
1016 font_spacing = dt->spacing;
1017 }
1018
paste_text_ok(font_dd * dt,void ** wdata,int what,void ** where)1019 static void paste_text_ok(font_dd *dt, void **wdata, int what, void **where)
1020 {
1021 run_query(wdata);
1022 store_values(dt);
1023 ft_render_text();
1024 if (mem_clipboard) pressed_paste(TRUE);
1025 run_destroy(wdata);
1026 }
1027
collect_fontnames(font_dd * dt)1028 static void collect_fontnames(font_dd *dt)
1029 {
1030 memx2 mem = dt->fnmmem;
1031 char buf2[256];
1032 fontNODE *fn;
1033 fontname_cc *fc;
1034 char *last_font_name = inifile_get("lastFontName", "");
1035 int last_font_name_dir = inifile_get_gint32("lastFontNameDir", 0),
1036 last_font_name_bitmap = inifile_get_gint32("lastFontNameBitmap", 0);
1037 int ofs, dir, cnt, b, strs[TX_MAX_DIRS];
1038
1039 /* Gather up nodes */
1040 for (fn = global_font_node , cnt = 0; fn; fn = fn->next) cnt++;
1041 dt->fontname = -1;
1042 memset(strs, 0, sizeof(strs));
1043 mem.here = 0;
1044 getmemx2(&mem, 8000); // default size
1045 b = mem.here += getmemx2(&mem, cnt * sizeof(fontname_cc)); // minimum size
1046 addstr(&mem, "B", 0);
1047 for (fn = global_font_node , cnt = 0; fn; fn = fn->next)
1048 {
1049 if ((fn->directory < 0) || (fn->directory >= TX_MAX_DIRS))
1050 continue;
1051 /* Trying to remember start row */
1052 if (!strcmp(fn->font_name, last_font_name) &&
1053 (last_font_name_dir == fn->directory) &&
1054 (last_font_name_bitmap == !!fn->style->size->size))
1055 dt->fontname = cnt;
1056 /* Prepare dir index */
1057 dir = fn->directory;
1058 if (!strs[dir])
1059 {
1060 strs[dir] = mem.here;
1061 sprintf(buf2, "%3d", dir + 1);
1062 addstr(&mem, buf2, 0);
1063 }
1064 dir = strs[dir];
1065 /* Convert name string (to UTF-8 in GTK+2) and store it */
1066 ofs = mem.here;
1067 /* Label bitmap/scalable fonts for scripting */
1068 if (script_cmds)
1069 addstr(&mem, fn->style->size->size ? "B " : "S ", 1);
1070 gtkuncpy(buf2, fn->font_name, sizeof(buf2));
1071 addstr(&mem, buf2, 0);
1072 /* Add a row - with offsets for now */
1073 fc = (fontname_cc *)mem.buf + cnt;
1074 fc->fn = fn;
1075 fc->dir = dir - ((char *)&fc->dir - mem.buf);
1076 // "B" if has size (is bitmap), "" otherwise
1077 fc->bm = (fn->style->size->size ? b : b + 1) -
1078 ((char *)&fc->bm - mem.buf);
1079 fc->name = ofs - ((char *)&fc->name - mem.buf);
1080 cnt++;
1081 }
1082 dt->nfontnames = cnt;
1083
1084 /* Allocations done - now set up pointers */
1085 dt->fontnames = (void *)mem.buf; // won't change now
1086
1087 /* Save allocator data */
1088 dt->fnmmem = mem;
1089 }
1090
collect_fontstyles(font_dd * dt)1091 static void collect_fontstyles(font_dd *dt)
1092 {
1093 static const char *default_styles[] =
1094 { "Regular", "Medium", "Book", "Roman", NULL };
1095 char *last_font_style = inifile_get("lastFontStyle", "");
1096 char buf2[256];
1097 styleNODE *sn;
1098 fontstyle_cc *fc;
1099 memx2 mem = dt->fstmem;
1100 int i, ofs, cnt, default_row = -1;
1101
1102 /* Gather up nodes */
1103 sn = dt->fontnames[dt->fontname].fn->style;
1104 for (cnt = 0; sn; sn = sn->next) cnt++;
1105 dt->fontstyle = -1;
1106 mem.here = 0;
1107 getmemx2(&mem, 4000); // default size
1108 mem.here += getmemx2(&mem, cnt * sizeof(fontstyle_cc)); // minimum size
1109 sn = dt->fontnames[dt->fontname].fn->style;
1110 for (cnt = 0; sn; sn = sn->next)
1111 {
1112 /* Trying to remember start row */
1113 if (!strcmp(sn->style_name, last_font_style))
1114 dt->fontstyle = cnt;
1115 else for (i = 0; default_styles[i]; i++)
1116 {
1117 if (!strcmp(sn->style_name, default_styles[i]))
1118 default_row = cnt;
1119 }
1120 /* Convert name string (to UTF-8 in GTK+2) and store it */
1121 ofs = mem.here;
1122 gtkuncpy(buf2, sn->style_name, sizeof(buf2));
1123 addstr(&mem, buf2, 0);
1124 /* Add a row - with offset for now */
1125 fc = (fontstyle_cc *)mem.buf + cnt;
1126 fc->sn = sn;
1127 fc->name = ofs - ((char *)&fc->name - mem.buf);
1128 cnt++;
1129 }
1130 /* Use default if last style not found */
1131 if (dt->fontstyle < 0) dt->fontstyle = default_row;
1132 dt->nfontstyles = cnt;
1133
1134 /* Allocations done - now set up pointers */
1135 dt->fontstyles = (void *)mem.buf; // won't change now
1136
1137 /* Save allocator data */
1138 dt->fstmem = mem;
1139 }
1140
collect_fontsizes(font_dd * dt)1141 static void collect_fontsizes(font_dd *dt)
1142 {
1143 static const unsigned char sizes[] = {
1144 8, 9, 10, 11, 12, 13, 14, 16, 18, 20,
1145 22, 24, 26, 28, 32, 36, 40, 48, 56, 64, 72 };
1146 char buf2[256];
1147 sizeNODE *zn;
1148 fontsize_cc *fc;
1149 memx2 mem = dt->fszmem;
1150 int i, cnt;
1151
1152 /* Gather up nodes */
1153 zn = dt->fontstyles[dt->fontstyle].sn->size;
1154 dt->lfontsize = -1;
1155 mem.here = 0;
1156 getmemx2(&mem, sizeof(sizes) * sizeof(fontsize_cc) > 4000 ?
1157 sizeof(sizes) * sizeof(fontsize_cc) : 4000); // default size
1158 if (zn && !zn->size)
1159 {
1160 mem.here += sizeof(sizes) * sizeof(fontsize_cc);
1161 for (i = 0; i < sizeof(sizes); i++)
1162 {
1163 int ofs, n = sizes[i];
1164 /* Trying to remember start row */
1165 if (n == font_size) dt->lfontsize = i;
1166 /* Prepare text */
1167 ofs = mem.here;
1168 sprintf(buf2, "%2d", n);
1169 addstr(&mem, buf2, 0);
1170 /* Add a row - with offset for now */
1171 fc = (fontsize_cc *)mem.buf + i;
1172 fc->zn = zn;
1173 fc->n = n;
1174 fc->what = ofs - ((char *)&fc->what - mem.buf);
1175 }
1176 cnt = sizeof(sizes);
1177 }
1178 else
1179 {
1180 int geom = inifile_get_gint32("lastfontBitmapGeometry", 0);
1181
1182 for (cnt = 0; zn; zn = zn->next) cnt++;
1183 mem.here += getmemx2(&mem, cnt * sizeof(fontsize_cc)); // minsize
1184 zn = dt->fontstyles[dt->fontstyle].sn->size;
1185 for (cnt = 0; zn; zn = zn->next)
1186 {
1187 int ofs, n = zn->size;
1188 /* Trying to remember start row */
1189 if (geom == n) dt->lfontsize = cnt;
1190 /* Prepare text */
1191 ofs = mem.here;
1192 snprintf(buf2, 32, "%2d x %2d",
1193 i = (n >> SIZE_SHIFT) & ((1 << SIZE_SHIFT) - 1),
1194 n & ((1 << SIZE_SHIFT) - 1));
1195 addstr(&mem, buf2, 0);
1196 /* Add a row - with offset for now */
1197 fc = (fontsize_cc *)mem.buf + cnt;
1198 fc->zn = zn;
1199 fc->n = 0; // !!! or maybe zn->size?
1200 fc->what = ofs - ((char *)&fc->what - mem.buf);
1201 cnt++;
1202 }
1203 }
1204 dt->nlfontsizes = cnt;
1205
1206 /* Allocations done - now set up pointers */
1207 dt->fontsizes = (void *)mem.buf; // won't change now
1208
1209 /* Save allocator data */
1210 dt->fszmem = mem;
1211 }
1212
collect_fontfiles(font_dd * dt)1213 static void collect_fontfiles(font_dd *dt)
1214 {
1215 char *last_filename = inifile_get("lastTextFilename", "");
1216 char buf2[256];
1217 filenameNODE *fn;
1218 fontfile_cc *fc;
1219 memx2 mem = dt->ffnmem;
1220 int cnt;
1221
1222 /* Gather up nodes */
1223 fn = dt->fontsizes[dt->lfontsize].zn->filename;
1224 for (cnt = 0; fn; fn = fn->next) cnt++;
1225 dt->fontfile = -1;
1226 mem.here = 0;
1227 getmemx2(&mem, 4000); // default size
1228 mem.here += getmemx2(&mem, cnt * sizeof(fontfile_cc)); // minimum size
1229 fn = dt->fontsizes[dt->lfontsize].zn->filename;
1230 for (cnt = 0; fn; fn = fn->next)
1231 {
1232 char *s, *nm = fn->filename;
1233 int ofs1, ofs2;
1234
1235 /* Trying to remember start row */
1236 if (!strcmp(nm, last_filename)) dt->fontfile = cnt;
1237 /* Convert name string (to UTF-8 in GTK+2) and store it */
1238 s = strrchr(nm, DIR_SEP);
1239 s = s ? s + 1 : nm;
1240 ofs1 = mem.here;
1241 gtkuncpy(buf2, s, sizeof(buf2));
1242 addstr(&mem, buf2, 0);
1243 /* Store index string */
1244 sprintf(buf2, "%3d", fn->face_index);
1245 ofs2 = mem.here;
1246 addstr(&mem, buf2, 0);
1247 /* Add a row - with offsets for now */
1248 fc = (fontfile_cc *)mem.buf + cnt;
1249 fc->fn = fn;
1250 fc->name = ofs1 - ((char *)&fc->name - mem.buf);
1251 fc->face = ofs2 - ((char *)&fc->face - mem.buf);
1252 cnt++;
1253 }
1254 dt->nfontfiles = cnt;
1255
1256 /* Allocations done - now set up pointers */
1257 dt->fontfiles = (void *)mem.buf; // won't change now
1258
1259 /* Save allocator data */
1260 dt->ffnmem = mem;
1261 }
1262
collect_dirnames(font_dd * dt)1263 static void collect_dirnames(font_dd *dt)
1264 {
1265 memx2 mem = dt->dirmem;
1266 char buf2[256];
1267 dir_cc *fc;
1268 int i;
1269
1270 /* Gather up nodes */
1271 mem.here = 0;
1272 getmemx2(&mem, 4000); // default size
1273 mem.here += getmemx2(&mem, font_dirs * sizeof(dir_cc)); // minimum size
1274 for (i = 0; i < font_dirs; i++)
1275 {
1276 int ofs1, ofs2;
1277 /* Prepare dir index */
1278 ofs1 = mem.here;
1279 sprintf(buf2, "%3d", i + 1);
1280 addstr(&mem, buf2, 0);
1281 /* Convert name string (to UTF-8 in GTK+2) and store it */
1282 ofs2 = mem.here;
1283 snprintf(buf2, 128, "font_dir%i", i);
1284 gtkuncpy(buf2, inifile_get(buf2, ""), sizeof(buf2));
1285 addstr(&mem, buf2, 0);
1286 /* Add a row - with offsets for now */
1287 fc = (dir_cc *)mem.buf + i;
1288 fc->idx = ofs1 - ((char *)&fc->idx - mem.buf);
1289 fc->dir = ofs2 - ((char *)&fc->dir - mem.buf);
1290 }
1291 dt->ndirs = font_dirs;
1292
1293 /* Allocations done - now set up pointers */
1294 dt->dirs = (void *)mem.buf; // won't change now
1295
1296 /* Save allocator data */
1297 dt->dirmem = mem;
1298 }
1299
1300
click_font_dir_btn(font_dd * dt,void ** wdata,int what,void ** where)1301 static void click_font_dir_btn(font_dd *dt, void **wdata, int what, void **where)
1302 {
1303 int i, rows = font_dirs;
1304 char buf[32], buf2[32];
1305
1306 run_query(wdata);
1307 if (origin_slot(where) != dt->add_b) // Remove row
1308 {
1309 if (!rows) return; // Nothing to delete
1310 rows--;
1311 // Re-work inifile items
1312 for (i = dt->dir; i < rows; i++)
1313 {
1314 snprintf(buf, sizeof(buf), "font_dir%i", i);
1315 snprintf(buf2, sizeof(buf2), "font_dir%i", i + 1);
1316 inifile_set(buf, inifile_get(buf2, ""));
1317 }
1318 // !!! List is sorted by index, so selected row == selected index
1319 if (dt->dir >= rows) dt->dir--;
1320 }
1321 else // Add row
1322 {
1323 if (!dt->dirp[0] || (rows >= TX_MAX_DIRS))
1324 return; // Cannot add
1325 snprintf(buf, sizeof(buf), "font_dir%i", rows++);
1326 inifile_set(buf, dt->dirp);
1327 }
1328 font_dirs = rows;
1329 collect_dirnames(dt);
1330 cmd_reset(dt->dir_l, dt);
1331 }
1332
1333 static void select_font(font_dd *dt, void **wdata, int what, void **where);
1334
click_create_font_index(font_dd * dt,void ** wdata)1335 static void click_create_font_index(font_dd *dt, void **wdata)
1336 {
1337 if (font_dirs > 0)
1338 {
1339 char txt[PATHBUF];
1340
1341 file_in_homedir(txt, FONT_INDEX_FILENAME, PATHBUF);
1342 font_gui_create_index(txt); // Create new index file
1343
1344 dt->lock = TRUE; // Paranoia
1345 font_mem_clear(); // Empty memory of current nodes
1346 font_index_load(txt);
1347 collect_fontnames(dt);
1348 cmd_reset(dt->fname_l, dt);
1349 dt->lock = FALSE;
1350 select_font(dt, wdata, op_EVT_SELECT, dt->fname_l); // reselect
1351 }
1352 else alert_box(_("Error"),
1353 _("You must select at least one directory to search for fonts."), NULL);
1354 }
1355
select_font(font_dd * dt,void ** wdata,int what,void ** where)1356 static void select_font(font_dd *dt, void **wdata, int what, void **where)
1357 {
1358 void *cause;
1359 fontNODE *fn;
1360 styleNODE *sn;
1361 fontsize_cc *fc;
1362 filenameNODE *nn;
1363 int bitmap_font, idx;
1364
1365
1366 cause = cmd_read(where, dt);
1367 if (dt->lock || !dt->nfontnames) return;
1368 dt->lock = TRUE;
1369
1370 idx = cause == &dt->fontname ? 0 : cause == &dt->fontstyle ? 1 :
1371 cause == &dt->lfontsize ? 2 : 3; /* cause == &dt->fontfile */
1372 switch (idx)
1373 {
1374 case 0: // Name list
1375 fn = dt->fontnames[dt->fontname].fn;
1376 bitmap_font = !!fn->style->size->size;
1377
1378 inifile_set_gboolean("fontTypeBitmap", bitmap_font);
1379 cmd_sensitive(dt->obl_c, !bitmap_font);
1380
1381 /* Update style list */
1382 collect_fontstyles(dt);
1383 cmd_reset(dt->fstyle_l, dt);
1384
1385 inifile_set("lastFontName", fn->font_name);
1386 inifile_set_gint32("lastFontNameDir", fn->directory);
1387 inifile_set_gint32("lastFontNameBitmap", bitmap_font);
1388 /* Fallthrough */
1389 case 1: // Style list
1390 sn = dt->fontstyles[dt->fontstyle].sn;
1391
1392 /* Update size list */
1393 collect_fontsizes(dt);
1394 cmd_reset(dt->fsize_l, dt);
1395
1396 inifile_set("lastFontStyle", sn->style_name);
1397 /* Fallthrough */
1398 case 2: // Size list
1399 fc = dt->fontsizes + dt->lfontsize;
1400
1401 // Scalable so remember size
1402 if (!fc->zn->size)
1403 {
1404 // Only if it really was reselected
1405 if (idx == 2) font_size = fc->n;
1406 }
1407 // Non-Scalable so remember index
1408 else inifile_set_gint32("lastfontBitmapGeometry", fc->zn->size);
1409
1410 /* Update file list */
1411 collect_fontfiles(dt);
1412 cmd_reset(dt->ffile_l, dt);
1413 /* Fallthrough */
1414 case 3: // File list
1415 nn = dt->fontfiles[dt->fontfile].fn;
1416
1417 inifile_set("lastTextFilename", nn->filename);
1418 inifile_set_gint32("lastTextFace", nn->face_index);
1419 break;
1420 }
1421
1422 /* Update size */
1423 dt->fontsize = dt->fontsizes[dt->lfontsize].zn->size ? // nonzero for bitmaps
1424 font_bmsize : font_size;
1425 cmd_reset(dt->size_spin, dt);
1426
1427 dt->lock = FALSE;
1428
1429 font_preview_update(dt); // Update the font preview area
1430 }
1431
1432
init_font_lists()1433 static void init_font_lists() // LIST INITIALIZATION
1434 {
1435 char txt[PATHBUF];
1436
1437 /* Get font directories if we don't have any */
1438 if (font_dirs <= 0)
1439 {
1440 #ifdef WIN32
1441 int new_dirs = 1;
1442 char *windir = getenv("WINDIR");
1443
1444 file_in_dir(txt, windir && *windir ? windir : "C:\\WINDOWS",
1445 "Fonts", PATHBUF);
1446 inifile_set("font_dir0", txt);
1447 #else
1448 int new_dirs = 0;
1449 FILE *fp;
1450 char buf[4096], buf2[128], *s;
1451
1452 if (!(fp = fopen("/etc/X11/xorg.conf", "r")))
1453 fp = fopen("/etc/X11/XF86Config", "r");
1454
1455 // If these files are not found the user will have to manually enter directories
1456
1457 if (fp)
1458 {
1459 while (fgets(buf, 4090, fp))
1460 {
1461 s = strstr(buf, "FontPath");
1462 if (!s) continue;
1463
1464 s = strstr(buf, ":unscaled\"");
1465 if (!s) s = strrchr(buf, '"');
1466 if (!s) continue;
1467 *s = '\0';
1468
1469 s = strchr(buf, '"');
1470 if (!s) continue;
1471
1472 snprintf(buf2, 128, "font_dir%i", new_dirs);
1473 inifile_set(buf2, s + 1);
1474 if (++new_dirs >= TX_MAX_DIRS) break;
1475 }
1476 fclose(fp);
1477 }
1478
1479 if (!new_dirs && (fp = fopen("/etc/fonts/fonts.conf", "r")))
1480 {
1481 char *s1, *s2;
1482
1483 for (s1 = NULL; s1 || (s1 = fgets(buf, 4090, fp)); s1 = s2)
1484 {
1485 s2 = strstr(s1, "</dir>");
1486 if (!s2) continue;
1487 *s2 = '\0';
1488 s2 += 6;
1489
1490 s = strstr(s1, "<dir>");
1491 if (!s) continue;
1492
1493 snprintf(buf2, 128, "font_dir%i", new_dirs);
1494 inifile_set(buf2, s + 5);
1495 if (++new_dirs >= TX_MAX_DIRS) break;
1496 }
1497 fclose(fp);
1498 }
1499
1500 /* Add user's font directory */
1501 file_in_homedir(txt, ".fonts", PATHBUF);
1502 snprintf(buf2, 128, "font_dir%i", new_dirs++);
1503 inifile_set(buf2, txt);
1504 #endif
1505 font_dirs = new_dirs;
1506 }
1507
1508 file_in_homedir(txt, FONT_INDEX_FILENAME, PATHBUF);
1509 font_index_load(txt); // Does a valid ~/.mtpaint_fonts index exist?
1510 if (!global_font_node) // Index file not loaded
1511 {
1512 font_gui_create_index(txt);
1513 font_index_load(txt); // Try for second and last time
1514 }
1515 }
1516
1517
font_entry_changed(font_dd * dt,void ** wdata,int what,void ** where)1518 static void font_entry_changed(font_dd *dt, void **wdata, int what, void **where)
1519 {
1520 if (cmd_read(where, dt) == &ft_setdpi) cmd_set(dt->book, ft_setdpi);
1521 if (dt->lock) return;
1522
1523 store_values(dt);
1524 font_preview_update(dt); // Update the font preview area
1525 }
1526
1527 #define WBbase font_dd
1528 static void *font_code[] = {
1529 WPWHEREVER, WINDOWm(_("Paste Text")),
1530 WXYWH("paste_text_window", 400, 450),
1531 BORDER(NBOOK, 0), NBOOK, // !!! Originally was in window directly
1532 // TAB 1 - TEXT
1533 PAGE(_("Text")),
1534 XHBOX, // !!! Originally the page was an hbox
1535 VBOXP, // !!! what for?
1536 XSCROLL(0, 1), // never/auto
1537 WLIST, OPNAME0,
1538 RTXTCOLUMND(fontname_cc, dir, 0, 0),
1539 RTXTCOLUMND(fontname_cc, bm, 0, 0),
1540 NRTXTCOLUMND(_("Font"), fontname_cc, name, 0, 0),
1541 COLUMNDATA(fontnames, sizeof(fontname_cc)), CLEANUP(fontnames),
1542 REF(fname_l), LISTCS(fontname, nfontnames, fnsort, select_font), TRIGGER,
1543 WDONE, // VBOXP
1544 XVBOXP,
1545 XHBOXP,
1546 XSCROLL(1, 1), // auto/auto
1547 WLIST,
1548 NRTXTCOLUMND(_("Style"), fontstyle_cc, name, 0, 0),
1549 COLUMNDATA(fontstyles, sizeof(fontstyle_cc)), CLEANUP(fontstyles),
1550 REF(fstyle_l), WIDTH(100), LISTC(fontstyle, nfontstyles, select_font),
1551 XVBOX,
1552 BORDER(SCROLL, 0),
1553 XSCROLL(1, 1), // auto/auto
1554 DEFBORDER(SCROLL),
1555 WLIST,
1556 NRTXTCOLUMND(_("Size"), fontsize_cc, what, 0, 1), // centered
1557 OPNAME("Bitmap size"), // scalable fonts use the spin
1558 COLUMNDATA(fontsizes, sizeof(fontsize_cc)), CLEANUP(fontsizes),
1559 REF(fsize_l), LISTC(lfontsize, nlfontsizes, select_font),
1560 BORDER(SPIN, 0),
1561 REF(size_spin), SPINc(fontsize, 1, 500), EVENT(CHANGE, font_entry_changed),
1562 DEFBORDER(SPIN), OPNAME("Size"),
1563 WDONE, // XVBOX
1564 XSCROLL(1, 1), // auto/auto
1565 WLIST,
1566 NRTXTCOLUMND(_("Filename"), fontfile_cc, name, 0, 0),
1567 NRTXTCOLUMND(_("Face"), fontfile_cc, face, 0, 0),
1568 COLUMNDATA(fontfiles, sizeof(fontfile_cc)), CLEANUP(fontfiles),
1569 REF(ffile_l), LISTC(fontfile, nfontfiles, select_font),
1570 WDONE, // XHBOXP
1571 // Text entry box
1572 FVBOXB(_("Text")), // !!! Originally was hbox
1573 BORDER(ENTRY, 0),
1574 MLENTRY(text), EVENT(CHANGE, font_entry_changed), FOCUS,
1575 WDONE,
1576 // PREVIEW AREA
1577 FXVBOXB(_("Preview")), // !!! Originally was hbox
1578 BORDER(SCROLL, 0),
1579 XSCROLL(1, 1), // auto/auto
1580 DEFBORDER(SCROLL),
1581 REF(preview_area), CANVASIMGB(preview_rgb, preview_whc),
1582 CLEANUP(preview_rgb),
1583 WDONE, // FXVBOXB
1584 // TOGGLES
1585 HBOX,
1586 UNLESSx(idx, 1),
1587 CHECKv(_("Antialias"), font_aa), EVENT(CHANGE, font_entry_changed),
1588 ENDIF(1),
1589 UNLESSx(img, 1),
1590 CHECKv(_("Invert"), font_bk), EVENT(CHANGE, font_entry_changed),
1591 ENDIF(1),
1592 IFx(img, 1), UNLESSx(script, 1), /* && */
1593 CHECKv(_("Background colour ="), font_bk),
1594 EVENT(CHANGE, font_entry_changed),
1595 ENDIF(1),
1596 IFx(img, 1),
1597 SPINa(bkg), EVENT(CHANGE, font_entry_changed),
1598 OPNAME("Background colour ="),
1599 ENDIF(1),
1600 UNLESSx(script, 1),
1601 CHECKv(_("DPI ="), ft_setdpi),
1602 EVENT(CHANGE, font_entry_changed), TRIGGER,
1603 ENDIF(1),
1604 REF(book), PLAINBOOK,
1605 NOSPINv(sys_dpi), WDONE, // page 0
1606 SPINa(dpi), EVENT(CHANGE, font_entry_changed), OPNAME("DPI ="),
1607 WDONE, // page 1
1608 WDONE, // HBOX
1609 HBOX,
1610 REF(obl_c), CHECKv(_("Oblique"), font_obl), EVENT(CHANGE, font_entry_changed),
1611 UNLESSx(script, 1),
1612 CHECKv(_("Angle of rotation ="), font_r),
1613 EVENT(CHANGE, font_entry_changed),
1614 ENDIF(1),
1615 FSPIN(angle, -36000, 36000), EVENT(CHANGE, font_entry_changed),
1616 OPNAME("Angle of rotation ="),
1617 MLABEL(_("Align")), OPTve(align_txt, 3, font_align, font_entry_changed),
1618 WDONE, // HBOX
1619 HBOX,
1620 MLABEL(_("Spacing")), FSPIN(spacing, -MAX_WIDTH * 100, MAX_WIDTH * 100),
1621 EVENT(CHANGE, font_entry_changed),
1622 WDONE, // HBOX
1623 HSEPl(200),
1624 OKBOXP(_("Paste Text"), paste_text_ok, _("Close"), NULL), WDONE,
1625 WDONE, WDONE, WDONE, // XVBOXP, XHBOX, PAGE
1626 /* !!! PATH isn't scriptable, and not much use to add dirs from script */
1627 ENDSCRIPT,
1628 // TAB 2 - DIRECTORIES
1629 PAGE(_("Font Directories")),
1630 // VBOX, // !!! utterly useless
1631 XSCROLL(1, 1), // auto/auto
1632 WLIST,
1633 RTXTCOLUMND(dir_cc, idx, 0, 0),
1634 NRTXTCOLUMND(_("Directory"), dir_cc, dir, 0, 0),
1635 COLUMNDATA(dirs, sizeof(dir_cc)), CLEANUP(dirs),
1636 REF(dir_l), LISTC(dir, ndirs, NULL),
1637 PATH(_("New Directory"), _("Select Directory"), FS_SELECT_DIR, dirp),
1638 HSEPl(200),
1639 HBOXP,
1640 /* !!! Keyboard shortcut doesn't work for invisible buttons in GTK+, and
1641 * doubled handlers of window close don't matter - destructor called by
1642 * the first one removes the other before it runs - WJ */
1643 CANCELBTN(_("Close"), NULL),
1644 REF(add_b), BUTTON(_("Add"), click_font_dir_btn),
1645 BUTTON(_("Remove"), click_font_dir_btn),
1646 BUTTON(_("Create Index"), click_create_font_index),
1647 WSHOW
1648 };
1649 #undef WBbase
1650
pressed_mt_text()1651 void pressed_mt_text()
1652 {
1653 font_dd tdata;
1654
1655 if (!global_font_node) init_font_lists();
1656 cmd_peekv(main_window_, &sys_dpi, sizeof(sys_dpi), WINDOW_DPI);
1657
1658 memset(&tdata, 0, sizeof(tdata));
1659 tdata.fontsize = font_size; // !!! is reset
1660 tdata.text = inifile_get("textString", __("Enter Text Here"));
1661 tdata.bkg[0] = font_bkg % mem_cols;
1662 tdata.bkg[1] = 0;
1663 tdata.bkg[2] = mem_cols - 1;
1664 tdata.angle = font_angle;
1665 tdata.preview_whc[0] = tdata.preview_whc[1] = 1;
1666 tdata.preview_whc[2] = mem_background * 0x010101;
1667 tdata.img = mem_channel == CHN_IMAGE;
1668 tdata.idx = tdata.img && (mem_img_bpp == 1);
1669 tdata.dpi[0] = font_dpi;
1670 tdata.dpi[1] = 1;
1671 tdata.dpi[2] = 65535;
1672 tdata.spacing = font_spacing;
1673 if (script_cmds) // Simplified controls - spins w/o toggles
1674 {
1675 tdata.script = TRUE;
1676 if (!font_bk) tdata.bkg[0] = -1;
1677 tdata.bkg[1] = -1;
1678 if (!font_r) tdata.angle = 0;
1679 if (!ft_setdpi) tdata.dpi[0] = 0;
1680 tdata.dpi[1] = 0;
1681 }
1682 collect_fontnames(&tdata);
1683 tdata.fnsort = 3; // By name column, ascending
1684 collect_dirnames(&tdata);
1685 tdata.dir = -1; // Top sorted
1686
1687 run_create_(font_code, &tdata, sizeof(tdata), script_cmds);
1688 }
1689
1690 #endif /* U_FREETYPE */
1691