guy 3bf951f42c Patch level : 10.0
Files correlati     : pdflib
Ricompilazione Demo : [ ]
Commento            :
Aggiornata pdflib.dll alla versione 7.0.4


git-svn-id: svn://10.65.10.50/trunk@18580 c028cbd2-c16b-5b4b-a496-9718f37d4682
2009-03-23 08:55:58 +00:00

3833 lines
109 KiB
C
Executable File

/*---------------------------------------------------------------------------*
| 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.4 2009-03-23 08:55:35 guy 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_fakebold, to_wordspacing,
to_textrise, to_underlinewidth, to_underlineposition
};
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;
if (currto->font != -1 &&
(p->fonts[to->font].metricflags & font_italic) !=
(p->fonts[currto->font].metricflags & font_italic))
{
to->mask |= (1 << to_italicangle);
currto->mask = to->mask; /* otherwise order dependent */
}
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 & (1U << to_fontsize) ||
(force && to->fontsize != PDC_FLOAT_MIN))
{
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 && PDC_ABS(fontsize) < minfontsize)
{
fontsize = PDC_SIGN(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 ---------------------- */
struct pdf_fitres_s
{
pdc_bool verbose;
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 */
int unmappedchars; /* number of characters not contained
* in the encoding and not in the font */
int replacedchars; /* number of characters replaced by
* typografically similar characters */
int unknownchars; /* number of characters replaced by
* given replcement character */
};
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, pdf_fitres *fitres,
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;
if (fitres != NULL)
fitres->unknownchars++;
}
else if (fitres != NULL)
{
fitres->replacedchars++;
}
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 pdc_false an error was occurred, otherwise pdc_true.
*
*/
pdc_bool
pdf_check_textstring(PDF *p, const char *text, int len, int flags,
pdf_text_options *to, pdf_fitres *fitres,
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 maxlen = PDF_MAXDICTSIZE;
int charlen = 1, newcharlen = 1;
int convflags = PDC_CONV_NOBOM;
pdf_ligat *ligat, *ligatlist = NULL;
pdc_byte *intext = (pdc_byte *) text, *outtext = NULL;
int newlen = -1, 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");
if (intextformat > pdc_bytes2)
pdc_warning(p->pdc, PDF_E_TEXT_BADTEXTFORMAT,
pdc_get_textformat(intextformat), 0, 0, 0);
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! */
if (*outlen > maxlen && !(flags & PDF_KEEP_TEXTLEN))
{
int textlen = *outlen;
if (!to->kerning && to->wordspacing == 0.0)
{
if (currfont->codesize == 1)
textlen /= charlen;
maxlen = PDF_MAXTEXTSIZE;
}
else
{
if (currfont->codesize == 2)
maxlen *= charlen;
}
if (textlen > maxlen)
{
pdc_set_errmsg(p->pdc, PDF_E_TEXT_TOOLONG,
pdc_errprintf(p->pdc, "%d", textlen),
pdc_errprintf(p->pdc, "%d", maxlen), 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)
{
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 (fitres != NULL)
fitres->unknownchars++;
}
if (fitres != NULL)
fitres->unmappedchars++;
}
}
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)
{
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,
fitres,
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 (fitres != NULL)
fitres->unmappedchars++;
}
else
{
gid = fnt_get_glyphid(code, &currfont->ft);
/* glyph id for code not available */
if (gid <= 0 && currfont->gid0code != code)
{
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,
fitres,
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 (fitres != NULL)
fitres->unmappedchars++;
}
}
}
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);
}
if (*outlen)
return pdc_true;
verbose = pdc_false;
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_RETHROW(p->pdc);
*outtext_p = NULL;
*outlen = 0;
return pdc_false;
}
/* ------------------------ 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;
len = pdc_check_text_length(p->pdc, &text, len, PDF_MAXTEXTSIZE);
if (!len)
return 0.0;
pdf_check_handle(p, font, pdc_fonthandle);
pdc_check_number_zero(p->pdc, "fontsize", fontsize);
/* convert text string */
to.font = font;
to.fontsize = fontsize;
if (pdf_check_textstring(p, text, len, PDF_KEEP_TEXTLEN | PDF_USE_TMPALLOC,
&to, NULL, &utext, &len, &charlen, pdc_true))
{
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 if (font > -1)
{
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;
len = pdc_check_text_length(p->pdc, &text, len, PDF_MAXTEXTSIZE);
if (!len && !cont)
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 */
if (pdf_check_textstring(p, text, len, PDF_USE_TMPALLOC,
currto, NULL, &utext, &len, &charlen, pdc_true))
{
/* width and height of text string */
width = pdf_calculate_textsize(p, utext, len, charlen,
currto, -1, &height, pdc_true);
}
else if (!cont)
{
return;
}
}
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;
len = pdc_check_text_length(p->pdc, &text, len, PDF_MAXTEXTSIZE);
if (!len)
return;
/* no font set */
if (currto->font == -1)
pdc_error(p->pdc, PDF_E_TEXT_NOFONT, 0, 0, 0, 0);
/* convert text string */
if (pdf_check_textstring(p, text, len, PDF_USE_TMPALLOC,
currto, NULL, &utext, &len, &charlen, pdc_true))
{
/* 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 ------------------------ */
/* 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;
len = pdc_check_text_length(p->pdc, &text, len, PDF_MAXTEXTSIZE);
if (!len)
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;
}
pdc_bool
pdf_fit_textline_internal(PDF *p, pdf_fitres *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_bool hasboxwidth = pdc_false;
pdc_bool verbose = pdc_true;
pdc_scalar font2user;
int indangle = fit->orientate / 90;
int 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);
else
verbose = fitres->verbose;
/* convert text string */
if (!pdf_check_textstring(p, to->text, to->textlen, PDF_USE_TMPALLOC,
to, fitres, &utext, &len, &charlen, verbose))
return pdc_false;
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);
/* incredible bug #1451
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;
hasboxwidth = boxwidth > PDC_FLOAT_PREC;
hasfitbox = hasboxwidth && 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:
/* non-Unicode compatible CMaps seem to work (see bug #1605)
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;
}
if (to->glyphcheck == text_error && poschar == -1)
{
pdc_set_errmsg(p->pdc, PDF_E_TEXT_ALIGNCHARNOTFOUND,
pdc_errprintf(p->pdc, "%04X", fit->alignchar),
0, 0, 0);
if (verbose)
PDC_RETHROW(p->pdc);
return pdc_false;
}
/* 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 && !PDC_FLOAT_ISNULL(width))
{
/* 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 wlen, plen;
/* 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;
wlen = sqrt(tx * tx + ty * ty);
if (!vertical)
{
/* width and x scaling */
fitres->width = wlen;
fitres->mwidth = wlen + mwidth;
}
else
{
/* height and y scaling */
fitres->height = wlen;
fitres->mheight = wlen + mheight;
}
/* unit vector of writingdir line */
if (!PDC_FLOAT_ISNULL(wlen))
{
fitres->writingdir.x = tx / wlen;
fitres->writingdir.y = ty / wlen;
}
else
{
fitres->writingdir.x = 0;
fitres->writingdir.y = 0;
}
if (!vertical)
{
/* 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;
plen = sqrt(tx * tx + ty * ty);
/* height and y scaling */
fitres->height = plen;
fitres->mheight = plen + mheight;
}
else
{
/* 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;
plen = sqrt(tx * tx + ty * ty);
/* width ans x scaling */
fitres->width = plen;
fitres->mwidth = plen + mwidth;
}
/* unit vector of perpendiculardir line */
if (!PDC_FLOAT_ISNULL(plen))
{
fitres->perpendiculardir.x = tx / plen;
fitres->perpendiculardir.y = ty / plen;
}
else
{
fitres->perpendiculardir.x = 0;
fitres->perpendiculardir.y = 0;
}
if (!PDC_FLOAT_ISNULL(width))
fitres->scale.x = fitres->width / width;
else
fitres->scale.x = 1.0;
if (!PDC_FLOAT_ISNULL(height))
fitres->scale.y = fitres->height / height;
else
fitres->scale.y = 1.0;
/* 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)
{
if (pdf_check_textstring(p, to->text, to->textlen,
PDF_USE_TMPALLOC | PDF_KEEP_UNICODE,
to, NULL, &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 pdc_true;
}
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_fitres fitres;
pdc_matrix ctminv;
/* calculate textline size for table cells */
fitres.verbose = pdc_true;
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},
{"angle", 17},
{"unmappedglyphs", 20}, /* deprecated */
{"unmappedchars", 20},
{"replacedchars", 21},
{"unknownchars", 22},
{"wellformed", 23},
{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_fitres fitres;
double tinfo = 0.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 */
fitres.verbose = to.glyphwarning;
fitres.unmappedchars = 0;
fitres.replacedchars = 0;
fitres.unknownchars = 0;
retval = pdf_fit_textline_internal(p, &fitres, &to, &fit, NULL);
pdf_cleanup_fit_options(p, &fit);
if (retval)
{
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) fitres.angle;
break;
case 20:
tinfo = (double) fitres.unmappedchars;
break;
case 21:
tinfo = (double) fitres.replacedchars;
break;
case 22:
tinfo = (double) fitres.unknownchars;
break;
/* wellformed */
case 23:
tinfo = (double) 1;
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;
len = pdc_check_text_length(p->pdc, &text, len, PDF_MAXTEXTSIZE);
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 */
if (!pdf_check_textstring(p, text, len,
PDF_KEEP_CONTROL | PDF_KEEP_TEXTLEN | PDF_USE_TMPALLOC,
currto, NULL, &utext, &len, &charlen, pdc_true))
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);
}