Subversion Repositories Kolibri OS

Rev

Go to most recent revision | Details | Last modification | View Log | RSS feed

Rev Author Line No. Line
4680 right-hear 1
#include "fitz.h"
2
#include "muxps.h"
3
 
4
#include 
5
#include FT_FREETYPE_H
6
#include FT_ADVANCES_H
7
 
8
static inline int ishex(int a)
9
{
10
	return (a >= 'A' && a <= 'F') ||
11
		(a >= 'a' && a <= 'f') ||
12
		(a >= '0' && a <= '9');
13
}
14
 
15
static inline int unhex(int a)
16
{
17
	if (a >= 'A' && a <= 'F') return a - 'A' + 0xA;
18
	if (a >= 'a' && a <= 'f') return a - 'a' + 0xA;
19
	if (a >= '0' && a <= '9') return a - '0';
20
	return 0;
21
}
22
 
23
int
24
xps_count_font_encodings(fz_font *font)
25
{
26
	FT_Face face = font->ft_face;
27
	return face->num_charmaps;
28
}
29
 
30
void
31
xps_identify_font_encoding(fz_font *font, int idx, int *pid, int *eid)
32
{
33
	FT_Face face = font->ft_face;
34
	*pid = face->charmaps[idx]->platform_id;
35
	*eid = face->charmaps[idx]->encoding_id;
36
}
37
 
38
void
39
xps_select_font_encoding(fz_font *font, int idx)
40
{
41
	FT_Face face = font->ft_face;
42
	FT_Set_Charmap(face, face->charmaps[idx]);
43
}
44
 
45
int
46
xps_encode_font_char(fz_font *font, int code)
47
{
48
	FT_Face face = font->ft_face;
49
	int gid = FT_Get_Char_Index(face, code);
50
	if (gid == 0 && face->charmap->platform_id == 3 && face->charmap->encoding_id == 0)
51
		gid = FT_Get_Char_Index(face, 0xF000 | code);
52
	return gid;
53
}
54
 
55
void
56
xps_measure_font_glyph(xps_context *ctx, fz_font *font, int gid, xps_glyph_metrics *mtx)
57
{
58
	int mask = FT_LOAD_NO_BITMAP | FT_LOAD_NO_HINTING | FT_LOAD_IGNORE_TRANSFORM;
59
	FT_Face face = font->ft_face;
60
	FT_Fixed hadv, vadv;
61
 
62
	FT_Set_Char_Size(face, 64, 64, 72, 72);
63
	FT_Get_Advance(face, gid, mask, &hadv);
64
	FT_Get_Advance(face, gid, mask | FT_LOAD_VERTICAL_LAYOUT, &vadv);
65
 
66
	mtx->hadv = hadv / 65536.0f;
67
	mtx->vadv = vadv / 65536.0f;
68
	mtx->vorg = face->ascender / (float) face->units_per_EM;
69
}
70
 
71
static fz_font *
72
xps_lookup_font(xps_context *ctx, char *name)
73
{
74
	xps_font_cache *cache;
75
	for (cache = ctx->font_table; cache; cache = cache->next)
76
		if (!xps_strcasecmp(cache->name, name))
77
			return fz_keep_font(cache->font);
78
	return NULL;
79
}
80
 
81
static void
82
xps_insert_font(xps_context *ctx, char *name, fz_font *font)
83
{
84
	xps_font_cache *cache = fz_malloc(sizeof(xps_font_cache));
85
	cache->name = fz_strdup(name);
86
	cache->font = fz_keep_font(font);
87
	cache->next = ctx->font_table;
88
	ctx->font_table = cache;
89
}
90
 
91
/*
92
 * Some fonts in XPS are obfuscated by XOR:ing the first 32 bytes of the
93
 * data with the GUID in the fontname.
94
 */
