Permalink
Fetching contributors…
Cannot retrieve contributors at this time
2757 lines (2520 sloc) 96.1 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 "dumppfa.h"
#include "autohint.h"
#include "bvedit.h"
#include "fontforge.h"
#include <stdio.h>
#include <math.h>
#include <stdlib.h>
#include <string.h>
#include <ustring.h>
#include <utype.h>
#include <unistd.h>
#include <locale.h>
#if !defined(__MINGW32__)
# include <pwd.h>
#endif
#include <stdarg.h>
#include <time.h>
#include "psfont.h"
#include "splinefont.h"
#include <gdraw.h> /* For image defn */
#include "print.h" /* For makePatName */
#ifdef __CygWin
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
#endif
extern int autohint_before_generate;
char *xuid=NULL;
typedef void (*DumpChar)(int ch,void *data);
struct fileencryptdata {
DumpChar olddump;
void *olddata;
unsigned short r;
int hexline;
};
#define c1 52845
#define c2 22719
static void encodehex(int plain, void *d) {
struct fileencryptdata *fed = d;
unsigned char cypher = ( plain ^ (fed->r>>8));
fed->r = (cypher + fed->r) * c1 + c2;
{
int ch1, ch2;
ch1 = cypher>>4;
if ( ch1<=9 )
(fed->olddump)('0'+ch1,fed->olddata);
else
(fed->olddump)('A'-10+ch1,fed->olddata);
ch2 = cypher&0xf;
if ( ch2<=9 )
(fed->olddump)('0'+ch2,fed->olddata);
else
(fed->olddump)('A'-10+ch2,fed->olddata);
fed->hexline += 2;
if ( fed->hexline>70 ) {
(fed->olddump)('\n',fed->olddata);
fed->hexline = 0;
}
}
}
static void encodebin(int plain, void *d) {
struct fileencryptdata *fed = d;
unsigned char cypher = ( plain ^ (fed->r>>8));
fed->r = (cypher + fed->r) * c1 + c2;
(fed->olddump)(cypher,fed->olddata);
}
static DumpChar startfileencoding(DumpChar dumpchar,void *data,
struct fileencryptdata *fed,int dobinary) {
static unsigned char randombytes[4] = { 0xaa, 0x55, 0x3e, 0x4d };
DumpChar func;
randombytes[0] += 3;
randombytes[1] += 5;
randombytes[2] += 7;
randombytes[3] += 11;
fed->olddump = dumpchar;
fed->olddata = data;
fed->r = 55665;
fed->hexline = 0;
if ( dobinary ) { unsigned short r; unsigned char cypher;
while ( 1 ) {
/* find some random bytes where at least one of them encrypts to */
/* a non hex character */
r = 55665;
cypher = ( randombytes[0] ^ (r>>8));
if ( isspace(cypher) )
goto try_again;
if ( cypher<'0' || (cypher>'9' && cypher<'A') || (cypher>'F' && cypher<'a') || cypher>'f' )
break;
r = (cypher + r) * c1 + c2;
cypher = ( randombytes[1] ^ (r>>8));
if ( cypher<'0' || (cypher>'9' && cypher<'A') || (cypher>'F' && cypher<'a') || cypher>'f' )
break;
r = (cypher + r) * c1 + c2;
cypher = ( randombytes[2] ^ (r>>8));
if ( cypher<'0' || (cypher>'9' && cypher<'A') || (cypher>'F' && cypher<'a') || cypher>'f' )
break;
r = (cypher + r) * c1 + c2;
cypher = ( randombytes[3] ^ (r>>8));
if ( cypher<'0' || (cypher>'9' && cypher<'A') || (cypher>'F' && cypher<'a') || cypher>'f' )
break;
try_again:
randombytes[0] += 3;
randombytes[1] += 5;
randombytes[2] += 7;
randombytes[3] += 11;
}
}
func = dobinary ? encodebin : encodehex;
func(randombytes[0],fed);
func(randombytes[1],fed);
func(randombytes[2],fed);
func(randombytes[3],fed);
return( func );
}
/* Encode a string in adobe's format. choose a different set of initial random*/
/* bytes every time. (the expected value of leniv is 4. we have some support */
/* for values bigger than 5 but not as much as for values <=4) */
static void encodestrout(void (*dumpchar)(int ch,void *data), void *data,
unsigned char *value, int len, int leniv) {
unsigned short r = 4330;
unsigned char plain, cypher;
static unsigned char randombytes[10] = { 0xaa, 0x55, 0x3e, 0x4d, 0x89, 0x98, 'a', '4', 0, 0xff };
randombytes[0] += 3;
randombytes[1] += 5;
randombytes[2] += 7;
randombytes[3] += 11;
randombytes[4] += 13;
while ( leniv>0 ) {
plain = randombytes[leniv--%10];
cypher = ( plain ^ (r>>8));
r = (cypher + r) * c1 + c2;
dumpchar(cypher,data);
}
while ( len-->0 ) {
plain = *value++;
cypher = ( plain ^ (r>>8));
r = (cypher + r) * c1 + c2;
dumpchar(cypher,data);
}
}
static void dumpstr(void (*dumpchar)(int ch,void *data), void *data, const char *buf) {
while ( *buf )
dumpchar(*buf++,data);
}
static void dumpcarefully(void (*dumpchar)(int ch,void *data), void *data, const char *buf) {
int ch;
while ( (ch = *(unsigned char *) buf++)!='\0' ) {
if ( ch<' ' || ch>=0x7f || ch=='\\' || ch=='(' || ch==')' ) {
dumpchar('\\',data);
dumpchar('0'+(ch>>6),data);
dumpchar('0'+((ch>>3)&0x7),data);
dumpchar('0'+(ch&0x7),data);
} else
dumpchar(ch,data);
}
}
static void dumpascomments(void (*dumpchar)(int ch,void *data), void *data, const char *buf) {
int ch;
dumpchar('%',data);
dumpchar(' ',data);
while ( (ch = *buf++)!='\0' ) {
if ( ch=='\n' || ch=='\r' ) {
dumpchar('\n',data);
if ( ch=='\r' && *buf=='\n' ) ++buf;
if ( *buf=='\0' )
return;
dumpchar('%',data);
dumpchar(' ',data);
} else
dumpchar(ch,data);
}
dumpchar('\n',data);
}
static void dumpstrn(void (*dumpchar)(int ch,void *data), void *data, const char *buf, int n) {
while ( n-->0 )
dumpchar(*buf++,data);
}
static void dumpf(void (*dumpchar)(int ch,void *data), void *data, const char *format, ...) {
va_list args;
char buffer[300];
va_start(args,format);
vsprintf( buffer, format, args);
va_end(args);
dumpstr(dumpchar,data,buffer);
}
static int isStdEncoding(const char *encoding[256]) {
int i;
for ( i=0; i<256; ++i )
if ( strcmp(encoding[i],".notdef")==0 )
/* that's ok */;
else if ( strcmp(encoding[i],AdobeStandardEncoding[i])!=0 )
return( 0 );
return( 1 );
}
static void dumpblues(void (*dumpchar)(int ch,void *data), void *data,
const char *name, real *arr, int len, const char *ND) {
int i;
for ( --len; len>=0 && arr[len]==0.0; --len );
++len;
if ( len&1 ) ++len;
dumpf( dumpchar,data,"/%s [",name);
for ( i=0; i<len; ++i )
dumpf( dumpchar,data,"%g ", (double) arr[i]);
dumpf( dumpchar,data,"]%s\n",ND );
}
static void dumpdblmaxarray(void (*dumpchar)(int ch,void *data), void *data,
const char *name, real *arr, int len, const char *modifiers, const char *ND) {
int i;
for ( --len; len>=0 && arr[len]==0.0; --len );
++len;
dumpf( dumpchar,data,"/%s [",name);
for ( i=0; i<len; ++i )
dumpf( dumpchar,data,"%g ", (double) arr[i]);
dumpf( dumpchar,data,"]%s%s\n", modifiers, ND );
}
static void dumpdblarray(void (*dumpchar)(int ch,void *data), void *data,
const char *name, double *arr, int len, const char *modifiers, int exec) {
int i;
dumpf( dumpchar,data,"/%s %c",name, exec? '{' : '[' );
for ( i=0; i<len; ++i )
dumpf( dumpchar,data,"%g ", arr[i]);
dumpf( dumpchar,data,"%c%sdef\n", exec ? '}' : ']', modifiers );
}
struct psdict *PSDictCopy(struct psdict *dict) {
struct psdict *ret;
int i;
if ( dict==NULL )
return( NULL );
ret = calloc(1,sizeof(struct psdict));
ret->cnt = dict->cnt; ret->next = dict->next;
ret->keys = calloc(ret->cnt,sizeof(char *));
ret->values = calloc(ret->cnt,sizeof(char *));
for ( i=0; i<dict->next; ++i ) {
ret->keys[i] = copy(dict->keys[i]);
ret->values[i] = copy(dict->values[i]);
}
return( ret );
}
int PSDictFindEntry(struct psdict *dict, const char *key) {
int i;
if ( dict==NULL )
return( -1 );
for ( i=0; i<dict->next; ++i )
if ( strcmp(dict->keys[i],key)==0 )
return( i );
return( -1 );
}
char *PSDictHasEntry(struct psdict *dict, const char *key) {
int i;
if ( dict==NULL )
return( NULL );
for ( i=0; i<dict->next; ++i )
if ( strcmp(dict->keys[i],key)==0 )
return( dict->values[i] );
return( NULL );
}
int PSDictSame(struct psdict *dict1, struct psdict *dict2) {
int i;
if ( (dict1==NULL || dict1->cnt==0) && (dict2==NULL || dict2->cnt==0))
return( true );
if ( dict1==NULL || dict2==NULL || dict1->cnt!=dict2->cnt )
return( false );
for ( i=0; i<dict1->cnt; ++i ) {
char *val = PSDictHasEntry(dict2,dict1->keys[i]);
if ( val==NULL || strcmp(val,dict1->values[i])!=0 )
return( false );
}
return( true );
}
int PSDictRemoveEntry(struct psdict *dict, const char *key) {
int i;
if ( dict==NULL )
return( false );
for ( i=0; i<dict->next; ++i )
if ( strcmp(dict->keys[i],key)==0 )
break;
if ( i==dict->next )
return( false );
free( dict->keys[i]);
free( dict->values[i] );
--dict->next;
while ( i<dict->next ) {
dict->keys[i] = dict->keys[i+1];
dict->values[i] = dict->values[i+1];
++i;
}
return( true );
}
int PSDictChangeEntry(struct psdict *dict, const char *key, const char *newval) {
int i;
if ( dict==NULL )
return( -1 );
for ( i=0; i<dict->next; ++i )
if ( strcmp(dict->keys[i],key)==0 )
break;
if ( i==dict->next ) {
if ( dict->next>=dict->cnt ) {
dict->cnt += 10;
dict->keys = realloc(dict->keys,dict->cnt*sizeof(char *));
dict->values = realloc(dict->values,dict->cnt*sizeof(char *));
}
dict->keys[dict->next] = copy(key);
dict->values[dict->next] = NULL;
++dict->next;
}
free(dict->values[i]);
dict->values[i] = copy(newval);
return( i );
}
static void dumpsubrs(void (*dumpchar)(int ch,void *data), void *data,
SplineFont *sf, struct pschars *subrs ) {
int leniv = 4;
int i;
char *pt;
if ( subrs==NULL )
return;
if ( (pt=PSDictHasEntry(sf->private,"lenIV"))!=NULL )
leniv = strtol(pt,NULL,10);
dumpf(dumpchar,data,"/Subrs %d array\n",subrs->next);
for ( i=0; i<subrs->next; ++i ) {
dumpf(dumpchar,data,"dup %d %d RD ", i, subrs->lens[i]+leniv );
encodestrout(dumpchar,data,subrs->values[i],subrs->lens[i],leniv);
dumpstr(dumpchar,data," NP\n");
}
dumpstr(dumpchar,data,"ND\n");
}
/* Dumped within the private dict to get access to ND and RD */
static int dumpcharstrings(void (*dumpchar)(int ch,void *data), void *data,
SplineFont *sf, struct pschars *chars ) {
int leniv = 4;
int i;
char *pt;
if ( (pt=PSDictHasEntry(sf->private,"lenIV"))!=NULL )
leniv = strtol(pt,NULL,10);
dumpf(dumpchar,data,"2 index /CharStrings %d dict dup begin\n",chars->cnt);
for ( i=0; i<chars->next; ++i ) {
if ( chars->keys[i]==NULL )
break;
dumpf(dumpchar,data,"/%s %d RD ", chars->keys[i], chars->lens[i]+leniv );
encodestrout(dumpchar,data,chars->values[i],chars->lens[i],leniv);
dumpstr(dumpchar,data," ND\n");
if ( !ff_progress_next())
return( false );
}
dumpstr(dumpchar,data,"end end\nreadonly put\n");
return( true );
}
static void dumpsplineset(void (*dumpchar)(int ch,void *data), void *data,
SplineSet *spl, int pdfopers, int forceclose, int makeballs,
int do_clips ) {
SplinePoint *first, *sp;
for ( ; spl!=NULL; spl=spl->next ) {
if ( do_clips ^ spl->is_clip_path )
continue;
first = NULL;
for ( sp = spl->first; ; sp=sp->next->to ) {
if ( first==NULL )
dumpf( dumpchar, data, "\t%g %g %s\n", (double) sp->me.x, (double) sp->me.y,
pdfopers ? "m" : "moveto" );
else if ( sp->prev->knownlinear )
dumpf( dumpchar, data, "\t %g %g %s\n", (double) sp->me.x, (double) sp->me.y,
pdfopers ? "l" : "lineto" );
else
dumpf( dumpchar, data, "\t %g %g %g %g %g %g %s\n",
(double) sp->prev->from->nextcp.x, (double) sp->prev->from->nextcp.y,
(double) sp->prevcp.x, (double) sp->prevcp.y,
(double) sp->me.x, (double) sp->me.y,
pdfopers ? "c" : "curveto" );
if ( sp==first )
break;
if ( first==NULL ) first = sp;
if ( sp->next==NULL )
break;
}
if ( makeballs && (spl->first->next==NULL || (spl->first->next->to==spl->first)) )
dumpstr( dumpchar, data, pdfopers ? "\th\n" : "\tclosepath\n" );
if ( forceclose || spl->first->prev!=NULL )
dumpstr( dumpchar, data, pdfopers ? "\th\n" : "\tclosepath\n" );
}
}
static int InvertTransform(real inverse[6], real transform[6]) {
real temp[6], val;
int i;
for ( i=0; i<6; ++i ) temp[i] = transform[i];
inverse[0] = inverse[3] = 1;
inverse[1] = inverse[2] = inverse[4] = inverse[5] = 0;
if ( temp[0]==0 && temp[2]==0 )
return( false ); /* Not invertable */
if ( temp[0]==0 ) {
val = temp[0]; temp[0] = temp[2]; temp[2] = val;
val = temp[1]; temp[1] = temp[3]; temp[3] = val;
inverse[0] = inverse[3] = 0;
inverse[1] = inverse[2] = 1;
}
val = 1/temp[0];
temp[1] *= val; inverse[0] *= val; inverse[1]*=val;
val = temp[2];
temp[3] -= val*temp[1]; inverse[2] -= val*inverse[0]; inverse[3] -= val*inverse[1];
val = temp[4];
temp[5] -= val*temp[1]; inverse[4] -= val*inverse[0]; inverse[5] -= val*inverse[1];
if ( temp[3]==0 )
return( false );
val = 1/temp[3];
inverse[2] *= val; inverse[3]*=val;
val = temp[1];
inverse[0] -= val*inverse[2]; inverse[1] -= val*inverse[3];
val = temp[5];
inverse[4] -= val*inverse[2]; inverse[5] -= val*inverse[3];
return( true );
}
static void dumpGradient(void (*dumpchar)(int ch,void *data), void *data,
struct gradient *grad, RefChar *ref, SplineChar *sc, int layer, int pdfopers, int isstroke ) {
if ( pdfopers ) {
char buffer[200];
dumpf(dumpchar,data,"/Pattern %s\n", isstroke ? "CS" : "cs" );
makePatName(buffer,ref,sc,layer,isstroke,true);
dumpf(dumpchar,data,"/%s %s\n", buffer, isstroke ? "SCN" : "scn" );
/* PDF output looks much simpler than postscript. It isn't. It's just */
/* that the equivalent pdf dictionaries need to be created as objects*/
/* and can't live in the content stream, so they are done elsewhere */
} else {
dumpf(dumpchar,data, "<<\n /PatternType 2\n /Shading <<\n" );
dumpf(dumpchar,data, " /ShadingType %d\n", grad->radius==0?2:3 );
dumpf(dumpchar,data, " /ColorSpace /DeviceRGB\n" );
if ( grad->radius==0 ) {
dumpf(dumpchar,data, " /Coords [%g %g %g %g]\n",
grad->start.x, grad->start.y, grad->stop.x, grad->stop.y);
} else {
dumpf(dumpchar,data, " /Coords [%g %g 0 %g %g %g]\n",
grad->start.x, grad->start.y, grad->stop.x, grad->stop.y,
grad->radius );
}
dumpf(dumpchar,data, " /Extend [true true]\n" ); /* implies pad */
dumpf(dumpchar,data, " /Function <<\n" );
dumpf(dumpchar,data, " /FunctionType 0\n" ); /* Iterpolation between samples */
dumpf(dumpchar,data, " /Domain [%g %g]\n", grad->grad_stops[0].offset,
grad->grad_stops[grad->stop_cnt-1].offset );
dumpf(dumpchar,data, " /Range [0 1.0 0 1.0 0 1.0]\n" );
dumpf(dumpchar,data, " /Size [%d]\n", grad->stop_cnt==2?2:101 );
dumpf(dumpchar,data, " /BitsPerSample 8\n" );
dumpf(dumpchar,data, " /Decode [0 1.0 0 1.0 0 1.0]\n" );
dumpf(dumpchar,data, " /DataSource <" );
if ( grad->stop_cnt==2 ) {
unsigned col = grad->grad_stops[0].col;
if ( col==COLOR_INHERITED ) col = 0x000000;
dumpf(dumpchar,data,"%02x",(col>>16)&0xff);
dumpf(dumpchar,data,"%02x",(col>>8 )&0xff);
dumpf(dumpchar,data,"%02x",(col )&0xff);
col = grad->grad_stops[1].col;
dumpf(dumpchar,data,"%02x",(col>>16)&0xff);
dumpf(dumpchar,data,"%02x",(col>>8 )&0xff);
dumpf(dumpchar,data,"%02x",(col )&0xff);
} else {
int i,j;
/* Rather than try and figure out the minimum common divisor */
/* off all the offsets, I'll just assume they are all percent*/
(dumpchar)('\n',data);
for ( i=0; i<=100; ++i ) {
unsigned col;
double t = grad->grad_stops[0].offset +
(grad->grad_stops[grad->stop_cnt-1].offset-
grad->grad_stops[0].offset)* i/100.0;
for ( j=0; j<grad->stop_cnt; ++j )
if ( t<=grad->grad_stops[j].offset )
break;
if ( j==grad->stop_cnt )
col = grad->grad_stops[j-1].col;
else if ( t==grad->grad_stops[j].offset )
col = grad->grad_stops[j].col;
else {
double percent = (t-grad->grad_stops[j-1].offset)/ (grad->grad_stops[j].offset-grad->grad_stops[j-1].offset);
uint32 col1 = grad->grad_stops[j-1].col;
uint32 col2 = grad->grad_stops[j ].col;
int red, green, blue;
if ( col1==COLOR_INHERITED ) col1 = 0x000000;
if ( col2==COLOR_INHERITED ) col2 = 0x000000;
red = ((col1>>16)&0xff)*(1-percent) + ((col2>>16)&0xff)*percent;
green = ((col1>>8 )&0xff)*(1-percent) + ((col2>>8 )&0xff)*percent;
blue = ((col1 )&0xff)*(1-percent) + ((col2 )&0xff)*percent;
col = (red<<16) | (green<<8) | blue;
}
if ( col==COLOR_INHERITED ) col = 0x000000;
dumpf(dumpchar,data,"%02x",(col>>16)&0xff);
dumpf(dumpchar,data,"%02x",(col>>8 )&0xff);
dumpf(dumpchar,data,"%02x",(col )&0xff);
}
}
dumpf(dumpchar,data,">\n");
dumpf(dumpchar,data, " >>\n" );
dumpf(dumpchar,data, " >>\n" );
dumpf(dumpchar,data, ">> matrix makepattern setpattern\n" );
}
}
static void dumpPattern(void (*dumpchar)(int ch,void *data), void *data,
struct pattern *pat, RefChar *ref, SplineChar *sc, int layer, int pdfopers, int isstroke ) {
SplineChar *pattern_sc = SFGetChar(sc->parent,-1,pat->pattern);
DBounds b;
real scale[6], result[6];
if ( pdfopers ) {
char buffer[200];
dumpf(dumpchar,data,"/Pattern %s\n", isstroke ? "CS" : "cs" );
makePatName(buffer,ref,sc,layer,isstroke,false);
dumpf(dumpchar,data,"/%s %s\n", buffer, isstroke ? "SCN" : "scn" );
/* PDF output looks much simpler than postscript. It isn't. It's just */
/* that the equivalent pdf dictionaries need to be created as objects*/
/* and can't live in the content stream, so they are done elsewhere */
} else {
if ( pattern_sc==NULL )
LogError(_("No glyph named %s, used as a pattern in %s\n"), pat->pattern, sc->name);
PatternSCBounds(pattern_sc,&b);
dumpf(dumpchar,data, "<<\n" );
dumpf(dumpchar,data, " /PatternType 1\n" );
dumpf(dumpchar,data, " /PaintType 1\n" ); /* The intricacies of uncolored tiles are not something into which I wish to delve */
dumpf(dumpchar,data, " /TilingType 1\n" );
dumpf(dumpchar,data, " /BBox [%g %g %g %g]\n", b.minx, b.miny, b.maxx, b.maxy );
dumpf(dumpchar,data, " /XStep %g\n", b.maxx-b.minx );
dumpf(dumpchar,data, " /YStep %g\n", b.maxy-b.miny );
dumpf(dumpchar,data, " /PaintProc { begin\n" ); /* The begin pops the pattern dictionary off the stack. Don't really use it, but do need the pop */
SC_PSDump(dumpchar,data, pattern_sc, true, false, ly_all );
dumpf(dumpchar,data, " end }\n" );
memset(scale,0,sizeof(scale));
scale[0] = pat->width/(b.maxx-b.minx);
scale[3] = pat->height/(b.maxy-b.miny);
MatMultiply(scale,pat->transform, result);
dumpf(dumpchar,data, ">> [%g %g %g %g %g %g] makepattern setpattern\n",
result[0], result[1], result[2],
result[3], result[4], result[5]);
}
}
static void dumpbrush(void (*dumpchar)(int ch,void *data), void *data,
struct brush *brush, RefChar *ref, SplineChar *sc, int layer, int pdfopers ) {
if ( brush->gradient!=NULL )
dumpGradient(dumpchar,data,brush->gradient,ref,sc,layer,pdfopers,false);
else if ( brush->pattern!=NULL )
dumpPattern(dumpchar,data,brush->pattern,ref,sc,layer,pdfopers,false);
else if ( brush->col!=COLOR_INHERITED ) {
int r, g, b;
r = (brush->col>>16)&0xff;
g = (brush->col>>8 )&0xff;
b = (brush->col )&0xff;
if ( r==g && b==g )
dumpf(dumpchar,data,(pdfopers ? "%g g\n" : "%g setgray\n"), r/255.0 );
else
dumpf(dumpchar,data,(pdfopers ? "%g %g %g rg\n" : "%g %g %g setrgbcolor\n"),
r/255.0, g/255.0, b/255.0 );
if ( pdfopers && (double)brush->opacity<1.0 && brush->opacity>=0 )
dumpf(dumpchar,data,"/gs_fill_opacity_%g gs\n", (double)brush->opacity );
}
}
/* Grumble. PDF uses different operators for colors for stroke and fill */
static void dumppenbrush(void (*dumpchar)(int ch,void *data), void *data,
struct brush *brush, RefChar *ref, SplineChar *sc, int layer, int pdfopers ) {
if ( brush->gradient!=NULL )
dumpGradient(dumpchar,data,brush->gradient,ref,sc,layer,pdfopers,true);
else if ( brush->pattern!=NULL )
dumpPattern(dumpchar,data,brush->pattern,ref,sc,layer,pdfopers,true);
else if ( brush->col!=COLOR_INHERITED ) {
int r, g, b;
r = (brush->col>>16)&0xff;
g = (brush->col>>8 )&0xff;
b = (brush->col )&0xff;
if ( r==g && b==g )
dumpf(dumpchar,data,(pdfopers ? "%g G\n" : "%g setgray\n"), r/255.0 );
else
dumpf(dumpchar,data,(pdfopers ? "%g %g %g RG\n" : "%g %g %g setrgbcolor\n"),
r/255.0, g/255.0, b/255.0 );
if ( pdfopers && (double)brush->opacity<1.0 && brush->opacity>=0 )
dumpf(dumpchar,data,"/gs_stroke_opacity_%g gs\n", (double)brush->opacity );
}
}
static void dumppen(void (*dumpchar)(int ch,void *data), void *data,
struct pen *pen, RefChar *ref, SplineChar *sc, int layer, int pdfopers) {
dumppenbrush(dumpchar,data,&pen->brush,ref,sc,layer,pdfopers);
if ( pen->width!=WIDTH_INHERITED )
dumpf(dumpchar,data,(pdfopers ? "%g w\n": "%g setlinewidth\n"), (double)pen->width );
if ( pen->linejoin!=lj_inherited )
dumpf(dumpchar,data,(pdfopers ? "%d j\n": "%d setlinejoin\n"), pen->linejoin );
if ( pen->linecap!=lc_inherited )
dumpf(dumpchar,data,(pdfopers ? "%d J\n": "%d setlinecap\n"), pen->linecap );
if ( pen->trans[0]!=1.0 || pen->trans[3]!=1.0 || pen->trans[1]!=0 || pen->trans[2]!=0 )
dumpf(dumpchar,data,(pdfopers ? "%g %g %g %g 0 0 cm\n" : "[%g %g %g %g 0 0] concat\n"),
(double) pen->trans[0], (double) pen->trans[1], (double) pen->trans[2], (double) pen->trans[3]);
if ( pen->dashes[0]!=0 || pen->dashes[1]!=DASH_INHERITED ) {
size_t i;
dumpchar('[',data);
for ( i=0; i<sizeof(pen->dashes)/sizeof(pen->dashes[0]) && pen->dashes[i]!=0; ++i )
dumpf(dumpchar,data,"%d ", pen->dashes[i]);
dumpf(dumpchar,data,pdfopers ? "] 0 d\n" : "] 0 setdash\n");
}
}
struct psfilter {
int ascii85encode, ascii85n, ascii85bytes_per_line;
void (*dumpchar)(int ch,void *data);
void *data;
};
static void InitFilter(struct psfilter *ps,void (*dumpchar)(int ch,void *data), void *data) {
ps->ascii85encode = 0;
ps->ascii85n = 0;
ps->ascii85bytes_per_line = 0;
ps->dumpchar = dumpchar;
ps->data = data;
}
static void Filter(struct psfilter *ps,uint8 ch) {
ps->ascii85encode = (ps->ascii85encode<<8) | ch;
if ( ++ps->ascii85n == 4 ) {
int ch5, ch4, ch3, ch2, ch1;
uint32 val = ps->ascii85encode;
if ( val==0 ) {
(ps->dumpchar)('z',ps->data);
ps->ascii85n = 0;
if ( ++ps->ascii85bytes_per_line >= 76 ) {
(ps->dumpchar)('\n',ps->data);
ps->ascii85bytes_per_line = 0;
}
} else {
ch5 = val%85; val /= 85;
ch4 = val%85; val /= 85;
ch3 = val%85; val /= 85;
ch2 = val%85;
ch1 = val/85;
dumpf(ps->dumpchar, ps->data, "%c%c%c%c%c",
ch1+'!', ch2+'!', ch3+'!', ch4+'!', ch5+'!' );
ps->ascii85encode = 0;
ps->ascii85n = 0;
if (( ps->ascii85bytes_per_line+=5) >= 80 ) {
(ps->dumpchar)('\n',ps->data);
ps->ascii85bytes_per_line = 0;
}
}
}
}
static void FlushFilter(struct psfilter *ps) {
uint32 val = ps->ascii85encode;
int n = ps->ascii85n;
if ( n!=0 ) {
int ch4, ch3, ch2, ch1;
while ( n++<4 )
val<<=8;
/* ch5 = val%85; */ val /= 85;
ch4 = val%85; val /= 85;
ch3 = val%85; val /= 85;
ch2 = val%85;
ch1 = val/85;
(ps->dumpchar)(ch1+'!',ps->data);
(ps->dumpchar)(ch2+'!',ps->data);
if ( ps->ascii85n>=2 )
(ps->dumpchar)(ch3+'!',ps->data);
if ( ps->ascii85n>=3 )
(ps->dumpchar)(ch4+'!',ps->data);
}
(ps->dumpchar)('~',ps->data);
(ps->dumpchar)('>',ps->data);
(ps->dumpchar)('\n',ps->data);
}
/* Inside a font, I can't use a <stdin> as a data source. Probably because */
/* the parser doesn't know what to do with those data when building the char */
/* proc (as opposed to executing) */
/* So I can't use run length filters or other compression technique */
static void FilterStr(struct psfilter *ps,uint8 *pt, int len ) {
uint8 *end = pt+len;
while ( pt<end )
Filter(ps,*pt++);
}
static void PSDumpBinaryData(void (*dumpchar)(int ch,void *data), void *data,
uint8 *bytes,int rows, int bytes_per_row, int useful_bytes_per_row) {
struct psfilter ps;
int i,j,cnt,group_cnt;
const int max_string = 65536;
if ( useful_bytes_per_row*rows<max_string ) {
/* It all fits in one string. Easy peasy */
dumpf(dumpchar,data, "{<~" );
InitFilter(&ps,dumpchar,data);
if (bytes_per_row==useful_bytes_per_row )
FilterStr(&ps,(uint8 *) bytes, rows*bytes_per_row);
else for ( i=0; i<rows; ++i ) {
FilterStr(&ps,(uint8 *) (bytes + i*bytes_per_row),
useful_bytes_per_row);
}
FlushFilter(&ps);
dumpchar('}',data);
} else {
cnt = (max_string-1)/useful_bytes_per_row;
if ( cnt==0 ) cnt=1;
group_cnt = -1;
for ( i=0; i<rows; ) {
if ( i+cnt>=rows )
dumpf(dumpchar,data, "{currentdict /ff-image-cnt undef <~" );
else {
dumpf(dumpchar,data, "{{/ff-image-cnt %d def <~", i/cnt );
group_cnt = i/cnt;
}
InitFilter(&ps,dumpchar,data);
for ( j=0; j<cnt && i<rows; ++i, ++j ) {
FilterStr(&ps,(uint8 *) (bytes + i*bytes_per_row),
useful_bytes_per_row);
}
FlushFilter(&ps);
dumpf(dumpchar,data,"}\n");
}
for ( i=group_cnt-1; i>=0; --i ) {
dumpf(dumpchar,data,"ff-image-cnt %d eq 3 1 roll ifelse}\n", i );
}
dumpf(dumpchar,data,"currentdict /ff-image-cnt known not 3 1 roll ifelse}\n" );
}
}
static void PSDump24BinaryData(void (*dumpchar)(int ch,void *data), void *data,
struct _GImage *base ) {
struct psfilter ps;
int i,j,cnt,group_cnt;
register uint32 val;
register uint32 *pt, *end;
const int max_string = 65536;
if ( 3*base->width*base->height<max_string ) {
/* It all fits in one string. Easy peasy */
dumpf(dumpchar,data, "{<~" );
InitFilter(&ps,dumpchar,data);
for ( i=0; i<base->height; ++i ) {
pt = (uint32 *) (base->data + i*base->bytes_per_line);
end = pt + base->width;
while ( pt<end ) {
val = *pt++;
Filter(&ps,COLOR_RED(val));
Filter(&ps,COLOR_GREEN(val));
Filter(&ps,COLOR_BLUE(val));
}
}
FlushFilter(&ps);
dumpchar('}',data);
} else {
cnt = (max_string-1)/(3*base->width);
if ( cnt==0 ) cnt=1;
group_cnt = -1;
for ( i=0; i<base->height; ) {
if ( i+cnt>=base->height )
dumpf(dumpchar,data, "{currentdict /ff-image-cnt undef <~" );
else {
dumpf(dumpchar,data, "{{/ff-image-cnt %d def <~", i/cnt );
group_cnt = i/cnt;
}
InitFilter(&ps,dumpchar,data);
for ( j=0; j<cnt && i<base->height; ++i, ++j ) {
pt = (uint32 *) (base->data + i*base->bytes_per_line);
end = pt + base->width;
while ( pt<end ) {
val = *pt++;
Filter(&ps,COLOR_RED(val));
Filter(&ps,COLOR_GREEN(val));
Filter(&ps,COLOR_BLUE(val));
}
}
FlushFilter(&ps);
dumpf(dumpchar,data,"}\n");
}
for ( i=group_cnt-1; i>=0; --i ) {
dumpf(dumpchar,data,"ff-image-cnt %d eq 3 1 roll ifelse}\n", i );
}
dumpf(dumpchar,data,"currentdict /ff-image-cnt known not 3 1 roll ifelse}\n" );
}
}
static void PSDrawMonoImg(void (*dumpchar)(int ch,void *data), void *data,
struct _GImage *base,int use_imagemask) {
dumpf(dumpchar,data, " %d %d ", base->width, base->height );
if ( base->trans==1 )
dumpf(dumpchar,data, "false ");
else
dumpf(dumpchar,data, "true ");
dumpf(dumpchar,data, "[%d 0 0 %d 0 %d]\n",
base->width, -base->height, base->height);
PSDumpBinaryData(dumpchar,data,(uint8 *) base->data,base->height,base->bytes_per_line,(base->width+7)/8);
dumpf(dumpchar,data, "%s\n",
use_imagemask?"imagemask":"image" );
}
static void PSSetIndexColors(void (*dumpchar)(int ch,void *data), void *data,
GClut *clut) {
int i;
dumpf(dumpchar,data, "[/Indexed /DeviceRGB %d <\n", clut->clut_len-1 );
for ( i=0; i<clut->clut_len; ++i )
dumpf(dumpchar,data, "%02X%02X%02X%s", COLOR_RED(clut->clut[i]),
COLOR_GREEN(clut->clut[i]), COLOR_BLUE(clut->clut[i]),
i%11==10?"\n":" ");
dumpf(dumpchar,data,">\n] setcolorspace\n");
}
static void PSBuildImageIndexDict(void (*dumpchar)(int ch,void *data), void *data,
struct _GImage *base) {
/* I need an image dict, otherwise I am restricted to grey scale */
dumpf(dumpchar,data, "<<\n" );
dumpf(dumpchar,data, " /ImageType 1\n" );
dumpf(dumpchar,data, " /Width %d\n", base->width );
dumpf(dumpchar,data, " /Height %d\n", base->height );
dumpf(dumpchar,data, " /ImageMatrix [%d 0 0 %d 0 %d]\n",
base->width, -base->height, base->height);
dumpf(dumpchar,data, " /MultipleDataSources false\n" );
dumpf(dumpchar,data, " /BitsPerComponent 8\n" );
dumpf(dumpchar,data, " /Decode [0 255]\n" );
dumpf(dumpchar,data, " /Interpolate false\n" );
dumpf(dumpchar,data, " /DataSource " );
PSDumpBinaryData(dumpchar,data,base->data,base->height,base->bytes_per_line,
base->width);
dumpf(dumpchar,data, ">> image\n" );
}
static void PSDrawImg(void (*dumpchar)(int ch,void *data), void *data,
struct _GImage *base) {
if ( base->image_type == it_index ) {
PSSetIndexColors(dumpchar,data,base->clut);
PSBuildImageIndexDict(dumpchar,data,base);
dumpf(dumpchar,data, "[/DeviceRGB] setcolorspace\n" );
} else {
dumpf(dumpchar,data, "%d %d 8 [%d 0 0 %d 0 %d] ",
base->width, base->height, base->width, -base->height, base->height);
PSDump24BinaryData(dumpchar,data,base);
dumpf(dumpchar,data, "false 3 colorimage\n" );
}
}
static void dumpimage(void (*dumpchar)(int ch,void *data), void *data,
ImageList *imgl, int use_imagemask, int pdfopers,
int layer, int icnt, SplineChar *sc ) {
GImage *image = imgl->image;
struct _GImage *base = image->list_len==0?image->u.image:image->u.images[0];
if ( pdfopers ) {
dumpf( dumpchar, data, " q 1 0 0 1 %g %g cm %g 0 0 %g 0 0 cm\n",
(double) imgl->xoff, (double) (imgl->yoff-imgl->yscale*base->height),
(double) (imgl->xscale*base->width), (double) (imgl->yscale*base->height) );
dumpf( dumpchar, data, "/%s_ly%d_%d_image", sc->name, layer, icnt );
dumpf( dumpchar, data, " Do " ); /* I think I use this for imagemasks too... */
dumpstr(dumpchar,data,"Q\n");
} else {
dumpf( dumpchar, data, " gsave %g %g translate %g %g scale\n",
(double) imgl->xoff, (double) (imgl->yoff-imgl->yscale*base->height),
(double) (imgl->xscale*base->width), (double) (imgl->yscale*base->height) );
if ( base->image_type==it_mono ) {
PSDrawMonoImg(dumpchar,data,base,use_imagemask);
} else {
/* Just draw the image, ignore the complexity of transparent images */
PSDrawImg(dumpchar,data,base);
}
dumpstr(dumpchar,data,"grestore\n");
}
}
void SC_PSDump(void (*dumpchar)(int ch,void *data), void *data,
SplineChar *sc, int refs_to_splines, int pdfopers, int layer ) {
RefChar *ref;
real inverse[6];
int i, last, first;
SplineSet *temp;
if ( sc==NULL )
return;
last = first = layer;
if ( layer==ly_all )
first = last = ly_fore;
if ( sc->parent->multilayer ) {
first = ly_fore;
last = sc->layer_cnt-1;
}
for ( i=first; i<=last; ++i ) {
if ( sc->layers[i].splines!=NULL ) {
temp = sc->layers[i].splines;
if ( sc->layers[i].order2 ) temp = SplineSetsPSApprox(temp);
if ( sc->parent->multilayer ) {
dumpstr(dumpchar,data,pdfopers ? "q " : "gsave " );
if ( SSHasClip(temp)) {
dumpsplineset(dumpchar,data,temp,pdfopers,true, false, true);
dumpstr(dumpchar,data,pdfopers ? "W n " : "clip newpath\n" );
}
dumpsplineset(dumpchar,data,temp,pdfopers,sc->layers[i].dofill,
sc->layers[i].dostroke && sc->layers[i].stroke_pen.linecap==lc_round,
false);
if ( sc->layers[i].dofill && sc->layers[i].dostroke ) {
if ( pdfopers ) {
dumpbrush(dumpchar,data, &sc->layers[i].fill_brush,NULL,sc,i,pdfopers);
dumppen(dumpchar,data, &sc->layers[i].stroke_pen,NULL,sc,i,pdfopers);
dumpstr(dumpchar,data, "B " );
} else if ( sc->layers[i].fillfirst ) {
dumpstr(dumpchar,data, "gsave " );
dumpbrush(dumpchar,data, &sc->layers[i].fill_brush,NULL,sc,i,pdfopers);
dumpstr(dumpchar,data,"fill grestore " );
dumppen(dumpchar,data, &sc->layers[i].stroke_pen,NULL,sc,i,pdfopers);
dumpstr(dumpchar,data,"stroke " );
} else {
dumpstr(dumpchar,data, "gsave " );
dumppen(dumpchar,data, &sc->layers[i].stroke_pen,NULL,sc,i,pdfopers);
dumpstr(dumpchar,data,"stroke grestore " );
dumpbrush(dumpchar,data, &sc->layers[i].fill_brush,NULL,sc,i,pdfopers);
dumpstr(dumpchar,data,"fill " );
}
} else if ( sc->layers[i].dofill ) {
dumpbrush(dumpchar,data, &sc->layers[i].fill_brush,NULL,sc,i,pdfopers);
dumpstr(dumpchar,data,pdfopers ? "f ": "fill " );
} else if ( sc->layers[i].dostroke ) {
dumppen(dumpchar,data, &sc->layers[i].stroke_pen,NULL,sc,i,pdfopers);
dumpstr(dumpchar,data, pdfopers ? "S ": "stroke " );
}
dumpstr(dumpchar,data,pdfopers ? "Q\n" : "grestore\n" );
} else
dumpsplineset(dumpchar,data,temp,pdfopers,!sc->parent->strokedfont,false,
false);
if ( sc->layers[i].order2 ) SplinePointListsFree(temp);
}
if ( sc->layers[i].refs!=NULL ) {
if ( sc->parent->multilayer ) {
dumpstr(dumpchar,data,pdfopers ? "q " : "gsave " );
if ( sc->layers[i].dofill )
dumpbrush(dumpchar,data, &sc->layers[i].fill_brush, NULL, sc, i, pdfopers);
}
if ( refs_to_splines ) {
if ( !pdfopers || !sc->parent->multilayer ) {
/* In PostScript, patterns are transformed by the page's */
/* transformation matrix. In PDF they are not. */
/* Of course if we have no patterns we can still use this code */
for ( ref = sc->layers[i].refs; ref!=NULL; ref=ref->next ) {
dumpstr(dumpchar,data,pdfopers ? "q " : "gsave " );
if ( !MatIsIdentity(ref->transform) ) {
dumpf(dumpchar,data,pdfopers ? "%g %g %g %g %g %g cm " : "[%g %g %g %g %g %g] concat ",
(double) ref->transform[0], (double) ref->transform[1], (double) ref->transform[2],
(double) ref->transform[3], (double) ref->transform[4], (double) ref->transform[5]);
}
SC_PSDump(dumpchar,data,ref->sc,refs_to_splines,pdfopers,ly_all);
dumpstr(dumpchar,data,pdfopers ? "Q\n" : "grestore\n" );
}
} else {
/* If we get here we are outputting pdf, are type3, refs_2_splines */
/* We need different gradients and patterns for different transform */
/* matrices of references */
int j;
for ( ref = sc->layers[i].refs; ref!=NULL; ref=ref->next ) {
for ( j=0; j<ref->layer_cnt; ++j ) {
temp = ref->layers[j].splines;
/*if ( sc->layers[i].order2 )
temp = SplineSetsPSApprox(temp);*/
dumpstr(dumpchar,data,pdfopers ? "q" : "gsave " );
dumpsplineset(dumpchar,data,temp,pdfopers,ref->layers[j].dofill,
ref->layers[j].dostroke && ref->layers[j].stroke_pen.linecap==lc_round,
false);
if ( ref->layers[j].dofill && ref->layers[j].dostroke ) {
dumpbrush(dumpchar,data, &ref->layers[j].fill_brush,ref,ref->sc,j,pdfopers);
dumppen(dumpchar,data, &ref->layers[j].stroke_pen,ref,ref->sc,j,pdfopers);
dumpstr(dumpchar,data, "B " );
} else if ( ref->layers[j].dofill ) {
dumpbrush(dumpchar,data, &ref->layers[j].fill_brush,ref,ref->sc,j,pdfopers);
dumpstr(dumpchar,data,pdfopers ? "f ": "fill " );
} else if ( ref->layers[j].dostroke ) {
dumppen(dumpchar,data, &ref->layers[j].stroke_pen,ref,ref->sc,j,pdfopers);
dumpstr(dumpchar,data, pdfopers ? "S ": "stroke " );
}
dumpstr(dumpchar,data,pdfopers ? "Q\n" : "grestore\n" );
/* if ( sc->layers[layer].order2 )
SplinePointListsFree(temp);*/
}
}
}
} else {
dumpstr(dumpchar,data," pop -1\n" );
for ( ref = sc->layers[i].refs; ref!=NULL; ref=ref->next ) {
if ( ref->transform[0]!=1 || ref->transform[1]!=0 || ref->transform[2]!=0 ||
ref->transform[3]!=1 || ref->transform[4]!=0 || ref->transform[5]!=0 ) {
if ( InvertTransform(inverse,ref->transform)) {
if ( ref->transform[0]!=1 || ref->transform[1]!=0 ||
ref->transform[2]!=0 || ref->transform[3]!=1 )
dumpf(dumpchar,data, " [ %g %g %g %g %g %g ] concat ",
(double) ref->transform[0], (double) ref->transform[1], (double) ref->transform[2],
(double) ref->transform[3], (double) ref->transform[4], (double) ref->transform[5]);
else
dumpf(dumpchar,data, " %g %g translate ",
(double) ref->transform[4], (double) ref->transform[5]);
dumpf(dumpchar,data, "1 index /CharProcs get /%s get exec ",
ref->sc->name );
if ( inverse[0]!=1 || inverse[1]!=0 ||
inverse[2]!=0 || inverse[3]!=1 )
dumpf(dumpchar,data, "[ %g %g %g %g %g %g ] concat \n",
(double) inverse[0], (double) inverse[1], (double) inverse[2], (double) inverse[3], (double) inverse[4], (double) inverse[5]
);
else
dumpf(dumpchar,data, "%g %g translate\n",
(double) inverse[4], (double) inverse[5] );
}
} else
dumpf(dumpchar,data, " 1 index /CharProcs get /%s get exec\n", ref->sc->name );
}
}
if ( sc->parent->multilayer )
dumpstr(dumpchar,data,pdfopers ? "Q\n" : "grestore\n" );
}
if ( sc->layers[i].images!=NULL ) { ImageList *img; int icnt=0;
dumpstr(dumpchar,data,pdfopers ? "q\n" : "gsave\n" );
if ( sc->layers[i].dofill )
dumpbrush(dumpchar,data, &sc->layers[i].fill_brush,NULL,sc,i,pdfopers);
for ( img = sc->layers[i].images; img!=NULL; img=img->next, ++icnt )
dumpimage(dumpchar,data,img,sc->layers[i].dofill,pdfopers,i,icnt,sc);
dumpstr(dumpchar,data,pdfopers ? "Q\n" : "grestore\n" );
}
}
}
static int SCSetsColor(SplineChar *sc) {
int l;
RefChar *r;
ImageList *img;
for ( l=ly_fore ; l<sc->layer_cnt; ++l ) {
if ( sc->layers[l].fill_brush.col != COLOR_INHERITED )
return( true );
if ( sc->layers[l].fill_brush.gradient != NULL || sc->layers[l].fill_brush.pattern != NULL )
return( true );
if ( sc->layers[l].stroke_pen.brush.col != COLOR_INHERITED )
return( true );
if ( sc->layers[l].stroke_pen.brush.gradient != NULL || sc->layers[l].stroke_pen.brush.pattern != NULL )
return( true );
for ( img = sc->layers[l].images; img!=NULL; img=img->next ) {
GImage *image = img->image;
struct _GImage *base = image->list_len==0?image->u.image:image->u.images[0];
if ( base->image_type!=it_mono )
return( true );
if ( !sc->layers[l].dofill )
return( true );
}
for ( r = sc->layers[l].refs; r!=NULL; r = r->next )
if ( SCSetsColor(r->sc) )
return( true );
}
return( false );
}
static void dumpproc(void (*dumpchar)(int ch,void *data), void *data, SplineChar *sc ) {
DBounds b;
SplineCharFindBounds(sc,&b);
dumpf(dumpchar,data," /%s { ",sc->name);
if ( sc->dependents!=NULL )
dumpstr(dumpchar,data,"dup -1 ne { ");
if ( !SCSetsColor(sc) ) {
dumpf(dumpchar,data,"%d 0 %d %d %d %d setcachedevice",
(int) sc->width, (int) floor(b.minx), (int) floor(b.miny),
(int) ceil(b.maxx), (int) ceil(b.maxy) );
} else {
/* can't cache it if we set colour/grey within */
dumpf(dumpchar,data,"%d 0 setcharwidth",
(int) sc->width );
}
if ( sc->dependents!=NULL )
dumpstr(dumpchar,data," } if\n");
else
dumpstr(dumpchar,data,"\n");
SC_PSDump(dumpchar,data,sc,false,false,ly_all);
dumpstr(dumpchar,data," } bind def\n" );
}
static int dumpcharprocs(void (*dumpchar)(int ch,void *data), void *data, SplineFont *sf ) {
/* for type 3 fonts */
int cnt, i, notdefpos = -1;
cnt = 0;
notdefpos = SFFindNotdef(sf,-2);
for ( i=0; i<sf->glyphcnt; ++i )
if ( SCWorthOutputting(sf->glyphs[i]) ) {
if ( strcmp(sf->glyphs[i]->name,".notdef")!=0 )
++cnt;
}
++cnt; /* one notdef entry */
dumpf(dumpchar,data,"/CharProcs %d dict def\nCharProcs begin\n", cnt );
i = 0;
if ( notdefpos!=-1 )
dumpproc(dumpchar,data,sf->glyphs[notdefpos]);
else {
dumpf(dumpchar, data, " /.notdef { %d 0 0 0 0 0 setcachedevice } bind def\n",
sf->ascent+sf->descent );
if ( sf->glyphs[0]!=NULL && strcmp(sf->glyphs[0]->name,".notdef")==0 )
++i;
}
for ( ; i<sf->glyphcnt; ++i ) if ( i!=notdefpos ) {
if ( SCWorthOutputting(sf->glyphs[i]) )
dumpproc(dumpchar,data,sf->glyphs[i]);
if ( !ff_progress_next())
return( false );
}
dumpstr(dumpchar,data,"end\ncurrentdict end\n" );
dumpf(dumpchar, data, "/%s exch definefont\n", sf->fontname );
return( true );
}
extern const uint8 *const subrs[10];
extern const int subrslens[10];
static struct pschars *initsubrs(MMSet *mm) {
int i;
struct pschars *sub;
sub = calloc(1,sizeof(struct pschars));
sub->cnt = 10;
sub->lens = malloc(10*sizeof(int));
sub->values = malloc(10*sizeof(uint8 *));
for ( i=0; i<5; ++i ) {
++sub->next;
sub->values[i] = (uint8 *) copyn((const char *) subrs[i],subrslens[i]);
sub->lens[i] = subrslens[i];
}
sub->next = 5;
if ( mm!=NULL ) {
static int cnts[] = { 1,2,3,4,6 };
for ( ; i<10 && cnts[i-5]*mm->instance_count<22; ++i ) {
++sub->next;
sub->values[i] = (uint8 *) copyn((const char *) subrs[i],subrslens[i]);
sub->values[i][0] += cnts[i-5]*mm->instance_count;
sub->lens[i] = subrslens[i];
}
sub->next = 10;
}
return( sub );
}
extern const char **othersubrs_copyright[];
extern const char **othersubrs[];
extern const char *cid_othersubrs[];
static void dumpothersubrs(void (*dumpchar)(int ch,void *data), void *data,
int incid, int needsflex, int needscounters, MMSet *mm ) {
int i,j;
dumpstr(dumpchar,data,"/OtherSubrs \n" );
if ( incid ) {
for ( i=0; cid_othersubrs[i]!=NULL; ++i ) {
dumpstr(dumpchar,data,cid_othersubrs[i]);
dumpchar('\n',data);
}
} else {
int max_subr, min_subr;
/* I assume I always want the hint replacement subr, (it's small) */
/* but the flex subrs are large, and if I can omit them, I shall */
if ( needsflex ) {
min_subr = 0;
max_subr = 3;
} else {
min_subr = 3;
max_subr = 3;
}
if ( needscounters )
max_subr = 13;
for ( i=0; othersubrs_copyright[0][i]!=NULL; ++i ) {
dumpstr(dumpchar,data,othersubrs_copyright[0][i]);
dumpchar('\n',data);
}
dumpstr(dumpchar,data,"[ "); /* start array */
for ( j=0; j<min_subr; ++j )
dumpstr(dumpchar,data," {}\n");
for ( ; j<=max_subr; ++j )
for ( i=0; othersubrs[j][i]!=NULL; ++i ) {
dumpstr(dumpchar,data,othersubrs[j][i]);
dumpchar('\n',data);
}
if ( mm!=NULL ) {
/* MM other subrs begin at 14, so skip anything up till then */
for ( ; j<=13; ++j )
dumpstr(dumpchar,data," {}\n");
}
if ( mm!=NULL ) {
/* the code for the multiple master subroutines depends on */
/* the number of font instances, so we can't just blithely copy */
/* an example from Adobe (and they don't provide one anyway) */
dumpf(dumpchar, data, "{ %d 1 roll $Blend } bind\n", mm->instance_count );
if ( 2*mm->instance_count<22 )
dumpf(dumpchar, data, "{ exch %d %d roll $Blend exch %d 2 roll $Blend } bind\n",
2*mm->instance_count, 1-mm->instance_count,
mm->instance_count+1);
if ( 3*mm->instance_count<22 )
dumpf(dumpchar, data, "{ 3 -1 roll %d %d roll $Blend 3 -1 roll %d %d roll $Blend 3 -1 roll %d 2 roll $Blend } bind\n",
3*mm->instance_count, 1-mm->instance_count,
2*mm->instance_count+1, 1-mm->instance_count,
mm->instance_count+2);
if ( 4*mm->instance_count<22 )
dumpf(dumpchar, data, "{ 4 -1 roll %d %d roll $Blend 4 -1 roll %d %d roll $Blend 4 -1 roll %d %d roll $Blend 4 -1 roll %d 3 roll $Blend } bind\n",
4*mm->instance_count, 1-mm->instance_count,
3*mm->instance_count+1, 1-mm->instance_count,
2*mm->instance_count+2, 1-mm->instance_count,
mm->instance_count+3);
if ( 6*mm->instance_count<22 )
dumpf(dumpchar, data, "{ 6 -1 roll %d %d roll $Blend 6 -1 roll %d %d roll $Blend 6 -1 roll %d %d roll $Blend 6 -1 roll %d %d roll $Blend 6 -1 roll %d %d roll $Blend 6 -1 roll %d 5 roll $Blend } bind\n",
6*mm->instance_count, 1-mm->instance_count,
5*mm->instance_count+1, 1-mm->instance_count,
4*mm->instance_count+2, 1-mm->instance_count,
3*mm->instance_count+3, 1-mm->instance_count,
2*mm->instance_count+4, 1-mm->instance_count,
mm->instance_count+5);
}
dumpstr(dumpchar,data,"] "); /* End array */
}
dumpstr(dumpchar,data,incid?"def\n":"ND\n" );
}
static char *dumptospace(void (*dumpchar)(int ch,void *data), void *data,
char *str) {
while ( *str!=' ' && *str!=']' && *str!='\0' )
dumpchar(*str++,data);
return( str );
}
static void dumpmmprivatearr(void (*dumpchar)(int ch,void *data), void *data,
char *privates[16], int instance_count) {
int j;
for ( j=0; j<instance_count; ++j )
while ( *privates[j]==' ' ) ++privates[j];
dumpchar('[',data);
if ( *privates[0]=='[' ) {
/* It's an array */
for ( j=0; j<instance_count; ++j )
++privates[j];
while ( *privates[0]!=']' && *privates[0]!='\0' ) {
for ( j=0; j<instance_count; ++j )
while ( *privates[j]==' ' ) ++privates[j];
if ( *privates[0]==']' || *privates[0]=='\0' )
break;
dumpchar('[',data);
privates[0] = dumptospace(dumpchar,data,privates[0]);
for ( j=1; j<instance_count; ++j ) {
dumpchar(' ',data);
privates[j] = dumptospace(dumpchar,data,privates[j]);
}
dumpchar(']',data);
}
} else {
/* It's not an array */
dumpstr(dumpchar,data,privates[0]);
for ( j=1; j<instance_count; ++j ) {
dumpchar(' ',data);
dumpstr(dumpchar,data,privates[j]);
}
}
dumpchar(']',data);
}
static void dumpmmprivate(void (*dumpchar)(int ch,void *data), void *data,MMSet *mm) {
char *privates[16];
int j,k, missing, allsame;
struct psdict *private = mm->instances[0]->private;
if ( private==NULL )
return;
dumpstr(dumpchar,data,"3 index /Blend get /Private get begin\n");
for ( k=0; k<private->next; ++k ) {
privates[0] = private->values[k];
missing = false; allsame = true;
for ( j=1; j<mm->instance_count; ++j ) {
privates[j] = PSDictHasEntry(mm->instances[j]->private,private->keys[k]);
if ( privates[j]==NULL ) {
missing = true;
break;
} else if ( strcmp(privates[j],privates[0])!=0 )
allsame = false;
}
if ( missing || allsame )
continue;
dumpf(dumpchar,data," /%s ", private->keys[k]);
dumpmmprivatearr(dumpchar,data,privates,mm->instance_count);
dumpstr(dumpchar,data, " def\n" );
}
dumpstr(dumpchar,data,"end\n");
}
static double FindMaxDiffOfBlues(char *pt, double max_diff) {
char *end;
double p1, p2;
while ( *pt==' ' || *pt=='[' ) ++pt;
for (;;) {
p1 = strtod(pt,&end);
if ( end==pt )
break;
pt = end;
p2 = strtod(pt,&end);
if ( end==pt )
break;
if ( p2-p1 >max_diff ) max_diff = p2-p1;
pt = end;
}
return( max_diff );
}
double BlueScaleFigureForced(struct psdict *private_,real bluevalues[], real otherblues[]) {
double max_diff=0;
char *pt;
int i;
pt = PSDictHasEntry(private_,"BlueValues");
if ( pt!=NULL ) {
max_diff = FindMaxDiffOfBlues(pt,max_diff);
} else if ( bluevalues!=NULL ) {
for ( i=0; i<14 && (bluevalues[i]!=0 || bluevalues[i+1])!=0; i+=2 ) {
if ( bluevalues[i+1] - bluevalues[i]>=max_diff )
max_diff = bluevalues[i+1] - bluevalues[i];
}
}
pt = PSDictHasEntry(private_,"FamilyBlues");
if ( pt!=NULL )
max_diff = FindMaxDiffOfBlues(pt,max_diff);
pt = PSDictHasEntry(private_,"OtherBlues");
if ( pt!=NULL )
max_diff = FindMaxDiffOfBlues(pt,max_diff);
else if ( otherblues!=NULL ) {
for ( i=0; i<10 && (otherblues[i]!=0 || otherblues[i+1]!=0); i+=2 ) {
if ( otherblues[i+1] - otherblues[i]>=max_diff )
max_diff = otherblues[i+1] - otherblues[i];
}
}
pt = PSDictHasEntry(private_,"FamilyOtherBlues");
if ( pt!=NULL )
max_diff = FindMaxDiffOfBlues(pt,max_diff);
if ( max_diff<=0 )
return( -1 );
if ( 1/max_diff > .039625 )
return( -1 );
return rint(240.0*0.99/max_diff)/240.0;
}
double BlueScaleFigure(struct psdict *private_,real bluevalues[], real otherblues[]) {
if ( PSDictHasEntry(private_,"BlueScale")!=NULL )
return( -1 );
return BlueScaleFigureForced(private_, bluevalues, otherblues);
}
static int dumpprivatestuff(void (*dumpchar)(int ch,void *data), void *data,
SplineFont *sf, struct fddata *incid, int flags, enum fontformat format,
EncMap *map, int layer ) {
int cnt, mi;
real bluevalues[14], otherblues[10];
real snapcnt[12];
real stemsnaph[12], stemsnapv[12];
real stdhw[1], stdvw[1];
int flex_max;
int i;
int hasblue=0, hash=0, hasv=0, hasshift/*, hasxuid*/, hasbold, haslg;
int isbold=false;
int iscjk;
struct pschars *subrs = NULL, *chars = NULL;
const char *ND="def";
MMSet *mm = (format==ff_mma || format==ff_mmb)? sf->mm : NULL;
double bluescale;
if ( incid==NULL ) {
flex_max = SplineFontIsFlexible(sf,layer,flags);
if ( (subrs = initsubrs(mm))==NULL )
return( false );
iscjk = SFIsCJK(sf,map);
} else {
flex_max = incid->flexmax;
iscjk = incid->iscjk;
}
hasbold = PSDictHasEntry(sf->private,"ForceBold")!=NULL;
hasblue = PSDictHasEntry(sf->private,"BlueValues")!=NULL;
hash = PSDictHasEntry(sf->private,"StdHW")!=NULL;
hasv = PSDictHasEntry(sf->private,"StdVW")!=NULL;
hasshift = PSDictHasEntry(sf->private,"BlueShift")!=NULL;
/*hasxuid = PSDictHasEntry(sf->private,"XUID")!=NULL;*/
haslg = PSDictHasEntry(sf->private,"LanguageGroup")!=NULL;
if ( sf->weight!=NULL &&
(strstrmatch(sf->weight,"Bold")!=NULL ||
strstrmatch(sf->weight,"Heavy")!=NULL ||
strstrmatch(sf->weight,"Black")!=NULL ||
strstrmatch(sf->weight,"Grass")!=NULL ||
strstrmatch(sf->weight,"Fett")!=NULL))
isbold = true;
if ( sf->pfminfo.pfmset && sf->pfminfo.weight>=700 )
isbold = true;
ff_progress_change_stages(2+2-hasblue);
if ( autohint_before_generate && SFNeedsAutoHint(sf) &&
!(flags&ps_flag_nohints)) {
ff_progress_change_line1(_("Auto Hinting Font..."));
SplineFontAutoHint(sf,layer);
}
if ( !ff_progress_next_stage())
{
PSCharsFree(subrs);
return( false );
}
otherblues[0] = otherblues[1] = bluevalues[0] = bluevalues[1] = 0;
if ( !hasblue ) {
FindBlues(sf,layer,bluevalues,otherblues);
if ( !ff_progress_next_stage())
return( false );
}
bluescale = BlueScaleFigure(sf->private,bluevalues,otherblues);
if ( !hash || !hasv )
stdhw[0] = stdvw[0] = 0;
if ( !hash ) {
FindHStems(sf,stemsnaph,snapcnt);
mi = -1;
for ( i=0; i<12 && stemsnaph[i]!=0; ++i )
if ( mi==-1 ) mi = i;
else if ( snapcnt[i]>snapcnt[mi] ) mi = i;
if ( mi!=-1 ) stdhw[0] = stemsnaph[mi];
}
if ( !hasv ) {
FindVStems(sf,stemsnapv,snapcnt);
mi = -1;
for ( i=0; i<12 && stemsnapv[i]!=0 ; ++i )
if ( mi==-1 ) mi = i;
else if ( snapcnt[i]>snapcnt[mi] ) mi = i;
if ( mi!=-1 ) stdvw[0] = stemsnapv[mi];
}
if ( incid==NULL ) {
ff_progress_next_stage();
ff_progress_change_line1(_("Converting PostScript"));
if ( (chars = SplineFont2ChrsSubrs(sf,iscjk,subrs,flags,format,layer))==NULL )
{
PSCharsFree(subrs);
return( false );
}
ff_progress_next_stage();
ff_progress_change_line1(_("Saving PostScript Font"));
}
if ( incid==NULL ) dumpstr(dumpchar,data,"dup\n");
cnt = 0;
if ( !hasblue ) ++cnt; /* bluevalues is required, but might be in private */
if ( !hasshift && flex_max>=7 ) ++cnt; /* BlueShift needs to be specified if flex wants something bigger than default */
if ( bluescale!=-1 ) ++cnt;
++cnt; /* minfeature is required */
if ( !hasblue && (otherblues[0]!=0 || otherblues[1]!=0) ) ++cnt;
++cnt; /* password is required */
if ( sf->tempuniqueid!=0 && sf->use_uniqueid )
++cnt; /* UniqueID should be in both private and public areas */
if ( incid==NULL ) {
++cnt; /* nd is required */
++cnt; /* np is required */
++cnt; /* rd is required */
}
if ( !haslg && iscjk ) ++cnt;
if ( !hash ) {
if ( stdhw[0]!=0 ) ++cnt;
if ( stemsnaph[0]!=0 ) ++cnt;
}
if ( !hasv ) {
if ( stdvw[0]!=0 ) ++cnt;
if ( stemsnapv[0]!=0 ) ++cnt;
}
cnt += sf->private!=NULL?sf->private->next:0;
if ( incid==NULL ) {
++cnt; /* Subrs entry */
++cnt; /* Other Subrs */
} else {
cnt += 3; /* subrmap, etc. */
++cnt; /* Other Subrs */
}
if ( flex_max>0 ) ++cnt;
if ( hasbold || isbold ) ++cnt;
dumpf(dumpchar,data,"/Private %d dict dup begin\n", cnt );
/* These guys are required and fixed */
if ( incid==NULL ) {
dumpstr(dumpchar,data,"/RD{string currentfile exch readstring pop}executeonly def\n" );
dumpstr(dumpchar,data,"/ND{noaccess def}executeonly def\n" );
dumpstr(dumpchar,data,"/NP{noaccess put}executeonly def\n" );
ND = "ND";
}
dumpf(dumpchar,data,"/MinFeature{16 16}%s\n", ND );
dumpstr(dumpchar,data,"/password 5839 def\n" );
if ( !hasblue ) {
dumpblues(dumpchar,data,"BlueValues",bluevalues,14,ND);
if ( otherblues[0]!=0 || otherblues[1]!=0 )
dumpblues(dumpchar,data,"OtherBlues",otherblues,10,ND);
}
if ( !hash ) {
if ( stdhw[0]!=0 )
dumpf(dumpchar,data,"/StdHW [%g] %s\n", (double) stdhw[0], ND );
if ( stemsnaph[0]!=0 )
dumpdblmaxarray(dumpchar,data,"StemSnapH",stemsnaph,12,"", ND);
}
if ( !hasv ) {
if ( stdvw[0]!=0 )
dumpf(dumpchar,data,"/StdVW [%g] %s\n", (double) stdvw[0],ND );
if ( stemsnapv[0]!=0 )
dumpdblmaxarray(dumpchar,data,"StemSnapV",stemsnapv,12,"", ND);
}
if ( !hasshift && flex_max>=7 )
dumpf(dumpchar,data,"/BlueShift %d def\n", flex_max+1 );
if ( bluescale!=-1 )
dumpf(dumpchar,data,"/BlueScale %g def\n", bluescale );
if ( isbold && !hasbold )
dumpf(dumpchar,data,"/ForceBold true def\n" );
if ( !haslg && iscjk )
dumpf(dumpchar,data,"/LanguageGroup 1 def\n" );
if ( sf->tempuniqueid!=0 && sf->tempuniqueid!=-1 && sf->use_uniqueid )
dumpf(dumpchar,data,"/UniqueID %d def\n", sf->tempuniqueid );
if ( sf->private!=NULL ) {
for ( i=0; i<sf->private->next; ++i ) {
dumpf(dumpchar,data,"/%s ", sf->private->keys[i]);
dumpstr(dumpchar,data,sf->private->values[i]);
if ( strcmp(sf->private->keys[i],"BlueValues")==0 ||
strcmp(sf->private->keys[i],"OtherBlues")==0 ||
strcmp(sf->private->keys[i],"StdHW")==0 ||
strcmp(sf->private->keys[i],"StdVW")==0 ||
strcmp(sf->private->keys[i],"StemSnapH")==0 ||
strcmp(sf->private->keys[i],"StemSnapV")==0 )
dumpf(dumpchar,data," %s\n", ND);
else
dumpstr(dumpchar,data," def\n");
}
}
if ( mm!=NULL )
dumpmmprivate(dumpchar,data,mm);
dumpothersubrs(dumpchar,data,incid!=NULL,flex_max>0,iscjk,mm);
if ( incid!=NULL ) {
dumpf(dumpchar,data," /SubrMapOffset %d def\n", incid->subrmapoff );
dumpf(dumpchar,data," /SDBytes %d def\n", incid->sdbytes );
dumpf(dumpchar,data," /SubrCount %d def\n", incid->subrcnt );
dumpstr(dumpchar,data,"end def\n");
} else {
dumpsubrs(dumpchar,data,sf,subrs);
dumpcharstrings(dumpchar,data,sf, chars );
dumpstr(dumpchar,data,"put\n");
PSCharsFree(chars);
PSCharsFree(subrs);
}
ff_progress_change_stages(1);
return( true );
}
static void dumpfontinfo(void (*dumpchar)(int ch,void *data), void *data, SplineFont *sf,
enum fontformat format) {
int cnt;
cnt = 0;
if ( sf->familyname!=NULL ) ++cnt;
if ( sf->fullname!=NULL ) ++cnt;
if ( sf->copyright!=NULL ) ++cnt;
if ( sf->weight!=NULL ) ++cnt;
if ( sf->pfminfo.fstype!=-1 ) ++cnt;
if ( sf->subfontcnt==0 ) {
if ( sf->version!=NULL ) ++cnt;
++cnt; /* ItalicAngle */
++cnt; /* isfixedpitch */
if ( sf->upos!=0 ) ++cnt;
if ( sf->uwidth!=0 ) ++cnt;
/* Fontographer also generates em, ascent and descent */
/* em is redundant, we can get that from the fontmatrix */
/* given em we only need one of ascent or descent */
/* On the off chance that fontlab objects to them let's not generate them */
if ( sf->ascent != 8*(sf->ascent+sf->descent)/10 )
++cnt; /* ascent */
}
if ( format==ff_mma || format==ff_mmb )
cnt += 3;
dumpf(dumpchar,data,"/FontInfo %d dict dup begin\n", cnt );
if ( sf->subfontcnt==0 && sf->version )
dumpf(dumpchar,data," /version (%s) readonly def\n", sf->version );
if ( sf->copyright ) {
dumpf(dumpchar,data," /Notice (");
dumpcarefully(dumpchar,data,sf->copyright);
dumpf(dumpchar,data,") readonly def\n" );
if ( strchr(sf->copyright,'\n')!=NULL || strchr(sf->copyright,'\r')!=NULL )
dumpascomments(dumpchar,data,sf->copyright);
}
if ( sf->fullname ) {
dumpf(dumpchar,data," /FullName (" );
dumpcarefully(dumpchar,data,sf->fullname);
dumpf(dumpchar,data,") readonly def\n" );
}
if ( sf->familyname ) {
dumpf(dumpchar,data," /FamilyName (" );
dumpcarefully(dumpchar,data,sf->familyname);
dumpf(dumpchar,data,") readonly def\n" );
}
if ( sf->weight )
dumpf(dumpchar,data," /Weight (%s) readonly def\n", sf->weight );
if ( sf->pfminfo.fstype!=-1 )
dumpf(dumpchar,data," /FSType %d def\n", sf->pfminfo.fstype );
if ( sf->subfontcnt==0 ) {
dumpf(dumpchar,data," /ItalicAngle %g def\n", (double) sf->italicangle );
dumpf(dumpchar,data," /isFixedPitch %s def\n", SFOneWidth(sf)!=-1?"true":"false" );
if ( format==ff_type42 || format==ff_type42cid ) {
if ( sf->upos )
dumpf(dumpchar,data," /UnderlinePosition %g def\n", (double) (sf->upos/(sf->ascent+sf->descent)) );
if ( sf->uwidth )
dumpf(dumpchar,data," /UnderlineThickness %g def\n", (double) (sf->uwidth/(sf->ascent+sf->descent)) );
} else {
if ( sf->upos )
dumpf(dumpchar,data," /UnderlinePosition %g def\n", (double) sf->upos );
if ( sf->uwidth )
dumpf(dumpchar,data," /UnderlineThickness %g def\n", (double) sf->uwidth );
}
if ( sf->ascent != 8*(sf->ascent+sf->descent)/10 )
dumpf(dumpchar,data," /ascent %d def\n", sf->ascent );
}
if ( format==ff_mma || format==ff_mmb ) {
MMSet *mm = sf->mm;
int j,k;
dumpstr(dumpchar,data," /BlendDesignPositions [" );
for ( j=0; j<mm->instance_count; ++j ) {
dumpstr(dumpchar,data," [" );
for ( k=0; k<mm->axis_count; ++k )
dumpf(dumpchar,data,"%g ", (double) mm->positions[j*mm->axis_count+k]);
dumpstr(dumpchar,data,"]" );
}
dumpstr(dumpchar,data," ] def\n" );
dumpstr(dumpchar,data," /BlendDesignMap [" );
for ( k=0; k<mm->axis_count; ++k ) {
dumpstr(dumpchar,data," [" );
for ( j=0; j<mm->axismaps[k].points; ++j )
dumpf(dumpchar,data,"[%g %g] ",
(double) mm->axismaps[k].designs[j], (double) mm->axismaps[k].blends[j]);
dumpstr(dumpchar,data,"]" );
}
dumpstr(dumpchar,data," ] def\n" );
dumpstr(dumpchar,data," /BlendAxisTypes [" );
for ( k=0; k<mm->axis_count; ++k )
dumpf(dumpchar,data,"/%s ", mm->axes[k]);
dumpstr(dumpchar,data," ] def\n" );
}
dumpstr(dumpchar,data,"end readonly def\n");
}
static void dumpfontcomments(void (*dumpchar)(int ch,void *data), void *data,
SplineFont *sf, int format ) {
time_t now;
const char *author = GetAuthor();
time(&now);
/* Werner points out that the DSC Version comment has a very specific */
/* syntax. We can't just put in a random string, must be <real> <int> */
/* So we can sort of do that for CID fonts (give it a <revsion> of 0 */
/* but for type1s just use a comment rather than a DSC statement */
if (( format==ff_cid || format==ff_cffcid || format==ff_type42cid ) &&
sf->cidregistry!=NULL ) {
dumpf(dumpchar,data, "%%%%Title: (%s %s %s %d)\n",
sf->fontname, sf->cidregistry, sf->ordering, sf->supplement );
dumpf(dumpchar,data, "%%%%Version: %g 0\n", (double)sf->cidversion );
/* CID keyed fonts are language level 3 */
} else {
dumpf(dumpchar,data,"%%%%Title: %s\n", sf->fontname);
dumpf(dumpchar,data,"%%Version: %s\n", sf->version);
}
dumpf(dumpchar,data,"%%%%CreationDate: %s", ctime(&now));
if ( author!=NULL )
dumpf(dumpchar,data,"%%%%Creator: %s\n", author);
if ( format==ff_cid || format==ff_cffcid || format==ff_type42cid ||
format==ff_cff || format==ff_type42 )
dumpf(dumpchar,data, "%%%%LanguageLevel: 3\n" );
else if ( sf->multilayer && format==ff_ptype3 ) {
int gid, ly;
SplineChar *sc;
int had_pat=0, had_grad=0;
for ( gid=0; gid<sf->glyphcnt; ++gid ) if ( (sc=sf->glyphs[gid])!=NULL ) {
for ( ly=ly_fore; ly<sc->layer_cnt; ++ly ) {
if ( sc->layers[ly].fill_brush.gradient!=NULL || sc->layers[ly].stroke_pen.brush.gradient!=NULL ) {
had_grad = true;
break;
}
if ( sc->layers[ly].fill_brush.gradient!=NULL || sc->layers[ly].stroke_pen.brush.gradient!=NULL )
had_pat = true;
}
}
if ( had_grad )
dumpf(dumpchar,data, "%%%%LanguageLevel: 3\n" );
else if ( had_pat )
dumpf(dumpchar,data, "%%%%LanguageLevel: 2\n" );
}
if ( sf->copyright!=NULL ) {
char *pt, *strt=sf->copyright, *npt;
while ( *strt!='\0' ) {
pt = strt;
while ( pt<strt+60 && *pt ) {
npt = strpbrk(pt,"\n\t\r ");
if ( npt==NULL ) npt = strt+strlen(strt);
if ( npt<strt+60 || pt==strt ) {
pt = npt;
if ( isspace(*pt)) {
++pt;
if ( pt[-1]=='\n' || pt[-1]=='\r' )
break;
}
} else
break;
}
dumpstr(dumpchar,data,strt==sf->copyright ? "%Copyright: ": "%Copyright: ");
dumpstrn(dumpchar,data,strt,*pt?pt-strt-1:pt-strt);
dumpchar('\n',data);
strt = pt;
}
}
if ( sf->comments!=NULL )
dumpascomments(dumpchar,data,sf->comments);
dumpf(dumpchar,data,"%% Generated by FontForge %d (http://fontforge.sf.net/)\n", FONTFORGE_VERSIONDATE_RAW);
dumpstr(dumpchar,data,"%%EndComments\n\n");
}
extern const char *mmfindfont[], *makeblendedfont[];
static void dumprequiredfontinfo(void (*dumpchar)(int ch,void *data), void *data,
SplineFont *sf, int format, EncMap *map, SplineFont *fullsf, int layer ) {
int cnt, i;
double fm[6];
const char (*encoding[256]);
DBounds b;
int uniqueid;
char buffer[50];
dumpf(dumpchar,data,"%%!PS-AdobeFont-1.0: %s %s\n", sf->fontname, sf->version?sf->version:"" );
if ( format==ff_ptype0 && (map->enc->is_unicodebmp || map->enc->is_unicodefull))
dumpf(dumpchar,data,"%%%%DocumentNeededResources: font ZapfDingbats\n" );
/* dumpf(dumpchar,data, "%%%%DocumentSuppliedResources: font %s\n", sf->fontname );*/
/* Werner says the above is not appropriate */
dumpfontcomments(dumpchar,data,sf,format);
cnt = 0;
++cnt; /* FID, added by definefont and not by us */
++cnt; /* fonttype */
++cnt; /* fontmatrix */
if ( sf->fontname!=NULL ) ++cnt;
++cnt; /* fontinfo */
++cnt; /* encoding */
++cnt; /* fontbb */
if ( sf->uniqueid!=-1 && sf->use_uniqueid ) ++cnt;
++cnt; /* painttype */
if ( sf->strokedfont )
++cnt; /* StrokeWidth */
if ( format==ff_ptype3 ) {
++cnt; /* charprocs */
++cnt; /* buildglyph */
++cnt; /* buildchar */
} else {
++cnt; /* private */
++cnt; /* chars */
}
if ( sf->xuid!=NULL && sf->use_xuid ) ++cnt;
if ( format==ff_mma || format==ff_mmb )
cnt += 7;
if ( sf->uniqueid==0 )
uniqueid = 4000000 + (rand()&0x3ffff);
else
uniqueid = sf->uniqueid ;
sf->tempuniqueid = uniqueid;
if ( format!=ff_ptype3 && uniqueid!=-1 && sf->use_uniqueid ) {
dumpf(dumpchar,data,"FontDirectory/%s known{/%s findfont dup/UniqueID known{dup\n", sf->fontname, sf->fontname);
dumpf(dumpchar,data,"/UniqueID get %d eq exch/FontType get 1 eq and}{pop false}ifelse\n", uniqueid );
dumpf(dumpchar,data,"{save true}{false}ifelse}{false}ifelse\n" );
}
dumpf(dumpchar,data,"%d dict begin\n", cnt );
dumpf(dumpchar,data,"/FontType %d def\n", format==ff_ptype3?3:1 );
fm[0] = fm[3] = 1.0/((sf->ascent+sf->descent));
fm[1] = fm[2] = fm[4] = fm[5] = 0;
dumpdblarray(dumpchar,data,"FontMatrix",fm,6,"readonly ",false);
if ( sf->fontname!=NULL )
dumpf(dumpchar,data,"/FontName /%s def\n", sf->fontname );
SplineFontLayerFindBounds(fullsf==NULL?sf:fullsf,layer,&b);
fm[0] = floor( b.minx);
fm[1] = floor( b.miny);
fm[2] = ceil( b.maxx);
fm[3] = ceil( b.maxy);
dumpdblarray(dumpchar,data,"FontBBox",fm,4,"readonly ", true);
if ( uniqueid!=-1 && sf->use_uniqueid )
dumpf(dumpchar,data,"/UniqueID %d def\n", uniqueid );
if ( sf->xuid!=NULL && sf->use_xuid ) {
dumpf(dumpchar,data,"/XUID %s def\n", sf->xuid );
if ( sf->changed_since_xuidchanged )
SFIncrementXUID(sf);
}
dumpf(dumpchar,data,"/PaintType %d def\n", sf->strokedfont?2:0 );
if ( sf->strokedfont )
dumpf(dumpchar,data,"/StrokeWidth %g def\n", (double) sf->strokewidth );
dumpfontinfo(dumpchar,data,sf,format);
if ( format==ff_mma || format==ff_mmb ) {
MMSet *mm = sf->mm;
int j,k;
DBounds mb[16];
dumpstr(dumpchar,data," /WeightVector [" );
for ( j=0; j<mm->instance_count; ++j ) {
dumpf(dumpchar,data,"%g ", (double) mm->defweights[j]);
}
dumpstr(dumpchar,data," ] def\n" );
dumpstr(dumpchar,data," /$Blend {" );
if ( mm->instance_count==2 )
dumpf(dumpchar,data,"%g mul add", (double) mm->defweights[1]);
else {
dumpf(dumpchar,data,"%g mul exch", (double) mm->defweights[1]);
for ( j=2; j<mm->instance_count-1; ++j )
dumpf(dumpchar,data,"%g mul add exch", (double) mm->defweights[j]);
dumpf(dumpchar,data,"%g mul add add", (double) mm->defweights[j]);
}
dumpstr(dumpchar,data," } bind def\n" );
dumpstr(dumpchar,data," /Blend 3 dict dup begin\n" );
for ( j=0; j<mm->instance_count; ++j )
SplineFontLayerFindBounds(mm->instances[j],layer,&mb[j]);
dumpstr(dumpchar,data," /FontBBox{" );
for ( k=0; k<4; ++k ) {
dumpstr(dumpchar,data,"{" );
for ( j=0; j<mm->instance_count; ++j )
dumpf(dumpchar,data,"%g ", k==0 ? floor(mb[j].minx) :
k==1 ? floor(mb[j].miny) :
k==2 ? ceil(mb[j].maxx) :
ceil(mb[j].maxy));
dumpstr(dumpchar,data,"}" );
}
dumpstr(dumpchar,data,"} def\n" );
dumpf(dumpchar,data," /Private %d dict def\n", sf->private->next+10 );
dumpstr(dumpchar,data," end def %End of Blend dict\n" );
for ( j=0; makeblendedfont[j]!=NULL; ++j ) {
dumpstr(dumpchar,data,makeblendedfont[j]);
(dumpchar)('\n',data);
}
dumpstr(dumpchar,data,"\n /NormalizeDesignVector\n" );
dumpstr(dumpchar,data, mm->ndv );
dumpstr(dumpchar,data," bind def\n" );
dumpstr(dumpchar,data," /ConvertDesignVector\n" );
dumpstr(dumpchar,data, mm->cdv );
dumpstr(dumpchar,data," bind def\n\n" );
for ( j=0; mmfindfont[j]!=NULL; ++j ) {
dumpstr(dumpchar,data,mmfindfont[j]);
(dumpchar)('\n',data);
}
}
for ( i=0; i<256 && i<map->enccount; ++i )
if ( map->map[i]!=-1 && SCWorthOutputting(sf->glyphs[map->map[i]]) )
encoding[i] = sf->glyphs[map->map[i]]->name;
else
encoding[i] = ".notdef";
for ( ; i<256; ++i )
encoding[i] = ".notdef";
if ( isStdEncoding(encoding) )
dumpstr(dumpchar,data,"/Encoding StandardEncoding def\n");
else {
dumpstr(dumpchar,data,"/Encoding 256 array\n" );
/* older versions of dvipdfm assume the following line is present.*/
/* Perhaps others do too? */
dumpstr(dumpchar,data," 0 1 255 { 1 index exch /.notdef put} for\n" );
for ( i=0; i<256; ++i )
if ( strcmp(encoding[i],".notdef")!=0 )
dumpf(dumpchar,data,"dup %d/%s put\n", i, encoding[i] );
dumpstr(dumpchar,data,"readonly def\n" );
}
if ( format==ff_ptype3 ) {
dumpstr(dumpchar,data,"/BuildChar { 1 index /Encoding get exch get 1 index /BuildGlyph get exec } bind def\n" );
dumpstr(dumpchar,data,"% I call all my CharProcs with two arguments, the top of the stack will be\n" );
dumpstr(dumpchar,data,"% 0 and then next thing is the fontdict. If the tos is zero the char will\n" );
dumpstr(dumpchar,data,"% do a setcachedevice, otherwise (for referenced chars) it will not. The\n" );
dumpstr(dumpchar,data,"% fontdict argument is so a char can invoke a referenced char. BuildGlyph\n" );
dumpstr(dumpchar,data,"% itself will remove the arguments from the stack, the CharProc will leave 'em\n" );
if ( sf->multilayer )
*buffer = '\0';
else if ( sf->strokedfont )
sprintf( buffer, "%g setlinewidth stroke", (double) sf->strokewidth );
else
strcpy(buffer, "fill");
dumpf(dumpchar,data,"/BuildGlyph { 2 copy exch /CharProcs get exch 2 copy known not { pop /.notdef} if get exch pop 0 exch exec pop pop %s} bind def\n",
buffer );
}
}
static void dumpinitialascii(void (*dumpchar)(int ch,void *data), void *data,
SplineFont *sf, int format, EncMap *map, SplineFont *fullsf, int layer ) {
dumprequiredfontinfo(dumpchar,data,sf,format,map,fullsf,layer);
dumpstr(dumpchar,data,"currentdict end\ncurrentfile eexec\n" );
}
static void dumpencodedstuff(void (*dumpchar)(int ch,void *data), void *data,
SplineFont *sf, int format, int flags, EncMap *map, int layer ) {
struct fileencryptdata fed;
void (*func)(int ch,void *data);
func = startfileencoding(dumpchar,data,&fed,format==ff_pfb || format==ff_mmb);
dumpprivatestuff(func,&fed,sf,NULL,flags,format,map,layer);
if ( format==ff_ptype0 ) {
dumpstr(func,&fed, "/" );
dumpstr(func,&fed, sf->fontname );
dumpstr(func,&fed,"Base exch definefont pop\n mark currentfile closefile\n" );
} else
dumpstr(func,&fed,"dup/FontName get exch definefont pop\n mark currentfile closefile\n" );
}
static void dumpfinalascii(void (*dumpchar)(int ch,void *data), void *data,
SplineFont *sf, int format) {
int i;
int uniqueid = sf->uniqueid ;
/* output 512 zeros */
dumpchar('\n',data);
for ( i = 0; i<8; ++i )
dumpstr(dumpchar,data,"0000000000000000000000000000000000000000000000000000000000000000\n");
dumpstr(dumpchar,data,"cleartomark\n");
if ( format!=ff_ptype3 && uniqueid!=-1 && sf->use_uniqueid )
dumpstr(dumpchar,data,"{restore}if\n");
}
static void mkheadercopyfile(FILE *temp,FILE *out,int headertype) {
char buffer[8*1024];
int len;
/* output the file header */
putc('\200',out);
putc(headertype,out);
len = ftell(temp); /* output byte count */
putc(len&0xff,out);
putc(((len>>8)&0xff),out);
putc(((len>>16)&0xff),out);
putc(((len>>24)&0xff),out);
fseek(temp,0,SEEK_SET);
while ((len=fread(buffer,sizeof(char),sizeof(buffer),temp))>0 )
fwrite(buffer,sizeof(char),len,out);
fclose(temp); /* deletes the temporary file */
}
static void dumptype42(FILE *out, SplineFont *sf, int format, int flags,
EncMap *map,int layer) {
double fm[6];
DBounds b;
int uniqueid;
int i,cnt,gid,hasnotdef;
SplineFont *cidmaster;
cidmaster = sf->cidmaster;
if ( sf->subfontcnt!=0 ) {
cidmaster = sf;
sf = sf->subfonts[0];
}
if ( format==ff_type42 )
cidmaster = NULL;
if ( format == ff_type42cid ) {
fprintf( out, "%%!PS-Adobe-3.0 Resource-CIDFont\n" );
fprintf( out, "%%%%DocumentNeededResources: ProcSet (CIDInit)\n" );
fprintf( out, "%%%%IncludeResource: ProcSet (CIDInit)\n" );
fprintf( out, "%%%%BeginResource: CIDFont %s\n", (cidmaster!=NULL?cidmaster:sf)->fontname );
} else
fprintf( out, "%%!PS-TrueTypeFont\n" ); /* Ignore the ttf version info */
/* Too hard to do right, and if done right doesn't mean much to */
/* a human observer */
dumpfontcomments((DumpChar)fputc,out,cidmaster!=NULL?cidmaster:sf,format);
if ( format == ff_type42cid ) {
fprintf( out, "/CIDInit /ProcSet findresource begin\n\n" );
fprintf( out, "16 dict begin\n" );
} else
fprintf( out, "12 dict begin\n" );
fprintf( out, " /FontName /%s def\n", sf->fontname );
if ( format==ff_type42cid ) {
fprintf( out, " /CIDFontType 2 def\n" );
/* Adobe Technical Note 5012.Type42_Spec.pdf Sec 5 CIDFontType 2 CID Fonts */
/* page 12 says that the FontType (not CIDFontType) of a TrueType CID font */
/* should be 42. The PLRM (v3) Chap 5 page 370 says the FontType should */
/* 11. Ain't that just grand? */
fprintf( out, " /FontType 11 def\n" );
fprintf( out, " /CIDFontName /%s def\n", sf->fontname );
/* 5012 doesn't mention this, but we probably need these... */
fprintf( out, " /CIDSystemInfo 3 dict dup begin\n" );
if ( cidmaster!=NULL ) {
fprintf( out, " /Registry (%s) def\n", cidmaster->cidregistry );
fprintf( out, " /Ordering (%s) def\n", cidmaster->ordering );
fprintf( out, " /Supplement %d def\n", cidmaster->supplement );
} else {
fprintf( out, " /Registry (Adobe) def\n" );
fprintf( out, " /Ordering (Identity) def\n" );
fprintf( out, " /Supplement 0 def\n" );
}
fprintf( out, " end def\n\n" );
} else
fprintf( out, " /FontType 42 def\n" );
fprintf( out, " /FontMatrix [1 0 0 1 0 0] def\n" );
fprintf( out, " /PaintType 0 def\n" );
SplineFontLayerFindBounds(sf,layer,&b);
/* The Type42 spec says that the bounding box should be scaled by the */
/* 1/emsize (to be numbers near 1.0). The example in the spec does not do */
/* this and gives numbers you'd expect to see in a type1 font */
fm[0] = b.minx / (sf->ascent+sf->descent);
fm[1] = b.miny / (sf->ascent+sf->descent);
fm[2] = b.maxx / (sf->ascent+sf->descent);
fm[3] = b.maxy / (sf->ascent+sf->descent);
fprintf( out, " ");
dumpdblarray((DumpChar) fputc,out,"FontBBox",fm,4,"readonly ", true);
dumpfontinfo((DumpChar) fputc,out,sf,format);
if ( sf->uniqueid==0 )
uniqueid = 4000000 + (rand()&0x3ffff);
else
uniqueid = sf->uniqueid ;
sf->tempuniqueid = uniqueid;
if ( uniqueid!=-1 && sf->use_uniqueid )
fprintf( out, " /UniqueID %d def\n", uniqueid );
if ( sf->xuid!=NULL && sf->use_xuid ) {
fprintf(out," /XUID %s def\n", sf->xuid );
if ( sf->changed_since_xuidchanged )
SFIncrementXUID(sf);
}
if ( format==ff_type42 ) {
fprintf(out," /Encoding 256 array\n" );
/* older versions of dvipdfm assume the following line is present.*/
/* Perhaps others do too? */
fprintf(out," 0 1 255 { 1 index exch /.notdef put} for\n" );
for ( i=0; i<256 && i<map->enccount; ++i ) if ( (gid = map->map[i])!=-1 )
if ( SCWorthOutputting(sf->glyphs[gid]) )
fprintf( out, " dup %d/%s put\n", i, sf->glyphs[gid]->name);
fprintf( out, " readonly def\n" );
}
fprintf( out, " /sfnts [\n" );
_WriteType42SFNTS(out,sf,format,flags,map,layer);
fprintf( out, " ] def\n" );
if ( format==ff_type42 ) {
hasnotdef = false;
for ( i=cnt=0; i<sf->glyphcnt; ++i ) {
if ( sf->glyphs[i]!=NULL && SCWorthOutputting(sf->glyphs[i])) {
++cnt;
if ( strcmp(sf->glyphs[i]->name,".notdef")==0 )
hasnotdef = true;
}
}
fprintf( out, " /CharStrings %d dict dup begin\n", cnt+1 );
/* Why check to see if there's a notdef char in the font? If there is */
/* we can define the dictionary entry twice */
/* We can, yes, but FreeType gets confused if we do. So let's check */
if ( !hasnotdef )
fprintf( out, " /.notdef 0 def\n" );
for ( i=0; i<sf->glyphcnt; ++i )
if ( sf->glyphs[i]!=NULL && SCWorthOutputting(sf->glyphs[i]))
fprintf( out, " /%s %d def\n", sf->glyphs[i]->name, sf->glyphs[i]->ttf_glyph );
fprintf( out, " end readonly def\n" );
fprintf( out, "FontName currentdict end definefont pop\n" );
} else {
if ( cidmaster!=NULL ) {
for ( i=cnt=0; i<sf->glyphcnt; ++i )
if ( sf->glyphs[i]!=NULL && SCWorthOutputting(sf->glyphs[i]))
++cnt;
fprintf( out, " /CIDMap %d dict dup begin\n", cnt );
for ( i=0; i<sf->glyphcnt; ++i )
if ( sf->glyphs[i]!=NULL && SCWorthOutputting(sf->glyphs[i]))
fprintf( out, " %d %d def\n", i, sf->glyphs[i]->ttf_glyph );
fprintf( out, " end readonly def\n" );
fprintf( out, " /CIDCount %d def\n", sf->glyphcnt );
fprintf( out, " /GDBytes %d def\n", sf->glyphcnt>65535?3:2 );
} else if ( flags & ps_flag_identitycidmap ) {
for ( i=cnt=0; i<sf->glyphcnt; ++i )
if ( sf->glyphs[i]!=NULL && cnt<sf->glyphs[i]->ttf_glyph )
cnt = sf->glyphs[i]->ttf_glyph;
fprintf( out, " /CIDCount %d def\n", cnt+1 );
fprintf( out, " /GDBytes %d def\n", cnt+1>65535?3:2 );
fprintf( out, " /CIDMap 0 def\n" );
} else { /* Use unicode */
int maxu = 0;
for ( i=cnt=0; i<sf->glyphcnt; ++i )
if ( sf->glyphs[i]!=NULL && SCWorthOutputting(sf->glyphs[i]) &&
sf->glyphs[i]->unicodeenc!=-1 && sf->glyphs[i]->unicodeenc<0x10000 ) {
++cnt;
if ( sf->glyphs[i]->unicodeenc > maxu )
maxu = sf->glyphs[i]->unicodeenc;
}
fprintf( out, " /CIDMap %d dict dup begin\n", cnt );
fprintf( out, " 0 0 def\n" ); /* .notdef doesn't have a unicode enc, will be missed. Needed */
if ( map->enc->is_unicodebmp || map->enc->is_unicodefull ) {
for ( i=0; i<map->enccount && i<0x10000; ++i ) if ( (gid = map->map[i])!=-1 ) {
if ( SCWorthOutputting(sf->glyphs[gid]))
fprintf( out, " %d %d def\n", i, sf->glyphs[gid]->ttf_glyph );
}
} else {
for ( i=0; i<sf->glyphcnt; ++i )
if ( sf->glyphs[i]!=NULL && SCWorthOutputting(sf->glyphs[i]) &&
sf->glyphs[i]->unicodeenc!=-1 && sf->glyphs[i]->unicodeenc<0x10000 )
fprintf( out, " %d %d def\n", sf->glyphs[i]->unicodeenc, sf->glyphs[i]->ttf_glyph );
}
fprintf( out, " end readonly def\n" );
fprintf( out, " /GDBytes %d def\n", maxu>65535?3:2 );
fprintf( out, " /CIDCount %d def\n", maxu+1 );
}
fprintf( out, "currentdict end dup /CIDFontName get exch /CIDFont defineresource pop\nend\n" );
fprintf( out, "%%%%EndResource\n" );
fprintf( out, "%%%%EOF\n" );
}
}
static void dumpfontdict(FILE *out, SplineFont *sf, int format, int flags,
EncMap *map, SplineFont *fullsf, int layer ) {
/* a pfb header consists of 6 bytes, the first is 0200, the second is a */
/* binary/ascii flag where 1=>ascii, 2=>binary, 3=>eof??, the next four */
/* are a count of bytes between this header and the next one. First byte */
/* is least significant */
if ( format==ff_pfb || format==ff_mmb ) {
FILE *temp;
temp = tmpfile();
dumpinitialascii((DumpChar) fputc,temp,sf,format,map,fullsf,layer );
mkheadercopyfile(temp,out,1);
temp = tmpfile();
dumpencodedstuff((DumpChar) fputc,temp,sf,format,flags,map,layer);
mkheadercopyfile(temp,out,2);
temp = tmpfile();
dumpfinalascii((DumpChar) fputc,temp,sf,format);
mkheadercopyfile(temp,out,1);
/* final header, 3=>eof??? */
dumpstrn((DumpChar) fputc,out,"\200\003",2);
} else if ( format==ff_ptype3 ) {
dumprequiredfontinfo((DumpChar) fputc,out,sf,ff_ptype3,map,NULL,layer);
dumpcharprocs((DumpChar) fputc,out,sf);
} else if ( format==ff_type42 || format==ff_type42cid ) {
dumptype42(out,sf,format,flags,map,layer);
} else {
dumpinitialascii((DumpChar) (fputc),out,sf,format,map,fullsf,layer );
dumpencodedstuff((DumpChar) (fputc),out,sf,format,flags,map,layer);
dumpfinalascii((DumpChar) (fputc),out,sf,format);
}
}
static void dumpreencodeproc(FILE *out) {
fprintf( out, "\n/reencodedict 10 dict def\n" );
fprintf( out, "/ReEncode\n" );
fprintf( out, " { reencodedict begin\n" );
fprintf( out, "\t/newencoding exch def\n" );
fprintf( out, "\t/newfontname exch def\n" );
fprintf( out, "\tfindfont /basefontdict exch def\n" );
fprintf( out, "\t/newfont basefontdict maxlength dict def\n" );
fprintf( out, "\tbasefontdict\n" );
fprintf( out, "\t { exch dup dup /FID ne exch /Encoding ne and\n" );
fprintf( out, "\t\t{ exch newfont 3 1 roll put }\n" );
fprintf( out, "\t\t{ pop pop }\n" );
fprintf( out, "\t\tifelse\n" );
fprintf( out, "\t } forall\n" );
fprintf( out, "\tnewfont /FontName newfontname put\n" );
fprintf( out, "\tnewfont /Encoding newencoding put\n" );
fprintf( out, "\tnewfontname newfont definefont pop\n" );
fprintf( out, "\tend\n" );
fprintf( out, " } def\n" );
fprintf( out, "\n" );
}
static const char *dumpnotdefenc(FILE *out,SplineFont *sf) {
const char *notdefname;
int i;
/* At one point I thought the unicode replacement char 0xfffd */
/* was a good replacement for notdef if the font contained */
/* no notdef. Probably not a good idea for a PS font */
notdefname = ".notdef";
fprintf( out, "/%sBase /%sNotDef [\n", sf->fontname, sf->fontname );
for ( i=0; i<256; ++i )
fprintf( out, " /%s\n", notdefname );
fprintf( out, "] ReEncode\n\n" );
return( notdefname );
}
static int somecharsused(SplineFont *sf, int bottom, int top, EncMap *map) {
int i;
for ( i=bottom; i<=top && i<map->enccount; ++i ) {
if ( map->map[i]!=-1 && SCWorthOutputting(sf->glyphs[map->map[i]]) )
return( true );
}
return( false );
}
extern char *zapfnomen[];
extern int8 zapfexists[];
static void dumptype0stuff(FILE *out,SplineFont *sf, EncMap *map) {
const char *notdefname;
int i,j;
dumpreencodeproc(out);
notdefname = dumpnotdefenc(out,sf);
for ( i=1; i<256; ++i ) {
if ( somecharsused(sf,i<<8, (i<<8)+0xff, map)) {
fprintf( out, "/%sBase /%s%d [\n", sf->fontname, sf->fontname, i );
for ( j=0; j<256 && (i<<8)+j<map->enccount; ++j )
if ( map->map[(i<<8)+j]!=-1 && SCWorthOutputting(sf->glyphs[map->map[(i<<8)+j]]) )
fprintf( out, " /%s\n", sf->glyphs[map->map[(i<<8)+j]]->name );
else
fprintf( out, "/%s\n", notdefname );
for ( ; j<256; ++j )
fprintf( out, " /%s\n", notdefname );
fprintf( out, "] ReEncode\n\n" );
} else if ( i==0x27 && (map->enc->is_unicodebmp || map->enc->is_unicodefull)) {
fprintf( out, "%% Add Zapf Dingbats to unicode font at 0x2700\n" );
fprintf( out, "%% But only if on the printer, else use notdef\n" );
fprintf( out, "%% gv, which has no Zapf, maps courier to the name\n" );
fprintf( out, "%% so we must check a bit more than is it null or not...\n" );
/* gv with no ZapfDingbats installed does weird stuff. */
/* If I do "/ZapfDingbats findfont" then it returns "/Courier findfont" the */
/* first time, but the second time it returns null */
/* So even if the printer thinks it's got Zapf we must check to make sure it's*/
/* the real Zapf. We do that by examining the name. If it's ZapfDingbats all*/
/* should be well, if it's Courier, then that counts as non-existant */
fprintf( out, "/ZapfDingbats findfont pop\n" );
fprintf( out, "/ZapfDingbats findfont null eq\n" );
fprintf( out, "{ true }\n" );
fprintf( out, " { /ZapfDingbats findfont /FontName get (ZapfDingbats) ne }\n" );
fprintf( out, " ifelse\n" );
fprintf( out, "{ /%s%d /%sNotDef findfont definefont pop }\n",
sf->fontname, i, sf->fontname);
fprintf( out, " { /ZapfDingbats /%s%d [\n", sf->fontname, i );
for ( j=0; j<0xc0; ++j )
fprintf( out, " /%s\n",
zapfexists[j]?zapfnomen[j]:".notdef" );
for ( ;j<256; ++j )
fprintf( out, " /%s\n", ".notdef" );
fprintf( out, "] ReEncode\n\n" );
fprintf( out, " } ifelse\n\n" );
}
}
fprintf( out, "/%s 21 dict dup begin\n", sf->fontname );
fprintf( out, "/FontInfo /%sBase findfont /FontInfo get def\n", sf->fontname );
fprintf( out, "/PaintType %d def\n", sf->strokedfont?2:0 );
if ( sf->strokedfont )
fprintf( out, "/StrokeWidth %g def\n", (double) sf->strokewidth );
fprintf( out, "/FontType 0 def\n" );
fprintf( out, "/LanguageLevel 2 def\n" );
fprintf( out, "/FontMatrix [1 0 0 1 0 0] readonly def\n" );
fprintf( out, "/FMapType 2 def\n" );
fprintf( out, "/Encoding [\n" );
for ( i=0; i<256; ++i )
fprintf( out, " %d\n", i );
fprintf( out, "] readonly def\n" );
fprintf( out, "/FDepVector [\n" );
fprintf( out, " /%sBase findfont\n", sf->fontname );
for ( i=1; i<256; ++i )
if ( somecharsused(sf,i<<8, (i<<8)+0xff, map) ||
(i==0x27 && (map->enc->is_unicodebmp || map->enc->is_unicodefull)) )
fprintf( out, " /%s%d findfont\n", sf->fontname, i );
else
fprintf( out, " /%sNotDef findfont\n", sf->fontname );
fprintf( out, " ] readonly def\n" );
fprintf( out, "end definefont pop\n" );
fprintf( out, "%%%%EOF\n" );
}
static void dumpt1str(FILE *binary,uint8 *data, int len, int leniv) {
if ( leniv==-1 )
fwrite(data,sizeof(uint8),len,binary);
else
encodestrout((DumpChar) fputc,binary,data,len,leniv);
}
static void dump_index(FILE *binary,int size,int val) {
if ( size>=4 )
putc((val>>24)&0xff,binary);
if ( size>=3 )
putc((val>>16)&0xff,binary);
if ( size>=2 )
putc((val>>8)&0xff,binary);
if ( size>=1 ) /* Yes, size may be 0 for the fd index */
putc((val)&0xff,binary);
}
static FILE *gencidbinarydata(SplineFont *cidmaster,struct cidbytes *cidbytes,
int flags,EncMap *map,int layer) {
int i,j, leniv, subrtot;
SplineFont *sf;
struct fddata *fd;
FILE *chrs, *subrs, *binary;
char *buffer;
long offset;
char *pt;
struct pschars *chars;
int len;
memset(cidbytes,'\0',sizeof(struct cidbytes));
cidbytes->fdcnt = cidmaster->subfontcnt;
cidbytes->fds = calloc(cidbytes->fdcnt,sizeof(struct fddata));
for ( i=0; i<cidbytes->fdcnt; ++i ) {
sf = cidmaster->subfonts[i];
fd = &cidbytes->fds[i];
fd->flexmax = SplineFontIsFlexible(sf,layer,flags);
fd->subrs = initsubrs(NULL);
if ( fd->subrs==NULL ) {
int j;
for ( j=0; j<i; ++j )
PSCharsFree(cidbytes->fds[j].subrs);
free( cidbytes->fds );
return( NULL );
}
fd->iscjk = SFIsCJK(sf,map);
pt = PSDictHasEntry(sf->private,"lenIV");
if ( pt!=NULL )
fd->leniv = strtol(pt,NULL,10);
else
fd->leniv = 4;
}
ff_progress_change_line1(_("Converting PostScript"));
if ( (chars = CID2ChrsSubrs(cidmaster,cidbytes,flags,layer))==NULL )
return( NULL );
ff_progress_next_stage();
ff_progress_change_line1(_("Saving PostScript Font"));
chrs = tmpfile();
for ( i=0; i<chars->next; ++i ) {
if ( chars->lens[i]!=0 ) {
leniv = cidbytes->fds[cidbytes->fdind[i]].leniv;
dumpt1str(chrs,chars->values[i],chars->lens[i],leniv);
if ( !ff_progress_next()) {
PSCharsFree(chars);
fclose(chrs);
return( NULL );
}
if ( leniv>0 )
chars->lens[i] += leniv;
}
}
subrs = tmpfile(); subrtot = 0;
for ( i=0; i<cidbytes->fdcnt; ++i ) {
fd = &cidbytes->fds[i];
leniv = fd->leniv;
for ( j=0; j<fd->subrs->next; ++j ) {
dumpt1str(subrs,fd->subrs->values[j],fd->subrs->lens[j],leniv);
if ( leniv>0 )
fd->subrs->lens[j] += leniv;
}
fd->subrcnt = j;
subrtot += j;
}
cidbytes->fdbytes = ( cidbytes->fdcnt==1 )? 0 :
( cidbytes->fdcnt<256 )? 1 : 2;
if ( (cidbytes->cidcnt+1)*(cidbytes->fdbytes+3) + /* size of the CID map region */
(subrtot+1) * 3 + /* size of the Subr map region */
ftell(subrs) + /* size of the subr region */
ftell(chrs) < 0x1000000 ) /* size of the charstring region */ /* Are all our offsets less than 3 bytes? */
cidbytes->gdbytes = 3; /* Adobe's convention is to use 3, so don't bother checking for anything less */
else
cidbytes->gdbytes = 4; /* but I suppose we might need more... */
cidbytes->errors = ferror(chrs) || ferror(subrs);
offset = (cidbytes->cidcnt+1)*(cidbytes->fdbytes+cidbytes->gdbytes) +
(subrtot+1) * cidbytes->gdbytes + ftell(subrs);
binary = tmpfile();
for ( i=0; i<cidbytes->cidcnt; ++i ) {
dump_index(binary,cidbytes->fdbytes,cidbytes->fdind[i]);
dump_index(binary,cidbytes->gdbytes,offset);
offset += chars->lens[i];
}
dump_index(binary,cidbytes->fdbytes,-1); /* Adobe says undefined */
dump_index(binary,cidbytes->gdbytes,offset);
if ( ftell(binary) != (cidbytes->cidcnt+1)*(cidbytes->fdbytes+cidbytes->gdbytes))
IError("CIDMap section the wrong length" );
offset = (cidbytes->cidcnt+1)*(cidbytes->fdbytes+cidbytes->gdbytes) +
(subrtot+1) * cidbytes->gdbytes;
for ( i=0; i<cidbytes->fdcnt; ++i ) {
fd = &cidbytes->fds[i];
fd->subrmapoff = ftell(binary);
fd->sdbytes = cidbytes->gdbytes;
fd->subrcnt = fd->subrs->next;
for ( j=0; j<fd->subrcnt; ++j ) {
dump_index(binary,fd->sdbytes,offset);
offset += fd->subrs->lens[j];
}
PSCharsFree(fd->subrs);
}
dump_index(binary,cidbytes->gdbytes,offset);
if ( ftell(binary) != (cidbytes->cidcnt+1)*(cidbytes->fdbytes+cidbytes->gdbytes) +
(subrtot+1) * cidbytes->gdbytes )
IError("SubrMap section the wrong length" );
buffer = malloc(8192);
rewind(subrs);
while ( (len=fread(buffer,1,8192,subrs))>0 )
fwrite(buffer,1,len,binary);
fclose(subrs);
rewind(chrs);
while ( (len=fread(buffer,1,8192,chrs))>0 )
fwrite(buffer,1,len,binary);
fclose(chrs);
PSCharsFree(chars);
free( cidbytes->fdind ); cidbytes->fdind = NULL;
free( buffer );
cidbytes->errors |= ferror(binary);
return( binary );
}
static int dumpcidstuff(FILE *out,SplineFont *cidmaster,int flags,EncMap *map,int layer) {
int i;
DBounds res;
FILE *binary;
SplineFont *sf;
struct cidbytes cidbytes;
char buffer[4096];
long len;
fprintf( out, "%%!PS-Adobe-3.0 Resource-CIDFont\n" );
fprintf( out, "%%%%DocumentNeededResources: ProcSet (CIDInit)\n" );
/* fprintf( out, "%%%%DocumentSuppliedResources: CIDFont (%s)\n", cidmaster->fontname ); */
/* Werner says this is inappropriate */
fprintf( out, "%%%%IncludeResource: ProcSet (CIDInit)\n" );
fprintf( out, "%%%%BeginResource: CIDFont (%s)\n", cidmaster->fontname );
dumpfontcomments((DumpChar) fputc, out, cidmaster, ff_cid );
fprintf( out, "/CIDInit /ProcSet findresource begin\n\n" );
fprintf( out, "20 dict begin\n\n" );
fprintf( out, "/CIDFontName /%s def\n", cidmaster->fontname );
fprintf( out, "/CIDFontVersion %g def\n", (double)cidmaster->cidversion );
fprintf( out, "/CIDFontType 0 def\n\n" );
fprintf( out, "/CIDSystemInfo 3 dict dup begin\n" );
fprintf( out, " /Registry (%s) def\n", cidmaster->cidregistry );
fprintf( out, " /Ordering (%s) def\n", cidmaster->ordering );
fprintf( out, " /Supplement %d def\n", cidmaster->supplement );
fprintf( out, "end def\n\n" );
CIDLayerFindBounds(cidmaster,layer,&res);
fprintf( out, "/FontBBox [ %g %g %g %g ] def\n",
floor(res.minx), floor(res.miny),
ceil(res.maxx), ceil(res.maxy));
if ( cidmaster->use_uniqueid ) {
fprintf( out,"/UIDBase %d def\n", cidmaster->uniqueid?cidmaster->uniqueid: 4000000 + (rand()&0x3ffff) );
if ( cidmaster->xuid!=NULL && cidmaster->use_xuid ) {
fprintf( out,"/XUID %s def\n", cidmaster->xuid );
/* SFIncrementXUID(cidmaster); */ /* Unique ID managment in CID fonts is too complex for this simple trick to work */
}
}
dumpfontinfo((DumpChar) fputc,out,cidmaster,ff_cid);
if ((binary = gencidbinarydata(cidmaster,&cidbytes,flags,map,layer))==NULL )
return( 0 );
fprintf( out, "\n/CIDMapOffset %d def\n", cidbytes.cidmapoffset );
fprintf( out, "/FDBytes %d def\n", cidbytes.fdbytes );
fprintf( out, "/GDBytes %d def\n", cidbytes.gdbytes );
fprintf( out, "/CIDCount %d def\n\n", cidbytes.cidcnt );
fprintf( out, "/FDArray %d array\n", cidbytes.fdcnt );
for ( i=0; i<cidbytes.fdcnt; ++i ) {
double factor;
sf = cidmaster->subfonts[i];
/* According to the PSRef Man, v3. only FontName, FontMatrix & Private*/
/* should be defined here. But adobe's example fonts have a few */
/* extra entries, so I'll put them in */
fprintf( out, "dup %d\n", i );
fprintf( out, "\n%%ADOBeginFontDict\n" );
fprintf( out, "15 dict\n begin\n" );
fprintf( out, " /FontName /%s def\n", sf->fontname );
fprintf( out, " /FontType 1 def\n" );
factor = 1.0/(sf->ascent+sf->descent);
fprintf( out, " /FontMatrix [ %g 0 0 %g 0 0 ] def\n",
factor, factor );
fprintf( out, "/PaintType %d def\n", sf->strokedfont?2:0 );
if ( sf->strokedfont )
fprintf( out, "/StrokeWidth %g def\n", (double) sf->strokewidth );
fprintf( out, "\n %%ADOBeginPrivateDict\n" );
dumpprivatestuff((DumpChar) fputc,out,sf,&cidbytes.fds[i],flags,ff_cid,map,layer);
fprintf( out, "\n %%ADOEndPrivateDict\n" );
fprintf( out, " currentdict end\n%%ADOEndFontDict\n put\n\n" );
}
fprintf( out, "def\n\n" );
fseek(binary,0,SEEK_END);
len = ftell(binary);
sprintf( buffer, "(Binary) %ld StartData ", len );
fprintf( out, "%%%%BeginData: %ld Binary Bytes\n", (long) (len+strlen(buffer)));
fputs( buffer, out );
fseek(binary,0,SEEK_SET);
while ( (len=fread(buffer,1,sizeof(buffer),binary))>0 )
fwrite(buffer,1,len,out);
cidbytes.errors |= ferror(binary);
fclose(binary);
free(cidbytes.fds);
fprintf( out, "\n%%%%EndData\n%%%%EndResource\n%%%%EOF\n" );
return( !cidbytes.errors );
}
int _WritePSFont(FILE *out,SplineFont *sf,enum fontformat format,int flags,
EncMap *map, SplineFont *fullsf,int layer) {
int err = false;
if ( format!=ff_cid && format!=ff_ptype3 &&
(othersubrs[0]==NULL || othersubrs[0][0]==NULL ||
(othersubrs[0][1]==NULL && strcmp(othersubrs[0][0],"{}")==0)))
flags &= ~ps_flag_noflex;
/* make sure that all reals get output with '.' for decimal points */
locale_t tmplocale; locale_t oldlocale; // Declare temporary locale storage.
switch_to_c_locale(&tmplocale, &oldlocale); // Switch to the C locale temporarily and cache the old locale.
if ( (format==ff_mma || format==ff_mmb) && sf->mm!=NULL )
sf = sf->mm->normal;
if ( format==ff_cid )
err = !dumpcidstuff(out,sf->subfontcnt>0?sf:sf->cidmaster,flags,map,layer);
else {
dumpfontdict(out,sf,format,flags,map,fullsf,layer);
if ( format==ff_ptype0 )
dumptype0stuff(out,sf,map);
}
switch_to_old_locale(&tmplocale, &oldlocale); // Switch to the cached locale.
if ( ferror(out) || err)
return( 0 );
#ifdef __CygWin
/* Modern versions of windows want the execute bit set on a ttf file */
/* It might also be needed for a postscript font, but I haven't checked */
/* It isn't needed on the pfb file, but is on the pfm, so this code */
/* isn't really useful */
/* I've no idea what this corresponds to in windows, nor any idea on */
/* how to set it from the windows UI, but this seems to work */
{
struct stat buf;
fstat(fileno(out),&buf);
fchmod(fileno(out),S_IXUSR | buf.st_mode );
}
#endif
return( true );
}
int WritePSFont(char *fontname,SplineFont *sf,enum fontformat format,int flags,
EncMap *map,SplineFont *fullsf,int layer) {
FILE *out;
int ret;
if ( strstr(fontname,"://")!=NULL ) {
if (( out = tmpfile())==NULL )
return( 0 );
} else {
if (( out=fopen(fontname,"wb"))==NULL )
return( 0 );
}
ret = _WritePSFont(out,sf,format,flags,map,fullsf,layer);
if ( strstr(fontname,"://")!=NULL && ret )
ret = URLFromFile(fontname,out);
if ( fclose(out)==-1 )
ret = 0;
return( ret );
}
static void dumpimageproc(FILE *file,BDFChar *bdfc,BDFFont *font) {
SplineFont *sf = font->sf;
double scale = (sf->ascent+sf->descent)/font->pixelsize;
int width = bdfc->xmax-bdfc->xmin+1, height = bdfc->ymax-bdfc->ymin+1;
int i;
struct psfilter ps;
/* wx wy ix iy urx ury setcachdevice */
fprintf( file, " /%s { %d 0 %d %d %d %d setcachedevice \n",
bdfc->sc->name, (int) rint(bdfc->width*scale),
(int) rint(bdfc->xmin*scale), (int) rint(bdfc->ymin*scale),
(int) rint((1+bdfc->xmax)*scale), (int) rint((1+bdfc->ymax)*scale));
fprintf( file, "\t%g %g translate %g %g scale %d %d true [%d 0 0 %d 0 0] {<~\n",
bdfc->xmin*scale, bdfc->ymax*scale, /* tx tx Translate */
width*scale, height*scale, /* x y Scale */
width, height,
width, -height);
InitFilter(&ps,(DumpChar) fputc,file);
if ( bdfc->bytes_per_line==(width+7)/8 )
FilterStr(&ps,(uint8 *) bdfc->bitmap, height*bdfc->bytes_per_line);
else for ( i=0; i<height; ++i )
FilterStr(&ps,(uint8 *) (bdfc->bitmap + i*bdfc->bytes_per_line),
(width+7)/8);
FlushFilter(&ps);
fprintf(file,"} imagemask } bind def\n" );
}
int PSBitmapDump(char *filename,BDFFont *font, EncMap *map) {
char buffer[300];
FILE *file;
int i, notdefpos, cnt;
int ret = 0;
SplineFont *sf = font->sf;
if ( filename==NULL ) {
sprintf(buffer,"%s-%d.pt3", sf->fontname, font->pixelsize );
filename = buffer;
}
file = fopen(filename,"w" );
if ( file==NULL )
LogError( _("Can't open %s\n"), filename );
else {
for ( i=0; i<font->glyphcnt; i++ ) if ( font->glyphs[i] != NULL )
BCPrepareForOutput( font->glyphs[i],true );
dumprequiredfontinfo((DumpChar) fputc, file, sf, ff_ptype3, map,NULL,ly_fore);
cnt = 0;
notdefpos = SFFindNotdef(sf,-2);
for ( i=0; i<sf->glyphcnt; ++i )
if ( font->glyphs[i]!=NULL ) {
if ( strcmp(font->glyphs[i]->sc->name,".notdef")!=0 )
++cnt;
}
++cnt; /* one notdef entry */
fprintf(file,"/CharProcs %d dict def\nCharProcs begin\n", cnt );
if ( notdefpos!=-1 && font->glyphs[notdefpos]!=NULL)
dumpimageproc(file,font->glyphs[notdefpos],font);
else
fprintf( file, " /.notdef { %d 0 0 0 0 0 setcachedevice } bind def\n",
sf->ascent+sf->descent );
for ( i=0; i<sf->glyphcnt; ++i ) if ( i!=notdefpos ) {
if ( font->glyphs[i]!=NULL )
dumpimageproc(file,font->glyphs[i],font);
}
fprintf(file,"end\ncurrentdict end\n" );
fprintf(file,"/%s exch definefont\n", sf->fontname );
ret = ferror(file)==0;
if ( fclose(file)!=0 )
ret = false;
for ( i=0; i<font->glyphcnt; i++ ) if ( font->glyphs[i] != NULL )
BCRestoreAfterOutput( font->glyphs[i] );
}
return( ret );
}