1 #include "apricot.h"
2 #include "guts.h"
3 #include "Drawable.h"
4 #include "Application.h"
5 
6 #ifdef WITH_FRIBIDI
7 #include <fribidi/fribidi.h>
8 #endif
9 #define MAX_CHARACTERS 8192
10 extern Bool   use_fribidi;
11 
12 #ifdef __cplusplus
13 extern "C" {
14 #endif
15 
16 #undef my
17 #define inherited CComponent->
18 #define my  ((( PDrawable) self)-> self)
19 #define var (( PDrawable) self)
20 
21 #define gpARGS            Bool inPaint = opt_InPaint
22 #define gpENTER(fail)     if ( !inPaint) if ( !my-> begin_paint_info( self)) return (fail)
23 #define gpLEAVE           if ( !inPaint) my-> end_paint_info( self)
24 
25 #define CHECK_GP(ret) \
26 	if ( !is_opt(optSystemDrawable)) { \
27 		warn("This method is not available because %s is not a system Drawable object. You need to implement your own (ref:%d)", my->className, __LINE__);\
28 		return ret; \
29 	}
30 
31 /*
32 
33 SECTION 1: FONT MAPPER
34 
35 font_passive_entries is queried at start and is filled with fonts that can be
36 used in font substitution. Each contains a Font record and a set of bit
37 vectors, split by FONTMAPPER_VECTOR_MASK (PassiveFontEntry). Bit vectors are
38 built on demand.
39 
40 font_active_entries is a sparse list that contains either NULLs or PList
41 entries.  Each index is a block for (INDEX >> FONTMAPPER_VECTOR_BASE), and
42 contains set of FONT IDs, each is font_passive_entries index. It is added to
43 whenever text_shape needs another font, selecting entries from font_passive_entries,
44 or filling them by querying ranges
45 
46 */
47 
48 #define FONTMAPPER_VECTOR_BASE 9 /* 512 chars or 64 bytes per vector - make sure it's greater than 256, text_wrap/abc depends on that */
49 #define FONTMAPPER_VECTOR_MASK ((1 << FONTMAPPER_VECTOR_BASE) - 1)
50 
51 static List  font_active_entries;
52 static List  font_passive_entries;
53 static PHash font_substitutions;
54 
55 #define STYLE_MASK (fsThin|fsItalic|fsBold)
56 #define N_STYLES   (1 + STYLE_MASK)
57 static int   font_mapper_default_id[N_STYLES];
58 
59 typedef struct {
60 	Font   font;
61 	List   vectors;
62 	Bool   ranges_queried;
63 	Bool   is_active;
64 	unsigned int flags, style;
65 } PassiveFontEntry, *PPassiveFontEntry;
66 
67 #define PASSIVE_FONT(fid) ((PPassiveFontEntry) font_passive_entries.items[(unsigned int)(fid)])
68 
69 PFont
prima_font_mapper_get_font(unsigned int fid)70 prima_font_mapper_get_font(unsigned int fid)
71 {
72 	if ( fid >= font_passive_entries.count ) return NULL;
73 	return &PASSIVE_FONT(fid)->font;
74 }
75 
76 void
prima_init_font_mapper(void)77 prima_init_font_mapper(void)
78 {
79 	int i;
80 	list_create(&font_passive_entries, 256, 256);
81 	list_create(&font_active_entries,  16,  16);
82 	for ( i = 0; i < N_STYLES; i++) font_mapper_default_id[i] = -1;
83 	font_substitutions = prima_hash_create();
84 
85 	prima_font_mapper_save_font(NULL, 0); /* occupy zero index */
86 }
87 
88 static Bool
kill_active_entry(PList fontlist,void * dummy)89 kill_active_entry( PList fontlist, void * dummy)
90 {
91 	if (fontlist)
92 		plist_destroy(fontlist);
93 	return false;
94 }
95 
96 static Bool
kill_passive_entry(PPassiveFontEntry entry,void * dummy)97 kill_passive_entry( PPassiveFontEntry entry, void * dummy)
98 {
99 	if ( entry-> ranges_queried ) {
100 		list_delete_all( &entry->vectors, true );
101 		list_destroy( &entry-> vectors);
102 	}
103 	free(entry);
104 	return false;
105 }
106 
107 void
prima_cleanup_font_mapper(void)108 prima_cleanup_font_mapper(void)
109 {
110 	list_first_that( &font_active_entries, (void*)kill_active_entry, NULL);
111 	list_destroy( &font_active_entries);
112 
113 	list_first_that( &font_passive_entries, (void*)kill_passive_entry, NULL);
114 	list_destroy( &font_passive_entries);
115 
116 	hash_destroy( font_substitutions, false);
117 }
118 
119 static char *
font_key(const char * name,unsigned int style)120 font_key( const char * name, unsigned int style)
121 {
122 	static char buf[2048];
123 	if ( !name ) return NULL;
124 	buf[0] = '0' + (style & STYLE_MASK);
125 	strncpy( buf + 1, name, 2046 );
126 	return buf;
127 }
128 
129 PFont
prima_font_mapper_save_font(const char * name,unsigned int style)130 prima_font_mapper_save_font(const char * name, unsigned int style)
131 {
132 	PPassiveFontEntry p;
133 	PFont f;
134 	char * key;
135 
136 	key = font_key(name, style);
137 	if ( name && PTR2IV(hash_fetch(font_substitutions, key, strlen(key))) != 0)
138 		return NULL;
139 
140 	if ( !( p = malloc(sizeof(PassiveFontEntry)))) {
141 		warn("not enough memory\n");
142 		return NULL;
143 	}
144 	bzero(p, sizeof(PassiveFontEntry));
145 	f = &p->font;
146 	memset( &f->undef, 0xff, sizeof(f->undef));
147 	f->undef.encoding = 0; /* needs enforcing */
148 	if (name) {
149 		f->undef.name = 0;
150 		strncpy(f->name, name, 256);
151 		f->name[255] = 0;
152 		f->undef.style = 0;
153 		f->style = style;
154 	}
155 
156 	if ( name ) hash_store(
157 		font_substitutions,
158 		key, strlen(key),
159 		INT2PTR(void*, font_passive_entries.count)
160 	);
161 
162 	list_add(&font_passive_entries, (Handle)f);
163 
164 	return f;
165 }
166 
167 static void
query_ranges(PPassiveFontEntry pfe)168 query_ranges(PPassiveFontEntry pfe)
169 {
170 	Font f;
171 	unsigned long * ranges;
172 	int i, count = 0, last;
173 
174 	f = pfe->font;
175 	f.undef.pitch = 1;
176 	f.pitch = fpDefault;
177 
178 	pfe-> ranges_queried = true;
179 	ranges = apc_gp_get_mapper_ranges(&f, &count, &pfe->flags);
180 	if ( count <= 0 ) {
181 		list_create( &pfe->vectors, 0, 1);
182 		return;
183 	}
184 
185 	last = (ranges[count - 1] >> FONTMAPPER_VECTOR_BASE) + 1;
186 	list_create( &pfe->vectors, last, 1);
187 	bzero( pfe->vectors.items, last * sizeof(Handle));
188 	pfe->vectors.count = last;
189 
190 	for ( i = 0; i < count; i += 2 ) {
191 		int j;
192 		int from = ranges[i];
193 		int to   = ranges[i+1];
194 		for (j = from; j <= to; j++) {
195 			Byte * map;
196 			unsigned int page = j >> FONTMAPPER_VECTOR_BASE, bit = j & FONTMAPPER_VECTOR_MASK;
197 
198 			if ( !(pfe->flags & MAPPER_FLAGS_COMBINING_SUPPORTED)) {
199 				if ( j >= 0x300 && j <= 0x36f )
200 					continue;
201 			}
202 
203 			if (( map = (Byte*) pfe->vectors.items[page]) == NULL ) {
204 				if (!( map = malloc(1 << (FONTMAPPER_VECTOR_BASE - 3)))) {
205 					warn("Not enough memory");
206 					return;
207 				}
208 				bzero(map, 1 << (FONTMAPPER_VECTOR_BASE - 3));
209 				pfe->vectors.items[page] = (Handle) map;
210 			}
211 			map[ bit >> 3 ] |= 1 << (bit & 7);
212 		}
213 	}
214 }
215 
216 static void
add_active_font(int fid)217 add_active_font(int fid)
218 {
219 	int page;
220 	PPassiveFontEntry pfe = PASSIVE_FONT(fid);
221 
222 	if ( pfe-> is_active ) return;
223 	pfe-> is_active = true;
224 
225 	for ( page = 0; page < pfe->vectors.count; page++) {
226 		if ( !pfe->vectors.items[page] ) continue;
227 
228 		if ( font_active_entries.count <= page ) {
229 			while (font_active_entries.count <= page )
230 				list_add(&font_active_entries, (Handle)NULL);
231 		}
232 		if ( font_active_entries.items[page] == NULL_HANDLE )
233 			font_active_entries.items[page] = (Handle) plist_create(4, 4);
234 		list_add((PList) font_active_entries.items[page], fid);
235 	}
236 }
237 
238 static void
remove_active_font(int fid)239 remove_active_font(int fid)
240 {
241 	int page;
242 	PPassiveFontEntry pfe = PASSIVE_FONT(fid);
243 
244 	if ( !pfe-> is_active ) return;
245 
246 	for ( page = 0; page < pfe->vectors.count; page++) {
247 		if ( !pfe->vectors.items[page] ) continue;
248 		if ( font_active_entries.items[page] == NULL_HANDLE ) continue;
249 		list_delete((PList) font_active_entries.items[page], fid);
250 	}
251 }
252 
253 static Bool
can_substitute(uint32_t c,int pitch,int fid)254 can_substitute(uint32_t c, int pitch, int fid)
255 {
256 	Byte * fa;
257 	unsigned int page, bit;
258 	PPassiveFontEntry pfe = PASSIVE_FONT(fid);
259 
260 	if ( !pfe-> ranges_queried )
261 		query_ranges(pfe);
262 
263 	if (
264 		pitch != fpDefault &&
265 		(( pfe->font.undef.pitch || pfe->font.pitch != pitch )) &&
266 		!( pfe-> flags & MAPPER_FLAGS_SYNTHETIC_PITCH)
267 	)
268 		return false;
269 
270 	page = c >> FONTMAPPER_VECTOR_BASE;
271 	if ( pfe-> vectors.count <= page ) return false;
272 
273 	fa = (Byte *) pfe-> vectors.items[ page ];
274 	if ( !fa ) return false;
275 
276 	bit  = c & FONTMAPPER_VECTOR_MASK;
277 	if (( fa[bit >> 3] & (1 << (bit & 7))) == 0) return false;
278 
279 	if ( !pfe-> is_active ) {
280 #ifdef _DEBUG
281 		printf("add polyfont %s for chr(%x)\n", pfe->font.name, c);
282 #endif
283 		add_active_font(fid);
284 	}
285 
286 	return true;
287 }
288 
289 static unsigned int
find_font(uint32_t c,int pitch,int style,uint16_t preferred_font)290 find_font(uint32_t c, int pitch, int style, uint16_t preferred_font)
291 {
292 	unsigned int i, def_style;
293 	unsigned int page = c >> FONTMAPPER_VECTOR_BASE;
294 
295 	if ( preferred_font > 0 && can_substitute(c, pitch, preferred_font)) {
296 		return preferred_font;
297 	}
298 
299 	if ( font_active_entries.count > page && font_active_entries.items[page] ) {
300 		PList fonts = (PList) font_active_entries.items[page];
301 		for (i = 0; i < fonts->count; i++) {
302 			int fid = (int) fonts->items[i];
303 			if ( style >= 0 ) {
304 				PPassiveFontEntry pfe = PASSIVE_FONT(fid);
305 				if ( pfe-> font.style != style ) continue;
306 			}
307 			if ( can_substitute(c, pitch, fid))
308 				return fid;
309 		}
310 	}
311 
312 	def_style = (style >= 0) ? style : 0;
313 	if ( font_mapper_default_id[def_style] == -1 ) {
314 		Font font;
315 		char *key;
316 		uint16_t fid;
317 		apc_font_default( &font);
318 		font_mapper_default_id[def_style] = -2;
319 		key = font_key(font.name, def_style);
320 		fid = PTR2IV(hash_fetch(font_substitutions, key, strlen(key)));
321 		if ( fid > 0 )
322 			font_mapper_default_id[def_style] = fid;
323 	}
324 
325 	if ( font_mapper_default_id[def_style] >= 0 && can_substitute(c, pitch, font_mapper_default_id[def_style]))
326 		return font_mapper_default_id[def_style];
327 
328 	for ( i = 1; i < font_passive_entries.count; i++) {
329 		if ( style >= 0 ) {
330 			PPassiveFontEntry pfe = PASSIVE_FONT(i);
331 			if ( pfe-> font.style != style ) continue;
332 		}
333 		if ( can_substitute(c, pitch, i))
334 			return i;
335 	}
336 
337 	if ( pitch == fpFixed ) {
338 		if ( font_mapper_default_id[def_style] >= 0 && can_substitute(c, pitch, font_mapper_default_id[def_style]))
339 			return font_mapper_default_id[def_style];
340 		for ( i = 1; i < font_passive_entries.count; i++) {
341 			if ( style >= 0 ) {
342 				PPassiveFontEntry pfe = PASSIVE_FONT(i);
343 				if ( pfe->font.style != style ) continue;
344 			}
345 			if ( can_substitute(c, fpDefault, i))
346 				return i;
347 		}
348 	}
349 
350 	if ( style >= 0 ) {
351 		if ( style & fsThin )
352 			return find_font(c, pitch, style & ~fsThin, preferred_font);
353 		if ( style & fsBold )
354 			return find_font(c, pitch, style & ~fsBold, preferred_font);
355 		if ( style & fsItalic )
356 			return find_font(c, pitch, style & ~fsItalic, preferred_font);
357 		return find_font(c, pitch, -1, preferred_font);
358 	}
359 
360 #ifdef _DEBUG
361 	printf("cannot map chr(%x)\n", c);
362 #endif
363 	return 0;
364 }
365 
366 SV *
Drawable_fontMapperPalette(Handle self,Bool set,int index,SV * sv)367 Drawable_fontMapperPalette( Handle self, Bool set, int index, SV * sv)
368 {
369 	if ( var->  stage > csFrozen) return NULL_SV;
370 	if ( set) {
371 		uint16_t fid;
372 		Font font;
373 		PPassiveFontEntry pfe;
374 		char * key;
375 
376 		SvHV_Font(sv, &font, "Drawable::fontMapperPalette");
377 		key = font_key(font.name, font.style);
378 		fid = PTR2IV(hash_fetch(font_substitutions, key, strlen(key)));
379 		if ( fid == 0 ) return NULL_SV;
380 		pfe = PASSIVE_FONT(fid);
381 
382 		switch ( index ) {
383 		case 0:
384 			/* delete */
385 			if ( !pfe-> is_active ) return NULL_SV;
386 			remove_active_font(fid);
387 			return newSViv(1);
388 		case 1:
389 			/* add */
390 			if ( pfe-> is_active ) return NULL_SV;
391 			add_active_font(fid);
392 			return newSViv(1);
393 		default:
394 			warn("Drawable::fontPalette(%d) operation is not defined", index);
395 			return NULL_SV;
396 		}
397 	} else if ( index < 0 ) {
398 		return newSViv( font_passive_entries.count );
399 	} else if ( index == 0 ) {
400 		char * key = font_key(var->font.name, var->font.style);
401 		index = PTR2IV(hash_fetch(font_substitutions, key, strlen(key)));
402 		return newSViv(index);
403 	} else {
404 		PFont f = prima_font_mapper_get_font(index);
405 		if (!f) return NULL_SV;
406 		return sv_Font2HV( f );
407 	}
408 }
409 
410 /* SECTION 2: GET TEXT WIDTH / TEXT OUT */
411 
412 static void*
read_subarray(AV * av,int index,int length_expected,int * plen,char * letter_expected,const char * caller,const char * subarray)413 read_subarray( AV * av, int index,
414 	int length_expected, int * plen, char * letter_expected,
415 	const char * caller, const char * subarray
416 ) {
417 	SV ** holder;
418 	void * ref;
419 	size_t length;
420 	char * letter;
421 	holder = av_fetch(av, index, 0);
422 
423 	if (
424 		!holder ||
425 		!*holder ||
426 		!SvOK(*holder)
427 	) {
428 		warn("invalid subarray #%d (%s) passed to %s", index, subarray, caller);
429 		return NULL;
430 	}
431 
432 	if ( !prima_array_parse( *holder, &ref, &length, &letter)) {
433 		warn("invalid subarray #%d (%s) passed to %s: %s", index, subarray, caller, "not a Prima::array");
434 		return NULL;
435 	}
436 
437 	if (*letter != *letter_expected) {
438 		warn("invalid subarray #%d (%s/%c) passed to %s: %s", index, subarray, *letter, caller, "not a Prima::array of 16-bit integers");
439 		return NULL;
440 	}
441 
442 	if ( length_expected >= 0 && length < length_expected ) {
443 		warn("invalid subarray #%d (%s) passed to %s: length must be at least %d", index, subarray, caller, length_expected);
444 		return NULL;
445 	}
446 	if ( plen ) *plen = length;
447 	return ref;
448 }
449 
450 static Bool
read_glyphs(PGlyphsOutRec t,SV * text,Bool indexes_required,const char * caller)451 read_glyphs( PGlyphsOutRec t, SV * text, Bool indexes_required, const char * caller)
452 {
453 	int len;
454 	AV* av;
455 	SV ** holder;
456 
457 	bzero(t, sizeof(GlyphsOutRec));
458 	/* assuming caller checked for SvTYPE( SvRV( text)) == SVt_PVAV */
459 
460 	av  = (AV*) SvRV(text);
461 
462 	/* is this just a single glyphstr? */
463 	if ( SvTIED_mg(( SV*) av, PERL_MAGIC_tied )) {
464 		void * ref;
465 		size_t length;
466 		char * letter;
467 
468 		if ( indexes_required ) {
469 			warn("%s requires glyphstr with indexes", caller);
470 			return false;
471 		}
472 
473 		if ( !prima_array_parse( text, &ref, &length, &letter) || *letter != 'S') {
474 			warn("invalid glyphstr passed to %s: %s", caller, "not a Prima::array");
475 			return false;
476 		}
477 
478 		t-> glyphs = ref;
479 		t-> len    = length;
480 		t-> text_len = 0;
481 		return true;
482 	}
483 
484 	len = av_len(av) + 1;
485 	if ( len > 5 ) len = 5; /* we don't need more */
486 	if ( len < 1 || len != 5 ) {
487 		warn("malformed glyphs array in %s", caller);
488 		return false;
489 	}
490 
491 	if ( !( t-> glyphs = read_subarray( av, 0, -1, &t->len, "S", caller, "glyphs")))
492 		return false;
493 	if ( t->len == 0 )
494 		return true;
495 
496 	holder = av_fetch(av, 4, 0);
497 	if ( holder && *holder && !SvOK(*holder))
498 		goto NO_FONTS;
499 	if ( !( t-> fonts = read_subarray( av, 4, t->len, NULL, "S", caller, "fonts")))
500 		return false;
501 
502 NO_FONTS:
503 	holder = av_fetch(av, 2, 0);
504 	if ( holder && *holder && !SvOK(*holder))
505 		goto NO_ADVANCES;
506 	if ( !( t-> advances = read_subarray( av, 2, t->len, NULL, "S", caller, "advances")))
507 		return false;
508 	if ( !( t-> positions = read_subarray( av, 3, t->len * 2, NULL, "s", caller, "positions")))
509 		return false;
510 
511 NO_ADVANCES:
512 	if ( !( t-> indexes = read_subarray( av, 1, t->len + 1, NULL, "S", caller, "indexes")))
513 		return false;
514 	t-> text_len = t-> indexes[t->len];
515 
516 	return true;
517 }
518 
519 static int
check_length(int from,int len,int real_len)520 check_length( int from, int len, int real_len )
521 {
522 	if ( len < 0 ) len = real_len;
523 	if ( from < 0 ) return 0;
524 	if ( from + len > real_len ) len = real_len - from;
525 	if ( len <= 0 ) return 0;
526 	return len;
527 }
528 
529 char *
hop_text(char * start,Bool utf8,int from)530 hop_text(char * start, Bool utf8, int from)
531 {
532 	if ( !utf8 ) return start + from;
533 	while ( from-- ) start = (char*)utf8_hop((U8*)start, 1);
534 	return start;
535 }
536 
537 void
hop_glyphs(GlyphsOutRec * t,int from,int len)538 hop_glyphs(GlyphsOutRec * t, int from, int len)
539 {
540 	if ( from == 0 && len == t->len ) return;
541 
542 	t->len      = len;
543 	t->glyphs  += from;
544 
545 	if ( t-> indexes ) {
546 		int i, max_index = 0, next_index = t->indexes[t->len];
547 		t->indexes += from;
548 		for ( i = 0; i <= len; i++ ) {
549 			int ix = t->indexes[i] & ~toRTL;
550 			if ( max_index < ix ) max_index = ix;
551 		}
552 		for ( i = 0; i <= t->len; i++ ) {
553 			int ix = t->indexes[i] & ~toRTL;
554 			if (ix > max_index && ix < next_index) next_index = ix;
555 		}
556 		t->text_len = next_index;
557 	}
558 
559 	if ( t->advances ) {
560 		t->advances  += from;
561 		t->positions += from * 2;
562 	}
563 
564 	if ( t-> fonts )
565 		t-> fonts    += from;
566 }
567 
568 static Bool
switch_font(Handle self,uint16_t fid)569 switch_font( Handle self, uint16_t fid)
570 {
571 	Font src, dst;
572 	src = PASSIVE_FONT(fid)->font;
573 	if ( is_opt(optSystemDrawable) ) {
574 		dst = var->font;
575 		src.size = dst.size;
576 		src.undef.size = 0;
577 		apc_font_pick( self, &src, &dst);
578 		if ( strcmp(dst.name, src.name) != 0 )
579 			return false;
580 		apc_gp_set_font( self, &dst);
581 	} else {
582 		dst = my->get_font(self);
583 		src.size = dst.size;
584 		src.undef.size = 0;
585 		my->set_font(self, src);
586 	}
587 	return true;
588 }
589 
590 Bool
Drawable_text_out(Handle self,SV * text,int x,int y,int from,int len)591 Drawable_text_out( Handle self, SV * text, int x, int y, int from, int len)
592 {
593 	Bool ok;
594 	if ( !opt_InPaint) return false;
595 
596 	if ( !SvROK( text )) {
597 		STRLEN dlen;
598 		char * c_text = SvPV( text, dlen);
599 		Bool   utf8 = prima_is_utf8_sv( text);
600 		CHECK_GP(false);
601 		if ( utf8) dlen = prima_utf8_length(c_text, dlen);
602 		if ((len = check_length(from,len,dlen)) == 0)
603 			return true;
604 		c_text = hop_text(c_text, utf8, from);
605 		ok = apc_gp_text_out( self, c_text, x, y, len, utf8 ? toUTF8 : 0);
606 		if ( !ok) perl_error();
607 	} else if ( SvTYPE( SvRV( text)) == SVt_PVAV) {
608 		GlyphsOutRec t;
609 		CHECK_GP(false);
610 		if (!read_glyphs(&t, text, 0, "Drawable::text_out"))
611 			return false;
612 		if (t.len == 0)
613 			return true;
614 		if (( len = check_length(from,len,t.len)) == 0)
615 			return true;
616 		hop_glyphs(&t, from, len);
617 		ok = apc_gp_glyphs_out( self, &t, x, y);
618 		if ( !ok) perl_error();
619 	} else {
620 		SV * ret = sv_call_perl(text, "text_out", "<Hiiii", self, x, y, from, len);
621 		ok = ret && SvTRUE(ret);
622 	}
623 	return ok;
624 }
625 
626 static PFontABC
627 call_get_font_abc( Handle self, unsigned int from, unsigned int to, int flags);
628 
629 static int
get_glyphs_width(Handle self,PGlyphsOutRec t,Bool add_overhangs)630 get_glyphs_width( Handle self, PGlyphsOutRec t, Bool add_overhangs)
631 {
632 	int i, ret;
633 	uint16_t * advances = t->advances;
634 
635 	for ( i = ret = 0; i < t-> len; i++)
636 		ret += *(advances++);
637 
638 	if ( add_overhangs ) {
639 		PFontABC abc;
640 		uint16_t glyph1 = t->glyphs[0], glyph2 = t->glyphs[t->len - 1];
641 
642 		abc = call_get_font_abc( self, glyph1, glyph1, toGlyphs);
643 		if ( !abc ) return ret;
644 		ret += ( abc->a < 0 ) ? (-abc->a + .5) : 0;
645 
646 		if ( glyph1 != glyph2 ) {
647 			free(abc);
648 			abc = call_get_font_abc( self, glyph2, glyph2, toGlyphs);
649 			if ( !abc ) return ret;
650 		}
651 		ret += ( abc->c < 0 ) ? (-abc->c + .5) : 0;
652 		free(abc);
653 	}
654 
655 	return ret;
656 }
657 
658 
659 int
Drawable_get_text_width(Handle self,SV * text,int flags,int from,int len)660 Drawable_get_text_width( Handle self, SV * text, int flags, int from, int len)
661 {
662 	gpARGS;
663 	int res;
664 
665 	if ( !SvROK( text )) {
666 		STRLEN dlen;
667 		char * c_text = SvPV( text, dlen);
668 		CHECK_GP(0);
669 		if ( prima_is_utf8_sv( text)) {
670 			dlen = utf8_length(( U8*) c_text, ( U8*) c_text + dlen);
671 			flags |= toUTF8;
672 		} else
673 			flags &= ~toUTF8;
674 		if (( len = check_length(from,len,dlen)) == 0)
675 			return 0;
676 		c_text = hop_text(c_text, flags & toUTF8, from);
677 		gpENTER(0);
678 		res = apc_gp_get_text_width( self, c_text, len, flags);
679 		gpLEAVE;
680 	} else if ( SvTYPE( SvRV( text)) == SVt_PVAV) {
681 		GlyphsOutRec t;
682 		CHECK_GP(0);
683 		if (!read_glyphs(&t, text, 0, "Drawable::get_text_width"))
684 			return false;
685 		if (t.len == 0)
686 			return true;
687 		if (( len = check_length(from,len,t.len)) == 0)
688 			return 0;
689 		hop_glyphs(&t, from, len);
690 		if (t.advances)
691 			return get_glyphs_width(self, &t, flags & toAddOverhangs);
692 		gpENTER(0);
693 		res = apc_gp_get_glyphs_width( self, &t);
694 		gpLEAVE;
695 	} else {
696 		SV * ret;
697 		gpENTER(0);
698 		ret = sv_call_perl(text, "get_text_width", "<Hiii", self, flags, from, len);
699 		gpLEAVE;
700 		res = (ret && SvOK(ret)) ? SvIV(ret) : 0;
701 	}
702 
703 	return res;
704 }
705 
706 static void
get_glyphs_box(Handle self,PGlyphsOutRec t,Point * pt)707 get_glyphs_box( Handle self, PGlyphsOutRec t, Point * pt)
708 {
709 	Bool text_out_baseline;
710 
711 	t-> flags = 0;
712 
713 	pt[0].y = pt[2]. y = var-> font. ascent - 1;
714 	pt[1].y = pt[3]. y = - var-> font. descent;
715 	pt[4].y = pt[0]. x = pt[1].x = 0;
716 	pt[3].x = pt[2]. x = pt[4].x = get_glyphs_width(self, t, false);
717 
718 	text_out_baseline = ( my-> textOutBaseline == Drawable_textOutBaseline) ?
719 		apc_gp_get_text_out_baseline(self) :
720 		my-> get_textOutBaseline(self);
721 	if ( !text_out_baseline ) {
722 		int i = 4, d = var->font. descent;
723 		while ( i--) pt[i]. y += d;
724 	}
725 
726 	if ( var-> font. direction != 0) {
727 		int i;
728 #define GRAD 57.29577951
729 		float s = sin( var-> font. direction / GRAD);
730 		float c = cos( var-> font. direction / GRAD);
731 #undef GRAD
732 		for ( i = 0; i < 5; i++) {
733 			float x = pt[i]. x * c - pt[i]. y * s;
734 			float y = pt[i]. x * s + pt[i]. y * c;
735 			pt[i]. x = x + (( x > 0) ? 0.5 : -0.5);
736 			pt[i]. y = y + (( y > 0) ? 0.5 : -0.5);
737 		}
738 	}
739 }
740 
741 SV *
Drawable_get_text_box(Handle self,SV * text,int from,int len)742 Drawable_get_text_box( Handle self, SV * text, int from, int len )
743 {
744 	gpARGS;
745 	Point * p;
746 	AV * av;
747 	int i, flags = 0;
748 	if ( !SvROK( text )) {
749 		STRLEN dlen;
750 		char * c_text = SvPV( text, dlen);
751 		CHECK_GP(NULL_SV);
752 
753 		if ( prima_is_utf8_sv( text)) {
754 			dlen = utf8_length(( U8*) c_text, ( U8*) c_text + dlen);
755 			flags |= toUTF8;
756 		}
757 		if ((len = check_length(from,len,dlen)) == 0)
758 			return newRV_noinc(( SV *) newAV());
759 		c_text = hop_text(c_text, flags & toUTF8, from);
760 		gpENTER( newRV_noinc(( SV *) newAV()));
761 		p = apc_gp_get_text_box( self, c_text, len, flags);
762 		gpLEAVE;
763 	} else if ( SvTYPE( SvRV( text)) == SVt_PVAV) {
764 		GlyphsOutRec t;
765 		CHECK_GP(NULL_SV);
766 		if (!read_glyphs(&t, text, 0, "Drawable::get_text_box"))
767 			return false;
768 		if (( len = check_length(from,len,t.len)) == 0)
769 			return newRV_noinc(( SV *) newAV());
770 		hop_glyphs(&t, from, len);
771 		if (t.advances) {
772 			if (!( p = malloc( sizeof(Point) * 5 )))
773 				return newRV_noinc(( SV *) newAV());
774 			get_glyphs_box(self, &t, p);
775 		} else {
776 			gpENTER( newRV_noinc(( SV *) newAV()));
777 			p = apc_gp_get_glyphs_box( self, &t);
778 			gpLEAVE;
779 		}
780 	} else {
781 		SV * ret;
782 		gpENTER( newRV_noinc(( SV *) newAV()));
783 		ret = newSVsv(sv_call_perl(text, "get_text_box", "<Hii", self, from, len ));
784 		gpLEAVE;
785 		return ret;
786 	}
787 
788 	av = newAV();
789 	if ( p) {
790 		for ( i = 0; i < 5; i++) {
791 			av_push( av, newSViv( p[ i]. x));
792 			av_push( av, newSViv( p[ i]. y));
793 		};
794 		free( p);
795 	}
796 	return newRV_noinc(( SV *) av);
797 }
798 
799 /* SECTION 3: SHAPING */
800 
801 static void*
warn_malloc(ssize_t size)802 warn_malloc(ssize_t size)
803 {
804 	void * ret;
805 	if (!(ret = malloc(size))) {
806 		warn("Drawable.text_shape: not enough memory");
807 		return NULL;
808 	}
809 	return ret;
810 }
811 
812 static uint32_t*
sv2uint32(SV * text,int * size,int * flags)813 sv2uint32( SV * text, int * size, int * flags)
814 {
815 	STRLEN dlen;
816 	register char * src;
817 	uint32_t *ret;
818 
819 	src = SvPV(text, dlen);
820 	if (prima_is_utf8_sv(text)) {
821 		*flags |= toUTF8;
822 		*size = prima_utf8_length(src, dlen);
823 	} else {
824 		*size = dlen;
825 	}
826 
827 	if (!(ret = ( uint32_t*) warn_malloc(sizeof(uint32_t) * (*size))))
828 		return NULL;
829 
830 	if (*flags & toUTF8 ) {
831 		uint32_t *dst = ret;
832 		while ( dlen > 0 && dst - ret < *size) {
833 			STRLEN charlen;
834 			UV uv;
835 			uv = prima_utf8_uvchr(src, dlen, &charlen);
836 			if ( uv > 0x10FFFF ) uv = 0x10FFFF;
837 			*(dst++) = uv;
838 			if ( charlen == 0 ) break;
839 			src  += charlen;
840 			dlen -= charlen;
841 		}
842 		*size = dst - ret;
843 	} else {
844 		register int i = *size;
845 		register uint32_t *dst = ret;
846 		while (i-- > 0) *(dst++) = *((unsigned char*) src++);
847 	}
848 
849 	return ret;
850 }
851 
852 /*
853 Iterator for individual shaper runs. Each run has same direction
854 and its indexes are increasing/decreasing monotonically. The latter is
855 to avoid situations where text A<PDF>B is being sent to shaper as single run AB
856 which might ligate.
857 */
858 
859 typedef struct {
860 	int i, vis_len;
861 	uint16_t index;
862 } BidiRunRec, *PBidiRunRec;
863 
864 static void
run_init(PBidiRunRec r,int visual_length)865 run_init(PBidiRunRec r, int visual_length)
866 {
867 	r->i       = 0;
868 	r->vis_len = visual_length;
869 }
870 
871 static int
run_next(PTextShapeRec t,PBidiRunRec r)872 run_next(PTextShapeRec t, PBidiRunRec r)
873 {
874 	int rtl, font, start = r->i;
875 
876 	if ( r->i >= r->vis_len ) return 0;
877 
878 	rtl  = t->analysis[r->i];
879 	if ( t-> fonts ) font = t->fonts[r->i];
880 	for ( ; r->i < r->vis_len + 1; r->i++) {
881 		if (
882 			r->i >= r->vis_len ||          /* eol */
883 			(t-> analysis[r->i] != rtl) || /* rtl != ltr */
884 			(t->fonts && font != t->fonts[r->i])
885 		) {
886 			return r->i - start;
887 		}
888 	}
889 
890 	return 0;
891 }
892 
893 #define INVERT(_type,_start,_end)             \
894 {                                             \
895 	register _type *s = _start, *e = _end;\
896 	while ( s < e ) {                     \
897 		register _type c = *s;        \
898 		*(s++) = *e;                  \
899 		*(e--) = c;                   \
900 	}                                     \
901 }
902 
903 static void
run_alloc(PTextShapeRec t,int visual_start,int visual_len,Bool invert_rtl,PTextShapeRec run)904 run_alloc( PTextShapeRec t, int visual_start, int visual_len, Bool invert_rtl, PTextShapeRec run)
905 {
906 	int i, flags = 0;
907 	bzero(run, sizeof(TextShapeRec));
908 
909 	run-> text         = t->text + visual_start;
910 	run-> v2l          = t->v2l + visual_start;
911 	if ( t-> fonts )
912 		run-> fonts  = t->fonts + visual_start;
913 	for ( i = 0; i < visual_len; i++) {
914 		register uint32_t c = run->text[i];
915 		if (
916 			(c >= 0x200e && c <= 0x200f) || /* dir marks */
917 			(c >= 0x202a && c <= 0x202e) || /* embedding */
918 			(c >= 0x2066 && c <= 0x2069)    /* isolates */
919 		)
920 			continue;
921 
922 		run->text[run->len] = run->text[i];
923 		run->v2l[run->len] = run->v2l[i];
924 		run->len++;
925 	}
926 	if ( t->analysis[visual_start] & 1) {
927 		if ( invert_rtl ) {
928 			INVERT(uint32_t, run->text, run->text + run->len - 1);
929 			INVERT(uint16_t, run->v2l,  run->v2l + run->len - 1);
930 		}
931 		flags = toRTL;
932 	}
933 	run-> flags        = ( t-> flags & ~toRTL ) | flags;
934 	run-> n_glyphs_max = t->n_glyphs_max - t-> n_glyphs;
935 	run-> glyphs       = t->glyphs  + t-> n_glyphs;
936 	run-> indexes      = t->indexes + t-> n_glyphs;
937 	if ( t-> positions )
938 		run-> positions = t->positions + t-> n_glyphs * 2;
939 	if ( t-> advances )
940 		run-> advances  = t->advances + t-> n_glyphs;
941 }
942 
943 
944 /* minimal bidi and unicode processing - does not swap RTLs */
945 static Bool
fallback_reorder(PTextShapeRec t)946 fallback_reorder(PTextShapeRec t)
947 {
948 	int i;
949 	register uint32_t* text = t->text;
950 	register Byte *analysis = t->analysis;
951 	register uint16_t* v2l  = t->v2l;
952 	for ( i = 0; i < t->len; i++) {
953 		register uint32_t c = *(text++);
954 		*(analysis++) = (
955 			(c == 0x590) ||
956 			(c == 0x5be) ||
957 			(c == 0x5c0) ||
958 			(c == 0x5c3) ||
959 			(c == 0x5c6) ||
960 			( c >= 0x5c8 && c <= 0x5ff) ||
961 			(c == 0x608) ||
962 			(c == 0x60b) ||
963 			(c == 0x60d) ||
964 			( c >= 0x61b && c <= 0x64a) ||
965 			( c >= 0x66d && c <= 0x66f) ||
966 			( c >= 0x671 && c <= 0x6d5) ||
967 			( c >= 0x6e5 && c <= 0x6e6) ||
968 			( c >= 0x6ee && c <= 0x6ef) ||
969 			( c >= 0x6fa && c <= 0x710) ||
970 			( c >= 0x712 && c <= 0x72f) ||
971 			( c >= 0x74b && c <= 0x7a5) ||
972 			( c >= 0x7b1 && c <= 0x7ea) ||
973 			( c >= 0x7f4 && c <= 0x7f5) ||
974 			( c >= 0x7fa && c <= 0x815) ||
975 			(c == 0x81a) ||
976 			(c == 0x824) ||
977 			(c == 0x828) ||
978 			( c >= 0x82e && c <= 0x858) ||
979 			( c >= 0x85c && c <= 0x8d3) ||
980 			(c == 0x200f) ||
981 			(c == 0xfb1d) ||
982 			( c >= 0xfb1f && c <= 0xfb28) ||
983 			( c >= 0xfb2a && c <= 0xfd3d) ||
984 			( c >= 0xfd40 && c <= 0xfdcf) ||
985 			( c >= 0xfdf0 && c <= 0xfdfc) ||
986 			( c >= 0xfdfe && c <= 0xfdff) ||
987 			( c >= 0xfe70 && c <= 0xfefe) ||
988 			( c >= 0x10800 && c <= 0x1091e) ||
989 			( c >= 0x10920 && c <= 0x10a00) ||
990 			(c == 0x10a04) ||
991 			( c >= 0x10a07 && c <= 0x10a0b) ||
992 			( c >= 0x10a10 && c <= 0x10a37) ||
993 			( c >= 0x10a3b && c <= 0x10a3e) ||
994 			( c >= 0x10a40 && c <= 0x10ae4) ||
995 			( c >= 0x10ae7 && c <= 0x10b38) ||
996 			( c >= 0x10b40 && c <= 0x10e5f) ||
997 			( c >= 0x10e7f && c <= 0x10fff) ||
998 			( c >= 0x1e800 && c <= 0x1e8cf) ||
999 			( c >= 0x1e8d7 && c <= 0x1e943) ||
1000 			( c >= 0x1e94b && c <= 0x1eeef) ||
1001 			( c >= 0x1eef2 && c <= 0x1efff)
1002 		) ? 1 : 0;
1003 		*(v2l++) = i;
1004 	}
1005 	return true;
1006 }
1007 
1008 #ifdef WITH_FRIBIDI
1009 /* fribidi unicode processing - swaps RTLs */
1010 static Bool
bidi_reorder(PTextShapeRec t,Bool arabic_shaping)1011 bidi_reorder(PTextShapeRec t, Bool arabic_shaping)
1012 {
1013 	Byte *buf, *ptr;
1014 	int i, sz, mlen;
1015 	FriBidiFlags flags = FRIBIDI_FLAGS_DEFAULT;
1016   	FriBidiParType   base_dir;
1017 	FriBidiCharType* types;
1018 	FriBidiArabicProp* ar;
1019 	FriBidiStrIndex* v2l;
1020 #if FRIBIDI_INTERFACE_VERSION > 3
1021 	FriBidiBracketType* bracket_types;
1022 #endif
1023 
1024 	mlen = sizeof(void*) * ((t->len / sizeof(void*)) + 1); /* align pointers */
1025 	sz = mlen * (
1026 		sizeof(FriBidiCharType) +
1027 		sizeof(FriBidiArabicProp) +
1028 		sizeof(FriBidiStrIndex) +
1029 #if FRIBIDI_INTERFACE_VERSION > 3
1030 		sizeof(FriBidiBracketType) +
1031 #endif
1032 		0
1033 	);
1034 	if ( !( buf = warn_malloc(sz)))
1035 		return false;
1036 	bzero(buf, sz);
1037 
1038 	types    = (FriBidiCharType*)   (ptr = buf);
1039 	ar       = (FriBidiArabicProp*) (ptr += mlen * sizeof(FriBidiCharType));
1040 	v2l      = (FriBidiStrIndex*)   (ptr += mlen * sizeof(FriBidiArabicProp));
1041 #if FRIBIDI_INTERFACE_VERSION > 3
1042 	bracket_types = (FriBidiBracketType*) (ptr += mlen * sizeof(FriBidiStrIndex));
1043 #endif
1044 	fribidi_get_bidi_types(t->text, t->len, types);
1045 	base_dir = ( t->flags & toRTL ) ? FRIBIDI_PAR_RTL : FRIBIDI_PAR_LTR;
1046 
1047 #if FRIBIDI_INTERFACE_VERSION > 3
1048 	fribidi_get_bracket_types(t->text, t->len, types, bracket_types);
1049 	if ( !fribidi_get_par_embedding_levels_ex(
1050 		types, bracket_types, t->len, &base_dir,
1051 		(FriBidiLevel*)t->analysis)) goto FAIL;
1052 #else
1053 	if ( !fribidi_get_par_embedding_levels(
1054 		types, t->len, &base_dir,
1055 		(FriBidiLevel*)t->analysis)) goto FAIL;
1056 #endif
1057 	if ( arabic_shaping ) {
1058 		flags |= FRIBIDI_FLAGS_ARABIC;
1059 		fribidi_get_joining_types(t->text, t->len, ar);
1060 		fribidi_join_arabic(types, t->len, (FriBidiLevel*)t->analysis, ar);
1061 		fribidi_shape(flags, (FriBidiLevel*)t->analysis, t->len, ar, t->text);
1062 	}
1063 
1064 	for ( i = 0; i < t->len; i++)
1065 		v2l[i] = i;
1066     	if ( !fribidi_reorder_line(flags, types, t->len, 0,
1067 		base_dir, (FriBidiLevel*)t->analysis, t->text, v2l))
1068 		goto FAIL;
1069 	for ( i = 0; i < t->len; i++)
1070 		t->v2l[i] = v2l[i];
1071 	free( buf );
1072 
1073 	return true;
1074 
1075 FAIL:
1076 	free( buf );
1077 	return false;
1078 }
1079 
1080 #endif
1081 
1082 static void
analyze_fonts(Handle self,PTextShapeRec t,uint16_t * fonts)1083 analyze_fonts( Handle self, PTextShapeRec t, uint16_t * fonts)
1084 {
1085 	int i;
1086 	uint32_t *text = t-> text;
1087 	int pitch      = (t->flags >> toPitch) & fpMask;
1088 	char * key     = font_key(var->font.name, var->font.style);
1089 	uint16_t fid   = PTR2IV(hash_fetch(font_substitutions, key, strlen(key)));
1090 
1091 	bzero(fonts, t->len * sizeof(uint16_t));
1092 
1093 	for ( i = 0; i < t->len; i++) {
1094 		unsigned int nfid = find_font(*(text++), pitch, var->font.style, fid);
1095 		if ( nfid != fid ) fonts[i] = nfid;
1096 	}
1097 
1098 	/* make sure that all combiners are same font as base glyph */
1099 	text = t->text;
1100 	for ( i = 0; i < t->len; ) {
1101 		int j, base, non_base_font = 0, need_align = 0;
1102 		if ( text[i] >= 0x300 && text[i] <= 0x36f ) {
1103 			i++;
1104 			continue;
1105 		}
1106 
1107 		base = i++;
1108 		for ( ; i < t->len; ) {
1109 			if ( text[i] < 0x300 || text[i] > 0x36f )
1110 				break;
1111 			if ( fonts[i] != fonts[base] ) {
1112 				need_align = 1;
1113 				non_base_font = fonts[i];
1114 				break;
1115 			}
1116 			i++;
1117 		}
1118 		if (!need_align) continue;
1119 
1120 		/* don't test all combiners for all fonts, such as f.ex. base glyph is Arial, ` is Courier, and ~ is Times.
1121 		   Wild guess is that if ` is supported, others should be too */
1122 		for ( j = base; j <= i; j++)
1123 			fonts[j] = non_base_font;
1124 
1125 	}
1126 }
1127 
1128 static Bool
shape_unicode(Handle self,PTextShapeRec t,PTextShapeFunc shaper,Bool glyph_mapper_only,Bool fribidi_arabic_shaping,Bool reorder)1129 shape_unicode(Handle self, PTextShapeRec t, PTextShapeFunc shaper,
1130 	Bool glyph_mapper_only, Bool fribidi_arabic_shaping, Bool reorder
1131 ) {
1132 	Bool ok, reorder_swaps_rtl, font_changed = false;
1133 	Byte analysis[MAX_CHARACTERS], *save_analysis;
1134 	uint16_t fonts[MAX_CHARACTERS], *save_fonts;
1135 	uint16_t i, l2v[MAX_CHARACTERS], run_offs, run_len;
1136 	BidiRunRec brr;
1137 
1138 #ifdef _DEBUG
1139 	printf("\n%s input: ", (t->flags & toRTL) ? "rtl" : "ltr");
1140 	for (i = 0; i < t->len; i++)
1141 		printf("%x ", t->text[i]);
1142 	printf("\n");
1143 #endif
1144 
1145 #ifdef WITH_FRIBIDI
1146 	if ( use_fribidi ) {
1147 		reorder_swaps_rtl = true;
1148 		ok = bidi_reorder(t, fribidi_arabic_shaping);
1149 	}
1150 	else
1151 #endif
1152 	{
1153 		reorder_swaps_rtl = false;
1154 		ok = fallback_reorder(t);
1155 	}
1156 	if (!ok) return false;
1157 
1158 	if ( !reorder )
1159 		reorder_swaps_rtl = !reorder_swaps_rtl;
1160 
1161 #ifdef _DEBUG
1162 	printf("%s output: ", (t->flags & toRTL) ? "rtl" : "ltr");
1163 	for (i = 0; i < t->len; i++)
1164 		printf("%d(%x) ", t->analysis[i], t->text[i]);
1165 	printf("\n");
1166 #endif
1167 
1168 	bzero(&l2v, MAX_CHARACTERS);
1169 	for ( i = 0; i < t->len; i++)
1170 		l2v[t->v2l[i]] = i;
1171 	for ( i = 0; i < t->len; i++)
1172 		analysis[i] = t->analysis[l2v[i]];
1173 	if ( t-> fonts )
1174 		analyze_fonts(self, t, fonts);
1175 
1176 #ifdef _DEBUG
1177 	printf("v2l: ");
1178 	for (i = 0; i < t->len; i++)
1179 		printf("%d ", t->v2l[i]);
1180 	printf("\n");
1181 	printf("l2v: ");
1182 	for (i = 0; i < t->len; i++)
1183 		printf("%d(%d) ", l2v[i], analysis[i]);
1184 	printf("\n");
1185 	if ( t-> fonts ) {
1186 		printf("fonts: ");
1187 		for (i = 0; i < t->len; i++)
1188 			printf("%d ", fonts[i]);
1189 		printf("\n");
1190 	}
1191 #endif
1192 
1193 	run_offs = 0;
1194 	run_init(&brr, t->len);
1195 	save_analysis = t->analysis;
1196 	t-> analysis = analysis;
1197 	save_fonts = t->fonts;
1198 	if ( t->fonts ) t-> fonts = fonts;
1199 	while (( run_len = run_next(t, &brr)) > 0) {
1200 		TextShapeRec run;
1201 		run_alloc(t, run_offs, run_len, glyph_mapper_only ^ reorder_swaps_rtl, &run);
1202 		if (run.len == 0) {
1203 			run_offs += run_len;
1204 			continue;
1205 		}
1206 #ifdef _DEBUG
1207 	{
1208 		int i;
1209 		printf("shaper input %s %d - %d: ", (run.flags & toRTL) ? "rtl" : "ltr", run_offs, run_offs + run.len - 1);
1210 		for (i = 0; i < run.len; i++) printf("%x ", run.text[i]);
1211 		printf("\n");
1212 	}
1213 #endif
1214 		if ( t-> fonts && ( run.fonts[0] != 0 || font_changed )) {
1215 			if ( run.fonts[0] == 0 ) {
1216 				apc_gp_set_font( self, &var->font);
1217 			} else {
1218 				if ( switch_font(self, run.fonts[0])) {
1219 #ifdef _DEBUG
1220 					printf("%d: set font #%d\n", run_offs, run.fonts[0]);
1221 #endif
1222 					font_changed = true;
1223 				}
1224 #ifdef _DEBUG
1225 				else {
1226 					printf("%d: failed to set font #%d\n", run_offs, run.fonts[0]);
1227 				}
1228 #endif
1229 			}
1230 		}
1231 		ok = shaper( self, &run );
1232 #ifdef _DEBUG
1233 	{
1234 		int i;
1235 		printf("shaper output: ");
1236 		for (i = 0; i < run.n_glyphs; i++)
1237 			printf("%d(%x) ", glyph_mapper_only ? -1 : run.indexes[i], run.glyphs[i]);
1238 		printf("\n");
1239 	}
1240 #endif
1241 		if (!ok) break;
1242 		for (i = t->n_glyphs; i < t->n_glyphs + run.n_glyphs; i++) {
1243 			int x = glyph_mapper_only ? i - t->n_glyphs : t->indexes[i];
1244 			t->indexes[i] = t->v2l[x + run_offs];
1245 			if ( t-> fonts ) save_fonts[i] = run.fonts[0];
1246 		}
1247 		run_offs += run_len;
1248 #ifdef _DEBUG
1249 	{
1250 		int i;
1251 		printf("indexes copied back: ");
1252 		if ( t-> fonts) printf("(font=%d) ", save_fonts[t->n_glyphs]);
1253 		for (i = t->n_glyphs; i < t->n_glyphs + run.n_glyphs; i++)
1254 			printf("%d ", t->indexes[i]);
1255 		printf("\n");
1256 	}
1257 #endif
1258 		t-> n_glyphs += run.n_glyphs;
1259 	}
1260 	t-> analysis = save_analysis;
1261 	t-> fonts = save_fonts;
1262 
1263 	if ( font_changed )
1264 		apc_gp_set_font( self, &var->font);
1265 
1266 	return ok;
1267 }
1268 
1269 static Bool
bidi_only_shaper(Handle self,PTextShapeRec r)1270 bidi_only_shaper( Handle self, PTextShapeRec r)
1271 {
1272         r-> n_glyphs = r->len;
1273 	bzero(r->glyphs, sizeof(uint16_t) * r->len);
1274 	if ( r-> advances ) {
1275 		bzero(r->advances, r->len * sizeof(uint16_t));
1276 		bzero(r->positions, r->len * 2 * sizeof(int16_t));
1277 	}
1278 	return true;
1279 }
1280 
1281 Bool
lang_is_rtl(void)1282 lang_is_rtl(void)
1283 {
1284 	static int cached = -1;
1285 	SV * app, *sub, *ret;
1286 
1287 	if ( cached >= 0 ) return cached;
1288 	app = newSVpv("Prima::Application", 0);
1289 	if ( !( sub = (SV*) sv_query_method( app, "lang_is_rtl", 0))) {
1290 		sv_free(app);
1291 		return cached = false;
1292 	}
1293 	ret = cv_call_perl( app, sub, "<");
1294 	sv_free(app);
1295 	return cached = (ret && SvOK(ret)) ? SvBOOL(ret) : 0;
1296 }
1297 
1298 SV*
Drawable_text_shape(Handle self,SV * text_sv,HV * profile)1299 Drawable_text_shape( Handle self, SV * text_sv, HV * profile)
1300 {
1301 	dPROFILE;
1302 	gpARGS;
1303 	int i;
1304 	SV * ret = NULL_SV,
1305 		*sv_glyphs = NULL_SV,
1306 		*sv_indexes = NULL_SV,
1307 		*sv_positions = NULL_SV,
1308 		*sv_advances = NULL_SV,
1309 		*sv_fonts = NULL_SV;
1310 	PTextShapeFunc system_shaper;
1311 	TextShapeRec t;
1312 	int shaper_type, level = tsDefault;
1313 	Bool skip_if_simple = false, return_zero = false, force_advances = false,
1314 		reorder = true, polyfont = true;
1315 	Bool gp_enter;
1316 
1317 	/* forward, if any */
1318 	if ( SvROK(text_sv)) {
1319 		SV * ref = newRV((SV*) profile);
1320 		gpENTER(NULL_SV);
1321 		ret = sv_call_perl(text_sv, "text_shape", "<HS", self, ref);
1322 		gpLEAVE;
1323 		hv_clear(profile); /* old gencls bork */
1324 		sv_free(ref);
1325 		return newSVsv(ret);
1326 	}
1327 	CHECK_GP(NULL_SV);
1328 	bzero(&t, sizeof(t));
1329 
1330 	/* asserts */
1331 #ifdef WITH_FRIBIDI
1332 	if ( use_fribidi && sizeof(FriBidiLevel) != 1) {
1333 		warn("sizeof(FriBidiLevel) != 1, fribidi is disabled");
1334 		use_fribidi = false;
1335 	}
1336 #endif
1337 
1338 	if ( pexist(rtl) ?
1339 		pget_B(rtl) :
1340 		(application ? PApplication(application)->textDirection : lang_is_rtl()
1341 	))
1342 		t.flags |= toRTL;
1343 	if ( pexist(language))
1344 		t.language = pget_c(language);
1345 	if ( pexist(skip_if_simple))
1346 		skip_if_simple = pget_B(skip_if_simple);
1347 	if ( pexist(advances))
1348 		force_advances = pget_B(advances);
1349 	if ( pexist(reorder)) {
1350 		reorder = pget_B(reorder);
1351 	}
1352 	if ( !reorder )
1353 		t.flags &= ~toRTL;
1354 	if ( pexist(level)) {
1355 		level = pget_i(level);
1356 		if ( level < tsNone || level > tsBytes ) level = tsFull;
1357 	}
1358 	if ( pexist(polyfont))
1359 		polyfont = pget_B(polyfont);
1360 	if ( level == tsBytes || level == tsNone )
1361 		polyfont = false;
1362 	if ( pexist(pitch)) {
1363 		int pitch = pget_i(pitch);
1364 		if ( pitch == fpVariable || pitch == fpFixed )
1365 			t.flags |= pitch << toPitch;
1366 	} else if ( var-> font. pitch == fpFixed )
1367 		t.flags |= fpFixed << toPitch;
1368 
1369 	hv_clear(profile); /* old gencls bork */
1370 
1371 	/* font supports shaping? */
1372 	if ( level == tsNone ) {
1373 		shaper_type    = tsNone;
1374 		force_advances = false;
1375 		gp_enter       = false;
1376 		system_shaper  = bidi_only_shaper;
1377 	} else {
1378 		shaper_type    = level;
1379 		gp_enter       = true;
1380 		gpENTER(NULL_SV);
1381 		if (!( system_shaper = apc_gp_get_text_shaper(self, &shaper_type))) {
1382 			return_zero = true;
1383 			goto EXIT;
1384 		}
1385 	}
1386 
1387 	if ( shaper_type == tsFull ) {
1388 		force_advances = false;
1389 		skip_if_simple = false; /* kerning may affect advances */
1390 	}
1391 
1392 	/* allocate buffers */
1393 	if (!(t.text = sv2uint32(text_sv, &t.len, &t.flags)))
1394 		goto EXIT;
1395 	if ( level == tsBytes ) {
1396 		int i;
1397 		uint32_t *c = t.text;
1398 		for ( i = 0; i < t.len; i++, c++)
1399 			if ( *c > 255 ) *c = 255;
1400 	}
1401 
1402 	if ( t.len == 0 ) {
1403 		return_zero = true;
1404 		goto EXIT;
1405 	}
1406 
1407 	if ( t.len > MAX_CHARACTERS ) {
1408 		warn("Drawable.text_shape: text too long, %dK max", MAX_CHARACTERS / 1024);
1409 		t.len = MAX_CHARACTERS;
1410 	}
1411 
1412 	if ( level != tsBytes ) {
1413 		if (!(t.analysis = warn_malloc(t.len)))
1414 			goto EXIT;
1415 		if (!(t.v2l = warn_malloc(sizeof(uint16_t) * t.len)))
1416 			goto EXIT;
1417 	}
1418 
1419 	/* MSDN, on ScriptShape: A reasonable value is (1.5 * cChars + 16) */
1420 	t.n_glyphs_max = t.len * 2 + 16;
1421 #define ALLOC(id,n,type) { \
1422 	sv_##id = prima_array_new( t.n_glyphs_max * n * sizeof(uint16_t)); \
1423 	t.id   = (type*) prima_array_get_storage(sv_##id); \
1424 }
1425 
1426 	ALLOC(glyphs,1,uint16_t);
1427 	ALLOC(indexes,1,uint16_t);
1428 	if ( shaper_type == tsFull || force_advances) {
1429 		ALLOC(positions,2,int16_t);
1430 		ALLOC(advances,1,uint16_t);
1431 	} else {
1432 		sv_positions = sv_advances = NULL_SV;
1433 		t.positions = NULL;
1434 		t.advances = NULL;
1435 	}
1436 	if ( polyfont ) {
1437 		ALLOC(fonts,1,uint16_t);
1438 		bzero(t.fonts, sizeof(uint16_t) * t.n_glyphs_max);
1439 	} else {
1440 		sv_fonts = NULL_SV;
1441 		t.fonts = NULL;
1442 	}
1443 #undef ALLOC
1444 
1445 	if ( level == tsBytes ) {
1446 		int i;
1447 		uint16_t * indexes;
1448 		if ( !system_shaper(self, &t))
1449 			goto EXIT;
1450 		for ( i = 0, indexes = t.indexes; i < t.n_glyphs; i++)
1451 			*(indexes++) = i;
1452 	} else {
1453 		if ( !shape_unicode(self, &t,
1454 			system_shaper, shaper_type < tsFull,
1455 			(level < tsFull) ? false : (shaper_type < tsFull), reorder)
1456 		) goto EXIT;
1457 	}
1458 
1459 	if ( skip_if_simple ) {
1460 		Bool is_simple = true;
1461 		for ( i = 0; i < t.n_glyphs; i++) {
1462 			if ( i != t.indexes[i] ) {
1463 				is_simple = false;
1464 				break;
1465 			}
1466 		}
1467 		if ( is_simple && !(t.flags & toUTF8))
1468 			for ( i = 0; i < t.len; i++) {
1469 				if (t.text[i] > 0x7f) {
1470 					is_simple = false;
1471 					break;
1472 				}
1473 			}
1474 		if ( is_simple ) {
1475 			return_zero = true;
1476 			goto EXIT;
1477 		}
1478 	}
1479 
1480 	if (gp_enter) gpLEAVE;
1481 
1482 	/* encode direction */
1483 	if ( level != tsBytes ) {
1484 		for ( i = 0; i < t.n_glyphs; i++) {
1485 			if ( t.analysis[ t.indexes[i] ] & 1 )
1486 				t.indexes[i] |= toRTL;
1487 		}
1488 	}
1489 	/* add an extra index as text length */
1490 	t.indexes[t.n_glyphs] = t.len;
1491 
1492 	free(t.v2l );
1493 	free(t.analysis );
1494 	free(t.text);
1495 	t.v2l = NULL;
1496 	t.analysis = NULL;
1497 	t.text = NULL;
1498 
1499 	if (sv_fonts != NULL_SV) {
1500 		int i, non_zero = 0;
1501 		for ( i = 0; i < t.n_glyphs; i++) {
1502 			if ( t.fonts[i] != 0 ) {
1503 				non_zero = 1;
1504 				break;
1505 			}
1506 		}
1507 		if (!non_zero) {
1508 			sv_free(sv_fonts);
1509 			sv_fonts = NULL_SV;
1510 		}
1511 	}
1512 
1513 #define BIND(x,n,d,letter) { \
1514 	prima_array_truncate(x, (t.n_glyphs * n + d) * sizeof(uint16_t)); \
1515 	x = sv_2mortal(prima_array_tie( x, sizeof(uint16_t), letter)); \
1516 }
1517 	BIND(sv_glyphs, 1, 0, "S");
1518 	BIND(sv_indexes, 1, 1, "S");
1519 	if ( sv_positions != NULL_SV ) BIND(sv_positions, 2, 0, "s");
1520 	if ( sv_advances  != NULL_SV ) BIND(sv_advances,  1, 0, "S");
1521 	if ( sv_fonts     != NULL_SV ) BIND(sv_fonts,     1, 0, "S");
1522 #undef BIND
1523 
1524 	return newSVsv(call_perl(self, "new_glyph_obj", "<SSSSS",
1525 		sv_glyphs, sv_indexes, sv_advances, sv_positions, sv_fonts
1526 	));
1527 
1528 EXIT:
1529 	if (gp_enter) gpLEAVE;
1530 	if ( t.text     ) free(t.text     );
1531 	if ( t.v2l      ) free(t.v2l      );
1532 	if ( t.analysis ) free(t.analysis );
1533 	if ( t.indexes  ) sv_free(sv_indexes );
1534 	if ( t.glyphs   ) sv_free(sv_glyphs   );
1535 	if ( t.positions) sv_free(sv_positions);
1536 	if ( t.advances ) sv_free(sv_advances );
1537 
1538 	return return_zero ? newSViv(0) : NULL_SV;
1539 }
1540 
1541 /* SECTION 4: TEXT WRAP */
1542 
1543 static PFontABC
find_abc_in_list_cache(PList p,int base)1544 find_abc_in_list_cache( PList p, int base )
1545 {
1546 	int i;
1547 	for ( i = 0; i < p-> count; i += 2)
1548 		if (( unsigned int) p-> items[ i] == base)
1549 			return ( PFontABC) p-> items[i + 1];
1550 	return NULL;
1551 }
1552 
1553 static Bool
fill_abc_list_cache(PList * cache,int base,PFontABC abc)1554 fill_abc_list_cache( PList * cache, int base, PFontABC abc)
1555 {
1556 	PList p;
1557 	if ( *cache == NULL )
1558 		*cache = plist_create( 8, 8);
1559 	if (( p = *cache) == NULL)
1560 		return false;
1561 	list_add( p, ( Handle) base);
1562 	list_add( p, ( Handle) abc);
1563 	return true;
1564 }
1565 
1566 static PFontABC
call_get_font_abc(Handle self,unsigned int from,unsigned int to,int flags)1567 call_get_font_abc( Handle self, unsigned int from, unsigned int to, int flags)
1568 {
1569 	PFontABC abc;
1570 
1571 	/* query ABC information */
1572 	if ( !self) {
1573 		abc = apc_gp_get_font_abc( self, from, to, flags);
1574 		if ( !abc) return NULL;
1575 	} else if ( my-> get_font_abc == Drawable_get_font_abc) {
1576 		gpARGS;
1577 		CHECK_GP(NULL);
1578 		gpENTER(NULL);
1579 		abc = apc_gp_get_font_abc( self, from, to, flags);
1580 		gpLEAVE;
1581 		if ( !abc) return NULL;
1582 	} else {
1583 		SV * sv;
1584 		int len = to - from + 1;
1585 		if ( !( abc = malloc(len * sizeof( FontABC)))) return NULL;
1586 		sv = my-> get_font_abc( self, from, to, flags);
1587 		if ( SvOK( sv) && SvROK( sv) && SvTYPE( SvRV( sv)) == SVt_PVAV) {
1588 			AV * av = ( AV*) SvRV( sv);
1589 			int i, j = 0, n = av_len( av) + 1;
1590 			if ( n > len * 3) n = len * 3;
1591 			n = ( n / 3) * 3;
1592 			if ( n < len) memset( abc, 0, len * sizeof( FontABC));
1593 			for ( i = 0; i < n; i += 3) {
1594 				SV ** holder = av_fetch( av, i, 0);
1595 				if ( holder) abc[j]. a = ( float) SvNV( *holder);
1596 				holder = av_fetch( av, i + 1, 0);
1597 				if ( holder) abc[j]. b = ( float) SvNV( *holder);
1598 				holder = av_fetch( av, i + 2, 0);
1599 				if ( holder) abc[j]. c = ( float) SvNV( *holder);
1600 				j++;
1601 			}
1602 		} else
1603 			memset( abc, 0, len * sizeof( FontABC));
1604 		sv_free( sv);
1605 	}
1606 
1607 	return abc;
1608 }
1609 
1610 static PFontABC
call_get_font_abc_base(Handle self,unsigned int base,int flags)1611 call_get_font_abc_base( Handle self, unsigned int base, int flags)
1612 {
1613 	return call_get_font_abc( self, base * 256, base * 256 + 255, flags);
1614 }
1615 
1616 static PFontABC
query_abc_range(Handle self,TextWrapRec * t,unsigned int base)1617 query_abc_range( Handle self, TextWrapRec * t, unsigned int base)
1618 {
1619 	PFontABC abc;
1620 
1621 	if ( t-> utf8_text) {
1622 		if (
1623 			*(t-> unicode) &&
1624 			(( abc = find_abc_in_list_cache( *(t->unicode), base)) != NULL)
1625 		)
1626 			return abc;
1627 	} else if (*( t-> ascii))
1628 		return *(t-> ascii);
1629 
1630 	if ( !( abc = call_get_font_abc_base(self, base, t-> utf8_text ? toUTF8 : 0)))
1631 		return NULL;
1632 
1633 	if ( t-> utf8_text) {
1634 		if ( !fill_abc_list_cache(t->unicode, base, abc)) {
1635 			free( abc);
1636 			return NULL;
1637 		}
1638 	} else
1639 		*(t-> ascii) = abc;
1640 
1641 	return abc;
1642 }
1643 
1644 static Bool
precalc_abc_buffer(PFontABC src,float * width,PFontABC dest)1645 precalc_abc_buffer( PFontABC src, float * width, PFontABC dest)
1646 {
1647 	int i;
1648 	if ( !dest) return false;
1649 	for ( i = 0; i < 256; i++) {
1650 		width[i] = src[i]. a + src[i]. b + src[i]. c;
1651 		dest[i]. a = ( src[i]. a < 0) ? - src[i]. a : 0;
1652 		dest[i]. b = src[i]. b;
1653 		dest[i]. c = ( src[i]. c < 0) ? - src[i]. c : 0;
1654 	}
1655 	return true;
1656 }
1657 
1658 static Bool
precalc_ac_buffer(PFontABC src,PFontABC dest)1659 precalc_ac_buffer( PFontABC src, PFontABC dest)
1660 {
1661 	int i;
1662 	if ( !dest) return false;
1663 	for ( i = 0; i < 256; i++) {
1664 		dest[i]. a = ( src[i]. a < 0) ? - src[i]. a : 0;
1665 		dest[i]. c = ( src[i]. c < 0) ? - src[i]. c : 0;
1666 	}
1667 	return true;
1668 }
1669 
1670 static Bool
fill_font_ranges(Handle self)1671 fill_font_ranges( Handle self )
1672 {
1673 	if ( Drawable_get_font_ranges == my->get_font_ranges ) {
1674 		CHECK_GP(false);
1675 		if ( !var-> font_abc_glyphs_ranges ) {
1676 			if ( !( var-> font_abc_glyphs_ranges = apc_gp_get_font_ranges(self, &var->font_abc_glyphs_n_ranges)))
1677 				return false;
1678 		}
1679 	} else {
1680 		SV * sv;
1681 		void * array;
1682 		Bool do_free;
1683 		sv = my-> get_font_ranges( self);
1684 		array = prima_read_array( sv, "get_font_ranges", 'i', 1, -1, -1, &var->font_abc_glyphs_n_ranges, &do_free);
1685 		if ( !array ) {
1686 			sv_free(sv);
1687 			return false;
1688 		}
1689 		if ( do_free ) {
1690 			var-> font_abc_glyphs_ranges = array;
1691 		} else {
1692 			int size = var->font_abc_glyphs_n_ranges * sizeof(int);
1693 			if ( !( var-> font_abc_glyphs_ranges = malloc(size))) {
1694 				warn("Not enough memory");
1695 				sv_free(sv);
1696 				return false;
1697 			}
1698 			memcpy( var-> font_abc_glyphs_ranges, array, size );
1699 			free(array);
1700 		}
1701 		sv_free(sv);
1702 	}
1703 	return true;
1704 }
1705 
1706 static PFontABC
query_abc_range_glyphs(Handle self,GlyphWrapRec * t,unsigned int base)1707 query_abc_range_glyphs( Handle self, GlyphWrapRec * t, unsigned int base)
1708 {
1709 	PFontABC abc;
1710 
1711 	if (
1712 		*(t-> cache) &&
1713 		(( abc = find_abc_in_list_cache( *(t->cache), base)) != NULL)
1714 	)
1715 		return abc;
1716 
1717 	if ( !( abc = call_get_font_abc_base(self, base, toGlyphs)))
1718 		return NULL;
1719 	if ( t->fonts) {
1720 		/* different fonts case */
1721 		Byte * fa;
1722 		PassiveFontEntry *pfe;
1723 		int i, font_changed = 0;
1724 		uint32_t from, to;
1725 		unsigned int page;
1726 		char * key;
1727 		Byte used_fonts[MAX_CHARACTERS / 8], filled_entries[256 / 8];
1728 
1729 		from = base * 256;
1730 		to   = from + 255;
1731 		page = from >> FONTMAPPER_VECTOR_BASE;
1732 		bzero(used_fonts, sizeof(used_fonts));
1733 		bzero(filled_entries, sizeof(filled_entries));
1734 		used_fonts[0] = 0x01; /* fid = 0 */
1735 		key = font_key(var->font.name, var->font.style);
1736 		i = PTR2IV(hash_fetch(font_substitutions, key, strlen(key)));
1737 		if ( i > 0 ) {
1738 			/* copy ranges from subst table */
1739 			pfe = PASSIVE_FONT(i);
1740 			if ( !pfe-> ranges_queried )
1741 				query_ranges(pfe);
1742 			if ( pfe-> vectors.count <= page ) goto NO_FONT_ABC; /* should be there, or some error */
1743 			/* page covers the 256 range in whole */
1744 			fa = (Byte *) pfe-> vectors.items[ page ];
1745 			if ( fa ) {
1746 				i = from & FONTMAPPER_VECTOR_MASK;
1747 				memcpy( filled_entries, fa + i, 256 / 8);
1748 			}
1749 		} else {
1750 			/* query the range and fill the cache */
1751 			unsigned long * ranges;
1752 			if ( !fill_font_ranges(self))
1753 				goto NO_FONT_ABC;
1754 			ranges = var-> font_abc_glyphs_ranges;
1755 			for ( i = 0; i < var->font_abc_glyphs_n_ranges; i += 2, ranges += 2 ) {
1756 				int j;
1757 				if ( ranges[0] > to || ranges[1] < from )
1758 					continue;
1759 				for ( j = ranges[0]; j <= ranges[1]; j++) {
1760 					if ( j >= from && j <= to )
1761 						filled_entries[(j - from) >> 3] |= 1 << ((j - from) & 7);
1762 				}
1763 			}
1764 		}
1765 
1766 		for ( i = 0; i < t->n_glyphs; i++) {
1767 			PFontABC abc2;
1768 			uint16_t fid = t->fonts[i];
1769 			uint32_t uv;
1770 			if ( used_fonts[fid >> 3] & ( 1 << (fid & 7)))
1771 				continue;
1772 			used_fonts[fid >> 3] |= 1 << (fid & 7);
1773 
1774 			pfe = PASSIVE_FONT(fid);
1775 			if ( !switch_font(self, fid))
1776 				continue;
1777 			font_changed = 1;
1778 
1779 			if ( !pfe-> ranges_queried )
1780 				query_ranges(pfe);
1781 			if ( pfe-> vectors.count <= page )
1782 				continue;
1783 
1784 			if ( !( abc2 = call_get_font_abc( self, from, to, toGlyphs)))
1785 				continue;
1786 
1787 			fa = (Byte *) pfe-> vectors.items[ page ];
1788 			if ( !fa ) continue;
1789 			for ( uv = from; uv <= to; uv++) {
1790 				unsigned int bit = uv & FONTMAPPER_VECTOR_MASK;
1791 				if (( fa[bit >> 3] & (1 << (bit & 7))) == 0) continue;
1792 				if ((filled_entries[(uv - from) >> 3] & (1 << ((uv - from) & 7))) != 0) continue;
1793 				filled_entries[(uv - from) >> 3] |= 1 << ((uv - from) & 7);
1794 				abc[uv - from] = abc2[uv - from];
1795 			}
1796 		}
1797 		if ( font_changed ) {
1798 			if ( Drawable_set_font == my->set_font && is_opt(optSystemDrawable))
1799 				apc_gp_set_font( self, &var->font);
1800 			else
1801 				my->set_font(self, var->font);
1802 		}
1803 	}
1804 NO_FONT_ABC:
1805 
1806 	if ( !fill_abc_list_cache(t->cache, base, abc)) {
1807 		free( abc);
1808 		return NULL;
1809 	}
1810 
1811 	return abc;
1812 }
1813 
1814 static int
find_tilde_position(TextWrapRec * t)1815 find_tilde_position( TextWrapRec * t )
1816 {
1817 	int i, tildeIndex = -100;
1818 
1819 	for ( i = 0; i < t-> textLen - 1; i++) {
1820 		if ( t-> text[ i] == '~') {
1821 			unsigned char c = t-> text[ i + 1];
1822 			if ( c == '~' || c < ' ') {
1823 				i++;
1824 				continue;
1825 			} else {
1826 				tildeIndex = i;
1827 				break;
1828 			}
1829 		}
1830 	}
1831 
1832 	return tildeIndex;
1833 }
1834 
1835 static void
fill_tilde_properties(Handle self,TextWrapRec * t,int tildeIndex,int tildePos,int tildeCharPos,int tildeOffset)1836 fill_tilde_properties(Handle self, TextWrapRec * t, int tildeIndex, int tildePos, int tildeCharPos, int tildeOffset)
1837 {
1838 	UV uv;
1839 	int base;
1840 	PFontABC abcc;
1841 	float start, end;
1842 
1843 	t-> t_bytepos = tildePos;
1844 	t-> t_pos  = tildeCharPos + (( t->options & twCollapseTilde ) ? 0 : 1);
1845 	t-> t_char = t-> text + tildeIndex + 1;
1846 	if ( t-> utf8_text) {
1847 		STRLEN len;
1848 		uv = prima_utf8_uvchr_end(t-> t_char, t->text + t-> textLen, &len);
1849 		if ( len == 0 ) return;
1850 	} else
1851 		uv = *(t->t_char);
1852 
1853 	abcc = query_abc_range( self, t, base = uv / 256) + (uv & 0xff);
1854 	start = tildeOffset;
1855 	end   = start + ((abcc->a < 0) ? 0 : abcc->a) + abcc-> b + ((abcc->c < 0) ? 0 : abcc->c) - 1.0;
1856 	if ( abcc-> a < 0.0 ) {
1857 		start += abcc->a;
1858 		end += abcc->a;
1859 	}
1860 	t-> t_start = start + .5 * (( start < 0 ) ? -1 : 1);
1861 	t-> t_end   = end   + .5 * (( end   < 0 ) ? -1 : 1);
1862 }
1863 
1864 static void
text_init_wrap_rec(Handle self,SV * text,int width,int options,int tabIndent,int from,int len,TextWrapRec * t)1865 text_init_wrap_rec( Handle self, SV * text, int width, int options, int tabIndent, int from, int len, TextWrapRec * t)
1866 {
1867 	STRLEN tlen;
1868 
1869 	t-> text      = SvPV( text, tlen);
1870 	t-> utf8_text = prima_is_utf8_sv( text);
1871 	if ( t-> utf8_text) {
1872 		t-> utf8_textLen = prima_utf8_length( t-> text, tlen);
1873 		if (( t-> utf8_textLen = check_length(from, len, t-> utf8_textLen)) == 0)
1874 			from = 0;
1875 		t-> text = hop_text(t->text, true, from);
1876 		t-> textLen = utf8_hop(( U8*) t-> text, t-> utf8_textLen) - (U8*) t-> text;
1877 	} else {
1878 		if ((tlen = check_length(from, len, tlen)) == 0)
1879 			from = 0;
1880 		t-> text = hop_text(t->text, false, from);
1881 		t-> utf8_textLen = t-> textLen = tlen;
1882 	}
1883 
1884 	t-> width     = width;
1885 	t-> tabIndent = ( tabIndent < 0) ? 0 : tabIndent;
1886 	t-> options   = options;
1887 	t-> ascii     = &var-> font_abc_ascii;
1888 	t-> unicode   = &var-> font_abc_unicode;
1889 	t-> t_char    = NULL;
1890 	t-> t_start   = C_NUMERIC_UNDEF;
1891 	t-> t_end     = C_NUMERIC_UNDEF;
1892 	t-> t_line    = C_NUMERIC_UNDEF;
1893 	t-> t_pos     = C_NUMERIC_UNDEF;
1894 	t-> t_bytepos = C_NUMERIC_UNDEF;
1895 	t-> count     = 0;
1896 }
1897 
1898 static SV*
mnemonic2sv(TextWrapRec * t)1899 mnemonic2sv(TextWrapRec * t)
1900 {
1901 	HV * profile = newHV();
1902 	if ( t-> t_char) {
1903 		STRLEN len = t-> utf8_text ? utf8_hop(( U8*) t-> t_char, 1) - ( U8*) t-> t_char : 1;
1904 		SV * sv_char = newSVpv( t-> t_char, len);
1905 		pset_sv_noinc( tildeChar, sv_char);
1906 		if ( t->utf8_text) SvUTF8_on( sv_char);
1907 		if ( t->t_start != C_NUMERIC_UNDEF) pset_i( tildeStart, t->t_start);
1908 		if ( t->t_end   != C_NUMERIC_UNDEF) pset_i( tildeEnd,   t->t_end);
1909 		if ( t->t_line  != C_NUMERIC_UNDEF) pset_i( tildeLine,  t->t_line);
1910 		if ( t->t_pos   != C_NUMERIC_UNDEF) pset_i( tildePos,   t->t_pos);
1911 	}
1912 	return newRV_noinc(( SV *) profile);
1913 }
1914 
1915 static SV*
first_line2sv(int * c,int count)1916 first_line2sv(int * c, int count )
1917 {
1918 	int rlen = ( c && count > 0) ? c[3] : 0;
1919 	return newSViv( rlen);
1920 }
1921 
1922 static SV*
chunks2sv(Handle self,int from,int * c,int count)1923 chunks2sv(Handle self, int from, int * c, int count)
1924 {
1925 	int i;
1926 	AV * av = newAV();
1927 
1928 	for ( i = 0; i < count; i += 4) {
1929 		av_push( av, newSViv(from + c[i+2]) );
1930 		av_push( av, newSViv(c[i+3]) );
1931 	}
1932 
1933 	return (SV*)av;
1934 }
1935 
1936 static SV*
textout2sv(Handle self,int * c,TextWrapRec * t)1937 textout2sv(Handle self, int * c, TextWrapRec * t)
1938 {
1939 	int i, line;
1940 	char buf[256];
1941 	semistatic_t pbuf;
1942 	AV * av = newAV();
1943 
1944 	if ( t-> options & twExpandTabs )
1945 		semistatic_init(&pbuf, &buf, 1, 256);
1946 
1947 	for ( i = line = 0; i < t-> count; i += 4, line++) {
1948 		SV * sv;
1949 		if ( t-> options & twExpandTabs ) {
1950 			int j, nt, len = c[i+1], sz;
1951 			char *src, *dst;
1952 			for ( j = nt = 0, src = t->text + c[i]; j < len; j++, src++ )
1953 				if ( *src == '\t' )
1954 					nt++;
1955 			if ( nt == 0 ) goto AS_IS;
1956 
1957 			sz = len + nt * (t->tabIndent - 1) + 1;
1958 			if ( !semistatic_expand(&pbuf, sz)) {
1959 				warn("Not enough memory");
1960 				sv_free((SV*) av);
1961 				return NULL_SV;
1962 			}
1963 			for ( j = 0, src = t->text + c[i], dst = (char*)pbuf.heap; j < len; j++, src++ ) {
1964 				if ( *src == '\t') {
1965 					int k;
1966 					for ( k = 0; k < t->tabIndent; k++ )
1967 						*(dst++) = ' ';
1968 				}
1969 				else
1970 					*(dst++) = *src;
1971 			}
1972 			sv = newSVpv((char*) pbuf.heap, sz - 1 );
1973 		} else {
1974 		AS_IS:
1975 			sv = newSVpv( t->text + c[i], c[i+1]);
1976 		}
1977 		if (( t-> options & twCollapseTilde) && ( line == t-> t_line) && t-> t_char) {
1978 			STRLEN tlen;
1979 			char * pv = SvPV( sv, tlen );
1980 			memmove( pv + t-> t_bytepos, pv + t-> t_bytepos + 1, tlen - t-> t_bytepos - 1);
1981 			pv[tlen] = 0;
1982 			SvCUR_set(sv, tlen-1);
1983 			SvPOK_only(sv);
1984 		}
1985 
1986 		if ( t->utf8_text) SvUTF8_on( sv);
1987 		av_push( av, sv );
1988 	}
1989 
1990 	if ( t-> options & twExpandTabs )
1991 		semistatic_done(&pbuf);
1992 
1993 	return (SV*)av;
1994 }
1995 
1996 typedef struct {
1997 	int * storage;
1998 	unsigned int bufsize, base, options;
1999 	int width, limit, utf8_limit;
2000 	float widths[256];
2001 	FontABC abcs[256];
2002 	struct {
2003 		int start, utf8_start;
2004 		int end;
2005 		int split_start, utf8_split_start;
2006 		int split_end;
2007 		int p, utf8_p;
2008 	} curr, prev;
2009 	Bool do_width_break, first_only;
2010 	int tilde_index, tilde_line, tilde_pos, tilde_char_pos, tilde_offset;
2011 } WrapRec;
2012 
2013 static Bool
wrap_init(WrapRec * w,TextWrapRec * tw,GlyphWrapRec * gw)2014 wrap_init( WrapRec * w, TextWrapRec * tw, GlyphWrapRec * gw)
2015 {
2016 	bzero( w, sizeof(WrapRec));
2017 	w-> width            = tw ? tw->width        : gw->width;
2018 	w-> options          = tw ? tw->options      : gw->options;
2019 	w-> limit            = tw ? tw->textLen      : gw->n_glyphs;
2020 	w-> utf8_limit       = tw ? tw->utf8_textLen : gw->n_glyphs;
2021 
2022 	w-> do_width_break   = w->width >= 0;
2023 	w-> first_only       = (w->options & twReturnFirstLineLength) == twReturnFirstLineLength;
2024 
2025 	w-> base             = 0x10000000;
2026 
2027 	w-> curr.split_start = w-> curr.split_end = w-> curr.utf8_split_start = -1;
2028 	w-> prev             = w-> curr;
2029 
2030 	w-> bufsize          = 128;
2031 	if (!( w-> storage = allocn( int, w-> bufsize))) return false;
2032 
2033 	w-> tilde_index      = -100;
2034 
2035 	return true;
2036 }
2037 
2038 static Bool
wrap_add_entry(WrapRec * w,TextWrapRec * tw,GlyphWrapRec * gw,int end,int utf_end)2039 wrap_add_entry( WrapRec * w, TextWrapRec * tw, GlyphWrapRec * gw, int end, int utf_end )
2040 {
2041 	int *count = tw ? &tw->count : &gw->count;
2042 	if ( *count == w-> bufsize) {
2043 		int * n;
2044 		if ( !( n = (int *)realloc( w->storage, sizeof(unsigned int) * (w->bufsize *= 2))))
2045 			return false;
2046 		w->storage = n;
2047 	}
2048 #ifdef _DEBUG
2049 	printf("add %d - %d\n", w-> curr. utf8_start, end);
2050 #endif
2051 
2052 	if (
2053 		tw &&
2054 		w-> tilde_index >= 0 &&
2055 		w-> tilde_index >= w-> curr.start &&
2056 		w-> tilde_index < end
2057 	) {
2058 		char
2059 			*line     = tw-> text + w-> curr.start,
2060 			*tilde_at = tw->text + w-> tilde_index;
2061 		w-> tilde_char_pos = 0;
2062 		while ( line < tilde_at ) {
2063 			line = (char*)utf8_hop((U8*)line, 1);
2064 			w-> tilde_char_pos++;
2065 		}
2066 		w-> tilde_line = tw-> t_line = *count / 4;
2067 		w-> tilde_pos  = w->tilde_index - w->curr.start;
2068 		if ( w-> tilde_index == end - 1) tw-> t_line++;
2069 	}
2070 
2071 	w-> storage[(*count)++] = w-> curr.start;
2072 	w-> storage[(*count)++] = end - w-> curr.start;
2073 	w-> storage[(*count)++] = w-> curr.utf8_start;
2074 	w-> storage[(*count)++] = utf_end - w-> curr.utf8_start;
2075 	if ( tw && gw ) gw-> count = tw-> count;
2076 
2077 	w-> curr.start      = end;
2078 	w-> curr.utf8_start = utf_end;
2079 
2080 	return !w-> first_only;
2081 }
2082 
2083 #define wrap_new_word(w,len)                      \
2084 	w.curr.split_start      = w.curr.p;       \
2085 	w.curr.split_end        = w.curr.p + len; \
2086 	w.curr.utf8_split_start = w.curr.utf8_p
2087 
2088 #define wrap_fetch_uvchr(w, tw, len)              \
2089 	((len = 1) && tw->utf8_text) ?            \
2090 		prima_utf8_uvchr_end(             \
2091 			tw->text + w.curr.p,      \
2092 			tw->text + tw-> textLen,  \
2093 			&len                      \
2094 		) :                               \
2095 		((unsigned char*)(tw-> text))[w.curr.p]
2096 
2097 #define wrap_step_ptr(w,len)                      \
2098 	w.curr.p += len;                          \
2099 	w.curr.utf8_p++
2100 
2101 static Bool
wrap_load_glyphs_abc(uint32_t uv,WrapRec * w,Handle self,GlyphWrapRec * g)2102 wrap_load_glyphs_abc(uint32_t uv, WrapRec * w, Handle self, GlyphWrapRec *g)
2103 {
2104 	PFontABC labc;
2105 	if ( uv / 256 == w-> base)
2106 		return true;
2107 	w-> base = uv / 256;
2108 	if ( !(labc = query_abc_range_glyphs( self, g, w->base)))
2109 		return false;
2110 	if ( g-> advances )
2111 		precalc_ac_buffer(labc, w->abcs);
2112 	else
2113 		precalc_abc_buffer(labc, w->widths, w->abcs);
2114 	return true;
2115 }
2116 
2117 /* a very quick check, if possible, if glyphstr fits */
2118 
2119 static SV*
glyphs_fit_quickcheck(Handle self,SV * glyphs,int width,int options,TextWrapRec * tw,GlyphsOutRec * g)2120 glyphs_fit_quickcheck(Handle self, SV * glyphs, int width, int options, TextWrapRec *tw, GlyphsOutRec *g)
2121 {
2122 	AV * av;
2123 	if (!(
2124 		(g->len == 0) ||
2125 		(g->advances && ( width >= get_glyphs_width(self, g, true)))
2126 	))
2127 		return NULL;
2128 
2129 	if (( options & twReturnFirstLineLength) == twReturnFirstLineLength)
2130 		return newSViv(tw ? tw-> utf8_textLen : g->len);
2131 
2132 	av = newAV();
2133 	if ( options & twReturnChunks) {
2134 		av_push( av, newSViv(0));
2135 		av_push( av, newSViv(tw ? tw-> utf8_textLen : g->len));
2136 	} else if ( !tw || options & twReturnGlyphs ) {
2137 		av_push( av, newSVsv(sv_call_perl(glyphs, "clone", "<S", glyphs)));
2138 	} else {
2139 		SV * sv = newSVpv( tw-> text, tw-> textLen );
2140 		if ( tw->utf8_text) SvUTF8_on( sv);
2141 		av_push( av, sv );
2142 	}
2143 	return newRV_noinc(( SV *) av);
2144 }
2145 
2146 static void
glyph_init_wrap_rec(Handle self,int width,int options,int offset,GlyphsOutRec * g,GlyphWrapRec * t)2147 glyph_init_wrap_rec( Handle self, int width, int options, int offset, GlyphsOutRec *g, GlyphWrapRec *t)
2148 {
2149 	t->offset    = offset;
2150 	t->n_glyphs  = g->len;
2151 	t->glyphs    = g->glyphs;
2152 	t->indexes   = g->indexes;
2153 	t->advances  = g->advances;
2154 	t->positions = g->positions;
2155 	t->fonts     = g->fonts;
2156 	t->width     = width;
2157 	t->text_len  = g->text_len;
2158 	t->options   = options;
2159 	t->cache     = &var-> font_abc_glyphs;
2160 	t->count     = 0;
2161 }
2162 
2163 static SV*
glyphout2sv(Handle self,int * c,GlyphsOutRec * g,TextWrapRec * tw,GlyphWrapRec * gw,uint16_t * log2vis)2164 glyphout2sv(Handle self, int * c, GlyphsOutRec *g, TextWrapRec *tw, GlyphWrapRec *gw, uint16_t* log2vis)
2165 {
2166 #define STATIC_BUF_SIZE 1024
2167 	int i, line;
2168 	AV * av;
2169 	int j,
2170 		mul[5] = { 1, 1, 1, 2, 1 },
2171 		extras[5] = {0, 1, 0, 0, 0},
2172 		got_tab = 0;
2173 	uint16_t *payload[5] = { g->glyphs, g->indexes, g->advances, (uint16_t*)g->positions, g->fonts };
2174 	uint16_t buf1[STATIC_BUF_SIZE];
2175 	uint32_t buf2[STATIC_BUF_SIZE];
2176 	semistatic_t sbuf, tbuf;
2177 
2178 	av = newAV();
2179 
2180 	semistatic_init(&sbuf, &buf1, sizeof(uint16_t), STATIC_BUF_SIZE);
2181 
2182 	if ( tw != NULL && (tw->options & twExpandTabs)) {
2183 		int k, l;
2184 		char *text;
2185 		semistatic_init(&tbuf, &buf2, sizeof(uint32_t), STATIC_BUF_SIZE);
2186 		for (
2187 			k = 0, l = 0, text = tw->text;
2188 			k < tw-> utf8_textLen;
2189 			k++, l++
2190 		) {
2191 			uint32_t uv;
2192 			STRLEN len;
2193 			if (tw->utf8_text) {
2194 				uv = prima_utf8_uvchr_end(text, tw->text + tw-> textLen, &len);
2195 				if ( len < 1 ) break;
2196 				text += len;
2197 			} else {
2198 				uv = *(text++);
2199 			}
2200 			if ( !semistatic_push(tbuf,uint32_t,uv))
2201 				goto FAIL;
2202 			if ( uv == '\t')
2203 				got_tab = 1;
2204 		}
2205 	}
2206 
2207 	for ( i = 2, line = 0; i < gw->count; i += 4, line++) {
2208 		SV *sv_payload[5];
2209 		uint16_t *dest[5];
2210 		int first_char = c[i] + gw->offset, last_char = first_char + c[i + 1];
2211 
2212 		sbuf.count = 0;
2213 
2214 		if ( tw && gw->indexes) {
2215 			/* copy subset by text and use indexes */
2216 			for ( j = 0; j < g->len; j++) {
2217 				int ix = g->indexes[j] & ~toRTL;
2218 				if ( ix < first_char || ix >= last_char ) continue;
2219 				if (
2220 					tw &&
2221 					( tw-> options & twCollapseTilde) &&
2222 					tw-> t_char &&
2223 					line == tw-> t_line &&
2224 					ix == tw-> t_pos
2225 				)
2226 					continue;
2227 				if ( !semistatic_push(sbuf,uint16_t,j))
2228 					goto FAIL;
2229 			}
2230 		} else {
2231 			/* copy as is */
2232 			for ( j = first_char; j < last_char; j++)
2233 				if ( !semistatic_push(sbuf,uint16_t,j))
2234 					goto FAIL;
2235 		}
2236 
2237 		for ( j = 0; j < 5; j++) {
2238 			SV * sv;
2239 			uint16_t *dst, k;
2240 			if ( payload[j] == NULL ) {
2241 				sv_payload[j] = NULL_SV;
2242 				continue;
2243 			}
2244 			sv  = prima_array_new(sizeof(uint16_t) * (sbuf.count * mul[j] + extras[j]));
2245 			dest[j] = dst = (uint16_t*)prima_array_get_storage(sv);
2246 			if ( mul[j] == 1 ) {
2247 				for ( k = 0; k < sbuf.count; k++)
2248 					*(dst++) = payload[j][semistatic_at(sbuf,uint16_t,k)];
2249 				if ( j == 1 )
2250 					*dst = payload[j][g->len];
2251 			} else {
2252 				for ( k = 0; k < sbuf.count; k++) {
2253 					int ix = semistatic_at(sbuf,uint16_t,k) * 2;
2254 					*(dst++) = payload[j][ix];
2255 					*(dst++) = payload[j][ix+1];
2256 				}
2257 			}
2258 
2259 			if ( j == 2 && got_tab ) {
2260 				uint16_t *advances = dest[2], *indexes = dest[1];
2261 				for ( k = 0; k < sbuf.count; k++)
2262 					if ( semistatic_at(tbuf, uint32_t, indexes[k] & ~toRTL) == '\t' )
2263 						advances[ k ] *= tw-> tabIndent;
2264 			}
2265 			sv_payload[j] = sv_2mortal(prima_array_tie( sv, sizeof(uint16_t), (j == 3) ? "s" : "S"));
2266 		}
2267 
2268 		av_push( av, newSVsv(
2269 			call_perl(self, "new_glyph_obj", "<SSSSS",
2270 				sv_payload[0],
2271 				sv_payload[1],
2272 				sv_payload[2],
2273 				sv_payload[3],
2274 				sv_payload[4]
2275 			)
2276 		));
2277 	}
2278 
2279 	semistatic_done(&sbuf);
2280 	return (SV*)av;
2281 #undef STATIC_BUF_SIZE
2282 
2283 FAIL:
2284 	semistatic_done(&sbuf);
2285 	sv_free((SV*)av);
2286 	return NULL_SV;
2287 }
2288 
2289 static uint16_t *
fill_log2vis(GlyphsOutRec * g,int from)2290 fill_log2vis(GlyphsOutRec *g, int from)
2291 {
2292 	int i;
2293 	uint16_t *l2v, *ix = g->indexes, last = 0;
2294 	if (( l2v = malloc( sizeof(uint16_t) * g->text_len)) == NULL)
2295 		return NULL;
2296 	if ( ix ) {
2297 		memset(l2v, 0xff, sizeof(uint16_t) * g->text_len);
2298 		for ( i = 0; i < g->len; i++) {
2299 			int v = ix[i] & ~toRTL;
2300 			if ( l2v[v] > i ) l2v[v] = i;
2301 		}
2302 		for ( i = 0; i < g->text_len; i++) {
2303 			if ( l2v[i] != 0xffff )
2304 				last = l2v[i];
2305 			else
2306 				l2v[i] = last;
2307 		}
2308 	} else {
2309 		for ( i = 0; i < g->text_len; i++)
2310 			l2v[i] = i;
2311 	}
2312 
2313 	return l2v;
2314 }
2315 
2316 int *
Drawable_do_text_wrap(Handle self,TextWrapRec * tw,GlyphWrapRec * gw,uint16_t * log2vis)2317 Drawable_do_text_wrap( Handle self, TextWrapRec * tw, GlyphWrapRec * gw, uint16_t * log2vis)
2318 {
2319 	WrapRec wr;
2320 	float w = 0, initial_overhang = 0;
2321 	Bool reassign_w = 0;
2322 	int space_width = -1, space_c = 0;
2323 
2324 	if ( !wrap_init(&wr, tw, gw))
2325 		return NULL;
2326 
2327 	/* determining ~ character location */
2328 	if ( wr.options & twCalcMnemonic)
2329 		wr.tilde_index = find_tilde_position(tw);
2330 
2331 #define ADD(ptr) \
2332 	if ( !wrap_add_entry( &wr, tw, gw, wr.curr.ptr, wr.curr.utf8_##ptr))  \
2333 		return wr.storage
2334 
2335 #define LOAD_ABC(x) \
2336 	if ( !wrap_load_glyphs_abc(x, &wr, self, gw)) \
2337 		return wr.storage
2338 
2339 #define RETURN_EMPTY if (1) {             \
2340 	if ( gw ) gw-> count = 0;         \
2341 	if ( tw ) tw-> count = 0;         \
2342 	return wr.storage;                \
2343 }
2344 
2345 	while ( wr.curr.p < wr.limit ) {
2346 		float dw, c;
2347 		unsigned int j, nc, ng, wmul = 1;
2348 		STRLEN len = 1;
2349 		uint32_t uv, uv0, last_uv = 0;
2350 		uint16_t index;
2351 
2352 		wr.prev = wr.curr;
2353 
2354 		/* nc: codepoints in the cluster */
2355 		if ( log2vis ) {
2356 			unsigned int v, cmp;
2357 			for (
2358 				nc = 1, v = wr.curr.utf8_p + 1, index = log2vis[wr.curr.utf8_p];
2359 				v < tw->utf8_textLen;
2360 				v++
2361 			) {
2362 				if ( log2vis[v] != index ) break;
2363 				nc++;
2364 			}
2365 			for (
2366 				ng = 1,
2367 					v = log2vis[wr.curr.utf8_p] + 1,
2368 					cmp = gw->indexes[log2vis[wr.curr.utf8_p]] & ~toRTL;
2369 				v < gw->n_glyphs;
2370 				v++
2371 			) {
2372 				if (( gw->indexes[v] & ~toRTL ) != cmp ) break;
2373 				ng++;
2374 			}
2375 		} else {
2376 			ng    = 1;
2377 			nc    = 1;
2378 			index = wr.curr.utf8_p;
2379 		}
2380 
2381 		uv = tw ? wrap_fetch_uvchr(wr,tw,len) : 0;
2382 		if ( !tw || nc > 1 ) goto NON_BREAKER;
2383 
2384 		if ( len < 1 ) break;
2385 
2386 		switch ( uv ) {
2387 		case '\n':
2388 		case 0x2028:
2389 		case 0x2029:
2390 			wrap_new_word(wr,len);
2391 			if (!( wr.options & twNewLineBreak))
2392 				goto NON_BREAKER;
2393 			break;
2394 
2395 		case ' ':
2396 			wrap_new_word(wr,len);
2397 			if (!( wr.options & twSpaceBreak))
2398 				goto NON_BREAKER;
2399 			break;
2400 		case '\t':
2401 			wrap_new_word(wr,len);
2402 			if ( wr.options & twCalcTabs)
2403 				wmul = tw->tabIndent;
2404 			if (!( wr.options & twSpaceBreak))
2405 				goto NON_BREAKER;
2406 			if ( space_width < 0 ) {
2407 				PFontABC s;
2408 				if ( !( s = query_abc_range( self, tw, 0)))
2409 					return wr.storage;
2410 				space_c     = (s[' '].c < 0) ? - s[' ']. c : 0;
2411 				space_width = (s[' '].a + s[' '].b + s[' '].c) * tw-> tabIndent;
2412 			}
2413 			dw   = space_width;
2414 			c    = space_c;
2415 			goto PREDEFINED_WIDTH;
2416 		case '~':
2417 			if ( wr.curr.p == wr.tilde_index ) {
2418 				wr.tilde_offset = w - initial_overhang;
2419 				dw = c = 0;
2420 				goto PREDEFINED_WIDTH;
2421 			}
2422 			goto NON_BREAKER;
2423 		default:
2424 			goto NON_BREAKER;
2425 		}
2426 		ADD(p);
2427 		wrap_step_ptr(wr, len);
2428 		wr.curr.start      = wr.curr.p;
2429 		wr.curr.utf8_start = wr.curr.utf8_p;
2430 		reassign_w = 1;
2431 		continue;
2432 	NON_BREAKER:
2433 
2434 		/* calculate widths */
2435 		dw = c = 0;
2436 		if ( gw ) {
2437 			for ( j = 0, uv = uv0 = 0; j < ng; j++) {
2438 				last_uv = uv;
2439 				uv = gw->glyphs[index + j];
2440 				if ( j == 0 ) uv0 = uv;
2441 				if (!gw-> advances || reassign_w) /* do not query ABC unnecessarily if advances are there */
2442 					LOAD_ABC(last_uv);
2443 				dw += (gw->advances ? gw->advances[index + j] : wr.widths[uv & 0xff]) * wmul;
2444 				if ( j == nc - 1 && !gw-> advances)
2445 					c = wr.abcs[uv & 0xff].c;
2446 				if ( reassign_w) {
2447 					w = initial_overhang = wr.abcs[uv & 0xff].a;
2448 					reassign_w = 0;
2449 				}
2450 			}
2451 		} else {
2452 			if ( uv / 256 != wr.base)
2453 				if ( !precalc_abc_buffer( query_abc_range( self, tw, wr.base = uv / 256), wr.widths, wr.abcs))
2454 					return wr.storage;
2455 			uv0 = uv;
2456 			dw  = wr.widths[uv & 0xff];
2457 			c   = wr.abcs[uv & 0xff].c;
2458 
2459 		}
2460 		if ( reassign_w) {
2461 			w = initial_overhang = wr.abcs[uv0 & 0xff].a;
2462 			reassign_w = 0;
2463 		}
2464 	PREDEFINED_WIDTH:
2465 
2466 		/* advance text pointers */
2467 		if ( tw ) {
2468 			for ( j = 0; j < nc; j++) {
2469 				wrap_fetch_uvchr(wr,tw,len);
2470 				if ( len < 1 ) break;
2471 				wrap_step_ptr(wr, len);
2472 			}
2473 		} else {
2474 			wrap_step_ptr(wr, 1);
2475 		}
2476 
2477 #ifdef _DEBUG
2478 		printf("i:%d/%d nc:%d ng:%d w:%f dw:%f c:%f index:%d uv:%x\n",  wr.curr.p, wr.curr.utf8_p, nc, ng, w, dw, c, index, uv0);
2479 #endif
2480 		if ( !wr.do_width_break || (w + dw + c <= wr.width)) {
2481 			w += dw;
2482 			continue;
2483 		}
2484 
2485 		if ( gw && gw-> advances && wr.prev.p > wr.curr.start ) {
2486 			/* this glyph is clearly out of bounds, but it could be that the previous was too.
2487 
2488 			The reason behind this complication is that fetching every glyphs A/C metrics under libXft,
2489 			on probably under win32, requires the whole glyph to be fetched. This hiccups if the string
2490 			or font size are so unfortunate that glyphs are being discarded often. But since C is used only
2491 			to check whether the last glyph hangs over the limit or not, we don't query C until necessary.
2492 			The complication is that we need to step back if the previous glyph's C was big enough to make it
2493 			not fit either.
2494 
2495 			The effect can be seen when selecting with mouse chinese text in podview in Prima/Drawable/Glyphs -
2496 			when each glyph is queried, it might take several seconds for each redraw.
2497 			*/
2498 			LOAD_ABC(last_uv);
2499 			if ( w + wr.abcs[last_uv & 0xff].c > wr.width ) /* ... and it is */
2500 				wr.curr = wr.prev;
2501 		}
2502 
2503 		if ( wr.prev.p == wr.curr.start) {
2504 			/* case when even single char cannot be fit in  */
2505 			if ( wr.options & twBreakSingle) RETURN_EMPTY;
2506 			/* or push this character disregarding the width */
2507 			ADD(p);
2508 			reassign_w = 1;
2509 		} else {
2510 			/* normal break condition */
2511 			if ( wr.options & twWordBreak) {
2512 				/* checking if break was at word boundary */
2513 				if ( wr.curr.start <= wr.curr.split_start) {
2514 					ADD(split_start);
2515 					wr.curr.p      = wr.curr.start      = wr.curr.split_end;
2516 					wr.curr.utf8_p = wr.curr.utf8_start = wr.curr.utf8_split_start + 1;
2517 					w = 0;
2518 					reassign_w = 1;
2519 					continue;
2520 				} else if ( wr.options & twBreakSingle) {
2521 					/* cannot be split */
2522 					RETURN_EMPTY;
2523 				}
2524 			}
2525 
2526 			/* repeat again */
2527 			wr.curr = wr.prev;
2528 			ADD(p);
2529 			reassign_w = 1;
2530 		}
2531 		w = 0;
2532 	}
2533 
2534 	/* adding or skipping last line */
2535 	if (
2536 		wr.limit - wr.curr.start > 0 ||
2537 		( tw && tw->count == 0) ||
2538 		( gw && gw->count == 0)
2539 	) {
2540 		wr.curr.p      = wr.limit;
2541 		wr.curr.utf8_p = wr.utf8_limit;
2542 		ADD(p);
2543 	}
2544 
2545 	/* fill ~ location */
2546 	if (tw && wr.tilde_index >= 0 && !(wr.options & twReturnChunks))
2547 		fill_tilde_properties(self, tw, wr.tilde_index, wr.tilde_pos, wr.tilde_char_pos, wr.tilde_offset);
2548 
2549 	return wr.storage;
2550 }
2551 #undef ADD
2552 #undef LOAD_ABC
2553 #undef RETURN_EMPTY
2554 
2555 static SV*
string_wrap(Handle self,SV * text,int width,int options,int tabIndent,int from,int len)2556 string_wrap( Handle self,SV * text, int width, int options, int tabIndent, int from, int len)
2557 {
2558 	gpARGS;
2559 	TextWrapRec t;
2560 	int * c;
2561 	SV *ret;
2562 
2563 	if ( options & twReturnGlyphs ) {
2564 		warn("Drawable.text_wrap only can use tw::ReturnGlyphs if glyphs are supplied");
2565 		options &= ~twReturnGlyphs;
2566 	}
2567 
2568 	text_init_wrap_rec( self, text, width, options, tabIndent, from, len, &t);
2569 	gpENTER(NULL_SV);
2570 	c = my->do_text_wrap( self, &t, NULL, NULL);
2571 	gpLEAVE;
2572 	t.t_pos += from;
2573 
2574 	if (( t. options & twReturnFirstLineLength) == twReturnFirstLineLength)
2575 		ret = first_line2sv(c, t.count);
2576 	else if ( !c)
2577 		return NULL_SV;
2578 	else if ( options & twReturnChunks ) {
2579 		SV * sv = chunks2sv(self, from, c, t.count);
2580 		ret = ( sv == NULL_SV ) ? NULL_SV : newRV_noinc(sv);
2581 	} else {
2582 		SV * av = textout2sv(self, c, &t);
2583 		if ( av != NULL_SV ) {
2584 			if  (t.options & ( twCalcMnemonic | twCollapseTilde))
2585 				av_push((AV*) av, mnemonic2sv(&t));
2586 			ret = newRV_noinc(av);
2587 		} else
2588 			ret = av;
2589 	}
2590 	free( c);
2591 
2592 	return ret;
2593 }
2594 
2595 
2596 static SV*
glyphs_wrap(Handle self,SV * text,int width,int options,int from,int len)2597 glyphs_wrap( Handle self, SV * text, int width, int options, int from, int len)
2598 {
2599 	gpARGS;
2600 	GlyphWrapRec t;
2601 	int * c;
2602 	GlyphsOutRec g;
2603 	SV *qt, *ret;
2604 
2605 	if (!read_glyphs(&g, text, 1, "Drawable::text_wrap"))
2606 		return NULL_SV;
2607 	if ((len = check_length(from, len, g.len)) == 0)
2608 		from = 0;
2609 	hop_glyphs(&g, from, len);
2610 	if (( qt = glyphs_fit_quickcheck(self, text, width, options, NULL, &g)) != NULL)
2611 		return qt;
2612 	glyph_init_wrap_rec( self, width, options, 0, &g, &t);
2613 	if (options & (twExpandTabs|twCollapseTilde|twCalcMnemonic|twCalcTabs|twWordBreak))
2614 		warn("Drawable::text_wrap(glyphs) does not accept tw::ExpandTabs,tw::CollapseTilde,tw::CalcMnemonic,tw::CalcTabs,tw::WordBreak");
2615 
2616 	gpENTER(NULL_SV);
2617 	c = my->do_text_wrap( self, NULL, &t, NULL);
2618 	gpLEAVE;
2619 
2620 	if (( t. options & twReturnFirstLineLength) == twReturnFirstLineLength)
2621 		ret = first_line2sv(c, t.count);
2622 	else if ( !c)
2623 		return NULL_SV;
2624 	else if ( options & twReturnChunks ) {
2625 		SV * sv = chunks2sv(self, from, c, t.count);
2626 		ret = (sv == NULL_SV) ? NULL_SV : newRV_noinc(sv);
2627 	} else {
2628 		SV * sv = glyphout2sv(self, c, &g, NULL, &t, NULL);
2629 		ret = (sv == NULL_SV) ? NULL_SV : newRV_noinc(sv);
2630 	}
2631 	free( c);
2632 
2633 	return ret;
2634 }
2635 
2636 static SV*
string_glyphs_wrap(Handle self,SV * text,int width,int options,int tabIndent,int from,int len,SV * glyphs)2637 string_glyphs_wrap( Handle self, SV * text, int width, int options, int tabIndent, int from, int len, SV * glyphs)
2638 {
2639 	gpARGS;
2640 	SV *qt, *ret, *av = NULL;
2641 	GlyphsOutRec g;
2642 	TextWrapRec tw;
2643 	GlyphWrapRec gw;
2644 	int *c;
2645 	void *subglyphs = NULL;
2646 	uint16_t *log2vis = NULL;
2647 
2648 	if ( !SvROK(glyphs) || SvTYPE( SvRV(glyphs)) != SVt_PVAV ) {
2649 		warn("Drawable::text_wrap: not a glyph array passed");
2650 		return NULL_SV;
2651 	}
2652 	if (!read_glyphs(&g, glyphs, 1, "Drawable::text_wrap"))
2653 		return NULL_SV;
2654 	text_init_wrap_rec( self, text, width, options, tabIndent, 0, -1, &tw);
2655 	if ( g.text_len != tw.utf8_textLen) {
2656 		warn("Drawable::text_wrap: text and glyphstr don't match");
2657 		return NULL_SV;
2658 	}
2659 	if ( from != 0 || len != -1 )
2660 		text_init_wrap_rec( self, text, width, options, tabIndent, from, len, &tw);
2661 
2662 	if (
2663 		from == 0 && len == -1 &&
2664 		!( options & (twCalcTabs|twExpandTabs|twSpaceBreak|twNewLineBreak|twCalcMnemonic|twCollapseTilde))
2665 	) {
2666 		if (( qt = glyphs_fit_quickcheck(self, glyphs, width, options, &tw, &g)) != NULL)
2667 			return qt;
2668 	}
2669 
2670 	glyph_init_wrap_rec( self, width, options, from, &g, &gw);
2671 	if ( g.indexes ) {
2672 		/* log2vis needs to address the whole string */
2673 		if ( !( log2vis = fill_log2vis(&g, from))) {
2674 			warn("not enough memory");
2675 			return NULL_SV;
2676 		}
2677 	}
2678 
2679 	gpENTER(NULL_SV);
2680 	c = my->do_text_wrap( self, &tw, &gw, log2vis + from);
2681 	gpLEAVE;
2682 	tw.t_pos += from;
2683 
2684 	if (( options & twReturnFirstLineLength) == twReturnFirstLineLength) {
2685 		ret = first_line2sv(c, gw.count);
2686 	} else if ( !c ) {
2687 		ret = NULL_SV;
2688 	} else if ( options & twReturnGlyphs ) {
2689 		av = glyphout2sv(self, c, &g, &tw, &gw, log2vis + from );
2690 		ret = ( av == NULL_SV ) ? NULL_SV : newRV_noinc(av);
2691 	} else if ( options & twReturnChunks ) {
2692 		SV * sv = chunks2sv(self, from, c, gw.count);
2693 		ret = ( sv == NULL_SV ) ? NULL_SV : newRV_noinc(sv);
2694 	} else {
2695 		av = textout2sv(self, c, &tw);
2696 		ret = ( av == NULL_SV ) ? NULL_SV : newRV_noinc(av);
2697 	}
2698 
2699 	if  (
2700 		(tw.options & ( twCalcMnemonic | twCollapseTilde)) &&
2701 		av &&
2702 		SvTYPE(av) == SVt_PVAV
2703 	)
2704 		av_push((AV*) av, mnemonic2sv(&tw));
2705 
2706 	if ( subglyphs ) free(subglyphs);
2707 	if ( log2vis) free(log2vis);
2708 	free( c);
2709 
2710 	return ret;
2711 }
2712 
2713 SV*
Drawable_text_wrap(Handle self,SV * text,int width,int options,int tabIndent,int from,int len,SV * glyphs)2714 Drawable_text_wrap( Handle self, SV * text, int width, int options, int tabIndent, int from, int len, SV * glyphs)
2715 {
2716 	if ( width < 0 ) width = INT_MAX;
2717 	if ( SvTYPE(glyphs) != SVt_NULL ) {
2718 		return string_glyphs_wrap(self, text, width, options, tabIndent, from, len, glyphs);
2719 	} else if ( !SvROK( text )) {
2720 		return string_wrap(self, text, width, options, tabIndent, from, len);
2721 	} else if ( SvTYPE( SvRV( text)) == SVt_PVAV) {
2722 		return glyphs_wrap(self, text, width, options, from, len);
2723 	} else {
2724 		SV * ret;
2725 		gpARGS;
2726 		gpENTER(
2727 			(( options & twReturnFirstLineLength) == twReturnFirstLineLength) ?
2728 				newSViv(0) : newRV_noinc(( SV *) newAV())
2729 		);
2730 		ret = newSVsv(sv_call_perl(text, "text_wrap", "<Hiiiii", self, width, options, tabIndent, from, len));
2731 		gpLEAVE;
2732 		return ret;
2733 	}
2734 }
2735 
2736 #ifdef __cplusplus
2737 }
2738 #endif
2739