95
static void
96
xps_deobfuscate_font_resource(xps_context *ctx, xps_part *part)
97
{
98
	byte buf[33];
99
	byte key[16];
100
	char *p;
101
	int i;
102
 
103
	p = strrchr(part->name, '/');
104
	if (!p)
105
		p = part->name;
106
 
107
	for (i = 0; i < 32 && *p; p++)
108
	{
109
		if (ishex(*p))
110
			buf[i++] = *p;
111
	}
112
	buf[i] = 0;
113
 
114
	if (i != 32)
115
	{
116
		fz_warn("cannot extract GUID from obfuscated font part name");
117
		return;
118
	}
119
 
120
	for (i = 0; i < 16; i++)
121
		key[i] = unhex(buf[i*2+0]) * 16 + unhex(buf[i*2+1]);
122
 
123
	for (i = 0; i < 16; i++)
124
	{
125
		part->data[i] ^= key[15-i];
126
		part->data[i+16] ^= key[15-i];
127
	}
128
}
129
 
130
static void
131
xps_select_best_font_encoding(fz_font *font)
132
{
133
	static struct { int pid, eid; } xps_cmap_list[] =
134
	{
135
		{ 3, 10 },		/* Unicode with surrogates */
136
		{ 3, 1 },		/* Unicode without surrogates */
137
		{ 3, 5 },		/* Wansung */
138
		{ 3, 4 },		/* Big5 */
139
		{ 3, 3 },		/* Prc */
140
		{ 3, 2 },		/* ShiftJis */
141
		{ 3, 0 },		/* Symbol */
142
		// { 0, * }, -- Unicode (deprecated)
143
		{ 1, 0 },
144
		{ -1, -1 },
145
	};
146
 
147
	int i, k, n, pid, eid;
148
 
149
	n = xps_count_font_encodings(font);
150
	for (k = 0; xps_cmap_list[k].pid != -1; k++)
151
	{
152
		for (i = 0; i < n; i++)
153
		{
154
			xps_identify_font_encoding(font, i, &pid, &eid);
155
			if (pid == xps_cmap_list[k].pid && eid == xps_cmap_list[k].eid)
156
			{
157
				xps_select_font_encoding(font, i);
158
				return;
159
			}
160
		}
161
	}
162
 
163
	fz_warn("cannot find a suitable cmap");
164
}
165
 
166
/*
167
 * Parse and draw an XPS  element.
168
 *
169
 * Indices syntax:
170
 
171
 GlyphIndices	= GlyphMapping ( ";" GlyphMapping )
172
 GlyphMapping	= ( [ClusterMapping] GlyphIndex ) [GlyphMetrics]
173
 ClusterMapping = "(" ClusterCodeUnitCount [":" ClusterGlyphCount] ")"
174
 ClusterCodeUnitCount	= * DIGIT
175
 ClusterGlyphCount		= * DIGIT
176
 GlyphIndex		= * DIGIT
177
 GlyphMetrics	= "," AdvanceWidth ["," uOffset ["," vOffset]]
178
 AdvanceWidth	= ["+"] RealNum
179
 uOffset		= ["+" | "-"] RealNum
180
 vOffset		= ["+" | "-"] RealNum
181
 RealNum		= ((DIGIT ["." DIGIT]) | ("." DIGIT)) [Exponent]
182
 Exponent		= ( ("E"|"e") ("+"|"-") DIGIT )
183
 
184
 */
185
 
186
static char *
187
xps_parse_digits(char *s, int *digit)
188
{
189
	*digit = 0;
190
	while (*s >= '0' && *s <= '9')
191
	{
192
		*digit = *digit * 10 + (*s - '0');
193
		s ++;
194
	}
195
	return s;
196
}
197
 
198
static inline int is_real_num_char(int c)
199
{
200
	return (c >= '0' && c <= '9') ||
201
		c == 'e' || c == 'E' || c == '+' || c == '-' || c == '.';
202
}
203
 
204
static char *
205
xps_parse_real_num(char *s, float *number)
206
{
207
	char buf[64];
208
	char *p = buf;
209
	while (is_real_num_char(*s))
210
		*p++ = *s++;
211
	*p = 0;
212
	if (buf[0])
213
		*number = fz_atof(buf);
214
	return s;
215
}
216
 
217
static char *
218
xps_parse_cluster_mapping(char *s, int *code_count, int *glyph_count)
219
{
220
	if (*s == '(')
221
		s = xps_parse_digits(s + 1, code_count);
222
	if (*s == ':')
223
		s = xps_parse_digits(s + 1, glyph_count);
224
	if (*s == ')')
225
		s ++;
226
	return s;
227
}
228
 
