/* -*- mode: c++; c-basic-offset: 4 -*- */ /* * Modified for use within matplotlib * 5 July 2007 * Michael Droettboom */ /* ** ~ppr/src/pprdrv/pprdrv_tt2.c ** Copyright 1995, Trinity College Computing Center. ** Written by David Chappell. ** ** Permission to use, copy, modify, and distribute this software and its ** documentation for any purpose and without fee is hereby granted, provided ** that the above copyright notice appear in all copies and that both that ** copyright notice and this permission notice appear in supporting ** documentation. This software is provided "as is" without express or ** implied warranty. ** ** TrueType font support. These functions allow PPR to generate ** PostScript fonts from Microsoft compatible TrueType font files. ** ** The functions in this file do most of the work to convert a ** TrueType font to a type 3 PostScript font. ** ** Most of the material in this file is derived from a program called ** "ttf2ps" which L. S. Ng posted to the usenet news group ** "comp.sources.postscript". The author did not provide a copyright ** notice or indicate any restrictions on use. ** ** Last revised 11 July 1995. */ #include #include #include #include #include "pprdrv.h" #include "truetype.h" #include #include #include class GlyphToType3 { private: GlyphToType3& operator=(const GlyphToType3& other); GlyphToType3(const GlyphToType3& other); /* The PostScript bounding box. */ int llx,lly,urx,ury; int advance_width; /* Variables to hold the character data. */ int *epts_ctr; /* array of contour endpoints */ int num_pts, num_ctr; /* number of points, number of coutours */ FWord *xcoor, *ycoor; /* arrays of x and y coordinates */ BYTE *tt_flags; /* array of TrueType flags */ int stack_depth; /* A book-keeping variable for keeping track of the depth of the PS stack */ void load_char(TTFONT* font, BYTE *glyph); void stack(TTStreamWriter& stream, int new_elem); void stack_end(TTStreamWriter& stream); void PSConvert(TTStreamWriter& stream); void PSCurveto(TTStreamWriter& stream, FWord x0, FWord y0, FWord x1, FWord y1, FWord x2, FWord y2); void PSMoveto(TTStreamWriter& stream, int x, int y); void PSLineto(TTStreamWriter& stream, int x, int y); void do_composite(TTStreamWriter& stream, struct TTFONT *font, BYTE *glyph); public: GlyphToType3(TTStreamWriter& stream, struct TTFONT *font, int charindex, bool embedded = false); ~GlyphToType3(); }; // Each point on a TrueType contour is either on the path or off it (a // control point); here's a simple representation for building such // contours. Added by Jouni Seppänen 2012-05-27. enum Flag { ON_PATH, OFF_PATH }; struct FlaggedPoint { enum Flag flag; FWord x; FWord y; FlaggedPoint(Flag flag_, FWord x_, FWord y_): flag(flag_), x(x_), y(y_) {}; }; /* ** This routine is used to break the character ** procedure up into a number of smaller ** procedures. This is necessary so as not to ** overflow the stack on certain level 1 interpreters. ** ** Prepare to push another item onto the stack, ** starting a new proceedure if necessary. ** ** Not all the stack depth calculations in this routine ** are perfectly accurate, but they do the job. */ void GlyphToType3::stack(TTStreamWriter& stream, int new_elem) { if ( num_pts > 25 ) /* Only do something of we will have a log of points. */ { if (stack_depth == 0) { stream.put_char('{'); stack_depth=1; } stack_depth += new_elem; /* Account for what we propose to add */ if (stack_depth > 100) { stream.puts("}_e{"); stack_depth = 3 + new_elem; /* A rough estimate */ } } } /* end of stack() */ void GlyphToType3::stack_end(TTStreamWriter& stream) /* called at end */ { if ( stack_depth ) { stream.puts("}_e"); stack_depth=0; } } /* end of stack_end() */ /* ** We call this routine to emmit the PostScript code ** for the character we have loaded with load_char(). */ void GlyphToType3::PSConvert(TTStreamWriter& stream) { int j, k; /* Step thru the contours. * j = index to xcoor, ycoor, tt_flags (point data) * k = index to epts_ctr (which points belong to the same contour) */ for(j = k = 0; k < num_ctr; k++) { // A TrueType contour consists of on-path and off-path points. // Two consecutive on-path points are to be joined with a // line; off-path points between on-path points indicate a // quadratic spline, where the off-path point is the control // point. Two consecutive off-path points have an implicit // on-path point midway between them. std::list points; // Represent flags and x/y coordinates as a C++ list for (; j <= epts_ctr[k]; j++) { if (!(tt_flags[j] & 1)) { points.push_back(FlaggedPoint(OFF_PATH, xcoor[j], ycoor[j])); } else { points.push_back(FlaggedPoint(ON_PATH, xcoor[j], ycoor[j])); } } if (points.size() == 0) { // Don't try to access the last element of an empty list continue; } // For any two consecutive off-path points, insert the implied // on-path point. FlaggedPoint prev = points.back(); for (std::list::iterator it = points.begin(); it != points.end(); it++) { if (prev.flag == OFF_PATH && it->flag == OFF_PATH) { points.insert(it, FlaggedPoint(ON_PATH, (prev.x + it->x) / 2, (prev.y + it->y) / 2)); } prev = *it; } // Handle the wrap-around: insert a point either at the beginning // or at the end that has the same coordinates as the opposite point. // This also ensures that the initial point is ON_PATH. if (points.front().flag == OFF_PATH) { assert(points.back().flag == ON_PATH); points.insert(points.begin(), points.back()); } else { assert(points.front().flag == ON_PATH); points.push_back(points.front()); } // The first point stack(stream, 3); PSMoveto(stream, points.front().x, points.front().y); // Step through the remaining points std::list::const_iterator it = points.begin(); for (it++; it != points.end(); /* incremented inside */) { const FlaggedPoint& point = *it; if (point.flag == ON_PATH) { stack(stream, 3); PSLineto(stream, point.x, point.y); it++; } else { std::list::const_iterator prev = it, next = it; prev--; next++; assert(prev->flag == ON_PATH); assert(next->flag == ON_PATH); stack(stream, 7); PSCurveto(stream, prev->x, prev->y, point.x, point.y, next->x, next->y); it++; it++; } } } /* Now, we can fill the whole thing. */ stack(stream, 1); stream.puts("_cl"); } /* end of PSConvert() */ void GlyphToType3::PSMoveto(TTStreamWriter& stream, int x, int y) { stream.printf("%d %d _m\n", x, y); } void GlyphToType3::PSLineto(TTStreamWriter& stream, int x, int y) { stream.printf("%d %d _l\n", x, y); } /* ** Emit a PostScript "curveto" command, assuming the current point ** is (x0, y0), the control point of a quadratic spline is (x1, y1), ** and the endpoint is (x2, y2). Note that this requires a conversion, ** since PostScript splines are cubic. */ void GlyphToType3::PSCurveto(TTStreamWriter& stream, FWord x0, FWord y0, FWord x1, FWord y1, FWord x2, FWord y2) { double sx[3], sy[3], cx[3], cy[3]; sx[0] = x0; sy[0] = y0; sx[1] = x1; sy[1] = y1; sx[2] = x2; sy[2] = y2; cx[0] = (2*sx[1]+sx[0])/3; cy[0] = (2*sy[1]+sy[0])/3; cx[1] = (sx[2]+2*sx[1])/3; cy[1] = (sy[2]+2*sy[1])/3; cx[2] = sx[2]; cy[2] = sy[2]; stream.printf("%d %d %d %d %d %d _c\n", (int)cx[0], (int)cy[0], (int)cx[1], (int)cy[1], (int)cx[2], (int)cy[2]); } /* ** Deallocate the structures which stored ** the data for the last simple glyph. */ GlyphToType3::~GlyphToType3() { free(tt_flags); /* The flags array */ free(xcoor); /* The X coordinates */ free(ycoor); /* The Y coordinates */ free(epts_ctr); /* The array of contour endpoints */ } /* ** Load the simple glyph data pointed to by glyph. ** The pointer "glyph" should point 10 bytes into ** the glyph data. */ void GlyphToType3::load_char(TTFONT* font, BYTE *glyph) { int x; BYTE c, ct; /* Read the contour endpoints list. */ epts_ctr = (int *)calloc(num_ctr,sizeof(int)); for (x = 0; x < num_ctr; x++) { epts_ctr[x] = getUSHORT(glyph); glyph += 2; } /* From the endpoint of the last contour, we can */ /* determine the number of points. */ num_pts = epts_ctr[num_ctr-1]+1; #ifdef DEBUG_TRUETYPE debug("num_pts=%d",num_pts); stream.printf("%% num_pts=%d\n",num_pts); #endif /* Skip the instructions. */ x = getUSHORT(glyph); glyph += 2; glyph += x; /* Allocate space to hold the data. */ tt_flags = (BYTE *)calloc(num_pts,sizeof(BYTE)); xcoor = (FWord *)calloc(num_pts,sizeof(FWord)); ycoor = (FWord *)calloc(num_pts,sizeof(FWord)); /* Read the flags array, uncompressing it as we go. */ /* There is danger of overflow here. */ for (x = 0; x < num_pts; ) { tt_flags[x++] = c = *(glyph++); if (c&8) /* If next byte is repeat count, */ { ct = *(glyph++); if ( (x + ct) > num_pts ) { throw TTException("Error in TT flags"); } while (ct--) { tt_flags[x++] = c; } } } /* Read the x coordinates */ for (x = 0; x < num_pts; x++) { if (tt_flags[x] & 2) /* one byte value with */ { /* external sign */ c = *(glyph++); xcoor[x] = (tt_flags[x] & 0x10) ? c : (-1 * (int)c); } else if (tt_flags[x] & 0x10) /* repeat last */ { xcoor[x] = 0; } else /* two byte signed value */ { xcoor[x] = getFWord(glyph); glyph+=2; } } /* Read the y coordinates */ for (x = 0; x < num_pts; x++) { if (tt_flags[x] & 4) /* one byte value with */ { /* external sign */ c = *(glyph++); ycoor[x] = (tt_flags[x] & 0x20) ? c : (-1 * (int)c); } else if (tt_flags[x] & 0x20) /* repeat last value */ { ycoor[x] = 0; } else /* two byte signed value */ { ycoor[x] = getUSHORT(glyph); glyph+=2; } } /* Convert delta values to absolute values. */ for (x = 1; x < num_pts; x++) { xcoor[x] += xcoor[x-1]; ycoor[x] += ycoor[x-1]; } for (x=0; x < num_pts; x++) { xcoor[x] = topost(xcoor[x]); ycoor[x] = topost(ycoor[x]); } } /* end of load_char() */ /* ** Emmit PostScript code for a composite character. */ void GlyphToType3::do_composite(TTStreamWriter& stream, struct TTFONT *font, BYTE *glyph) { USHORT flags; USHORT glyphIndex; int arg1; int arg2; /* Once around this loop for each component. */ do { flags = getUSHORT(glyph); /* read the flags word */ glyph += 2; glyphIndex = getUSHORT(glyph); /* read the glyphindex word */ glyph += 2; if (flags & ARG_1_AND_2_ARE_WORDS) { /* The tt spec. seems to say these are signed. */ arg1 = getSHORT(glyph); glyph += 2; arg2 = getSHORT(glyph); glyph += 2; } else /* The tt spec. does not clearly indicate */ { /* whether these values are signed or not. */ arg1 = *(signed char *)(glyph++); arg2 = *(signed char *)(glyph++); } if (flags & WE_HAVE_A_SCALE) { glyph += 2; } else if (flags & WE_HAVE_AN_X_AND_Y_SCALE) { glyph += 4; } else if (flags & WE_HAVE_A_TWO_BY_TWO) { glyph += 8; } else { } /* Debugging */ #ifdef DEBUG_TRUETYPE stream.printf("%% flags=%d, arg1=%d, arg2=%d\n", (int)flags,arg1,arg2); #endif /* If we have an (X,Y) shift and it is non-zero, */ /* translate the coordinate system. */ if ( flags & ARGS_ARE_XY_VALUES ) { if ( arg1 != 0 || arg2 != 0 ) stream.printf("gsave %d %d translate\n", topost(arg1), topost(arg2) ); } else { stream.printf("%% unimplemented shift, arg1=%d, arg2=%d\n",arg1,arg2); } /* Invoke the CharStrings procedure to print the component. */ stream.printf("false CharStrings /%s get exec\n", ttfont_CharStrings_getname(font, glyphIndex)); /* If we translated the coordinate system, */ /* put it back the way it was. */ if ( flags & ARGS_ARE_XY_VALUES && (arg1 != 0 || arg2 != 0) ) { stream.puts("grestore "); } } while (flags & MORE_COMPONENTS); } /* end of do_composite() */ /* ** Return a pointer to a specific glyph's data. */ BYTE *find_glyph_data(struct TTFONT *font, int charindex) { ULONG off; ULONG length; /* Read the glyph offset from the index to location table. */ if (font->indexToLocFormat == 0) { off = getUSHORT( font->loca_table + (charindex * 2) ); off *= 2; length = getUSHORT( font->loca_table + ((charindex+1) * 2) ); length *= 2; length -= off; } else { off = getULONG( font->loca_table + (charindex * 4) ); length = getULONG( font->loca_table + ((charindex+1) * 4) ); length -= off; } if (length > 0) { return font->glyf_table + off; } else { return (BYTE*)NULL; } } /* end of find_glyph_data() */ GlyphToType3::GlyphToType3(TTStreamWriter& stream, struct TTFONT *font, int charindex, bool embedded /* = false */) { BYTE *glyph; tt_flags = NULL; xcoor = NULL; ycoor = NULL; epts_ctr = NULL; stack_depth = 0; /* Get a pointer to the data. */ glyph = find_glyph_data( font, charindex ); /* If the character is blank, it has no bounding box, */ /* otherwise read the bounding box. */ if ( glyph == (BYTE*)NULL ) { llx=lly=urx=ury=0; /* A blank char has an all zero BoundingBox */ num_ctr=0; /* Set this for later if()s */ } else { /* Read the number of contours. */ num_ctr = getSHORT(glyph); /* Read PostScript bounding box. */ llx = getFWord(glyph + 2); lly = getFWord(glyph + 4); urx = getFWord(glyph + 6); ury = getFWord(glyph + 8); /* Advance the pointer. */ glyph += 10; } /* If it is a simple character, load its data. */ if (num_ctr > 0) { load_char(font, glyph); } else { num_pts=0; } /* Consult the horizontal metrics table to determine */ /* the character width. */ if ( charindex < font->numberOfHMetrics ) { advance_width = getuFWord( font->hmtx_table + (charindex * 4) ); } else { advance_width = getuFWord( font->hmtx_table + ((font->numberOfHMetrics-1) * 4) ); } /* Execute setcachedevice in order to inform the font machinery */ /* of the character bounding box and advance width. */ stack(stream, 7); if (font->target_type == PS_TYPE_42_3_HYBRID) { stream.printf("pop gsave .001 .001 scale %d 0 %d %d %d %d setcachedevice\n", topost(advance_width), topost(llx), topost(lly), topost(urx), topost(ury) ); } else { stream.printf("%d 0 %d %d %d %d _sc\n", topost(advance_width), topost(llx), topost(lly), topost(urx), topost(ury) ); } /* If it is a simple glyph, convert it, */ /* otherwise, close the stack business. */ if ( num_ctr > 0 ) /* simple */ { PSConvert(stream); } else if ( num_ctr < 0 ) /* composite */ { do_composite(stream, font, glyph); } if (font->target_type == PS_TYPE_42_3_HYBRID) { stream.printf("\ngrestore\n"); } stack_end(stream); } /* ** This is the routine which is called from pprdrv_tt.c. */ void tt_type3_charproc(TTStreamWriter& stream, struct TTFONT *font, int charindex) { GlyphToType3 glyph(stream, font, charindex); } /* end of tt_type3_charproc() */ /* ** Some of the given glyph ids may refer to composite glyphs. ** This function adds all of the dependencies of those composite ** glyphs to the glyph id vector. Michael Droettboom [06-07-07] */ void ttfont_add_glyph_dependencies(struct TTFONT *font, std::vector& glyph_ids) { std::sort(glyph_ids.begin(), glyph_ids.end()); std::stack glyph_stack; for (std::vector::iterator i = glyph_ids.begin(); i != glyph_ids.end(); ++i) { glyph_stack.push(*i); } while (glyph_stack.size()) { int gind = glyph_stack.top(); glyph_stack.pop(); BYTE* glyph = find_glyph_data( font, gind ); if (glyph != (BYTE*)NULL) { int num_ctr = getSHORT(glyph); if (num_ctr <= 0) // This is a composite glyph { glyph += 10; USHORT flags = 0; do { flags = getUSHORT(glyph); glyph += 2; gind = (int)getUSHORT(glyph); glyph += 2; std::vector::iterator insertion = std::lower_bound(glyph_ids.begin(), glyph_ids.end(), gind); if (insertion == glyph_ids.end() || *insertion != gind) { glyph_ids.insert(insertion, gind); glyph_stack.push(gind); } if (flags & ARG_1_AND_2_ARE_WORDS) { glyph += 4; } else { glyph += 2; } if (flags & WE_HAVE_A_SCALE) { glyph += 2; } else if (flags & WE_HAVE_AN_X_AND_Y_SCALE) { glyph += 4; } else if (flags & WE_HAVE_A_TWO_BY_TWO) { glyph += 8; } } while (flags & MORE_COMPONENTS); } } } } /* end of file */