Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with
or
.
Download ZIP
Fetching contributors…

Cannot retrieve contributors at this time

7800 lines (7122 sloc) 246.079 kB
/* Copyright (C) 2000-2012 by George Williams */
/*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
* Redistributions of source code must retain the above copyright notice, this
* list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
* The name of the author may not be used to endorse or promote products
* derived from this software without specific prior written permission.
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
* EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
* OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
* WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
* OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#include "fontforgevw.h"
#include <math.h>
#include "psfont.h"
#include "ustring.h"
#include "utype.h"
#include "views.h" /* for FindSel structure */
#ifdef HAVE_IEEEFP_H
# include <ieeefp.h> /* Solaris defines isnan in ieeefp rather than math.h */
#endif
#include <locale.h>
#include "sfd1.h" // This has the extended SplineFont type SplineFont1 for old file versions.
#include "c-strtod.h"
#ifdef FF_UTHASH_GLIF_NAMES
# include "glif_name_hash.h"
#endif
/*#define DEBUG 1*/
typedef struct quartic {
bigreal a,b,c,d,e;
} Quartic;
/* In an attempt to make allocation more efficient I just keep preallocated */
/* lists of certain common sizes. It doesn't seem to make much difference */
/* when allocating stuff, but does when freeing. If the extra complexity */
/* is bad then put: */
/* #define chunkalloc(size) calloc(1,size) */
/* #define chunkfree(item,size) free(item) */
/* into splinefont.h after (or instead of) the definition of chunkalloc()*/
#define ALLOC_CHUNK 100 /* Number of small chunks to malloc at a time */
#ifndef FONTFORGE_CONFIG_USE_DOUBLE
# define CHUNK_MAX 100 /* Maximum size (in chunk units) that we are prepared to allocate */
/* The size of our data structures */
#else
# define CHUNK_MAX 129
#endif
# define CHUNK_UNIT sizeof(void *) /* will vary with the word size of */
/* the machine. if pointers are 64 bits*/
/* we may need twice as much space as for 32 bits */
#ifdef FLAG
#undef FLAG
#define FLAG 0xbadcafe
#endif
#ifdef CHUNKDEBUG
static int chunkdebug = 0; /* When this is set we never free anything, insuring that each chunk is unique */
#endif
#if ALLOC_CHUNK>1
struct chunk { struct chunk *next; };
struct chunk2 { struct chunk2 *next; int flag; };
#endif
#if defined(FLAG) && ALLOC_CHUNK>1
void chunktest(void) {
int i;
struct chunk2 *c;
for ( i=2; i<CHUNK_MAX; ++i )
for ( c=(struct chunk2 *) chunklists[i]; c!=NULL; c=c->next )
if ( c->flag!=FLAG ) {
fprintf( stderr, "Chunk memory list has been corrupted\n" );
abort();
}
}
#endif
char *strconcat(const char *str1,const char *str2) {
char *ret;
int len1 = strlen(str1);
if ( (ret=malloc(len1+strlen(str2)+1))!=NULL ) {
strcpy(ret,str1);
strcpy(ret+len1,str2);
}
return( ret );
}
char *strconcat3(const char *str1,const char *str2, const char *str3) {
char *ret;
int len1 = strlen(str1), len2 = strlen(str2);
if ( (ret=malloc(len1+len2+strlen(str3)+1))!=NULL ) {
strcpy(ret,str1);
strcpy(ret+len1,str2);
strcpy(ret+len1+len2,str3);
}
return( ret );
}
void LineListFree(LineList *ll) {
LineList *next;
while ( ll!=NULL ) {
next = ll->next;
chunkfree(ll,sizeof(LineList));
ll = next;
}
}
void LinearApproxFree(LinearApprox *la) {
LinearApprox *next;
while ( la!=NULL ) {
next = la->next;
LineListFree(la->lines);
chunkfree(la,sizeof(LinearApprox));
la = next;
}
}
void SplineFree(Spline *spline) {
LinearApproxFree(spline->approx);
chunkfree(spline,sizeof(Spline));
}
SplinePoint *SplinePointCreate(real x, real y) {
SplinePoint *sp;
if ( (sp=chunkalloc(sizeof(SplinePoint)))!=NULL ) {
sp->me.x = x; sp->me.y = y;
sp->nextcp = sp->prevcp = sp->me;
sp->nonextcp = sp->noprevcp = true;
sp->nextcpdef = sp->prevcpdef = false;
sp->ttfindex = sp->nextcpindex = 0xfffe;
sp->name = NULL;
}
return( sp );
}
Spline *SplineMake3(SplinePoint *from, SplinePoint *to) {
Spline *spline = chunkalloc(sizeof(Spline));
spline->from = from; spline->to = to;
from->next = to->prev = spline;
SplineRefigure3(spline);
return( spline );
}
void SplinePointFree(SplinePoint *sp) {
chunkfree(sp->hintmask,sizeof(HintMask));
free(sp->name);
chunkfree(sp,sizeof(SplinePoint));
}
void SplinePointMDFree(SplineChar *sc, SplinePoint *sp) {
MinimumDistance *md, *prev, *next;
if ( sc!=NULL ) {
prev = NULL;
for ( md = sc->md; md!=NULL; md = next ) {
next = md->next;
if ( md->sp1==sp || md->sp2==sp ) {
if ( prev==NULL )
sc->md = next;
else
prev->next = next;
chunkfree(md,sizeof(MinimumDistance));
} else
prev = md;
}
}
chunkfree(sp->hintmask,sizeof(HintMask));
free(sp->name);
chunkfree(sp,sizeof(SplinePoint));
}
void SplinePointsFree(SplinePointList *spl) {
Spline *first, *spline, *next;
int nonext;
if ( spl==NULL )
return;
if ( spl->first!=NULL ) {
nonext = spl->first->next==NULL; // If there is no spline, we set a flag.
first = NULL;
// We start on the first spline if it exists.
for ( spline = spl->first->next; spline!=NULL && spline!=first; spline = next ) {
next = spline->to->next; // Cache the location of the next spline.
SplinePointFree(spline->to); // Free the destination point.
SplineFree(spline); // Free the spline.
if ( first==NULL ) first = spline; // We want to avoid repeating the circuit.
}
// If the path is open or has no splines, free the starting point.
if ( spl->last!=spl->first || nonext )
SplinePointFree(spl->first);
}
}
void SplineSetBeziersClear(SplinePointList *spl) {
if ( spl==NULL ) return;
SplinePointsFree(spl);
spl->first = spl->last = NULL;
spl->start_offset = 0;
}
void SplinePointListFree(SplinePointList *spl) {
if ( spl==NULL ) return;
SplinePointsFree(spl);
free(spl->spiros);
free(spl->contour_name);
chunkfree(spl,sizeof(SplinePointList));
}
void SplinePointListMDFree(SplineChar *sc,SplinePointList *spl) {
Spline *first, *spline, *next;
int freefirst;
if ( spl==NULL )
return;
if ( spl->first!=NULL ) {
first = NULL;
freefirst = ( spl->last!=spl->first || spl->first->next==NULL );
for ( spline = spl->first->next; spline!=NULL && spline!=first; spline = next ) {
next = spline->to->next;
SplinePointMDFree(sc,spline->to);
SplineFree(spline);
if ( first==NULL ) first = spline;
}
if ( freefirst )
SplinePointMDFree(sc,spl->first);
}
free(spl->spiros);
free(spl->contour_name);
chunkfree(spl,sizeof(SplinePointList));
}
void SplinePointListsMDFree(SplineChar *sc,SplinePointList *spl) {
SplinePointList *next;
while ( spl!=NULL ) {
next = spl->next;
SplinePointListMDFree(sc,spl);
spl = next;
}
}
void SplinePointListsFree(SplinePointList *spl) {
SplinePointList *next;
while ( spl!=NULL ) {
next = spl->next;
SplinePointListFree(spl);
spl = next;
}
}
void SplineSetSpirosClear(SplineSet *spl) {
free(spl->spiros);
spl->spiros = NULL;
spl->spiro_cnt = spl->spiro_max = 0;
}
void ImageListsFree(ImageList *imgs) {
ImageList *inext;
while ( imgs!=NULL ) {
inext = imgs->next;
chunkfree(imgs,sizeof(ImageList));
imgs = inext;
}
}
void RefCharFree(RefChar *ref) {
int i;
if ( ref==NULL )
return;
for ( i=0; i<ref->layer_cnt; ++i ) {
SplinePointListsFree(ref->layers[i].splines);
ImageListsFree(ref->layers[i].images);
GradientFree(ref->layers[i].fill_brush.gradient);
GradientFree(ref->layers[i].stroke_pen.brush.gradient);
PatternFree(ref->layers[i].fill_brush.pattern);
PatternFree(ref->layers[i].stroke_pen.brush.pattern);
}
free(ref->layers);
chunkfree(ref,sizeof(RefChar));
}
RefChar *RefCharCreate(void) {
RefChar *ref = chunkalloc(sizeof(RefChar));
ref->layer_cnt = 1;
ref->layers = calloc(1,sizeof(struct reflayer));
ref->layers[0].fill_brush.opacity = ref->layers[0].stroke_pen.brush.opacity = 1.0;
ref->layers[0].fill_brush.col = ref->layers[0].stroke_pen.brush.col = COLOR_INHERITED;
ref->layers[0].stroke_pen.width = WIDTH_INHERITED;
ref->layers[0].stroke_pen.linecap = lc_inherited;
ref->layers[0].stroke_pen.linejoin = lj_inherited;
ref->layers[0].dofill = true;
ref->round_translation_to_grid = true;
return( ref );
}
void RefCharsFree(RefChar *ref) {
RefChar *rnext;
while ( ref!=NULL ) {
rnext = ref->next;
RefCharFree(ref);
ref = rnext;
}
}
/* Remove line segments which are just one point long */
/* Merge colinear line segments */
/* Merge two segments each of which involves a single pixel change in one dimension (cut corners) */
static void SimplifyLineList(LineList *prev) {
LineList *next, *lines;
if ( prev->next==NULL )
return;
lines = prev->next;
while ( (next = lines->next)!=NULL ) {
if ( (prev->here.x==lines->here.x && prev->here.y == lines->here.y ) ||
( prev->here.x==lines->here.x && lines->here.x==next->here.x ) ||
( prev->here.y==lines->here.y && lines->here.y==next->here.y ) ||
(( prev->here.x==next->here.x+1 || prev->here.x==next->here.x-1 ) &&
( prev->here.y==next->here.y+1 || prev->here.y==next->here.y-1 )) ) {
lines->here = next->here;
lines->next = next->next;
chunkfree(next,sizeof(*next));
} else {
prev = lines;
lines = next;
}
}
if ( prev!=NULL &&
prev->here.x==lines->here.x && prev->here.y == lines->here.y ) {
prev->next = lines->next;
chunkfree(lines,sizeof(*lines));
lines = prev->next;
}
if ( lines!=NULL ) while ( (next = lines->next)!=NULL ) {
if ( prev->here.x!=next->here.x ) {
bigreal slope = (next->here.y-prev->here.y) / (bigreal) (next->here.x-prev->here.x);
bigreal inter = prev->here.y - slope*prev->here.x;
int y = rint(lines->here.x*slope + inter);
if ( y == lines->here.y ) {
lines->here = next->here;
lines->next = next->next;
chunkfree(next,sizeof(*next));
} else
lines = next;
} else
lines = next;
}
}
typedef struct spline1 {
Spline1D sp;
real s0, s1;
real c0, c1;
} Spline1;
static void FigureSpline1(Spline1 *sp1,bigreal t0, bigreal t1, Spline1D *sp ) {
bigreal s = (t1-t0);
if ( sp->a==0 && sp->b==0 ) {
sp1->sp.d = sp->d + t0*sp->c;
sp1->sp.c = s*sp->c;
sp1->sp.b = sp1->sp.a = 0;
} else {
sp1->sp.d = sp->d + t0*(sp->c + t0*(sp->b + t0*sp->a));
sp1->sp.c = s*(sp->c + t0*(2*sp->b + 3*sp->a*t0));
sp1->sp.b = s*s*(sp->b+3*sp->a*t0);
sp1->sp.a = s*s*s*sp->a;
}
sp1->c0 = sp1->sp.c/3 + sp1->sp.d;
sp1->c1 = sp1->c0 + (sp1->sp.b+sp1->sp.c)/3;
}
static LineList *SplineSegApprox(LineList *last, Spline *spline, bigreal start, bigreal end, real scale) {
/* Divide into n equal segments */
/* (first point is already on the line list) */
/* what's a good value for n? Perhaps the normal distance of the control */
/* points to the line between the end points. */
int i,n;
bigreal t, diff, len;
bigreal x,y;
LineList *cur;
BasePoint startp, endp, slope, off;
bigreal temp;
n = 6;
if ( start==0 && end==1 ) {
/* No different from the latter case, except we can optimize here */
/* and it's easier to understand what is happening in the simple */
/* case */
slope.x = spline->to->me.x - spline->from->me.x;
slope.y = spline->to->me.y - spline->from->me.y;
len = slope.x*slope.x + slope.y*slope.y;
if ( len==0 )
return( last );
len = sqrt(len);
slope.x /= len; slope.y /= len;
off.x = spline->from->nextcp.x - spline->from->me.x;
off.y = spline->from->nextcp.y - spline->from->me.y;
temp = (off.x*slope.y - off.y*slope.x) * scale;
if ( temp<0 ) temp = -temp;
if ( temp>n ) n = temp;
off.x = spline->to->prevcp.x - spline->from->me.x;
off.y = spline->to->prevcp.y - spline->from->me.y;
temp = (off.x*slope.y - off.y*slope.x) * scale;
if ( temp<0 ) temp = -temp;
if ( temp>n ) n = temp;
} else {
Spline1 xsp, ysp;
startp.x = ((spline->splines[0].a*start+spline->splines[0].b)*start+spline->splines[0].c)*start + spline->splines[0].d;
startp.y = ((spline->splines[1].a*start+spline->splines[1].b)*start+spline->splines[1].c)*start + spline->splines[1].d;
endp.x = ((spline->splines[0].a*end+spline->splines[0].b)*end+spline->splines[0].c)*end + spline->splines[0].d;
endp.y = ((spline->splines[1].a*end+spline->splines[1].b)*end+spline->splines[1].c)*end + spline->splines[1].d;
slope.x = endp.x - startp.x;
slope.y = endp.y - startp.y;
FigureSpline1(&xsp,start,end,&spline->splines[0]);
FigureSpline1(&ysp,start,end,&spline->splines[1]);
len = slope.x*slope.x + slope.y*slope.y;
if ( len==0 )
return( last );
len = sqrt(len);
slope.x /= len; slope.y /= len;
off.x = xsp.c0 - startp.x;
off.y = ysp.c0 - startp.y;
temp = (off.x*slope.y - off.y*slope.x) * scale;
if ( temp<0 ) temp = -temp;
if ( temp>n ) n = temp;
off.x = xsp.c1 - endp.x;
off.y = ysp.c1 - endp.y;
temp = (off.x*slope.y - off.y*slope.x) * scale;
if ( temp<0 ) temp = -temp;
if ( temp>n ) n = temp;
}
diff = (end-start)/n;
for ( t=start+diff, i=1; i<=n; ++i, t+=diff ) {
if ( i==n ) t = end; /* Avoid rounding errors */
cur = chunkalloc(sizeof(LineList) );
x = ((spline->splines[0].a*t+spline->splines[0].b)*t+spline->splines[0].c)*t + spline->splines[0].d;
y = ((spline->splines[1].a*t+spline->splines[1].b)*t+spline->splines[1].c)*t + spline->splines[1].d;
cur->here.x = rint(x*scale);
cur->here.y = rint(y*scale);
last->next = cur;
last = cur;
}
return( last );
}
LinearApprox *SplineApproximate(Spline *spline, real scale) {
LinearApprox *test;
LineList *cur, *last=NULL;
extended poi[2], lastt;
int i,n;
for ( test = spline->approx; test!=NULL && test->scale!=scale; test = test->next );
if ( test!=NULL )
return( test );
test = chunkalloc(sizeof(LinearApprox));
test->scale = scale;
test->next = spline->approx;
spline->approx = test;
cur = chunkalloc(sizeof(LineList) );
cur->here.x = rint(spline->from->me.x*scale);
cur->here.y = rint(spline->from->me.y*scale);
test->lines = last = cur;
if ( spline->knownlinear ) {
cur = chunkalloc(sizeof(LineList) );
cur->here.x = rint(spline->to->me.x*scale);
cur->here.y = rint(spline->to->me.y*scale);
last->next = cur;
} else if ( spline->isquadratic ) {
last = SplineSegApprox(last,spline,0,1,scale);
} else {
n = Spline2DFindPointsOfInflection(spline,poi);
lastt=0;
for ( i=0; i<n; ++i ) {
last = SplineSegApprox(last,spline,lastt,poi[i],scale);
lastt = poi[i];
}
last = SplineSegApprox(last,spline,lastt,1,scale);
}
SimplifyLineList(test->lines);
if ( test->lines->next==NULL ) {
test->oneline = 1;
test->onepoint = 1;
} else if ( test->lines->next->next == NULL ) {
test->oneline = 1;
}
return( test );
}
static void SplineFindBounds(const Spline *sp, DBounds *bounds) {
real t, b2_fourac, v;
real min, max;
const Spline1D *sp1;
int i;
/* first try the end points */
for ( i=0; i<2; ++i ) {
sp1 = &sp->splines[i];
if ( i==0 ) {
if ( sp->to->me.x<bounds->minx ) bounds->minx = sp->to->me.x;
if ( sp->to->me.x>bounds->maxx ) bounds->maxx = sp->to->me.x;
min = bounds->minx; max = bounds->maxx;
} else {
if ( sp->to->me.y<bounds->miny ) bounds->miny = sp->to->me.y;
if ( sp->to->me.y>bounds->maxy ) bounds->maxy = sp->to->me.y;
min = bounds->miny; max = bounds->maxy;
}
/* then try the extrema of the spline (assuming they are between t=(0,1) */
/* (I don't bother fixing up for tiny rounding errors here. they don't matter */
/* But we could call CheckExtremaForSingleBitErrors */
if ( sp1->a!=0 ) {
b2_fourac = 4*sp1->b*sp1->b - 12*sp1->a*sp1->c;
if ( b2_fourac>=0 ) {
b2_fourac = sqrt(b2_fourac);
t = (-2*sp1->b + b2_fourac) / (6*sp1->a);
if ( t>0 && t<1 ) {
v = ((sp1->a*t+sp1->b)*t+sp1->c)*t + sp1->d;
if ( v<min ) min = v;
if ( v>max ) max = v;
}
t = (-2*sp1->b - b2_fourac) / (6*sp1->a);
if ( t>0 && t<1 ) {
v = ((sp1->a*t+sp1->b)*t+sp1->c)*t + sp1->d;
if ( v<min ) min = v;
if ( v>max ) max = v;
}
}
} else if ( sp1->b!=0 ) {
t = -sp1->c/(2.0*sp1->b);
if ( t>0 && t<1 ) {
v = (sp1->b*t+sp1->c)*t + sp1->d;
if ( v<min ) min = v;
if ( v>max ) max = v;
}
}
if ( i==0 ) {
bounds->minx = min; bounds->maxx = max;
} else {
bounds->miny = min; bounds->maxy = max;
}
}
}
static void _SplineSetFindBounds(const SplinePointList *spl, DBounds *bounds) {
Spline *spline, *first;
/* Ignore contours consisting of a single point (used for hinting, anchors */
/* for mark to base, etc. */
for ( ; spl!=NULL; spl = spl->next ) if ( spl->first->next!=NULL && spl->first->next->to != spl->first ) {
first = NULL;
if ( bounds->minx==0 && bounds->maxx==0 && bounds->miny==0 && bounds->maxy == 0 ) {
bounds->minx = bounds->maxx = spl->first->me.x;
bounds->miny = bounds->maxy = spl->first->me.y;
} else {
if ( spl->first->me.x<bounds->minx ) bounds->minx = spl->first->me.x;
if ( spl->first->me.x>bounds->maxx ) bounds->maxx = spl->first->me.x;
if ( spl->first->me.y<bounds->miny ) bounds->miny = spl->first->me.y;
if ( spl->first->me.y>bounds->maxy ) bounds->maxy = spl->first->me.y;
}
for ( spline = spl->first->next; spline!=NULL && spline!=first; spline=spline->to->next ) {
SplineFindBounds(spline,bounds);
if ( first==NULL ) first = spline;
}
}
}
static void _SplineSetFindClippedBounds(const SplinePointList *spl, DBounds *bounds,DBounds *clipb) {
Spline *spline, *first;
/* Ignore contours consisting of a single point (used for hinting, anchors */
/* for mark to base, etc. */
for ( ; spl!=NULL; spl = spl->next ) if ( spl->first->next!=NULL && spl->first->next->to != spl->first ) {
first = NULL;
if ( !spl->is_clip_path ) {
if ( bounds->minx==0 && bounds->maxx==0 && bounds->miny==0 && bounds->maxy == 0 ) {
bounds->minx = bounds->maxx = spl->first->me.x;
bounds->miny = bounds->maxy = spl->first->me.y;
} else {
if ( spl->first->me.x<bounds->minx ) bounds->minx = spl->first->me.x;
if ( spl->first->me.x>bounds->maxx ) bounds->maxx = spl->first->me.x;
if ( spl->first->me.y<bounds->miny ) bounds->miny = spl->first->me.y;
if ( spl->first->me.y>bounds->maxy ) bounds->maxy = spl->first->me.y;
}
for ( spline = spl->first->next; spline!=NULL && spline!=first; spline=spline->to->next ) {
SplineFindBounds(spline,bounds);
if ( first==NULL ) first = spline;
}
} else {
if ( clipb->minx==0 && clipb->maxx==0 && clipb->miny==0 && clipb->maxy == 0 ) {
clipb->minx = clipb->maxx = spl->first->me.x;
clipb->miny = clipb->maxy = spl->first->me.y;
} else {
if ( spl->first->me.x<clipb->minx ) clipb->minx = spl->first->me.x;
if ( spl->first->me.x>clipb->maxx ) clipb->maxx = spl->first->me.x;
if ( spl->first->me.y<clipb->miny ) clipb->miny = spl->first->me.y;
if ( spl->first->me.y>clipb->maxy ) clipb->maxy = spl->first->me.y;
}
for ( spline = spl->first->next; spline!=NULL && spline!=first; spline=spline->to->next ) {
SplineFindBounds(spline,clipb);
if ( first==NULL ) first = spline;
}
}
}
}
void SplineSetFindBounds(const SplinePointList *spl, DBounds *bounds) {
DBounds clipb;
memset(bounds,'\0',sizeof(*bounds));
memset(&clipb,'\0',sizeof(clipb));
_SplineSetFindClippedBounds(spl, bounds,&clipb);
if ( clipb.minx!=0 || clipb.miny!=0 || clipb.maxx!=0 || clipb.maxy!=0 ) {
if ( bounds->minx<clipb.minx ) bounds->minx = clipb.minx;
if ( bounds->miny<clipb.miny ) bounds->miny = clipb.miny;
if ( bounds->maxx>clipb.maxx ) bounds->maxx = clipb.maxx;
if ( bounds->maxy>clipb.maxy ) bounds->maxy = clipb.maxy;
}
}
static void _ImageFindBounds(ImageList *img,DBounds *bounds) {
if ( bounds->minx==0 && bounds->maxx==0 && bounds->miny==0 && bounds->maxy == 0 )
*bounds = img->bb;
else if ( img->bb.minx!=0 || img->bb.maxx != 0 || img->bb.maxy != 0 || img->bb.miny!=0 ) {
if ( img->bb.minx < bounds->minx ) bounds->minx = img->bb.minx;
if ( img->bb.miny < bounds->miny ) bounds->miny = img->bb.miny;
if ( img->bb.maxx > bounds->maxx ) bounds->maxx = img->bb.maxx;
if ( img->bb.maxy > bounds->maxy ) bounds->maxy = img->bb.maxy;
}
}
static void _SplineCharLayerFindBounds(SplineChar *sc,int layer, DBounds *bounds) {
RefChar *rf;
ImageList *img;
real e;
DBounds b, clipb;
for ( rf=sc->layers[layer].refs; rf!=NULL; rf = rf->next ) {
if ( bounds->minx==0 && bounds->maxx==0 && bounds->miny==0 && bounds->maxy == 0 )
*bounds = rf->bb;
else if ( rf->bb.minx!=0 || rf->bb.maxx != 0 || rf->bb.maxy != 0 || rf->bb.miny!=0 ) {
if ( rf->bb.minx < bounds->minx ) bounds->minx = rf->bb.minx;
if ( rf->bb.miny < bounds->miny ) bounds->miny = rf->bb.miny;
if ( rf->bb.maxx > bounds->maxx ) bounds->maxx = rf->bb.maxx;
if ( rf->bb.maxy > bounds->maxy ) bounds->maxy = rf->bb.maxy;
}
}
memset(&b,0,sizeof(b));
memset(&clipb,0,sizeof(clipb));
_SplineSetFindClippedBounds(sc->layers[layer].splines,&b,&clipb);
for ( img=sc->layers[layer].images; img!=NULL; img=img->next )
_ImageFindBounds(img,bounds);
if ( sc->layers[layer].dostroke ) {
if ( sc->layers[layer].stroke_pen.width!=WIDTH_INHERITED )
e = sc->layers[layer].stroke_pen.width*sc->layers[layer].stroke_pen.trans[0];
else
e = sc->layers[layer].stroke_pen.trans[0];
b.minx -= e; b.maxx += e;
b.miny -= e; b.maxy += e;
}
if ( clipb.minx!=0 || clipb.miny!=0 || clipb.maxx!=0 || clipb.maxy!=0 ) {
if ( b.minx<clipb.minx ) b.minx = clipb.minx;
if ( b.miny<clipb.miny ) b.miny = clipb.miny;
if ( b.maxx>clipb.maxx ) b.maxx = clipb.maxx;
if ( b.maxy>clipb.maxy ) b.maxy = clipb.maxy;
}
if ( bounds->minx==0 && bounds->maxx==0 && bounds->miny==0 && bounds->maxy == 0 )
*bounds = b;
else if ( b.minx!=0 || b.maxx != 0 || b.maxy != 0 || b.miny!=0 ) {
if ( b.minx < bounds->minx ) bounds->minx = b.minx;
if ( b.miny < bounds->miny ) bounds->miny = b.miny;
if ( b.maxx > bounds->maxx ) bounds->maxx = b.maxx;
if ( b.maxy > bounds->maxy ) bounds->maxy = b.maxy;
}
if ( sc->parent!=NULL && sc->parent->strokedfont &&
(bounds->minx!=bounds->maxx || bounds->miny!=bounds->maxy)) {
real sw = sc->parent->strokewidth;
bounds->minx -= sw; bounds->miny -= sw;
bounds->maxx += sw; bounds->maxy += sw;
}
}
void SplineCharLayerFindBounds(SplineChar *sc,int layer,DBounds *bounds) {
if ( sc->parent!=NULL && sc->parent->multilayer ) {
SplineCharFindBounds(sc,bounds);
return;
}
/* a char with no splines (ie. a space) must have an lbearing of 0 */
bounds->minx = bounds->maxx = 0;
bounds->miny = bounds->maxy = 0;
_SplineCharLayerFindBounds(sc,layer,bounds);
}
void SplineCharFindBounds(SplineChar *sc,DBounds *bounds) {
int i;
int first,last;
/* a char with no splines (ie. a space) must have an lbearing of 0 */
bounds->minx = bounds->maxx = 0;
bounds->miny = bounds->maxy = 0;
first = last = ly_fore;
if ( sc->parent!=NULL && sc->parent->multilayer )
last = sc->layer_cnt-1;
for ( i=first; i<=last; ++i )
_SplineCharLayerFindBounds(sc,i,bounds);
}
void SplineFontLayerFindBounds(SplineFont *sf,int layer,DBounds *bounds) {
int i, k, first, last;
if ( sf->multilayer ) {
SplineFontFindBounds(sf,bounds);
return;
}
bounds->minx = bounds->maxx = 0;
bounds->miny = bounds->maxy = 0;
for ( i = 0; i<sf->glyphcnt; ++i ) {
SplineChar *sc = sf->glyphs[i];
if ( sc!=NULL ) {
first = last = ly_fore;
if ( sc->parent != NULL && sc->parent->multilayer )
last = sc->layer_cnt-1;
for ( k=first; k<=last; ++k )
_SplineCharLayerFindBounds(sc,k,bounds);
}
}
}
void SplineFontFindBounds(SplineFont *sf,DBounds *bounds) {
int i, k, first, last;
bounds->minx = bounds->maxx = 0;
bounds->miny = bounds->maxy = 0;
for ( i = 0; i<sf->glyphcnt; ++i ) {
SplineChar *sc = sf->glyphs[i];
if ( sc!=NULL ) {
first = last = ly_fore;
if ( sf->multilayer )
last = sc->layer_cnt-1;
for ( k=first; k<=last; ++k )
_SplineCharLayerFindBounds(sc,k,bounds);
}
}
}
void CIDLayerFindBounds(SplineFont *cidmaster,int layer,DBounds *bounds) {
SplineFont *sf;
int i;
DBounds b;
real factor;
if ( cidmaster->cidmaster )
cidmaster = cidmaster->cidmaster;
if ( cidmaster->subfonts==NULL ) {
SplineFontLayerFindBounds(cidmaster,layer,bounds);
return;
}
sf = cidmaster->subfonts[0];
SplineFontLayerFindBounds(sf,layer,bounds);
factor = 1000.0/(sf->ascent+sf->descent);
bounds->maxx *= factor; bounds->minx *= factor; bounds->miny *= factor; bounds->maxy *= factor;
for ( i=1; i<cidmaster->subfontcnt; ++i ) {
sf = cidmaster->subfonts[i];
SplineFontLayerFindBounds(sf,layer,&b);
factor = 1000.0/(sf->ascent+sf->descent);
b.maxx *= factor; b.minx *= factor; b.miny *= factor; b.maxy *= factor;
if ( b.maxx>bounds->maxx ) bounds->maxx = b.maxx;
if ( b.maxy>bounds->maxy ) bounds->maxy = b.maxy;
if ( b.miny<bounds->miny ) bounds->miny = b.miny;
if ( b.minx<bounds->minx ) bounds->minx = b.minx;
}
}
static void _SplineSetFindTop(SplineSet *ss,BasePoint *top) {
SplinePoint *sp;
for ( ; ss!=NULL; ss=ss->next ) {
for ( sp=ss->first; ; ) {
if ( sp->me.y > top->y ) *top = sp->me;
if ( sp->next==NULL )
break;
sp = sp->next->to;
if ( sp==ss->first )
break;
}
}
}
void SplineSetQuickBounds(SplineSet *ss,DBounds *b) {
SplinePoint *sp;
b->minx = b->miny = 1e10;
b->maxx = b->maxy = -1e10;
for ( ; ss!=NULL; ss=ss->next ) {
for ( sp=ss->first; ; ) {
if ( sp->me.y < b->miny ) b->miny = sp->me.y;
if ( sp->me.x < b->minx ) b->minx = sp->me.x;
if ( sp->me.y > b->maxy ) b->maxy = sp->me.y;
if ( sp->me.x > b->maxx ) b->maxx = sp->me.x;
// Frank added the control points to the calculation since,
// according to Adam Twardoch,
// the OpenType values that rely upon this function
// expect control points to be included.
if ( !sp->noprevcp ) {
if ( sp->prevcp.y < b->miny ) b->miny = sp->prevcp.y;
if ( sp->prevcp.x < b->minx ) b->minx = sp->prevcp.x;
if ( sp->prevcp.y > b->maxy ) b->maxy = sp->prevcp.y;
if ( sp->prevcp.x > b->maxx ) b->maxx = sp->prevcp.x;
}
if ( !sp->nonextcp ) {
if ( sp->nextcp.y < b->miny ) b->miny = sp->nextcp.y;
if ( sp->nextcp.x < b->minx ) b->minx = sp->nextcp.x;
if ( sp->nextcp.y > b->maxy ) b->maxy = sp->nextcp.y;
if ( sp->nextcp.x > b->maxx ) b->maxx = sp->nextcp.x;
}
if ( sp->next==NULL )
break;
sp = sp->next->to;
if ( sp==ss->first )
break;
}
}
if ( b->minx>65536 ) b->minx = 0;
if ( b->miny>65536 ) b->miny = 0;
if ( b->maxx<-65536 ) b->maxx = 0;
if ( b->maxy<-65536 ) b->maxy = 0;
}
void SplineCharQuickBounds(SplineChar *sc, DBounds *b) {
RefChar *ref;
int i,first, last;
DBounds temp;
real e;
ImageList *img;
b->minx = b->miny = 1e10;
b->maxx = b->maxy = -1e10;
first = last = ly_fore;
if ( sc->parent!=NULL && sc->parent->multilayer )
last = sc->layer_cnt-1;
for ( i=first; i<=last; ++i ) {
SplineSetQuickBounds(sc->layers[i].splines,&temp);
for ( img=sc->layers[i].images; img!=NULL; img=img->next )
_ImageFindBounds(img,b);
if ( sc->layers[i].dostroke && sc->layers[i].splines!=NULL ) {
if ( sc->layers[i].stroke_pen.width!=WIDTH_INHERITED )
e = sc->layers[i].stroke_pen.width*sc->layers[i].stroke_pen.trans[0];
else
e = sc->layers[i].stroke_pen.trans[0];
temp.minx -= e; temp.maxx += e;
temp.miny -= e; temp.maxy += e;
}
if ( temp.minx!=0 || temp.maxx != 0 || temp.maxy != 0 || temp.miny!=0 ) {
if ( temp.minx < b->minx ) b->minx = temp.minx;
if ( temp.miny < b->miny ) b->miny = temp.miny;
if ( temp.maxx > b->maxx ) b->maxx = temp.maxx;
if ( temp.maxy > b->maxy ) b->maxy = temp.maxy;
}
for ( ref = sc->layers[i].refs; ref!=NULL; ref = ref->next ) {
/*SplineSetQuickBounds(ref->layers[0].splines,&temp);*/
if ( b->minx==0 && b->maxx==0 && b->miny==0 && b->maxy == 0 )
*b = ref->bb;
else if ( ref->bb.minx!=0 || ref->bb.maxx != 0 || ref->bb.maxy != 0 || ref->bb.miny!=0 ) {
if ( ref->bb.minx < b->minx ) b->minx = ref->bb.minx;
if ( ref->bb.miny < b->miny ) b->miny = ref->bb.miny;
if ( ref->bb.maxx > b->maxx ) b->maxx = ref->bb.maxx;
if ( ref->bb.maxy > b->maxy ) b->maxy = ref->bb.maxy;
}
}
}
if ( sc->parent!=NULL && sc->parent->strokedfont &&
(b->minx!=b->maxx || b->miny!=b->maxy)) {
real sw = sc->parent->strokewidth;
b->minx -= sw; b->miny -= sw;
b->maxx += sw; b->maxy += sw;
}
if ( b->minx>1e9 )
memset(b,0,sizeof(*b));
}
void SplineCharLayerQuickBounds(SplineChar *sc,int layer,DBounds *bounds) {
RefChar *ref;
DBounds temp;
if ( sc->parent!=NULL && sc->parent->multilayer ) {
SplineCharQuickBounds(sc,bounds);
return;
}
bounds->minx = bounds->miny = 1e10;
bounds->maxx = bounds->maxy = -1e10;
SplineSetQuickBounds(sc->layers[layer].splines,bounds);
for ( ref = sc->layers[layer].refs; ref!=NULL; ref = ref->next ) {
SplineSetQuickBounds(ref->layers[0].splines,&temp);
if ( bounds->minx==0 && bounds->maxx==0 && bounds->miny==0 && bounds->maxy == 0 )
*bounds = temp;
else if ( temp.minx!=0 || temp.maxx != 0 || temp.maxy != 0 || temp.miny!=0 ) {
if ( temp.minx < bounds->minx ) bounds->minx = temp.minx;
if ( temp.miny < bounds->miny ) bounds->miny = temp.miny;
if ( temp.maxx > bounds->maxx ) bounds->maxx = temp.maxx;
if ( temp.maxy > bounds->maxy ) bounds->maxy = temp.maxy;
}
}
/* a char with no splines (ie. a space) must have an lbearing of 0 */
if ( bounds->minx>1e9 )
memset(bounds,0,sizeof(*bounds));
}
void SplineSetQuickConservativeBounds(SplineSet *ss,DBounds *b) {
SplinePoint *sp;
b->minx = b->miny = 1e10;
b->maxx = b->maxy = -1e10;
for ( ; ss!=NULL; ss=ss->next ) {
for ( sp=ss->first; ; ) {
if ( sp->me.y < b->miny ) b->miny = sp->me.y;
if ( sp->me.x < b->minx ) b->minx = sp->me.x;
if ( sp->me.y > b->maxy ) b->maxy = sp->me.y;
if ( sp->me.x > b->maxx ) b->maxx = sp->me.x;
if ( sp->nextcp.y < b->miny ) b->miny = sp->nextcp.y;
if ( sp->nextcp.x < b->minx ) b->minx = sp->nextcp.x;
if ( sp->nextcp.y > b->maxy ) b->maxy = sp->nextcp.y;
if ( sp->nextcp.x > b->maxx ) b->maxx = sp->nextcp.x;
if ( sp->prevcp.y < b->miny ) b->miny = sp->prevcp.y;
if ( sp->prevcp.x < b->minx ) b->minx = sp->prevcp.x;
if ( sp->prevcp.y > b->maxy ) b->maxy = sp->prevcp.y;
if ( sp->prevcp.x > b->maxx ) b->maxx = sp->prevcp.x;
if ( sp->next==NULL )
break;
sp = sp->next->to;
if ( sp==ss->first )
break;
}
}
if ( b->minx>65536 ) b->minx = 0;
if ( b->miny>65536 ) b->miny = 0;
if ( b->maxx<-65536 ) b->maxx = 0;
if ( b->maxy<-65536 ) b->maxy = 0;
}
void SplineCharQuickConservativeBounds(SplineChar *sc, DBounds *b) {
RefChar *ref;
int i, first,last;
DBounds temp;
real e;
ImageList *img;
memset(b,0,sizeof(*b));
first = last = ly_fore;
if ( sc->parent!=NULL && sc->parent->multilayer )
last = sc->layer_cnt-1;
for ( i=first; i<=last; ++i ) {
SplineSetQuickConservativeBounds(sc->layers[i].splines,&temp);
for ( img=sc->layers[i].images; img!=NULL; img=img->next )
_ImageFindBounds(img,b);
if ( sc->layers[i].dostroke && sc->layers[i].splines!=NULL ) {
if ( sc->layers[i].stroke_pen.width!=WIDTH_INHERITED )
e = sc->layers[i].stroke_pen.width*sc->layers[i].stroke_pen.trans[0];
else
e = sc->layers[i].stroke_pen.trans[0];
temp.minx -= e; temp.maxx += e;
temp.miny -= e; temp.maxy += e;
}
if ( temp.minx!=0 || temp.maxx != 0 || temp.maxy != 0 || temp.miny!=0 ) {
if ( temp.minx < b->minx ) b->minx = temp.minx;
if ( temp.miny < b->miny ) b->miny = temp.miny;
if ( temp.maxx > b->maxx ) b->maxx = temp.maxx;
if ( temp.maxy > b->maxy ) b->maxy = temp.maxy;
}
for ( ref = sc->layers[i].refs; ref!=NULL; ref = ref->next ) {
/*SplineSetQuickConservativeBounds(ref->layers[0].splines,&temp);*/
if ( b->minx==0 && b->maxx==0 && b->miny==0 && b->maxy == 0 )
*b = ref->bb;
else if ( ref->bb.minx!=0 || ref->bb.maxx != 0 || ref->bb.maxy != 0 || ref->bb.miny!=0 ) {
if ( ref->bb.minx < b->minx ) b->minx = ref->bb.minx;
if ( ref->bb.miny < b->miny ) b->miny = ref->bb.miny;
if ( ref->bb.maxx > b->maxx ) b->maxx = ref->bb.maxx;
if ( ref->bb.maxy > b->maxy ) b->maxy = ref->bb.maxy;
}
}
}
if ( sc->parent->strokedfont && (b->minx!=b->maxx || b->miny!=b->maxy)) {
real sw = sc->parent->strokewidth;
b->minx -= sw; b->miny -= sw;
b->maxx += sw; b->maxy += sw;
}
}
void SplineFontQuickConservativeBounds(SplineFont *sf,DBounds *b) {
DBounds bb;
int i;
b->minx = b->miny = 1e10;
b->maxx = b->maxy = -1e10;
for ( i=0; i<sf->glyphcnt; ++i ) if ( sf->glyphs[i]!=NULL ) {
SplineCharQuickConservativeBounds(sf->glyphs[i],&bb);
if ( bb.minx < b->minx ) b->minx = bb.minx;
if ( bb.miny < b->miny ) b->miny = bb.miny;
if ( bb.maxx > b->maxx ) b->maxx = bb.maxx;
if ( bb.maxy > b->maxy ) b->maxy = bb.maxy;
}
if ( b->minx>65536 ) b->minx = 0;
if ( b->miny>65536 ) b->miny = 0;
if ( b->maxx<-65536 ) b->maxx = 0;
if ( b->maxy<-65536 ) b->maxy = 0;
}
void SplinePointCategorize(SplinePoint *sp) {
int oldpointtype = sp->pointtype;
sp->pointtype = pt_corner;
if ( sp->next==NULL && sp->prev==NULL )
;
else if ( (sp->next!=NULL && sp->next->to->me.x==sp->me.x && sp->next->to->me.y==sp->me.y) ||
(sp->prev!=NULL && sp->prev->from->me.x==sp->me.x && sp->prev->from->me.y==sp->me.y ))
;
else if ( sp->next==NULL ) {
sp->pointtype = sp->noprevcp ? pt_corner : pt_curve;
} else if ( sp->prev==NULL ) {
sp->pointtype = sp->nonextcp ? pt_corner : pt_curve;
} else if ( sp->nonextcp && sp->noprevcp ) {
;
} else {
BasePoint ndir, ncdir, ncunit, pdir, pcdir, pcunit;
bigreal nlen, nclen, plen, pclen;
bigreal cross, bounds;
ncdir.x = sp->nextcp.x - sp->me.x; ncdir.y = sp->nextcp.y - sp->me.y;
pcdir.x = sp->prevcp.x - sp->me.x; pcdir.y = sp->prevcp.y - sp->me.y;
ndir.x = ndir.y = pdir.x = pdir.y = 0;
if ( sp->next!=NULL ) {
ndir.x = sp->next->to->me.x - sp->me.x; ndir.y = sp->next->to->me.y - sp->me.y;
}
if ( sp->prev!=NULL ) {
pdir.x = sp->prev->from->me.x - sp->me.x; pdir.y = sp->prev->from->me.y - sp->me.y;
}
nclen = sqrt(ncdir.x*ncdir.x + ncdir.y*ncdir.y);
pclen = sqrt(pcdir.x*pcdir.x + pcdir.y*pcdir.y);
nlen = sqrt(ndir.x*ndir.x + ndir.y*ndir.y);
plen = sqrt(pdir.x*pdir.x + pdir.y*pdir.y);
ncunit = ncdir; pcunit = pcdir;
if ( nclen!=0 ) { ncunit.x /= nclen; ncunit.y /= nclen; }
if ( pclen!=0 ) { pcunit.x /= pclen; pcunit.y /= pclen; }
if ( nlen!=0 ) { ndir.x /= nlen; ndir.y /= nlen; }
if ( plen!=0 ) { pdir.x /= plen; pdir.y /= plen; }
/* find out which side has the shorter control vector. Cross that vector */
/* with the normal of the unit vector on the other side. If the */
/* result is less than 1 em-unit then we've got colinear control points */
/* (within the resolution of the integer grid) */
/* Not quite... they could point in the same direction */
if ( oldpointtype==pt_curve )
bounds = 4.0;
else
bounds = 1.0;
if ( nclen!=0 && pclen!=0 &&
((nclen>=pclen && (cross = pcdir.x*ncunit.y - pcdir.y*ncunit.x)<bounds && cross>-bounds ) ||
(pclen>nclen && (cross = ncdir.x*pcunit.y - ncdir.y*pcunit.x)<bounds && cross>-bounds )) &&
ncdir.x*pcdir.x + ncdir.y*pcdir.y < 0 )
sp->pointtype = pt_curve;
/* Cross product of control point with unit vector normal to line in */
/* opposite direction should be less than an em-unit for a tangent */
else if (( nclen==0 && pclen!=0 && (cross = pcdir.x*ndir.y-pcdir.y*ndir.x)<bounds && cross>-bounds ) ||
( pclen==0 && nclen!=0 && (cross = ncdir.x*pdir.y-ncdir.y*pdir.x)<bounds && cross>-bounds ))
sp->pointtype = pt_tangent;
/* If a point started out hv, and could still be hv, them make it so */
/* but don't make hv points de novo, Alexey doesn't like change */
/* (this only works because hv isn't a default setting, so if it's */
/* there it was done intentionally) */
if ( sp->pointtype == pt_curve && oldpointtype == pt_hvcurve &&
((sp->nextcp.x==sp->me.x && sp->prevcp.x==sp->me.x && sp->nextcp.y!=sp->me.y) ||
(sp->nextcp.y==sp->me.y && sp->prevcp.y==sp->me.y && sp->nextcp.x!=sp->me.x)))
sp->pointtype = pt_hvcurve;
}
}
int SplinePointIsACorner(SplinePoint *sp) {
enum pointtype old = sp->pointtype, new;
SplinePointCategorize(sp);
new = sp->pointtype;
sp->pointtype = old;
return( new==pt_corner );
}
void SPLCategorizePoints(SplinePointList *spl) {
Spline *spline, *first, *last=NULL;
for ( ; spl!=NULL; spl = spl->next ) {
first = NULL;
for ( spline = spl->first->next; spline!=NULL && spline!=first; spline=spline->to->next ) {
SplinePointCategorize(spline->from);
last = spline;
if ( first==NULL ) first = spline;
}
if ( spline==NULL && last!=NULL )
SplinePointCategorize(last->to);
}
}
void SPLCategorizePointsKeepCorners(SplinePointList *spl) {
// It's important when round-tripping U. F. O. data that we keep corners as corners and non-corners as non-corners.
Spline *spline, *first, *last=NULL;
int old_type;
for ( ; spl!=NULL; spl = spl->next ) {
first = NULL;
for ( spline = spl->first->next; spline!=NULL && spline!=first; spline=spline->to->next ) {
// If it is a corner, we leave it as a corner.
if ((old_type = spline->from->pointtype) != pt_corner) {
SplinePointCategorize(spline->from);
// If it was not a corner, we do not let it change to a corner.
if (spline->from->pointtype == pt_corner) spline->from->pointtype = old_type;
}
last = spline;
if ( first==NULL ) first = spline;
}
if ( spline==NULL && last!=NULL )
// If it is a corner, we leave it as a corner.
if ((old_type = last->to->pointtype) != pt_corner) {
SplinePointCategorize(last->to);
// If it was not a corner, we do not let it change to a corner.
if (last->to->pointtype == pt_corner) last->to->pointtype = old_type;
}
}
}
void SCCategorizePoints(SplineChar *sc) {
int i;
for ( i=ly_fore; i<sc->layer_cnt; ++i )
SPLCategorizePoints(sc->layers[i].splines);
}
static int CharsNotInEncoding(FontDict *fd) {
int i, cnt, j;
for ( i=cnt=0; i<fd->chars->cnt; ++i ) {
if ( fd->chars->keys[i]!=NULL ) {
for ( j=0; j<256; ++j )
if ( fd->encoding[j]!=NULL &&
strcmp(fd->encoding[j],fd->chars->keys[i])==0 )
break;
if ( j==256 )
++cnt;
}
}
/* And for type 3 fonts... */
if ( fd->charprocs!=NULL ) for ( i=0; i<fd->charprocs->cnt; ++i ) {
if ( fd->charprocs->keys[i]!=NULL ) {
for ( j=0; j<256; ++j )
if ( fd->encoding[j]!=NULL &&
strcmp(fd->encoding[j],fd->charprocs->keys[i])==0 )
break;
if ( j==256 )
++cnt;
}
}
return( cnt );
}
static int LookupCharString(char *encname,struct pschars *chars) {
int k;
if ( encname==NULL ) encname = ".notdef"; /* In case of an incomplete encoding array */
for ( k=0; k<chars->cnt; ++k ) {
if ( chars->keys[k]!=NULL )
if ( strcmp(encname,chars->keys[k])==0 )
return( k );
}
return( -1 );
}
SplinePointList *SplinePointListCopy1(const SplinePointList *spl) {
SplinePointList *cur;
const SplinePoint *pt; SplinePoint *cpt;
Spline *spline;
cur = chunkalloc(sizeof(SplinePointList));
cur->is_clip_path = spl->is_clip_path;
cur->spiro_cnt = cur->spiro_max = 0;
cur->spiros = 0;
if (spl->contour_name != NULL) cur->contour_name = copy(spl->contour_name);
for ( pt=spl->first; ; ) {
cpt = SplinePointCreate( 0, 0 );
*cpt = *pt;
if ( pt->hintmask!=NULL ) {
cpt->hintmask = chunkalloc(sizeof(HintMask));
memcpy(cpt->hintmask,pt->hintmask,sizeof(HintMask));
}
if ( pt->name!=NULL ) {
cpt->name = copy(pt->name);
}
cpt->next = cpt->prev = NULL;
if ( cur->first==NULL ) {
cur->first = cur->last = cpt;
cur->start_offset = 0;
} else {
spline = chunkalloc(sizeof(Spline));
*spline = *pt->prev;
spline->from = cur->last;
cur->last->next = spline;
cpt->prev = spline;
spline->to = cpt;
spline->approx = NULL;
cur->last = cpt;
}
if ( pt->next==NULL )
break;
pt = pt->next->to;
if ( pt==spl->first )
break;
}
if ( spl->first->prev!=NULL ) {
cpt = cur->first;
spline = chunkalloc(sizeof(Spline));
*spline = *pt->prev;
spline->from = cur->last;
cur->last->next = spline;
cpt->prev = spline;
spline->to = cpt;
spline->approx = NULL;
cur->last = cpt;
}
if ( spl->spiro_cnt!=0 ) {
cur->spiro_cnt = cur->spiro_max = spl->spiro_cnt;
cur->spiros = malloc(cur->spiro_cnt*sizeof(spiro_cp));
memcpy(cur->spiros,spl->spiros,cur->spiro_cnt*sizeof(spiro_cp));
}
return( cur );
}
/* If this routine is called we are guarenteed that:
at least one point on the splineset is selected
not all points on the splineset are selected
*/
static SplinePointList *SplinePointListCopySelected1(SplinePointList *spl) {
SplinePointList *head=NULL, *last=NULL, *cur;
SplinePoint *cpt, *first, *start;
Spline *spline;
start = spl->first;
if ( spl->first==spl->last ) {
/* If it's a closed contour and the start point is selected then we */
/* don't know where that selection began (and we have to keep it with */
/* the things that precede it when we make the new splinesets), so */
/* loop until we find something unselected */
while ( start->selected )
start = start->next->to;
}
first = NULL;
while ( start != NULL && start!=first ) {
while ( start!=NULL && start!=first && !start->selected ) {
if ( first==NULL ) first = start;
if ( start->next==NULL ) {
start = NULL;
break;
}
start = start->next->to;
}
if ( start==NULL || start==first )
break;
cur = chunkalloc(sizeof(SplinePointList));
if ( head==NULL )
head = cur;
else
last->next = cur;
last = cur;
while ( start!=NULL && start->selected && start!=first ) {
cpt = chunkalloc(sizeof(SplinePoint));
*cpt = *start;
cpt->hintmask = NULL;
cpt->name = NULL;
cpt->next = cpt->prev = NULL;
if ( cur->first==NULL ) {
cur->first = cur->last = cpt;
cur->start_offset = 0;
} else {
spline = chunkalloc(sizeof(Spline));
*spline = *start->prev;
spline->from = cur->last;
cur->last->next = spline;
cpt->prev = spline;
spline->to = cpt;
spline->approx = NULL;
cur->last = cpt;
}
if ( first==NULL ) first = start;
if ( start->next==NULL ) {
start = NULL;
break;
}
start = start->next->to;
}
}
return( head );
}
/* If this routine is called we are guarenteed that:
at least one point on the splineset is selected
not all points on the splineset are selected
*/
static SplinePointList *SplinePointListCopySpiroSelected1(SplinePointList *spl) {
SplinePointList *head=NULL, *last=NULL, *cur;
int i,j;
spiro_cp *list = spl->spiros, *freeme = NULL, *temp = NULL;
if ( !SPIRO_SPL_OPEN(spl)) {
/* If it's a closed contour and the start point is selected then we */
/* don't know where that selection began (and we have to keep it with */
/* the things that precede it when we make the new splinesets), so */
/* loop until we find something unselected */
for ( i=0 ; i<spl->spiro_cnt-1; ++i )
if ( !(SPIRO_SELECTED(&list[i])) )
break;
if ( i!=0 ) {
freeme = malloc(spl->spiro_cnt*sizeof(spiro_cp));
memcpy(freeme,list+i,(spl->spiro_cnt-1-i)*sizeof(spiro_cp));
memcpy(freeme+(spl->spiro_cnt-1-i),list,i*sizeof(spiro_cp));
/* And copy the list terminator */
memcpy(freeme+spl->spiro_cnt-1,list+spl->spiro_cnt-1,sizeof(spiro_cp));
list = freeme;
}
}
for ( i=0 ; i<spl->spiro_cnt-1; ) {
/* Skip unselected things */
for ( ; i<spl->spiro_cnt-1 && !SPIRO_SELECTED(&list[i]); ++i );
if ( i==spl->spiro_cnt-1 )
break;
for ( j=i; j<spl->spiro_cnt-1 && SPIRO_SELECTED(&list[j]); ++j );
temp = malloc((j-i+2)*sizeof(spiro_cp));
memcpy(temp,list+i,(j-i)*sizeof(spiro_cp));
temp[0].ty = SPIRO_OPEN_CONTOUR;
memset(temp+(j-i),0,sizeof(spiro_cp));
temp[j-i].ty = SPIRO_END;
cur = SpiroCP2SplineSet( temp );
if ( head==NULL )
head = cur;
else
last->next = cur;
last = cur;
i = j;
}
return( head );
}
SplinePointList *SplinePointListCopy(const SplinePointList *base) {
SplinePointList *head=NULL, *last=NULL, *cur;
for ( ; base!=NULL; base = base->next ) {
cur = SplinePointListCopy1(base);
if ( head==NULL )
head = cur;
else
last->next = cur;
last = cur;
}
return( head );
}
SplinePointList *SplinePointListCopySelected(SplinePointList *base) {
SplinePointList *head=NULL, *last=NULL, *cur=NULL;
SplinePoint *pt, *first;
int anysel, allsel;
for ( ; base!=NULL; base = base->next ) {
anysel = false; allsel = true;
first = NULL;
for ( pt=base->first; pt!=NULL && pt!=first; pt = pt->next->to ) {
if ( pt->selected ) anysel = true;
else allsel = false;
if ( first==NULL ) first = pt;
if ( pt->next==NULL )
break;
}
if ( allsel )
cur = SplinePointListCopy1(base);
else if ( anysel )
cur = SplinePointListCopySelected1(base);
if ( anysel ) {
if ( head==NULL )
head = cur;
else
last->next = cur;
for ( last = cur; last->next ; last = last->next );
}
}
return( head );
}
SplinePointList *SplinePointListCopySpiroSelected(SplinePointList *base) {
SplinePointList *head=NULL, *last=NULL, *cur=NULL;
int anysel, allsel;
int i;
for ( ; base!=NULL; base = base->next ) {
anysel = false; allsel = true;
for ( i=0; i<base->spiro_cnt-1; ++i ) {
if ( SPIRO_SELECTED(&base->spiros[i]) )
anysel = true;
else
allsel = false;
}
if ( allsel )
cur = SplinePointListCopy1(base);
else if ( anysel )
cur = SplinePointListCopySpiroSelected1(base);
if ( anysel ) {
if ( head==NULL )
head = cur;
else
last->next = cur;
for ( last = cur; last->next ; last = last->next );
}
}
return( head );
}
static SplinePointList *SplinePointListSplitSpiros(SplineChar *sc,SplinePointList *spl) {
SplinePointList *head=NULL, *last=NULL, *cur;
int i;
spiro_cp *list = spl->spiros, *freeme = NULL, *temp = NULL;
if ( !SPIRO_SPL_OPEN(spl)) {
/* If it's a closed contour and the start point is selected then we */
/* don't know where that selection began (and we have to keep it with */
/* the things that precede it when we make the new splinesets), so */
/* loop until we find something unselected */
for ( i=0 ; i<spl->spiro_cnt-1; ++i )
if ( !(SPIRO_SELECTED(&list[i])) )
break;
if ( i!=0 ) {
freeme = malloc(spl->spiro_cnt*sizeof(spiro_cp));
memcpy(freeme,list+i,(spl->spiro_cnt-1-i)*sizeof(spiro_cp));
memcpy(freeme+(spl->spiro_cnt-1-i),list,i*sizeof(spiro_cp));
/* And copy the list terminator */
memcpy(freeme+spl->spiro_cnt-1,list+spl->spiro_cnt-1,sizeof(spiro_cp));
list = freeme;
}
}
for ( i=0 ; i<spl->spiro_cnt-1; ) {
int start = i;
/* Retain unselected things */
for ( ; i<spl->spiro_cnt-1 && !SPIRO_SELECTED(&list[i]); ++i );
if ( i!=start ) {
temp = malloc((i-start+2)*sizeof(spiro_cp));
memcpy(temp,list+start,(i-start)*sizeof(spiro_cp));
temp[0].ty = SPIRO_OPEN_CONTOUR;
memset(temp+(i-start),0,sizeof(spiro_cp));
temp[i-start].ty = SPIRO_END;
cur = SpiroCP2SplineSet( temp );
if ( head==NULL )
head = cur;
else
last->next = cur;
last = cur;
}
for ( ; i<spl->spiro_cnt-1 && SPIRO_SELECTED(&list[i]); ++i );
}
SplinePointListFree(spl);
return( head );
}
static SplinePointList *SplinePointListSplit(SplineChar *sc,SplinePointList *spl) {
SplinePointList *head=NULL, *last=NULL, *cur;
SplinePoint *first, *start, *next;
start = spl->first;
if ( spl->first==spl->last ) {
/* If it's a closed contour and the start point is selected then we */
/* don't know where that selection began (and we have to keep it with */
/* the things that precede it when we make the new splinesets), so */
/* loop until we find something unselected */
while ( !start->selected )
start = start->next->to;
}
first = NULL;
while ( start != NULL && start!=first ) {
while ( start!=NULL && start!=first && start->selected ) {
if ( first==NULL ) first = start;
if ( start->prev!=NULL ) {
start->prev->from->next = NULL;
SplineFree(start->prev);
}
if ( start->next!=NULL ) {
next = start->next->to;
next->prev = NULL;
SplineFree(start->next);
} else
next = NULL;
SplinePointMDFree(sc,start);
start = next;
}
if ( start==NULL || start==first )
break;
if ( head==NULL ) {
head = cur = spl;
spl->first = spl->last = NULL;
spl->start_offset = 0;
} else {
cur = chunkalloc(sizeof(SplinePointList));
last->next = cur;
}
last = cur;
while ( start!=NULL && !start->selected && start!=first ) {
if ( cur->first==NULL ) {
cur->first = start;
cur->start_offset = 0;
}
cur->last = start;
if ( start->next!=NULL ) {
next = start->next->to;
if ( next->selected ) {
SplineFree(start->next);
start->next = NULL;
next->prev = NULL;
}
} else
next = NULL;
if ( first==NULL ) first = start;
start = next;
}
}
return( last );
}
SplinePointList *SplinePointListRemoveSelected(SplineChar *sc,SplinePointList *base) {
SplinePointList *head=NULL, *last=NULL, *next;
SplinePoint *pt, *first;
int anysel, allsel;
for ( ; base!=NULL; base = next ) {
next = base->next;
anysel = false; allsel = true;
if ( !sc->inspiro || !hasspiro()) {
first = NULL;
for ( pt=base->first; pt!=NULL && pt!=first; pt = pt->next->to ) {
if ( pt->selected ) anysel = true;
else allsel = false;
if ( first==NULL ) first = pt;
if ( pt->next==NULL )
break;
}
} else {
int i;
for ( i=0; i<base->spiro_cnt; ++i ) {
if ( SPIRO_SELECTED(&base->spiros[i]) )
anysel = true;
else
allsel = false;
}
}
if ( allsel ) {
SplinePointListMDFree(sc,base);
continue;
}
if ( !sc->inspiro || !anysel || !hasspiro()) {
if ( head==NULL )
head = base;
else
last->next = base;
last = base;
if ( anysel )
last = SplinePointListSplit(sc,base);
} else {
SplineSet *ret;
ret = SplinePointListSplitSpiros(sc,base);
if ( head==NULL )
head = ret;
else
last->next = ret;
if ( ret!=NULL )
for ( last=ret; last->next!=NULL; last=last->next );
}
}
if ( last!=NULL ) last->next = NULL;
return( head );
}
ImageList *ImageListCopy(ImageList *cimg) {
ImageList *head=NULL, *last=NULL, *new;
for ( ; cimg!=NULL; cimg=cimg->next ) {
new = chunkalloc(sizeof(ImageList));
*new = *cimg;
new->next = NULL;
if ( last==NULL )
head = last = new;
else {
last->next = new;
last = new;
}
}
return( head );
}
ImageList *ImageListTransform(ImageList *img, real transform[6],int everything) {
ImageList *head = img;
/* Don't support rotating, flipping or skewing images */;
if ( transform[0]!=0 && transform[3]!=0 ) {
while ( img!=NULL ) {
if ( everything || (!everything && img->selected)) {
bigreal x = img->xoff;
img->xoff = transform[0]*x + transform[2]*img->yoff + transform[4];
img->yoff = transform[1]*x + transform[3]*img->yoff + transform[5];
if (( img->xscale *= transform[0])<0 ) {
img->xoff += img->xscale *
(img->image->list_len==0?img->image->u.image:img->image->u.images[0])->width;
img->xscale = -img->xscale;
}
if (( img->yscale *= transform[3])<0 ) {
img->yoff += img->yscale *
(img->image->list_len==0?img->image->u.image:img->image->u.images[0])->height;
img->yscale = -img->yscale;
}
img->bb.minx = img->xoff; img->bb.maxy = img->yoff;
img->bb.maxx = img->xoff + GImageGetWidth(img->image)*img->xscale;
img->bb.miny = img->yoff - GImageGetHeight(img->image)*img->yscale;
}
img = img->next;
}
}
return( head );
}
void BpTransform(BasePoint *to, BasePoint *from, real transform[6]) {
BasePoint p;
p.x = transform[0]*from->x + transform[2]*from->y + transform[4];
p.y = transform[1]*from->x + transform[3]*from->y + transform[5];
to->x = rint(1024*p.x)/1024;
to->y = rint(1024*p.y)/1024;
}
void ApTransform(AnchorPoint *ap, real transform[6]) {
BpTransform(&ap->me,&ap->me,transform);
}
static void TransformPointExtended(SplinePoint *sp, real transform[6], enum transformPointMask tpmask )
{
/**
* If we are to transform selected BCP instead of their base splinepoint
* then lets do that.
*/
if( tpmask & tpmask_operateOnSelectedBCP
&& (sp->nextcpselected || sp->prevcpselected ))
{
if( sp->nextcpselected )
{
int order2 = sp->next ? sp->next->order2 : 0;
BpTransform(&sp->nextcp,&sp->nextcp,transform);
SPTouchControl( sp, &sp->nextcp, order2 );
}
else if( sp->prevcpselected )
{
int order2 = sp->next ? sp->next->order2 : 0;
BpTransform(&sp->prevcp,&sp->prevcp,transform);
SPTouchControl( sp, &sp->prevcp, order2 );
}
}
else
{
/**
* Transform the base splinepoints.
*/
BpTransform(&sp->me,&sp->me,transform);
if ( !sp->nonextcp )
{
BpTransform(&sp->nextcp,&sp->nextcp,transform);
}
else
{
sp->nextcp = sp->me;
}
if ( !sp->noprevcp )
{
BpTransform(&sp->prevcp,&sp->prevcp,transform);
}
else
{
sp->prevcp = sp->me;
}
}
if ( sp->pointtype == pt_hvcurve )
{
if(
((sp->nextcp.x==sp->me.x && sp->prevcp.x==sp->me.x && sp->nextcp.y!=sp->me.y) ||
(sp->nextcp.y==sp->me.y && sp->prevcp.y==sp->me.y && sp->nextcp.x!=sp->me.x)))
{
/* Do Nothing */;
}
else
{
sp->pointtype = pt_curve;
}
}
}
static void TransformPoint(SplinePoint *sp, real transform[6])
{
TransformPointExtended( sp, transform, 0 );
}
static void TransformSpiro(spiro_cp *cp, real transform[6]) {
bigreal x;
x = transform[0]*cp->x + transform[2]*cp->y + transform[4];
cp->y = transform[1]*cp->x + transform[3]*cp->y + transform[5];
cp->x = x;
}
static void TransformPTsInterpolateCPs(BasePoint *fromorig,Spline *spline,
BasePoint *toorig,real transform[6] ) {
BasePoint totrans, temp;
bigreal fraction;
/* Normally the "from" point will already have been translated, and the "to" */
/* point will need to be. But if we have a closed contour then on the */
/* last spline both from and to will have been transform. We can detect */
/* this because toorig will be different from &spline->to->me */
if ( spline->to->selected && toorig==&spline->to->me )
BpTransform(&totrans,&spline->to->me,transform);
else
totrans = spline->to->me;
/* None of the control points will have been transformed yet */
if ( fromorig->x!=toorig->x ) {
fraction = (spline->from->nextcp.x-fromorig->x)/( toorig->x-fromorig->x );
spline->from->nextcp.x = spline->from->me.x + fraction*( totrans.x-spline->from->me.x );
fraction = (spline->to->prevcp.x-fromorig->x)/( toorig->x-fromorig->x );
spline->to->prevcp.x = spline->from->me.x + fraction*( totrans.x-spline->from->me.x );
} else {
BpTransform(&temp,&spline->from->nextcp,transform);
spline->from->nextcp.x = temp.x;
BpTransform(&temp,&spline->to->prevcp,transform);
spline->to->prevcp.x = temp.x;
}
if ( fromorig->y!=toorig->y ) {
fraction = (spline->from->nextcp.y-fromorig->y)/( toorig->y-fromorig->y );
spline->from->nextcp.y = spline->from->me.y + fraction*( totrans.y-spline->from->me.y );
fraction = (spline->to->prevcp.y-fromorig->y)/( toorig->y-fromorig->y );
spline->to->prevcp.y = spline->from->me.y + fraction*( totrans.y-spline->from->me.y );
} else {
BpTransform(&temp,&spline->from->nextcp,transform);
spline->from->nextcp.y = temp.y;
BpTransform(&temp,&spline->to->prevcp,transform);
spline->to->prevcp.y = temp.y;
}
if ( spline->to->selected )
spline->to->me = totrans;
}
SplinePointList *SplinePointListTransformExtended(SplinePointList *base, real transform[6],
enum transformPointType tpt, enum transformPointMask tpmask ) {
Spline *spline, *first;
SplinePointList *spl;
SplinePoint *spt, *pfirst;
int allsel, anysel, alldone=true;
BasePoint lastpointorig, firstpointorig, orig;
for ( spl = base; spl!=NULL; spl = spl->next ) {
pfirst = NULL; first = NULL;
allsel = true; anysel=false;
if ( tpt==tpt_OnlySelectedInterpCPs && spl->first->next!=NULL && !spl->first->next->order2 ) {
lastpointorig = firstpointorig = spl->first->me;
printf("SplinePointListTransformExtended() spl->first->selected %d\n", spl->first->selected );
if ( spl->first->selected ) {
anysel = true;
BpTransform(&spl->first->me,&spl->first->me,transform);
} else
allsel = false;
for ( spline = spl->first->next; spline!=NULL && spline!=first; spline=spline->to->next ) {
if ( first==NULL ) first = spline;
orig = spline->to->me;
if ( spline->from->selected || spline->to->selected )
{
TransformPTsInterpolateCPs( &lastpointorig, spline,
spl->first==spline->to? &firstpointorig : &spline->to->me,
transform );
}
lastpointorig = orig;
if ( spline->to->selected ) anysel = true; else allsel = false;
}
} else {
for ( spt = spl->first ; spt!=pfirst; spt = spt->next->to ) {
if ( pfirst==NULL ) pfirst = spt;
if ( tpt==tpt_AllPoints || spt->selected ) {
TransformPointExtended(spt,transform,tpmask);
if ( tpt!=tpt_AllPoints ) {
if ( spt->next!=NULL && spt->next->order2 && !spt->next->to->selected && spt->next->to->ttfindex==0xffff ) {
SplinePoint *to = spt->next->to;
to->prevcp = spt->nextcp;
to->me.x = (to->prevcp.x+to->nextcp.x)/2;
to->me.y = (to->prevcp.y+to->nextcp.y)/2;
}
if ( spt->prev!=NULL && spt->prev->order2 && !spt->prev->from->selected && spt->prev->from->ttfindex==0xffff ) {
SplinePoint *from = spt->prev->from;
from->nextcp = spt->prevcp;
from->me.x = (from->prevcp.x+from->nextcp.x)/2;
from->me.y = (from->prevcp.y+from->nextcp.y)/2;
}
}
anysel = true;
} else
allsel = alldone = false;
if ( spt->next==NULL )
break;
}
}
if ( !anysel ) /* This splineset had no selected points it's unchanged */
continue;
/* If we changed all the points, then transform the spiro version too */
/* otherwise if we just changed some points, throw away the spiro */
if ( allsel ) {
int i;
for ( i=0; i<spl->spiro_cnt-1; ++i )
TransformSpiro(&spl->spiros[i], transform);
} else
SplineSetSpirosClear(spl);
/* if we changed all the points then the control points are right */
/* otherwise those near the edges may be wonky, fix 'em up */
/* Figuring out where the edges of the selection are is difficult */
/* so let's just tweak all points, it shouldn't matter */
/* It does matter. Let's tweak all default points */
if( !(tpmask & tpmask_dontFixControlPoints))
{
if ( tpt!=tpt_AllPoints && !allsel && spl->first->next!=NULL && !spl->first->next->order2 )
{
pfirst = NULL;
for ( spt = spl->first ; spt!=pfirst; spt = spt->next->to )
{
if ( pfirst==NULL ) pfirst = spt;
if ( spt->selected && spt->prev!=NULL && !spt->prev->from->selected &&
spt->prev->from->pointtype == pt_tangent )
SplineCharTangentPrevCP(spt->prev->from);
if ( spt->selected && spt->next!=NULL && !spt->next->to->selected &&
spt->next->to->pointtype == pt_tangent )
SplineCharTangentNextCP(spt->next->to);
if ( spt->prev!=NULL && spt->prevcpdef && tpt==tpt_OnlySelected )
SplineCharDefaultPrevCP(spt);
if ( spt->next==NULL )
break;
if ( spt->nextcpdef && tpt==tpt_OnlySelected )
SplineCharDefaultNextCP(spt);
}
}
}
first = NULL;
for ( spline = spl->first->next; spline!=NULL && spline!=first; spline=spline->to->next ) {
if ( !alldone ) SplineRefigureFixup(spline); else SplineRefigure(spline);
if ( first==NULL ) first = spline;
}
}
return( base );
}
SplinePointList *SplinePointListTransform( SplinePointList *base, real transform[6],
enum transformPointType tpt )
{
enum transformPointMask tpmask = 0;
return SplinePointListTransformExtended( base, transform, tpt, tpmask );
}
SplinePointList *SplinePointListSpiroTransform(SplinePointList *base, real transform[6], int allpoints ) {
SplinePointList *spl;
int allsel, anysel;
int i;
if ( allpoints )
return( SplinePointListTransform(base,transform,tpt_AllPoints));
for ( spl = base; spl!=NULL; spl = spl->next ) {
allsel = true; anysel=false;
for ( i=0; i<spl->spiro_cnt-1; ++i )
if ( spl->spiros[i].ty & 0x80 )
anysel = true;
else
allsel = false;
if ( !anysel )
continue;
if ( allsel ) {
SplinePointList *next = spl->next;
/* If we are transforming everything, then we can just transform */
/* the beziers too */
spl->next = NULL;
SplinePointListTransform(spl,transform,tpt_AllPoints);
spl->next = next;
continue;
}
/* If we are transformings some things, then we need to transform the */
/* selected spiros and then regenerate the beziers */
for ( i=0; i<spl->spiro_cnt-1; ++i )
if ( spl->spiros[i].ty & 0x80 )
TransformSpiro(&spl->spiros[i], transform);
SSRegenerateFromSpiros(spl);
}
return( base );
}
SplinePointList *SplinePointListShift(SplinePointList *base,real xoff,enum transformPointType allpoints ) {
real transform[6];
if ( xoff==0 )
return( base );
transform[0] = transform[3] = 1;
transform[1] = transform[2] = transform[5] = 0;
transform[4] = xoff;
return( SplinePointListTransform(base,transform,allpoints));
}
HintMask *HintMaskFromTransformedRef(RefChar *ref,BasePoint *trans,
SplineChar *basesc,HintMask *hm) {
StemInfo *st, *st2;
int hst_cnt, bcnt;
real start, width;
int i;
if ( ref->transform[1]!=0 || ref->transform[2]!=0 )
return(NULL);
memset(hm,0,sizeof(HintMask));
for ( st = ref->sc->hstem; st!=NULL; st=st->next ) {
start = st->start*ref->transform[3] + ref->transform[5] + trans->y;
width = st->width*ref->transform[3];
for ( st2=basesc->hstem,bcnt=0; st2!=NULL; st2=st2->next, bcnt++ )
if ( st2->start == start && st2->width == width )
break;
if ( st2!=NULL )
(*hm)[bcnt>>3] |= (0x80>>(bcnt&7));
}
for ( st2=basesc->hstem,hst_cnt=0; st2!=NULL; st2=st2->next, hst_cnt++ );
for ( st = ref->sc->vstem; st!=NULL; st=st->next ) {
start = st->start*ref->transform[0] + ref->transform[4] + trans->x;
width = st->width*ref->transform[0];
for ( st2=basesc->vstem,bcnt=hst_cnt; st2!=NULL; st2=st2->next, bcnt++ )
if ( st2->start == start && st2->width == width )
break;
if ( st2!=NULL )
(*hm)[bcnt>>3] |= (0x80>>(bcnt&7));
}
for ( i=0; i<HntMax/8; ++i )
if ( (*hm)[i]!=0 )
return( hm );
return( NULL );
}
static HintMask *HintMaskTransform(HintMask *oldhm,real transform[6],
SplineChar *basesc,SplineChar *subsc) {
HintMask *newhm;
StemInfo *st, *st2;
int cnt, hst_cnt, bcnt;
real start, width;
if ( transform[1]!=0 || transform[2]!=0 )
return( NULL );
newhm = chunkalloc(sizeof(HintMask));
for ( st = subsc->hstem,cnt = 0; st!=NULL; st=st->next, cnt++ ) {
if ( (*oldhm)[cnt>>3]&(0x80>>(cnt&7)) ) {
start = st->start*transform[3] + transform[5];
width = st->width*transform[3];
for ( st2=basesc->hstem,bcnt=0; st2!=NULL; st2=st2->next, bcnt++ )
if ( st2->start == start && st2->width == width )
break;
if ( st2!=NULL )
(*newhm)[bcnt>>3] |= (0x80>>(bcnt&7));
}
}
for ( st2=basesc->hstem,hst_cnt=0; st2!=NULL; st2=st2->next, hst_cnt++ );
for ( st = subsc->vstem; st!=NULL; st=st->next, cnt++ ) {
if ( (*oldhm)[cnt>>3]&(0x80>>(cnt&7)) ) {
start = st->start*transform[0] + transform[4];
width = st->width*transform[0];
for ( st2=basesc->vstem,bcnt=hst_cnt; st2!=NULL; st2=st2->next, bcnt++ )
if ( st2->start == start && st2->width == width )
break;
if ( st2!=NULL )
(*newhm)[bcnt>>3] |= (0x80>>(bcnt&7));
}
}
return( newhm );
}
SplinePointList *SPLCopyTranslatedHintMasks(SplinePointList *base,
SplineChar *basesc, SplineChar *subsc, BasePoint *trans ) {
SplinePointList *spl, *spl2, *head;
SplinePoint *spt, *spt2, *pfirst;
real transform[6];
Spline *s, *first;
head = SplinePointListCopy(base);
transform[0] = transform[3] = 1; transform[1] = transform[2] = 0;
transform[4] = trans->x; transform[5] = trans->y;
for ( spl = head, spl2=base; spl!=NULL; spl = spl->next, spl2 = spl2->next ) {
pfirst = NULL;
for ( spt = spl->first, spt2 = spl2->first ; spt!=pfirst; spt = spt->next->to, spt2 = spt2->next->to ) {
if ( pfirst==NULL ) pfirst = spt;
TransformPoint(spt,transform);
if ( spt2->hintmask ) {
chunkfree(spt->hintmask,sizeof(HintMask));
spt->hintmask = HintMaskTransform(spt2->hintmask,transform,basesc,subsc);
}
if ( spt->next==NULL )
break;
}
first = NULL;
for ( s = spl->first->next; s!=NULL && s!=first; s=s->to->next ) {
SplineRefigure(s);
if ( first==NULL ) first = s;
}
}
return( head );
}
static SplinePointList *_SPLCopyTransformedHintMasks(SplineChar *subsc,int layer,
real transform[6], SplineChar *basesc ) {
SplinePointList *spl, *spl2, *head, *last=NULL, *cur, *base;
SplinePoint *spt, *spt2, *pfirst;
Spline *s, *first;
real trans[6];
RefChar *rf;
base = subsc->layers[layer].splines;
head = SplinePointListCopy(base);
if ( head!=NULL )
for ( last = head; last->next!=NULL; last=last->next );
for ( spl = head, spl2=base; spl!=NULL; spl = spl->next, spl2=spl2->next ) {
pfirst = NULL;
for ( spt = spl->first, spt2 = spl2->first ; spt!=pfirst; spt = spt->next->to, spt2 = spt2->next->to ) {
if ( pfirst==NULL ) pfirst = spt;
TransformPoint(spt,transform);
if ( spt2->hintmask ) {
chunkfree(spt->hintmask,sizeof(HintMask));
spt->hintmask = HintMaskTransform(spt2->hintmask,transform,basesc,subsc);
}
if ( spt->next==NULL )
break;
}
first = NULL;
for ( s = spl->first->next; s!=NULL && s!=first; s=s->to->next ) {
SplineRefigure(s);
if ( first==NULL ) first = s;
}
}
for ( rf=subsc->layers[layer].refs; rf!=NULL; rf=rf->next ) {
trans[0] = rf->transform[0]*transform[0] +
rf->transform[1]*transform[2];
trans[1] = rf->transform[0]*transform[1] +
rf->transform[1]*transform[3];
trans[2] = rf->transform[2]*transform[0] +
rf->transform[3]*transform[2];
trans[3] = rf->transform[2]*transform[1] +
rf->transform[3]*transform[3];
trans[4] = rf->transform[4]*transform[0] +
rf->transform[5]*transform[2] +
transform[4];
trans[5] = rf->transform[4]*transform[1] +
rf->transform[5]*transform[3] +
transform[5];
cur = _SPLCopyTransformedHintMasks(rf->sc,layer,trans,basesc);
if ( head==NULL )
head = cur;
else
last->next = cur;
if ( cur!=NULL ) {
while ( cur->next!=NULL ) cur = cur->next;
last = cur;
}
}
return( head );
}
SplinePointList *SPLCopyTransformedHintMasks(RefChar *r,
SplineChar *basesc, BasePoint *trans,int layer ) {
real transform[6];
memcpy(transform,r->transform,sizeof(transform));
transform[4] += trans->x; transform[5] += trans->y;
return( _SPLCopyTransformedHintMasks(r->sc,layer,transform,basesc));
}
void SplinePointListSelect(SplinePointList *spl,int sel) {
Spline *spline, *first;
for ( ; spl!=NULL; spl = spl->next ) {
first = NULL;
spl->first->selected = sel;
for ( spline = spl->first->next; spline!=NULL && spline!=first; spline=spline->to->next ) {
spline->to->selected = sel;
if ( first==NULL ) first = spline;
}
}
}
void SCMakeDependent(SplineChar *dependent,SplineChar *base) {
struct splinecharlist *dlist;
if ( dependent->searcherdummy )
return;
for ( dlist=base->dependents; dlist!=NULL && dlist->sc!=dependent; dlist = dlist->next);
if ( dlist==NULL ) {
dlist = chunkalloc(sizeof(struct splinecharlist));
dlist->sc = dependent;
dlist->next = base->dependents;
base->dependents = dlist;
}
}
static void InstanciateReference(SplineFont *sf, RefChar *topref, RefChar *refs,
real transform[6], SplineChar *dsc, int layer) {
real trans[6];
RefChar *rf;
SplineChar *rsc;
SplinePointList *spl, *new;
int i;
if ( !refs->checked ) {
if ( refs->sc!=NULL )
i = refs->sc->orig_pos; /* Can happen in type3 fonts */
else for ( i=0; i<sf->glyphcnt; ++i ) if ( sf->glyphs[i]!=NULL )
if ( strcmp(sf->glyphs[i]->name,AdobeStandardEncoding[refs->adobe_enc])==0 )
break;
if ( i!=sf->glyphcnt && !sf->glyphs[i]->ticked ) {
refs->checked = true;
refs->sc = rsc = sf->glyphs[i];
refs->orig_pos = rsc->orig_pos;
refs->unicode_enc = rsc->unicodeenc;
SCMakeDependent(dsc,rsc);
} else {
LogError( _("Couldn't find referenced character \"%s\" in %s\n"),
AdobeStandardEncoding[refs->adobe_enc], dsc->name);
return;
}
} else if ( refs->sc->ticked )
return;
rsc = refs->sc;
rsc->ticked = true;
for ( rf=rsc->layers[ly_fore].refs; rf!=NULL; rf = rf->next ) {
trans[0] = rf->transform[0]*transform[0] +
rf->transform[1]*transform[2];
trans[1] = rf->transform[0]*transform[1] +
rf->transform[1]*transform[3];
trans[2] = rf->transform[2]*transform[0] +
rf->transform[3]*transform[2];
trans[3] = rf->transform[2]*transform[1] +
rf->transform[3]*transform[3];
trans[4] = rf->transform[4]*transform[0] +
rf->transform[5]*transform[2] +
transform[4];
trans[5] = rf->transform[4]*transform[1] +
rf->transform[5]*transform[3] +
transform[5];
InstanciateReference(sf,topref,rf,trans,rsc,layer);
}
rsc->ticked = false;
if ( sf->multilayer ) {
int lbase = topref->layer_cnt;
if ( topref->layer_cnt==0 ) {
topref->layers = calloc(rsc->layer_cnt-1,sizeof(struct reflayer));
topref->layer_cnt = rsc->layer_cnt-1;
} else {
topref->layer_cnt += rsc->layer_cnt-1;
topref->layers = realloc(topref->layers,topref->layer_cnt*sizeof(struct reflayer));
memset(topref->layers+lbase,0,(rsc->layer_cnt-1)*sizeof(struct reflayer));
}
for ( i=ly_fore; i<rsc->layer_cnt; ++i ) {
topref->layers[i-ly_fore+lbase].splines = SplinePointListTransform(SplinePointListCopy(rsc->layers[i].splines),transform,tpt_AllPoints);
BrushCopy(&topref->layers[i-ly_fore+lbase].fill_brush, &rsc->layers[i].fill_brush,transform);
PenCopy(&topref->layers[i-ly_fore+lbase].stroke_pen, &rsc->layers[i].stroke_pen,transform);
topref->layers[i-ly_fore+lbase].dofill = rsc->layers[i].dofill;
topref->layers[i-ly_fore+lbase].dostroke = rsc->layers[i].dostroke;
topref->layers[i-ly_fore+lbase].fillfirst = rsc->layers[i].fillfirst;
/* ??? and images? */
}
} else {
if ( topref->layer_cnt==0 ) {
topref->layers = calloc(1,sizeof(struct reflayer));
topref->layer_cnt = 1;
}
new = SplinePointListTransform(SplinePointListCopy(rsc->layers[layer].splines),transform,tpt_AllPoints);
if ( new!=NULL ) {
for ( spl = new; spl->next!=NULL; spl = spl->next );
spl->next = topref->layers[0].splines;
topref->layers[0].splines = new;
}
}
}
static char *copyparse(char *str) {
char *ret, *rpt;
int ch,i;
if ( str==NULL )
return( str );
rpt=ret=malloc(strlen(str)+1);
while ( *str ) {
if ( *str=='\\' ) {
++str;
if ( *str=='n' ) ch = '\n';
else if ( *str=='r' ) ch = '\r';
else if ( *str=='t' ) ch = '\t';
else if ( *str=='b' ) ch = '\b';
else if ( *str=='f' ) ch = '\f';
else if ( *str=='\\' ) ch = '\\';
else if ( *str=='(' ) ch = '(';
else if ( *str==')' ) ch = ')';
else if ( *str>='0' && *str<='7' ) {
for ( i=ch = 0; i<3 && *str>='0' && *str<='7'; ++i )
ch = (ch<<3) + *str++-'0';
--str;
} else
ch = *str;
++str;
*rpt++ = ch;
} else
*rpt++ = *str++;
}
*rpt = '\0';
if ( !utf8_valid(ret)) {
/* Assume latin1, convert to utf8 */
rpt = latin1_2_utf8_copy(ret);
free(ret);
ret = rpt;
}
if ( !AllAscii(ret)) {
rpt = StripToASCII(ret);
free(ret);
ret = rpt;
}
return(ret);
}
char *XUIDFromFD(int xuid[20]) {
int i;
char *ret=NULL;
for ( i=19; i>=0 && xuid[i]==0; --i );
if ( i>=0 ) {
int j; char *pt;
ret = malloc(2+20*(i+1));
pt = ret;
*pt++ = '[';
for ( j=0; j<=i; ++j ) {
sprintf(pt,"%d ", xuid[j]);
pt += strlen(pt);
}
pt[-1] = ']';
}
return( ret );
}
static void SplineFontMetaData(SplineFont *sf,struct fontdict *fd) {
int em;
if ( fd->cidfontname || fd->fontname ) sf->fontname = utf8_verify_copy(fd->cidfontname?fd->cidfontname:fd->fontname);
sf->display_size = -default_fv_font_size;
sf->display_antialias = default_fv_antialias;
if ( fd->fontinfo!=NULL ) {
if ( sf->fontname==NULL && fd->fontinfo->fullname!=NULL ) {
sf->fontname = EnforcePostScriptName(fd->fontinfo->fullname);
}
if ( sf->fontname==NULL && fd->fontinfo->familyname!=NULL ){
sf->fontname = EnforcePostScriptName(fd->fontinfo->familyname);
}
sf->fullname = copyparse(fd->fontinfo->fullname);
sf->familyname = copyparse(fd->fontinfo->familyname);
sf->weight = copyparse(fd->fontinfo->weight);
sf->version = copyparse(fd->fontinfo->version);
sf->copyright = copyparse(fd->fontinfo->notice);
sf->italicangle = fd->fontinfo->italicangle;
sf->upos = fd->fontinfo->underlineposition;
sf->uwidth = fd->fontinfo->underlinethickness;
sf->strokedfont = fd->painttype==2;
sf->strokewidth = fd->strokewidth;
sf->ascent = fd->fontinfo->ascent;
sf->descent = fd->fontinfo->descent;
}
if ( sf->uniqueid==0 ) sf->uniqueid = fd->uniqueid;
if ( sf->fontname==NULL ) sf->fontname = GetNextUntitledName();
if ( sf->fullname==NULL ) sf->fullname = copy(sf->fontname);
if ( sf->familyname==NULL ) sf->familyname = copy(sf->fontname);
if ( sf->weight==NULL ) sf->weight = copy("");
if ( fd->modificationtime!=0 ) {
sf->modificationtime = fd->modificationtime;
sf->creationtime = fd->creationtime;
}
sf->cidversion = fd->cidversion;
sf->xuid = XUIDFromFD(fd->xuid);
/*sf->wasbinary = fd->wasbinary;*/
if ( fd->fontmatrix[0]==0 )
em = 1000;
else
em = rint(1/fd->fontmatrix[0]);
if ( sf->ascent==0 && sf->descent!=0 )
sf->ascent = em-sf->descent;
else if ( fd->fontbb[3]-fd->fontbb[1]==em ) {
if ( sf->ascent==0 ) sf->ascent = fd->fontbb[3];
if ( sf->descent==0 ) sf->descent = fd->fontbb[1];
} else if ( sf->ascent==0 )
sf->ascent = 8*em/10;
sf->descent = em-sf->ascent;
sf->private = fd->private->private; fd->private->private = NULL;
PSDictRemoveEntry(sf->private, "OtherSubrs");
sf->cidregistry = copy(fd->registry);
sf->ordering = copy(fd->ordering);
sf->supplement = fd->supplement;
sf->pfminfo.fstype = fd->fontinfo->fstype;
if ( sf->ordering!=NULL ) {
if ( strnmatch(sf->ordering,"Japan",5)==0 )
sf->uni_interp = ui_japanese;
else if ( strnmatch(sf->ordering,"Korea",5)==0 )
sf->uni_interp = ui_korean;
else if ( strnmatch(sf->ordering,"CNS",3)==0 )
sf->uni_interp = ui_trad_chinese;
else if ( strnmatch(sf->ordering,"GB",2)==0 )
sf->uni_interp = ui_simp_chinese;
}
}
static void TransByFontMatrix(SplineFont *sf,real fontmatrix[6]) {
real trans[6];
int em = sf->ascent+sf->descent, i;
SplineChar *sc;
RefChar *refs;
if ( fontmatrix[0]==fontmatrix[3] &&
fontmatrix[1]==0 && fontmatrix[2]==0 &&
fontmatrix[4]==0 && fontmatrix[5]==0 )
return; /* It's just the expected matrix */
trans[0] = 1;
if ( fontmatrix[0]==fontmatrix[3] ) trans[3] = 1;
else trans[3] = rint(fontmatrix[3]*em);
trans[1] = fontmatrix[1]*em;
trans[2] = fontmatrix[2]*em;
trans[4] = rint(fontmatrix[4]*em);
trans[5] = rint(fontmatrix[5]*em);
for ( i=0; i<sf->glyphcnt; ++i ) if ( (sc=sf->glyphs[i])!=NULL ) {
SplinePointListTransform(sc->layers[ly_fore].splines,trans,tpt_AllPoints);
for ( refs=sc->layers[ly_fore].refs; refs!=NULL; refs=refs->next ) {
/* Just scale the offsets. we'll do all the base characters */
real temp = refs->transform[4]*trans[0] +
refs->transform[5]*trans[2] +
/*transform[4]*/0;
refs->transform[5] = refs->transform[4]*trans[1] +
refs->transform[5]*trans[3] +
/*transform[5]*/0;
refs->transform[4] = temp;
}
sc->changedsincelasthinted = true;
sc->manualhints = false;
}
for ( i=0; i<sf->glyphcnt; ++i ) if ( (sc=sf->glyphs[i])!=NULL ) {
for ( refs=sc->layers[ly_fore].refs; refs!=NULL; refs=refs->next )
SCReinstanciateRefChar(sc,refs,ly_fore);
}
}
void SFInstanciateRefs(SplineFont *sf) {
int i, layer;
RefChar *refs, *next, *pr;
for ( i=0; i<sf->glyphcnt; ++i ) if ( sf->glyphs[i]!=NULL )
sf->glyphs[i]->ticked = false;
for ( i=0; i<sf->glyphcnt; ++i ) if ( sf->glyphs[i]!=NULL ) {
SplineChar *sc = sf->glyphs[i];
for ( layer=ly_back; layer<sc->layer_cnt; ++layer ) {
for ( pr=NULL, refs = sc->layers[layer].refs; refs!=NULL; refs=next ) {
next = refs->next;
sc->ticked = true;
InstanciateReference(sf, refs, refs, refs->transform,sc,layer);
if ( refs->sc!=NULL ) {
SplineSetFindBounds(refs->layers[0].splines,&refs->bb);
sc->ticked = false;
pr = refs;
} else {
/* In some mal-formed postscript fonts we can have a reference */
/* to a character that is not actually in the font. I even */
/* generated one by mistake once... */
if ( pr==NULL )
sc->layers[layer].refs = next;
else
pr->next = next;
refs->next = NULL;
RefCharsFree(refs);
}
}
}
}
}
/* Also handles type3s */
static void _SplineFontFromType1(SplineFont *sf, FontDict *fd, struct pscontext *pscontext) {
int i, j, notdefpos;
RefChar *refs, *next;
int istype2 = fd->fonttype==2; /* Easy enough to deal with even though it will never happen... */
int istype3 = fd->charprocs->next!=0;
EncMap *map;
if ( istype2 )
fd->private->subrs->bias = fd->private->subrs->cnt<1240 ? 107 :
fd->private->subrs->cnt<33900 ? 1131 : 32768;
sf->glyphmax = sf->glyphcnt = istype3 ? fd->charprocs->next : fd->chars->next;
if ( sf->map==NULL ) {
sf->map = map = EncMapNew(256+CharsNotInEncoding(fd),sf->glyphcnt,fd->encoding_name);
} else
map = sf->map;
sf->glyphs = calloc(map->backmax,sizeof(SplineChar *));
if ( istype3 ) /* We read a type3 */
sf->multilayer = true;
if ( istype3 )
notdefpos = LookupCharString(".notdef",(struct pschars *) (fd->charprocs));
else
notdefpos = LookupCharString(".notdef",fd->chars);
for ( i=0; i<256; ++i ) {
int k;
if ( istype3 ) {
k = LookupCharString(fd->encoding[i],(struct pschars *) (fd->charprocs));
} else {
k = LookupCharString(fd->encoding[i],fd->chars);
}
if ( k==-1 ) k = notdefpos;
map->map[i] = k;
if ( k!=-1 && map->backmap[k]==-1 )
map->backmap[k] = i;
}
if ( map->enccount>256 ) {
int k, j;
for ( k=0; k<fd->chars->cnt; ++k ) {
if ( fd->chars->keys[k]!=NULL ) {
for ( j=0; j<256; ++j )
if ( fd->encoding[j]!=NULL &&
strcmp(fd->encoding[j],fd->chars->keys[k])==0 )
break;
if ( j==256 ) {
map->map[i] = k;
if ( map->backmap[k]==-1 )
map->backmap[k] = i;
++i;
}
}
}
/* And for type3s */
for ( k=0; k<fd->charprocs->cnt; ++k ) {
if ( fd->charprocs->keys[k]!=NULL ) {
for ( j=0; j<256; ++j )
if ( fd->encoding[j]!=NULL &&
strcmp(fd->encoding[j],fd->charprocs->keys[k])==0 )
break;
if ( j==256 ) {
map->map[i] = k;
if ( map->backmap[k]==-1 )
map->backmap[k] = i;
++i;
}
}
}
}
for ( i=0; i<map->enccount; ++i ) if ( map->map[i]==-1 )
map->map[i] = notdefpos;
for ( i=0; i<sf->glyphcnt; ++i ) {
if ( !istype3 )
sf->glyphs[i] = PSCharStringToSplines(fd->chars->values[i],fd->chars->lens[i],
pscontext,fd->private->subrs,NULL,fd->chars->keys[i]);
else
sf->glyphs[i] = fd->charprocs->values[i];
if ( sf->glyphs[i]!=NULL ) {
sf->glyphs[i]->orig_pos = i;
sf->glyphs[i]->vwidth = sf->ascent+sf->descent;
sf->glyphs[i]->unicodeenc = UniFromName(sf->glyphs[i]->name,sf->uni_interp,map->enc);
sf->glyphs[i]->parent = sf;
/* SCLigDefault(sf->glyphs[i]);*/ /* Also reads from AFM file, but it probably doesn't exist */
}
ff_progress_next();
}
SFInstanciateRefs(sf);
if ( fd->metrics!=NULL ) {
for ( i=0; i<fd->metrics->next; ++i ) {
int width = strtol(fd->metrics->values[i],NULL,10);
for ( j=sf->glyphcnt-1; j>=0; --j ) {
if ( sf->glyphs[j]!=NULL && sf->glyphs[j]->name!=NULL &&
strcmp(fd->metrics->keys[i],sf->glyphs[j]->name)==0 ) {
sf->glyphs[j]->width = width;
break;
}
}
}
}
for ( i=0; i<sf->glyphcnt; ++i ) if ( sf->glyphs[i]!=NULL )
for ( refs = sf->glyphs[i]->layers[ly_fore].refs; refs!=NULL; refs=next ) {
next = refs->next;
if ( refs->adobe_enc==' ' && refs->layers[0].splines==NULL ) {
/* When I have a link to a single character I will save out a */
/* seac to that character and a space (since I can only make */
/* real char links), so if we find a space link, get rid of*/
/* it. It's an artifact */
SCRefToSplines(sf->glyphs[i],refs,ly_fore);
}
}
/* sometimes (some apple oblique fonts) the fontmatrix is not just a */
/* formality, it acutally contains a skew. So be ready */
if ( fd->fontmatrix[0]!=0 )
TransByFontMatrix(sf,fd->fontmatrix);
AltUniFigure(sf,sf->map,true);
}
static void SplineFontFromType1(SplineFont *sf, FontDict *fd, struct pscontext *pscontext) {
int i;
SplineChar *sc;
_SplineFontFromType1(sf,fd,pscontext);
/* Clean up the hint masks, We create an initial hintmask whether we need */
/* it or not */
for ( i=0; i<sf->glyphcnt; ++i ) {
if ( (sc = sf->glyphs[i])!=NULL && !sc->hconflicts && !sc->vconflicts &&
sc->layers[ly_fore].splines!=NULL ) {
chunkfree( sc->layers[ly_fore].splines->first->hintmask,sizeof(HintMask) );
sc->layers[ly_fore].splines->first->hintmask = NULL;
}
}
}
static SplineFont *SplineFontFromMMType1(SplineFont *sf, FontDict *fd, struct pscontext *pscontext) {
char *pt, *end, *origweight;
MMSet *mm;
int ipos, apos, ppos, item, i;
real blends[12]; /* At most twelve points/axis in a blenddesignmap */
real designs[12];
EncMap *map;
if ( fd->weightvector==NULL || fd->fontinfo->blenddesignpositions==NULL ||
fd->fontinfo->blenddesignmap==NULL || fd->fontinfo->blendaxistypes==NULL ) {
ff_post_error(_("Bad Multiple Master Font"),_("Bad Multiple Master Font"));
SplineFontFree(sf);
return( NULL );
}
mm = chunkalloc(sizeof(MMSet));
pt = fd->weightvector;
while ( *pt==' ' || *pt=='[' ) ++pt;
while ( *pt!=']' && *pt!='\0' ) {
pscontext->blend_values[ pscontext->instance_count ] =
c_strtod(pt,&end);
if ( pt==end )
break;
++(pscontext->instance_count);
if ( pscontext->instance_count>=sizeof(pscontext->blend_values)/sizeof(pscontext->blend_values[0])) {
LogError( _("Multiple master font with more than 16 instances\n") );
break;
}
for ( pt = end; *pt==' '; ++pt );
}
mm->instance_count = pscontext->instance_count;
mm->instances = malloc(pscontext->instance_count*sizeof(SplineFont *));
mm->defweights = malloc(mm->instance_count*sizeof(real));
memcpy(mm->defweights,pscontext->blend_values,mm->instance_count*sizeof(real));
mm->normal = sf;
_SplineFontFromType1(mm->normal,fd,pscontext);
map = sf->map;
for ( i=0; i<sf->glyphcnt; ++i ) if ( sf->glyphs[i]!=NULL )
sf->glyphs[i]->blended = true;
sf->mm = mm;
pt = fd->fontinfo->blendaxistypes;
while ( *pt==' ' || *pt=='[' ) ++pt;
while ( *pt!=']' && *pt!='\0' ) {
if ( *pt=='/' ) ++pt;
for ( end=pt; *end!=' ' && *end!=']' && *end!='\0'; ++end );
if ( pt==end )
break;
if ( mm->axis_count>=sizeof(mm->axes)/sizeof(mm->axes[0])) {
LogError( _("Multiple master font with more than 4 axes\n") );
break;
}
mm->axes[ mm->axis_count++ ] = copyn( pt,end-pt );
for ( pt = end; *pt==' '; ++pt );
}
if ( mm->instance_count < (1<<mm->axis_count) )
ff_post_error(_("Bad Multiple Master Font"),_("This multiple master font has %1$d instance fonts, but it needs at least %2$d master fonts for %3$d axes. FontForge will not be able to edit this correctly"),mm->instance_count,1<<mm->axis_count,mm->axis_count);
else if ( mm->instance_count > (1<<mm->axis_count) )
ff_post_error(_("Bad Multiple Master Font"),_("This multiple master font has %1$d instance fonts, but FontForge can only handle %2$d master fonts for %3$d axes. FontForge will not be able to edit this correctly"),mm->instance_count,1<<mm->axis_count,mm->axis_count);
mm->positions = calloc(mm->axis_count*mm->instance_count,sizeof(real));
pt = fd->fontinfo->blenddesignpositions;
while ( *pt==' ' ) ++pt;
if ( *pt=='[' ) ++pt;
ipos = 0;
while ( *pt!=']' && *pt!='\0' ) {
while ( *pt==' ' ) ++pt;
if ( *pt==']' || *pt=='\0' )
break;
if ( ipos>=mm->instance_count )
break;
if ( *pt=='[' ) {
++pt;
apos=0;
while ( *pt!=']' && *pt!='\0' ) {
if ( apos>=mm->axis_count ) {
LogError( _("Too many axis positions specified in /BlendDesignPositions.\n") );
break;
}
mm->positions[ipos*mm->axis_count+apos] =
c_strtod(pt,&end);
if ( pt==end )
break;
++apos;
for ( pt = end; *pt==' '; ++pt );
}
if ( *pt==']' ) ++pt;
++ipos;
} else
++pt;
}
mm->axismaps = calloc(mm->axis_count,sizeof(struct axismap));
pt = fd->fontinfo->blenddesignmap;
while ( *pt==' ' ) ++pt;
if ( *pt=='[' ) ++pt;
apos = 0;
while ( *pt!=']' && *pt!='\0' ) {
while ( *pt==' ' ) ++pt;
if ( *pt==']' || *pt=='\0' )
break;
if ( apos>=mm->axis_count )
break;
if ( *pt=='[' ) {
++pt;
ppos=0;
while ( *pt!=']' && *pt!='\0' ) {
if ( ppos>=12 ) {
LogError( _("Too many mapping data points specified in /BlendDesignMap for axis %s.\n"), mm->axes[apos] );
break;
}
while ( *pt==' ' ) ++pt;
if ( *pt=='[' ) {
++pt;
designs[ppos] = c_strtod(pt,&end);
blends[ppos] = c_strtod(end,&end);
if ( blends[ppos]<0 || blends[ppos]>1 ) {
LogError( _("Bad value for blend in /BlendDesignMap for axis %s.\n"), mm->axes[apos] );
if ( blends[ppos]<0 ) blends[ppos] = 0;
if ( blends[ppos]>1 ) blends[ppos] = 1;
}
pt = end;
while ( *pt!=']' && *pt!='\0' ) ++pt;
ppos ++;
}
++pt;
while ( *pt==' ' ) ++pt;
}
if ( *pt==']' ) ++pt;
if ( ppos<2 )
LogError( _("Bad few values in /BlendDesignMap for axis %s.\n"), mm->axes[apos] );
mm->axismaps[apos].points = ppos;
mm->axismaps[apos].blends = malloc(ppos*sizeof(real));
mm->axismaps[apos].designs = malloc(ppos*sizeof(real));
memcpy(mm->axismaps[apos].blends,blends,ppos*sizeof(real));
memcpy(mm->axismaps[apos].designs,designs,ppos*sizeof(real));
++apos;
} else
++pt;
}
mm->cdv = copy(fd->cdv);
mm->ndv = copy(fd->ndv);
origweight = fd->fontinfo->weight;
/* Now figure out the master designs, being careful to interpolate */
/* BlueValues, ForceBold, UnderlinePosition etc. We need to copy private */
/* generate a font name */
for ( ipos = 0; ipos<mm->instance_count; ++ipos ) {
free(fd->fontname);
free(fd->fontinfo->fullname);
fd->fontname = MMMakeMasterFontname(mm,ipos,&fd->fontinfo->fullname);
fd->fontinfo->weight = MMGuessWeight(mm,ipos,copy(origweight));
if ( fd->blendfontinfo!=NULL ) {
for ( item=0; item<3; ++item ) {
static char *names[] = { "ItalicAngle", "UnderlinePosition", "UnderlineThickness" };
pt = PSDictHasEntry(fd->blendfontinfo,names[item]);
if ( pt!=NULL ) {
pt = MMExtractNth(pt,ipos);
if ( pt!=NULL ) {
bigreal val = c_strtod(pt,NULL);
free(pt);
switch ( item ) {
case 0: fd->fontinfo->italicangle = val; break;
case 1: fd->fontinfo->underlineposition = val; break;
case 2: fd->fontinfo->underlinethickness = val; break;
}
}
}
}
}
fd->private->private = PSDictCopy(sf->private);
if ( fd->blendprivate!=NULL ) {
static char *arrnames[] = { "BlueValues", "OtherBlues", "FamilyBlues", "FamilyOtherBlues", "StdHW", "StdVW", "StemSnapH", "StemSnapV", NULL };
static char *scalarnames[] = { "ForceBold", "BlueFuzz", "BlueScale", "BlueShift", NULL };
for ( item=0; scalarnames[item]!=NULL; ++item ) {
pt = PSDictHasEntry(fd->blendprivate,scalarnames[item]);
if ( pt!=NULL ) {
pt = MMExtractNth(pt,ipos);
PSDictChangeEntry(fd->private->private,scalarnames[item],pt);
free(pt);
}
}
for ( item=0; arrnames[item]!=NULL; ++item ) {
pt = PSDictHasEntry(fd->blendprivate,arrnames[item]);
if ( pt!=NULL ) {
pt = MMExtractArrayNth(pt,ipos);
PSDictChangeEntry(fd->private->private,arrnames[item],pt);
free(pt);
}
}
}
for ( item=0; item<mm->instance_count; ++item )
pscontext->blend_values[item] = 0;
pscontext->blend_values[ipos] = 1;
mm->instances[ipos] = SplineFontEmpty();
SplineFontMetaData(mm->instances[ipos],fd);
free(fd->fontinfo->weight);
mm->instances[ipos]->map = map;
_SplineFontFromType1(mm->instances[ipos],fd,pscontext);
mm->instances[ipos]->mm = mm;
}
fd->fontinfo->weight = origweight;
/* Clean up hintmasks. We always create a hintmask on the first point */
/* only keep them if we actually have conflicts. */
for ( i=0; i<mm->normal->glyphcnt; ++i )
if ( mm->normal->glyphs[i]!=NULL &&
mm->normal->glyphs[i]->layers[ly_fore].splines != NULL ) {
for ( item=0; item<mm->instance_count; ++item )
if ( mm->instances[item]->glyphs[i]->vconflicts ||
mm->instances[item]->glyphs[i]->hconflicts )
break;
if ( item==mm->instance_count ) { /* No conflicts */
for ( item=0; item<mm->instance_count; ++item ) {
chunkfree( mm->instances[item]->glyphs[i]->layers[ly_fore].splines->first->hintmask, sizeof(HintMask) );
mm->instances[item]->glyphs[i]->layers[ly_fore].splines->first->hintmask = NULL;
}
chunkfree( mm->normal->glyphs[i]->layers[ly_fore].splines->first->hintmask, sizeof(HintMask) );
mm->normal->glyphs[i]->layers[ly_fore].splines->first->hintmask = NULL;
}
}
return( sf );
}
static SplineFont *SplineFontFromCIDType1(SplineFont *sf, FontDict *fd,
struct pscontext *pscontext) {
int i,j,k, bad, uni;
SplineChar **chars;
char buffer[100];
struct cidmap *map;
SplineFont *_sf;
SplineChar *sc;
EncMap *encmap;
bad = 0x80000000;
for ( i=0; i<fd->fdcnt; ++i )
if ( fd->fds[i]->fonttype!=1 && fd->fds[i]->fonttype!=2 )
bad = fd->fds[i]->fonttype;
if ( bad!=0x80000000 || fd->cidfonttype!=0 ) {
LogError( _("Could not parse a CID font, %sCIDFontType %d, %sfonttype %d\n"),
( fd->cidfonttype!=0 ) ? "unexpected " : "",
( bad!=0x80000000 ) ? "unexpected " : "",
fd->cidfonttype, bad );
SplineFontFree(sf);
return( NULL );
}
if ( fd->cidstrs==NULL || fd->cidcnt==0 ) {
LogError( _("CID format doesn't contain what we expected it to.\n") );
SplineFontFree(sf);
return( NULL );
}
encmap = EncMap1to1(fd->cidcnt);
sf->subfontcnt = fd->fdcnt;
sf->subfonts = malloc((sf->subfontcnt+1)*sizeof(SplineFont *));
for ( i=0; i<fd->fdcnt; ++i ) {
if ( fd->fontmatrix[0]!=0 ) {
MatMultiply(fd->fontmatrix,fd->fds[i]->fontmatrix,fd->fds[i]->fontmatrix);
}
sf->subfonts[i] = SplineFontEmpty();
SplineFontMetaData(sf->subfonts[i],fd->fds[i]);
sf->subfonts[i]->cidmaster = sf;
sf->subfonts[i]->uni_interp = sf->uni_interp;
sf->subfonts[i]->map = encmap;
if ( fd->fds[i]->fonttype==2 )
fd->fds[i]->private->subrs->bias =
fd->fds[i]->private->subrs->cnt<1240 ? 107 :
fd->fds[i]->private->subrs->cnt<33900 ? 1131 : 32768;
}
map = FindCidMap(sf->cidregistry,sf->ordering,sf->supplement,sf);
chars = calloc(fd->cidcnt,sizeof(SplineChar *));
for ( i=0; i<fd->cidcnt; ++i ) if ( fd->cidlens[i]>0 ) {
j = fd->cidfds[i]; /* We get font indexes of 255 for non-existant chars */
uni = CID2NameUni(map,i,buffer,sizeof(buffer));
pscontext->is_type2 = fd->fds[j]->fonttype==2;
chars[i] = PSCharStringToSplines(fd->cidstrs[i],fd->cidlens[i],
pscontext,fd->fds[j]->private->subrs,
NULL,buffer);
chars[i]->vwidth = sf->subfonts[j]->ascent+sf->subfonts[j]->descent;
chars[i]->unicodeenc = uni;
chars[i]->orig_pos = i;
chars[i]->altuni = CIDSetAltUnis(map,i);
/* There better not be any references (seac's) because we have no */
/* encoding on which to base any fixups */
if ( chars[i]->layers[ly_fore].refs!=NULL )
IError( "Reference found in CID font. Can't fix it up");
sf->subfonts[j]->glyphcnt = sf->subfonts[j]->glyphmax = i+1;
ff_progress_next();
}
for ( i=0; i<fd->fdcnt; ++i )
sf->subfonts[i]->glyphs = calloc(sf->subfonts[i]->glyphcnt,sizeof(SplineChar *));
for ( i=0; i<fd->cidcnt; ++i ) if ( chars[i]!=NULL ) {
j = fd->cidfds[i];
if ( j<sf->subfontcnt ) {
sf->subfonts[j]->glyphs[i] = chars[i];
chars[i]->parent = sf->subfonts[j];
}
}
free(chars);
/* Clean up the hint masks, We create an initial hintmask whether we */
/* need it or not */
k=0;
do {
_sf = k<sf->subfontcnt?sf->subfonts[k]:sf;
for ( i=0; i<_sf->glyphcnt; ++i ) {
if ( (sc = _sf->glyphs[i])!=NULL && !sc->hconflicts && !sc->vconflicts &&
sc->layers[ly_fore].splines!=NULL ) {
chunkfree( sc->layers[ly_fore].splines->first->hintmask,sizeof(HintMask) );
sc->layers[ly_fore].splines->first->hintmask = NULL;
}
}
++k;
} while ( k<sf->subfontcnt );
return( sf );
}
SplineFont *SplineFontFromPSFont(FontDict *fd) {
SplineFont *sf;
struct pscontext pscontext;
if ( fd->sf!=NULL )
sf = fd->sf;
else {
memset(&pscontext,0,sizeof(pscontext));
pscontext.is_type2 = fd->fonttype==2;
pscontext.painttype = fd->painttype;
sf = SplineFontEmpty();
SplineFontMetaData(sf,fd);
if ( fd->wascff ) {
SplineFontFree(sf);
sf = fd->sf;
} else if ( fd->fdcnt!=0 )
sf = SplineFontFromCIDType1(sf,fd,&pscontext);
else if ( fd->weightvector!=NULL )
SplineFontFromMMType1(sf,fd,&pscontext);
else
SplineFontFromType1(sf,fd,&pscontext);
if ( loaded_fonts_same_as_new && new_fonts_are_order2 &&
fd->weightvector==NULL )
SFConvertToOrder2(sf);
}
if ( sf->multilayer )
SFCheckPSBitmap(sf);
return( sf );
}
static void LayerToRefLayer(struct reflayer *rl,Layer *layer, real transform[6]) {
BrushCopy(&rl->fill_brush, &layer->fill_brush,transform);
PenCopy(&rl->stroke_pen, &layer->stroke_pen,transform);
rl->dofill = layer->dofill;
rl->dostroke = layer->dostroke;
rl->fillfirst = layer->fillfirst;
}
int RefLayerFindBaseLayerIndex(RefChar *rf, int layer) {
// Note that most of the logic below is copied and lightly modified from SCReinstanciateRefChar.
SplineChar *rsc = rf->sc;
int i = 0, j = 0, cnt = 0;
RefChar *subref;
for ( i=ly_fore; i<rsc->layer_cnt; ++i ) {
if ( rsc->layers[i].splines!=NULL || rsc->layers[i].images!=NULL ) {
if (cnt == layer) return i;
++cnt;
}
for ( subref=rsc->layers[i].refs; subref!=NULL; subref=subref->next ) {
for ( j=0; j<subref->layer_cnt; ++j ) if ( subref->layers[j].images!=NULL || subref->layers[j].splines!=NULL ) {
if (cnt == layer) return i;
++cnt;
}
}
}
return -1;
}
void RefCharFindBounds(RefChar *rf) {
int i;
SplineChar *rsc = rf->sc;
real extra=0,e;
memset(&rf->bb,'\0',sizeof(rf->bb));
rf->top.y = -1e10;
for ( i=0; i<rf->layer_cnt; ++i ) {
_SplineSetFindBounds(rf->layers[i].splines,&rf->bb);
_SplineSetFindTop(rf->layers[i].splines,&rf->top);
int baselayer = RefLayerFindBaseLayerIndex(rf, i);
if ( baselayer >= 0 && rsc->layers[baselayer].dostroke ) {
if ( rf->layers[i].stroke_pen.width!=WIDTH_INHERITED )
e = rf->layers[i].stroke_pen.width*rf->layers[i].stroke_pen.trans[0];
else
e = rf->layers[i].stroke_pen.trans[0];
if ( e>extra ) extra = e;
}
}
if ( rf->top.y < -65536 ) rf->top.y = rf->top.x = 0;
rf->bb.minx -= extra; rf->bb.miny -= extra;
rf->bb.maxx += extra; rf->bb.maxy += extra;
}
void SCReinstanciateRefChar(SplineChar *sc,RefChar *rf,int layer) {
SplinePointList *new, *last;
RefChar *refs;
int i,j;
SplineChar *rsc = rf->sc;
real extra=0,e;
for ( i=0; i<rf->layer_cnt; ++i ) {
SplinePointListsFree(rf->layers[i].splines);
GradientFree(rf->layers[i].fill_brush.gradient);
PatternFree(rf->layers[i].fill_brush.pattern);
GradientFree(rf->layers[i].stroke_pen.brush.gradient);
PatternFree(rf->layers[i].stroke_pen.brush.pattern);
}
free( rf->layers );
rf->layers = NULL;
rf->layer_cnt = 0;
if ( rsc==NULL )
return;
/* Can be called before sc->parent is set, but only when reading a ttf */
/* file which won't be multilayer */
if ( sc->parent!=NULL && sc->parent->multilayer ) {
int cnt = 0;
RefChar *subref;
for ( i=ly_fore; i<rsc->layer_cnt; ++i ) {
if ( rsc->layers[i].splines!=NULL || rsc->layers[i].images!=NULL )
++cnt;
for ( subref=rsc->layers[i].refs; subref!=NULL; subref=subref->next )
cnt += subref->layer_cnt;
}
rf->layer_cnt = cnt;
rf->layers = calloc(cnt,sizeof(struct reflayer));
cnt = 0;
for ( i=ly_fore; i<rsc->layer_cnt; ++i ) {
if ( rsc->layers[i].splines!=NULL || rsc->layers[i].images!=NULL ) {
rf->layers[cnt].splines =
SplinePointListTransform(
SplinePointListCopy(rsc->layers[i].splines),rf->transform,tpt_AllPoints);
rf->layers[cnt].images =
ImageListTransform(
ImageListCopy(rsc->layers[i].images),rf->transform,true);
LayerToRefLayer(&rf->layers[cnt],&rsc->layers[i],rf->transform);
++cnt;
}
for ( subref=rsc->layers[i].refs; subref!=NULL; subref=subref->next ) {
for ( j=0; j<subref->layer_cnt; ++j ) if ( subref->layers[j].images!=NULL || subref->layers[j].splines!=NULL ) {
rf->layers[cnt] = subref->layers[j];
rf->layers[cnt].splines =
SplinePointListTransform(
SplinePointListCopy(subref->layers[j].splines),rf->transform,tpt_AllPoints);
rf->layers[cnt].images =
ImageListTransform(
ImageListCopy(subref->layers[j].images),rf->transform,true);
++cnt;
}
}
}
memset(&rf->bb,'\0',sizeof(rf->bb));
rf->top.y = -1e10;
for ( i=0; i<rf->layer_cnt; ++i ) {
_SplineSetFindBounds(rf->layers[i].splines,&rf->bb);
_SplineSetFindTop(rf->layers[i].splines,&rf->top);
int baselayer = RefLayerFindBaseLayerIndex(rf, i);
if ( baselayer >= 0 && rsc->layers[baselayer].dostroke ) {
if ( rf->layers[i].stroke_pen.width!=WIDTH_INHERITED )
e = rf->layers[i].stroke_pen.width*rf->layers[i].stroke_pen.trans[0];
else
e = rf->layers[i].stroke_pen.trans[0];
if ( e>extra ) extra = e;
}
}
if ( rf->top.y < -65536 ) rf->top.y = rf->top.x = 0;
rf->bb.minx -= extra; rf->bb.miny -= extra;
rf->bb.maxx += extra; rf->bb.maxy += extra;
} else {
if ( rf->layer_cnt>0 ) {
SplinePointListsFree(rf->layers[0].splines);
rf->layers[0].splines = NULL;
}
rf->layers = calloc(1,sizeof(struct reflayer));
rf->layer_cnt = 1;
rf->layers[0].dofill = true;
new = SplinePointListTransform(SplinePointListCopy(rf->sc->layers[layer].splines),rf->transform,tpt_AllPoints);
rf->layers[0].splines = new;
last = NULL;
if ( new!=NULL )
for ( last = new; last->next!=NULL; last = last->next );
for ( refs = rf->sc->layers[layer].refs; refs!=NULL; refs = refs->next ) {
new = SplinePointListTransform(SplinePointListCopy(refs->layers[0].splines),rf->transform,tpt_AllPoints);
if ( last!=NULL )
last->next = new;
else
rf->layers[0].splines = new;
if ( new!=NULL )
for ( last = new; last->next!=NULL; last = last->next );
}
}
RefCharFindBounds(rf);
}
static void _SFReinstanciateRefs(SplineFont *sf) {
int i, undone, undoable, j, cnt;
RefChar *ref;
for ( i=0; i<sf->glyphcnt; ++i ) if ( sf->glyphs[i]!=NULL )
sf->glyphs[i]->ticked = false;
undone = true;
cnt = 0;
while ( undone && cnt<200) {
undone = false;
for ( i=0; i<sf->glyphcnt; ++i ) if ( sf->glyphs[i]!=NULL && !sf->glyphs[i]->ticked ) {
undoable = false;
for ( j=0; j<sf->glyphs[i]->layer_cnt; ++j ) {
for ( ref=sf->glyphs[i]->layers[j].refs; ref!=NULL; ref=ref->next ) {
if ( !ref->sc->ticked )
undoable = true;
}
}
if ( undoable )
undone = true;
else {
for ( j=0; j<sf->glyphs[i]->layer_cnt; ++j ) {
for ( ref=sf->glyphs[i]->layers[j].refs; ref!=NULL; ref=ref->next )
SCReinstanciateRefChar(sf->glyphs[i],ref,j);
}
sf->glyphs[i]->ticked = true;
}
}
++cnt;
}
}
void SFReinstanciateRefs(SplineFont *sf) {
int i;
if ( sf->cidmaster!=NULL || sf->subfontcnt!=0 ) {
if ( sf->cidmaster!=NULL ) sf = sf->cidmaster;
for ( i=0; i<sf->subfontcnt; ++i )
_SFReinstanciateRefs(sf->subfonts[i]);
} else
_SFReinstanciateRefs(sf);
}
void SCReinstanciateRef(SplineChar *sc,SplineChar *rsc,int layer) {
RefChar *rf;
for ( rf=sc->layers[layer].refs; rf!=NULL; rf=rf->next ) if ( rf->sc==rsc ) {
SCReinstanciateRefChar(sc,rf,layer);
}
}
void SCRemoveDependent(SplineChar *dependent,RefChar *rf,int layer) {
struct splinecharlist *dlist, *pd;
RefChar *prev;
int i;
if ( dependent->layers[layer].refs==rf )
dependent->layers[layer].refs = rf->next;
else {
for ( prev = dependent->layers[layer].refs; prev->next!=rf; prev=prev->next );
prev->next = rf->next;
}
/* Check for multiple dependencies (colon has two refs to period) */
/* Also check other layers (they may include references to the same glyph as well */
/* if there are none, then remove dependent from ref->sc's dependents list */
for ( i=0; i<dependent->layer_cnt; i++ ) {
for ( prev = dependent->layers[i].refs; prev!=NULL && (prev==rf || prev->sc!=rf->sc); prev = prev->next );
}
if ( prev==NULL ) {
dlist = rf->sc->dependents;
if ( dlist==NULL )
/* Do nothing */;
else if ( dlist->sc==dependent ) {
rf->sc->dependents = dlist->next;
} else {
for ( pd=dlist, dlist = pd->next; dlist!=NULL && dlist->sc!=dependent; pd=dlist, dlist = pd->next );
if ( dlist!=NULL )
pd->next = dlist->next;
}
chunkfree(dlist,sizeof(struct splinecharlist));
}
RefCharFree(rf);
}
void SCRemoveLayerDependents(SplineChar *dependent,int layer) {
RefChar *rf, *next;
for ( rf=dependent->layers[layer].refs; rf!=NULL; rf=next ) {
next = rf->next;
SCRemoveDependent(dependent,rf,layer);
}
dependent->layers[layer].refs = NULL;
}
void SCRemoveDependents(SplineChar *dependent) {
int layer;
for ( layer=ly_fore; layer<dependent->layer_cnt; ++layer )
SCRemoveLayerDependents(dependent,layer);
}
void SCRefToSplines(SplineChar *sc,RefChar *rf,int layer) {
SplineSet *spl;
int rlayer;
if ( sc->parent->multilayer ) {
Layer *old = sc->layers;
sc->layers = realloc(sc->layers,(sc->layer_cnt+rf->layer_cnt)*sizeof(Layer));
for ( rlayer = 0; rlayer<rf->layer_cnt; ++rlayer ) {
LayerDefault(&sc->layers[sc->layer_cnt+rlayer]);
sc->layers[sc->layer_cnt+rlayer].splines = rf->layers[rlayer].splines;
rf->layers[rlayer].splines = NULL;
sc->layers[sc->layer_cnt+rlayer].images = rf->layers[rlayer].images;
rf->layers[rlayer].images = NULL;
sc->layers[sc->layer_cnt+rlayer].refs = NULL;
sc->layers[sc->layer_cnt+rlayer].undoes = NULL;
sc->layers[sc->layer_cnt+rlayer].redoes = NULL;
BrushCopy(&sc->layers[sc->layer_cnt+rlayer].fill_brush, &rf->layers[rlayer].fill_brush,rf->transform);
PenCopy(&sc->layers[sc->layer_cnt+rlayer].stroke_pen, &rf->layers[rlayer].stroke_pen,rf->transform);
sc->layers[sc->layer_cnt+rlayer].dofill = rf->layers[rlayer].dofill;
sc->layers[sc->layer_cnt+rlayer].dostroke = rf->layers[rlayer].dostroke;
sc->layers[sc->layer_cnt+rlayer].fillfirst = rf->layers[rlayer].fillfirst;
}
sc->layer_cnt += rf->layer_cnt;
SCMoreLayers(sc,old);
} else {
if ( (spl = rf->layers[0].splines)!=NULL ) {
while ( spl->next!=NULL )
spl = spl->next;
spl->next = sc->layers[layer].splines;
sc->layers[layer].splines = rf->layers[0].splines;
rf->layers[0].splines = NULL;
if ( sc->layers[layer].order2 && !sc->layers[layer].background )
SCClearInstrsOrMark(sc,layer,true);
}
}
SCRemoveDependent(sc,rf,layer);
}
/* This returns all real solutions, even those out of bounds */
/* I use -999999 as an error flag, since we're really only interested in */
/* solns near 0 and 1 that should be ok. -1 is perhaps a little too close */
/* Sigh. When solutions are near 0, the rounding errors are appalling. */
int _CubicSolve(const Spline1D *sp,bigreal sought, extended ts[3]) {
extended d, xN, yN, delta2, temp, delta, h, t2, t3, theta;
extended sa=sp->a, sb=sp->b, sc=sp->c, sd=sp->d-sought;
int i=0;
ts[0] = ts[1] = ts[2] = -999999;
if ( sd==0 && sa!=0 ) {
/* one of the roots is 0, the other two are the soln of a quadratic */
ts[0] = 0;
if ( sc==0 ) {
ts[1] = -sb/(extended) sa; /* two zero roots */
} else {
temp = sb*(extended) sb-4*(extended) sa*sc;
if ( RealNear(temp,0))
ts[1] = -sb/(2*(extended) sa);
else if ( temp>=0 ) {
temp = sqrt(temp);
ts[1] = (-sb+temp)/(2*(extended) sa);
ts[2] = (-sb-temp)/(2*(extended) sa);
}
}
} else if ( sa!=0 ) {
/* http://www.m-a.org.uk/eb/mg/mg077ch.pdf */
/* this nifty solution to the cubic neatly avoids complex arithmatic */
xN = -sb/(3*(extended) sa);
yN = ((sa*xN + sb)*xN+sc)*xN + sd;
delta2 = (sb*(extended) sb-3*(extended) sa*sc)/(9*(extended) sa*sa);
/*if ( RealWithin(delta2,0,.00000001) ) delta2 = 0;*/
/* the descriminant is yN^2-h^2, but delta might be <0 so avoid using h */
d = yN*yN - 4*sa*sa*delta2*delta2*delta2;
if ( ((yN>.01 || yN<-.01) && RealNear(d/yN,0)) || ((yN<=.01 && yN>=-.01) && RealNear(d,0)) )
d = 0;
if ( d>0 ) {
temp = sqrt(d);
t2 = (-yN-temp)/(2*sa);
t2 = (t2==0) ? 0 : (t2<0) ? -pow(-t2,1./3.) : pow(t2,1./3.);
t3 = (-yN+temp)/(2*sa);
t3 = t3==0 ? 0 : (t3<0) ? -pow(-t3,1./3.) : pow(t3,1./3.);
ts[0] = xN + t2 + t3;
} else if ( d<0 ) {
if ( delta2>=0 ) {
delta = sqrt(delta2);
h = 2*sa*delta2*delta;
temp = -yN/h;
if ( temp>=-1.0001 && temp<=1.0001 ) {
if ( temp<-1 ) temp = -1; else if ( temp>1 ) temp = 1;
theta = acos(temp)/3;
ts[i++] = xN+2*delta*cos(theta);
ts[i++] = xN+2*delta*cos(2.0943951+theta); /* 2*pi/3 */
ts[i++] = xN+2*delta*cos(4.1887902+theta); /* 4*pi/3 */
}
}
} else if ( /* d==0 && */ delta2!=0 ) {
delta = yN/(2*sa);
delta = delta==0 ? 0 : delta>0 ? pow(delta,1./3.) : -pow(-delta,1./3.);
ts[i++] = xN + delta; /* this root twice, but that's irrelevant to me */
ts[i++] = xN - 2*delta;
} else if ( /* d==0 && */ delta2==0 ) {
if ( xN>=-0.0001 && xN<=1.0001 ) ts[0] = xN;
}
} else if ( sb!=0 ) {
extended d = sc*(extended) sc-4*(extended) sb*sd;
if ( d<0 && RealNear(d,0)) d=0;
if ( d<0 )
return(false); /* All roots imaginary */
d = sqrt(d);
ts[0] = (-sc-d)/(2*(extended) sb);
ts[1] = (-sc+d)/(2*(extended) sb);
} else if ( sc!=0 ) {
ts[0] = -sd/(extended) sc;
} else {
/* If it's a point then either everything is a solution, or nothing */
}
return( ts[0]!=-999999 );
}
int CubicSolve(const Spline1D *sp,bigreal sought, extended ts[3]) {
extended t;
extended ts2[3];
int i,j;
/* This routine gives us all solutions between [0,1] with -1 as an error flag */
/* http://mathforum.org/dr.math/faq/faq.cubic.equations.html */
ts[0] = ts[1] = ts[2] = -1;
if ( !_CubicSolve(sp,sought,ts2)) {
return( false );
}
for ( i=j=0; i<3; ++i ) {
if ( ts2[i]>-.0001 && ts2[i]<1.0001 ) {
if ( ts2[i]<0 ) ts[j++] = 0;
else if ( ts2[i]>1 ) ts[j++] = 1;
else
ts[j++] = ts2[i];
}
}
if ( j==0 )
return( false );
if ( ts[0]>ts[2] && ts[2]!=-1 ) {
t = ts[0]; ts[0] = ts[2]; ts[2] = t;
}
if ( ts[0]>ts[1] && ts[1]!=-1 ) {
t = ts[0]; ts[0] = ts[1]; ts[1] = t;
}
if ( ts[1]>ts[2] && ts[2]!=-1 ) {
t = ts[1]; ts[1] = ts[2]; ts[2] = t;
}
return( true );
}
static int _QuarticSolve(Quartic *q,extended ts[4]) {
extended extrema[5];
Spline1D sp;
int ecnt = 0, i, zcnt;
/* Two special cases */
if ( q->a==0 ) { /* It's really a cubic */
sp.a = q->b;
sp.b = q->c;
sp.c = q->d;
sp.d = q->e;
ts[3] = -999999;
return( _CubicSolve(&sp,0,ts));
} else if ( q->e==0 ) { /* we can factor out a zero root */
sp.a = q->a;
sp.b = q->b;
sp.c = q->c;
sp.d = q->d;
ts[0] = 0;
return( _CubicSolve(&sp,0,ts+1)+1);
}
sp.a = 4*q->a;
sp.b = 3*q->b;
sp.c = 2*q->c;
sp.d = q->d;
if ( _CubicSolve(&sp,0,extrema)) {
ecnt = 1;
if ( extrema[1]!=-999999 ) {
ecnt = 2;
if ( extrema[1]<extrema[0] ) {
extended temp = extrema[1]; extrema[1] = extrema[0]; extrema[0]=temp;
}
if ( extrema[2]!=-999999 ) {
ecnt = 3;
if ( extrema[2]<extrema[0] ) {
extended temp = extrema[2]; extrema[2] = extrema[0]; extrema[0]=temp;
}
if ( extrema[2]<extrema[1] ) {
extended temp = extrema[2]; extrema[2] = extrema[1]; extrema[1]=temp;
}
}
}
}
for ( i=ecnt-1; i>=0 ; --i )
extrema[i+1] = extrema[i];
/* Upper and lower bounds within which we'll search */
extrema[0] = -999;
extrema[ecnt+1] = 999;
ecnt += 2;
/* divide into monotonic sections & use binary search to find zeroes */
for ( i=zcnt=0; i<ecnt-1; ++i ) {
extended top, bottom, val;
extended topt, bottomt, t;
topt = extrema[i+1];
bottomt = extrema[i];
top = (((q->a*topt+q->b)*topt+q->c)*topt+q->d)*topt+q->e;
bottom = (((q->a*bottomt+q->b)*bottomt+q->c)*bottomt+q->d)*bottomt+q->e;
if ( top<bottom ) {
extended temp = top; top = bottom; bottom = temp;
temp = topt; topt = bottomt; bottomt = temp;
}
if ( bottom>.001 ) /* this monotonic is all above 0 */
continue;
if ( top<-.001 ) /* this monotonic is all below 0 */
continue;
if ( bottom>0 ) {
ts[zcnt++] = bottomt;
continue;
}
if ( top<0 ) {
ts[zcnt++] = topt;
continue;
}
for (;;) {
t = (topt+bottomt)/2;
if ( t==topt || t==bottomt ) {
ts[zcnt++] = t;
break;
}
val = (((q->a*t+q->b)*t+q->c)*t+q->d)*t+q->e;
if ( val>-.0001 && val<.0001 ) {
ts[zcnt++] = t;
break;
} else if ( val>0 ) {
top = val;
topt = t;
} else {
bottom = val;
bottomt = t;
}
}
}
for ( i=zcnt; i<4; ++i )
ts[i] = -999999;
return( zcnt );
}
extended SplineSolve(const Spline1D *sp, real tmin, real tmax, extended sought) {
/* We want to find t so that spline(t) = sought */
/* the curve must be monotonic */
/* returns t which is near sought or -1 */
extended ts[3];
int i;
extended t;
CubicSolve(sp,sought,ts);
if ( tmax<tmin ) { t = tmax; tmax = tmin; tmin = t; }
for ( i=0; i<3; ++i )
if ( ts[i]>=tmin && ts[i]<=tmax )
return( ts[i] );
return( -1 );
}
/* An IEEE double has 52 bits of precision. So one unit of rounding error will be */
/* the number divided by 2^51 */
# define D_RE_Factor (1024.0*1024.0*1024.0*1024.0*1024.0*2.0)
/* But that's not going to work near 0, so, since the t values we care about */
/* are [0,1], let's use 1.0/D_RE_Factor */
extended SplineSolveFixup(const Spline1D *sp, real tmin, real tmax, extended sought) {
extended ts[3];
int i;
bigreal factor;
extended t;
extended val, valp, valm;
CubicSolve(sp,sought,ts);
if ( tmax<tmin ) { t = tmax; tmax = tmin; tmin = t; }
for ( i=0; i<3; ++i )
if ( ts[i]>=tmin && ts[i]<=tmax )
break;
if ( i==3 ) {
/* nothing in range, but ... */
/* did a rounding error take a solution just outside the bounds? */
extended bestd = .0001; int besti = -1;
extended off;
for ( i=0; i<3 && ts[i]!=-1; ++i ) {
if ( ts[i]<tmin )
off = tmin-ts[i];
else
off = ts[i]-tmax;
if ( off<bestd ) {
bestd = off;
besti = i;
}
}
if ( besti==-1 )
return( -1 );
i = besti;
}
t = ts[i];
if ((val = (((sp->a*t+sp->b)*t+sp->c)*t+sp->d) - sought)<0 )
val=-val;
if ( val!=0 ) {
for ( factor=1024.0*1024.0*1024.0*1024.0*1024.0; factor>.5; factor/=2.0 ) {
extended tp = t + (factor*t)/D_RE_Factor;
extended tm = t - (factor*t)/D_RE_Factor;
if ( (valp = (((sp->a*tp+sp->b)*tp+sp->c)*tp+sp->d) - sought)<0 )
valp = -valp;
if ( (valm = (((sp->a*tm+sp->b)*tm+sp->c)*tm+sp->d) - sought)<0 )
valm = -valm;
if ( valp<val && valp<valm ) {
if ( factor==1024.0*1024.0*1024.0*1024*1024 ) {
bigreal it = IterateSplineSolve(sp,tmin,tmax,sought);
printf( "Used %g: orig-t: %g, new-t: %g iter-t: %g\n", (double) factor, (double) t, (double) tp, (double) it );
}
t = tp;
val = valp;
} else if ( valm<val ) {
if ( factor==1024.0*1024.0*1024.0*1024*1024 ) {
bigreal it = IterateSplineSolve(sp,tmin,tmax,sought);
printf( "Used -%g: orig-t: %g, new-t: %g iter-t: %g\n", (double) factor, (double) t, (double) tm, (double) it );
}
t = tm;
val = valm;
}
}
}
if ( t>=tmin && t<=tmax )
return( t );
return( -1 );
}
extended IterateSplineSolve(const Spline1D *sp, extended tmin, extended tmax,
extended sought) {
extended t, low, high, test;
Spline1D temp;
/* Now the closed form CubicSolver can have rounding errors so if we know */
/* the spline to be monotonic, an iterative approach is more accurate */
if ( tmin>tmax ) {
t=tmin; tmin=tmax; tmax=t;
}
temp = *sp;
temp.d -= sought;
if ( temp.a==0 && temp.b==0 && temp.c!=0 ) {
t = -temp.d/(extended) temp.c;
if ( t<tmin || t>tmax )
return( -1 );
return( t );
}
low = ((temp.a*tmin+temp.b)*tmin+temp.c)*tmin+temp.d;
high = ((temp.a*tmax+temp.b)*tmax+temp.c)*tmax+temp.d;
if ( low==0 )
return(tmin);
if ( high==0 )
return(tmax);
if (( low<0 && high>0 ) ||
( low>0 && high<0 )) {
for (;;) {
t = (tmax+tmin)/2;
if ( t==tmax || t==tmin )
return( t );
test = ((temp.a*t+temp.b)*t+temp.c)*t+temp.d;
if ( test==0 ) /* someone complained that this test relied on exact arithmetic. In fact this test will almost never be hit, the real exit test is the line above, when tmin/tmax are so close that there is no space between them in the floating representation */
return( t );
if ( (low<0 && test<0) || (low>0 && test>0) )
tmin=t;
else
tmax = t;
}
} else if ( low<.0001 && low>-.0001 )
return( tmin ); /* Rounding errors */
else if ( high<.0001 && high>-.0001 )
return( tmax );
return( -1 );
}
extended IterateSplineSolveFixup(const Spline1D *sp, extended tmin, extended tmax,
extended sought) {
// Search between tmin and tmax for a t-value at which the spline outputs sought.
extended t;
bigreal factor;
extended val, valp, valm;
if ( tmin>tmax ) {
t=tmin; tmin=tmax; tmax=t;
}
t = IterateSplineSolve(sp,tmin,tmax,sought);
if ( t==-1 )
return( -1 );
if ((val = (((sp->a*t+sp->b)*t+sp->c)*t+sp->d) - sought)<0 )
val=-val;
if ( val!=0 ) {
for ( factor=1024.0*1024.0*1024.0*1024.0*1024.0; factor>.5; factor/=2.0 ) {
extended tp = t + (factor*t)/D_RE_Factor;
extended tm = t - (factor*t)/D_RE_Factor;
if ( tp>tmax ) tp=tmax;
if ( tm<tmin ) tm=tmin;
if ( (valp = (((sp->a*tp+sp->b)*tp+sp->c)*tp+sp->d) - sought)<0 )
valp = -valp;
if ( (valm = (((sp->a*tm+sp->b)*tm+sp->c)*tm+sp->d) - sought)<0 )
valm = -valm;
if ( valp<val && valp<valm ) {
t = tp;
val = valp;
} else if ( valm<val ) {
t = tm;
val = valm;
}
}
}
if ( t==0 && !Within16RoundingErrors(sought,sought+val))
return( -1 );
/* if t!=0 then we we get the chance of far worse rounding errors */
else if ( t==tmax || t==tmin ) {
if ( Within16RoundingErrors(sought,sought+val) ||
Within16RoundingErrors(sp->a,sp->a+val) ||
Within16RoundingErrors(sp->b,sp->b+val) ||
Within16RoundingErrors(sp->c,sp->c+val) ||
Within16RoundingErrors(sp->c,sp->c+val) ||
Within16RoundingErrors(sp->d,sp->d+val))
return( t );
else
return( -1 );
}
if ( t>=tmin && t<=tmax )
return( t );
/* I don't think this can happen... */
return( -1 );
}
double CheckExtremaForSingleBitErrors(const Spline1D *sp, double t, double othert) {
double u1, um1;
double slope, slope1, slopem1;
int err;
double diff, factor;
if ( t<0 || t>1 )
return( t );
factor = t*0x40000/D_RE_Factor;
if ( (diff = t-othert)<0 ) diff= -diff;
if ( factor>diff/4 && diff!=0 ) /* This little check is to insure we don't skip beyond the well of this extremum into the next */
factor = diff/4;
slope = (3*(double) sp->a*t+2*sp->b)*t+sp->c;
if ( slope<0 ) slope = -slope;
for ( err = 0x40000; err!=0; err>>=1 ) {
u1 = t+factor;
slope1 = (3*(double) sp->a*u1+2*sp->b)*u1+sp->c;
if ( slope1<0 ) slope1 = -slope1;
um1 = t-factor;
slopem1 = (3*(double) sp->a*um1+2*sp->b)*um1+sp->c;
if ( slopem1<0 ) slopem1 = -slopem1;
if ( slope1<slope && slope1<=slopem1 && u1<=1.0 ) {
t = u1;
} else if ( slopem1<slope && slopem1<=slope1 && um1>=0.0 ) {
t = um1;
}
factor /= 2.0;
}
/* that seems as good as it gets */
return( t );
}
static void _SplineFindExtrema(const Spline1D *sp, extended *_t1, extended *_t2 ) {
extended t1= -1, t2= -1;
extended b2_fourac;
/* Find the extreme points on the curve */
/* Set to -1 if there are none or if they are outside the range [0,1] */
/* Order them so that t1<t2 */
/* If only one valid extremum it will be t1 */
/* (Does not check the end points unless they have derivative==0) */
/* (Does not check to see if d/dt==0 points are inflection points (rather than extrema) */
if ( sp->a!=0 ) {
/* cubic, possibly 2 extrema (possibly none) */
b2_fourac = 4*(extended)sp->b*sp->b - 12*(extended)sp->a*sp->c;
if ( b2_fourac>=0 ) {
b2_fourac = sqrt(b2_fourac);
t1 = (-2*sp->b - b2_fourac) / (6*sp->a);
t2 = (-2*sp->b + b2_fourac) / (6*sp->a);
t1 = CheckExtremaForSingleBitErrors(sp,t1,t2);
t2 = CheckExtremaForSingleBitErrors(sp,t2,t1);
if ( t1>t2 ) { extended temp = t1; t1 = t2; t2 = temp; }
else if ( t1==t2 ) t2 = -1;
if ( RealNear(t1,0)) t1=0; else if ( RealNear(t1,1)) t1=1;
if ( RealNear(t2,0)) t2=0; else if ( RealNear(t2,1)) t2=1;
}
} else if ( sp->b!=0 ) {
/* Quadratic, at most one extremum */
t1 = -sp->c/(2.0*(extended) sp->b);
} else /*if ( sp->c!=0 )*/ {
/* linear, no extrema */
}
*_t1 = t1; *_t2 = t2;
}
void SplineFindExtrema(const Spline1D *sp, extended *_t1, extended *_t2 ) {
extended t1= -1, t2= -1;
extended b2_fourac;
/* Find the extreme points on the curve */
/* Set to -1 if there are none or if they are outside the range [0,1] */
/* Order them so that t1<t2 */
/* If only one valid extremum it will be t1 */
/* (Does not check the end points unless they have derivative==0) */
/* (Does not check to see if d/dt==0 points are inflection points (rather than extrema) */
if ( sp->a!=0 ) {
/* cubic, possibly 2 extrema (possibly none) */
b2_fourac = 4*(extended) sp->b*sp->b - 12*(extended) sp->a*sp->c;
if ( b2_fourac>=0 ) {
b2_fourac = sqrt(b2_fourac);
t1 = (-2*sp->b - b2_fourac) / (6*sp->a);
t2 = (-2*sp->b + b2_fourac) / (6*sp->a);
t1 = CheckExtremaForSingleBitErrors(sp,t1,t2);
t2 = CheckExtremaForSingleBitErrors(sp,t2,t1);
if ( t1>t2 ) { extended temp = t1; t1 = t2; t2 = temp; }
else if ( t1==t2 ) t2 = -1;
if ( RealNear(t1,0)) t1=0; else if ( RealNear(t1,1)) t1=1;
if ( RealNear(t2,0)) t2=0; else if ( RealNear(t2,1)) t2=1;
if ( t2<=0 || t2>=1 ) t2 = -1;
if ( t1<=0 || t1>=1 ) { t1 = t2; t2 = -1; }
}
} else if ( sp->b!=0 ) {
/* Quadratic, at most one extremum */
t1 = -sp->c/(2.0*(extended) sp->b);
if ( t1<=0 || t1>=1 ) t1 = -1;
} else /*if ( sp->c!=0 )*/ {
/* linear, no extrema */
}
*_t1 = t1; *_t2 = t2;
}
bigreal SplineCurvature(Spline *s, bigreal t) {
/* Kappa = (x'y'' - y'x'') / (x'^2 + y'^2)^(3/2) */
bigreal dxdt, dydt, d2xdt2, d2ydt2, denom, numer;
if ( s==NULL )
return( CURVATURE_ERROR );
dxdt = (3*s->splines[0].a*t+2*s->splines[0].b)*t+s->splines[0].c;
dydt = (3*s->splines[1].a*t+2*s->splines[1].b)*t+s->splines[1].c;
d2xdt2 = 6*s->splines[0].a*t + 2*s->splines[0].b;
d2ydt2 = 6*s->splines[1].a*t + 2*s->splines[1].b;
denom = pow( dxdt*dxdt + dydt*dydt, 3.0/2.0 );
numer = dxdt*d2ydt2 - dydt*d2xdt2;
if ( numer==0 )
return( 0 );
if ( denom==0 )
return( CURVATURE_ERROR );
return( numer/denom );
}
int SplineAtInflection(Spline1D *sp, bigreal t ) {
/* It's a point of inflection if d sp/dt==0 and d2 sp/dt^2==0 */
return ( RealNear( (3*sp->a*t + 2*sp->b)*t + sp->c,0) &&
RealNear( 6*sp->a*t + 2*sp->b, 0));
}
int SplineAtMinMax(Spline1D *sp, bigreal t ) {
/* It's a point of inflection if d sp/dt==0 and d2 sp/dt^2!=0 */
return ( RealNear( (3*sp->a*t + 2*sp->b)*t + sp->c,0) &&
!RealNear( 6*sp->a*t + 2*sp->b, 0));
}
int Spline2DFindExtrema(const Spline *sp, extended extrema[4] ) {
int i,j;
BasePoint last, cur, mid;
/* If the control points are at the end-points then this (1D) spline is */
/* basically a line. But rounding errors can give us very faint extrema */
/* if we look for them */
if ( !Spline1DCantExtremeX(sp) )
SplineFindExtrema(&sp->splines[0],&extrema[0],&extrema[1]);
else
extrema[0] = extrema[1] = -1;
if ( !Spline1DCantExtremeY(sp) )
SplineFindExtrema(&sp->splines[1],&extrema[2],&extrema[3]);
else
extrema[2] = extrema[3] = -1;
for ( i=0; i<3; ++i ) for ( j=i+1; j<4; ++j ) {
if ( (extrema[i]==-1 && extrema[j]!=-1) || (extrema[i]>extrema[j] && extrema[j]!=-1) ) {
extended temp = extrema[i];
extrema[i] = extrema[j];
extrema[j] = temp;
}
}
for ( i=j=0; i<3 && extrema[i]!=-1; ++i ) {
if ( extrema[i]==extrema[i+1] ) {
for ( j=i+1; j<3; ++j )
extrema[j] = extrema[j+1];
extrema[3] = -1;
}
}
/* Extrema which are too close together are not interesting */
last = sp->from->me;
for ( i=0; i<4 && extrema[i]!=-1; ++i ) {
cur.x = ((sp->splines[0].a*extrema[i]+sp->splines[0].b)*extrema[i]+
sp->splines[0].c)*extrema[i]+sp->splines[0].d;
cur.y = ((sp->splines[1].a*extrema[i]+sp->splines[1].b)*extrema[i]+
sp->splines[1].c)*extrema[i]+sp->splines[1].d;
mid.x = (last.x+cur.x)/2; mid.y = (last.y+cur.y)/2;
if ( (mid.x==last.x || mid.x==cur.x) &&
(mid.y==last.y || mid.y==cur.y)) {
for ( j=i; j<3; ++j )
extrema[j] = extrema[j+1];
extrema[3] = -1;
--i;
} else
last = cur;
}
if ( extrema[0]!=-1 ) {
mid.x = (last.x+sp->to->me.x)/2; mid.y = (last.y+sp->to->me.y)/2;
if ( (mid.x==last.x || mid.x==cur.x) &&
(mid.y==last.y || mid.y==cur.y))
extrema[i-1] = -1;
}
for ( i=0; i<4 && extrema[i]!=-1; ++i );
if ( i!=0 ) {
cur = sp->to->me;
mid.x = (last.x+cur.x)/2; mid.y = (last.y+cur.y)/2;
if ( (mid.x==last.x || mid.x==cur.x) &&
(mid.y==last.y || mid.y==cur.y))
extrema[--i] = -1;
}
return( i );
}
int Spline2DFindPointsOfInflection(const Spline *sp, extended poi[2] ) {
int cnt=0;
extended a, b, c, b2_fourac, t;
/* A POI happens when d2 y/dx2 is zero. This is not the same as d2y/dt2 / d2x/dt2 */
/* d2 y/dx^2 = d/dt ( dy/dt / dx/dt ) / dx/dt */
/* = ( (dx/dt) * d2 y/dt2 - ((dy/dt) * d2 x/dt2) )/ (dx/dt)^3 */
/* (3ax*t^2+2bx*t+cx) * (6ay*t+2by) - (3ay*t^2+2by*t+cy) * (6ax*t+2bx) == 0 */
/* (3ax*t^2+2bx*t+cx) * (3ay*t+by) - (3ay*t^2+2by*t+cy) * (3ax*t+bx) == 0 */
/* 9*ax*ay*t^3 + (3ax*by+6bx*ay)*t^2 + (2bx*by+3cx*ay)*t + cx*by */
/* -(9*ax*ay*t^3 + (3ay*bx+6by*ax)*t^2 + (2by*bx+3cy*ax)*t + cy*bx)==0 */
/* 3*(ax*by-ay*bx)*t^2 + 3*(cx*ay-cy*ax)*t+ (cx*by-cy*bx) == 0 */
a = 3*((extended) sp->splines[1].a*sp->splines[0].b-(extended) sp->splines[0].a*sp->splines[1].b);
b = 3*((extended) sp->splines[0].c*sp->splines[1].a - (extended) sp->splines[1].c*sp->splines[0].a);
c = (extended) sp->splines[0].c*sp->splines[1].b-(extended) sp->splines[1].c*sp->splines[0].b;
if ( !RealNear(a,0) ) {
b2_fourac = b*b - 4*a*c;
poi[0] = poi[1] = -1;
if ( b2_fourac<0 )
return( 0 );
b2_fourac = sqrt( b2_fourac );
t = (-b+b2_fourac)/(2*a);
if ( t>=0 && t<=1.0 )
poi[cnt++] = t;
t = (-b-b2_fourac)/(2*a);
if ( t>=0 && t<=1.0 ) {
if ( cnt==1 && poi[0]>t ) {
poi[1] = poi[0];
poi[0] = t;
++cnt;
} else
poi[cnt++] = t;
}
} else if ( !RealNear(b,0) ) {
t = -c/b;
if ( t>=0 && t<=1.0 )
poi[cnt++] = t;
}
if ( cnt<2 )
poi[cnt] = -1;
return( cnt );
}
/* Ok, if the above routine finds an extremum that less than 1 unit */
/* from an endpoint or another extremum, then many things are */
/* just going to skip over it, and other things will be confused by this */
/* so just remove it. It should be so close the difference won't matter */
void SplineRemoveExtremaTooClose(Spline1D *sp, extended *_t1, extended *_t2 ) {
extended last, test;
extended t1= *_t1, t2 = *_t2;
if ( t1>t2 && t2!=-1 ) {
t1 = t2;
t2 = *_t1;
}
last = sp->d;