386BSD 0.1 development
[unix-history] / usr / othersrc / public / ghostscript-2.4.1 / gschar.c
CommitLineData
14026336
WJ
1/* Copyright (C) 1989, 1992 Aladdin Enterprises. All rights reserved.
2 Distributed by Free Software Foundation, Inc.
3
4This file is part of Ghostscript.
5
6Ghostscript is distributed in the hope that it will be useful, but
7WITHOUT ANY WARRANTY. No author or distributor accepts responsibility
8to anyone for the consequences of using it or for whether it serves any
9particular purpose or works at all, unless he says so in writing. Refer
10to the Ghostscript General Public License for full details.
11
12Everyone is granted permission to copy, modify and redistribute
13Ghostscript, but only under the conditions described in the Ghostscript
14General Public License. A copy of this license is supposed to have been
15given to you along with Ghostscript so you can know your rights and
16responsibilities. It should be in a file named COPYING. Among other
17things, the copyright notice and this notice must be preserved on all
18copies. */
19
20/* gschar.c */
21/* Character writing operators for Ghostscript library */
22#include "gx.h"
23#include "memory_.h"
24#include "string_.h"
25#include "gserrors.h"
26#include "gxfixed.h" /* ditto */
27#include "gxarith.h"
28#include "gxmatrix.h"
29#include "gzstate.h" /* must precede gzdevice */
30#include "gzdevice.h" /* must precede gxchar */
31#include "gxdevmem.h"
32#include "gxchar.h"
33#include "gxcache.h"
34#include "gxfont.h"
35#include "gspath.h"
36#include "gzpath.h"
37#include "gzcolor.h"
38
39/* Exported size of enumerator */
40const uint gs_show_enum_sizeof = sizeof(gs_show_enum);
41
42/* Imported procedures */
43extern void gx_set_black(P1(gs_state *));
44
45/* Forward declarations */
46private int continue_show(P1(gs_show_enum *));
47private int continue_show_update(P1(gs_show_enum *));
48private int show_setup(P3(gs_show_enum *, gs_state *, char *));
49private int stringwidth_setup(P3(gs_show_enum *, gs_state *, char *));
50
51/* Print the ctm if debugging */
52#define print_ctm(s,pgs)\
53 dprintf7("[p]%sctm=[%g %g %g %g %g %g]\n", s,\
54 pgs->ctm.xx, pgs->ctm.xy, pgs->ctm.yx, pgs->ctm.yy,\
55 pgs->ctm.tx, pgs->ctm.ty)
56
57/* ------ String writing operators ------ */
58
59/* Setup macros for show operators */
60#define setup_show()\
61 penum->size = strlen(str)
62#define setup_show_n()\
63 penum->size = size
64#define setup_a()\
65 penum->add = 1, penum->ax = ax, penum->ay = ay,\
66 penum->slow_show = 1
67#define setup_width()\
68 penum->wchr = chr, penum->wcx = cx, penum->wcy = cy,\
69 penum->slow_show = 1
70#define no_chr ~(char_code)0
71
72/* show[_n] */
73int
74gs_show_init(register gs_show_enum *penum,
75 gs_state *pgs, char *str)
76{ setup_show();
77 penum->slow_show = 0;
78 return show_setup(penum, pgs, str);
79}
80int
81gs_show_n_init(register gs_show_enum *penum,
82 gs_state *pgs, char *str, uint size)
83{ setup_show_n();
84 penum->slow_show = 0;
85 return show_setup(penum, pgs, str);
86}
87
88/* ashow[_n] */
89int
90gs_ashow_init(register gs_show_enum *penum,
91 gs_state *pgs, floatp ax, floatp ay, char *str)
92{ int code;
93 setup_show();
94 code = show_setup(penum, pgs, str);
95 setup_a();
96 return code;
97}
98int
99gs_ashow_n_init(register gs_show_enum *penum,
100 gs_state *pgs, floatp ax, floatp ay, char *str, uint size)
101{ int code;
102 setup_show_n();
103 code = show_setup(penum, pgs, str);
104 setup_a();
105 return code;
106}
107
108/* widthshow[_n] */
109int
110gs_widthshow_init(register gs_show_enum *penum,
111 gs_state *pgs, floatp cx, floatp cy, char chr, char *str)
112{ int code;
113 setup_show();
114 code = show_setup(penum, pgs, str);
115 setup_width();
116 return code;
117}
118int
119gs_widthshow_n_init(register gs_show_enum *penum,
120 gs_state *pgs, floatp cx, floatp cy, char chr, char *str, uint size)
121{ int code;
122 setup_show_n();
123 code = show_setup(penum, pgs, str);
124 setup_width();
125 return code;
126}
127
128/* awidthshow[_n] */
129int
130gs_awidthshow_init(register gs_show_enum *penum,
131 gs_state *pgs, floatp cx, floatp cy, char chr, floatp ax, floatp ay,
132 char *str)
133{ int code;
134 setup_show();
135 code = show_setup(penum, pgs, str);
136 setup_a();
137 setup_width();
138 return code;
139}
140int
141gs_awidthshow_n_init(register gs_show_enum *penum,
142 gs_state *pgs, floatp cx, floatp cy, char chr, floatp ax, floatp ay,
143 char *str, uint size)
144{ int code;
145 setup_show_n();
146 code = show_setup(penum, pgs, str);
147 setup_a();
148 setup_width();
149 return code;
150}
151
152/* kshow[_n] */
153int
154gs_kshow_init(register gs_show_enum *penum,
155 gs_state *pgs, char *str)
156{ int code;
157 if ( pgs->font->FontType == ft_composite)
158 return_error(gs_error_invalidfont);
159 setup_show();
160 code = show_setup(penum, pgs, str);
161 penum->do_kern = penum->slow_show = 1;
162 return code;
163}
164int
165gs_kshow_n_init(register gs_show_enum *penum,
166 gs_state *pgs, char *str, uint size)
167{ int code;
168 if ( pgs->font->FontType == ft_composite)
169 return_error(gs_error_invalidfont);
170 setup_show_n();
171 code = show_setup(penum, pgs, str);
172 penum->do_kern = penum->slow_show = 1;
173 return code;
174}
175
176/* ------ Related operators ------ */
177
178/* stringwidth[_n] */
179int
180gs_stringwidth_init(gs_show_enum *penum, gs_state *pgs, char *str)
181{ setup_show();
182 return stringwidth_setup(penum, pgs, str);
183}
184int
185gs_stringwidth_n_init(gs_show_enum *penum, gs_state *pgs, char *str, uint size)
186{ setup_show_n();
187 return stringwidth_setup(penum, pgs, str);
188}
189
190/* Common code for stringwidth[_n] */
191private int
192stringwidth_setup(gs_show_enum *penum, gs_state *pgs, char *str)
193{ int code = show_setup(penum, pgs, str);
194 if ( code < 0 ) return_error(code);
195 penum->stringwidth_flag = 1;
196 /* Do an extra gsave and suppress output */
197 if ( (code = gs_gsave(pgs)) < 0 ) return code;
198 penum->level = pgs->level; /* for level check in show_update */
199 gx_device_no_output(pgs);
200 /* Establish an arbitrary current point. */
201 return gx_path_add_point(pgs->path, pgs->ctm.tx_fixed, pgs->ctm.ty_fixed);
202}
203
204/* charpath[_n] */
205int
206gs_charpath_init(gs_show_enum *penum, gs_state *pgs,
207 char *str, int bool)
208{ int code;
209 setup_show();
210 code = show_setup(penum, pgs, str);
211 penum->charpath_flag = (bool ? 2 : 1);
212 penum->can_cache = 0;
213 return code;
214}
215int
216gs_charpath_n_init(gs_show_enum *penum, gs_state *pgs,
217 char *str, uint size, int bool)
218{ int code;
219 setup_show_n();
220 code = show_setup(penum, pgs, str);
221 penum->charpath_flag = (bool ? 2 : 1);
222 penum->can_cache = 0;
223 return code;
224}
225
226/* ------ Width/cache operators ------ */
227
228/* setcachedevice */
229int
230gs_setcachedevice(register gs_show_enum *penum, gs_state *pgs,
231 floatp wx, floatp wy, floatp llx, floatp lly, floatp urx, floatp ury)
232{ int code = gs_setcharwidth(penum, pgs, wx, wy); /* default is don't cache */
233 if ( code < 0 ) return code;
234 /* See if we want to cache this character. */
235 if ( pgs->in_cachedevice ) /* no recursion! */
236 return 0;
237 pgs->in_cachedevice = 1; /* disable color/gray/image operators */
238 /* We can only use the cache if ctm is unchanged */
239 /* (aside from a possible translation), */
240 /* and if the extent of the box is non-negative. */
241 if ( !penum->can_cache || !pgs->char_tm_valid ||
242 llx > urx || lly > ury
243 )
244 return 0;
245 { gs_font_dir *dir = pgs->font->dir;
246 gs_fixed_point cbox_ll, cbox_ur, cdim;
247 long iwidth, iheight;
248 cached_char *cc;
249 gs_fixed_rect clip_box;
250 gs_distance_transform2fixed(&pgs->ctm, llx, lly, &cbox_ll);
251 gs_distance_transform2fixed(&pgs->ctm, urx, ury, &cbox_ur);
252 cdim.x = cbox_ur.x - cbox_ll.x;
253 cdim.y = cbox_ur.y - cbox_ll.y;
254 if ( cdim.x < 0 ) cdim.x = -cdim.x;
255 if ( cdim.y < 0 ) cdim.y = -cdim.y;
256#ifdef DEBUG
257if ( gs_debug['k'] )
258 { dprintf4("[k]cbox=[%g %g %g %g]\n",
259 fixed2float(cbox_ll.x), fixed2float(cbox_ll.y),
260 fixed2float(cbox_ur.x), fixed2float(cbox_ur.y));
261 print_ctm(" ", pgs);
262 }
263#endif
264 iwidth = fixed2long(cdim.x) + 2;
265 iheight = fixed2long(cdim.y) + 2;
266 if ( iwidth != (ushort)iwidth ||
267 iheight != (ushort)iheight
268 )
269 return 0; /* much too big */
270 if ( !penum->dev_cache_set )
271 { /* Set up the memory device for the character cache */
272 device *dev = &penum->dev_cache_dev;
273 penum->dev_cache_info = mem_mono_device;
274 dev->info = (gx_device *)&penum->dev_cache_info;
275 dev->is_band_device = 0;
276 dev->white = 1;
277 dev->black = 1;
278 penum->dev_cache_set = 1;
279 }
280 if ( (cc = gx_alloc_char_bits(dir,
281 (gx_device_memory *)&penum->dev_cache_info,
282 (ushort)iwidth,
283 (ushort)iheight)) == 0 )
284 return 0; /* too big for cache */
285 /* The mins handle transposed coordinate systems.... */
286 /* Truncate the offsets to avoid artifacts later. */
287 cc->offset.x = fixed_ceiling(-min(cbox_ll.x, cbox_ur.x));
288 cc->offset.y = fixed_ceiling(-min(cbox_ll.y, cbox_ur.y));
289#ifdef DEBUG
290if ( gs_debug['k'] )
291 dprintf2("[k]offset=[%g %g]\n", fixed2float(cc->offset.x),
292 fixed2float(cc->offset.y));
293#endif
294 if ( !color_is_pure(pgs->dev_color) ) /* can't use cache */
295 { gx_free_cached_char(dir, cc);
296 return code;
297 }
298 if ( (code = gs_gsave(pgs)) < 0 )
299 { gx_free_cached_char(dir, cc);
300 return code;
301 }
302 /* Nothing can go wrong now.... */
303 penum->cc = cc;
304 cc->code = gs_show_current_char(penum);
305 cc->wxy = penum->wxy;
306 /* Install the device */
307 pgs->device = &penum->dev_cache_dev;
308 pgs->device_is_shared = 1; /* don't deallocate */
309 /* Adjust the translation in the graphics context */
310 /* so that the character lines up with the cache. */
311 gs_translate_to_fixed(pgs, cc->offset.x, cc->offset.y);
312 /* Reset the clipping path to match the metrics. */
313 clip_box.p.x = clip_box.p.y = 0;
314 clip_box.q.x = int2fixed(iwidth);
315 clip_box.q.y = int2fixed(iheight);
316 if ( (code = gx_clip_to_rectangle(pgs, &clip_box)) < 0 )
317 return code;
318 gx_set_black(pgs); /* Set the color to black. */
319 }
320 penum->width_status = sws_cache;
321 return 0;
322}
323
324/* setcharwidth */
325int
326gs_setcharwidth(register gs_show_enum *penum, gs_state *pgs, floatp wx, floatp wy)
327{ if ( penum->width_status != sws_none )
328 return_error(gs_error_undefined);
329 gs_distance_transform2fixed(&pgs->ctm, wx, wy, &penum->wxy);
330 penum->width_status = sws_no_cache;
331 return 0;
332}
333
334/* setmetrics */
335int
336gs_setmetrics(register gs_show_enum *penum, gs_state *pgs,
337 gs_point *psbxy, gs_point *pwxy)
338{ if ( penum->width_status != sws_none )
339 return_error(gs_error_undefined);
340 if ( psbxy != 0 )
341 { penum->metrics_sb.x = float2fixed(psbxy->x);
342 penum->metrics_sb.y = float2fixed(psbxy->y);
343 penum->sb_set = 1;
344 }
345 if ( pwxy != 0 )
346 { penum->metrics_width.x = float2fixed(pwxy->x);
347 penum->metrics_width.y = float2fixed(pwxy->y);
348 penum->width_set = 1;
349 }
350 return 0;
351}
352
353/* ------ Enumerator ------ */
354
355/* Do the next step of a show (or stringwidth) operation */
356int
357gs_show_next(gs_show_enum *penum)
358{ return (*penum->continue_proc)(penum);
359}
360
361/* Continuation procedures */
362#define show_fast_move(wxy, pgs)\
363 gx_path_add_rel_point_inline(pgs->path, wxy.x, wxy.y)
364private int show_update(P1(register gs_show_enum *penum));
365private int show_move(P1(register gs_show_enum *penum));
366private int show_proceed(P1(register gs_show_enum *penum));
367private int show_finish(P1(register gs_show_enum *penum));
368private int
369continue_show_update(register gs_show_enum *penum)
370{ int code = show_update(penum);
371 if ( code < 0 ) return code;
372 code = show_move(penum);
373 if ( code != 0 ) return code;
374 return show_proceed(penum);
375}
376private int
377continue_show(register gs_show_enum *penum)
378{ return show_proceed(penum);
379}
380
381/* Update position */
382private int
383show_update(register gs_show_enum *penum)
384{ register gs_state *pgs = penum->pgs;
385 /* Update position for last character */
386 switch ( penum->width_status )
387 {
388 case sws_none:
389 /* Adobe interpreters assume a character width of 0, */
390 /* even though the documentation says this is an error.... */
391 penum->wxy.x = penum->wxy.y = 0;
392 break;
393 case sws_cache:
394 { /* Finish installing the cache entry. */
395 cached_char *cc = penum->cc;
396 int code;
397 /* If the BuildChar procedure did a save and a restore, */
398 /* it already undid the gsave in setcachedevice. */
399 /* We have to check for this by comparing levels. */
400 switch ( pgs->level - penum->level )
401 {
402 default:
403 return_error(gs_error_invalidfont); /* WRONG */
404 case 2:
405 code = gs_grestore(pgs);
406 if ( code < 0 ) return code;
407 case 1:
408 ;
409 }
410 gx_add_cached_char(pgs->font->dir, &penum->dev_cache_info,
411 cc, gx_lookup_fm_pair(pgs));
412 if ( !penum->stringwidth_flag && !penum->charpath_flag )
413 { /* Copy the bits to the real output device. */
414 penum->color_loaded = 0; /* force gx_color_render */
415 code = gs_grestore(pgs);
416 if ( code < 0 ) return code;
417 return gx_image_cached_char(penum, cc);
418 }
419 }
420 case sws_no_cache: ;
421 }
422 return gs_grestore(pgs);
423}
424
425/* Move to next character */
426private int
427show_move(register gs_show_enum *penum)
428{ register gs_state *pgs = penum->pgs;
429 if ( penum->add )
430 gs_rmoveto(pgs, penum->ax, penum->ay);
431 if ( penum->str[penum->index - 1] == penum->wchr )
432 gs_rmoveto(pgs, penum->wcx, penum->wcy);
433 /* wxy is in device coordinates */
434 { int code = show_fast_move(penum->wxy, pgs);
435 if ( code < 0 ) return code;
436 }
437 /* Check for kerning, but not on the last character. */
438 if ( penum->do_kern && penum->index < penum->size )
439 { penum->continue_proc = continue_show;
440 return gs_show_kern;
441 }
442 return 0;
443}
444/* Process next character */
445private int
446show_proceed(register gs_show_enum *penum)
447{ register gs_state *pgs = penum->pgs;
448 byte *str = penum->str;
449 uint index;
450 cached_fm_pair *pair = 0;
451 char_code chr;
452 int code;
453 penum->color_loaded = 0;
454more: /* Proceed to next character */
455 if ( penum->can_cache )
456 { /* Loop with cache */
457 if ( pair == 0 )
458 pair = gx_lookup_fm_pair(pgs);
459 if ( penum->stringwidth_flag )
460 while ( (index = penum->index++) != penum->size )
461 { cached_char *cc;
462 chr = str[index];
463 cc = gx_lookup_cached_char(pgs, pair, chr);
464 if ( cc == 0 ) goto no_cache;
465 /* Character is in cache. */
466 code = show_fast_move(cc->wxy, pgs);
467 if ( code ) return code;
468 }
469 else
470 while ( (index = penum->index++) != penum->size )
471 { cached_char *cc;
472 chr = str[index];
473 cc = gx_lookup_cached_char(pgs, pair, chr);
474 if ( cc == 0 ) goto no_cache;
475 /* Character is in cache. */
476 code = gx_image_cached_char(penum, cc);
477 if ( code < 0 ) return code;
478 else if ( code > 0 ) goto no_cache;
479 if ( penum->slow_show )
480 { penum->wxy = cc->wxy;
481 code = show_move(penum);
482 }
483 else
484 code = show_fast_move(cc->wxy, pgs);
485 if ( code ) return code;
486 }
487 /* All done. */
488 return show_finish(penum);
489 }
490 else
491 { /* Can't use cache */
492 if ( (index = penum->index++) == penum->size )
493 { /* All done. */
494 return show_finish(penum);
495 }
496 chr = str[index];
497 }
498no_cache:
499 /* Character is not cached, client must render it. */
500 if ( (code = gs_gsave(pgs)) < 0 ) return code;
501 /* Set the charpath flag in the graphics context if necessary, */
502 /* so that fill and stroke will add to the path */
503 /* rather than having their usual effect. */
504 pgs->in_charpath = penum->charpath_flag;
505 { gs_fixed_point cpt;
506 gx_path *ppath = pgs->path;
507 if ( (code = gx_path_current_point_inline(ppath, &cpt)) < 0 )
508 return code;
509 cpt.x -= pgs->ctm.tx_fixed;
510 cpt.y -= pgs->ctm.ty_fixed;
511 gs_setmatrix(pgs, &pgs->char_tm);
512 cpt.x += pgs->ctm.tx_fixed;
513 cpt.y += pgs->ctm.ty_fixed;
514 if ( !penum->stringwidth_flag && !penum->charpath_flag )
515 { /* Round the translation in the graphics state. */
516 /* This helps prevent rounding artifacts later. */
517 cpt.x = fixed_rounded(cpt.x);
518 cpt.y = fixed_rounded(cpt.y);
519 }
520 gs_translate_to_fixed(pgs, cpt.x, cpt.y);
521 gs_newpath(pgs);
522 gx_path_add_point(ppath, pgs->ctm.tx_fixed,
523 pgs->ctm.ty_fixed);
524 }
525 penum->width_status = sws_none;
526 penum->width_set = penum->sb_set = 0;
527 penum->continue_proc = continue_show_update;
528 /* Try using the build procedure in the font. */
529 /* < 0 means error, 0 means success, 1 means failure. */
530 { gs_font *pfont = pgs->font;
531 code = (*pfont->build_char_proc)(penum, pgs, pfont, chr, pfont->build_char_data);
532 if ( code < 0 ) return_error(code);
533 if ( code == 0 )
534 { code = show_update(penum);
535 if ( code < 0 ) return code;
536 penum->color_loaded = 0;
537 code = show_move(penum);
538 if ( code ) return code;
539 goto more;
540 }
541 }
542 return gs_show_render;
543}
544
545/* Finish show or stringwidth */
546private int
547show_finish(register gs_show_enum *penum)
548{ register gs_state *pgs = penum->pgs;
549 int code;
550 if ( !penum->stringwidth_flag ) return 0;
551 /* Save the accumulated width before returning, */
552 /* and undo the extra gsave. */
553 code = gs_currentpoint(pgs, &penum->width);
554 if ( code < 0 ) return code;
555 return gs_grestore(pgs);
556}
557
558/* Return the current character for rendering. */
559char_code
560gs_show_current_char(gs_show_enum *penum)
561{ return penum->str[penum->index - 1];
562}
563
564/* Return the just-displayed character for kerning. */
565char_code
566gs_kshow_previous_char(gs_show_enum *penum)
567{ return penum->str[penum->index - 1];
568}
569
570/* Return the about-to-be-displayed character for kerning. */
571char_code
572gs_kshow_next_char(gs_show_enum *penum)
573{ return penum->str[penum->index];
574}
575
576/* Return the accumulated width for stringwidth. */
577void
578gs_show_width(gs_show_enum *penum, gs_point *ppt)
579{ *ppt = penum->width;
580}
581
582/* Return the charpath flag. */
583int
584gs_show_in_charpath(gs_show_enum *penum)
585{ return penum->charpath_flag;
586}
587
588/* ------ Internal routines ------ */
589
590/* Initialize a show enumerator */
591private int
592show_setup(register gs_show_enum *penum, gs_state *pgs, char *str)
593{ int code;
594 gs_font *pfont = pgs->font;
595 penum->pgs = pgs;
596 penum->level = pgs->level;
597 penum->str = (byte *)str; /* avoid signed chars */
598 penum->wchr = no_chr;
599 penum->add = 0;
600 penum->do_kern = 0;
601 penum->charpath_flag = 0;
602 penum->stringwidth_flag = 0;
603 penum->dev_cache_set = 0;
604 penum->index = 0;
605 penum->continue_proc = continue_show;
606 if ( (penum->is_composite = pfont->FontType == ft_composite) )
607 { gs_font *rfont = pgs->font;
608 penum->fstack[0] = rfont;
609 penum->fdepth = 0;
610 penum->pfont =
611 rfont->data.type0_data.FDepVector[rfont->data.type0_data.Encoding[0]];
612 }
613 if ( !pgs->char_tm_valid )
614 { /* Compute combined transformation */
615 gs_make_identity(&pgs->char_tm); /* make sure type */
616 /* fields are set in char_tm! */
617 code = gs_matrix_multiply(&pgs->font->FontMatrix,
618 &ctm_only(pgs), &pgs->char_tm);
619 if ( code < 0 ) return code;
620 pgs->char_tm_valid = 1;
621 }
622 if ( penum->can_cache = /* no skewing or non-rectangular rotation */
623 (is_fzero2(pgs->char_tm.xy, pgs->char_tm.yx) ||
624 is_fzero2(pgs->char_tm.xx, pgs->char_tm.yy)) )
625 { gs_fixed_rect cbox;
626 gx_cpath_box_for_check(pgs->clip_path, &cbox);
627 penum->cxmin = fixed2int_var(cbox.p.x);
628 penum->cymin = fixed2int_var(cbox.p.y);
629 penum->cxmax = fixed2int_var(cbox.q.x);
630 penum->cymax = fixed2int_var(cbox.q.y);
631 penum->ftx = (int)fixed2long(float2fixed(pgs->char_tm.tx) - pgs->ctm.tx_fixed);
632 penum->fty = (int)fixed2long(float2fixed(pgs->char_tm.ty) - pgs->ctm.ty_fixed);
633 }
634 return 0;
635}