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