1 /*
2  * Copyright 2005 James Bursa <bursa@users.sourceforge.net>
3  *           2008 Vincent Sanders <vince@simtec.co.uk>
4  *
5  * This file is part of NetSurf, http://www.netsurf-browser.org/
6  *
7  * NetSurf is free software; you can redistribute it and/or modify
8  * it under the terms of the GNU General Public License as published by
9  * the Free Software Foundation; version 2 of the License.
10  *
11  * NetSurf 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 this program.  If not, see <http://www.gnu.org/licenses/>.
18  */
19 
20 #include <inttypes.h>
21 #include <string.h>
22 #include <assert.h>
23 #include <stdlib.h>
24 
25 #include "utils/nsoption.h"
26 #include "utils/utf8.h"
27 #include "netsurf/utf8.h"
28 #include "netsurf/layout.h"
29 #include "netsurf/plot_style.h"
30 
31 #include "framebuffer/gui.h"
32 #include "framebuffer/font.h"
33 
34 #include <font-ns-sans.h>
35 
36 #define GLYPH_LEN		16
37 
38 uint8_t code_point[GLYPH_LEN];
39 uint8_t glyph_x2[GLYPH_LEN * 4];
40 
41 #define SEVEN_SET	((1 << 0) | (1 << 1) | (1 << 2) | (1 << 3) | 	\
42 			 (1 << 4) | (1 << 5) | (1 << 6))
43 
44 #define THREE_SSS	 ((1 << 0) | (1 << 1) | (1 << 2))
45 #define THREE_S_S	 ((1 << 0) |            (1 << 2))
46 #define THREE__SS	 ((1 << 0) | (1 << 1)           )
47 #define THREE_SS_	 (           (1 << 1) | (1 << 2))
48 #define THREE_S__	                        (1 << 2)
49 #define THREE__S_	             (1 << 1)
50 #define THREE___S	  (1 << 0)
51 
52 uint8_t frag[16][5] = {
53 	{ THREE_SSS,
54 	  THREE_S_S,
55 	  THREE_S_S,
56 	  THREE_S_S,
57 	  THREE_SSS },
58 
59 	{ THREE__S_,
60 	  THREE_SS_,
61 	  THREE__S_,
62 	  THREE__S_,
63 	  THREE_SSS },
64 
65 	{ THREE_SS_,
66 	  THREE___S,
67 	  THREE__S_,
68 	  THREE_S__,
69 	  THREE_SSS },
70 
71 	{ THREE_SS_,
72 	  THREE___S,
73 	  THREE_SS_,
74 	  THREE___S,
75 	  THREE_SS_ },
76 
77 	{ THREE_S_S,
78 	  THREE_S_S,
79 	  THREE_SSS,
80 	  THREE___S,
81 	  THREE___S },
82 
83 	{ THREE_SSS,
84 	  THREE_S__,
85 	  THREE_SSS,
86 	  THREE___S,
87 	  THREE_SSS },
88 
89 	{ THREE__SS,
90 	  THREE_S__,
91 	  THREE_SSS,
92 	  THREE_S_S,
93 	  THREE_SSS },
94 
95 	{ THREE_SSS,
96 	  THREE___S,
97 	  THREE__S_,
98 	  THREE__S_,
99 	  THREE__S_ },
100 
101 	{ THREE_SSS,
102 	  THREE_S_S,
103 	  THREE_SSS,
104 	  THREE_S_S,
105 	  THREE_SSS },
106 
107 	{ THREE_SSS,
108 	  THREE_S_S,
109 	  THREE_SSS,
110 	  THREE___S,
111 	  THREE___S },
112 
113 	{ THREE__S_,
114 	  THREE_S_S,
115 	  THREE_SSS,
116 	  THREE_S_S,
117 	  THREE_S_S },
118 
119 	{ THREE_SS_,
120 	  THREE_S_S,
121 	  THREE_SS_,
122 	  THREE_S_S,
123 	  THREE_SS_ },
124 
125 	{ THREE__S_,
126 	  THREE_S_S,
127 	  THREE_S__,
128 	  THREE_S_S,
129 	  THREE__S_ },
130 
131 	{ THREE_SS_,
132 	  THREE_S_S,
133 	  THREE_S_S,
134 	  THREE_S_S,
135 	  THREE_SS_ },
136 
137 	{ THREE_SSS,
138 	  THREE_S__,
139 	  THREE_SS_,
140 	  THREE_S__,
141 	  THREE_SSS },
142 
143 	{ THREE_SSS,
144 	  THREE_S__,
145 	  THREE_SS_,
146 	  THREE_S__,
147 	  THREE_S__ }
148 };
149 
get_codepoint(uint32_t id,bool italic)150 static uint8_t * get_codepoint(uint32_t id, bool italic)
151 {
152 	int shift = 0;
153 	int l;
154 	int r;
155 
156 	if (!italic)
157 		shift = 1;
158 
159 	l =       (id >> 12);
160 	r = 0xf & (id >>  8);
161 
162 	code_point[ 0] = SEVEN_SET << shift;
163 
164 	code_point[ 2] = (frag[l][0] << (4 + shift)) | (frag[r][0] << shift);
165 	code_point[ 3] = (frag[l][1] << (4 + shift)) | (frag[r][1] << shift);
166 	code_point[ 4] = (frag[l][2] << (4 + shift)) | (frag[r][2] << shift);
167 	code_point[ 5] = (frag[l][3] << (4 + shift)) | (frag[r][3] << shift);
168 	code_point[ 6] = (frag[l][4] << (4 + shift)) | (frag[r][4] << shift);
169 
170 	shift = 1;
171 
172 	l = 0xf & (id >>  4);
173 	r = 0xf & id;
174 
175 	code_point[ 8] = (frag[l][0] << (4 + shift)) | (frag[r][0] << shift);
176 	code_point[ 9] = (frag[l][1] << (4 + shift)) | (frag[r][1] << shift);
177 	code_point[10] = (frag[l][2] << (4 + shift)) | (frag[r][2] << shift);
178 	code_point[11] = (frag[l][3] << (4 + shift)) | (frag[r][3] << shift);
179 	code_point[12] = (frag[l][4] << (4 + shift)) | (frag[r][4] << shift);
180 
181 	code_point[14] = SEVEN_SET << shift;
182 
183 	return (uint8_t *)code_point;
184 }
185 
186 
fb_font_init(void)187 bool fb_font_init(void)
188 {
189 	return true;
190 }
191 
fb_font_finalise(void)192 bool fb_font_finalise(void)
193 {
194 	return true;
195 }
196 
197 enum fb_font_style
fb_get_font_style(const plot_font_style_t * fstyle)198 fb_get_font_style(const plot_font_style_t *fstyle)
199 {
200 	enum fb_font_style style = FB_REGULAR;
201 
202 	if (fstyle->weight >= 700)
203 		style |= FB_BOLD;
204 	if ((fstyle->flags & FONTF_ITALIC) || (fstyle->flags & FONTF_OBLIQUE))
205 		style |= FB_ITALIC;
206 
207 	return style;
208 }
209 
210 int
fb_get_font_size(const plot_font_style_t * fstyle)211 fb_get_font_size(const plot_font_style_t *fstyle)
212 {
213 	int size = fstyle->size * 10 /
214 			(((nsoption_int(font_min_size) * 3 +
215 			   nsoption_int(font_size)) / 4) * PLOT_STYLE_SCALE);
216 	if (size > 2)
217 		size = 2;
218 	else if (size <= 0)
219 		size = 1;
220 
221 	return size;
222 }
223 
224 /** Lookup table to scale 4 bits to 8 bits, so e.g. 0101 --> 00110011 */
225 const uint8_t glyph_lut[16] = {
226 		0x00, 0x03, 0x0c, 0x0f,
227 		0x30, 0x33, 0x3c, 0x3f,
228 		0xc0, 0xc3, 0xcc, 0xcf,
229 		0xf0, 0xf3, 0xfc, 0xff
230 };
231 
232 static const uint8_t *
glyph_scale_2(const uint8_t * glyph_data)233 glyph_scale_2(const uint8_t *glyph_data)
234 {
235 	const uint8_t *glyph_max = glyph_data + GLYPH_LEN;
236 	uint8_t *pos = glyph_x2;
237 
238 	do {
239 		*pos++ = glyph_lut[*glyph_data >> 4];
240 		*pos++ = glyph_lut[*glyph_data & 0xf];
241 		*pos++ = glyph_lut[*glyph_data >> 4];
242 		*pos++ = glyph_lut[*glyph_data & 0xf];
243 	} while (++glyph_data < glyph_max);
244 
245 	return glyph_x2;
246 }
247 
248 const uint8_t *
fb_get_glyph(uint32_t ucs4,enum fb_font_style style,int scale)249 fb_get_glyph(uint32_t ucs4, enum fb_font_style style, int scale)
250 {
251 	const uint8_t *glyph_data;
252 	unsigned int section;
253 	unsigned int offset;
254 	uint16_t g_offset;
255 
256 	/* Internal font has no glyphs beyond U+FFFF and there isn't
257 	 * space to render a >4 digit codepoint; just show replacement
258 	 * character. */
259 	if (ucs4 > 0xffff)
260 		ucs4 = 0xfffd;
261 
262 	switch (style) {
263 	case FB_BOLD_ITALIC:
264 		section = fb_bold_italic_section_table[ucs4 / 256];
265 		if (section != 0 || ucs4 / 256 == 0) {
266 			offset = section * 256 + (ucs4 & 0xff);
267 			g_offset = fb_bold_italic_sections[offset] * 16;
268 			if (g_offset != 0) {
269 				glyph_data = &font_glyph_data[g_offset];
270 				break;
271 			}
272 		}
273 		/* Fall through. */
274 	case FB_BOLD:
275 		section = fb_bold_section_table[ucs4 / 256];
276 		if (section != 0 || ucs4 / 256 == 0) {
277 			offset = section * 256 + (ucs4 & 0xff);
278 			g_offset = fb_bold_sections[offset] * 16;
279 			if (g_offset != 0) {
280 				glyph_data = &font_glyph_data[g_offset];
281 				break;
282 			}
283 		}
284 		/* Fall through. */
285 	case FB_ITALIC:
286 		section = fb_italic_section_table[ucs4 / 256];
287 		if (section != 0 || ucs4 / 256 == 0) {
288 			offset = section * 256 + (ucs4 & 0xff);
289 			g_offset = fb_italic_sections[offset] * 16;
290 			if (g_offset != 0) {
291 				glyph_data = &font_glyph_data[g_offset];
292 				break;
293 			}
294 		}
295 		/* Fall through. */
296 	case FB_REGULAR:
297 		section = fb_regular_section_table[ucs4 / 256];
298 		if (section != 0 || ucs4 / 256 == 0) {
299 			offset = section * 256 + (ucs4 & 0xff);
300 			g_offset = fb_regular_sections[offset] * 16;
301 			if (g_offset != 0) {
302 				glyph_data = &font_glyph_data[g_offset];
303 				break;
304 			}
305 		}
306 		/* Fall through. */
307 	default:
308 		glyph_data = get_codepoint(ucs4, style & FB_ITALIC);
309 		break;
310 	}
311 
312 	switch (scale) {
313 	case 1:
314 		break;
315 	case 2:
316 		glyph_data = glyph_scale_2(glyph_data);
317 		break;
318 	default:
319 		assert(scale >= 1 && scale <= 2);
320 		break;
321 	}
322 
323 	return glyph_data;
324 }
325 
utf8_to_local(const char * string,size_t len,char ** result)326 static nserror utf8_to_local(const char *string,
327 				       size_t len,
328 				       char **result)
329 {
330 	return utf8_to_enc(string, "CP1252", len, result);
331 
332 }
333 
utf8_from_local(const char * string,size_t len,char ** result)334 static nserror utf8_from_local(const char *string,
335 					size_t len,
336 					char **result)
337 {
338 	*result = malloc(len + 1);
339 	if (*result == NULL) {
340 		return NSERROR_NOMEM;
341 	}
342 
343 	memcpy(*result, string, len);
344 
345 	(*result)[len] = '\0';
346 
347 	return NSERROR_OK;
348 }
349 
350 
351 /* exported interface documented in framebuffer/freetype_font.h */
352 nserror
fb_font_width(const plot_font_style_t * fstyle,const char * string,size_t length,int * width)353 fb_font_width(const plot_font_style_t *fstyle,
354 	      const char *string,
355 	      size_t length,
356 	      int *width)
357 {
358         size_t nxtchr = 0;
359 
360 	*width = 0;
361         while (nxtchr < length) {
362 		uint32_t ucs4;
363 		ucs4 = utf8_to_ucs4(string + nxtchr, length - nxtchr);
364 		if (codepoint_displayable(ucs4)) {
365 			*width += FB_FONT_WIDTH;
366 		}
367 
368 		nxtchr = utf8_next(string, length, nxtchr);
369         }
370 
371 	*width *= fb_get_font_size(fstyle);
372 	return NSERROR_OK;
373 }
374 
375 
376 /* exported interface documented in framebuffer/freetype_font.h */
377 nserror
fb_font_position(const plot_font_style_t * fstyle,const char * string,size_t length,int x,size_t * char_offset,int * actual_x)378 fb_font_position(const plot_font_style_t *fstyle,
379 		 const char *string,
380 		 size_t length,
381 		 int x,
382 		 size_t *char_offset,
383 		 int *actual_x)
384 {
385         const int width = fb_get_font_size(fstyle) * FB_FONT_WIDTH;
386         size_t nxtchr = 0;
387 	int x_pos = 0;
388 
389         while (nxtchr < length) {
390 		uint32_t ucs4;
391                 if (abs(x_pos - x) <= (width / 2))
392                         break;
393 
394 		ucs4 = utf8_to_ucs4(string + nxtchr, length - nxtchr);
395 		if (codepoint_displayable(ucs4)) {
396 			x_pos += width;
397 		}
398 
399                 nxtchr = utf8_next(string, length, nxtchr);
400         }
401 
402         *actual_x = x_pos;
403 
404         *char_offset = nxtchr;
405 	return NSERROR_OK;
406 }
407 
408 
409 /**
410  * Find where to split a string to make it fit a width.
411  *
412  * \param  fstyle       style for this text
413  * \param  string       UTF-8 string to measure
414  * \param  length       length of string, in bytes
415  * \param  x            width available
416  * \param  char_offset  updated to offset in string of actual_x, [1..length]
417  * \param  actual_x     updated to x coordinate of character closest to x
418  * \return  true on success, false on error and error reported
419  *
420  * On exit, char_offset indicates first character after split point.
421  *
422  * Note: char_offset of 0 should never be returned.
423  *
424  *   Returns:
425  *     char_offset giving split point closest to x, where actual_x <= x
426  *   else
427  *     char_offset giving split point closest to x, where actual_x > x
428  *
429  * Returning char_offset == length means no split possible
430  */
431 static nserror
fb_font_split(const plot_font_style_t * fstyle,const char * string,size_t length,int x,size_t * char_offset,int * actual_x)432 fb_font_split(const plot_font_style_t *fstyle,
433 	      const char *string,
434 	      size_t length,
435 	      int x,
436 	      size_t *char_offset,
437 	      int *actual_x)
438 {
439         const int width = fb_get_font_size(fstyle) * FB_FONT_WIDTH;
440         size_t nxtchr = 0;
441         int last_space_x = 0;
442         int last_space_idx = 0;
443 
444         *actual_x = 0;
445         while (nxtchr < length) {
446 		uint32_t ucs4;
447 
448                 if (string[nxtchr] == ' ') {
449                         last_space_x = *actual_x;
450                         last_space_idx = nxtchr;
451                 }
452 
453 		ucs4 = utf8_to_ucs4(string + nxtchr, length - nxtchr);
454 		if (codepoint_displayable(ucs4)) {
455 			*actual_x += width;
456 		}
457 
458                 if (*actual_x > x && last_space_idx != 0) {
459                         /* string has exceeded available width and we've
460                          * found a space; return previous space */
461                         *actual_x = last_space_x;
462                         *char_offset = last_space_idx;
463                         return NSERROR_OK;
464                 }
465 
466                 nxtchr = utf8_next(string, length, nxtchr);
467         }
468 
469         *char_offset = nxtchr;
470 
471 	return NSERROR_OK;
472 }
473 
474 
475 static struct gui_layout_table layout_table = {
476 	.width = fb_font_width,
477 	.position = fb_font_position,
478 	.split = fb_font_split,
479 };
480 
481 struct gui_layout_table *framebuffer_layout_table = &layout_table;
482 
483 
484 static struct gui_utf8_table utf8_table = {
485 	.utf8_to_local = utf8_to_local,
486 	.local_to_utf8 = utf8_from_local,
487 };
488 
489 struct gui_utf8_table *framebuffer_utf8_table = &utf8_table;
490 
491 
492 /*
493  * Local Variables:
494  * c-basic-offset:8
495  * End:
496  */
497