1/*---------------------------------------------------------------------------*
2 |              PDFlib - A library for generating PDF on the fly             |
3 +---------------------------------------------------------------------------+
4 | Copyright (c) 1997-2004 Thomas Merz and PDFlib GmbH. All rights reserved. |
5 +---------------------------------------------------------------------------+
6 |                                                                           |
7 |    This software is subject to the PDFlib license. It is NOT in the       |
8 |    public domain. Extended versions and commercial licenses are           |
9 |    available, please check http://www.pdflib.com.                         |
10 |                                                                           |
11 *---------------------------------------------------------------------------*/
12
13/* $Id: p_text.c 14574 2005-10-29 16:27:43Z bonefish $
14 *
15 * PDFlib text routines
16 *
17 */
18
19#define P_TEXT_C
20
21#include "p_intern.h"
22#include "p_font.h"
23
24/* ------------------------ Text object operators ------------------------ */
25
26static void
27pdf_begin_text(PDF *p)
28{
29    if (p->contents != c_text)
30    {
31	p->contents = c_text;
32	p->textparams_done = pdc_false;
33
34	pdc_puts(p->out, "BT\n");
35
36	/* BT resets the current point, text matrix, and line matrix */
37	p->gstate[p->sl].x = (float) 0.0;
38	p->gstate[p->sl].y = (float) 0.0;
39    }
40}
41
42static void
43pdf_put_textmatrix(PDF *p)
44{
45    pdc_matrix *m = &p->tstate[p->sl].m;
46
47    pdc_printf(p->out, "%f %f %f %f %f %f Tm\n",
48                       m->a, m->b, m->c, m->d, m->e, m->f);
49    p->tstate[p->sl].me = p->tstate[p->sl].m.e;
50    p->tstate[p->sl].potm = pdc_false;
51}
52
53void
54pdf_end_text(PDF *p)
55{
56    if (p->contents != c_text)
57	return;
58
59    p->contents	= c_page;
60
61    pdc_puts(p->out, "ET\n");
62    p->tstate[p->sl].potm = pdc_true;
63}
64
65/* ------------------------ Text state operators ------------------------ */
66
67/* Initialize the text state at the beginning of each page */
68void
69pdf_init_tstate(PDF *p)
70{
71    pdf_tstate *ts;
72
73    ts = &p->tstate[p->sl];
74
75    ts->c	= (float) 0;
76    ts->w	= (float) 0;
77    ts->h       = (float) 1;
78    ts->l	= (float) 0;
79    ts->f	= -1;
80    ts->fs	= (float) 0;
81
82    ts->m.a	= (float) 1;
83    ts->m.b	= (float) 0;
84    ts->m.c	= (float) 0;
85    ts->m.d	= (float) 1;
86    ts->m.e	= (float) 0;
87    ts->m.f     = (float) 0;
88    ts->me      = (float) 0;
89
90    ts->mode	= 0;
91    ts->rise	= (float) 0;
92
93    ts->lm.a	= (float) 1;
94    ts->lm.b	= (float) 0;
95    ts->lm.c	= (float) 0;
96    ts->lm.d	= (float) 1;
97    ts->lm.e	= (float) 0;
98    ts->lm.f    = (float) 0;
99
100    ts->potm    = pdc_false;
101
102    p->underline	= pdc_false;
103    p->overline		= pdc_false;
104    p->strikeout	= pdc_false;
105    p->textparams_done	= pdc_false;
106}
107
108/* reset the text state */
109void
110pdf_reset_tstate(PDF *p)
111{
112    float ydirection = p->ydirection;
113    p->ydirection = (float) 1.0;
114
115    pdf_set_horiz_scaling(p, 100);
116    pdf_set_leading(p, 0);
117    pdf_set_text_rise(p, 0);
118    pdf_end_text(p);
119
120    p->ydirection = ydirection;
121}
122
123/* character spacing for justified lines */
124void
125pdf_set_char_spacing(PDF *p, float spacing)
126{
127    spacing *= p->ydirection;
128    if (PDF_GET_STATE(p) == pdf_state_document)
129    {
130	p->tstate[p->sl].c = spacing;
131	return;
132    }
133
134    /*
135     * We take care of spacing values != 0 in the text output functions,
136     * but must explicitly reset here.
137     */
138    if (spacing == (float) 0) {
139	pdf_begin_text(p);
140	pdc_printf(p->out, "%f Tc\n", spacing);
141    }
142
143    p->tstate[p->sl].c = spacing;
144    p->textparams_done = pdc_false;
145}
146
147/* word spacing for justified lines */
148void
149pdf_set_word_spacing(PDF *p, float spacing)
150{
151    spacing *= p->ydirection;
152    if (PDF_GET_STATE(p) == pdf_state_document)
153    {
154	p->tstate[p->sl].w = spacing;
155	return;
156    }
157
158    /*
159     * We take care of spacing values != 0 in the text output functions,
160     * but must explicitly reset here.
161     */
162    if (spacing == (float) 0) {
163	pdf_begin_text(p);
164	pdc_printf(p->out, "%f Tw\n", spacing);
165    }
166
167    p->tstate[p->sl].w = spacing;
168    p->textparams_done = pdc_false;
169}
170
171void
172pdf_set_horiz_scaling(PDF *p, float scale)
173{
174    if (scale == (float) 0.0)
175	pdc_error(p->pdc, PDC_E_ILLARG_FLOAT,
176	    "horizscaling", pdc_errprintf(p->pdc, "%f", scale), 0, 0);
177
178    scale *= p->ydirection;
179    if (PDF_GET_STATE(p) == pdf_state_document)
180    {
181	p->tstate[p->sl].h = scale / (float) 100.0;
182	return;
183    }
184
185    if (scale == 100 * p->tstate[p->sl].h && !PDF_FORCE_OUTPUT())
186	return;
187
188    pdf_begin_text(p);
189    pdc_printf(p->out, "%f Tz\n", scale);
190
191    p->tstate[p->sl].h = scale / (float) 100.0;
192}
193
194float
195pdf_get_horiz_scaling(PDF *p)
196{
197   return (float) (p->ydirection * p->tstate[p->sl].h * 100);
198}
199
200void
201pdf_set_leading(PDF *p, float l)
202{
203    l *= p->ydirection;
204    if (l == p->tstate[p->sl].l && !PDF_FORCE_OUTPUT())
205	return;
206
207    p->tstate[p->sl].l = l;
208    p->textparams_done = pdc_false;
209}
210
211void
212pdf_set_text_rendering(PDF *p, int mode)
213{
214    if (mode < 0 || mode > PDF_LAST_TRMODE)
215	pdc_error(p->pdc, PDC_E_ILLARG_INT,
216	    "textrendering", pdc_errprintf(p->pdc, "%d", mode), 0, 0);
217
218    if (mode == p->tstate[p->sl].mode && !PDF_FORCE_OUTPUT())
219	return;
220
221    pdf_begin_text(p);
222    pdc_printf(p->out, "%d Tr\n", mode);
223
224    p->tstate[p->sl].mode = mode;
225}
226
227void
228pdf_set_text_rise(PDF *p, float rise)
229{
230    rise *= p->ydirection;
231    if (PDF_GET_STATE(p) == pdf_state_document)
232    {
233	p->tstate[p->sl].rise = rise;
234	return;
235    }
236
237    if (rise == p->tstate[p->sl].rise && !PDF_FORCE_OUTPUT())
238	return;
239
240    pdf_begin_text(p);
241    pdc_printf(p->out, "%f Ts\n", rise);
242
243    p->tstate[p->sl].rise = rise;
244}
245
246/* Text positioning operators */
247
248PDFLIB_API void PDFLIB_CALL
249PDF_set_text_matrix(PDF *p,
250    float a, float b, float c, float d, float e, float f)
251{
252    static const char fn[] = "PDF_set_text_matrix";
253
254    if (!pdf_enter_api(p, fn,
255        (pdf_state) (pdf_state_content | pdf_state_document),
256	"(p[%p], %g, %g, %g, %g, %g, %g)\n", (void *) p, a, b, c, d, e, f))
257    {
258	return;
259    }
260
261    p->tstate[p->sl].m.a = a;
262    p->tstate[p->sl].m.b = b;
263    p->tstate[p->sl].m.c = c;
264    p->tstate[p->sl].m.d = d;
265    p->tstate[p->sl].m.e = e;
266    p->tstate[p->sl].m.f = f;
267
268    if (PDF_GET_STATE(p) != pdf_state_document)
269    {
270	pdf_begin_text(p);
271        pdf_put_textmatrix(p);
272    }
273}
274
275PDFLIB_API void PDFLIB_CALL
276PDF_set_text_pos(PDF *p, float x, float y)
277{
278    static const char fn[] = "PDF_set_text_pos";
279
280    if (!pdf_enter_api(p, fn, pdf_state_content, "(p[%p], %g, %g)\n",
281	(void *) p, x, y))
282	return;
283
284    p->tstate[p->sl].m.e = x;
285    p->tstate[p->sl].m.f = y;
286
287    p->tstate[p->sl].lm.e = x;
288    p->tstate[p->sl].lm.f = y;
289
290    pdf_begin_text(p);
291    pdf_put_textmatrix(p);
292}
293
294/* String width calculations */
295
296static float
297pdf_str_width(PDF *p, pdc_byte *text, int len, int charlen,
298              int font, float fontsize)
299{
300    int i;
301    pdc_font *currfont = &p->fonts[font];
302    pdc_ushort uv;
303    pdc_ushort *ustext = (pdc_ushort *) text;
304    float chwidth = (float) 0.0, width = (float) 0.0;
305    pdc_bool haswidths = (currfont->widths != NULL);
306
307    /* We cannot handle empty strings and fonts with unknown
308     * code size - especially CID fonts with no UCS-2 CMap */
309    if (!len || !currfont->codeSize)
310        return width;
311
312    for (i = 0; i < len/charlen; i++)
313    {
314        if (charlen == 1)
315            uv = (pdc_ushort) text[i];
316        else
317            uv = ustext[i];
318
319        /* take word spacing parameter into account at each blank */
320        if (currfont->codeSize == 1 && uv == (pdc_ushort) PDF_SPACE)
321            width += p->tstate[p->sl].w;
322
323        /* start by adding in the width of the character */
324        if (currfont->monospace)
325        {
326            chwidth = (float) currfont->monospace;
327        }
328        else if (haswidths)
329        {
330            chwidth = (float) currfont->widths[uv];
331        }
332        width += fontsize * chwidth / ((float) 1000);
333
334
335	/* now add the character spacing parameter */
336	width += p->tstate[p->sl].c;
337    }
338
339    /* take current text matrix and horizontal scaling factor into account */
340    width = width * p->tstate[p->sl].m.a * p->tstate[p->sl].h;
341
342    return width;
343}
344
345/* Convert and check input text string.
346 * The function returns a pointer to an allocated buffer
347 * containing the converted string.
348 * The caller is responsible for freeing this buffer.
349 * If return value is NULL error was occurred.
350 */
351static pdc_byte *
352pdf_check_textstring(PDF *p, const char *text, int len, int font,
353                     pdc_bool textflow, pdc_bool calcwidth,
354                     int *outlen, int *outcharlen)
355{
356    pdc_font *currfont = &p->fonts[font];
357    pdc_byte *outtext;
358    pdc_text_format textformat, targettextformt;
359    int maxlen, charlen = 1;
360    int codesize = abs(currfont->codeSize);
361
362    (void) textflow;
363
364    /* Convert to 8-bit or UTF-16 text string */
365    textformat = p->textformat;
366    if (textformat == pdc_auto && codesize <= 1)
367        textformat = pdc_bytes;
368    else if (textformat == pdc_auto2 && codesize <= 1)
369        textformat = pdc_bytes2;
370    targettextformt = pdc_utf16;
371    pdc_convert_string(p->pdc, textformat, NULL, (pdc_byte *)text, len,
372                       &targettextformt, NULL, &outtext, outlen,
373                       PDC_CONV_NOBOM | PDC_CONV_KEEPBYTES, pdc_true);
374    textformat = targettextformt;
375
376    if (outtext && *outlen)
377    {
378        /* 2 byte storage length of a character */
379        if (textformat == pdc_utf16)
380        {
381            if (codesize <= 1 && currfont->encoding == pdc_builtin)
382                pdc_error(p->pdc, PDF_E_FONT_BADTEXTFORM, 0, 0, 0, 0);
383            charlen = 2;
384        }
385
386        /* Maximal text string length - found out emprirically! */
387        maxlen = (codesize == 1) ? PDF_MAXARRAYSIZE : PDF_MAXDICTSIZE;
388        if (!calcwidth && *outlen > maxlen)
389        {
390            pdc_warning(p->pdc, PDF_E_TEXT_TOOLONG,
391                pdc_errprintf(p->pdc, "%d", maxlen), 0, 0, 0);
392            *outlen = maxlen;
393        }
394
395    }
396    *outcharlen = charlen;
397
398    return outtext;
399}
400
401float
402pdf__stringwidth(PDF *p, const char *text, int len, int font, float fontsize)
403{
404    pdc_byte *utext;
405    int charlen;
406    float result = (float) 0.0;
407
408    if (text && len == 0)
409        len = (int) strlen(text);
410    if (text == NULL || len <= 0)
411        return result;
412
413    pdf_check_handle(p, font, pdc_fonthandle);
414
415    if (fontsize == (float) 0.0)
416        pdc_error(p->pdc, PDC_E_ILLARG_FLOAT, "fontsize", "0", 0, 0);
417
418    /* convert text string */
419    fontsize *= p->ydirection;
420    utext = pdf_check_textstring(p, text, len, font, pdc_false, pdc_true,
421                                 &len, &charlen);
422    if (utext)
423    {
424        p->currtext = utext;
425        result = pdf_str_width(p, utext, len, charlen, font, fontsize);
426        p->currtext = NULL;
427        pdc_free(p->pdc, utext);
428    }
429    return result;
430}
431
432PDFLIB_API float PDFLIB_CALL
433PDF_stringwidth(PDF *p, const char *text, int font, float fontsize)
434{
435    static const char fn[] = "PDF_stringwidth";
436    float result = (float) 0.0;
437
438    if (pdf_enter_api(p, fn,
439        (pdf_state) (pdf_state_document | pdf_state_content | pdf_state_path),
440        "(p[%p], \"%s\", %d, %g)",
441        (void *) p, pdc_strprint(p->pdc, text, 0), font, fontsize))
442    {
443        int len = text ? (int) strlen(text) : 0;
444        PDF_INPUT_HANDLE(p, font)
445        result = pdf__stringwidth(p, text, len, font, fontsize);
446    }
447    pdc_trace(p->pdc, "[%g]\n", result);
448    return result;
449}
450
451PDFLIB_API float PDFLIB_CALL
452PDF_stringwidth2(PDF *p, const char *text, int len, int font, float fontsize)
453{
454    static const char fn[] = "PDF_stringwidth2";
455    float result = (float) 0.0;
456
457    if (pdf_enter_api(p, fn,
458        (pdf_state) (pdf_state_document | pdf_state_content | pdf_state_path),
459        "(p[%p], \"%s\", %d, %d, %g)",
460        (void *) p, pdc_strprint(p->pdc, text, len), len, font, fontsize))
461    {
462        PDF_INPUT_HANDLE(p, font)
463        result = pdf__stringwidth(p, text, len, font, fontsize);
464    }
465    pdc_trace(p->pdc, "[%g]\n", result);
466    return result;
467}
468
469
470/* ------------------------ Text control functions ------------------------ */
471
472static void
473pdf_underline(PDF *p, float x, float y, float length)
474{
475    float delta_y, xscale, yscale;
476    double linewidth;
477    pdf_tstate *ts;
478
479    ts = &p->tstate[p->sl];
480
481    xscale = (float) fabs((double) ts->m.a);
482    yscale = (float) fabs((double) ts->m.d);
483
484    linewidth = fabs(ts->fs * p->fonts[ts->f].underlineThickness / 1000.0 *
485                     ts->h * (xscale > yscale ?  xscale : yscale));
486
487    delta_y = (ts->fs * p->fonts[ts->f].underlinePosition / 1000 + ts->rise) *
488            (float) fabs((double) ts->h) * (xscale > yscale ?  xscale : yscale);
489
490    pdf__save(p);
491
492    pdf__setlinewidth(p, (float) linewidth);
493    pdf__setlinecap(p, 0);
494    pdf__setdash(p, 0, 0);
495    pdf__moveto(p, x,          y + delta_y);
496    pdf__lineto(p, x + length, y + delta_y);
497    pdf__stroke(p);
498
499    pdf__restore(p);
500}
501
502static void
503pdf_overline(PDF *p, float x, float y, float length)
504{
505    float delta_y, xscale, yscale, lineheight;
506    double linewidth;
507    pdf_tstate *ts;
508
509    ts = &p->tstate[p->sl];
510
511    xscale = (float) fabs((double) ts->m.a);
512    yscale = (float) fabs((double) ts->m.d);
513
514    linewidth = fabs(ts->fs * p->fonts[ts->f].underlineThickness / 1000.0 *
515                     ts->h * (xscale > yscale ?  xscale : yscale));
516
517    lineheight = ((float) p->fonts[ts->f].ascender/1000) * ts->fs;
518
519    delta_y = (ts->fs * p->fonts[ts->f].underlinePosition / 1000 - ts->rise) *
520	    (float) fabs((double) ts->h) * (xscale > yscale ?  xscale : yscale);
521
522    pdf__save(p);
523
524    pdf__setlinewidth(p, (float)linewidth);
525    pdf__setlinecap(p, 0);
526    pdf__setdash(p, 0, 0);
527    pdf__moveto(p, x,          y + lineheight - delta_y);
528    pdf__lineto(p, x + length, y + lineheight - delta_y);
529    pdf__stroke(p);
530
531    pdf__restore(p);
532}
533
534static void
535pdf_strikeout(PDF *p, float x, float y, float length)
536{
537    float delta_y, xscale, yscale, lineheight;
538    double linewidth;
539    pdf_tstate *ts;
540
541    ts = &p->tstate[p->sl];
542
543    xscale = (float) fabs((double) ts->m.a);
544    yscale = (float) fabs((double) ts->m.d);
545
546    linewidth = fabs(ts->fs * p->fonts[ts->f].underlineThickness / 1000.0 *
547                     ts->h * (xscale > yscale ?  xscale : yscale));
548
549    lineheight = ((float) p->fonts[ts->f].ascender/1000) * ts->fs;
550
551    delta_y = (ts->fs * p->fonts[ts->f].underlinePosition / 1000 + ts->rise) *
552	    (float) fabs((double) ts->h) * (xscale > yscale ?  xscale : yscale);
553    pdf__save(p);
554
555    pdf__setlinewidth(p, (float)linewidth);
556    pdf__setlinecap(p, 0);
557    pdf__setdash(p, 0, 0);
558    pdf__moveto(p, x,          y + lineheight/2 + delta_y);
559    pdf__lineto(p, x + length, y + lineheight/2 + delta_y);
560    pdf__stroke(p);
561
562    pdf__restore(p);
563}
564
565/* ------------------------ font operator ------------------------ */
566
567void
568pdf__setfont(PDF *p, int font, float fontsize)
569{
570    /* make font the current font */
571    p->fonts[font].used_on_current_page = pdc_true;
572    p->tstate[p->sl].fs = p->ydirection * fontsize;
573    p->tstate[p->sl].f = font;
574
575    pdf_begin_text(p);
576    pdc_printf(p->out, "/F%d %f Tf\n", font, p->tstate[p->sl].fs);
577    pdf_set_leading(p, fontsize);
578}
579
580PDFLIB_API void PDFLIB_CALL
581PDF_setfont(PDF *p, int font, float fontsize)
582{
583    static const char fn[] = "PDF_setfont";
584
585    if (!pdf_enter_api(p, fn, pdf_state_content, "(p[%p], %d, %g)\n",
586        (void *) p, font, fontsize))
587        return;
588
589    /* Check parameters */
590    PDF_INPUT_HANDLE(p, font)
591    pdf_check_handle(p, font, pdc_fonthandle);
592
593    if (fontsize == (float) 0.0)
594        pdc_error(p->pdc, PDC_E_ILLARG_FLOAT, "fontsize", "0", 0, 0);
595
596    pdf__setfont(p, font, fontsize);
597}
598
599float
600pdf_get_fontsize(PDF *p)
601{
602    if (p->fonts_number == 0 || p->tstate[p->sl].f == -1) /* no font set */
603	pdc_error(p->pdc, PDF_E_TEXT_NOFONT_PAR, "fontsize", 0, 0, 0);
604
605
606    return p->ydirection * p->tstate[p->sl].fs;
607}
608
609/* ------------------------ Text rendering operators ------------------------ */
610
611
612void
613pdf_put_textstring(PDF *p, pdc_byte *text, int len, int charlen)
614{
615    (void) charlen;
616
617    pdc_put_pdfstring(p->out, (const char *) text, len);
618}
619
620
621static void
622pdf_output_text(PDF *p, pdc_byte *text, int len, int charlen)
623{
624    {
625        pdf_put_textstring(p, text, len, charlen);
626        pdc_puts(p->out, "Tj\n");
627    }
628}
629
630#define PDF_RENDERMODE_FILLCLIP 4
631
632static void
633pdf_place_text(PDF *p, pdc_byte *utext, int len, int charlen,
634               float x, float y, float width, pdc_bool cont)
635{
636    pdf_tstate *ts = &p->tstate[p->sl];
637
638    if (width)
639    {
640        if (p->underline)
641            pdf_underline(p, x, y, width);
642        if (p->overline)
643            pdf_overline(p, x, y, width);
644        if (p->strikeout)
645            pdf_strikeout(p, x, y, width);
646    }
647
648    pdf_begin_text(p);
649    if (ts->potm)
650        pdf_put_textmatrix(p);
651
652    /* set leading, word, and character spacing if required */
653    if (!p->textparams_done)
654    {
655        pdc_printf(p->out, "%f TL\n", ts->l);
656
657        if (ts->w != (float) 0)
658            pdc_printf(p->out,"%f Tw\n", ts->w);
659
660        if (ts->c != (float) 0)
661            pdc_printf(p->out,"%f Tc\n", ts->c);
662
663        p->textparams_done = pdc_true;
664    }
665
666    /* text output */
667    if (!cont)
668    {
669        pdf_output_text(p, utext, len, charlen);
670    }
671    else
672    {
673        {
674            pdf_put_textstring(p, utext, len, charlen);
675            pdc_puts(p->out, "'\n");
676        }
677    }
678
679    if (ts->mode >= PDF_RENDERMODE_FILLCLIP)
680        pdf_end_text(p);
681
682    ts->m.e += width;
683    if (cont) ts->m.f -= ts->l;
684}
685
686void
687pdf_show_text(
688    PDF *p,
689    const char *text,
690    int len,
691    const float *x_p,
692    const float *y_p,
693    pdc_bool cont)
694{
695    static const char *fn = "pdf_show_text";
696    pdf_tstate *ts = &p->tstate[p->sl];
697    pdc_byte *utext = NULL;
698    int charlen = 1;
699    float x, y, width;
700
701    if (x_p != NULL)
702    {
703        x = *x_p;
704        y = *y_p;
705        ts->m.e = x;
706        ts->m.f = y;
707        ts->lm.e = x;
708        ts->lm.f = y;
709        ts->potm = pdc_true;
710    }
711    else if (cont)
712    {
713        ts->m.e = ts->lm.e;
714        x = ts->m.e;
715        y = ts->m.f - ts->l;
716
717        /* we must output line begin if necessary */
718        if (fabs((double) (ts->me - ts->lm.e)) > PDC_FLOAT_PREC)
719            ts->potm = pdc_true;
720    }
721    else
722    {
723        x = ts->m.e;
724        y = ts->m.f;
725    }
726
727    if (text && len == 0)
728        len = (int) strlen(text);
729    if (text == NULL || len <= 0)
730    {
731        if (cont)
732            len = 0;
733        else
734            return;
735    }
736
737    /* no font set */
738    if (ts->f == -1)
739        pdc_error(p->pdc, PDF_E_TEXT_NOFONT, 0, 0, 0, 0);
740
741    width = 0;
742    if (len)
743    {
744        /* convert text string */
745        utext = pdf_check_textstring(p, text, len, ts->f, pdc_false, pdc_false,
746                                     &len, &charlen);
747        if (!utext)
748            return;
749
750        p->currtext = utext;
751
752        /* length of text */
753        width = pdf_str_width(p, utext, len, charlen, ts->f, ts->fs);
754    }
755    else
756    {
757        utext = (pdc_byte *) pdc_calloc(p->pdc, 2, fn);
758        p->currtext = utext;
759    }
760
761    /* place text */
762    pdf_place_text(p, utext, len, charlen, x, y, width, cont);
763
764    p->currtext = NULL;
765    if (utext)
766        pdc_free(p->pdc, utext);
767}
768
769PDFLIB_API void PDFLIB_CALL
770PDF_show(PDF *p, const char *text)
771{
772    static const char fn[] = "PDF_show";
773    if (pdf_enter_api(p, fn, pdf_state_content, "(p[%p], \"%s\")\n",
774        (void *) p, pdc_strprint(p->pdc, text, 0)))
775    {
776        int len = text ? (int) strlen(text) : 0;
777        pdf_show_text(p, text, len, NULL, NULL, pdc_false);
778    }
779}
780
781PDFLIB_API void PDFLIB_CALL
782PDF_show2(PDF *p, const char *text, int len)
783{
784    static const char fn[] = "PDF_show2";
785    if (pdf_enter_api(p, fn, pdf_state_content,
786	"(p[%p], \"%s\", %d)\n",
787        (void *) p, pdc_strprint(p->pdc, text, len), len))
788    {
789        pdf_show_text(p, text, len, NULL, NULL, pdc_false);
790    }
791}
792
793PDFLIB_API void PDFLIB_CALL
794PDF_show_xy(PDF *p, const char *text, float x, float y)
795{
796    static const char fn[] = "PDF_show_xy";
797    if (pdf_enter_api(p, fn, pdf_state_content, "(p[%p], \"%s\", %g, %g)\n",
798        (void *) p, pdc_strprint(p->pdc, text, 0), x, y))
799    {
800        int len = text ? (int) strlen(text) : 0;
801        pdf_show_text(p, text, len, &x, &y, pdc_false);
802    }
803}
804
805PDFLIB_API void PDFLIB_CALL
806PDF_show_xy2(PDF *p, const char *text, int len, float x, float y)
807{
808    static const char fn[] = "PDF_show_xy2";
809    if (pdf_enter_api(p, fn, pdf_state_content, "(p[%p], \"%s\", %d, %g, %g)\n",
810        (void *) p, pdc_strprint(p->pdc, text, len), len, x, y))
811    {
812        pdf_show_text(p, text, len, &x, &y, pdc_false);
813    }
814}
815
816PDFLIB_API void PDFLIB_CALL
817PDF_continue_text(PDF *p, const char *text)
818{
819    static const char fn[] = "PDF_continue_text";
820    if (pdf_enter_api(p, fn, pdf_state_content, "(p[%p], \"%s\")\n",
821        (void *) p, pdc_strprint(p->pdc, text, 0)))
822    {
823        int len = text ? (int) strlen(text) : 0;
824        pdf_show_text(p, text, len, NULL, NULL, pdc_true);
825    }
826}
827
828PDFLIB_API void PDFLIB_CALL
829PDF_continue_text2(PDF *p, const char *text, int len)
830{
831    static const char fn[] = "PDF_continue_text2";
832    if (pdf_enter_api(p, fn, pdf_state_content,
833        "(p[%p], \"%s\", %d)\n",
834        (void *) p, pdc_strprint(p->pdc, text, len), len))
835    {
836        pdf_show_text(p, text, len, NULL, NULL, pdc_true);
837    }
838}
839
840
841/* ----------------------- Text fitting routines ------------------------ */
842
843#define PDF_KERNING_FLAG PDC_OPT_UNSUPP
844
845/* definitions of fit text options */
846static const pdc_defopt pdf_fit_textline_options[] =
847{
848    {"font", pdc_fonthandle, PDC_OPT_NONE, 1, 1,
849      0, 0, NULL},
850
851    {"fontsize", pdc_floatlist, PDC_OPT_REQUIRIF1 | PDC_OPT_NOZERO, 1, 1,
852      PDC_FLOAT_MIN, PDC_FLOAT_MAX, NULL},
853
854    {"textrendering", pdc_integerlist, PDC_OPT_NONE, 1, 1,
855      0, PDF_LAST_TRMODE, NULL},
856
857    {"wordspacing", pdc_floatlist, PDC_OPT_NONE, 1, 1,
858      PDC_FLOAT_MIN, PDC_FLOAT_MAX, NULL},
859
860    {"charspacing", pdc_floatlist, PDC_OPT_NONE, 1, 1,
861      PDC_FLOAT_MIN, PDC_FLOAT_MAX, NULL},
862
863    {"horizscaling", pdc_floatlist,  PDC_OPT_NOZERO, 1, 1,
864      PDC_FLOAT_MIN, PDC_FLOAT_MAX, NULL},
865
866    {"textrise", pdc_floatlist, PDC_OPT_NONE, 1, 1,
867      PDC_FLOAT_MIN, PDC_FLOAT_MAX, NULL},
868
869    {"underline", pdc_booleanlist, PDC_OPT_NONE, 1, 1,
870      0.0, 0.0, NULL},
871
872    {"overline", pdc_booleanlist, PDC_OPT_NONE, 1, 1,
873      0.0, 0.0, NULL},
874
875    {"strikeout", pdc_booleanlist, PDC_OPT_NONE, 1, 1,
876      0.0, 0.0, NULL},
877
878    {"kerning", pdc_booleanlist, PDF_KERNING_FLAG, 1, 1,
879      0.0, 0.0, NULL},
880
881    {"textformat", pdc_keywordlist, PDC_OPT_NONE, 1, 1,
882      0.0, 0.0, pdf_textformat_keylist},
883
884    {"margin", pdc_floatlist, PDC_OPT_NONE, 1, 2,
885      PDC_FLOAT_MIN, PDC_FLOAT_MAX, NULL},
886
887    {"orientate", pdc_keywordlist, PDC_OPT_NONE, 1, 1,
888      0.0, 0.0, pdf_orientate_keylist},
889
890    {"boxsize", pdc_floatlist, PDC_OPT_NONE, 2, 2,
891      PDC_FLOAT_MIN, PDC_FLOAT_MAX, NULL},
892
893    {"rotate", pdc_floatlist, PDC_OPT_NONE, 1, 1,
894      PDC_FLOAT_MIN, PDC_FLOAT_MAX, NULL},
895
896    {"position", pdc_floatlist, PDC_OPT_NONE, 1, 2,
897      PDC_FLOAT_MIN, PDC_FLOAT_MAX, NULL},
898
899    {"fitmethod", pdc_keywordlist, PDC_OPT_NONE, 1, 1,
900      0.0, 0.0, pdf_fitmethod_keylist},
901
902    {"distortionlimit", pdc_floatlist, PDC_OPT_NONE, 1, 1,
903      0.0, 100.0, NULL},
904
905    PDC_OPT_TERMINATE
906};
907
908void
909pdf__fit_textline(PDF *p, const char *text, int len, float x, float y,
910                  const char *optlist)
911{
912    pdc_byte *utext = (pdc_byte *) "";
913    pdc_resopt *results;
914    pdc_clientdata data;
915    pdc_text_format textformat_save = p->textformat;
916    pdc_bool underline_save = p->underline;
917    pdc_bool overline_save = p->overline;
918    pdc_bool strikeout_save = p->strikeout;
919    int charlen;
920
921    int font;
922    float fontsize;
923    int textrendering, ntextrendering;
924    float textrise, wordspacing, charspacing, horizscaling;
925    int nfontsize, ntextrise, nwordspacing, ncharspacing, nhorizscaling;
926    float margin[2], boxsize[2], position[2], angle, distortionlimit;
927    pdc_fitmethod method;
928    int inum, orientangle, indangle;
929
930    if (text && len == 0)
931        len = (int) strlen(text);
932    if (text == NULL || len <= 0)
933        return;
934
935    /* defaults */
936    font = p->tstate[p->sl].f;
937    fontsize = p->tstate[p->sl].fs;
938    orientangle = 0;
939    margin[0] = margin[1] = (float) 0.0;
940    boxsize[0] = boxsize[1] = (float) 0.0;
941    position[0] = position[1] = (float) 0.0;
942    angle = (float) 0.0;
943    method = pdc_nofit;
944    distortionlimit = (float) 75.0;
945
946    /* parsing optlist */
947    data.compatibility = p->compatibility;
948    data.maxfont = p->fonts_number - 1;
949    data.hastobepos = p->hastobepos;
950    results = pdc_parse_optionlist(p->pdc, optlist, pdf_fit_textline_options,
951                                   &data, pdc_true);
952
953    /* save options */
954    pdc_get_optvalues(p->pdc, "font", results, &font, NULL);
955
956    nfontsize = pdc_get_optvalues(p->pdc, "fontsize", results,
957                                  &fontsize, NULL);
958
959    ntextrendering = pdc_get_optvalues(p->pdc, "textrendering", results,
960                                       &textrendering, NULL);
961
962    nwordspacing = pdc_get_optvalues(p->pdc, "wordspacing", results,
963                                     &wordspacing, NULL);
964
965    ncharspacing = pdc_get_optvalues(p->pdc, "charspacing", results,
966                                     &charspacing, NULL);
967
968    nhorizscaling = pdc_get_optvalues(p->pdc, "horizscaling", results,
969                                      &horizscaling, NULL);
970
971    ntextrise = pdc_get_optvalues(p->pdc, "textrise", results,
972                                  &textrise, NULL);
973
974    pdc_get_optvalues(p->pdc, "underline", results, &p->underline, NULL);
975
976    pdc_get_optvalues(p->pdc, "overline", results, &p->overline, NULL);
977
978    pdc_get_optvalues(p->pdc, "strikeout", results, &p->strikeout, NULL);
979
980
981    if (pdc_get_optvalues(p->pdc, "textformat", results, &inum, NULL))
982        p->textformat = (pdc_text_format) inum;
983
984    if (1 == pdc_get_optvalues(p->pdc, "margin", results, margin, NULL))
985        margin[1] = margin[0];
986
987    pdc_get_optvalues(p->pdc, "orientate", results, &orientangle, NULL);
988
989    pdc_get_optvalues(p->pdc, "boxsize", results, boxsize, NULL);
990
991    pdc_get_optvalues(p->pdc, "rotate", results, &angle, NULL);
992
993    pdc_get_optvalues(p->pdc, "distortionlimit", results,
994                      &distortionlimit, NULL);
995
996    if (1 == pdc_get_optvalues(p->pdc, "position", results, position, NULL))
997        position[1] = position[0];
998
999    if (pdc_get_optvalues(p->pdc, "fitmethod", results, &inum, NULL))
1000        method = (pdc_fitmethod) inum;
1001
1002    pdc_cleanup_optionlist(p->pdc, results);
1003
1004    /* save graphic state */
1005    pdf__save(p);
1006
1007    while (1)
1008    {
1009        pdf_tstate *ts = &p->tstate[p->sl];
1010        pdc_matrix m;
1011        pdc_vector elemsize, elemscale, elemmargin, relpos, polyline[5];
1012        pdc_box fitbox, elembox;
1013        pdc_scalar ss, minfscale;
1014        float width, height, tx = 0, ty = 0;
1015
1016        /* font size */
1017        if (nfontsize)
1018        {
1019            pdf__setfont(p, font, fontsize);
1020            fontsize = p->tstate[p->sl].fs;
1021        }
1022
1023        /* no font set */
1024        if (p->tstate[p->sl].f == -1)
1025            pdc_error(p->pdc, PDF_E_TEXT_NOFONT, 0, 0, 0, 0);
1026
1027        /* convert text string */
1028        utext = pdf_check_textstring(p, text, len, font, pdc_false, pdc_false,
1029                                     &len, &charlen);
1030        if (utext == NULL || len == 0)
1031            break;
1032        p->currtext = utext;
1033
1034        /* text rendering */
1035        if (ntextrendering)
1036            pdf_set_text_rendering(p, textrendering);
1037
1038        /* options for text width and height */
1039        if (nwordspacing)
1040            pdf_set_word_spacing(p, wordspacing);
1041        if (ncharspacing)
1042            pdf_set_char_spacing(p, charspacing);
1043        if (nhorizscaling)
1044            pdf_set_horiz_scaling(p, horizscaling);
1045        if (ntextrise)
1046            pdf_set_text_rise(p, textrise);
1047
1048        /* minimal horizontal scaling factor */
1049        minfscale = distortionlimit / 100.0;
1050
1051        /* text width */
1052        width = pdf_str_width(p, utext, len, charlen, font, fontsize);
1053        if (width < PDF_SMALLREAL)
1054            break;
1055        elemmargin.x = margin[0];
1056        elemsize.x = width + 2 * elemmargin.x;
1057
1058        /* text height */
1059        height = (float) fabs((p->fonts[ts->f].capHeight / 1000.0f) * ts->fs);
1060        elemmargin.y = margin[1];
1061        elemsize.y = height + 2 * elemmargin.y;
1062
1063        /* orientation */
1064        indangle = orientangle / 90;
1065        if (indangle % 2)
1066        {
1067            ss = elemsize.x;
1068            elemsize.x = elemsize.y;
1069            elemsize.y = ss;
1070        }
1071
1072        /* box for fitting */
1073        fitbox.ll.x = 0;
1074        fitbox.ll.y = 0;
1075        fitbox.ur.x = boxsize[0];
1076        fitbox.ur.y = boxsize[1];
1077
1078        /* relative position */
1079        relpos.x = position[0] / 100.0;
1080        relpos.y = position[1] / 100.0;
1081
1082        /* calculate image box */
1083        pdc_place_element(method, minfscale, &fitbox, &relpos,
1084                          &elemsize, &elembox, &elemscale);
1085
1086        /* reference point */
1087        pdc_translation_matrix(x, y, &m);
1088        pdf_concat_raw(p, &m);
1089
1090        /* clipping */
1091        if (method == pdc_clip || method == pdc_slice)
1092        {
1093            pdf__rect(p, 0, 0, boxsize[0], boxsize[1]);
1094            pdf__clip(p);
1095        }
1096
1097        /* optional rotation */
1098        if (fabs((double)(angle)) > PDC_FLOAT_PREC)
1099        {
1100            pdc_rotation_matrix(p->ydirection * angle, &m);
1101            pdf_concat_raw(p, &m);
1102        }
1103
1104        /* translation of element box */
1105        elembox.ll.y *= p->ydirection;
1106        elembox.ur.y *= p->ydirection;
1107        pdc_box2polyline(&elembox, polyline);
1108        tx = (float) polyline[indangle].x;
1109        ty = (float) polyline[indangle].y;
1110        pdc_translation_matrix(tx, ty, &m);
1111        pdf_concat_raw(p, &m);
1112
1113        /* orientation of text */
1114        if (orientangle != 0)
1115        {
1116            pdc_rotation_matrix(p->ydirection * orientangle, &m);
1117            pdf_concat_raw(p, &m);
1118            if (indangle % 2)
1119            {
1120                ss = elemscale.x;
1121                elemscale.x = elemscale.y;
1122                elemscale.y = ss;
1123            }
1124        }
1125
1126        if (elemscale.x != 1 || elemscale.y != 1)
1127        {
1128            pdc_scale_matrix((float) elemscale.x, (float) elemscale.y, &m);
1129            pdf_concat_raw(p, &m);
1130        }
1131
1132        /* place text */
1133        x = (float) elemmargin.x;
1134        y = (float) elemmargin.y;
1135        p->tstate[p->sl].m.e = x;
1136        p->tstate[p->sl].m.f = y;
1137        p->tstate[p->sl].lm.e = x;
1138        p->tstate[p->sl].lm.f = y;
1139        p->tstate[p->sl].potm = pdc_true;
1140        pdf_place_text(p, utext, len, charlen, x, y, width, pdc_false);
1141
1142        break;
1143    }
1144
1145    /* restore graphic state */
1146    pdf__restore(p);
1147    p->underline = underline_save;
1148    p->overline = overline_save;
1149    p->strikeout = strikeout_save;
1150    p->textformat = textformat_save;
1151    p->currtext = NULL;
1152    if (utext)
1153        pdc_free(p->pdc, utext);
1154}
1155
1156PDFLIB_API void PDFLIB_CALL
1157PDF_fit_textline(PDF *p, const char *text, int len, float x, float y,
1158                 const char *optlist)
1159{
1160    static const char fn[] = "PDF_fit_textline";
1161
1162    if (pdf_enter_api(p, fn, pdf_state_content,
1163        "(p[%p], \"%s\", %d, %g, %g, \"%s\")\n",
1164        (void *) p, pdc_strprint(p->pdc, text, len), len, x, y, optlist))
1165    {
1166        pdf__fit_textline(p, text, len, x, y, optlist);
1167    }
1168}
1169
1170/* ----------------------- Text formatting routines ------------------------ */
1171
1172/* text alignment modes */
1173typedef enum { pdf_align_left, pdf_align_right, pdf_align_center,
1174	pdf_align_justify, pdf_align_fulljustify
1175} pdf_alignment;
1176
1177/* this helper function returns the width of the null-terminated string
1178** 'text' for the current font and size EXCLUDING the last character's
1179** additional charspacing.
1180*/
1181static float
1182pdf_swidth(PDF *p, const char *text)
1183{
1184    pdf_tstate	*ts = &p->tstate[p->sl];
1185
1186    return pdf_str_width(p, (pdc_byte *)text, (int) strlen(text), 1,
1187                         ts->f, ts->fs) - ts->c * ts->m.a * ts->h;
1188}
1189
1190static void
1191pdf_show_aligned(PDF *p, const char *text, float x, float y, pdf_alignment mode)
1192{
1193    if (!text)
1194	return;
1195
1196    switch (mode) {
1197	case pdf_align_left:
1198	case pdf_align_justify:
1199	case pdf_align_fulljustify:
1200	    /* nothing extra here... */
1201	    break;
1202
1203	case pdf_align_right:
1204	    x -= pdf_swidth(p, text);
1205	    break;
1206
1207	case pdf_align_center:
1208	    x -= pdf_swidth(p, text) / 2;
1209	    break;
1210    }
1211
1212    pdf_show_text(p, text, (int) strlen(text), &x, &y, pdc_false);
1213}
1214
1215static int
1216pdf__show_boxed(
1217    PDF *p,
1218    const char *text,
1219    int totallen,
1220    float left,
1221    float bottom,
1222    float width,
1223    float height,
1224    const char *hmode,
1225    const char *feature)
1226{
1227    float	textwidth, old_word_spacing, curx, cury;
1228    pdc_bool	prematureexit;	/* return because box is too small */
1229    int		curTextPos;	/* character currently processed */
1230    int		lastdone;	/* last input character processed */
1231    int         toconv = totallen;
1232    pdf_tstate *ts = &p->tstate[p->sl];
1233    pdc_byte *utext = NULL;
1234    pdc_text_format textformat = p->textformat;
1235    pdf_alignment mode = pdf_align_left;
1236    pdc_bool blind = pdc_false;
1237
1238    if (hmode == NULL || *hmode == '\0')
1239	pdc_error(p->pdc, PDC_E_ILLARG_EMPTY, "hmode", 0, 0, 0);
1240
1241    if (!strcmp(hmode, "left"))
1242	mode = pdf_align_left;
1243    else if (!strcmp(hmode, "right"))
1244	mode = pdf_align_right;
1245    else if (!strcmp(hmode, "center"))
1246	mode = pdf_align_center;
1247    else if (!strcmp(hmode, "justify"))
1248	mode = pdf_align_justify;
1249    else if (!strcmp(hmode, "fulljustify"))
1250	mode = pdf_align_fulljustify;
1251    else
1252	pdc_error(p->pdc, PDC_E_ILLARG_STRING, "hmode", hmode, 0, 0);
1253
1254    if (feature != NULL && *feature != '\0')
1255    {
1256	if (!strcmp(feature, "blind"))
1257	    blind = pdc_true;
1258	else
1259	    pdc_error(p->pdc, PDC_E_ILLARG_STRING, "feature", feature, 0, 0);
1260    }
1261
1262    /* no font set */
1263    if (ts->f == -1)
1264        pdc_error(p->pdc, PDF_E_TEXT_NOFONT, 0, 0, 0, 0);
1265
1266    if (width == 0 && height != 0)
1267        pdc_error(p->pdc, PDC_E_ILLARG_FLOAT,
1268            "width", pdc_errprintf(p->pdc, "%f", width), 0, 0);
1269
1270    if (width != 0 && height == 0)
1271        pdc_error(p->pdc, PDC_E_ILLARG_FLOAT,
1272            "height", pdc_errprintf(p->pdc, "%f", height), 0, 0);
1273
1274    /* we cannot handle several encodings */
1275    if (p->fonts[ts->f].encoding == pdc_unicode)
1276    {
1277        pdc_error(p->pdc, PDF_E_DOC_FUNCUNSUPP, "Unicode", 0, 0, 0);
1278    }
1279
1280    if (p->fonts[ts->f].encoding == pdc_glyphid)
1281    {
1282        pdc_error(p->pdc, PDF_E_DOC_FUNCUNSUPP, "glyphid", 0, 0, 0);
1283    }
1284
1285    if (p->fonts[ts->f].encoding == pdc_cid)
1286    {
1287	pdc_error(p->pdc, PDF_E_DOC_FUNCUNSUPP, "CID", 0, 0, 0);
1288    }
1289
1290    if (p->fonts[ts->f].encoding == pdc_ebcdic)
1291    {
1292	pdc_error(p->pdc, PDF_E_DOC_FUNCUNSUPP, "EBCDIC", 0, 0, 0);
1293    }
1294
1295    /* convert text string */
1296    if (toconv)
1297    {
1298        int charlen;
1299        utext = pdf_check_textstring(p, text, totallen, ts->f, pdc_true,
1300                                     pdc_true, &totallen, &charlen);
1301        if (!utext)
1302            return 0;
1303
1304        utext[totallen] = 0;
1305        text = (const char *) utext;
1306        p->textformat = pdc_bytes;
1307    }
1308
1309    /* text length */
1310    if (!totallen)
1311        totallen = (int) strlen(text);
1312
1313    /* special case for a single aligned line */
1314    if (width == 0 && height == 0)
1315    {
1316        if (!blind)
1317            pdf_show_aligned(p, text, left, bottom, mode);
1318
1319        if (toconv)
1320        {
1321            pdc_free(p->pdc, utext);
1322            p->textformat = textformat;
1323        }
1324        return 0;
1325    }
1326
1327    old_word_spacing = ts->w;
1328
1329    curx = left;
1330    cury = bottom + p->ydirection * height;
1331    prematureexit = pdc_false;
1332    curTextPos = 0;
1333    lastdone = 0;
1334
1335    /* switch curx for right and center justification */
1336    if (mode == pdf_align_right)
1337	curx += width;
1338    else if (mode == pdf_align_center)
1339	curx += (width / 2);
1340
1341#define	MAX_CHARS_IN_LINE	2048
1342
1343    /* loop until all characters processed, or box full */
1344
1345    while ((curTextPos < totallen) && !prematureexit) {
1346	/* buffer for constructing the line */
1347	char	linebuf[MAX_CHARS_IN_LINE];
1348	int	curCharsInLine = 0;	/* # of chars in constructed line */
1349	int	lastWordBreak = 0;	/* the last seen space char */
1350	int	wordBreakCount = 0;	/* # of blanks in this line */
1351
1352	/* loop over the input string */
1353	while (curTextPos < totallen) {
1354	    if (curCharsInLine >= MAX_CHARS_IN_LINE)
1355		pdc_error(p->pdc, PDC_E_ILLARG_TOOLONG, "(text line)",
1356		    pdc_errprintf(p->pdc, "%d", MAX_CHARS_IN_LINE-1), 0, 0);
1357
1358	    /* abandon DOS line-ends */
1359	    if (text[curTextPos] == PDF_RETURN &&
1360		text[curTextPos+1] == PDF_NEWLINE)
1361		    curTextPos++;
1362
1363	    /* if it's a forced line break draw the line */
1364	    if (text[curTextPos] == PDF_NEWLINE ||
1365		text[curTextPos] == PDF_RETURN) {
1366
1367		cury -= ts->l;			/* adjust cury by leading */
1368
1369                if (p->ydirection * (cury - bottom) < 0) {
1370		    prematureexit = pdc_true;	/* box full */
1371		    break;
1372		}
1373
1374                linebuf[curCharsInLine] = 0;    /* terminate the line */
1375
1376		/* check whether the line is too long */
1377		ts->w = (float) 0.0;
1378                textwidth = pdf_swidth(p, linebuf);
1379
1380		/* the forced break occurs too late for this line */
1381		if (textwidth > width) {
1382		    if (wordBreakCount == 0) {	/* no blank found */
1383			prematureexit = pdc_true;
1384			break;
1385		    }
1386                    linebuf[lastWordBreak] = 0;   /* terminate at last blank */
1387		    if (curTextPos > 0 && text[curTextPos-1] == PDF_RETURN)
1388			--curTextPos;
1389		    curTextPos -= (curCharsInLine - lastWordBreak);
1390
1391		    if (!blind) {
1392                        textwidth = pdf_swidth(p, linebuf);
1393			if (wordBreakCount != 1 &&
1394				(mode == pdf_align_justify ||
1395				mode == pdf_align_fulljustify)) {
1396			    pdf_set_word_spacing(p,
1397				p->ydirection * (width - textwidth) /
1398				((wordBreakCount - 1) * ts->h * ts->m.a));
1399			}
1400			else
1401			    pdf_set_word_spacing(p, (float) 0.0);
1402                        pdf_show_aligned(p, linebuf, curx, cury, mode);
1403		    }
1404
1405		} else if (!blind) {
1406
1407		    if (mode == pdf_align_fulljustify && wordBreakCount > 0) {
1408			pdf_set_word_spacing(p,
1409                            p->ydirection * (width - textwidth) /
1410			    (wordBreakCount * ts->h * ts->m.a));
1411		    } else {
1412			pdf_set_word_spacing(p, (float) 0.0);
1413		    }
1414
1415                    pdf_show_aligned(p, linebuf, curx, cury, mode);
1416		}
1417
1418		lastdone = curTextPos;
1419		curCharsInLine = lastWordBreak = wordBreakCount = 0;
1420		curTextPos++;
1421
1422	    } else if (text[curTextPos] == PDF_SPACE) {
1423                linebuf[curCharsInLine] = 0;    /* terminate the line */
1424		ts->w = (float) 0.0;
1425
1426		/* line too long ==> break at last blank */
1427                if (pdf_swidth(p, linebuf) > width) {
1428		    cury -= ts->l;		/* adjust cury by leading */
1429
1430                    if (p->ydirection * (cury - bottom) < 0) {
1431			prematureexit = pdc_true; 	/* box full */
1432			break;
1433		    }
1434
1435                    linebuf[lastWordBreak] = 0; /* terminate at last blank */
1436		    curTextPos -= (curCharsInLine - lastWordBreak - 1);
1437
1438		    if (lastWordBreak == 0)
1439			curTextPos--;
1440
1441		    /* LATER: * force break if wordBreakCount == 1,
1442		     * i.e., no blank
1443		     */
1444		    if (wordBreakCount == 0) {
1445			prematureexit = pdc_true;
1446			break;
1447		    }
1448
1449		    /* adjust word spacing for full justify */
1450		    if (wordBreakCount != 1 && (mode == pdf_align_justify ||
1451					    mode == pdf_align_fulljustify)) {
1452			ts->w = (float) 0.0;
1453                        textwidth = pdf_swidth(p, linebuf);
1454			if (!blind) {
1455			    pdf_set_word_spacing(p,
1456                                p->ydirection * (width - textwidth) /
1457				((wordBreakCount - 1) * ts->h * ts->m.a));
1458			}
1459		    }
1460		    else if (!blind) {
1461			pdf_set_word_spacing(p, (float) 0.0);
1462		    }
1463
1464
1465		    lastdone = curTextPos;
1466		    if (!blind)
1467                        pdf_show_aligned(p, linebuf, curx, cury, mode);
1468		    curCharsInLine = lastWordBreak = wordBreakCount = 0;
1469
1470		} else {
1471		    /* blank found, and line still fits */
1472		    wordBreakCount++;
1473		    lastWordBreak = curCharsInLine;
1474		    linebuf[curCharsInLine++] = text[curTextPos++];
1475		}
1476
1477	    } else {
1478		/* regular character ==> store in buffer */
1479		linebuf[curCharsInLine++] = text[curTextPos++];
1480	    }
1481	}
1482
1483	if (prematureexit) {
1484	    break;		/* box full */
1485	}
1486
1487	/* if there is anything left in the buffer, draw it */
1488	if (curTextPos >= totallen && curCharsInLine != 0) {
1489	    cury -= ts->l;		/* adjust cury for line height */
1490
1491            if (p->ydirection * (cury - bottom) < 0) {
1492		prematureexit = pdc_true; 	/* box full */
1493		break;
1494	    }
1495
1496            linebuf[curCharsInLine] = 0;        /* terminate the line */
1497
1498	    /* check if the last line is too long */
1499	    ts->w = (float) 0.0;
1500            textwidth = pdf_swidth(p, linebuf);
1501
1502	    if (textwidth > width) {
1503		if (wordBreakCount == 0)
1504		{
1505		    prematureexit = pdc_true;
1506		    break;
1507		}
1508
1509                linebuf[lastWordBreak] = 0;     /* terminate at last blank */
1510		curTextPos -= (curCharsInLine - lastWordBreak - 1);
1511
1512		/* recalculate the width */
1513                textwidth = pdf_swidth(p, linebuf);
1514
1515		/* adjust word spacing for full justify */
1516		if (wordBreakCount != 1 && (mode == pdf_align_justify ||
1517					    mode == pdf_align_fulljustify)) {
1518		    if (!blind) {
1519			pdf_set_word_spacing(p,
1520                            p->ydirection * (width - textwidth) /
1521			    ((wordBreakCount - 1) * ts->h * ts->m.a));
1522		    }
1523		}
1524
1525	    } else if (!blind) {
1526
1527		if (mode == pdf_align_fulljustify && wordBreakCount) {
1528		    pdf_set_word_spacing(p,
1529                            p->ydirection * (width - textwidth) /
1530			    (wordBreakCount * ts->h * ts->m.a));
1531		} else {
1532		    pdf_set_word_spacing(p, (float) 0.0);
1533		}
1534	    }
1535
1536	    lastdone = curTextPos;
1537	    if (!blind)
1538                pdf_show_aligned(p, linebuf, curx, cury, mode);
1539	    curCharsInLine = lastWordBreak = wordBreakCount = 0;
1540	}
1541    }
1542
1543    if (!blind)
1544	pdf_set_word_spacing(p, old_word_spacing);
1545
1546    /* return number of remaining characters  */
1547
1548    while (text[lastdone] == PDF_SPACE)
1549	++lastdone;
1550
1551    if ((text[lastdone] == PDF_RETURN ||
1552	text[lastdone] == PDF_NEWLINE) && text[lastdone+1] == 0)
1553	    ++lastdone;
1554
1555    if (toconv)
1556    {
1557        pdc_free(p->pdc, utext);
1558        p->textformat = textformat;
1559    }
1560
1561    return (int) (totallen - lastdone);
1562}
1563
1564PDFLIB_API int PDFLIB_CALL
1565PDF_show_boxed(
1566    PDF *p,
1567    const char *text,
1568    float left,
1569    float bottom,
1570    float width,
1571    float height,
1572    const char *hmode,
1573    const char *feature)
1574{
1575    static const char fn[] = "PDF_show_boxed";
1576    int remchars;
1577
1578    if (!pdf_enter_api(p, fn, pdf_state_content,
1579        "(p[%p], \"%s\", %g, %g, %g, %g, \"%s\", \"%s\")",
1580        (void *) p, pdc_strprint(p->pdc, text, 0),
1581        left, bottom, width, height, hmode, feature))
1582    {
1583        return 0;
1584    }
1585
1586    if (text == NULL || *text == '\0') {
1587        pdc_trace(p->pdc, "[%d]\n", 0);
1588        return 0;
1589    }
1590
1591    remchars = pdf__show_boxed(p, text, 0, left, bottom,
1592                              width, height, hmode, feature);
1593
1594    pdc_trace(p->pdc, "[%d]\n", remchars);
1595    return remchars;
1596}
1597
1598PDFLIB_API int PDFLIB_CALL
1599PDF_show_boxed2(
1600    PDF *p,
1601    const char *text,
1602    int len,
1603    float left,
1604    float bottom,
1605    float width,
1606    float height,
1607    const char *hmode,
1608    const char *feature)
1609{
1610    static const char fn[] = "PDF_show_boxed2";
1611    int remchars;
1612
1613    if (!pdf_enter_api(p, fn, pdf_state_content,
1614        "(p[%p], \"%s\", %d, %g, %g, %g, %g, \"%s\", \"%s\")",
1615        (void *) p, pdc_strprint(p->pdc, text, len), len,
1616        left, bottom, width, height, hmode, feature))
1617    {
1618        return 0;
1619    }
1620
1621    if (text == NULL || len == 0) {
1622        pdc_trace(p->pdc, "[%d]\n", 0);
1623        return 0;
1624    }
1625
1626    remchars = pdf__show_boxed(p, text, len, left, bottom,
1627                               width, height, hmode, feature);
1628
1629    pdc_trace(p->pdc, "[%d]\n", remchars);
1630    return remchars;
1631}
1632
1633