summaryrefslogtreecommitdiff
path: root/src/pdflib/pdflib/p_text.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/pdflib/pdflib/p_text.c')
-rw-r--r--src/pdflib/pdflib/p_text.c3715
1 files changed, 3715 insertions, 0 deletions
diff --git a/src/pdflib/pdflib/p_text.c b/src/pdflib/pdflib/p_text.c
new file mode 100644
index 0000000..105df11
--- /dev/null
+++ b/src/pdflib/pdflib/p_text.c
@@ -0,0 +1,3715 @@
+/*---------------------------------------------------------------------------*
+ | PDFlib - A library for generating PDF on the fly |
+ +---------------------------------------------------------------------------+
+ | Copyright (c) 1997-2006 Thomas Merz and PDFlib GmbH. All rights reserved. |
+ +---------------------------------------------------------------------------+
+ | |
+ | This software is subject to the PDFlib license. It is NOT in the |
+ | public domain. Extended versions and commercial licenses are |
+ | available, please check http://www.pdflib.com. |
+ | |
+ *---------------------------------------------------------------------------*/
+
+/* $Id: p_text.c,v 1.1 2008/10/17 06:11:49 scuri Exp $
+ *
+ * PDFlib text routines
+ *
+ */
+
+#define P_TEXT_C
+
+#include "p_intern.h"
+#include "p_color.h"
+#include "p_defopt.h"
+#include "p_font.h"
+#include "p_layer.h"
+#include "p_tagged.h"
+
+
+/* --------------------- Text state and options functions ------------------- */
+
+struct pdf_tstate_s
+{
+ pdc_bool glyphinit; /* glyph description initialized */
+ pdc_bool hsinit; /* horizontal scaling initialized */
+ int mask; /* bit mask for text options */
+ int font; /* slot number of the current font */
+ int trm; /* text rendering mode */
+ pdc_scalar fs; /* font size */
+ pdc_scalar ld; /* leading */
+ pdc_scalar cs; /* character spacing */
+ pdc_scalar ws; /* word spacing */
+ pdc_scalar hs; /* horizontal scaling */
+ pdc_scalar ia; /* italic angle */
+ pdc_bool fb; /* fake bold */
+ pdc_scalar rise; /* text rise */
+ pdc_scalar ulw; /* underline width */
+ pdc_scalar ulp; /* underline position */
+
+ pdc_bool newpos; /* new text position */
+ pdc_scalar currtx; /* x coordinate of current text position */
+ pdc_scalar currty; /* y coordinate of current text position */
+ pdc_scalar prevtx; /* x coordinate of previous text position */
+ pdc_scalar prevty; /* y coordinate of previous text position */
+ pdc_scalar linetx; /* x coordinate of text line start position */
+ pdc_scalar refptx; /* x and y coordinate of reference position */
+ pdc_scalar refpty; /* for moving to next text line start position*/
+};
+
+/* Initialize the text state at the beginning of each page */
+void
+pdf_init_tstate(PDF *p)
+{
+ static const char fn[] = "pdf_init_tstate";
+
+ /* text state */
+ pdf_ppt *ppt = p->curr_ppt;
+ pdf_tstate *ts;
+
+ if (!p->curr_ppt->tstate)
+ {
+ p->curr_ppt->tstate = (pdf_tstate *) pdc_malloc(p->pdc,
+ PDF_MAX_SAVE_LEVEL * sizeof(pdf_tstate), fn);
+ ppt->currto = (pdf_text_options *) pdc_malloc(p->pdc,
+ sizeof(pdf_text_options), fn);
+ }
+
+ ts = &ppt->tstate[ppt->sl];
+
+ ts->glyphinit = pdc_undef;
+ ts->hsinit = (p->ydirection == -1) ? pdc_false : pdc_true;
+
+ ts->mask = 0;
+ ts->font = -1;
+ ts->trm = 0;
+ ts->fs = PDC_FLOAT_MIN;
+ ts->ld = 0;
+ ts->cs = 0;
+ ts->ws = 0;
+ ts->hs = 1;
+ ts->ia = 0;
+ ts->fb = pdc_false;
+ ts->rise = 0;
+ ts->ulw = PDF_UNDERLINEWIDTH_AUTO;
+ ts->ulp = PDF_UNDERLINEPOSITION_AUTO;
+
+ ts->newpos = pdc_false;
+ ts->currtx = 0;
+ ts->currty = 0;
+ ts->prevtx = 0;
+ ts->prevty = 0;
+ ts->linetx = 0;
+ ts->refptx = 0;
+ ts->refpty = 0;
+
+ /* current text options */
+ pdf_init_text_options(p, ppt->currto);
+}
+
+void
+pdf_cleanup_page_tstate(PDF *p, pdf_ppt *ppt)
+{
+ if (ppt->tstate != NULL)
+ {
+ pdc_free(p->pdc, ppt->tstate);
+ pdc_free(p->pdc, ppt->currto);
+ ppt->tstate = NULL;
+ ppt->currto = NULL;
+ }
+}
+
+void
+pdf_save_tstate(PDF *p)
+{
+ pdf_ppt *ppt = p->curr_ppt;
+ int sl = ppt->sl;
+
+ memcpy(&ppt->tstate[sl + 1], &ppt->tstate[sl], sizeof(pdf_tstate));
+}
+
+void
+pdf_restore_currto(PDF *p)
+{
+ pdf_ppt *ppt = p->curr_ppt;
+ pdf_text_options *currto = ppt->currto;
+ pdf_tstate *ts = &ppt->tstate[ppt->sl];
+
+ currto->mask = ts->mask;
+ currto->font = ts->font;
+ currto->textrendering = ts->trm;
+ currto->fontsize = ts->fs;
+ currto->leading = ts->ld;
+ currto->charspacing = ts->cs;
+ currto->wordspacing = ts->ws;
+ currto->horizscaling = ts->hs;
+ currto->italicangle = ts->ia;
+ currto->fakebold = ts->fb;
+ currto->textrise = ts->rise;
+ currto->underlinewidth = ts->ulw;
+ currto->underlineposition = ts->ulp;
+}
+
+void
+pdf_set_tstate(PDF *p, pdc_scalar value, pdf_text_optflags tflag)
+{
+ pdf_ppt *ppt = p->curr_ppt;
+ pdf_tstate *ts = &ppt->tstate[ppt->sl];
+ pdf_text_options *currto = ppt->currto;
+ int ivalue = (int) value;
+ pdc_scalar prevvalue;
+
+ /* text state parameter values can never be percentages */
+
+ switch (tflag)
+ {
+ case to_font:
+ pdf_check_handle(p, ivalue, pdc_fonthandle);
+ prevvalue = ts->font;
+ ts->font = currto->font = ivalue;
+ if (prevvalue != -1 &&
+ (p->fonts[(int) prevvalue].metricflags & font_italic) !=
+ (p->fonts[currto->font].metricflags & font_italic))
+ currto->mask |= (1 << to_italicangle);
+ break;
+
+ case to_textrendering:
+ if (ivalue < 0 || ivalue > PDF_LAST_TRMODE)
+ pdc_error(p->pdc, PDC_E_ILLARG_INT,
+ "textrendering", pdc_errprintf(p->pdc, "%d", ivalue),
+ 0, 0);
+ prevvalue = ts->trm;
+ ts->trm = currto->textrendering = ivalue;
+ break;
+
+ case to_fontsize:
+ pdc_check_number_zero(p->pdc, "fontsize", value);
+ prevvalue = ts->ld;
+ ts->ld = currto->leading = value;
+ if (!PDC_FLOAT_ISNULL(value - prevvalue))
+ currto->mask |= (1 << to_leading);
+ prevvalue = ts->fs;
+ ts->fs = currto->fontsize = value;
+ break;
+
+ case to_leading:
+ prevvalue = ts->ld;
+ ts->ld = currto->leading = value;
+ break;
+
+ case to_charspacing:
+ prevvalue = ts->cs;
+ ts->cs = currto->charspacing = value;
+ break;
+
+ case to_wordspacing:
+ prevvalue = ts->ws;
+ ts->ws = currto->wordspacing = value;
+ break;
+
+ case to_underlinewidth:
+ prevvalue = ts->ulw;
+ ts->ulw = currto->underlinewidth = value;
+ break;
+
+ case to_underlineposition:
+ prevvalue = ts->ulp;
+ ts->ulp = currto->underlineposition = value;
+ break;
+
+ case to_horizscaling:
+ pdc_check_number_zero(p->pdc, "horizscaling", value);
+ prevvalue = ts->hs;
+ ts->hs = currto->horizscaling = value;
+ break;
+
+ case to_italicangle:
+ pdc_check_number_limits(p->pdc, "italicangle", value,
+ -90 + PDC_FLOAT_PREC, 90 + PDC_FLOAT_MAX);
+ prevvalue = ts->ia;
+ ts->ia = currto->italicangle = value;
+ break;
+
+ case to_fakebold:
+ prevvalue = ts->fb;
+ ts->fb = currto->fakebold = (pdc_bool) ivalue;
+ return;
+
+ case to_textrise:
+ prevvalue = ts->rise;
+ ts->rise = currto->textrise = value;
+ break;
+
+
+ case to_overline:
+ currto->overline = (pdc_bool) ivalue;
+ return;
+
+ case to_strikeout:
+ currto->strikeout = (pdc_bool) ivalue;
+ return;
+
+ case to_underline:
+ currto->underline = (pdc_bool) ivalue;
+ return;
+
+ case to_textformat:
+ currto->textformat = (pdc_text_format) ivalue;
+ return;
+
+ case to_charref:
+ currto->charref = (pdc_bool) ivalue;
+ return;
+
+ case to_escapesequence:
+ currto->escapesequence = (pdc_bool) ivalue;
+ return;
+
+ case to_glyphcheck:
+ currto->glyphcheck = (pdc_glyphcheck) ivalue;
+ return;
+
+ case to_glyphwarning:
+ currto->glyphwarning = (pdc_bool) ivalue;
+ return;
+
+ default:
+ return;
+ }
+
+ if (!PDC_FLOAT_ISNULL(value - prevvalue))
+ currto->mask |= (1 << tflag);
+ ts->mask = currto->mask;
+}
+
+void
+pdf__setfont(PDF *p, int font, pdc_scalar fontsize)
+{
+ pdf_set_tstate(p, (pdc_scalar) font, to_font);
+ pdf_set_tstate(p, fontsize, to_fontsize);
+}
+
+void
+pdf__set_text_pos(PDF *p, pdc_scalar x, pdc_scalar y)
+{
+ pdf_ppt *ppt = p->curr_ppt;
+ pdf_tstate *ts = &ppt->tstate[ppt->sl];
+
+ pdc_check_number(p->pdc, "x", x);
+ pdc_check_number(p->pdc, "y", y);
+
+ ts->newpos = pdc_true;
+ ts->currtx = x;
+ ts->currty = y;
+ ts->prevtx = ts->refptx;
+ ts->prevty = ts->refpty;
+ ts->linetx = x;
+}
+
+double
+pdf_get_tstate(PDF *p, pdf_text_optflags tflag)
+{
+ pdf_ppt *ppt = p->curr_ppt;
+ pdf_text_options *currto = ppt->currto;
+
+ switch (tflag)
+ {
+ case to_font:
+ return (double) currto->font;
+
+ case to_textrendering:
+ return (double) currto->textrendering;
+
+ case to_fontsize:
+ return (double) currto->fontsize;
+
+ case to_leading:
+ return (double) currto->leading;
+
+ case to_charspacing:
+ return (double) currto->charspacing;
+
+ case to_wordspacing:
+ return (double) currto->wordspacing;
+
+ case to_horizscaling:
+ return (double) currto->horizscaling;
+
+ case to_italicangle:
+ return (double) currto->italicangle;
+
+ case to_fakebold:
+ return (double) currto->fakebold;
+
+ case to_textrise:
+ return (double) currto->textrise;
+
+ case to_underlinewidth:
+ return (double) currto->underlinewidth;
+
+ case to_underlineposition:
+ return (double) currto->underlineposition;
+
+
+ case to_overline:
+ return (double) currto->overline;
+
+ case to_strikeout:
+ return (double) currto->strikeout;
+
+ case to_underline:
+ return (double) currto->underline;
+
+ case to_textx:
+ return (double) ppt->tstate[ppt->sl].currtx;
+
+ case to_texty:
+ return (double) ppt->tstate[ppt->sl].currty;
+
+ default:
+ break;
+ }
+
+ return 0;
+}
+
+int
+pdf_get_font(PDF *p)
+{
+ if (p->curr_ppt)
+ return (int) pdf_get_tstate(p, to_font);
+ return -1;
+}
+
+void
+pdf_init_text_options(PDF *p, pdf_text_options *to)
+{
+ to->mask = 0;
+ to->pcmask = 0;
+ to->font = -1;
+ to->fontsize = PDC_FLOAT_MIN;
+ to->fontsize_pc = 0;
+ to->fontsize_st = (int) text_fontsize;
+ to->fontset = 0;
+ to->leading = 0;
+ to->leading_pc = 0;
+ to->textrendering = 0;
+ to->charspacing = 0;
+ to->charspacing_pc = 0;
+ to->horizscaling = 1;
+ to->italicangle = 0;
+ to->fakebold = pdc_false;
+ to->textrise = 0;
+ to->textrise_pc = 0;
+ to->wordspacing = 0;
+ to->wordspacing_pc = 0;
+ to->underlinewidth = PDF_UNDERLINEWIDTH_AUTO;
+ to->underlineposition = PDF_UNDERLINEPOSITION_AUTO;
+ to->overline = pdc_false;
+ to->strikeout = pdc_false;
+ to->underline = pdc_false;
+ to->text = NULL;
+ to->textlen = 0;
+ to->textformat = p->textformat;
+ to->charref = p->pdc->charref;
+ to->escapesequence = p->pdc->escapesequ;
+ to->glyphcheck = p->glyphcheck;
+ to->glyphwarning = p->debug[(int) 'g'];
+ to->glyphwarning = pdf_get_errorpolicy(p, NULL, to->glyphwarning);
+ pdf_init_coloropt(p, &to->fillcolor);
+ pdf_init_coloropt(p, &to->strokecolor);
+ to->strokewidth = PDF_UNDERLINEWIDTH_AUTO;
+ to->dasharray[0] = 0;
+ to->dasharray[1] = 0;
+ to->xadvancelist = NULL;
+ to->nglyphs = 0;
+ to->link = NULL;
+}
+
+static pdf_text_optflags pdf_toptflags[] =
+{
+ to_font, to_fontsize, to_textrendering, to_charspacing,
+ to_horizscaling, to_italicangle, to_wordspacing, to_textrise
+};
+
+void
+pdf_set_text_options(PDF *p, pdf_text_options *to)
+{
+ pdf_ppt *ppt = p->curr_ppt;
+ pdf_text_options *currto = p->curr_ppt->currto;
+ pdf_tstate *ts = &ppt->tstate[ppt->sl];
+ pdf_text_optflags tflag;
+ int i, n;
+
+ /* we synchronize both text state and text options */
+
+ n = sizeof(pdf_toptflags) / sizeof(pdf_text_optflags);
+ for (i = 0; i < n; i++)
+ {
+ tflag = pdf_toptflags[i];
+ if (to->mask & (1 << tflag))
+ {
+ switch (tflag)
+ {
+ case to_font:
+ if (!(currto->mask & (1 << tflag)) &&
+ to->font == currto->font)
+ break;
+ ts->font = currto->font = to->font;
+ continue;
+
+ case to_fontsize:
+ if (!(currto->mask & (1 << tflag)) &&
+ PDC_FLOAT_ISNULL(to->fontsize - currto->fontsize))
+ break;
+ ts->fs = currto->fontsize = to->fontsize;
+ continue;
+
+ case to_textrendering:
+ if (!(currto->mask & (1 << tflag)) &&
+ to->textrendering == currto->textrendering)
+ break;
+ ts->trm = currto->textrendering = to->textrendering;
+ continue;
+
+ /* to->leading is never used */
+
+ case to_charspacing:
+ if (!(currto->mask & (1 << tflag)) &&
+ PDC_FLOAT_ISNULL(to->charspacing - currto->charspacing))
+ break;
+ ts->cs = currto->charspacing = to->charspacing;
+ continue;
+
+ case to_horizscaling:
+ if (!(currto->mask & (1 << tflag)) &&
+ PDC_FLOAT_ISNULL(to->horizscaling - currto->horizscaling))
+ break;
+ ts->hs = currto->horizscaling = to->horizscaling;
+ continue;
+
+ case to_italicangle:
+ if (!(currto->mask & (1 << tflag)) &&
+ PDC_FLOAT_ISNULL(to->italicangle - currto->italicangle))
+ break;
+ ts->ia = currto->italicangle = to->italicangle;
+ continue;
+
+ case to_fakebold:
+ ts->fb = currto->fakebold = to->fakebold;
+ continue;
+
+ case to_wordspacing:
+ if (!(currto->mask & (1 << tflag)) &&
+ PDC_FLOAT_ISNULL(to->wordspacing - currto->wordspacing))
+ break;
+ ts->ws = currto->wordspacing = to->wordspacing;
+ continue;
+
+ case to_textrise:
+ if (!(currto->mask & (1 << tflag)) &&
+ PDC_FLOAT_ISNULL(to->textrise - currto->textrise))
+ break;
+ ts->rise = currto->textrise = to->textrise;
+ continue;
+
+ case to_underlinewidth:
+ if (!(currto->mask & (1 << tflag)) &&
+ PDC_FLOAT_ISNULL(to->underlinewidth -
+ currto->underlinewidth))
+ break;
+ ts->ulw = currto->underlinewidth = to->underlinewidth;
+ continue;
+
+ case to_underlineposition:
+ if (!(currto->mask & (1 << tflag)) &&
+ PDC_FLOAT_ISNULL(to->underlineposition -
+ currto->underlineposition))
+ break;
+ ts->ulp = currto->underlineposition = to->underlineposition;
+ continue;
+
+ default:
+ continue;
+ }
+
+ to->mask &= ~(1 << tflag);
+ }
+ }
+
+ ts->mask = currto->mask = to->mask;
+}
+
+int
+pdf_get_fontsize_option(PDF *p, int font, pdc_resopt *resopts,
+ pdc_scalar *fontsize)
+{
+ pdc_scalar fs[2], fm ;
+ int ns;
+
+ /* *fontsize is initialized from outside */
+
+ fs[0] = 0; /* see "auto" */
+ fs[1] = 0;
+ ns = pdc_get_optvalues("fontsize", resopts, (pdc_scalar *) fs, NULL);
+ if (ns == 1)
+ {
+ *fontsize = fs[0];
+ }
+ else if (ns == 2)
+ {
+ int kind = (int) fs[0];
+
+ pdf_check_handle(p, font, pdc_fonthandle);
+
+ switch(kind)
+ {
+ case text_xheight:
+ fm = (pdc_scalar) p->fonts[font].ft.m.xHeight;
+ break;
+
+ case text_capheight:
+ fm = (pdc_scalar) p->fonts[font].ft.m.capHeight;
+ break;
+
+ case text_ascender:
+ fm = (pdc_scalar) p->fonts[font].ft.m.ascender;
+ break;
+
+ default:
+ fm = 1000.0;
+ break;
+ }
+
+ *fontsize = fs[1] * 1000.0 / fm;
+ }
+
+ return ns;
+}
+
+pdc_bool
+pdf_calculate_text_options(PDF *p, pdf_text_options *to, pdc_bool force,
+ pdc_scalar fontscale, pdc_scalar minfontsize,
+ pdc_scalar fontsizeref)
+{
+ pdc_bool kminfs = pdc_false;
+
+ if (to->mask & (1 << to_fontsize) || force)
+ {
+ pdc_scalar fontsize;
+
+ if (fontsizeref == 0)
+ fontsizeref = to->fontsize;
+
+ if (to->pcmask & (1 << to_fontsize))
+ fontsize = to->fontsize_pc * fontsizeref;
+ else
+ fontsize = fontscale * to->fontsize;
+
+ if (to->fontsize_st != (int) text_fontsize)
+ {
+ pdf_font *currfont = &p->fonts[to->font];
+ pdc_scalar fm;
+
+ switch(to->fontsize_st)
+ {
+ case text_xheight:
+ fm = (pdc_scalar) currfont->ft.m.xHeight;
+ break;
+
+ case text_capheight:
+ fm = (pdc_scalar) currfont->ft.m.capHeight;
+ break;
+
+ case text_ascender:
+ fm = (pdc_scalar) currfont->ft.m.ascender;
+ break;
+
+ default:
+ fm = 1000.0;
+ break;
+ }
+
+ fontsize *= 1000.0 / fm;
+ }
+
+ if (fontscale < 1.0 && fabs(fontsize) < minfontsize)
+ {
+ if (fontsize > 0)
+ fontsize = minfontsize;
+ else
+ fontsize = -minfontsize;
+ kminfs = pdc_true;
+ }
+ to->fontsize = fontsize;
+
+ /* we reset relative fontsize specifications */
+ if (to->mask & (1L << to_fontsize))
+ {
+ to->pcmask &= ~(1 << to_fontsize);
+ to->fontsize_st = (int) text_fontsize;
+ }
+ }
+
+ if ((to->mask & (1 << to_charspacing) || force) &&
+ (to->pcmask & (1 << to_charspacing)))
+ {
+ to->charspacing = to->charspacing_pc * to->fontsize;
+ }
+
+ if ((to->mask & (1 << to_wordspacing) || force) &&
+ (to->pcmask & (1 << to_wordspacing)))
+ {
+ to->wordspacing = to->wordspacing_pc * to->fontsize;
+ }
+
+ if ((to->mask & (1 << to_textrise) || force) &&
+ (to->pcmask & (1 << to_textrise)))
+ {
+ to->textrise = to->textrise_pc * to->fontsize;
+ }
+
+ /* maybe used in a future version */
+ if ((to->mask & (1 << to_leading) || force) &&
+ (to->pcmask & (1 << to_leading)))
+ {
+ to->leading = to->leading_pc * to->fontsize;
+ }
+
+ return kminfs;
+}
+
+void
+pdf_get_text_options(PDF *p, pdf_text_options *to, pdc_resopt *resopts)
+{
+ char **strlist;
+ int i, inum;
+ pdc_scalar fs[2];
+
+ if (pdc_get_optvalues("glyphwarning", resopts, &to->glyphwarning, NULL))
+ to->mask |= (1L << to_glyphwarning);
+ to->glyphwarning = pdf_get_errorpolicy(p, resopts, to->glyphwarning);
+
+ if (pdc_get_optvalues("font", resopts, &to->font, NULL))
+ {
+ pdf_check_handle(p, to->font, pdc_fonthandle);
+ to->mask |= (1L << to_font);
+ to->fontset |= (1L << to_font);
+ }
+
+ fs[0] = 0; /* see "auto" */
+ fs[1] = 0;
+ inum = pdc_get_optvalues("fontsize", resopts, (pdc_scalar *) fs, NULL);
+ if (inum)
+ {
+ i = inum - 1;
+ to->fontsize = fs[i];
+ if (inum == 2)
+ to->fontsize_st = (int) fs[0];
+ else
+ to->fontsize_st = (int) text_fontsize;
+ to->mask |= (1L << to_fontsize);
+ to->mask |= (1L << to_fontsize_st);
+
+ if (pdc_is_lastopt_percent(resopts, i))
+ {
+ to->pcmask |= (1 << to_fontsize);
+ to->fontsize_pc = to->fontsize;
+ }
+ else
+ to->pcmask &= ~(1 << to_fontsize);
+
+ to->fontset |= (1L << to_fontsize);
+ }
+
+ if (pdc_get_optvalues("charref", resopts, &to->charref, NULL))
+ to->mask |= (1L << to_charref);
+
+ if (pdc_get_optvalues("escapesequence", resopts, &to->escapesequence, NULL))
+ to->mask |= (1L << to_escapesequence);
+
+ if (pdc_get_optvalues("glyphcheck", resopts, &inum, NULL))
+ {
+ to->glyphcheck = (pdc_glyphcheck) inum;
+ to->mask |= (1L << to_glyphcheck);
+ }
+
+ if (pdc_get_optvalues("charspacing", resopts, &to->charspacing, NULL))
+ {
+ if (pdc_is_lastopt_percent(resopts, 0))
+ {
+ to->pcmask |= (1 << to_charspacing);
+ to->charspacing_pc = to->charspacing;
+ }
+ else
+ to->pcmask &= ~(1 << to_charspacing);
+ to->mask |= (1L << to_charspacing);
+ }
+
+ if (pdc_get_optvalues("horizscaling", resopts, &to->horizscaling, NULL))
+ {
+ if (!pdc_is_lastopt_percent(resopts, 0))
+ to->horizscaling /= 100.0;
+ to->mask |= (1L << to_horizscaling);
+ }
+
+ if (pdc_get_optvalues("italicangle", resopts, &to->italicangle, NULL))
+ to->mask |= (1L << to_italicangle);
+
+ if (pdc_get_optvalues("fakebold", resopts, &to->fakebold, NULL))
+ to->mask |= (1L << to_fakebold);
+
+
+ if (pdc_get_optvalues("overline", resopts, &to->overline, NULL))
+ to->mask |= (1L << to_overline);
+
+ if (pdc_get_optvalues("strikeout", resopts, &to->strikeout, NULL))
+ to->mask |= (1L << to_strikeout);
+
+ if (pdc_get_optvalues("textformat", resopts, &inum, NULL))
+ {
+ to->textformat = (pdc_text_format) inum;
+ to->mask |= (1L << to_textformat);
+ pdf_check_textformat(p, to->textformat);
+ }
+
+ if (pdc_get_optvalues("textrendering", resopts, &to->textrendering, NULL))
+ to->mask |= (1L << to_textrendering);
+
+ if (pdc_get_optvalues("textrise", resopts, &to->textrise, NULL))
+ {
+ if (pdc_is_lastopt_percent(resopts, 0))
+ {
+ to->pcmask |= (1 << to_textrise);
+ to->textrise_pc = to->textrise;
+ }
+ else
+ to->pcmask &= ~(1 << to_textrise);
+ to->mask |= (1L << to_textrise);
+ }
+
+ if (pdc_get_optvalues("underline", resopts, &to->underline, NULL))
+ to->mask |= (1L << to_underline);
+
+ if (pdc_get_optvalues("wordspacing", resopts, &to->wordspacing, NULL))
+ {
+ if (pdc_is_lastopt_percent(resopts, 0))
+ {
+ to->pcmask |= (1 << to_wordspacing);
+ to->wordspacing_pc = to->wordspacing;
+ }
+ else
+ to->pcmask &= ~(1 << to_wordspacing);
+ to->mask |= (1L << to_wordspacing);
+ }
+
+ if (pdc_get_optvalues("underlinewidth", resopts, &to->underlinewidth, NULL))
+ {
+ if (pdc_is_lastopt_percent(resopts, 0))
+ {
+ to->pcmask |= (1 << to_underlinewidth);
+ }
+ else
+ to->pcmask &= ~(1 << to_underlinewidth);
+ to->mask |= (1L << to_underlinewidth);
+ }
+
+ if (pdc_get_optvalues("underlineposition", resopts,
+ &to->underlineposition, NULL))
+ {
+ if (pdc_is_lastopt_percent(resopts, 0))
+ {
+ to->pcmask |= (1 << to_underlineposition);
+ }
+ else
+ to->pcmask &= ~(1 << to_underlineposition);
+ to->mask |= (1L << to_underlineposition);
+ }
+
+ inum = pdc_get_optvalues("fillcolor", resopts, NULL, &strlist);
+ if (inum)
+ {
+ pdf_parse_coloropt(p, "fillcolor", strlist, inum, (int) color_max,
+ &to->fillcolor);
+ to->mask |= (1L << to_fillcolor);
+ }
+
+ inum = pdc_get_optvalues("strokecolor", resopts, NULL, &strlist);
+ if (inum)
+ {
+ pdf_parse_coloropt(p, "strokecolor", strlist, inum, (int) color_max,
+ &to->strokecolor);
+ to->mask |= (1L << to_strokecolor);
+ }
+
+ if (pdc_get_optvalues("strokewidth", resopts, &to->strokewidth, NULL))
+ {
+ if (pdc_is_lastopt_percent(resopts, 0))
+ {
+ to->pcmask |= (1 << to_strokewidth);
+ }
+ else
+ to->pcmask &= ~(1 << to_strokewidth);
+ to->mask |= (1L << to_strokewidth);
+ }
+
+ inum = pdc_get_optvalues("dasharray", resopts, to->dasharray, NULL);
+ if (inum)
+ {
+ if (inum == 1)
+ to->dasharray[1] = to->dasharray[0];
+ to->mask |= (1L << to_dasharray);
+ }
+
+ inum = pdc_get_optvalues("xadvancelist", resopts, NULL, &strlist);
+ if (inum)
+ {
+ to->xadvancelist = (pdc_scalar *) strlist;
+ to->nglyphs = inum;
+ }
+
+ /*
+ * deprecated
+ */
+ if (pdc_get_optvalues("weblink", resopts, NULL, &strlist))
+ {
+ to->link = strlist[0];
+ to->linktype = "URI";
+ }
+ else if (pdc_get_optvalues("locallink", resopts, NULL, &strlist))
+ {
+ to->link = strlist[0];
+ to->linktype = "GoTo";
+ }
+ else if (pdc_get_optvalues("pdflink", resopts, NULL, &strlist))
+ {
+ to->link = strlist[0];
+ to->linktype = "GoToR";
+ }
+}
+
+/* ------------------------ Text object functions -------------------------- */
+
+static void
+pdf_begin_text(PDF *p)
+{
+ pdf_ppt *ppt = p->curr_ppt;
+ pdf_tstate *ts = &ppt->tstate[ppt->sl];
+ pdf_text_options *currto = ppt->currto;
+ pdf_font *currfont = NULL;
+
+ if (currto->font > -1)
+ currfont = &p->fonts[currto->font];
+
+ /* end text object if italicangle changed */
+ if (currto->mask & (1L << to_italicangle))
+ pdf_end_text(p);
+
+ /* begin text object */
+ if (!p->in_text)
+ {
+ p->in_text = pdc_true;
+ pdc_puts(p->out, "BT\n");
+ }
+
+ if (PDF_FORCE_OUTPUT() && ts->glyphinit == pdc_undef)
+ ts->glyphinit = pdc_false;
+
+ if (currfont &&
+ ((currto->mask & (1L << to_font)) ||
+ (currto->mask & (1L << to_fontsize)) || !ts->glyphinit))
+ {
+ pdc_printf(p->out, "/F%d %f Tf\n",
+ ppt->fn_bias + currto->font, p->ydirection * currto->fontsize);
+
+ currfont->used_in_current_doc = pdc_true;
+ currfont->used_on_current_page = pdc_true;
+ }
+
+ if (currto->mask & (1L << to_textrendering) || !ts->glyphinit)
+ pdc_printf(p->out, "%d Tr\n", currto->textrendering);
+
+ if (currto->mask & (1L << to_leading) || !ts->glyphinit)
+ pdc_printf(p->out, "%f TL\n", p->ydirection * currto->leading);
+
+ if (currto->mask & (1L << to_charspacing) || !ts->glyphinit)
+ pdc_printf(p->out, "%f Tc\n", p->ydirection * currto->charspacing);
+
+ if (!ts->hsinit || currto->mask & (1L << to_horizscaling) || !ts->glyphinit)
+ pdc_printf(p->out, "%f Tz\n",
+ 100 * p->ydirection * currto->horizscaling);
+
+ if (currto->mask & (1L << to_textrise) || !ts->glyphinit)
+ pdc_printf(p->out, "%f Ts\n", p->ydirection * currto->textrise);
+
+ /* initialize */
+ if (!ts->glyphinit)
+ ts->glyphinit = pdc_true;
+ ts->hsinit = pdc_true;
+ ts->mask = currto->mask = 0;
+}
+
+void
+pdf_end_text(PDF *p)
+{
+ pdf_ppt *ppt = p->curr_ppt;
+ pdf_tstate *ts = &ppt->tstate[ppt->sl];
+
+ if (p->in_text)
+ {
+ p->in_text = pdc_false;
+ pdc_puts(p->out, "ET\n");
+
+ ts->newpos = pdc_false;
+ ts->prevtx = 0;
+ ts->prevty = 0;
+ ts->refptx = 0;
+ ts->refpty = 0;
+ }
+}
+
+void
+pdf_reset_tstate(PDF *p)
+{
+ pdf_ppt *ppt = p->curr_ppt;
+ pdf_tstate *ts = &ppt->tstate[ppt->sl];
+
+ pdf_set_tstate(p, 0, to_textrendering);
+ pdf_set_tstate(p, 0, to_leading);
+ pdf_set_tstate(p, 0, to_charspacing);
+ pdf_set_tstate(p, 0, to_wordspacing);
+ pdf_set_tstate(p, 1, to_horizscaling);
+ pdf_set_tstate(p, 0, to_italicangle);
+ pdf_set_tstate(p, 0, to_fakebold);
+ pdf_set_tstate(p, 0, to_textrise);
+ pdf_set_tstate(p, PDF_UNDERLINEWIDTH_AUTO, to_underlinewidth);
+ pdf_set_tstate(p, PDF_UNDERLINEPOSITION_AUTO, to_underlineposition);
+
+ ts->hsinit = (p->ydirection == -1) ? pdc_false : pdc_true;
+ if (ts->mask || !ts->hsinit)
+ {
+ pdc_scalar ydirection = p->ydirection;
+ p->ydirection = 1;
+ pdf_begin_text(p);
+ pdf_end_text(p);
+ p->ydirection = ydirection;
+ }
+}
+
+
+/* ------------------- Text string checking function ---------------------- */
+
+typedef struct pdf_ligat_s pdf_ligat;
+
+struct pdf_ligat_s
+{
+ pdf_ligat *next;
+ int icp; /* text position */
+ int nb; /* number of parts */
+ pdc_byte culist[2 * PDC_MAX_UVLIST];
+ /* ligature parts */
+};
+
+static pdf_ligat *
+pdf_register_ligat(PDF *p, pdf_ligat *ligatlist, int icp, int nv,
+ pdc_ushort *culist, int charlen)
+{
+ static const char fn[] = "pdf_hook_ligat";
+ int i;
+
+ pdf_ligat *ligat =
+ (pdf_ligat *) pdc_malloc_tmp(p->pdc, sizeof(pdf_ligat), fn, NULL, NULL);
+
+ if (ligatlist == NULL)
+ {
+ ligatlist = ligat;
+ }
+ else
+ {
+ pdf_ligat *next = ligatlist;
+
+ while (next->next != NULL)
+ next = next->next;
+ next->next = ligat;
+ }
+
+ ligat->next = NULL;
+ ligat->icp = charlen * icp;
+ nv--;
+ ligat->nb = charlen * nv;
+
+ if (charlen == 1)
+ {
+ for (i = 0; i < nv; i++)
+ ligat->culist[i] = (pdc_byte) culist[i+1];
+ }
+ else
+ {
+ memcpy(ligat->culist, &culist[1], (size_t) ligat->nb);
+ }
+ return ligatlist;
+}
+
+static void
+pdf_cleanup_ligat(PDF *p, pdf_ligat *list)
+{
+ pdf_ligat *next;
+
+ while (list != NULL)
+ {
+ next = list->next;
+ pdc_free_tmp(p->pdc, list);
+ list = next;
+ }
+}
+
+
+#define PDF_MAX_GLYPHCHECKS 8
+
+int
+pdf_get_approximate_uvlist(PDF *p, pdf_font *currfont, pdc_encodingvector *ev,
+ int usv, pdc_bool replace,
+ pdc_ushort *uvlist, pdc_ushort *cglist)
+{
+ int cg = 0, nv = 1;
+
+ (void) p;
+ (void) ev;
+ (void) usv;
+
+
+ if (cg <= 0)
+ {
+ if (replace)
+ {
+ cglist[0] = (pdc_ushort) currfont->replacementcode;
+ uvlist[0] = (pdc_ushort) currfont->replacementchar;
+ }
+ else
+ {
+ cglist[0] = 0;
+ uvlist[0] = 0;
+ }
+ nv = 1;
+ }
+
+ return nv;
+}
+
+static void
+pdf_logg_glyph_replacement(PDF *p, int ic, int code,
+ pdc_encoding enc, int charlen,
+ pdc_ushort *uvlist, pdc_ushort *cglist, int nv)
+{
+ const char *glyphname;
+ int i;
+
+ pdc_logg(p->pdc, "\t\tat text position %d: ", ic);
+
+ if (charlen == 1)
+ pdc_logg(p->pdc, "code x%02X ", code);
+ else
+ pdc_logg(p->pdc, "U+%04X ", code);
+
+ pdc_logg(p->pdc, "was replaced by: ");
+ if (nv > 1)
+ pdc_logg(p->pdc, "\n");
+
+ for (i = 0; i < nv; i++)
+ {
+ if (nv > 1)
+ pdc_logg(p->pdc, "\t\t\t");
+
+ if (charlen == 1)
+ pdc_logg(p->pdc, "code x%02X ", cglist[i]);
+ else
+ pdc_logg(p->pdc, "U+%04X ", uvlist[i]);
+
+ if (enc >= 0)
+ {
+ if (charlen == 1)
+ pdc_logg(p->pdc, "U+%04X ", uvlist[i]);
+ else
+ pdc_logg(p->pdc, "code x%02X ", cglist[i]);
+ }
+
+ glyphname = pdc_unicode2glyphname(p->pdc, uvlist[i]);
+ if (glyphname && strlen(glyphname))
+ pdc_logg(p->pdc, "\"%s\"", glyphname);
+
+ pdc_logg(p->pdc, "\n");
+ }
+}
+
+void
+pdf_get_input_textformat(pdf_font *currfont,
+ pdc_text_format *intextformat, int *convflags)
+{
+ /* input text format */
+ if (currfont->unibyte)
+ {
+ /* encoding=unicode, but 8-bit encoding ev is available */
+ *convflags |= PDC_CONV_FORCEUTF16;
+ }
+ else if (currfont->codesize <= 1)
+ {
+ /* we must force bytes[2] source input format
+ * for 8-bit encodings because of "pass through mode".
+ */
+ if (*intextformat == pdc_auto)
+ *intextformat = pdc_bytes;
+ else if (*intextformat == pdc_auto2)
+ *intextformat = pdc_bytes2;
+ }
+}
+
+/* Converts and checks input text string.
+ *
+ * The function returns a pointer to an allocated temporary memory
+ * (pdc_malloc_tmp, pdc_free_tmp) containing the converted string
+ * if flag PDF_USE_TMPALLOC is set.
+ *
+ * If return value is -1 an error was occurred, otherwise >= 0.
+ * glyphcheck = none: the number of unmapped glyphs will be returned.
+ *
+ */
+
+int
+pdf_check_textstring(PDF *p, const char *text, int len, int flags,
+ pdf_text_options *to, pdc_byte **outtext_p, int *outlen,
+ int *outcharlen, pdc_bool verbose)
+{
+ static const char fn[] = "pdf_check_textstring";
+
+ pdc_bool logg1 = pdc_logg_is_enabled(p->pdc, 1, trc_text);
+ pdc_bool logg2 = pdc_false;
+ pdf_font *currfont = &p->fonts[to->font];
+ pdc_encoding enc = currfont->ft.enc;
+ pdc_encodingvector *ev = pdc_get_encoding_vector(p->pdc, enc);
+ pdc_encodingvector *inev = NULL;
+ pdc_encodingvector *outev = NULL;
+
+ pdc_text_format intextformat = to->textformat;
+ pdc_text_format outtextformat = pdc_utf16;
+
+ int charlen = 1, newcharlen = 1;
+ int convflags = PDC_CONV_NOBOM;
+ int unmapgids = 0;
+
+ pdf_ligat *ligat, *ligatlist = NULL;
+ pdc_byte *intext = (pdc_byte *) text, *outtext = NULL;
+ int newlen = -1, maxlen, replchar;
+
+ if (logg1)
+ {
+ logg2 = pdc_logg_is_enabled(p->pdc, 2, trc_text);
+
+ if (logg2)
+ pdc_logg_hexdump(p->pdc, "input text", "\t\t", (char *) text, len);
+ else
+ pdc_logg(p->pdc,
+ "\tText will be checked and converted: \"%T\"\n", text, len);
+
+ if (logg2)
+ {
+ pdc_logg(p->pdc,
+ "\t\tfont: \"%s\"\n"
+ "\t\tencoding: \"%s\"\n"
+ "\t\ttextformat: %s\n"
+ "\t\tcharref: %s\n"
+ "\t\tescapesequence: %s\n"
+ "\t\tglyphwarning: %s\n"
+ "\t\tglyphcheck: %s\n",
+ currfont->ft.name,
+ pdf_get_encoding_name(p, enc, currfont),
+ pdc_get_keyword(intextformat, pdf_textformat_keylist),
+ PDC_BOOLSTR(to->charref),
+ PDC_BOOLSTR(to->escapesequence),
+ PDC_BOOLSTR(to->glyphwarning),
+ pdc_get_keyword(to->glyphcheck, pdf_glyphcheck_keylist));
+
+ convflags |= PDC_CONV_LOGGING;
+ }
+ }
+
+ /* text is passed through for CID fonts with non Unicode CMap */
+ if (currfont->passthrough)
+ {
+ if (logg2)
+ pdc_logg(p->pdc, "\t\ttext is passed through as is\n");
+
+ outtext = (pdc_byte *) ((flags & PDF_USE_TMPALLOC) ?
+ pdc_malloc_tmp(p->pdc, (size_t) len + 2, fn, NULL, NULL) :
+ pdc_malloc(p->pdc, (size_t) len + 2, fn));
+ memcpy(outtext, text, (size_t) len);
+ outtext[len] = 0;
+ outtext[len + 1] = 0;
+ *outlen = len;
+
+ outtextformat = pdc_bytes;
+ }
+ else
+ {
+
+ if (flags & PDF_FORCE_NEWALLOC)
+ convflags |= PDC_CONV_NEWALLOC;
+
+ if (flags & PDF_USE_TMPALLOC)
+ convflags |= PDC_CONV_TMPALLOC;
+
+ if (to->glyphcheck == (int) text_replace)
+ {
+ replchar = currfont->replacementchar;
+ }
+ else
+ {
+ replchar = (int) to->glyphcheck;
+ if (to->glyphcheck == text_error)
+ {
+ convflags |= PDC_CONV_ENCERROR;
+ if (flags & PDF_KEEP_CONTROL)
+ convflags |= PDC_CONV_KEEPLBCHAR;
+ }
+ }
+
+ if (flags & PDF_KEEP_UNICODE || to->glyphcheck != text_nocheck)
+ inev = ev;
+
+
+ /* "Pass through mode" for 8-bit text.
+ * The encoding vector must be specified, because
+ * the text could emerge as a Unicode text due to a BOM.
+ */
+ if ((enc >= 0 && inev == NULL) ||
+ (enc == pdc_builtin && !(flags & PDF_KEEP_UNICODE)))
+ {
+ inev = ev;
+ outev = ev;
+ outtextformat = pdc_bytes;
+ }
+
+ /* input text format */
+ pdf_get_input_textformat(currfont, &intextformat, &convflags);
+
+ /* convert to 8-bit or UTF-16 text string */
+ if (pdc_convert_textstring(p->pdc, intextformat, currfont->codepage,
+ inev,
+ NULL, 0,
+ replchar, intext, len,
+ &outtextformat, outev, &outtext, outlen,
+ convflags, pdc_false))
+ {
+ if (newlen > -1)
+ pdc_free_tmp(p->pdc, intext);
+ goto PDF_CHECK_TEXT_ERROR;
+ }
+ }
+
+ if (newlen > -1)
+ pdc_free_tmp(p->pdc, intext);
+
+ /* check text string */
+ if (outtext != NULL && *outlen)
+ {
+ pdc_ushort *usouttext = (pdc_ushort *) outtext;
+ pdc_ushort uvlist[PDC_MAX_UVLIST];
+ pdc_ushort cglist[PDC_MAX_UVLIST];
+ pdc_bool kcheck = pdc_true;
+ int i = 0, nv = 1, icp = 0, usvp;
+ int code, gid, usv, ic;
+
+ (void) i;
+
+ /* storage length of a character */
+ if (outtextformat == pdc_utf16)
+ {
+ charlen = 2;
+ newcharlen = 2;
+ }
+
+ /* maximal text string length - found out emprirically! */
+ maxlen = (currfont->codesize == 1) ? PDF_MAXARRAYSIZE : PDF_MAXDICTSIZE;
+ if (!(flags & PDF_KEEP_TEXTLEN) && *outlen > maxlen)
+ {
+ pdc_set_errmsg(p->pdc, PDF_E_TEXT_TOOLONG,
+ pdc_errprintf(p->pdc, "%d", maxlen), 0, 0, 0);
+ goto PDF_CHECK_TEXT_ERROR;
+ }
+
+ len = *outlen / charlen;
+ switch (enc)
+ {
+
+
+ /*
+ * builtin
+ */
+ case pdc_builtin:
+ if (charlen == 1 || !(flags & PDF_KEEP_UNICODE))
+ newcharlen = 1;
+ for (ic = 0; ic < len; ic++)
+ {
+ if (charlen == 1)
+ code = (int) outtext[ic];
+ else
+ code = (int) usouttext[ic];
+
+ if (code)
+ {
+ gid = fnt_get_glyphid(code, &currfont->ft);
+
+ /* glyph id for code value not available */
+ if (gid <= 0)
+ {
+ unmapgids++;
+ if (to->glyphcheck == text_error)
+ {
+ pdc_set_errmsg(p->pdc, PDF_E_FONT_CODENOTFOUND1,
+ pdc_errprintf(p->pdc, "x%02X", code),
+ currfont->ft.name, 0, 0);
+ goto PDF_CHECK_TEXT_ERROR;
+ }
+ else if (to->glyphcheck == text_replace)
+ {
+ pdc_warning(p->pdc, PDF_E_FONT_CODENOTFOUNDREP1,
+ pdc_errprintf(p->pdc, "x%02X", code),
+ currfont->ft.name, 0, 0);
+ code = currfont->replacementcode;
+ }
+ }
+ }
+
+ if (newcharlen == 1)
+ outtext[icp] = (pdc_byte) code;
+ else
+ usouttext[icp] = (pdc_ushort) code;
+ icp++;
+ }
+
+ break;
+
+
+ /*
+ * cid
+ */
+ case pdc_cid:
+ /*
+ * pass through. check and temporary conversion in
+ * pdf_calculate_textsize(), because we want to keep native code.
+ */
+ break;
+
+
+ /*
+ * encoding vector
+ */
+ default:
+ if (charlen == 1 || !(flags & PDF_KEEP_UNICODE))
+ newcharlen = 1;
+ for (ic = 0; ic < len; ic++)
+ {
+ if (charlen == 1)
+ {
+ code = (int) outtext[ic];
+ usv = ev->codes[code];
+ kcheck = code > 0;
+ }
+ else
+ {
+ usv = (int) usouttext[ic];
+ code = pdc_get_encoding_bytecode(p->pdc, ev,
+ (pdc_ushort) usv);
+ if (code < 0)
+ code = 0;
+ kcheck = usv > 0;
+ }
+
+ if ((flags & PDF_KEEP_CONTROL) &&
+ pdc_is_linebreaking_relchar((pdc_ushort) usv))
+ {
+ kcheck = pdc_false;
+ }
+
+ /* glyph check */
+ if (kcheck)
+ {
+ /* encoding vector hasn't defined [Uni]code */
+ if (usv <= 0 || code <= 0)
+ {
+ unmapgids++;
+ if (to->glyphcheck == text_error)
+ {
+ if (usv <= 0)
+ {
+ pdc_set_errmsg(p->pdc, PDC_E_ENC_NOTDEF_CODE,
+ pdc_errprintf(p->pdc, "x%02X", code),
+ ev->apiname, 0, 0);
+ goto PDF_CHECK_TEXT_ERROR;
+ }
+ else if (code <= 0)
+ {
+ pdc_set_errmsg(p->pdc, PDC_E_ENC_NOTDEF_UNICODE,
+ pdc_errprintf(p->pdc, "%04X", usv),
+ ev->apiname, 0, 0);
+ goto PDF_CHECK_TEXT_ERROR;
+ }
+ }
+ else if (to->glyphcheck == text_replace)
+ {
+ usvp = (usv <= 0) ? code : usv;
+ nv = pdf_get_approximate_uvlist(p, currfont, ev,
+ usv, pdc_true,
+ uvlist, cglist);
+ usv = (int) uvlist[0];
+ code = (int) cglist[0];
+
+ if (logg2)
+ {
+ pdf_logg_glyph_replacement(p, ic, usvp,
+ enc, charlen, uvlist, cglist, nv);
+ }
+ }
+ }
+ else
+ {
+ gid = fnt_get_glyphid(code, &currfont->ft);
+
+ /* glyph id for code not available */
+ if (gid <= 0 && currfont->gid0code != code)
+ {
+ unmapgids++;
+ if (to->glyphcheck == text_error)
+ {
+ pdc_set_errmsg(p->pdc, PDF_E_FONT_CODENOTFOUND2,
+ pdc_errprintf(p->pdc, "x%02X", code),
+ pdc_errprintf(p->pdc, "%04X", usv),
+ currfont->ft.name, 0);
+ goto PDF_CHECK_TEXT_ERROR;
+ }
+ else if (to->glyphcheck == text_replace)
+ {
+ pdc_warning(p->pdc, PDF_E_FONT_CODENOTFOUNDREP2,
+ pdc_errprintf(p->pdc, "x%02X", code),
+ pdc_errprintf(p->pdc, "%04X", usv),
+ currfont->ft.name, 0);
+
+ usvp = (usv <= 0) ? code : usv;
+ nv = pdf_get_approximate_uvlist(p, currfont, ev,
+ usv, pdc_true,
+ uvlist, cglist);
+ usv = (int) uvlist[0];
+ code = (int) cglist[0];
+
+ if (logg2)
+ {
+ pdf_logg_glyph_replacement(p, ic, usvp,
+ enc, charlen, uvlist, cglist, nv);
+ }
+ }
+ }
+ }
+ }
+
+ if (newcharlen == 1)
+ {
+ outtext[icp] = (pdc_byte) code;
+ }
+ else
+ {
+ usouttext[icp] = (pdc_ushort) usv;
+ }
+ if (nv > 1)
+ {
+ if (newcharlen == 1)
+ ligatlist = pdf_register_ligat(p, ligatlist, icp, nv,
+ cglist, newcharlen);
+ else
+ ligatlist = pdf_register_ligat(p, ligatlist,
+ icp, nv, uvlist, newcharlen);
+ nv = 1;
+ }
+ icp++;
+ }
+
+ break;
+ }
+
+ if (icp)
+ {
+ /* calculate complete text length */
+ len = newcharlen * icp;
+ if (ligatlist != NULL)
+ {
+ ligat = ligatlist;
+ while (ligat != NULL)
+ {
+ len += ligat->nb;
+ ligat = ligat->next;
+ }
+ }
+
+ if (len != *outlen)
+ {
+ *outlen = len;
+ if (flags & PDF_USE_TMPALLOC)
+ outtext = (pdc_byte *) pdc_realloc_tmp(p->pdc, outtext,
+ (size_t) (*outlen + newcharlen), fn);
+ else
+ outtext = (pdc_byte *) pdc_realloc(p->pdc, outtext,
+ (size_t) (*outlen + newcharlen), fn);
+ outtext[*outlen] = 0;
+ if (newcharlen == 2)
+ outtext[*outlen + 1] = 0;
+ }
+
+ /* insert ligature parts */
+ if (ligatlist != NULL)
+ {
+ int nbrest, nbgap, nbmove = 0;
+
+ len = newcharlen * icp;
+ ligat = ligatlist;
+ while (ligat != NULL)
+ {
+ nbgap = ligat->nb;
+ icp = ligat->icp + nbmove;
+ nbrest = len - icp;
+ icp += newcharlen;
+ ic = icp + nbgap;
+
+ memmove(&outtext[ic], &outtext[icp], (size_t) nbrest);
+ memcpy(&outtext[icp], ligat->culist, (size_t) nbgap);
+
+ nbmove += nbgap;
+ len += nbgap;
+
+ ligat = ligat->next;
+ }
+
+ pdf_cleanup_ligat(p, ligatlist);
+ }
+ }
+ }
+
+ *outtext_p = outtext;
+ *outcharlen = newcharlen;
+
+ if (logg1)
+ {
+ if (logg2)
+ pdc_logg_hexdump(p->pdc, "converted text", "\t\t",
+ (char *) outtext, *outlen);
+ else
+ pdc_logg(p->pdc,
+ "\tChecked and converted text of length %d: \"%T\"\n",
+ *outlen, outtext, *outlen);
+ }
+ return unmapgids;
+
+ PDF_CHECK_TEXT_ERROR:
+
+ if (outtext != NULL)
+ {
+ if (flags & PDF_USE_TMPALLOC)
+ pdc_free_tmp(p->pdc, outtext);
+ else
+ pdc_free(p->pdc, outtext);
+ }
+
+ pdf_cleanup_ligat(p, ligatlist);
+
+ if (verbose)
+ pdc_error(p->pdc, -1, 0, 0, 0, 0);
+
+ *outtext_p = NULL;
+ *outlen = 0;
+
+ return -1;
+}
+
+
+/* ------------------------ Text width functions ------------------------ */
+
+/* Calculates the geometrical width and height of input text string
+ * depending on
+ *
+ * to->font, to->fontsize, to->kerning,
+ * to->charspacing, to->horizscaling, to->wordspacing,
+ * to->xadvancelist
+ *
+ * In the case of vertical writing mode the width is the maximum
+ * of all glyph widths and height the height of the text string.
+ *
+ */
+
+pdc_scalar
+pdf_calculate_textsize(PDF *p, const pdc_byte *text, int len, int charlen,
+ pdf_text_options *to, int breakchar, pdc_scalar *height,
+ pdc_bool verbose)
+{
+ pdf_font *currfont = &p->fonts[to->font];
+ pdc_encoding enc = currfont->ft.enc;
+ pdc_byte *tmpstring = (pdc_byte *) text;
+ pdc_ushort *ustext = (pdc_ushort *) text;
+ pdc_scalar glwidth = 0, width = 0;
+ pdc_scalar font2user = to->fontsize / 1000.0;
+ pdc_bool kbreak = pdc_false;
+ int usv, ic, icc, numglyphs = 0, numspaces = 0;
+
+ /* We cannot handle empty strings or fonts without widths info */
+ if (!len || currfont->widthsmissing)
+ {
+ if (height)
+ *height = 0.0;
+ return width;
+ }
+
+ if (enc != pdc_cid)
+ len /= charlen;
+
+ for (ic = 0; ic < len; )
+ {
+ icc = ic;
+
+ {
+ if (charlen == 1)
+ {
+ usv = (int) text[ic];
+ }
+ else if (enc == pdc_unicode)
+ {
+ usv = pdc_char16_to_char32(p->pdc, ustext, &ic, len, verbose);
+ }
+ else
+ {
+ usv = (int) ustext[ic];
+ }
+
+ /* count spaces */
+ if (usv == (int) currfont->ft.spacechar)
+ numspaces++;
+
+ /* break character */
+ if (breakchar > 0)
+ kbreak = (usv == breakchar);
+
+ ic++;
+ }
+
+ /* start by adding in the width of the character */
+ if (currfont->opt.monospace)
+ {
+ glwidth = (pdc_scalar) currfont->opt.monospace;
+ }
+ else
+ {
+ glwidth = (pdc_scalar) fnt_get_glyphwidth(usv, &currfont->ft);
+ if (glwidth == FNT_MISSING_WIDTH)
+ glwidth = currfont->ft.m.defwidth;
+ }
+
+ /* count glyphs */
+ numglyphs++;
+
+ /* horizontal or vertical writing mode */
+ if (!currfont->ft.vertical)
+ {
+ width += glwidth;
+
+
+ /* supplied glyph widths */
+ if (icc < to->nglyphs)
+ {
+ pdc_scalar shift = to->xadvancelist[icc] / font2user - glwidth;
+ width += shift;
+ if (p->pdc->ptfrun)
+ shift = PDC_ROUND(1e10 * shift) / 1e10;
+ shift = PDC_ROUND(1e1 * shift) / 1e1;
+ to->xadvancelist[icc] = shift;
+ }
+ }
+ else
+ {
+ /* maximum of width */
+ if (glwidth > width)
+ width = glwidth;
+ }
+
+ /* length of text part ranging to decimal character */
+ if (kbreak)
+ break;
+ }
+
+ if (breakchar > 0 && !kbreak)
+ return 0;
+
+ /* charspacing and wordspacing */
+ if (!currfont->ft.vertical)
+ {
+ if (to->charspacing)
+ width += numglyphs * to->charspacing / font2user;
+ if (to->wordspacing)
+ width += numspaces * to->wordspacing / font2user;
+ if (height)
+ *height = 0.0;
+ }
+ else
+ {
+ /* height for positive y direction.
+ * Acrobat calculates with negative direction (see pdf_place_text).
+ */
+ *height = numglyphs * (to->fontsize + -to->charspacing) +
+ numspaces * (-to->wordspacing);
+ }
+
+ /* take horizontal scaling factor and font scaling factor into account */
+ width *= font2user * to->horizscaling;
+
+ if (tmpstring != text)
+ pdc_free_tmp(p->pdc, tmpstring);
+
+ return width;
+
+}
+
+pdc_scalar
+pdf_trim_textwidth(pdc_scalar width, pdf_text_options *to)
+{
+ if (!PDC_FLOAT_ISNULL(width))
+ width -= to->horizscaling * to->charspacing;
+
+ return width;
+}
+
+
+pdc_scalar
+pdf__stringwidth(PDF *p, const char *text, int len, int font,
+ pdc_scalar fontsize)
+{
+ pdc_byte *utext;
+ int charlen;
+ pdc_scalar width = 0, height = 0;
+ pdf_text_options to = *p->curr_ppt->currto;
+
+ if (text && len == 0)
+ len = (int) strlen(text);
+ if (text == NULL || len <= 0)
+ return width;
+
+ pdf_check_handle(p, font, pdc_fonthandle);
+
+ pdc_check_number_zero(p->pdc, "fontsize", fontsize);
+
+ /* convert text string */
+ to.font = font;
+ to.fontsize = fontsize;
+ pdf_check_textstring(p, text, len, PDF_KEEP_TEXTLEN | PDF_USE_TMPALLOC,
+ &to, &utext, &len, &charlen, pdc_true);
+ if (utext && len)
+ width = pdf_calculate_textsize(p, utext, len, charlen,
+ &to, -1, &height, pdc_true);
+
+ return width;
+}
+
+
+/* ------------------------ Text output functions ------------------------ */
+
+static void
+pdf_convert_text_towinansi(PDF *p, const pdc_byte *fromtext, int len,
+ pdc_byte *totext, pdf_font *currfont)
+{
+ pdc_encodingvector *evfrom =
+ pdc_get_encoding_vector(p->pdc, currfont->ft.enc);
+ pdc_encodingvector *evto =
+ pdc_get_encoding_vector(p->pdc, currfont->towinansi);
+ int i;
+
+ for (i = 0; i < len; i++)
+ totext[i] = pdc_transform_bytecode(p->pdc, evto, evfrom, fromtext[i]);
+}
+
+void
+pdf_put_fieldtext(PDF *p, const char *text, int font)
+{
+ if (pdc_is_utf8_bytecode(text))
+ {
+ pdf_put_hypertext(p, text);
+ }
+ else
+ {
+ static const char fn[] = "pdf_put_fieldtext";
+ pdf_font *currfont = &p->fonts[font];
+ char *tmpstring = (char *) text;
+ int len = (int) pdc_strlen(text);
+
+ if (len && currfont->towinansi != pdc_invalidenc &&
+ !pdc_is_utf16be_unicode(text))
+ {
+ /* Convert 8-bit code string to winansi */
+ tmpstring = (char *) pdc_malloc_tmp(p->pdc,
+ (size_t) len, fn, NULL, NULL);
+ pdf_convert_text_towinansi(p, (pdc_byte *) text, len,
+ (pdc_byte *) tmpstring, currfont);
+ }
+
+ pdc_put_pdfstring(p->out, tmpstring, len);
+ if (tmpstring != text)
+ pdc_free_tmp(p->pdc, tmpstring);
+ }
+}
+
+static void
+pdf_put_textstring(PDF *p, const pdc_byte *text, int len, int charlen,
+ pdf_font *currfont)
+{
+ static const char fn[] = "pdf_put_textstring";
+ pdc_byte *tmpstring = (pdc_byte *) text;
+
+ (void) charlen;
+
+ if (len)
+ {
+
+ /* Convert 8-bit code string to winansi */
+ if (currfont->towinansi != pdc_invalidenc)
+ {
+ tmpstring = (pdc_byte *) pdc_malloc_tmp(p->pdc,
+ (size_t) len, fn, NULL, NULL);
+ pdf_convert_text_towinansi(p, text, len, tmpstring, currfont);
+ }
+
+ }
+
+ pdc_put_pdfstring(p->out, (char *) tmpstring, len);
+ if (tmpstring != text)
+ pdc_free_tmp(p->pdc, tmpstring);
+}
+
+static void
+pdf_put_textstring_shift(PDF *p, pdc_byte *text, int len, int charlen,
+ pdf_text_options *to, pdc_scalar spaceshift)
+{
+ pdf_font *currfont = &p->fonts[to->font];
+ pdc_ushort *ustext = (pdc_ushort *) text;
+ pdc_byte *currtext;
+ pdc_scalar shift;
+ pdc_bool isutf16;
+ int currlen, nchars;
+ int leftchar = 0, rightchar;
+ int ic, icp, incr = charlen;
+
+ currlen = 0;
+ currtext = text;
+ nchars = len/charlen;
+ isutf16 = charlen == 2 &&
+ currfont->codesize == 2 &&
+ currfont->ft.enc == pdc_unicode;
+ for (ic = 0; ic < nchars; ic++)
+ {
+ if (charlen == 1)
+ {
+ rightchar = (int) text[ic];
+ }
+ else if(!isutf16)
+ {
+ rightchar = (int) ustext[ic];
+ }
+ else
+ {
+ icp = ic;
+ rightchar =
+ pdc_char16_to_char32(p->pdc, ustext, &ic, nchars, pdc_true);
+ incr = (1 + ic - icp) * charlen;
+ }
+
+ if (ic)
+ {
+ /* PDF wants the inverse shift amount
+ * (positive numbers move left, negative move right!) */
+
+ if (spaceshift != 0 && leftchar == (int) currfont->ft.spacechar)
+ shift = -spaceshift;
+ else
+ shift = 0;
+
+
+ if (ic <= to->nglyphs)
+ shift -= to->xadvancelist[ic-1];
+
+ if (shift)
+ {
+ pdf_put_textstring(p, currtext, currlen, charlen, currfont);
+ pdc_printf(p->out, "%f", shift);
+ currtext = &text[charlen * ic];
+ currlen = 0;
+ }
+ }
+ leftchar = rightchar;
+ currlen += incr;
+ }
+
+ pdf_put_textstring(p, currtext, currlen, charlen, currfont);
+
+ if (to->nglyphs && to->nglyphs >= nchars)
+ pdc_printf(p->out, "%f", -to->xadvancelist[nchars - 1]);
+
+}
+
+
+/* --------------------- General text placing function --------------------- */
+
+
+#define PDF_RENDERMODE_FILLCLIP 4
+#define PDF_ITALICANGLE_DEFAULT -12
+
+static void
+pdf_place_singletext(PDF *p, pdc_byte *text, int len, int charlen,
+ pdf_text_options *to, pdc_scalar tx, pdc_scalar ty,
+ pdc_scalar width, pdc_scalar height, pdc_scalar leading,
+ pdc_bool cont)
+{
+ pdf_tstate *ts = &p->curr_ppt->tstate[p->curr_ppt->sl];
+ pdf_font *currfont = &p->fonts[to->font];
+ pdc_scalar dx, dy, spaceshift = 0;
+ pdc_scalar font2user = to->fontsize / 1000.0;
+ pdc_scalar linewidth = 0;
+ pdc_scalar deflinewidth = 0;
+ pdc_bool hasdeco = to->underline || to->overline || to->strikeout;
+ pdc_bool takeTJ = pdc_false;
+
+ /* default linewidth for underlinewidth and strokewidth */
+ if (hasdeco || (to->mask & (1 << to_strokewidth)))
+ {
+ if (currfont->ft.m.underlineThickness == 0)
+ currfont->ft.m.underlineThickness = 50;
+ deflinewidth = fabs(to->horizscaling * font2user *
+ currfont->ft.m.underlineThickness);
+ }
+
+ /* fill and stroke color */
+ if (to->mask & (1 << to_fillcolor))
+ pdf_set_coloropt(p, (int) pdf_fill, &to->fillcolor);
+ if (to->mask & (1 << to_strokecolor))
+ pdf_set_coloropt(p, (int) pdf_stroke, &to->strokecolor);
+
+
+ /* stroke width and dasharray for stroked text */
+ if (to->mask & (1 << to_strokewidth))
+ {
+ if (to->strokewidth == PDF_UNDERLINEWIDTH_AUTO)
+ {
+ linewidth = deflinewidth;
+ }
+ else
+ {
+ linewidth = to->strokewidth;
+ if ((to->pcmask & (1 << to_strokewidth)))
+ linewidth *= fabs(to->fontsize);
+ }
+ pdf__setlinewidth(p, linewidth);
+ }
+ if (to->mask & (1 << to_dasharray))
+ pdf__setdash(p, to->dasharray[0], to->dasharray[1]);
+
+ /* text decoration */
+ if (width && hasdeco)
+ {
+ pdc_scalar scale = fabs(to->horizscaling);
+ pdc_scalar delta, fs, trise, lineheight;
+ pdc_scalar txe = 0, tye = 0;
+ pdc_scalar lineposition = 0;
+
+ fs = p->ydirection * font2user;
+ trise = p->ydirection * to->textrise;
+ lineheight = fs * currfont->ft.m.ascender;
+ delta = scale * (fs * currfont->ft.m.underlinePosition + trise);
+
+ pdf__save(p);
+
+ if (to->underlinewidth == PDF_UNDERLINEWIDTH_AUTO)
+ {
+ linewidth = deflinewidth;
+ }
+ else
+ {
+ linewidth = to->underlinewidth;
+ if ((to->pcmask & (1 << to_underlinewidth)))
+ linewidth *= fabs(to->fontsize);
+ }
+
+ if (to->underlineposition == PDF_UNDERLINEPOSITION_AUTO)
+ {
+ lineposition = delta;
+ }
+ else
+ {
+ lineposition = p->ydirection * to->underlineposition;
+ if ((to->pcmask & (1 << to_underlineposition)))
+ lineposition *= to->fontsize;
+ }
+
+ if (!currfont->ft.vertical)
+ {
+ txe = tx + width;
+ }
+ else
+ {
+ txe = tx - width / 2.0;
+ tye = ty - p->ydirection * height;
+ lineposition *= p->ydirection;
+ delta = fabs(delta);
+ }
+
+ pdf__setlinecap(p, 0);
+ if (!(to->mask & (1 << to_dasharray)))
+ pdf__setdash(p, 0, 0);
+
+ if (to->underline)
+ {
+ pdf__setlinewidth(p, linewidth);
+ if (!currfont->ft.vertical)
+ {
+ pdf__moveto(p, tx, ty + lineposition);
+ pdf__lineto(p, txe, ty + lineposition);
+ }
+ else
+ {
+ pdf__moveto(p, txe + lineposition, ty);
+ pdf__lineto(p, txe + lineposition, tye);
+ }
+ pdf__stroke(p);
+ }
+
+ if (to->strikeout)
+ {
+ pdf__setlinewidth(p, deflinewidth);
+ if (!currfont->ft.vertical)
+ {
+ pdf__moveto(p, tx, ty + lineheight/2 + delta);
+ pdf__lineto(p, txe, ty + lineheight/2 + delta);
+ }
+ else
+ {
+ pdf__moveto(p, tx, ty);
+ pdf__lineto(p, tx, tye);
+ }
+ pdf__stroke(p);
+ }
+
+ if (to->overline)
+ {
+ pdf__setlinewidth(p, deflinewidth);
+ if (!currfont->ft.vertical)
+ {
+ delta = scale * (fs * currfont->ft.m.underlinePosition - trise);
+ pdf__moveto(p, tx, ty + lineheight - delta);
+ pdf__lineto(p, txe, ty + lineheight - delta);
+ }
+ else
+ {
+ pdf__moveto(p, txe + width + delta, ty);
+ pdf__lineto(p, txe + width + delta, tye);
+ }
+ pdf__stroke(p);
+ }
+
+ pdf__restore(p);
+ }
+
+
+
+ /* wordspacing */
+ if (!PDC_FLOAT_ISNULL(to->wordspacing))
+ {
+ spaceshift = to->wordspacing / font2user;
+ if (p->pdc->ptfrun)
+ spaceshift = PDC_ROUND(1e10 * spaceshift) / 1e10;
+ spaceshift = PDC_ROUND(1e1 * spaceshift) / 1e1;
+ takeTJ = PDC_FLOAT_ISNULL(spaceshift) ? pdc_false : pdc_true;
+ }
+
+
+ /* supplied glyph widths */
+ if (!takeTJ)
+ takeTJ = to->nglyphs;
+
+ /* begin text object */
+ pdf_begin_text(p);
+
+ /* italic angle - realized by Tm operator */
+ if (!PDC_FLOAT_ISNULL(to->italicangle) ||
+ currfont->metricflags & font_italic)
+ {
+ if (!currfont->ft.vertical)
+ {
+ pdc_scalar italicangle = -p->ydirection * to->italicangle;
+
+ if (currfont->metricflags & font_italic && italicangle == 0)
+ italicangle = -p->ydirection * PDF_ITALICANGLE_DEFAULT;
+
+ if (ts->hs < 0)
+ italicangle = -italicangle;
+
+ pdc_printf(p->out, "1 0 %f 1 %f %f Tm\n",
+ tan(italicangle * PDC_DEG2RAD), tx, ty);
+
+ cont = pdc_false;
+ ts->newpos = pdc_false;
+ ts->refptx = tx;
+ ts->refpty = ty;
+ }
+ else
+ {
+ pdc_error(p->pdc, PDF_E_TEXT_ITALUNSUPP, 0, 0, 0, 0);
+ }
+ }
+ else
+ {
+ /* components of text displacement vector */
+ if (!cont)
+ {
+ dx = tx - ts->prevtx;
+ dy = ty - ts->prevty;
+ }
+ else
+ {
+ dx = tx - ts->refptx;
+ dy = ty - ts->refpty + leading;
+ }
+
+ /* condition for text displacement operator Td */
+ if (!PDC_FLOAT_ISNULL(dx) || !PDC_FLOAT_ISNULL(dy) ||
+ ts->newpos || (cont && takeTJ))
+ {
+ if (cont)
+ {
+ dy -= leading;
+ cont = pdc_false;
+ }
+ pdc_printf(p->out, "%f %f Td\n", dx, dy);
+
+ /* new reference position for next line */
+ ts->newpos = pdc_false;
+ ts->refptx = tx;
+ ts->refpty = ty;
+ }
+ else
+ {
+ ts->refpty -= leading;
+ }
+ }
+
+ /* show text */
+ if (!takeTJ)
+ {
+ pdf_put_textstring(p, text, len, charlen, currfont);
+ if (!cont)
+ pdc_puts(p->out, "Tj\n");
+ else
+ pdc_puts(p->out, "'\n");
+ }
+ else
+ {
+ pdc_puts(p->out, "[");
+ pdf_put_textstring_shift(p, text, len, charlen, to, spaceshift);
+ pdc_puts(p->out, "]TJ\n");
+ }
+
+ /* new text position */
+ if (!currfont->ft.vertical)
+ {
+ ts->currtx = tx + width;
+ ts->currty = ty;
+ }
+ else
+ {
+ ts->currtx = tx;
+ ts->currty = ty - p->ydirection * height;
+ }
+ ts->prevtx = ts->currtx;
+ ts->prevty = ts->currty;
+
+ if (to->textrendering >= PDF_RENDERMODE_FILLCLIP)
+ pdf_end_text(p);
+}
+
+#define PDF_FAKEBOLD_OFFSET 0.03 /* 3% of font size */
+
+void
+pdf_place_text(PDF *p, pdc_byte *text, int len, int charlen,
+ pdf_text_options *to, pdc_scalar width, pdc_scalar height,
+ pdc_bool cont)
+{
+ pdf_tstate *ts = &p->curr_ppt->tstate[p->curr_ppt->sl];
+ pdf_font *currfont = &p->fonts[to->font];
+ pdc_scalar tx, ty, leading = 0;
+
+ /* text position */
+ if (!cont)
+ {
+ tx = ts->currtx;
+ ty = ts->currty;
+ }
+ else
+ {
+ leading = p->ydirection * to->leading;
+ tx = ts->linetx;
+ ty = ts->currty - leading;
+ }
+
+ pdf_place_singletext(p, text, len, charlen, to, tx, ty,
+ width, height, leading, cont);
+
+ /* text bolding */
+ if (to->fakebold || currfont->metricflags & font_bold)
+ {
+ static const pdc_scalar fx[] = {0, 0.70711, 1};
+ static const pdc_scalar fy[] = {1, 0.70711, 0};
+ pdc_scalar offset, currtx, currty, linetx;
+ int it, nt = 3;
+
+ offset = PDF_FAKEBOLD_OFFSET * to->fontsize;
+
+ linetx = ts->linetx;
+ currtx = ts->currtx;
+ currty = ts->currty;
+ for (it = 0; it < nt; it++)
+ {
+ pdf__set_text_pos(p, tx + fx[it] * offset,
+ ty + p->ydirection * fy[it] * offset);
+ pdf_place_singletext(p, text, len, charlen, to,
+ ts->currtx, ts->currty,
+ width, height, leading, pdc_false);
+ }
+ pdf__set_text_pos(p, currtx, currty);
+ ts->linetx = linetx;
+ }
+}
+
+/* --------------------- Simple text showing functions --------------------- */
+
+void
+pdf__show_text(
+ PDF *p,
+ const char *text,
+ int len,
+ pdc_bool cont)
+{
+ static const char *fn = "pdf__show_text";
+ pdf_text_options *currto = p->curr_ppt->currto;
+ pdc_byte *utext = NULL;
+ int charlen = 1;
+ pdc_scalar width = 0, height = 0;
+
+ if (text && len == 0)
+ len = (int) strlen(text);
+ if (text == NULL || len <= 0)
+ {
+ if (cont)
+ len = 0;
+ else
+ return;
+ }
+
+ /* no font set */
+ if (currto->font == -1)
+ pdc_error(p->pdc, PDF_E_TEXT_NOFONT, 0, 0, 0, 0);
+
+ if (len)
+ {
+ /* convert text string */
+ pdf_check_textstring(p, text, len, PDF_USE_TMPALLOC,
+ currto, &utext, &len, &charlen, pdc_true);
+ if (utext == NULL || (!cont && !len))
+ return;
+
+ /* width and height of text string */
+ width = pdf_calculate_textsize(p, utext, len, charlen,
+ currto, -1, &height, pdc_true);
+ }
+ else
+ {
+ utext = (pdc_byte *) pdc_calloc_tmp(p->pdc, 2, fn, NULL, NULL);
+ }
+
+
+ /* place text */
+ pdf_place_text(p, utext, len, charlen, currto, width, height, cont);
+}
+
+
+/* ---------- Text showing function with explicit glyph widths ---------- */
+
+void
+pdf__xshow(PDF *p, const char *text, int len, const pdc_scalar *xadvancelist)
+{
+ static const char *fn = "pdf__xshow";
+ pdf_text_options *currto = p->curr_ppt->currto;
+ pdc_byte *utext = NULL;
+ int charlen = 1;
+ size_t nbytes = 0;
+ pdc_scalar width, height;
+
+ if (text && len == 0)
+ len = (int) strlen(text);
+ if (text == NULL || !len)
+ return;
+
+ /* no font set */
+ if (currto->font == -1)
+ pdc_error(p->pdc, PDF_E_TEXT_NOFONT, 0, 0, 0, 0);
+
+ /* convert text string */
+ pdf_check_textstring(p, text, len, PDF_USE_TMPALLOC,
+ currto, &utext, &len, &charlen, pdc_true);
+ if (utext == NULL || !len)
+ return;
+
+ /* allocating glyph widths arrays */
+ nbytes = (size_t) (len / charlen) * sizeof(pdc_scalar);
+ currto->xadvancelist = (pdc_scalar *) pdc_malloc_tmp(p->pdc,
+ nbytes, fn, NULL, NULL);
+ memcpy(currto->xadvancelist, xadvancelist, nbytes);
+ currto->nglyphs = len / charlen;
+
+ /* length of text */
+ width = pdf_calculate_textsize(p, utext, len, charlen,
+ currto, -1, &height, pdc_true);
+
+
+ /* place text */
+ pdf_place_text(p, utext, len, charlen, currto, width, height, pdc_false);
+
+ currto->xadvancelist = NULL;
+ currto->nglyphs = 0;
+}
+
+
+/* --------------------------- Leader functions ---------------------------- */
+
+
+
+/* ----------------------- Text fitting function ------------------------ */
+
+struct pdf_fittext_s
+{
+ pdc_vector start; /* text start position */
+ pdc_vector end; /* text end position */
+ pdc_vector writingdir; /* unit vector of text writing direction */
+ pdc_vector perpendiculardir;/* unit vector perpendicular to writing dir. */
+ pdc_vector scale; /* x/y scaling */
+ pdc_scalar angle; /* rotation angle of writingdir in degree */
+ pdc_scalar width; /* textline width */
+ pdc_scalar height; /* textline height */
+ pdc_scalar mwidth; /* textline width with margins */
+ pdc_scalar mheight; /* textline height with margins */
+ pdc_scalar ascender; /* textline ascender */
+ pdc_scalar capheight; /* textline capheight */
+ pdc_scalar xheight; /* textline xheight */
+ pdc_scalar descender; /* textline descender */
+};
+
+
+/* definitions of fit text options */
+static const pdc_defopt pdf_fit_textline_options[] =
+{
+ PDF_TEXT_OPTIONS
+
+ {"xadvancelist", pdc_scalarlist, PDC_OPT_NOZERO, 0, PDC_USHRT_MAX,
+ PDC_FLOAT_MIN, PDC_FLOAT_MAX, NULL},
+
+ PDF_FIT_OPTIONS1
+ PDF_FIT_OPTIONS2
+ PDF_FIT_OPTIONS6
+
+ PDF_FONT_OPTIONS1
+ PDF_FONT_OPTIONS2
+
+ PDF_ERRORPOLICY_OPTION
+ PDC_OPT_TERMINATE
+};
+
+pdc_resopt *
+pdf_parse_fittextline_optlist(PDF *p, pdf_text_options *to,
+ pdf_fit_options *fit, const char *optlist)
+{
+ pdc_resopt *resopts = NULL;
+ pdf_font_options fo;
+
+ /* *to must be initialized */
+
+ /* initialize fit options */
+ pdf_init_fit_options(p, pdc_false, fit);
+ fit->flags |= is_textline;
+
+ /* initialize font options */
+ pdf_init_font_options(p, &fo);
+ fo.flags |= is_textline;
+
+ /* parsing option list */
+ if (optlist && strlen(optlist))
+ {
+ pdc_clientdata data;
+
+ pdf_set_clientdata(p, &data);
+ resopts = pdc_parse_optionlist(p->pdc, optlist,
+ pdf_fit_textline_options, &data, pdc_true);
+
+ pdf_get_font_options(p, &fo, resopts);
+ pdf_get_text_options(p, to, resopts);
+ pdf_get_fit_options(p, pdc_false, fit, resopts);
+ }
+
+ /* font options specified */
+ if (fo.mask & (1 << fo_fontname) && fo.mask & (1 << fo_encoding))
+ {
+ to->font = pdf_load_font_internal(p, &fo);
+ to->mask |= (1L << to_font);
+ to->fontset |= (1L << to_font);
+ }
+ else
+ {
+ pdf_cleanup_font_options(p, &fo);
+ }
+
+ return resopts;
+}
+
+static pdc_bool
+pdf_parse_textline_options(PDF *p, const char *text, int len,
+ pdf_text_options *to, pdf_fit_options *fit,
+ const char *optlist)
+{
+ pdf_ppt *ppt = p->curr_ppt;
+
+ if (text && len == 0)
+ len = (int) strlen(text);
+ if (text == NULL || len <= 0)
+ return pdc_false;
+
+ /* initialize text options */
+ *to = *ppt->currto;
+ to->text = (char *) text;
+ to->textlen = len;
+
+ /* parsing option list */
+ pdf_parse_fittextline_optlist(p, to, fit, optlist);
+
+ /* no font set */
+ if (to->font == -1)
+ pdc_error(p->pdc, PDF_E_TEXT_NOFONT, 0, 0, 0, 0);
+
+ /* no font size set */
+ if (to->fontsize == PDC_FLOAT_MIN)
+ {
+ pdc_error(p->pdc, PDF_E_TEXT_NOFONTSIZESET, 0, 0, 0, 0);
+ }
+
+ return pdc_true;
+}
+
+int
+pdf_fit_textline_internal(PDF *p, pdf_fittext *fitres,
+ pdf_text_options *to, pdf_fit_options *fit,
+ pdc_matrix *matrix)
+{
+ pdc_bool logg5 = pdc_logg_is_enabled(p->pdc, 5, trc_text);
+ pdf_ppt *ppt = p->curr_ppt;
+ pdf_font *currfont = &p->fonts[to->font];
+ pdc_byte *utext = (pdc_byte *) "";
+ int len, charlen;
+ pdc_bool blind = (fitres != NULL) ? pdc_true : pdc_false;
+ pdc_bool vertical = currfont->ft.vertical;
+
+ pdc_matrix ctm = ppt->gstate[ppt->sl].ctm;
+ pdc_matrix m, mm;
+ pdc_vector elemsize, elemscale, elemmargin, textrelpos, fitrelpos;
+ pdc_vector polyline[5];
+ pdc_scalar textyextent[2];
+ pdc_box fitbox, elembox;
+ pdc_scalar ss, width, height, boxwidth, boxheight, fontsizeref;
+ pdc_scalar ascender, capheight, xheight, descender;
+ pdc_scalar x, y, tx = 0, ty = 0, basey = 0;
+ pdc_bool hasfitbox = pdc_false;
+ pdc_scalar font2user;
+ int indangle = fit->orientate / 90;
+ int unmapgids = 0, i;
+
+ (void) ppt;
+
+ /* box size */
+ boxwidth = fit->boxsize[0];
+ boxheight = fit->boxsize[1];
+
+ /* reference for font size as percentage */
+ if (indangle % 2)
+ fontsizeref = boxwidth;
+ else
+ fontsizeref = boxheight;
+
+ /* calculate and set text options */
+ pdf_calculate_text_options(p, to, pdc_false, 1.0, PDC_FLOAT_PREC,
+ fontsizeref);
+ if (!blind)
+ pdf_set_text_options(p, to);
+
+ /* convert text string */
+ unmapgids = pdf_check_textstring(p, to->text, to->textlen, PDF_USE_TMPALLOC,
+ to, &utext, &len, &charlen, pdc_true);
+ if (utext == NULL || !len)
+ return -1;
+
+ if (to->nglyphs && len/charlen != to->nglyphs)
+ pdc_warning(p->pdc, PDF_E_TEXT_SIZENOMATCH,
+ pdc_errprintf(p->pdc, "%d", to->nglyphs),
+ pdc_errprintf(p->pdc, "%d", len/charlen), 0, 0);
+
+ /* width and height of text */
+ width = pdf_calculate_textsize(p, utext, len, charlen,
+ to, -1, &height, pdc_true);
+ width = pdf_trim_textwidth(width, to);
+ if (PDC_FLOAT_ISNULL(width))
+ return -1;
+
+ /* font specifics */
+ font2user = to->fontsize / 1000.0;
+ ascender = font2user * currfont->ft.m.ascender;
+ capheight = font2user * currfont->ft.m.capHeight;
+ xheight = font2user * currfont->ft.m.xHeight;
+ descender = font2user * currfont->ft.m.descender;
+
+ /* margin lower left corner */
+ elemmargin.x = fit->margin[0];
+ elemmargin.y = fit->margin[1];
+
+ /* new box size */
+ boxwidth -= 2 * elemmargin.x;
+ if (boxwidth < 0)
+ boxwidth = 0;
+ boxheight -= 2 * elemmargin.y;
+ if (boxheight < 0)
+ boxheight = 0;
+ hasfitbox = boxwidth > PDC_FLOAT_PREC && boxheight > PDC_FLOAT_PREC;
+
+ /* kind of text box */
+ pdf_get_mbox_boxheight(p, fit->matchbox, textyextent);
+
+ /* text x size */
+ elemsize.x = width;
+
+ /* TODO: for vertical text */
+ /* text y size */
+ if (!vertical)
+ {
+ height = 0;
+ for (i = 0; i < 2; i++)
+ {
+ basey = 0;
+ if (textyextent[i] <= 0)
+ {
+ switch ((int) textyextent[i])
+ {
+ case text_none:
+ break;
+
+ case text_ascender:
+ basey = ascender;
+ break;
+
+ case text_capheight:
+ basey = capheight;
+ break;
+
+ case text_xheight:
+ basey = xheight;
+ break;
+
+ case text_descender:
+ basey = -descender;
+ break;
+
+ case text_textrise:
+ basey = to->textrise;
+ break;
+
+ case text_fontsize:
+ basey = to->fontsize;
+ break;
+
+ case text_leading:
+ basey = to->leading;
+ break;
+ }
+ }
+ else
+ {
+ basey = textyextent[i];
+ }
+ height += basey;
+ }
+ }
+ elemsize.y = height;
+
+ /* orientation */
+ if (indangle % 2)
+ {
+ ss = elemsize.x;
+ elemsize.x = elemsize.y;
+ elemsize.y = ss;
+ }
+
+ /* box for fitting */
+ fitbox.ll.x = 0;
+ fitbox.ll.y = 0;
+ fitbox.ur.x = boxwidth;
+ fitbox.ur.y = boxheight;
+
+ /* relativ position in fit and text box */
+ fitrelpos.x = fit->position[0] / 100.0;
+ fitrelpos.y = fit->position[1] / 100.0;
+ textrelpos = fitrelpos;
+
+ /* decimal character and position */
+ if (fit->alignchar)
+ {
+ pdc_encoding enc = currfont->ft.enc;
+ pdc_encodingvector *ev = pdc_get_encoding_vector(p->pdc, enc);
+ pdc_scalar decwidth, decheight, pos1, pos2;
+ int poschar = (int) fit->alignchar;
+
+ switch(enc)
+ {
+ case pdc_cid:
+ if (currfont->codesize != 2)
+ poschar = -1;
+ break;
+
+ case pdc_glyphid:
+ poschar = fnt_get_glyphid(poschar, &currfont->ft);
+ break;
+
+ case pdc_builtin:
+ poschar = -1;
+ break;
+
+ default:
+ if (ev != NULL && charlen == 1)
+ {
+ poschar = pdc_get_encoding_bytecode(p->pdc, ev,
+ (pdc_ushort) poschar);
+ }
+ break;
+ }
+
+ /* width and height of text part ranging to decimal character */
+ decwidth = pdf_calculate_textsize(p, utext, len, charlen,
+ to, poschar, &decheight, pdc_true);
+
+ /* position found */
+ if (decwidth > 0)
+ {
+ /* relative position of position character */
+ pos1 = decwidth / width;
+ pos2 = 1 - pos1;
+ i = vertical ? ((indangle + 3) % 4) : indangle;
+ switch (i)
+ {
+ case 0:
+ textrelpos.x = pos1;
+ break;
+
+ case 1:
+ textrelpos.y = pos1;
+ break;
+
+ case 2:
+ textrelpos.x = pos2;
+ break;
+
+ case 3:
+ textrelpos.y = pos2;
+ break;
+ }
+ }
+ }
+
+ /* calculate image box */
+ pdc_place_element(fit->fitmethod, fit->shrinklimit, &fitbox, &fitrelpos,
+ &elemsize, &textrelpos, &elembox, &elemscale);
+
+ if (logg5)
+ pdc_logg(p->pdc,
+ "\t\tFitting input parameter:\n"
+ "\t\t\tfitmethod = %s\n"
+ "\t\t\tshrinklimit = %f\n"
+ "\t\t\trefpoint = %f, %f\n"
+ "\t\t\tboxsize = %f, %f\n"
+ "\t\t\tfitrelpos = %f, %f\n"
+ "\t\t\telemsize = %f, %f\n"
+ "\t\t\ttextrelpos = %f, %f\n"
+ "\t\tFitting output parameter:\n"
+ "\t\t\telembox = %f, %f, %f, %f\n"
+ "\t\t\telemscale = %f, %f\n",
+ pdc_get_keyword(fit->fitmethod, pdf_fitmethod_keylist),
+ fit->shrinklimit, fit->refpoint[0], fit->refpoint[1],
+ fitbox.ur.x, fitbox.ur.y, fitrelpos.x, fitrelpos.y,
+ elemsize.x, elemsize.y, textrelpos.x, textrelpos.y,
+ elembox.ll.x, elembox.ll.y, elembox.ur.x, elembox.ur.y,
+ elemscale.x, elemscale.y);
+
+ /* reference point */
+ pdc_translation_matrix(fit->refpoint[0], fit->refpoint[1], &mm);
+ if (matrix == NULL)
+ {
+ if (blind)
+ {
+ m = ctm;
+ pdc_multiply_matrix(&mm, &m);
+ }
+ else
+ m = mm;
+ }
+ else
+ {
+ m = *matrix;
+ pdc_multiply_matrix(&mm, &m);
+ }
+
+ /* optional rotation */
+ if (fabs(fit->rotate) > PDC_FLOAT_PREC)
+ {
+ pdc_rotation_matrix(p->ydirection * fit->rotate, &mm);
+ pdc_multiply_matrix(&mm, &m);
+ }
+
+ /* output after translation and rotation */
+ if (!blind)
+ {
+ /* new CTM */
+ if (fit->showborder ||
+ fit->fitmethod == pdc_clip || fit->fitmethod == pdc_slice)
+ {
+ pdf_concat_raw(p, &m);
+ pdc_identity_matrix(&m);
+ }
+
+ /* show border */
+ if (fit->showborder)
+ {
+ pdf__rect(p, elemmargin.x, p->ydirection * elemmargin.y,
+ boxwidth, boxheight);
+ pdf__rect(p, 0, 0, fit->boxsize[0], fit->boxsize[1]);
+ pdf__stroke(p);
+ }
+
+ /* clipping */
+ if (
+ (fit->fitmethod == pdc_clip || fit->fitmethod == pdc_slice))
+ {
+ pdc_scalar cw = fit->boxsize[0];
+ pdc_scalar ch = fit->boxsize[1];
+
+ if (cw < PDC_FLOAT_PREC)
+ cw = PDF_ACRO_MAXPAGE;
+ if (ch < PDC_FLOAT_PREC)
+ ch = PDF_ACRO_MAXPAGE;
+ pdf__rect(p, 0, 0, cw, ch);
+ pdf__clip(p);
+ }
+ }
+
+ /* reference point for elembox */
+ if (elemmargin.x > PDC_FLOAT_PREC || elemmargin.y > PDC_FLOAT_PREC)
+ {
+ tx = elemmargin.x;
+ if (boxwidth < PDC_FLOAT_PREC)
+ tx *= 1.0 - 2 * fitrelpos.x;
+ ty = elemmargin.y;
+ if (boxheight < PDC_FLOAT_PREC)
+ ty *= 1.0 - 2 * fitrelpos.y;
+
+ pdc_translation_matrix(tx, p->ydirection * ty, &mm);
+ pdc_multiply_matrix(&mm, &m);
+ }
+
+
+ /* translation of element box */
+ elembox.ll.y *= p->ydirection;
+ elembox.ur.y *= p->ydirection;
+ pdc_box2polyline(NULL, &elembox, polyline);
+ tx = polyline[indangle].x;
+ ty = polyline[indangle].y;
+ pdc_translation_matrix(tx, ty, &mm);
+ pdc_multiply_matrix(&mm, &m);
+ boxwidth = elembox.ur.x - elembox.ll.x;
+ boxheight = elembox.ur.y - elembox.ll.y;
+
+ /* orientation of text */
+ if (fit->orientate != 0)
+ {
+ pdc_rotation_matrix(p->ydirection * fit->orientate, &mm);
+ pdc_multiply_matrix(&mm, &m);
+ if (indangle % 2)
+ {
+ ss = elemscale.x;
+ elemscale.x = elemscale.y;
+ elemscale.y = ss;
+
+ ss = boxwidth;
+ boxwidth = p->ydirection * boxheight;
+ boxheight = p->ydirection * ss;
+
+ ss = elemmargin.x;
+ elemmargin.x = p->ydirection * elemmargin.y;
+ elemmargin.y = p->ydirection * ss;
+ }
+ }
+
+ /* matchbox */
+ if (!blind && fit->matchbox)
+ {
+ pdc_rectangle matchrect;
+
+ pdf_concat_raw(p, &m);
+ pdc_identity_matrix(&m);
+
+ matchrect.llx = 0;
+ matchrect.lly = 0;
+ matchrect.urx = boxwidth;
+ matchrect.ury = boxheight;
+
+ pdf_set_mbox_rectangle(p, fit->matchbox, &matchrect, 0);
+ pdf_draw_mbox_rectangle(p, fit->matchbox,
+ mbox_saverestore | mbox_area | mbox_border);
+
+ pdf_add_page_mbox(p, fit->matchbox);
+ }
+
+ /* scaling */
+ if (elemscale.x != 1 || elemscale.y != 1)
+ {
+ pdc_scale_matrix(elemscale.x, elemscale.y, &mm);
+ pdc_multiply_matrix(&mm, &m);
+ }
+
+ /* relative text position */
+ if (!vertical)
+ {
+ x = 0;
+ y = p->ydirection * basey;
+ }
+ else
+ {
+ x = width / 2.0;
+ y = p->ydirection * height;
+ }
+
+ if (logg5)
+ pdc_logg(p->pdc,
+ "\t\tReference point:\n"
+ "\t\t\tx = %f\n"
+ "\t\t\ty = %f\n"
+ "\t\tEmbedding matrix components of textline fitting:\n"
+ "\t\t\ta = %f\n"
+ "\t\t\tb = %f\n"
+ "\t\t\tc = %f\n"
+ "\t\t\td = %f\n"
+ "\t\t\te = %f\n"
+ "\t\t\tf = %f\n",
+ x, y, m.a, m.b, m.c, m.d, m.e, m.f);
+
+ /*
+ * blind mode: pdf__info_textline
+ */
+ if (blind)
+ {
+ pdc_scalar mwidth = 2 * fabs(elemmargin.x);
+ pdc_scalar mheight = 2 * fabs(elemmargin.y);
+ pdc_scalar vlen;
+
+ /* start position */
+ pdc_transform_point(&m, x, y, &tx, &ty);
+ fitres->start.x = tx;
+ fitres->start.y = ty;
+
+ /* end position */
+ if (!vertical)
+ {
+ tx = x + width;
+ ty = y;
+ }
+ else
+ {
+ tx = x;
+ ty = y - p->ydirection * height;
+ }
+ pdc_transform_point(&m, tx, ty, &tx, &ty);
+ fitres->end.x = tx;
+ fitres->end.y = ty;
+
+ /* relative vector from start to end */
+ tx = fitres->end.x - fitres->start.x;
+ ty = fitres->end.y - fitres->start.y;
+ vlen = sqrt(tx * tx + ty * ty);
+ if (!vertical)
+ {
+ /* width and x scaling */
+ fitres->width = vlen;
+ fitres->mwidth = vlen + mwidth;
+ fitres->scale.x = fitres->width / width;
+
+ /* unit vector of base line */
+ fitres->writingdir.x = tx / vlen;
+ fitres->writingdir.y = ty / vlen;
+
+ /* relative vector of fontsize */
+ tx = x;
+ ty = y + p->ydirection * height;
+ pdc_transform_point(&m, tx, ty, &tx, &ty);
+ tx -= fitres->start.x;
+ ty -= fitres->start.y;
+ vlen = sqrt(tx * tx + ty * ty);
+
+ /* height and y scaling */
+ fitres->height = vlen;
+ fitres->mheight = vlen + mheight;
+ fitres->scale.y = fitres->height / height;
+ }
+ else
+ {
+ /* height and y scaling */
+ fitres->height = vlen;
+ fitres->mheight = vlen + mheight;
+ fitres->scale.y = fitres->height / height;
+
+ /* unit vector of perpendiculardir line */
+ fitres->writingdir.x = tx / vlen;
+ fitres->writingdir.y = ty / vlen;
+
+ /* relative vector of width */
+ tx = x + width;
+ ty = y;
+ pdc_transform_point(&m, tx, ty, &tx, &ty);
+ tx -= fitres->start.x;
+ ty -= fitres->start.y;
+ vlen = sqrt(tx * tx + ty * ty);
+
+ /* width ans x scaling */
+ fitres->width = vlen;
+ fitres->mwidth = vlen + mwidth;
+ fitres->scale.x = fitres->width / width;
+ }
+
+ /* unit vector of perpendiculardir line */
+ fitres->perpendiculardir.x = tx / vlen;
+ fitres->perpendiculardir.y = ty / vlen;
+
+ /* rotation angle of base line */
+ fitres->angle = atan2(fitres->writingdir.y, fitres->writingdir.x) /
+ PDC_DEG2RAD;
+
+ /* font specifics */
+ fitres->ascender = pdc_transform_scalar(&m, ascender);
+ fitres->capheight = pdc_transform_scalar(&m, capheight);
+ fitres->xheight = pdc_transform_scalar(&m, xheight);
+ fitres->descender = pdc_transform_scalar(&m, descender);
+ }
+ else
+ {
+ /* CTM */
+ pdf_concat_raw(p, &m);
+
+ /* set text position */
+ pdf__set_text_pos(p, x, y);
+
+
+ /* place text */
+ pdf_place_text(p, utext, len, charlen, to, width, height, pdc_false);
+
+
+ /* create a link - deprecated - */
+ if (to->link)
+ {
+ pdf_check_textstring(p, to->text, to->textlen,
+ PDF_USE_TMPALLOC | PDF_KEEP_UNICODE,
+ to, &utext, &len, &charlen, pdc_true);
+ pdf_create_link(p, to->linktype, x, y + p->ydirection * descender,
+ x + width, y + p->ydirection * to->fontsize,
+ to->link, (char *) utext, len);
+ pdc_free_tmp(p->pdc, utext);
+ }
+ }
+
+ return unmapgids;
+}
+
+void
+pdf_calculate_textline_size(PDF *p, pdf_text_options *to, pdf_fit_options *fit,
+ pdc_scalar *width, pdc_scalar *height)
+{
+ pdf_ppt *ppt = p->curr_ppt;
+ pdf_fittext fitres;
+ pdc_matrix ctminv;
+
+ /* calculate textline size for table cells */
+ fitres.width = 0.0;
+ fitres.height = 0.0;
+ pdf_fit_textline_internal(p, &fitres, to, fit, NULL);
+
+ pdc_invert_matrix(p->pdc, &ctminv, &ppt->gstate[ppt->sl].ctm);
+ if (width)
+ *width = pdc_transform_scalar(&ctminv, fitres.mwidth);
+ if (height)
+ *height = pdc_transform_scalar(&ctminv, fitres.mheight);
+}
+
+void
+pdf__fit_textline(PDF *p, const char *text, int len, pdc_scalar x, pdc_scalar y,
+ const char *optlist)
+{
+ pdf_ppt *ppt = p->curr_ppt;
+ pdf_tstate *ts;
+ pdf_text_options to;
+ pdf_fit_options fit;
+ pdc_matrix ctminv;
+ pdc_scalar currtx, currty;
+
+ pdc_check_number(p->pdc, "x", x);
+ pdc_check_number(p->pdc, "y", y);
+
+ /* parse options */
+ if (!pdf_parse_textline_options(p, text, len, &to, &fit, optlist))
+ return;
+
+ fit.refpoint[0] = x;
+ fit.refpoint[1] = y;
+
+ pdf__save(p);
+
+ /* output text line */
+ pdf_fit_textline_internal(p, NULL, &to, &fit, NULL);
+ pdf_cleanup_fit_options(p, &fit);
+
+ ts = &ppt->tstate[ppt->sl];
+ pdc_transform_point(&ppt->gstate[ppt->sl].ctm,
+ ts->currtx, ts->currty, &currtx, &currty);
+
+ pdf__restore(p);
+
+ /* calculate current text position*/
+ pdc_invert_matrix(p->pdc, &ctminv, &ppt->gstate[ppt->sl].ctm);
+ pdc_transform_point(&ctminv, currtx, currty, &currtx, &currty);
+ pdf__set_text_pos(p, currtx, currty);
+}
+
+static const pdc_keyconn pdf_info_keylist[] =
+{
+ {"startx", 1},
+ {"starty", 2},
+ {"endx", 3},
+ {"endy", 4},
+ {"writingdirx", 5},
+ {"writingdiry", 6},
+ {"perpendiculardirx", 7},
+ {"perpendiculardiry", 8},
+ {"scalex", 9},
+ {"scaley", 10},
+ {"width", 11},
+ {"height", 12},
+ {"ascender", 13},
+ {"capheight", 14},
+ {"xheight", 15},
+ {"descender", 16},
+ {"unmappedglyphs", 17},
+ {"angle", 18},
+ {NULL, 0}
+};
+
+double
+pdf__info_textline(PDF *p, const char *text, int len, const char *keyword,
+ const char *optlist)
+{
+ pdf_ppt *ppt = p->curr_ppt;
+ pdf_text_options to;
+ pdf_fit_options fit;
+ pdf_fittext fitres;
+ double tinfo = 0;
+ int retval, infokey;
+
+ if (!keyword || !*keyword)
+ pdc_error(p->pdc, PDC_E_ILLARG_EMPTY, "keyword", 0, 0, 0);
+
+ infokey = pdc_get_keycode_ci(keyword, pdf_info_keylist);
+ if (infokey == PDC_KEY_NOTFOUND)
+ pdc_error(p->pdc, PDC_E_ILLARG_STRING, "keyword", keyword, 0, 0);
+
+ /* parse options */
+ retval = pdf_parse_textline_options(p, text, len, &to, &fit, optlist);
+
+ if (retval)
+ {
+ /* calculate textline */
+ retval = pdf_fit_textline_internal(p, &fitres, &to, &fit, NULL);
+ pdf_cleanup_fit_options(p, &fit);
+
+ if (retval > -1)
+ {
+ pdf_font *currfont = &p->fonts[to.font];
+ pdc_matrix ctminv;
+
+ pdc_invert_matrix(p->pdc, &ctminv, &ppt->gstate[ppt->sl].ctm);
+
+ switch(infokey)
+ {
+ case 1:
+ case 2:
+ pdc_transform_vector(&ctminv, &fitres.start, NULL);
+ break;
+
+ case 3:
+ case 4:
+ pdc_transform_vector(&ctminv, &fitres.end, NULL);
+ break;
+
+ case 5:
+ case 6:
+ pdc_transform_rvector(&ctminv, &fitres.writingdir, NULL);
+ break;
+
+ case 7:
+ case 8:
+ pdc_transform_rvector(&ctminv, &fitres.perpendiculardir, NULL);
+ break;
+ }
+
+ pdc_logg_cond(p->pdc, 1, trc_text,
+ "\tInfo textline%s:\n"
+ "\tstartx = %f\n"
+ "\tstarty = %f\n"
+ "\tendx = %f\n"
+ "\tendy = %f\n"
+ "\twritingdirx = %f\n"
+ "\twritingdiry = %f\n"
+ "\tperpendiculardirx = %f\n"
+ "\tperpendiculardiry = %f\n"
+ "\tscalex = %f\n"
+ "\tscaley = %f\n"
+ "\twidth = %f\n"
+ "\theight = %f\n"
+ "\tascender = %f\n"
+ "\tcapheight = %f\n"
+ "\txheight = %f\n"
+ "\tdescender = %f\n",
+ currfont->ft.vertical ? " (vertical writing mode)" : "",
+ fitres.start.x, fitres.start.y,
+ fitres.end.x, fitres.end.y,
+ fitres.writingdir.x, fitres.writingdir.y,
+ fitres.perpendiculardir.x, fitres.perpendiculardir.y,
+ fitres.scale.x, fitres.scale.y,
+ fitres.width, fitres.height,
+ fitres.ascender, fitres.capheight,
+ fitres.xheight,fitres.descender);
+
+ switch(infokey)
+ {
+ case 1:
+ tinfo = (double) fitres.start.x;
+ break;
+
+ case 2:
+ tinfo = (double) fitres.start.y;
+ break;
+
+ case 3:
+ tinfo = (double) fitres.end.x;
+ break;
+
+ case 4:
+ tinfo = (double) fitres.end.y;
+ break;
+
+ case 5:
+ tinfo = (double) fitres.writingdir.x;
+ break;
+
+ case 6:
+ tinfo = (double) fitres.writingdir.y;
+ break;
+
+ case 7:
+ tinfo = (double) fitres.perpendiculardir.x;
+ break;
+
+ case 8:
+ tinfo = (double) fitres.perpendiculardir.y;
+ break;
+
+ case 9:
+ tinfo = (double) fitres.scale.x;
+ break;
+
+ case 10:
+ tinfo = (double) fitres.scale.y;
+ break;
+
+ case 11:
+ tinfo = (double) fitres.width;
+ break;
+
+ case 12:
+ tinfo = (double) fitres.height;
+ break;
+
+ case 13:
+ tinfo = (double) fitres.ascender;
+ break;
+
+ case 14:
+ tinfo = (double) fitres.capheight;
+ break;
+
+ case 15:
+ tinfo = (double) fitres.xheight;
+ break;
+
+ case 16:
+ tinfo = (double) fitres.descender;
+ break;
+
+ case 17:
+ tinfo = (double) retval;
+ break;
+
+ case 18:
+ tinfo = (double) fitres.angle;
+ break;
+ }
+ }
+ }
+
+ return tinfo;
+}
+
+
+
+/*****************************************************************************/
+/** deprecated historical text formatting function **/
+/*****************************************************************************/
+
+/* this helper function returns the width of the null-terminated string
+** 'text' for the current font and size EXCLUDING the last character's
+** additional charspacing.
+*/
+static pdc_scalar
+pdf_swidth(PDF *p, const char *text)
+{
+ pdf_text_options *currto = p->curr_ppt->currto;
+
+ pdc_scalar width = pdf_calculate_textsize(p,
+ (pdc_byte *) text, (int)strlen(text), 1,
+ currto, -1, NULL, pdc_true);
+ return (width - currto->horizscaling * currto->charspacing);
+}
+
+static void
+pdf_show_aligned(PDF *p, const char *text, pdc_scalar x, pdc_scalar y,
+ pdc_scalar wordspacing, pdf_alignment mode)
+{
+ if (!text)
+ return;
+
+ switch (mode) {
+ default:
+ case text_left:
+ case text_justify:
+ case text_fulljustify:
+ /* nothing extra here... */
+ break;
+
+ case text_right:
+ x -= pdf_swidth(p, text);
+ break;
+
+ case text_center:
+ x -= pdf_swidth(p, text) / 2;
+ break;
+ }
+
+ pdf__set_text_pos(p, x, y);
+ pdf_set_tstate(p, wordspacing, to_wordspacing);
+ pdf__show_text(p, text, (int) strlen(text), pdc_false);
+}
+
+int
+pdf__show_boxed(
+ PDF *p,
+ const char *text, int len,
+ pdc_scalar left,
+ pdc_scalar bottom,
+ pdc_scalar width,
+ pdc_scalar height,
+ const char *hmode,
+ const char *feature)
+{
+ pdc_scalar old_wordspacing, wordspacing, textwidth, curx, cury;
+ pdc_bool prematureexit; /* return because box is too small */
+ int curTextPos; /* character currently processed */
+ int lastdone; /* last input character processed */
+ int toconv = len;
+ pdf_text_options *currto = p->curr_ppt->currto;
+ pdf_font *currfont;
+ pdc_byte *utext = NULL;
+ pdc_text_format old_textformat;
+ pdf_alignment mode = text_left;
+ pdc_bool blind = pdc_false;
+
+ /* text length */
+ if (text == NULL)
+ return 0;
+ if (!len)
+ len = (int) strlen(text);
+ if (!len)
+ return 0;
+
+ pdc_check_number(p->pdc, "left", left);
+ pdc_check_number(p->pdc, "bottom", bottom);
+ pdc_check_number(p->pdc, "width", width);
+ pdc_check_number(p->pdc, "height", height);
+
+ if (hmode == NULL || *hmode == '\0')
+ pdc_error(p->pdc, PDC_E_ILLARG_EMPTY, "hmode", 0, 0, 0);
+
+ if (!strcmp(hmode, "left"))
+ mode = text_left;
+ else if (!strcmp(hmode, "right"))
+ mode = text_right;
+ else if (!strcmp(hmode, "center"))
+ mode = text_center;
+ else if (!strcmp(hmode, "justify"))
+ mode = text_justify;
+ else if (!strcmp(hmode, "fulljustify"))
+ mode = text_fulljustify;
+ else
+ pdc_error(p->pdc, PDC_E_ILLARG_STRING, "hmode", hmode, 0, 0);
+
+ if (feature != NULL && *feature != '\0')
+ {
+ if (!strcmp(feature, "blind"))
+ blind = pdc_true;
+ else
+ pdc_error(p->pdc, PDC_E_ILLARG_STRING, "feature", feature, 0, 0);
+ }
+
+ /* no font set */
+ if (currto->font == -1)
+ pdc_error(p->pdc, PDF_E_TEXT_NOFONT, 0, 0, 0, 0);
+ currfont = &p->fonts[currto->font];
+
+ if (width == 0 && height != 0)
+ pdc_error(p->pdc, PDC_E_ILLARG_FLOAT,
+ "width", pdc_errprintf(p->pdc, "%f", width), 0, 0);
+
+ if (width != 0 && height == 0)
+ pdc_error(p->pdc, PDC_E_ILLARG_FLOAT,
+ "height", pdc_errprintf(p->pdc, "%f", height), 0, 0);
+
+ if (currfont->ft.vertical)
+ {
+ pdc_error(p->pdc, PDF_E_DOC_FUNCUNSUPP, "vertical writing mode",
+ 0, 0, 0);
+ }
+
+ /* we cannot handle several encodings */
+ if (currfont->ft.enc == pdc_unicode)
+ {
+ pdc_error(p->pdc, PDF_E_DOC_FUNCUNSUPP, "Unicode", 0, 0, 0);
+ }
+
+ if (currfont->ft.enc == pdc_glyphid)
+ {
+ pdc_error(p->pdc, PDF_E_DOC_FUNCUNSUPP, "glyphid", 0, 0, 0);
+ }
+
+ if (currfont->ft.enc == pdc_cid)
+ {
+ pdc_error(p->pdc, PDF_E_DOC_FUNCUNSUPP, "CID", 0, 0, 0);
+ }
+
+ if (currfont->ft.enc == pdc_ebcdic ||
+ currfont->ft.enc == pdc_ebcdic_37 ||
+ currfont->ft.enc == pdc_ebcdic_winansi)
+ {
+ pdc_error(p->pdc, PDF_E_DOC_FUNCUNSUPP, "EBCDIC", 0, 0, 0);
+ }
+
+ /* old wordspacing */
+ old_textformat = currto->textformat;
+
+ /* convert text string */
+ if (toconv)
+ {
+ int charlen;
+
+ /* convert text string */
+ pdf_check_textstring(p, text, len,
+ PDF_KEEP_CONTROL | PDF_KEEP_TEXTLEN | PDF_USE_TMPALLOC,
+ currto, &utext, &len, &charlen, pdc_true);
+ if (utext == NULL || !len)
+ return 0;
+
+ utext[len] = 0;
+ text = (const char *) utext;
+ currto->textformat = pdc_bytes;
+ }
+
+ /* old wordspacing */
+ old_wordspacing = currto->wordspacing;
+
+ /* special case for a single aligned line */
+ if (width == 0 && height == 0)
+ {
+ if (!blind)
+ pdf_show_aligned(p, text, left, bottom, old_wordspacing, mode);
+
+ if (toconv)
+ currto->textformat = old_textformat;
+ return 0;
+ }
+
+ curx = left;
+ cury = bottom + p->ydirection * height;
+ prematureexit = pdc_false;
+ curTextPos = 0;
+ lastdone = 0;
+
+ /* switch curx for right and center justification */
+ if (mode == text_right)
+ curx += width;
+ else if (mode == text_center)
+ curx += (width / 2);
+
+#define MAX_CHARS_IN_LINE 2048
+
+ /* loop until all characters processed, or box full */
+
+ while ((curTextPos < len) && !prematureexit)
+ {
+ /* buffer for constructing the line */
+ char linebuf[MAX_CHARS_IN_LINE];
+ int curCharsInLine = 0; /* # of chars in constructed line */
+ int lastWordBreak = 0; /* the last seen space char */
+ int wordBreakCount = 0; /* # of blanks in this line */
+
+ /* loop over the input string */
+ while (curTextPos < len)
+ {
+ if (curCharsInLine >= MAX_CHARS_IN_LINE)
+ pdc_error(p->pdc, PDC_E_ILLARG_TOOLONG, "(text line)",
+ pdc_errprintf(p->pdc, "%d", MAX_CHARS_IN_LINE-1), 0, 0);
+
+ /* abandon DOS line-ends */
+ if (text[curTextPos] == PDF_RETURN &&
+ text[curTextPos+1] == PDF_NEWLINE)
+ curTextPos++;
+
+ /* if it's a forced line break draw the line */
+ if (text[curTextPos] == PDF_NEWLINE ||
+ text[curTextPos] == PDF_RETURN)
+ {
+ cury -= p->ydirection * currto->leading;
+
+ if (p->ydirection * (cury - bottom) < 0) {
+ prematureexit = pdc_true; /* box full */
+ break;
+ }
+
+ linebuf[curCharsInLine] = 0; /* terminate the line */
+
+ /* check whether the line is too long */
+ wordspacing = 0;
+ pdf_set_tstate(p, wordspacing, to_wordspacing);
+ textwidth = pdf_swidth(p, linebuf);
+
+ /* the forced break occurs too late for this line */
+ if (textwidth > width)
+ {
+ if (wordBreakCount == 0) { /* no blank found */
+ prematureexit = pdc_true;
+ break;
+ }
+ linebuf[lastWordBreak] = 0; /* terminate at last blank */
+ if (curTextPos > 0 && text[curTextPos-1] == PDF_RETURN)
+ --curTextPos;
+ curTextPos -= (curCharsInLine - lastWordBreak);
+
+ if (!blind)
+ {
+ textwidth = pdf_swidth(p, linebuf);
+ if (wordBreakCount != 1 &&
+ (mode == text_justify ||
+ mode == text_fulljustify))
+ {
+ wordspacing = (width - textwidth) /
+ ((wordBreakCount - 1) * currto->horizscaling);
+ }
+ pdf_show_aligned(p, linebuf, curx, cury, wordspacing,
+ mode);
+ }
+ }
+ else if (!blind)
+ {
+ if (mode == text_fulljustify && wordBreakCount > 0)
+ {
+ wordspacing = (width - textwidth) /
+ (wordBreakCount * currto->horizscaling);
+ }
+ pdf_show_aligned(p, linebuf, curx, cury, wordspacing, mode);
+ }
+
+ lastdone = curTextPos;
+ curCharsInLine = lastWordBreak = wordBreakCount = 0;
+ curTextPos++;
+
+ }
+ else if (text[curTextPos] == PDF_SPACE)
+ {
+ linebuf[curCharsInLine] = 0; /* terminate the line */
+
+ /* line too long ==> break at last blank */
+ wordspacing = 0;
+ pdf_set_tstate(p, wordspacing, to_wordspacing);
+ if (pdf_swidth(p, linebuf) > width)
+ {
+ cury -= p->ydirection * currto->leading;
+
+ if (p->ydirection * (cury - bottom) < 0)
+ {
+ prematureexit = pdc_true; /* box full */
+ break;
+ }
+
+ linebuf[lastWordBreak] = 0; /* terminate at last blank */
+ curTextPos -= (curCharsInLine - lastWordBreak - 1);
+
+ if (lastWordBreak == 0)
+ curTextPos--;
+
+ /* LATER: * force break if wordBreakCount == 1,
+ * i.e., no blank
+ */
+ if (wordBreakCount == 0)
+ {
+ prematureexit = pdc_true;
+ break;
+ }
+
+ /* adjust word spacing for full justify */
+ if (wordBreakCount != 1 && (mode == text_justify ||
+ mode == text_fulljustify))
+ {
+ textwidth = pdf_swidth(p, linebuf);
+ wordspacing = (width - textwidth) /
+ ((wordBreakCount - 1) * currto->horizscaling);
+ }
+
+ lastdone = curTextPos;
+ if (!blind)
+ {
+ pdf_show_aligned(p, linebuf, curx, cury, wordspacing,
+ mode);
+ }
+ curCharsInLine = lastWordBreak = wordBreakCount = 0;
+ }
+ else
+ {
+ /* blank found, and line still fits */
+ wordBreakCount++;
+ lastWordBreak = curCharsInLine;
+ linebuf[curCharsInLine++] = text[curTextPos++];
+ }
+ }
+ else
+ {
+ /* regular character ==> store in buffer */
+ linebuf[curCharsInLine++] = text[curTextPos++];
+ }
+ }
+
+ if (prematureexit) {
+ break; /* box full */
+ }
+
+ /* if there is anything left in the buffer, draw it */
+ if (curTextPos >= len && curCharsInLine != 0)
+ {
+ cury -= p->ydirection * currto->leading;
+
+ if (p->ydirection * (cury - bottom) < 0)
+ {
+ prematureexit = pdc_true; /* box full */
+ break;
+ }
+
+ linebuf[curCharsInLine] = 0; /* terminate the line */
+
+ /* check if the last line is too long */
+ wordspacing = 0;
+ pdf_set_tstate(p, wordspacing, to_wordspacing);
+ textwidth = pdf_swidth(p, linebuf);
+
+ if (textwidth > width)
+ {
+ if (wordBreakCount == 0)
+ {
+ prematureexit = pdc_true;
+ break;
+ }
+
+ linebuf[lastWordBreak] = 0; /* terminate at last blank */
+ curTextPos -= (curCharsInLine - lastWordBreak - 1);
+
+ /* recalculate the width */
+ textwidth = pdf_swidth(p, linebuf);
+
+ /* adjust word spacing for full justify */
+ if (wordBreakCount != 1 && (mode == text_justify ||
+ mode == text_fulljustify))
+ {
+ wordspacing = (width - textwidth) /
+ ((wordBreakCount - 1) * currto->horizscaling);
+ }
+ }
+ else if (!blind)
+ {
+ if (mode == text_fulljustify && wordBreakCount)
+ {
+ wordspacing = (width - textwidth) /
+ (wordBreakCount * currto->horizscaling);
+ }
+ }
+
+ lastdone = curTextPos;
+ if (!blind)
+ {
+ pdf_show_aligned(p, linebuf, curx, cury, wordspacing, mode);
+ }
+ curCharsInLine = lastWordBreak = wordBreakCount = 0;
+ }
+ }
+
+ pdf_set_tstate(p, old_wordspacing, to_wordspacing);
+
+ /* return number of remaining characters */
+
+ while (text[lastdone] == PDF_SPACE)
+ ++lastdone;
+
+ if ((text[lastdone] == PDF_RETURN ||
+ text[lastdone] == PDF_NEWLINE) && text[lastdone+1] == 0)
+ ++lastdone;
+
+ if (toconv)
+ currto->textformat = old_textformat;
+
+ return (int) (len - lastdone);
+}