/* Copyright (C) 1998, 2000 artofcode LLC. All rights reserved. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA, 02111-1307. */ /*$Id: gstext.c,v 1.6.2.2.2.1 2003/01/17 00:49:03 giles Exp $ */ /* Driver text interface support */ #include "memory_.h" #include "gstypes.h" #include "gdebug.h" #include "gserror.h" #include "gserrors.h" #include "gsmemory.h" #include "gsstruct.h" #include "gstypes.h" #include "gxdevcli.h" #include "gxdcolor.h" /* for gs_state_color_load */ #include "gxfont.h" /* for init_fstack */ #include "gxpath.h" #include "gxtext.h" #include "gzstate.h" /* GC descriptors */ public_st_gs_text_params(); public_st_gs_text_enum(); private ENUM_PTRS_WITH(text_params_enum_ptrs, gs_text_params_t *tptr) return 0; case 0: if (tptr->operation & TEXT_FROM_STRING) { return ENUM_CONST_STRING2(tptr->data.bytes, tptr->size); } if (tptr->operation & TEXT_FROM_BYTES) return ENUM_OBJ(tptr->data.bytes); if (tptr->operation & TEXT_FROM_CHARS) return ENUM_OBJ(tptr->data.chars); if (tptr->operation & TEXT_FROM_GLYPHS) return ENUM_OBJ(tptr->data.glyphs); return ENUM_OBJ(NULL); case 1: return ENUM_OBJ(tptr->operation & TEXT_REPLACE_WIDTHS ? tptr->x_widths : NULL); case 2: return ENUM_OBJ(tptr->operation & TEXT_REPLACE_WIDTHS ? tptr->y_widths : NULL); ENUM_PTRS_END private RELOC_PTRS_WITH(text_params_reloc_ptrs, gs_text_params_t *tptr) { if (tptr->operation & TEXT_FROM_STRING) { gs_const_string str; str.data = tptr->data.bytes; str.size = tptr->size; RELOC_CONST_STRING_VAR(str); tptr->data.bytes = str.data; } else if (tptr->operation & TEXT_FROM_BYTES) RELOC_OBJ_VAR(tptr->data.bytes); else if (tptr->operation & TEXT_FROM_CHARS) RELOC_OBJ_VAR(tptr->data.chars); else if (tptr->operation & TEXT_FROM_GLYPHS) RELOC_OBJ_VAR(tptr->data.glyphs); if (tptr->operation & TEXT_REPLACE_WIDTHS) { RELOC_OBJ_VAR(tptr->x_widths); RELOC_OBJ_VAR(tptr->y_widths); } } RELOC_PTRS_END private ENUM_PTRS_WITH(text_enum_enum_ptrs, gs_text_enum_t *eptr) { index -= 8; if (index <= eptr->fstack.depth) ENUM_RETURN(eptr->fstack.items[index].font); index -= eptr->fstack.depth + 1; return ENUM_USING(st_gs_text_params, &eptr->text, sizeof(eptr->text), index); } case 0: return ENUM_OBJ(gx_device_enum_ptr(eptr->dev)); case 1: return ENUM_OBJ(gx_device_enum_ptr(eptr->imaging_dev)); ENUM_PTR3(2, gs_text_enum_t, pis, orig_font, path); ENUM_PTR3(5, gs_text_enum_t, pdcolor, pcpath, current_font); ENUM_PTRS_END private RELOC_PTRS_WITH(text_enum_reloc_ptrs, gs_text_enum_t *eptr) { int i; RELOC_USING(st_gs_text_params, &eptr->text, sizeof(eptr->text)); eptr->dev = gx_device_reloc_ptr(eptr->dev, gcst); eptr->imaging_dev = gx_device_reloc_ptr(eptr->imaging_dev, gcst); RELOC_PTR3(gs_text_enum_t, pis, orig_font, path); RELOC_PTR3(gs_text_enum_t, pdcolor, pcpath, current_font); for (i = 0; i <= eptr->fstack.depth; i++) RELOC_PTR(gs_text_enum_t, fstack.items[i].font); } RELOC_PTRS_END /* Begin processing text. */ int gx_device_text_begin(gx_device * dev, gs_imager_state * pis, const gs_text_params_t * text, gs_font * font, gx_path * path, /* unless DO_NONE & !RETURN_WIDTH */ const gx_device_color * pdcolor, /* DO_DRAW */ const gx_clip_path * pcpath, /* DO_DRAW */ gs_memory_t * mem, gs_text_enum_t ** ppte) { if (TEXT_PARAMS_ARE_INVALID(text)) return_error(gs_error_rangecheck); { gx_path *tpath = ((text->operation & TEXT_DO_NONE) && !(text->operation & TEXT_RETURN_WIDTH) ? 0 : path); const gx_device_color *tcolor = (text->operation & TEXT_DO_DRAW ? pdcolor : 0); const gx_clip_path *tcpath = (text->operation & TEXT_DO_DRAW ? pcpath : 0); return dev_proc(dev, text_begin) (dev, pis, text, font, tpath, tcolor, tcpath, mem, ppte); } } /* * Initialize a newly created text enumerator. Implementations of * text_begin must call this just after allocating the enumerator. */ private int gs_text_enum_init_dynamic(gs_text_enum_t *pte, gs_font *font) { pte->current_font = font; pte->index = 0; pte->xy_index = 0; pte->FontBBox_as_Metrics2.x = pte->FontBBox_as_Metrics2.y = 0; return font->procs.init_fstack(pte, font); } int gs_text_enum_init(gs_text_enum_t *pte, const gs_text_enum_procs_t *procs, gx_device *dev, gs_imager_state *pis, const gs_text_params_t *text, gs_font *font, gx_path *path, const gx_device_color *pdcolor, const gx_clip_path *pcpath, gs_memory_t *mem) { int code; pte->text = *text; pte->dev = dev; pte->imaging_dev = NULL; pte->pis = pis; pte->orig_font = font; pte->path = path; pte->pdcolor = pdcolor; pte->pcpath = pcpath; pte->memory = mem; pte->procs = procs; /* text_begin procedure sets rc */ /* init_dynamic sets current_font */ pte->log2_scale.x = pte->log2_scale.y = 0; /* init_dynamic sets index, xy_index, fstack */ code = gs_text_enum_init_dynamic(pte, font); if (code >= 0) rc_increment(dev); return code; } /* * Copy the dynamically changing elements from one enumerator to another. * This is useful primarily for enumerators that sometimes pass the * operation to a subsidiary enumerator. */ void gs_text_enum_copy_dynamic(gs_text_enum_t *pto, const gs_text_enum_t *pfrom, bool for_return) { int depth = pfrom->fstack.depth; pto->current_font = pfrom->current_font; pto->index = pfrom->index; pto->xy_index = pfrom->xy_index; pto->fstack.depth = depth; pto->FontBBox_as_Metrics2 = pfrom->FontBBox_as_Metrics2; if (depth >= 0) memcpy(pto->fstack.items, pfrom->fstack.items, (depth + 1) * sizeof(pto->fstack.items[0])); if (for_return) { pto->cmap_code = pfrom->cmap_code; pto->returned = pfrom->returned; } } /* Begin processing text based on a graphics state. */ int gs_text_begin(gs_state * pgs, const gs_text_params_t * text, gs_memory_t * mem, gs_text_enum_t ** ppte) { gx_clip_path *pcpath = 0; if (text->operation & TEXT_DO_DRAW) { int code = gx_effective_clip_path(pgs, &pcpath); if (code < 0) return code; gx_set_dev_color(pgs); code = gs_state_color_load(pgs); if (code < 0) return code; } return gx_device_text_begin(pgs->device, (gs_imager_state *) pgs, text, pgs->font, pgs->path, pgs->dev_color, pcpath, mem, ppte); } /* Begin PostScript-equivalent text operations. */ int gs_show_begin(gs_state * pgs, const byte * str, uint size, gs_memory_t * mem, gs_text_enum_t ** ppte) { gs_text_params_t text; text.operation = TEXT_FROM_STRING | TEXT_DO_DRAW | TEXT_RETURN_WIDTH; text.data.bytes = str, text.size = size; return gs_text_begin(pgs, &text, mem, ppte); } int gs_ashow_begin(gs_state * pgs, floatp ax, floatp ay, const byte * str, uint size, gs_memory_t * mem, gs_text_enum_t ** ppte) { gs_text_params_t text; text.operation = TEXT_FROM_STRING | TEXT_ADD_TO_ALL_WIDTHS | TEXT_DO_DRAW | TEXT_RETURN_WIDTH; text.data.bytes = str, text.size = size; text.delta_all.x = ax; text.delta_all.y = ay; return gs_text_begin(pgs, &text, mem, ppte); } int gs_widthshow_begin(gs_state * pgs, floatp cx, floatp cy, gs_char chr, const byte * str, uint size, gs_memory_t * mem, gs_text_enum_t ** ppte) { gs_text_params_t text; text.operation = TEXT_FROM_STRING | TEXT_ADD_TO_SPACE_WIDTH | TEXT_DO_DRAW | TEXT_RETURN_WIDTH; text.data.bytes = str, text.size = size; text.delta_space.x = cx; text.delta_space.y = cy; text.space.s_char = chr; return gs_text_begin(pgs, &text, mem, ppte); } int gs_awidthshow_begin(gs_state * pgs, floatp cx, floatp cy, gs_char chr, floatp ax, floatp ay, const byte * str, uint size, gs_memory_t * mem, gs_text_enum_t ** ppte) { gs_text_params_t text; text.operation = TEXT_FROM_STRING | TEXT_ADD_TO_ALL_WIDTHS | TEXT_ADD_TO_SPACE_WIDTH | TEXT_DO_DRAW | TEXT_RETURN_WIDTH; text.data.bytes = str, text.size = size; text.delta_space.x = cx; text.delta_space.y = cy; text.space.s_char = chr; text.delta_all.x = ax; text.delta_all.y = ay; return gs_text_begin(pgs, &text, mem, ppte); } int gs_kshow_begin(gs_state * pgs, const byte * str, uint size, gs_memory_t * mem, gs_text_enum_t ** ppte) { gs_text_params_t text; text.operation = TEXT_FROM_STRING | TEXT_DO_DRAW | TEXT_INTERVENE | TEXT_RETURN_WIDTH; text.data.bytes = str, text.size = size; return gs_text_begin(pgs, &text, mem, ppte); } int gs_xyshow_begin(gs_state * pgs, const byte * str, uint size, const float *x_widths, const float *y_widths, uint widths_size, gs_memory_t * mem, gs_text_enum_t ** ppte) { gs_text_params_t text; text.operation = TEXT_FROM_STRING | TEXT_REPLACE_WIDTHS | TEXT_DO_DRAW | TEXT_RETURN_WIDTH; text.data.bytes = str, text.size = size; text.x_widths = x_widths; text.y_widths = y_widths; text.widths_size = widths_size; return gs_text_begin(pgs, &text, mem, ppte); } private void setup_FontBBox_as_Metrics2 (gs_text_enum_t * pte, gs_font * pfont) { /* When we exec a operator like `show' that has a a chance to get a glyph from a char, we can set FontBBox_as_Metrics2 in gschar0.c:gs_type0_next_char_glyph. In other hand, when we exec a operator like `glyphshow' that get a glyph directly from an input file, gschar0.c:gs_type0_next_char_glyph is exec'ed. For the later case, we set up FontBBox_as_Metrics2 with using this procedure.. */ if (pfont->FontType == ft_CID_encrypted || pfont->FontType == ft_CID_TrueType) pte->FontBBox_as_Metrics2 = ((gs_font_base *)pfont)->FontBBox.q; } int gs_glyphshow_begin(gs_state * pgs, gs_glyph glyph, gs_memory_t * mem, gs_text_enum_t ** ppte) { gs_text_params_t text; int result; text.operation = TEXT_FROM_SINGLE_GLYPH | TEXT_DO_DRAW | TEXT_RETURN_WIDTH; text.data.d_glyph = glyph; text.size = 1; result = gs_text_begin(pgs, &text, mem, ppte); if (result == 0) setup_FontBBox_as_Metrics2(*ppte, pgs->font); return result; } int gs_cshow_begin(gs_state * pgs, const byte * str, uint size, gs_memory_t * mem, gs_text_enum_t ** ppte) { gs_text_params_t text; text.operation = TEXT_FROM_STRING | TEXT_DO_NONE | TEXT_INTERVENE; text.data.bytes = str, text.size = size; return gs_text_begin(pgs, &text, mem, ppte); } int gs_stringwidth_begin(gs_state * pgs, const byte * str, uint size, gs_memory_t * mem, gs_text_enum_t ** ppte) { gs_text_params_t text; text.operation = TEXT_FROM_STRING | TEXT_DO_NONE | TEXT_RETURN_WIDTH; text.data.bytes = str, text.size = size; return gs_text_begin(pgs, &text, mem, ppte); } int gs_charpath_begin(gs_state * pgs, const byte * str, uint size, bool stroke_path, gs_memory_t * mem, gs_text_enum_t ** ppte) { gs_text_params_t text; text.operation = TEXT_FROM_STRING | TEXT_RETURN_WIDTH | (stroke_path ? TEXT_DO_TRUE_CHARPATH : TEXT_DO_FALSE_CHARPATH); text.data.bytes = str, text.size = size; return gs_text_begin(pgs, &text, mem, ppte); } int gs_charboxpath_begin(gs_state * pgs, const byte * str, uint size, bool stroke_path, gs_memory_t * mem, gs_text_enum_t ** ppte) { gs_text_params_t text; text.operation = TEXT_FROM_STRING | TEXT_RETURN_WIDTH | (stroke_path ? TEXT_DO_TRUE_CHARBOXPATH : TEXT_DO_FALSE_CHARBOXPATH); text.data.bytes = str, text.size = size; return gs_text_begin(pgs, &text, mem, ppte); } int gs_glyphpath_begin(gs_state * pgs, gs_glyph glyph, bool stroke_path, gs_memory_t * mem, gs_text_enum_t ** ppte) { gs_text_params_t text; int result; text.operation = TEXT_FROM_SINGLE_GLYPH | TEXT_RETURN_WIDTH | (stroke_path ? TEXT_DO_TRUE_CHARPATH : TEXT_DO_FALSE_CHARPATH); text.data.d_glyph = glyph; text.size = 1; result = gs_text_begin(pgs, &text, mem, ppte); if (result == 0) setup_FontBBox_as_Metrics2(*ppte, pgs->font); return result; } int gs_glyphwidth_begin(gs_state * pgs, gs_glyph glyph, gs_memory_t * mem, gs_text_enum_t ** ppte) { gs_text_params_t text; int result; text.operation = TEXT_FROM_SINGLE_GLYPH | TEXT_DO_NONE | TEXT_RETURN_WIDTH; text.data.d_glyph = glyph; text.size = 1; result = gs_text_begin(pgs, &text, mem, ppte); if (result == 0) setup_FontBBox_as_Metrics2(*ppte, pgs->font); return result; } /* Restart processing with different parameters. */ int gs_text_restart(gs_text_enum_t *pte, const gs_text_params_t *text) { gs_text_enum_t tenum; tenum = *pte; tenum.text = *text; gs_text_enum_init_dynamic(&tenum, pte->orig_font); setup_FontBBox_as_Metrics2(pte, pte->orig_font); return gs_text_resync(pte, &tenum); } /* * Resync text processing with new parameters and string position. */ int gs_text_resync(gs_text_enum_t *pte, const gs_text_enum_t *pfrom) { return pte->procs->resync(pte, pfrom); } /* Process text after 'begin'. */ int gs_text_process(gs_text_enum_t * pte) { return pte->procs->process(pte); } /* Access elements of the enumerator. */ gs_font * gs_text_current_font(const gs_text_enum_t *pte) { return pte->current_font; } gs_char gs_text_current_char(const gs_text_enum_t *pte) { return pte->returned.current_char; } gs_char gs_text_next_char(const gs_text_enum_t *pte) { const uint operation = pte->text.operation; if (pte->index >= pte->text.size) return gs_no_char; /* rangecheck */ if (operation & (TEXT_FROM_STRING | TEXT_FROM_BYTES)) return pte->text.data.bytes[pte->index]; if (operation & TEXT_FROM_CHARS) return pte->text.data.chars[pte->index]; return gs_no_char; /* rangecheck */ } gs_glyph gs_text_current_glyph(const gs_text_enum_t *pte) { return pte->returned.current_glyph; } int gs_text_total_width(const gs_text_enum_t *pte, gs_point *pwidth) { *pwidth = pte->returned.total_width; return 0; } /* Assuming REPLACE_WIDTHS is set, return the width of the i'th character. */ int gs_text_replaced_width(const gs_text_params_t *text, uint index, gs_point *pwidth) { const float *x_widths = text->x_widths; const float *y_widths = text->y_widths; if (index > text->size) return_error(gs_error_rangecheck); if (x_widths == y_widths) { if (x_widths) { index *= 2; pwidth->x = x_widths[index]; pwidth->y = x_widths[index + 1]; } else pwidth->x = pwidth->y = 0; } else { pwidth->x = (x_widths ? x_widths[index] : 0.0); pwidth->y = (y_widths ? y_widths[index] : 0.0); } return 0; } /* Determine whether only the width is needed. */ bool gs_text_is_width_only(const gs_text_enum_t * pte) { return pte->procs->is_width_only(pte); } /* Return the width of the current character. */ int gs_text_current_width(const gs_text_enum_t * pte, gs_point *pwidth) { return pte->procs->current_width(pte, pwidth); } /* Set text metrics and optionally enable caching. */ int gs_text_set_cache(gs_text_enum_t * pte, const double *values, gs_text_cache_control_t control) { return pte->procs->set_cache(pte, values, control); } int gs_text_setcharwidth(gs_text_enum_t * pte, const double wxy[2]) { return pte->procs->set_cache(pte, wxy, TEXT_SET_CHAR_WIDTH); } int gs_text_setcachedevice(gs_text_enum_t * pte, const double wbox[6]) { return pte->procs->set_cache(pte, wbox, TEXT_SET_CACHE_DEVICE); } int gs_text_setcachedevice2(gs_text_enum_t * pte, const double wbox2[10]) { return pte->procs->set_cache(pte, wbox2, TEXT_SET_CACHE_DEVICE2); } /* Retry processing the last character without caching. */ int gs_text_retry(gs_text_enum_t * pte) { return pte->procs->retry(pte); } /* Release the text processing structures. */ void gx_default_text_release(gs_text_enum_t *pte, client_name_t cname) { rc_decrement_only(pte->dev, cname); rc_decrement_only(pte->imaging_dev, cname); } void rc_free_text_enum(gs_memory_t * mem, void *obj, client_name_t cname) { gs_text_enum_t *pte = obj; pte->procs->release(pte, cname); rc_free_struct_only(mem, obj, cname); } void gs_text_release(gs_text_enum_t * pte, client_name_t cname) { rc_decrement_only(pte, cname); } /* ---------------- Default font rendering procedures ---------------- */ /* Default fstack initialization procedure. */ int gs_default_init_fstack(gs_text_enum_t *pte, gs_font *pfont) { pte->fstack.depth = -1; return 0; } /* Default next-glyph procedure. */ int gs_default_next_char_glyph(gs_text_enum_t *pte, gs_char *pchr, gs_glyph *pglyph) { if (pte->index >= pte->text.size) return 2; if (pte->text.operation & (TEXT_FROM_STRING | TEXT_FROM_BYTES)) { /* ordinary string */ *pchr = pte->text.data.bytes[pte->index]; *pglyph = gs_no_glyph; } else if (pte->text.operation & TEXT_FROM_SINGLE_GLYPH) { /* glyphshow or glyphpath */ *pchr = gs_no_char; *pglyph = pte->text.data.d_glyph; } else if (pte->text.operation & TEXT_FROM_GLYPHS) { *pchr = gs_no_char; *pglyph = pte->text.data.glyphs[pte->index]; } else if (pte->text.operation & TEXT_FROM_SINGLE_CHAR) { *pchr = pte->text.data.d_char; *pglyph = gs_no_glyph; } else if (pte->text.operation & TEXT_FROM_CHARS) { *pchr = pte->text.data.chars[pte->index]; *pglyph = gs_no_glyph; } else return_error(gs_error_rangecheck); /* shouldn't happen */ pte->index++; return 0; } /* Dummy (ineffective) BuildChar/BuildGlyph procedure */ int gs_no_build_char(gs_text_enum_t *pte, gs_state *pgs, gs_font *pfont, gs_char chr, gs_glyph glyph) { return 1; /* failure, but not error */ }