1 /* Copyright (C) 1989, 1995, 1996, 1997, 1998, 1999 Aladdin Enterprises. All rights reserved. 2 3 This file is part of AFPL Ghostscript. 4 5 AFPL Ghostscript is distributed with NO WARRANTY OF ANY KIND. No author or 6 distributor accepts any responsibility for the consequences of using it, or 7 for whether it serves any particular purpose or works at all, unless he or 8 she says so in writing. Refer to the Aladdin Free Public License (the 9 "License") for full details. 10 11 Every copy of AFPL Ghostscript must include a copy of the License, normally 12 in a plain ASCII text file named PUBLIC. The License grants you the right 13 to copy, modify and redistribute AFPL Ghostscript, but only under certain 14 conditions described in the License. Among other things, the License 15 requires that the copyright notice and this notice be preserved on all 16 copies. 17 */ 18 19 /*$Id: gxchar.c,v 1.5.2.1 2001/10/26 16:35:25 raph Exp $ */ 20 /* Default implementation of text writing */ 21 #include "gx.h" 22 #include "memory_.h" 23 #include "string_.h" 24 #include "gserrors.h" 25 #include "gsstruct.h" 26 #include "gxfixed.h" /* ditto */ 27 #include "gxarith.h" 28 #include "gxmatrix.h" 29 #include "gzstate.h" 30 #include "gxcoord.h" 31 #include "gxdevice.h" 32 #include "gxdevmem.h" 33 #include "gxchar.h" 34 #include "gxfont.h" 35 #include "gxfont0.h" 36 #include "gxfcache.h" 37 #include "gspath.h" 38 #include "gzpath.h" 39 40 /* Define whether or not to cache characters rotated by angles other than */ 41 /* multiples of 90 degrees. */ 42 private const bool CACHE_ROTATED_CHARS = true; 43 44 /* Define whether or not to oversample characters at small sizes. */ 45 private const bool OVERSAMPLE = true; 46 47 /* Define the maximum size of a full temporary bitmap when rasterizing, */ 48 /* in bits (not bytes). */ 49 private const uint MAX_TEMP_BITMAP_BITS = 80000; 50 51 /* Define whether the show operation uses the character outline data, */ 52 /* as opposed to just needing the width (or nothing). */ 53 #define SHOW_USES_OUTLINE(penum)\ 54 !SHOW_IS(penum, TEXT_DO_NONE | TEXT_DO_CHARWIDTH) 55 56 /* Structure descriptors */ 57 public_st_gs_show_enum(); 58 extern_st(st_gs_text_enum); 59 extern_st(st_gs_state); /* only for testing */ 60 private 61 ENUM_PTRS_BEGIN(show_enum_enum_ptrs) 62 return ENUM_USING(st_gs_text_enum, vptr, size, index - 5); 63 ENUM_PTR(0, gs_show_enum, pgs); 64 ENUM_PTR(1, gs_show_enum, show_gstate); 65 ENUM_PTR3(2, gs_show_enum, dev_cache, dev_cache2, dev_null); 66 ENUM_PTRS_END 67 private RELOC_PTRS_WITH(show_enum_reloc_ptrs, gs_show_enum *eptr) 68 { 69 RELOC_USING(st_gs_text_enum, vptr, size); /* superclass */ 70 RELOC_VAR(eptr->pgs); 71 RELOC_VAR(eptr->show_gstate); 72 RELOC_PTR3(gs_show_enum, dev_cache, dev_cache2, dev_null); 73 } 74 RELOC_PTRS_END 75 76 /* Forward declarations */ 77 private int continue_kshow(P1(gs_show_enum *)); 78 private int continue_show(P1(gs_show_enum *)); 79 private int continue_show_update(P1(gs_show_enum *)); 80 private void show_set_scale(P1(gs_show_enum *)); 81 private int show_cache_setup(P1(gs_show_enum *)); 82 private int show_state_setup(P1(gs_show_enum *)); 83 private int show_origin_setup(P4(gs_state *, fixed, fixed, gs_char_path_mode)); 84 85 /* Accessors for current_char and current_glyph. */ 86 #define CURRENT_CHAR(penum) ((penum)->returned.current_char) 87 #define SET_CURRENT_CHAR(penum, chr)\ 88 ((penum)->returned.current_char = (chr)) 89 #define CURRENT_GLYPH(penum) ((penum)->returned.current_glyph) 90 #define SET_CURRENT_GLYPH(penum, glyph)\ 91 ((penum)->returned.current_glyph = (glyph)) 92 93 /* Allocate a show enumerator. */ 94 gs_show_enum * 95 gs_show_enum_alloc(gs_memory_t * mem, gs_state * pgs, client_name_t cname) 96 { 97 gs_show_enum *penum; 98 99 rc_alloc_struct_1(penum, gs_show_enum, &st_gs_show_enum, mem, 100 return 0, cname); 101 penum->rc.free = rc_free_text_enum; 102 penum->auto_release = true; /* old API */ 103 /* Initialize pointers for GC */ 104 penum->text.operation = 0; /* no pointers relevant */ 105 penum->dev = 0; 106 penum->pgs = pgs; 107 penum->show_gstate = 0; 108 penum->dev_cache = 0; 109 penum->dev_cache2 = 0; 110 penum->dev_null = 0; 111 penum->fstack.depth = -1; 112 return penum; 113 } 114 115 /* ------ Driver procedure ------ */ 116 117 private text_enum_proc_resync(gx_show_text_resync); 118 private text_enum_proc_process(gx_show_text_process); 119 private text_enum_proc_is_width_only(gx_show_text_is_width_only); 120 private text_enum_proc_current_width(gx_show_text_current_width); 121 private text_enum_proc_set_cache(gx_show_text_set_cache); 122 private text_enum_proc_retry(gx_show_text_retry); 123 private text_enum_proc_release(gx_show_text_release); /* not default */ 124 125 private const gs_text_enum_procs_t default_text_procs = { 126 gx_show_text_resync, gx_show_text_process, 127 gx_show_text_is_width_only, gx_show_text_current_width, 128 gx_show_text_set_cache, gx_show_text_retry, 129 gx_show_text_release 130 }; 131 132 int 133 gx_default_text_begin(gx_device * dev, gs_imager_state * pis, 134 const gs_text_params_t * text, gs_font * font, 135 gx_path * path, const gx_device_color * pdcolor, 136 const gx_clip_path * pcpath, 137 gs_memory_t * mem, gs_text_enum_t ** ppte) 138 { 139 uint operation = text->operation; 140 bool propagate_charpath = (operation & TEXT_DO_DRAW) != 0; 141 int code; 142 gs_state *pgs = (gs_state *)pis; 143 gs_show_enum *penum; 144 145 /* 146 * For the moment, require pis to be a gs_state *, since all the 147 * procedures for character rendering expect it. 148 */ 149 if (gs_object_type(mem, pis) != &st_gs_state) 150 return_error(gs_error_Fatal); 151 penum = gs_show_enum_alloc(mem, pgs, "gx_default_text_begin"); 152 if (!penum) 153 return_error(gs_error_VMerror); 154 code = gs_text_enum_init((gs_text_enum_t *)penum, &default_text_procs, 155 dev, pis, text, font, path, pdcolor, pcpath, mem); 156 if (code < 0) { 157 gs_free_object(mem, penum, "gx_default_text_begin"); 158 return code; 159 } 160 penum->auto_release = false; /* new API */ 161 penum->level = pgs->level; 162 if (operation & TEXT_DO_ANY_CHARPATH) 163 penum->charpath_flag = 164 (operation & TEXT_DO_FALSE_CHARPATH ? cpm_false_charpath : 165 operation & TEXT_DO_TRUE_CHARPATH ? cpm_true_charpath : 166 operation & TEXT_DO_FALSE_CHARBOXPATH ? cpm_false_charboxpath : 167 operation & TEXT_DO_TRUE_CHARBOXPATH ? cpm_true_charboxpath : 168 operation & TEXT_DO_CHARWIDTH ? cpm_charwidth : 169 cpm_show /* can't happen */ ); 170 else 171 penum->charpath_flag = 172 (propagate_charpath ? pgs->in_charpath : cpm_show); 173 penum->cc = 0; 174 penum->continue_proc = continue_show; 175 /* Note: show_state_setup may reset can_cache. */ 176 switch (penum->charpath_flag) { 177 case cpm_false_charpath: case cpm_true_charpath: 178 penum->can_cache = -1; break; 179 case cpm_false_charboxpath: case cpm_true_charboxpath: 180 penum->can_cache = 0; break; 181 case cpm_charwidth: 182 default: /* cpm_show */ 183 penum->can_cache = 1; break; 184 } 185 code = show_state_setup(penum); 186 if (code < 0) 187 return code; 188 penum->show_gstate = 189 (propagate_charpath && (pgs->in_charpath != 0) ? 190 pgs->show_gstate : pgs); 191 if (!(~operation & (TEXT_DO_NONE | TEXT_RETURN_WIDTH))) { 192 /* This is stringwidth. */ 193 gx_device_null *dev_null = 194 gs_alloc_struct(mem, gx_device_null, &st_device_null, 195 "stringwidth(dev_null)"); 196 197 if (dev_null == 0) 198 return_error(gs_error_VMerror); 199 /* Do an extra gsave and suppress output */ 200 if ((code = gs_gsave(pgs)) < 0) 201 return code; 202 penum->level = pgs->level; /* for level check in show_update */ 203 /* Set up a null device that forwards xfont requests properly. */ 204 gs_make_null_device(dev_null, gs_currentdevice_inline(pgs), mem); 205 pgs->ctm_default_set = false; 206 penum->dev_null = dev_null; 207 /* Retain this device, since it is referenced from the enumerator. */ 208 gx_device_retain((gx_device *)dev_null, true); 209 gs_setdevice_no_init(pgs, (gx_device *) dev_null); 210 /* Establish an arbitrary translation and current point. */ 211 gs_newpath(pgs); 212 gx_translate_to_fixed(pgs, fixed_0, fixed_0); 213 code = gx_path_add_point(pgs->path, fixed_0, fixed_0); 214 if (code < 0) 215 return code; 216 } 217 *ppte = (gs_text_enum_t *)penum; 218 return 0; 219 } 220 221 /* ------ Width/cache setting ------ */ 222 223 private int 224 set_char_width(P4(gs_show_enum *penum, gs_state *pgs, 225 floatp wx, floatp wy)), 226 set_cache_device(P6(gs_show_enum *penum, gs_state *pgs, 227 floatp llx, floatp lly, floatp urx, floatp ury)); 228 229 /* This is the default implementation of text enumerator set_cache. */ 230 private int 231 gx_show_text_set_cache(gs_text_enum_t *pte, const double *pw, 232 gs_text_cache_control_t control) 233 { 234 gs_show_enum *const penum = (gs_show_enum *)pte; 235 gs_state *pgs = penum->pgs; 236 237 switch (control) { 238 case TEXT_SET_CHAR_WIDTH: 239 return set_char_width(penum, pgs, pw[0], pw[1]); 240 case TEXT_SET_CACHE_DEVICE: { 241 int code = set_char_width(penum, pgs, pw[0], pw[1]); /* default is don't cache */ 242 243 if (code < 0) 244 return code; 245 return set_cache_device(penum, pgs, pw[2], pw[3], pw[4], pw[5]); 246 } 247 case TEXT_SET_CACHE_DEVICE2: { 248 int code; 249 250 if (gs_rootfont(pgs)->WMode) { 251 float vx = pw[8], vy = pw[9]; 252 gs_fixed_point pvxy, dvxy; 253 cached_char *cc; 254 255 if ((code = gs_point_transform2fixed(&pgs->ctm, -vx, -vy, &pvxy)) < 0 || 256 (code = gs_distance_transform2fixed(&pgs->ctm, vx, vy, &dvxy)) < 0 257 ) 258 return 0; /* don't cache */ 259 if ((code = set_char_width(penum, pgs, pw[6], pw[7])) < 0) 260 return code; 261 /* Adjust the origin by (vx, vy). */ 262 gx_translate_to_fixed(pgs, pvxy.x, pvxy.y); 263 code = set_cache_device(penum, pgs, pw[2], pw[3], pw[4], pw[5]); 264 if (code != 1) 265 return code; 266 /* Adjust the character origin too. */ 267 cc = penum->cc; 268 cc->offset.x += dvxy.x; 269 cc->offset.y += dvxy.y; 270 } else { 271 code = set_char_width(penum, pgs, pw[0], pw[1]); 272 if (code < 0) 273 return code; 274 code = set_cache_device(penum, pgs, pw[2], pw[3], pw[4], pw[5]); 275 } 276 return code; 277 } 278 default: 279 return_error(gs_error_rangecheck); 280 } 281 } 282 283 /* Set the character width. */ 284 /* Note that this returns 1 if the current show operation is */ 285 /* non-displaying (stringwidth or cshow). */ 286 private int 287 set_char_width(gs_show_enum *penum, gs_state *pgs, floatp wx, floatp wy) 288 { 289 int code; 290 291 if (penum->width_status != sws_none) 292 return_error(gs_error_undefined); 293 if ((code = gs_distance_transform2fixed(&pgs->ctm, wx, wy, &penum->wxy)) < 0) 294 return code; 295 /* Check whether we're setting the scalable width */ 296 /* for a cached xfont character. */ 297 if (penum->cc != 0) { 298 penum->cc->wxy = penum->wxy; 299 penum->width_status = sws_cache_width_only; 300 } else { 301 penum->width_status = sws_no_cache; 302 } 303 return !SHOW_IS_DRAWING(penum); 304 } 305 306 /* Set up the cache device if relevant. */ 307 /* Return 1 if we just set up a cache device. */ 308 /* Used by setcachedevice and setcachedevice2. */ 309 private int 310 set_cache_device(gs_show_enum * penum, gs_state * pgs, floatp llx, floatp lly, 311 floatp urx, floatp ury) 312 { 313 gs_glyph glyph; 314 315 /* See if we want to cache this character. */ 316 if (pgs->in_cachedevice) /* no recursion! */ 317 return 0; 318 if (SHOW_IS_ALL_OF(penum, TEXT_DO_NONE | TEXT_INTERVENE)) { /* cshow */ 319 int code; 320 if_debug0('k', "[k]no cache: cshow"); 321 code = gs_nulldevice(pgs); 322 if (code < 0) 323 return code; 324 return 0; 325 } 326 pgs->in_cachedevice = CACHE_DEVICE_NOT_CACHING; /* disable color/gray/image operators */ 327 /* We can only use the cache if we know the glyph. */ 328 glyph = CURRENT_GLYPH(penum); 329 if (glyph == gs_no_glyph) 330 return 0; 331 /* We can only use the cache if ctm is unchanged */ 332 /* (aside from a possible translation). */ 333 if (penum->can_cache <= 0 || !pgs->char_tm_valid) { 334 if_debug2('k', "[k]no cache: can_cache=%d, char_tm_valid=%d\n", 335 penum->can_cache, (int)pgs->char_tm_valid); 336 return 0; 337 } { 338 const gs_font *pfont = pgs->font; 339 gs_font_dir *dir = pfont->dir; 340 gx_device *dev = gs_currentdevice_inline(pgs); 341 int alpha_bits = 342 (*dev_proc(dev, get_alpha_bits)) (dev, go_text); 343 gs_log2_scale_point log2_scale; 344 static const fixed max_cdim[3] = 345 { 346 #define max_cd(n)\ 347 (fixed_1 << (arch_sizeof_short * 8 - n)) - (fixed_1 >> n) * 3 348 max_cd(0), max_cd(1), max_cd(2) 349 #undef max_cd 350 }; 351 ushort iwidth, iheight; 352 cached_char *cc; 353 gs_fixed_rect clip_box; 354 int code; 355 356 /* Compute the bounding box of the transformed character. */ 357 /* Since we accept arbitrary transformations, the extrema */ 358 /* may occur in any order; however, we can save some work */ 359 /* by observing that opposite corners before transforming */ 360 /* are still opposite afterwards. */ 361 gs_fixed_point cll, clr, cul, cur, cdim; 362 363 if ((code = gs_distance_transform2fixed(&pgs->ctm, llx, lly, &cll)) < 0 || 364 (code = gs_distance_transform2fixed(&pgs->ctm, llx, ury, &clr)) < 0 || 365 (code = gs_distance_transform2fixed(&pgs->ctm, urx, lly, &cul)) < 0 || 366 (code = gs_distance_transform2fixed(&pgs->ctm, urx, ury, &cur)) < 0 367 ) 368 return 0; /* don't cache */ 369 { 370 fixed ctemp; 371 372 #define swap(a, b) ctemp = a, a = b, b = ctemp 373 #define make_min(a, b) if ( (a) > (b) ) swap(a, b) 374 375 make_min(cll.x, cur.x); 376 make_min(cll.y, cur.y); 377 make_min(clr.x, cul.x); 378 make_min(clr.y, cul.y); 379 #undef make_min 380 #undef swap 381 } 382 /* Now take advantage of symmetry. */ 383 if (clr.x < cll.x) 384 cll.x = clr.x, cur.x = cul.x; 385 if (clr.y < cll.y) 386 cll.y = clr.y, cur.y = cul.y; 387 /* Now cll and cur are the extrema of the box. */ 388 cdim.x = cur.x - cll.x; 389 cdim.y = cur.y - cll.y; 390 show_set_scale(penum); 391 log2_scale.x = penum->log2_suggested_scale.x; 392 log2_scale.y = penum->log2_suggested_scale.y; 393 #ifdef DEBUG 394 if (gs_debug_c('k')) { 395 dlprintf6("[k]cbox=[%g %g %g %g] scale=%dx%d\n", 396 fixed2float(cll.x), fixed2float(cll.y), 397 fixed2float(cur.x), fixed2float(cur.y), 398 1 << log2_scale.x, 1 << log2_scale.y); 399 dlprintf6("[p] ctm=[%g %g %g %g %g %g]\n", 400 pgs->ctm.xx, pgs->ctm.xy, pgs->ctm.yx, pgs->ctm.yy, 401 pgs->ctm.tx, pgs->ctm.ty); 402 } 403 #endif 404 /* 405 * If the device wants anti-aliased text, 406 * increase the sampling scale to ensure that 407 * if we want N bits of alpha, we generate 408 * at least 2^N sampled bits per pixel. 409 */ 410 if (alpha_bits > 1) { 411 int more_bits = 412 alpha_bits - (log2_scale.x + log2_scale.y); 413 414 if (more_bits > 0) { 415 if (log2_scale.x <= log2_scale.y) { 416 log2_scale.x += (more_bits + 1) >> 1; 417 log2_scale.y += more_bits >> 1; 418 } else { 419 log2_scale.x += more_bits >> 1; 420 log2_scale.y += (more_bits + 1) >> 1; 421 } 422 } 423 } else if (!OVERSAMPLE || pfont->PaintType != 0) { 424 /* Don't oversample artificially stroked fonts. */ 425 log2_scale.x = log2_scale.y = 0; 426 } 427 if (cdim.x > max_cdim[log2_scale.x] || 428 cdim.y > max_cdim[log2_scale.y] 429 ) 430 return 0; /* much too big */ 431 iwidth = ((ushort) fixed2int_var(cdim.x) + 2) << log2_scale.x; 432 iheight = ((ushort) fixed2int_var(cdim.y) + 2) << log2_scale.y; 433 if_debug3('k', "[k]iwidth=%u iheight=%u dev_cache %s\n", 434 (uint) iwidth, (uint) iheight, 435 (penum->dev_cache == 0 ? "not set" : "set")); 436 if (penum->dev_cache == 0) { 437 code = show_cache_setup(penum); 438 if (code < 0) 439 return code; 440 } 441 /* 442 * If we're oversampling (i.e., the temporary bitmap is 443 * larger than the final monobit or alpha array) and the 444 * temporary bitmap is large, use incremental conversion 445 * from oversampled bitmap strips to alpha values instead of 446 * full oversampling with compression at the end. 447 */ 448 cc = gx_alloc_char_bits(dir, penum->dev_cache, 449 (iwidth > MAX_TEMP_BITMAP_BITS / iheight && 450 log2_scale.x + log2_scale.y > alpha_bits ? 451 penum->dev_cache2 : NULL), 452 iwidth, iheight, &log2_scale, alpha_bits); 453 if (cc == 0) 454 return 0; /* too big for cache */ 455 /* The mins handle transposed coordinate systems.... */ 456 /* Truncate the offsets to avoid artifacts later. */ 457 cc->offset.x = fixed_ceiling(-cll.x); 458 cc->offset.y = fixed_ceiling(-cll.y); 459 if_debug4('k', "[k]width=%u, height=%u, offset=[%g %g]\n", 460 (uint) iwidth, (uint) iheight, 461 fixed2float(cc->offset.x), 462 fixed2float(cc->offset.y)); 463 pgs->in_cachedevice = CACHE_DEVICE_NONE; /* Provide correct grestore */ 464 if ((code = gs_gsave(pgs)) < 0) { 465 gx_free_cached_char(dir, cc); 466 return code; 467 } 468 /* Nothing can go wrong now.... */ 469 penum->cc = cc; 470 cc->code = glyph; 471 cc->wmode = gs_rootfont(pgs)->WMode; 472 cc->wxy = penum->wxy; 473 /* Install the device */ 474 gx_set_device_only(pgs, (gx_device *) penum->dev_cache); 475 pgs->ctm_default_set = false; 476 /* Adjust the transformation in the graphics context */ 477 /* so that the character lines up with the cache. */ 478 gx_translate_to_fixed(pgs, 479 cc->offset.x << log2_scale.x, 480 cc->offset.y << log2_scale.y); 481 if ((log2_scale.x | log2_scale.y) != 0) 482 gx_scale_char_matrix(pgs, 1 << log2_scale.x, 483 1 << log2_scale.y); 484 /* Set the initial matrix for the cache device. */ 485 penum->dev_cache->initial_matrix = ctm_only(pgs); 486 /* Set the oversampling factor. */ 487 penum->log2_scale.x = log2_scale.x; 488 penum->log2_scale.y = log2_scale.y; 489 /* Reset the clipping path to match the metrics. */ 490 clip_box.p.x = clip_box.p.y = 0; 491 clip_box.q.x = int2fixed(iwidth); 492 clip_box.q.y = int2fixed(iheight); 493 if ((code = gx_clip_to_rectangle(pgs, &clip_box)) < 0) 494 return code; 495 gx_set_device_color_1(pgs); /* write 1's */ 496 pgs->in_cachedevice = CACHE_DEVICE_CACHING; 497 } 498 penum->width_status = sws_cache; 499 return 1; 500 } 501 502 /* Return the cache device status. */ 503 gs_in_cache_device_t 504 gs_incachedevice(const gs_state *pgs) 505 { 506 return pgs->in_cachedevice; 507 } 508 509 /* ------ Enumerator ------ */ 510 511 /* 512 * Set the encode_char procedure in an enumerator. 513 */ 514 private void 515 show_set_encode_char(gs_show_enum * penum) 516 { 517 penum->encode_char = 518 (SHOW_IS(penum, TEXT_FROM_GLYPHS | TEXT_FROM_SINGLE_GLYPH) ? 519 gs_no_encode_char : 520 gs_show_current_font(penum)->procs.encode_char); 521 } 522 523 /* 524 * Resync a text operation with a different set of parameters. 525 * Currently this is implemented only for changing the data source. 526 */ 527 private int 528 gx_show_text_resync(gs_text_enum_t *pte, const gs_text_enum_t *pfrom) 529 { 530 gs_show_enum *const penum = (gs_show_enum *)pte; 531 int old_index = pte->index; 532 533 if ((pte->text.operation ^ pfrom->text.operation) & ~TEXT_FROM_ANY) 534 return_error(gs_error_rangecheck); 535 pte->text = pfrom->text; 536 if (pte->index == old_index) { 537 show_set_encode_char(penum); 538 return 0; 539 } else 540 return show_state_setup(penum); 541 } 542 543 /* Do the next step of a show (or stringwidth) operation */ 544 private int 545 gx_show_text_process(gs_text_enum_t *pte) 546 { 547 gs_show_enum *const penum = (gs_show_enum *)pte; 548 549 return (*penum->continue_proc)(penum); 550 } 551 552 /* Continuation procedures */ 553 private int show_update(P1(gs_show_enum * penum)); 554 private int show_move(P1(gs_show_enum * penum)); 555 private int show_proceed(P1(gs_show_enum * penum)); 556 private int show_finish(P1(gs_show_enum * penum)); 557 private int 558 continue_show_update(gs_show_enum * penum) 559 { 560 int code = show_update(penum); 561 562 if (code < 0) 563 return code; 564 code = show_move(penum); 565 if (code != 0) 566 return code; 567 return show_proceed(penum); 568 } 569 private int 570 continue_show(gs_show_enum * penum) 571 { 572 return show_proceed(penum); 573 } 574 /* For kshow, the CTM or font may have changed, so we have to reestablish */ 575 /* the cached values in the enumerator. */ 576 private int 577 continue_kshow(gs_show_enum * penum) 578 { int code; 579 gs_state *pgs = penum->pgs; 580 581 if (pgs->font != penum->orig_font) 582 gs_setfont(pgs, penum->orig_font); 583 584 code = show_state_setup(penum); 585 586 if (code < 0) 587 return code; 588 return show_proceed(penum); 589 } 590 591 /* Update position */ 592 private int 593 show_update(gs_show_enum * penum) 594 { 595 gs_state *pgs = penum->pgs; 596 cached_char *cc = penum->cc; 597 int code; 598 599 /* Update position for last character */ 600 switch (penum->width_status) { 601 case sws_none: 602 /* Adobe interpreters assume a character width of 0, */ 603 /* even though the documentation says this is an error.... */ 604 penum->wxy.x = penum->wxy.y = 0; 605 break; 606 case sws_cache: 607 /* Finish installing the cache entry. */ 608 /* If the BuildChar/BuildGlyph procedure did a save and a */ 609 /* restore, it already undid the gsave in setcachedevice. */ 610 /* We have to check for this by comparing levels. */ 611 switch (pgs->level - penum->level) { 612 default: 613 return_error(gs_error_invalidfont); /* WRONG */ 614 case 2: 615 code = gs_grestore(pgs); 616 if (code < 0) 617 return code; 618 case 1: 619 ; 620 } 621 gx_add_cached_char(pgs->font->dir, penum->dev_cache, 622 cc, gx_lookup_fm_pair(pgs->font, pgs), 623 &penum->log2_scale); 624 if (!SHOW_USES_OUTLINE(penum) || 625 penum->charpath_flag != cpm_show 626 ) 627 break; 628 /* falls through */ 629 case sws_cache_width_only: 630 /* Copy the bits to the real output device. */ 631 code = gs_grestore(pgs); 632 if (code < 0) 633 return code; 634 code = gs_state_color_load(pgs); 635 if (code < 0) 636 return code; 637 return gx_image_cached_char(penum, cc); 638 case sws_no_cache: 639 ; 640 } 641 if (penum->charpath_flag != cpm_show) { 642 /* Move back to the character origin, so that */ 643 /* show_move will get us to the right place. */ 644 code = gx_path_add_point(pgs->show_gstate->path, 645 penum->origin.x, penum->origin.y); 646 if (code < 0) 647 return code; 648 } 649 return gs_grestore(pgs); 650 } 651 652 /* Move to next character */ 653 private int 654 show_fast_move(gs_state * pgs, gs_fixed_point * pwxy) 655 { 656 int code = gx_path_add_rel_point_inline(pgs->path, pwxy->x, pwxy->y); 657 658 /* If the current position is out of range, don't try to move. */ 659 if (code == gs_error_limitcheck && pgs->clamp_coordinates) 660 code = 0; 661 return code; 662 } 663 private int 664 show_move(gs_show_enum * penum) 665 { 666 gs_state *pgs = penum->pgs; 667 668 if (SHOW_IS(penum, TEXT_REPLACE_WIDTHS)) { 669 gs_point dpt; 670 671 gs_text_replaced_width(&penum->text, penum->xy_index - 1, &dpt); 672 gs_distance_transform2fixed(&pgs->ctm, dpt.x, dpt.y, &penum->wxy); 673 } else { 674 double dx = 0, dy = 0; 675 676 if (SHOW_IS_ADD_TO_SPACE(penum)) { 677 gs_char chr = CURRENT_CHAR(penum) & 0xff; 678 int fdepth = penum->fstack.depth; 679 680 if (fdepth > 0) { 681 /* Add in the shifted font number. */ 682 uint fidx = penum->fstack.items[fdepth].index; 683 684 switch (((gs_font_type0 *) (penum->fstack.items[fdepth - 1].font))->data.FMapType) { 685 case fmap_1_7: 686 case fmap_9_7: 687 chr += fidx << 7; 688 break; 689 case fmap_CMap: 690 chr = CURRENT_CHAR(penum); /* the full character */ 691 if (!penum->cmap_code) 692 break; 693 /* falls through */ 694 default: 695 chr += fidx << 8; 696 } 697 } 698 if (chr == penum->text.space.s_char) { 699 dx = penum->text.delta_space.x; 700 dy = penum->text.delta_space.y; 701 } 702 } 703 if (SHOW_IS_ADD_TO_ALL(penum)) { 704 dx += penum->text.delta_all.x; 705 dy += penum->text.delta_all.y; 706 } 707 if (!is_fzero2(dx, dy)) { 708 gs_fixed_point dxy; 709 710 gs_distance_transform2fixed(&pgs->ctm, dx, dy, &dxy); 711 penum->wxy.x += dxy.x; 712 penum->wxy.y += dxy.y; 713 } 714 } 715 if (SHOW_IS_ALL_OF(penum, TEXT_DO_NONE | TEXT_INTERVENE)) { 716 /* HACK for cshow */ 717 penum->continue_proc = continue_kshow; 718 return TEXT_PROCESS_INTERVENE; 719 } 720 /* wxy is in device coordinates */ 721 { 722 int code = show_fast_move(pgs, &penum->wxy); 723 724 if (code < 0) 725 return code; 726 } 727 /* Check for kerning, but not on the last character. */ 728 if (SHOW_IS_DO_KERN(penum) && penum->index < penum->text.size) { 729 penum->continue_proc = continue_kshow; 730 return TEXT_PROCESS_INTERVENE; 731 } 732 return 0; 733 } 734 /* Process next character */ 735 private int 736 show_proceed(gs_show_enum * penum) 737 { 738 gs_state *pgs = penum->pgs; 739 gs_font *pfont; 740 cached_fm_pair *pair = 0; 741 gs_font *rfont = 742 (penum->fstack.depth < 0 ? pgs->font : penum->fstack.items[0].font); 743 int wmode = rfont->WMode; 744 font_proc_next_char_glyph((*next_char_glyph)) = 745 rfont->procs.next_char_glyph; 746 #define get_next_char_glyph(pte, pchr, pglyph)\ 747 (++(penum->xy_index), next_char_glyph(pte, pchr, pglyph)) 748 gs_char chr; 749 gs_glyph glyph; 750 int code; 751 cached_char *cc; 752 gx_device *dev = gs_currentdevice_inline(pgs); 753 int alpha_bits = (*dev_proc(dev, get_alpha_bits)) (dev, go_text); 754 755 if (penum->charpath_flag == cpm_show && SHOW_USES_OUTLINE(penum)) { 756 code = gs_state_color_load(pgs); 757 if (code < 0) 758 return code; 759 } 760 more: /* Proceed to next character */ 761 pfont = (penum->fstack.depth < 0 ? pgs->font : 762 penum->fstack.items[penum->fstack.depth].font); 763 penum->current_font = pfont; 764 /* can_cache >= 0 allows us to use cached characters, */ 765 /* even if we can't make new cache entries. */ 766 if (penum->can_cache >= 0) { 767 /* Loop with cache */ 768 for (;;) { 769 switch ((code = get_next_char_glyph((gs_text_enum_t *)penum, 770 &chr, &glyph)) 771 ) { 772 default: /* error */ 773 return code; 774 case 2: /* done */ 775 return show_finish(penum); 776 case 1: /* font change */ 777 pfont = penum->fstack.items[penum->fstack.depth].font; 778 penum->current_font = pfont; 779 pgs->char_tm_valid = false; 780 show_state_setup(penum); 781 pair = 0; 782 /* falls through */ 783 case 0: /* plain char */ 784 /* 785 * We don't need to set penum->current_char in the 786 * normal cases, but it's needed for widthshow, 787 * kshow, and one strange client, so we may as well 788 * do it here. 789 */ 790 SET_CURRENT_CHAR(penum, chr); 791 if (glyph == gs_no_glyph) { 792 glyph = (*penum->encode_char)(pfont, chr, 793 GLYPH_SPACE_NAME); 794 if (glyph == gs_no_glyph) { 795 cc = 0; 796 goto no_cache; 797 } 798 } 799 if (pair == 0) 800 pair = gx_lookup_fm_pair(pfont, pgs); 801 cc = gx_lookup_cached_char(pfont, pair, glyph, wmode, 802 alpha_bits); 803 if (cc == 0) { 804 /* Character is not in cache. */ 805 /* If possible, try for an xfont before */ 806 /* rendering from the outline. */ 807 if (pfont->ExactSize == fbit_use_outlines || 808 pfont->PaintType == 2 809 ) 810 goto no_cache; 811 if (pfont->BitmapWidths) { 812 cc = gx_lookup_xfont_char(pgs, pair, chr, 813 glyph, &pfont->procs.callbacks, wmode); 814 if (cc == 0) 815 goto no_cache; 816 } else { 817 if (!SHOW_USES_OUTLINE(penum) || 818 (penum->charpath_flag != cpm_show && 819 penum->charpath_flag != cpm_charwidth) 820 ) 821 goto no_cache; 822 /* We might have an xfont, but we still */ 823 /* want the scalable widths. */ 824 cc = gx_lookup_xfont_char(pgs, pair, chr, 825 glyph, &pfont->procs.callbacks, wmode); 826 /* Render up to the point of */ 827 /* setcharwidth or setcachedevice, */ 828 /* just as for stringwidth. */ 829 /* This is the only case in which we can */ 830 /* to go no_cache with cc != 0. */ 831 goto no_cache; 832 } 833 } 834 /* Character is in cache. */ 835 /* We might be doing .charboxpath or stringwidth; */ 836 /* check for these now. */ 837 if (penum->charpath_flag == cpm_charwidth) { 838 /* This is charwidth. Just move by the width. */ 839 DO_NOTHING; 840 } else if (penum->charpath_flag != cpm_show) { 841 /* This is .charboxpath. Get the bounding box */ 842 /* and append it to a path. */ 843 gx_path box_path; 844 gs_fixed_point pt; 845 fixed llx, lly, urx, ury; 846 847 code = gx_path_current_point(pgs->path, &pt); 848 if (code < 0) 849 return code; 850 llx = fixed_rounded(pt.x - cc->offset.x) + 851 int2fixed(penum->ftx); 852 lly = fixed_rounded(pt.y - cc->offset.y) + 853 int2fixed(penum->fty); 854 urx = llx + int2fixed(cc->width), 855 ury = lly + int2fixed(cc->height); 856 gx_path_init_local(&box_path, pgs->memory); 857 code = 858 gx_path_add_rectangle(&box_path, llx, lly, 859 urx, ury); 860 if (code >= 0) 861 code = 862 gx_path_add_char_path(pgs->show_gstate->path, 863 &box_path, 864 penum->charpath_flag); 865 if (code >= 0) 866 code = gx_path_add_point(pgs->path, pt.x, pt.y); 867 gx_path_free(&box_path, "show_proceed(box path)"); 868 if (code < 0) 869 return code; 870 } else if (SHOW_IS_DRAWING(penum)) { 871 code = gx_image_cached_char(penum, cc); 872 if (code < 0) 873 return code; 874 else if (code > 0) { 875 cc = 0; 876 goto no_cache; 877 } 878 } 879 if (SHOW_IS_SLOW(penum)) { 880 /* Split up the assignment so that the */ 881 /* Watcom compiler won't reserve esi/edi. */ 882 penum->wxy.x = cc->wxy.x; 883 penum->wxy.y = cc->wxy.y; 884 code = show_move(penum); 885 } else 886 code = show_fast_move(pgs, &cc->wxy); 887 if (code) { 888 /* Might be kshow, so store the state. */ 889 SET_CURRENT_GLYPH(penum, glyph); 890 return code; 891 } 892 } 893 } 894 } else { 895 /* Can't use cache */ 896 switch ((code = get_next_char_glyph((gs_text_enum_t *)penum, 897 &chr, &glyph)) 898 ) { 899 default: 900 return code; 901 case 2: 902 return show_finish(penum); 903 case 1: 904 pfont = penum->fstack.items[penum->fstack.depth].font; 905 penum->current_font = pfont; 906 show_state_setup(penum); 907 case 0: 908 ; 909 } 910 SET_CURRENT_CHAR(penum, chr); 911 if (glyph == gs_no_glyph) { 912 glyph = (*penum->encode_char)(pfont, chr, GLYPH_SPACE_NAME); 913 } 914 cc = 0; 915 } 916 no_cache: 917 /* 918 * We must call the client's rendering code. Normally, 919 * we only do this if the character is not cached (cc = 0); 920 * however, we also must do this if we have an xfont but 921 * are using scalable widths. In this case, and only this case, 922 * we get here with cc != 0. penum->current_char has already 923 * been set, but not penum->current_glyph. 924 */ 925 SET_CURRENT_GLYPH(penum, glyph); 926 if ((code = gs_gsave(pgs)) < 0) 927 return code; 928 /* Set the font to the current descendant font. */ 929 pgs->font = pfont; 930 /* Reset the in_cachedevice flag, so that a recursive show */ 931 /* will use the cache properly. */ 932 pgs->in_cachedevice = CACHE_DEVICE_NONE; 933 /* Reset the sampling scale. */ 934 penum->log2_scale.x = penum->log2_scale.y = 0; 935 /* Set the charpath data in the graphics context if necessary, */ 936 /* so that fill and stroke will add to the path */ 937 /* rather than having their usual effect. */ 938 pgs->in_charpath = penum->charpath_flag; 939 pgs->show_gstate = 940 (penum->show_gstate == pgs ? pgs->saved : penum->show_gstate); 941 pgs->stroke_adjust = false; /* per specification */ 942 { 943 gs_fixed_point cpt; 944 gx_path *ppath = pgs->path; 945 946 if ((code = gx_path_current_point_inline(ppath, &cpt)) < 0) { 947 /* For cshow, having no current point is acceptable. */ 948 if (!SHOW_IS(penum, TEXT_DO_NONE)) 949 goto rret; 950 cpt.x = cpt.y = 0; /* arbitrary */ 951 } 952 penum->origin.x = cpt.x; 953 penum->origin.y = cpt.y; 954 /* Normally, char_tm is valid because of show_state_setup, */ 955 /* but if we're in a cshow, it may not be. */ 956 gs_currentcharmatrix(pgs, NULL, true); 957 #if 1 /*USE_FPU <= 0 */ 958 if (pgs->ctm.txy_fixed_valid && pgs->char_tm.txy_fixed_valid) { 959 fixed tx = pgs->ctm.tx_fixed; 960 fixed ty = pgs->ctm.ty_fixed; 961 962 gs_settocharmatrix(pgs); 963 cpt.x += pgs->ctm.tx_fixed - tx; 964 cpt.y += pgs->ctm.ty_fixed - ty; 965 } else 966 #endif 967 { 968 double tx = pgs->ctm.tx; 969 double ty = pgs->ctm.ty; 970 double fpx, fpy; 971 972 gs_settocharmatrix(pgs); 973 fpx = fixed2float(cpt.x) + (pgs->ctm.tx - tx); 974 fpy = fixed2float(cpt.y) + (pgs->ctm.ty - ty); 975 #define f_fits_in_fixed(f) f_fits_in_bits(f, fixed_int_bits) 976 if (!(f_fits_in_fixed(fpx) && f_fits_in_fixed(fpy))) { 977 gs_note_error(code = gs_error_limitcheck); 978 goto rret; 979 } 980 cpt.x = float2fixed(fpx); 981 cpt.y = float2fixed(fpy); 982 } 983 gs_newpath(pgs); 984 code = show_origin_setup(pgs, cpt.x, cpt.y, 985 penum->charpath_flag); 986 if (code < 0) 987 goto rret; 988 } 989 penum->width_status = sws_none; 990 penum->continue_proc = continue_show_update; 991 /* Try using the build procedure in the font. */ 992 /* < 0 means error, 0 means success, 1 means failure. */ 993 penum->cc = cc; /* set this now for build procedure */ 994 code = (*pfont->procs.build_char)((gs_text_enum_t *)penum, pgs, pfont, 995 chr, glyph); 996 if (code < 0) { 997 discard(gs_note_error(code)); 998 goto rret; 999 } 1000 if (code == 0) { 1001 code = show_update(penum); 1002 if (code < 0) 1003 goto rret; 1004 /* Note that show_update does a grestore.... */ 1005 code = show_move(penum); 1006 if (code) 1007 return code; /* ... so don't go to rret here. */ 1008 goto more; 1009 } 1010 /* 1011 * Some BuildChar procedures do a save before the setcachedevice, 1012 * and a restore at the end. If we waited to allocate the cache 1013 * device until the setcachedevice, we would attempt to free it 1014 * after the restore. Therefore, allocate it now. 1015 */ 1016 if (penum->dev_cache == 0) { 1017 code = show_cache_setup(penum); 1018 if (code < 0) 1019 goto rret; 1020 } 1021 return TEXT_PROCESS_RENDER; 1022 /* If we get an error while setting up for BuildChar, */ 1023 /* we must undo the partial setup. */ 1024 rret:gs_grestore(pgs); 1025 return code; 1026 #undef get_next_char_glyph 1027 } 1028 1029 /* 1030 * Prepare to retry rendering of the current character. (This is only used 1031 * in one place in zchar1.c; a different approach may be better.) 1032 */ 1033 private int 1034 gx_show_text_retry(gs_text_enum_t *pte) 1035 { 1036 gs_show_enum *const penum = (gs_show_enum *)pte; 1037 1038 if (penum->cc) { 1039 gs_font *pfont = penum->current_font; 1040 1041 gx_free_cached_char(pfont->dir, penum->cc); 1042 penum->cc = 0; 1043 } 1044 gs_grestore(penum->pgs); 1045 penum->width_status = sws_none; 1046 penum->log2_scale.x = penum->log2_scale.y = 0; 1047 return 0; 1048 } 1049 1050 /* Finish show or stringwidth */ 1051 private int 1052 show_finish(gs_show_enum * penum) 1053 { 1054 gs_state *pgs = penum->pgs; 1055 int code, rcode; 1056 1057 if (penum->auto_release) 1058 penum->procs->release((gs_text_enum_t *)penum, "show_finish"); 1059 if (!SHOW_IS_STRINGWIDTH(penum)) 1060 return 0; 1061 /* Save the accumulated width before returning, */ 1062 /* and undo the extra gsave. */ 1063 code = gs_currentpoint(pgs, &penum->returned.total_width); 1064 rcode = gs_grestore(pgs); 1065 return (code < 0 ? code : rcode); 1066 } 1067 1068 /* Release the structure. */ 1069 private void 1070 gx_show_text_release(gs_text_enum_t *pte, client_name_t cname) 1071 { 1072 gs_show_enum *const penum = (gs_show_enum *)pte; 1073 1074 penum->cc = 0; 1075 if (penum->dev_cache2) { 1076 gx_device_retain((gx_device *)penum->dev_cache2, false); 1077 penum->dev_cache2 = 0; 1078 } 1079 if (penum->dev_cache) { 1080 gx_device_retain((gx_device *)penum->dev_cache, false); 1081 penum->dev_cache = 0; 1082 } 1083 if (penum->dev_null) { 1084 gx_device_retain((gx_device *)penum->dev_null, false); 1085 penum->dev_null = 0; 1086 } 1087 gx_default_text_release(pte, cname); 1088 } 1089 1090 /* ------ Miscellaneous accessors ------ */ 1091 1092 /* Return the charpath mode. */ 1093 gs_char_path_mode 1094 gs_show_in_charpath(const gs_show_enum * penum) 1095 { 1096 return penum->charpath_flag; 1097 } 1098 1099 /* Return true if we only need the width from the rasterizer */ 1100 /* and can short-circuit the full rendering of the character, */ 1101 /* false if we need the actual character bits. */ 1102 /* This is only meaningful just before calling gs_setcharwidth or */ 1103 /* gs_setcachedevice[2]. */ 1104 /* Note that we can't do this if the procedure has done any extra [g]saves. */ 1105 private bool 1106 gx_show_text_is_width_only(const gs_text_enum_t *pte) 1107 { 1108 const gs_show_enum *const penum = (const gs_show_enum *)pte; 1109 1110 /* penum->cc will be non-zero iff we are calculating */ 1111 /* the scalable width for an xfont character. */ 1112 return ((!SHOW_USES_OUTLINE(penum) || penum->cc != 0) && 1113 penum->pgs->level == penum->level + 1); 1114 } 1115 1116 /* Return the width of the just-enumerated character (for cshow). */ 1117 private int 1118 gx_show_text_current_width(const gs_text_enum_t *pte, gs_point *pwidth) 1119 { 1120 const gs_show_enum *const penum = (const gs_show_enum *)pte; 1121 1122 return gs_idtransform(penum->pgs, 1123 fixed2float(penum->wxy.x), 1124 fixed2float(penum->wxy.y), pwidth); 1125 } 1126 1127 /* Return the current font for cshow. */ 1128 gs_font * 1129 gs_show_current_font(const gs_show_enum * penum) 1130 { 1131 return (penum->fstack.depth < 0 ? penum->pgs->font : 1132 penum->fstack.items[penum->fstack.depth].font); 1133 } 1134 1135 /* ------ Internal routines ------ */ 1136 1137 /* Initialize the gstate-derived parts of a show enumerator. */ 1138 /* We do this both when starting the show operation, */ 1139 /* and when returning from the kshow callout. */ 1140 /* Uses only penum->pgs, penum->fstack. */ 1141 private int 1142 show_state_setup(gs_show_enum * penum) 1143 { 1144 gs_state *pgs = penum->pgs; 1145 gx_clip_path *pcpath; 1146 gs_font *pfont; 1147 1148 if (penum->fstack.depth <= 0) { 1149 pfont = pgs->font; 1150 gs_currentcharmatrix(pgs, NULL, 1); /* make char_tm valid */ 1151 } else { 1152 /* We have to concatenate the parent's FontMatrix as well. */ 1153 gs_matrix mat; 1154 const gx_font_stack_item_t *pfsi = 1155 &penum->fstack.items[penum->fstack.depth]; 1156 1157 pfont = pfsi->font; 1158 gs_matrix_multiply(&pfont->FontMatrix, 1159 &pfsi[-1].font->FontMatrix, &mat); 1160 gs_setcharmatrix(pgs, &mat); 1161 } 1162 penum->current_font = pfont; 1163 /* Skewing or non-rectangular rotation are not supported. */ 1164 if (!CACHE_ROTATED_CHARS && 1165 (is_fzero2(pgs->char_tm.xy, pgs->char_tm.yx) || 1166 is_fzero2(pgs->char_tm.xx, pgs->char_tm.yy)) 1167 ) 1168 penum->can_cache = 0; 1169 if (penum->can_cache >= 0 && 1170 gx_effective_clip_path(pgs, &pcpath) >= 0 1171 ) { 1172 gs_fixed_rect cbox; 1173 1174 gx_cpath_inner_box(pcpath, &cbox); 1175 /* Since characters occupy an integral number of pixels, */ 1176 /* we can (and should) round the inner clipping box */ 1177 /* outward rather than inward. */ 1178 penum->ibox.p.x = fixed2int_var(cbox.p.x); 1179 penum->ibox.p.y = fixed2int_var(cbox.p.y); 1180 penum->ibox.q.x = fixed2int_var_ceiling(cbox.q.x); 1181 penum->ibox.q.y = fixed2int_var_ceiling(cbox.q.y); 1182 gx_cpath_outer_box(pcpath, &cbox); 1183 penum->obox.p.x = fixed2int_var(cbox.p.x); 1184 penum->obox.p.y = fixed2int_var(cbox.p.y); 1185 penum->obox.q.x = fixed2int_var_ceiling(cbox.q.x); 1186 penum->obox.q.y = fixed2int_var_ceiling(cbox.q.y); 1187 #if 1 /*USE_FPU <= 0 */ 1188 if (pgs->ctm.txy_fixed_valid && pgs->char_tm.txy_fixed_valid) { 1189 penum->ftx = (int)fixed2long(pgs->char_tm.tx_fixed - 1190 pgs->ctm.tx_fixed); 1191 penum->fty = (int)fixed2long(pgs->char_tm.ty_fixed - 1192 pgs->ctm.ty_fixed); 1193 } else { 1194 #endif 1195 double fdx = pgs->char_tm.tx - pgs->ctm.tx; 1196 double fdy = pgs->char_tm.ty - pgs->ctm.ty; 1197 1198 #define int_bits (arch_sizeof_int * 8 - 1) 1199 if (!(f_fits_in_bits(fdx, int_bits) && 1200 f_fits_in_bits(fdy, int_bits)) 1201 ) 1202 return_error(gs_error_limitcheck); 1203 #undef int_bits 1204 penum->ftx = (int)fdx; 1205 penum->fty = (int)fdy; 1206 } 1207 } 1208 show_set_encode_char(penum); 1209 return 0; 1210 } 1211 1212 /* Set the suggested oversampling scale for character rendering. */ 1213 private void 1214 show_set_scale(gs_show_enum * penum) 1215 { 1216 /* 1217 * Decide whether to oversample. 1218 * We have to decide this each time setcachedevice is called. 1219 */ 1220 const gs_state *pgs = penum->pgs; 1221 1222 if ((penum->charpath_flag == cpm_show || 1223 penum->charpath_flag == cpm_charwidth) && 1224 SHOW_USES_OUTLINE(penum) && 1225 gx_path_is_void_inline(pgs->path) && 1226 /* Oversampling rotated characters doesn't work well. */ 1227 (is_fzero2(pgs->char_tm.xy, pgs->char_tm.yx) || 1228 is_fzero2(pgs->char_tm.xx, pgs->char_tm.yy)) 1229 ) { 1230 const gs_font_base *pfont = (gs_font_base *) pgs->font; 1231 gs_fixed_point extent; 1232 int code = gs_distance_transform2fixed(&pgs->char_tm, 1233 pfont->FontBBox.q.x - pfont->FontBBox.p.x, 1234 pfont->FontBBox.q.y - pfont->FontBBox.p.y, 1235 &extent); 1236 1237 if (code >= 0) { 1238 int sx = 1239 (extent.x == 0 ? 0 : 1240 any_abs(extent.x) < int2fixed(25) ? 2 : 1241 any_abs(extent.x) < int2fixed(60) ? 1 : 1242 0); 1243 int sy = 1244 (extent.y == 0 ? 0 : 1245 any_abs(extent.y) < int2fixed(25) ? 2 : 1246 any_abs(extent.y) < int2fixed(60) ? 1 : 1247 0); 1248 1249 /* If we oversample at all, make sure we do it */ 1250 /* in both X and Y. */ 1251 if (sx == 0 && sy != 0) 1252 sx = 1; 1253 else if (sy == 0 && sx != 0) 1254 sy = 1; 1255 penum->log2_suggested_scale.x = sx; 1256 penum->log2_suggested_scale.y = sy; 1257 return; 1258 } 1259 } 1260 /* By default, don't scale. */ 1261 penum->log2_suggested_scale.x = 1262 penum->log2_suggested_scale.y = 0; 1263 } 1264 1265 /* Set up the cache device and related information. */ 1266 /* Note that we always allocate both cache devices, */ 1267 /* even if we only use one of them. */ 1268 private int 1269 show_cache_setup(gs_show_enum * penum) 1270 { 1271 gs_state *pgs = penum->pgs; 1272 gs_memory_t *mem = pgs->memory; 1273 gx_device_memory *dev = 1274 gs_alloc_struct(mem, gx_device_memory, &st_device_memory, 1275 "show_cache_setup(dev_cache)"); 1276 gx_device_memory *dev2 = 1277 gs_alloc_struct(mem, gx_device_memory, &st_device_memory, 1278 "show_cache_setup(dev_cache2)"); 1279 1280 if (dev == 0 || dev2 == 0) { 1281 gs_free_object(mem, dev2, "show_cache_setup(dev_cache2)"); 1282 gs_free_object(mem, dev, "show_cache_setup(dev_cache)"); 1283 return_error(gs_error_VMerror); 1284 } 1285 /* 1286 * We only initialize the devices for the sake of the GC, 1287 * (since we have to re-initialize dev as either a mem_mono 1288 * or a mem_abuf device before actually using it) and also 1289 * to set its memory pointer. 1290 */ 1291 gs_make_mem_mono_device(dev, mem, gs_currentdevice_inline(pgs)); 1292 penum->dev_cache = dev; 1293 gs_make_mem_mono_device(dev2, mem, gs_currentdevice_inline(pgs)); 1294 penum->dev_cache2 = dev2; 1295 /* Retain these devices, since they are referenced from the enumerator. */ 1296 gx_device_retain((gx_device *)dev, true); 1297 gx_device_retain((gx_device *)dev2, true); 1298 return 0; 1299 } 1300 1301 /* Set the character origin as the origin of the coordinate system. */ 1302 /* Used before rendering characters, and for moving the origin */ 1303 /* in setcachedevice2 when WMode=1. */ 1304 private int 1305 show_origin_setup(gs_state * pgs, fixed cpt_x, fixed cpt_y, 1306 gs_char_path_mode charpath_flag) 1307 { 1308 if (charpath_flag == cpm_show) { 1309 /* Round the translation in the graphics state. */ 1310 /* This helps prevent rounding artifacts later. */ 1311 cpt_x = fixed_rounded(cpt_x); 1312 cpt_y = fixed_rounded(cpt_y); 1313 } 1314 /* 1315 * BuildChar procedures expect the current point to be undefined, 1316 * so we omit the gx_path_add_point with ctm.t*_fixed. 1317 */ 1318 return gx_translate_to_fixed(pgs, cpt_x, cpt_y); 1319 } 1320