229
static char *
230
xps_parse_glyph_index(char *s, int *glyph_index)
231
{
232
	if (*s >= '0' && *s <= '9')
233
		s = xps_parse_digits(s, glyph_index);
234
	return s;
235
}
236
 
237
static char *
238
xps_parse_glyph_metrics(char *s, float *advance, float *uofs, float *vofs)
239
{
240
	if (*s == ',')
241
		s = xps_parse_real_num(s + 1, advance);
242
	if (*s == ',')
243
		s = xps_parse_real_num(s + 1, uofs);
244
	if (*s == ',')
245
		s = xps_parse_real_num(s + 1, vofs);
246
	return s;
247
}
248
 
249
/*
250
 * Parse unicode and indices strings and encode glyphs.
251
 * Calculate metrics for positioning.
252
 */
253
static fz_text *
254
xps_parse_glyphs_imp(xps_context *ctx, fz_matrix ctm,
255
	fz_font *font, float size, float originx, float originy,
256
	int is_sideways, int bidi_level,
257
	char *indices, char *unicode)
258
{
259
	xps_glyph_metrics mtx;
260
	fz_text *text;
261
	fz_matrix tm;
262
	float e, f;
263
	float x = originx;
264
	float y = originy;
265
	char *us = unicode;
266
	char *is = indices;
267
	int un = 0;
268
 
269
	if (!unicode && !indices)
270
		fz_warn("glyphs element with neither characters nor indices");
271
 
272
	if (us)
273
	{
274
		if (us[0] == '{' && us[1] == '}')
275
			us = us + 2;
276
		un = strlen(us);
277
	}
278
 
279
	if (is_sideways)
280
		tm = fz_concat(fz_scale(-size, size), fz_rotate(90));
281
	else
282
		tm = fz_scale(size, -size);
283
 
284
	text = fz_new_text(font, tm, is_sideways);
285
 
286
	while ((us && un > 0) || (is && *is))
287
	{
288
		int char_code = '?';
289
		int code_count = 1;
290
		int glyph_count = 1;
291
 
292
		if (is && *is)
293
		{
294
			is = xps_parse_cluster_mapping(is, &code_count, &glyph_count);
295
		}
296
 
297
		if (code_count < 1)
298
			code_count = 1;
299
		if (glyph_count < 1)
300
			glyph_count = 1;
301
 
302
		/* TODO: add code chars with cluster mappings for text extraction */
303
 
304
		while (code_count--)
305
		{
306
			if (us && un > 0)
307
			{
308
				int t = chartorune(&char_code, us);
309
				us += t; un -= t;
310
			}
311
		}
312
 
313
		while (glyph_count--)
314
		{
315
			int glyph_index = -1;
316
			float u_offset = 0;
317
			float v_offset = 0;
318
			float advance;
319
 
320
			if (is && *is)
321
				is = xps_parse_glyph_index(is, &glyph_index);
322
 
323
			if (glyph_index == -1)
324
				glyph_index = xps_encode_font_char(font, char_code);
325
 
326
			xps_measure_font_glyph(ctx, font, glyph_index, &mtx);
327
			if (is_sideways)
328
				advance = mtx.vadv * 100;
329
			else if (bidi_level & 1)
330
				advance = -mtx.hadv * 100;
331
			else
332
				advance = mtx.hadv * 100;
333
 
334
			if (is && *is)
335
			{
336
				is = xps_parse_glyph_metrics(is, &advance, &u_offset, &v_offset);
337
				if (*is == ';')
338
					is ++;
339
			}
340
 
341
			if (bidi_level & 1)
342
				u_offset = -mtx.hadv * 100 - u_offset;
343
 
344
			u_offset = u_offset * 0.01f * size;
345
			v_offset = v_offset * 0.01f * size;
346
 
347
			if (is_sideways)
348
			{
349
				e = x + u_offset + (mtx.vorg * size);
350
				f = y - v_offset + (mtx.hadv * 0.5f * size);
351
			}
352
			else
353
			{
354
				e = x + u_offset;
355
				f = y - v_offset;
356
			}
357
 
358
			fz_add_text(text, glyph_index, char_code, e, f);
359
 
360
			x += advance * 0.01f * size;
361
		}
362
	}
363
 
364
	return text;
365
}
366
 
