1 /* Copyright (C) 1996, 2000 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: gdevpdft.c,v 1.36 2001/07/30 15:11:00 lpd Exp $ */ 20 /* Text handling for PDF-writing driver. */ 21 #include "math_.h" 22 #include "memory_.h" 23 #include "string_.h" 24 #include "gx.h" 25 #include "gserrors.h" 26 #include "gxpath.h" /* for getting current point */ 27 #include "gdevpdfx.h" 28 #include "gdevpdff.h" 29 #include "gdevpdfg.h" 30 #include "scommon.h" 31 32 /* GC descriptors */ 33 private_st_pdf_text_enum(); 34 35 /* Define the auxiliary procedures for text processing. */ 36 private int 37 pdf_text_resync(gs_text_enum_t *pte, const gs_text_enum_t *pfrom) 38 { 39 pdf_text_enum_t *const penum = (pdf_text_enum_t *)pte; 40 41 if ((pte->text.operation ^ pfrom->text.operation) & ~TEXT_FROM_ANY) 42 return_error(gs_error_rangecheck); 43 if (penum->pte_default) { 44 int code = gs_text_resync(penum->pte_default, pfrom); 45 46 if (code < 0) 47 return code; 48 } 49 pte->text = pfrom->text; 50 gs_text_enum_copy_dynamic(pte, pfrom, false); 51 return 0; 52 } 53 private bool 54 pdf_text_is_width_only(const gs_text_enum_t *pte) 55 { 56 const pdf_text_enum_t *const penum = (const pdf_text_enum_t *)pte; 57 58 if (penum->pte_default) 59 return gs_text_is_width_only(penum->pte_default); 60 return false; 61 } 62 private int 63 pdf_text_current_width(const gs_text_enum_t *pte, gs_point *pwidth) 64 { 65 const pdf_text_enum_t *const penum = (const pdf_text_enum_t *)pte; 66 67 if (penum->pte_default) 68 return gs_text_current_width(penum->pte_default, pwidth); 69 return_error(gs_error_rangecheck); /* can't happen */ 70 } 71 private int 72 pdf_text_set_cache(gs_text_enum_t *pte, const double *pw, 73 gs_text_cache_control_t control) 74 { 75 pdf_text_enum_t *const penum = (pdf_text_enum_t *)pte; 76 77 if (penum->pte_default) 78 return gs_text_set_cache(penum->pte_default, pw, control); 79 return_error(gs_error_rangecheck); /* can't happen */ 80 } 81 private int 82 pdf_text_retry(gs_text_enum_t *pte) 83 { 84 pdf_text_enum_t *const penum = (pdf_text_enum_t *)pte; 85 86 if (penum->pte_default) 87 return gs_text_retry(penum->pte_default); 88 return_error(gs_error_rangecheck); /* can't happen */ 89 } 90 private void 91 pdf_text_release(gs_text_enum_t *pte, client_name_t cname) 92 { 93 pdf_text_enum_t *const penum = (pdf_text_enum_t *)pte; 94 95 if (penum->pte_default) { 96 gs_text_release(penum->pte_default, cname); 97 penum->pte_default = 0; 98 } 99 gx_default_text_release(pte, cname); 100 } 101 102 /* Begin processing text. */ 103 extern text_enum_proc_process(pdf_text_process); /* in gdevpdfs.c */ 104 private const gs_text_enum_procs_t pdf_text_procs = { 105 pdf_text_resync, pdf_text_process, 106 pdf_text_is_width_only, pdf_text_current_width, 107 pdf_text_set_cache, pdf_text_retry, 108 pdf_text_release 109 }; 110 int 111 gdev_pdf_text_begin(gx_device * dev, gs_imager_state * pis, 112 const gs_text_params_t *text, gs_font * font, 113 gx_path * path, const gx_device_color * pdcolor, 114 const gx_clip_path * pcpath, 115 gs_memory_t * mem, gs_text_enum_t ** ppte) 116 { 117 gx_device_pdf *const pdev = (gx_device_pdf *)dev; 118 pdf_text_enum_t *penum; 119 gs_fixed_point cpt; 120 int code; 121 122 /* Track the dominant text rotation. */ 123 { 124 gs_matrix tmat; 125 int i; 126 127 gs_matrix_multiply(&font->FontMatrix, &ctm_only(pis), &tmat); 128 if (is_xxyy(&tmat)) 129 i = (tmat.xx >= 0 ? 0 : 2); 130 else if (is_xyyx(&tmat)) 131 i = (tmat.xy >= 0 ? 1 : 3); 132 else 133 i = 4; 134 pdf_current_page(pdev)->text_rotation.counts[i] += text->size; 135 } 136 137 if (!(text->operation & TEXT_DO_DRAW) || path == 0 || 138 gx_path_current_point(path, &cpt) < 0 139 ) 140 return gx_default_text_begin(dev, pis, text, font, path, pdcolor, 141 pcpath, mem, ppte); 142 143 code = pdf_prepare_fill(pdev, pis); 144 if (code < 0) 145 return code; 146 147 if (text->operation & TEXT_DO_DRAW) { 148 /* 149 * Set the clipping path and drawing color. We set both the fill 150 * and stroke color, because we don't know whether the fonts will be 151 * filled or stroked, and we can't set a color while we are in text 152 * mode. (This is a consequence of the implementation, not a 153 * limitation of PDF.) 154 */ 155 156 if (pdf_must_put_clip_path(pdev, pcpath)) { 157 int code = pdf_open_page(pdev, PDF_IN_STREAM); 158 159 if (code < 0) 160 return code; 161 pdf_put_clip_path(pdev, pcpath); 162 } 163 164 if ((code = 165 pdf_set_drawing_color(pdev, pdcolor, &pdev->stroke_color, 166 &psdf_set_stroke_color_commands)) < 0 || 167 (code = 168 pdf_set_drawing_color(pdev, pdcolor, &pdev->fill_color, 169 &psdf_set_fill_color_commands)) < 0 170 ) 171 return code; 172 } 173 174 /* Allocate and initialize the enumerator. */ 175 176 rc_alloc_struct_1(penum, pdf_text_enum_t, &st_pdf_text_enum, mem, 177 return_error(gs_error_VMerror), "gdev_pdf_text_begin"); 178 penum->rc.free = rc_free_text_enum; 179 penum->pte_default = 0; 180 code = gs_text_enum_init((gs_text_enum_t *)penum, &pdf_text_procs, 181 dev, pis, text, font, path, pdcolor, pcpath, mem); 182 if (code < 0) { 183 gs_free_object(mem, penum, "gdev_pdf_text_begin"); 184 return code; 185 } 186 187 *ppte = (gs_text_enum_t *)penum; 188 189 return 0; 190 } 191 192 /* ---------------- Text and font utilities ---------------- */ 193 194 /* Forward declarations */ 195 private int assign_char_code(P1(gx_device_pdf * pdev)); 196 197 /* 198 * Set the current font and size, writing a Tf command if needed. 199 */ 200 int 201 pdf_set_font_and_size(gx_device_pdf * pdev, pdf_font_t * font, floatp size) 202 { 203 if (font != pdev->text.font || size != pdev->text.size) { 204 int code = pdf_open_page(pdev, PDF_IN_TEXT); 205 stream *s = pdev->strm; 206 207 if (code < 0) 208 return code; 209 pprints1(s, "/%s ", font->rname); 210 pprintg1(s, "%g Tf\n", size); 211 pdev->text.font = font; 212 pdev->text.size = size; 213 } 214 font->where_used |= pdev->used_mask; 215 return 0; 216 } 217 218 /* 219 * Set the text matrix for writing text. 220 * The translation component of the matrix is the text origin. 221 * If the non-translation components of the matrix differ from the 222 * current ones, write a Tm command; if there is only a Y translation 223 * and it matches the leading, set use_leading so the next text string 224 * will be written with ' rather than Tj; otherwise, write either a TL 225 * command or a Tj command using space pseudo-characters. 226 */ 227 private int 228 set_text_distance(gs_point *pdist, const gs_point *ppt, const gs_matrix *pmat) 229 { 230 double rounded; 231 232 gs_distance_transform_inverse(pmat->tx - ppt->x, pmat->ty - ppt->y, 233 pmat, pdist); 234 /* If the distance is very close to integers, round it. */ 235 if (fabs(pdist->x - (rounded = floor(pdist->x + 0.5))) < 0.0005) 236 pdist->x = rounded; 237 if (fabs(pdist->y - (rounded = floor(pdist->y + 0.5))) < 0.0005) 238 pdist->y = rounded; 239 return 0; 240 } 241 int 242 pdf_set_text_matrix(gx_device_pdf * pdev, const gs_matrix * pmat) 243 { 244 stream *s = pdev->strm; 245 double sx = 72.0 / pdev->HWResolution[0], 246 sy = 72.0 / pdev->HWResolution[1]; 247 int code; 248 249 if (pmat->xx == pdev->text.matrix.xx && 250 pmat->xy == pdev->text.matrix.xy && 251 pmat->yx == pdev->text.matrix.yx && 252 pmat->yy == pdev->text.matrix.yy && 253 /* 254 * If we aren't already in text context, BT will reset 255 * the text matrix. 256 */ 257 (pdev->context == PDF_IN_TEXT || pdev->context == PDF_IN_STRING) 258 ) { 259 /* Use leading, Td or a pseudo-character. */ 260 gs_point dist; 261 262 set_text_distance(&dist, &pdev->text.current, pmat); 263 if (dist.y == 0 && dist.x >= X_SPACE_MIN && 264 dist.x <= X_SPACE_MAX && 265 pdev->text.font != 0 && 266 PDF_FONT_IS_SYNTHESIZED(pdev->text.font) 267 ) { /* Use a pseudo-character. */ 268 int dx = (int)dist.x; 269 int dx_i = dx - X_SPACE_MIN; 270 byte space_char = pdev->text.font->spaces[dx_i]; 271 272 if (space_char == 0) { 273 if (pdev->text.font != pdev->open_font) 274 goto not_spaces; 275 code = assign_char_code(pdev); 276 if (code <= 0) 277 goto not_spaces; 278 space_char = pdev->open_font->spaces[dx_i] = (byte)code; 279 if (pdev->space_char_ids[dx_i] == 0) { 280 /* Create the space char_proc now. */ 281 char spstr[3 + 14 + 1]; 282 stream *s; 283 284 sprintf(spstr, "%d 0 0 0 0 0 d1\n", dx); 285 pdev->space_char_ids[dx_i] = pdf_begin_separate(pdev); 286 s = pdev->strm; 287 pprintd1(s, "<</Length %d>>\nstream\n", strlen(spstr)); 288 pprints1(s, "%sendstream\n", spstr); 289 pdf_end_separate(pdev); 290 } 291 } 292 pdf_append_chars(pdev, &space_char, 1); 293 pdev->text.current.x += dx * pmat->xx; 294 pdev->text.current.y += dx * pmat->xy; 295 /* Don't change use_leading -- it only affects Y placement. */ 296 return 0; 297 } 298 not_spaces: 299 code = pdf_open_page(pdev, PDF_IN_TEXT); 300 if (code < 0) 301 return code; 302 set_text_distance(&dist, &pdev->text.line_start, pmat); 303 if (pdev->text.use_leading) { 304 /* Leading was deferred: take it into account now. */ 305 dist.y -= pdev->text.leading; 306 } 307 if (dist.x == 0 && dist.y < 0) { 308 /* Use TL, if needed, + '. */ 309 float dist_y = (float)-dist.y; 310 311 if (fabs(pdev->text.leading - dist_y) > 0.0005) { 312 pprintg1(s, "%g TL\n", dist_y); 313 pdev->text.leading = dist_y; 314 } 315 pdev->text.use_leading = true; 316 } else { 317 /* Use Td. */ 318 pprintg2(s, "%g %g Td\n", dist.x, dist.y); 319 pdev->text.use_leading = false; 320 } 321 } else { /* Use Tm. */ 322 code = pdf_open_page(pdev, PDF_IN_TEXT); 323 if (code < 0) 324 return code; 325 /* 326 * See stream_to_text in gdevpdf.c for why we need the following 327 * matrix adjustments. 328 */ 329 pprintg6(pdev->strm, "%g %g %g %g %g %g Tm\n", 330 pmat->xx * sx, pmat->xy * sy, 331 pmat->yx * sx, pmat->yy * sy, 332 pmat->tx * sx, pmat->ty * sy); 333 pdev->text.matrix = *pmat; 334 pdev->text.use_leading = false; 335 } 336 pdev->text.line_start.x = pmat->tx; 337 pdev->text.line_start.y = pmat->ty; 338 pdev->text.current.x = pmat->tx; 339 pdev->text.current.y = pmat->ty; 340 return 0; 341 } 342 343 /* 344 * Append characters to a string being accumulated. 345 */ 346 int 347 pdf_append_chars(gx_device_pdf * pdev, const byte * str, uint size) 348 { 349 const byte *p = str; 350 uint left = size; 351 352 while (left) 353 if (pdev->text.buffer_count == max_text_buffer) { 354 int code = pdf_open_page(pdev, PDF_IN_TEXT); 355 356 if (code < 0) 357 return code; 358 } else { 359 int code = pdf_open_page(pdev, PDF_IN_STRING); 360 uint copy; 361 362 if (code < 0) 363 return code; 364 copy = min(max_text_buffer - pdev->text.buffer_count, left); 365 memcpy(pdev->text.buffer + pdev->text.buffer_count, p, copy); 366 pdev->text.buffer_count += copy; 367 p += copy; 368 left -= copy; 369 } 370 return 0; 371 } 372 373 /* ---------------- Synthesized fonts ---------------- */ 374 375 /* Assign a code for a char_proc. */ 376 private int 377 assign_char_code(gx_device_pdf * pdev) 378 { 379 pdf_font_t *font = pdev->open_font; 380 int c; 381 382 if (pdev->embedded_encoding_id == 0) 383 pdev->embedded_encoding_id = pdf_obj_ref(pdev); 384 if (font == 0 || font->num_chars == 256 || !pdev->use_open_font) { 385 /* Start a new synthesized font. */ 386 int code = pdf_alloc_font(pdev, gs_no_id, &font, NULL, NULL); 387 char *pc; 388 389 if (code < 0) 390 return code; 391 if (pdev->open_font == 0) 392 font->rname[0] = 0; 393 else 394 strcpy(font->rname, pdev->open_font->rname); 395 for (pc = font->rname; *pc == 'Z'; ++pc) 396 *pc = '@'; 397 if ((*pc)++ == 0) 398 *pc = 'A', pc[1] = 0; 399 pdev->open_font = font; 400 pdev->use_open_font = true; 401 } 402 c = font->num_chars++; 403 if (c > pdev->max_embedded_code) 404 pdev->max_embedded_code = c; 405 return c; 406 } 407 408 /* Begin a CharProc for a synthesized (bitmap) font. */ 409 int 410 pdf_begin_char_proc(gx_device_pdf * pdev, int w, int h, int x_width, 411 int y_offset, gs_id id, pdf_char_proc_t ** ppcp, pdf_stream_position_t * ppos) 412 { 413 pdf_resource_t *pres; 414 pdf_char_proc_t *pcp; 415 int char_code = assign_char_code(pdev); 416 pdf_font_t *font = pdev->open_font; 417 int code; 418 419 if (char_code < 0) 420 return char_code; 421 code = pdf_begin_resource(pdev, resourceCharProc, id, &pres); 422 if (code < 0) 423 return code; 424 pcp = (pdf_char_proc_t *) pres; 425 pcp->font = font; 426 pcp->char_next = font->char_procs; 427 font->char_procs = pcp; 428 pcp->char_code = char_code; 429 pcp->width = w; 430 pcp->height = h; 431 pcp->x_width = x_width; 432 pcp->y_offset = y_offset; 433 font->max_y_offset = max(font->max_y_offset, h + (h >> 2)); 434 *ppcp = pcp; 435 { 436 stream *s = pdev->strm; 437 438 /* 439 * The resource file is positionable, so rather than use an 440 * object reference for the length, we'll go back and fill it in 441 * at the end of the definition. Take 1M as the longest 442 * definition we can handle. (This used to be 10K, but there was 443 * a real file that exceeded this limit.) 444 */ 445 stream_puts(s, "<</Length >>stream\n"); 446 ppos->start_pos = stell(s); 447 } 448 return 0; 449 } 450 451 /* End a CharProc. */ 452 int 453 pdf_end_char_proc(gx_device_pdf * pdev, pdf_stream_position_t * ppos) 454 { 455 stream *s = pdev->strm; 456 long start_pos = ppos->start_pos; 457 long end_pos = stell(s); 458 long length = end_pos - start_pos; 459 460 if (length > 999999) 461 return_error(gs_error_limitcheck); 462 sseek(s, start_pos - 15); 463 pprintd1(s, "%d", length); 464 sseek(s, end_pos); 465 stream_puts(s, "endstream\n"); 466 pdf_end_separate(pdev); 467 return 0; 468 } 469 470 /* Put out a reference to an image as a character in a synthesized font. */ 471 int 472 pdf_do_char_image(gx_device_pdf * pdev, const pdf_char_proc_t * pcp, 473 const gs_matrix * pimat) 474 { 475 pdf_set_font_and_size(pdev, pcp->font, 1.0); 476 { 477 gs_matrix tmat; 478 479 tmat = *pimat; 480 tmat.ty -= pcp->y_offset; 481 pdf_set_text_matrix(pdev, &tmat); 482 } 483 pdf_append_chars(pdev, &pcp->char_code, 1); 484 pdev->text.current.x += pcp->x_width * pdev->text.matrix.xx; 485 return 0; 486 } 487