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