367
void
368
xps_parse_glyphs(xps_context *ctx, fz_matrix ctm,
369
		char *base_uri, xps_resource *dict, xml_element *root)
370
{
371
	xml_element *node;
372
	int code;
373
 
374
	char *fill_uri;
375
	char *opacity_mask_uri;
376
 
377
	char *bidi_level_att;
378
	char *caret_stops_att;
379
	char *fill_att;
380
	char *font_size_att;
381
	char *font_uri_att;
382
	char *origin_x_att;
383
	char *origin_y_att;
384
	char *is_sideways_att;
385
	char *indices_att;
386
	char *unicode_att;
387
	char *style_att;
388
	char *transform_att;
389
	char *clip_att;
390
	char *opacity_att;
391
	char *opacity_mask_att;
392
 
393
	xml_element *transform_tag = NULL;
394
	xml_element *clip_tag = NULL;
395
	xml_element *fill_tag = NULL;
396
	xml_element *opacity_mask_tag = NULL;
397
 
398
	char *fill_opacity_att = NULL;
399
 
400
	xps_part *part;
401
	fz_font *font;
402
 
403
	char partname[1024];
404
	char *subfont;
405
 
406
	float font_size = 10;
407
	int subfontid = 0;
408
	int is_sideways = 0;
409
	int bidi_level = 0;
410
 
411
	fz_text *text;
412
	fz_rect area;
413
 
414
	/*
415
	 * Extract attributes and extended attributes.
416
	 */
417
 
418
	bidi_level_att = xml_att(root, "BidiLevel");
419
	caret_stops_att = xml_att(root, "CaretStops");
420
	fill_att = xml_att(root, "Fill");
421
	font_size_att = xml_att(root, "FontRenderingEmSize");
422
	font_uri_att = xml_att(root, "FontUri");
423
	origin_x_att = xml_att(root, "OriginX");
424
	origin_y_att = xml_att(root, "OriginY");
425
	is_sideways_att = xml_att(root, "IsSideways");
426
	indices_att = xml_att(root, "Indices");
427
	unicode_att = xml_att(root, "UnicodeString");
428
	style_att = xml_att(root, "StyleSimulations");
429
	transform_att = xml_att(root, "RenderTransform");
430
	clip_att = xml_att(root, "Clip");
431
	opacity_att = xml_att(root, "Opacity");
432
	opacity_mask_att = xml_att(root, "OpacityMask");
433
 
434
	for (node = xml_down(root); node; node = xml_next(node))
435
	{
436
		if (!strcmp(xml_tag(node), "Glyphs.RenderTransform"))
437
			transform_tag = xml_down(node);
438
		if (!strcmp(xml_tag(node), "Glyphs.OpacityMask"))
439
			opacity_mask_tag = xml_down(node);
440
		if (!strcmp(xml_tag(node), "Glyphs.Clip"))
441
			clip_tag = xml_down(node);
442
		if (!strcmp(xml_tag(node), "Glyphs.Fill"))
443
			fill_tag = xml_down(node);
444
	}
445
 
446
	fill_uri = base_uri;
447
	opacity_mask_uri = base_uri;
448
 
449
	xps_resolve_resource_reference(ctx, dict, &transform_att, &transform_tag, NULL);
450
	xps_resolve_resource_reference(ctx, dict, &clip_att, &clip_tag, NULL);
451
	xps_resolve_resource_reference(ctx, dict, &fill_att, &fill_tag, &fill_uri);
452
	xps_resolve_resource_reference(ctx, dict, &opacity_mask_att, &opacity_mask_tag, &opacity_mask_uri);
453
 
454
	/*
455
	 * Check that we have all the necessary information.
456
	 */
457
 
458
	if (!font_size_att || !font_uri_att || !origin_x_att || !origin_y_att) {
459
		fz_warn("missing attributes in glyphs element");
460
		return;
461
	}
462
 
463
	if (!indices_att && !unicode_att)
464
		return; /* nothing to draw */
465
 
466
	if (is_sideways_att)
467
		is_sideways = !strcmp(is_sideways_att, "true");
468
 
469
	if (bidi_level_att)
470
		bidi_level = atoi(bidi_level_att);
471
 
472
	/*
473
	 * Find and load the font resource
474
	 */
475
 
476
	xps_absolute_path(partname, base_uri, font_uri_att, sizeof partname);
477
	subfont = strrchr(partname, '#');
478
	if (subfont)
479
	{
480
		subfontid = atoi(subfont + 1);
481
		*subfont = 0;
482
	}
483
 
484
	font = xps_lookup_font(ctx, partname);
485
	if (!font)
486
	{
487
		part = xps_read_part(ctx, partname);
488
		if (!part) {
489
			fz_warn("cannot find font resource part '%s'", partname);
490
			return;
491
		}
492
 
493
		/* deobfuscate if necessary */
494
		if (strstr(part->name, ".odttf"))
495
			xps_deobfuscate_font_resource(ctx, part);
496
		if (strstr(part->name, ".ODTTF"))
497
			xps_deobfuscate_font_resource(ctx, part);
498
 
499
		code = fz_new_font_from_memory(&font, part->data, part->size, subfontid);
500
		if (code) {
501
			fz_catch(code, "cannot load font resource '%s'", partname);
502
			xps_free_part(ctx, part);
503
			return;
504
		}
505
 
506
		xps_select_best_font_encoding(font);
507
 
508
		xps_insert_font(ctx, part->name, font);
509
 
510
		/* NOTE: we keep part->data in the font */
511
		font->ft_data = part->data;
512
		font->ft_size = part->size;
513
		fz_free(part->name);
514
		fz_free(part);
515
	}
516
 
517
	/*
518
	 * Set up graphics state.
519
	 */
520
 
521
	if (transform_att || transform_tag)
522
	{
523
		fz_matrix transform;
524
		if (transform_att)
525
			xps_parse_render_transform(ctx, transform_att, &transform);
526
		if (transform_tag)
527
			xps_parse_matrix_transform(ctx, transform_tag, &transform);
528
		ctm = fz_concat(transform, ctm);
529
	}
530
 
531
	if (clip_att || clip_tag)
532
		xps_clip(ctx, ctm, dict, clip_att, clip_tag);
533
 
534
	font_size = fz_atof(font_size_att);
535
 
536
	text = xps_parse_glyphs_imp(ctx, ctm, font, font_size,
537
			fz_atof(origin_x_att), fz_atof(origin_y_att),
538
			is_sideways, bidi_level, indices_att, unicode_att);
539
 
540
	area = fz_bound_text(text, ctm);
541
 
542
	xps_begin_opacity(ctx, ctm, area, opacity_mask_uri, dict, opacity_att, opacity_mask_tag);
543
 
544
	/* If it's a solid color brush fill/stroke do a simple fill */
545
 
546
	if (fill_tag && !strcmp(xml_tag(fill_tag), "SolidColorBrush"))
547
	{
548
		fill_opacity_att = xml_att(fill_tag, "Opacity");
549
		fill_att = xml_att(fill_tag, "Color");
550
		fill_tag = NULL;
551
	}
552
 
553
	if (fill_att)
554
	{
555
		float samples[32];
556
		fz_colorspace *colorspace;
557
 
558
		xps_parse_color(ctx, base_uri, fill_att, &colorspace, samples);
559
		if (fill_opacity_att)
560
			samples[0] = fz_atof(fill_opacity_att);
561
		xps_set_color(ctx, colorspace, samples);
562
 
563
		fz_fill_text(ctx->dev, text, ctm,
564
			ctx->colorspace, ctx->color, ctx->alpha);
565
	}
566
 
567
	/* If it's a complex brush, use the charpath as a clip mask */
568
 
569
	if (fill_tag)
570
	{
571
		fz_clip_text(ctx->dev, text, ctm, 0);
572
		xps_parse_brush(ctx, ctm, area, fill_uri, dict, fill_tag);
573
		fz_pop_clip(ctx->dev);
574
	}
575
 
576
	xps_end_opacity(ctx, opacity_mask_uri, dict, opacity_att, opacity_mask_tag);
577
 
578
	fz_free_text(text);
579
 
580
	if (clip_att || clip_tag)
581
		fz_pop_clip(ctx->dev);
582
 
583
	fz_drop_font(font);
584
}