summaryrefslogtreecommitdiff
path: root/Build/source/texk/xdv2pdf/xdv2pdf.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'Build/source/texk/xdv2pdf/xdv2pdf.cpp')
-rwxr-xr-xBuild/source/texk/xdv2pdf/xdv2pdf.cpp2348
1 files changed, 2348 insertions, 0 deletions
diff --git a/Build/source/texk/xdv2pdf/xdv2pdf.cpp b/Build/source/texk/xdv2pdf/xdv2pdf.cpp
new file mode 100755
index 00000000000..49f2ef3906f
--- /dev/null
+++ b/Build/source/texk/xdv2pdf/xdv2pdf.cpp
@@ -0,0 +1,2348 @@
+/****************************************************************************\
+ Part of the XeTeX typesetting system
+ copyright (c) 1994-2005 by SIL International
+ written by Jonathan Kew
+
+ This software is distributed under the terms of the Common Public License,
+ version 1.0.
+ For details, see <http://www.opensource.org/licenses/cpl1.0.php> or the file
+ cpl1.0.txt included with the software.
+\****************************************************************************/
+
+/*
+ xdv2pdf
+ Convert xdv file from XeTeX to PDF format for viewing/printing
+
+ usage: xdv2pdf [-d debug] [-m mag] [-p papersize] [-v] xdvfile [-o pdffile]
+
+ -m override magnification from xdv file
+ -v show progress messages (page counters)
+ -d set kpathsea_debug values
+ -p papersize [default: from Mac OS X printing system]
+
+ known papersize values:
+ a0-a10
+ b0-b10
+ c0-c10
+ jb0-jb10
+ letter
+ legal
+ ledger
+ tabloid
+ can append ":landscape"
+ or use "x,y" where x and y are dimensions in "big points" or with units
+
+ output file defaults to <xdvfile>.pdf
+
+ also permissible to omit xdvfile but specify pdffile; then input is from stdin
+*/
+
+#ifdef __POWERPC__
+#define MAC_OS_X_VERSION_MIN_REQUIRED MAC_OS_X_VERSION_10_2
+#else
+#define MAC_OS_X_VERSION_MIN_REQUIRED MAC_OS_X_VERSION_10_4
+#endif
+
+#include "config.h"
+
+#include <ApplicationServices/ApplicationServices.h>
+#include <QuickTime/QuickTime.h>
+
+#include <string>
+#include <vector>
+#include <list>
+#include <map>
+
+#include <sys/stat.h>
+
+#include "DVIops.h"
+
+#define XDV_ID 5 // current id_byte value for .xdv files from xetex
+
+#define XDV_FLAG_FONTTYPE_ATSUI 0x0001
+#define XDV_FLAG_FONTTYPE_ICU 0x0002
+
+#define XDV_FLAG_VERTICAL 0x0100
+#define XDV_FLAG_COLORED 0x0200
+#define XDV_FLAG_FEATURES 0x0400
+#define XDV_FLAG_VARIATIONS 0x0800
+
+#define pdfbox_crop 1
+#define pdfbox_media 2
+#define pdfbox_bleed 3
+#define pdfbox_trim 4
+#define pdfbox_art 5
+
+
+#define DEFINE_GLOBALS 1
+#include "xdv2pdf_globals.h"
+
+paperSizeRec gPaperSizes[] =
+{
+ { "a0", 2383.937008, 3370.393701 },
+ { "a1", 1683.779528, 2383.937008 },
+ { "a2", 1190.551181, 1683.779528 },
+ { "a3", 841.8897638, 1190.551181 },
+ { "a4", 595.2755906, 841.8897638 },
+ { "a5", 419.5275591, 595.2755906 },
+ { "a6", 297.6377953, 419.5275591 },
+ { "a7", 209.7637795, 297.6377953 },
+ { "a8", 147.4015748, 209.7637795 },
+ { "a9", 104.8818898, 147.4015748 },
+ { "a10", 73.7007874, 104.8818898 },
+
+ { "b0", 2834.645669, 4008.188976 },
+ { "b1", 2004.094488, 2834.645669 },
+ { "b2", 1417.322835, 2004.094488 },
+ { "b3", 1000.629921, 1417.322835 },
+ { "b4", 708.6614173, 1000.629921 },
+ { "b5", 498.8976378, 708.6614173 },
+ { "b6", 354.3307087, 498.8976378 },
+ { "b7", 249.4488189, 354.3307087 },
+ { "b8", 175.7480315, 249.4488189 },
+ { "b9", 124.7244094, 175.7480315 },
+ { "b10", 87.87401575, 124.7244094 },
+
+ { "c0", 2599.370079, 3676.535433 },
+ { "c1", 1836.850394, 2599.370079 },
+ { "c2", 1298.267717, 1836.850394 },
+ { "c3", 918.4251969, 1298.267717 },
+ { "c4", 649.1338583, 918.4251969 },
+ { "c5", 459.2125984, 649.1338583 },
+ { "c6", 323.1496063, 459.2125984 },
+ { "c7", 229.6062992, 323.1496063 },
+ { "c8", 161.5748031, 229.6062992 },
+ { "c9", 113.3858268, 161.5748031 },
+ { "c10", 79.37007874, 113.3858268 },
+
+ { "jb0", 2919.685039, 4127.244094 },
+ { "jb1", 2063.622047, 2919.685039 },
+ { "jb2", 1459.84252, 2063.622047 },
+ { "jb3", 1031.811024, 1459.84252 },
+ { "jb4", 728.503937, 1031.811024 },
+ { "jb5", 515.9055118, 728.503937 },
+ { "jb6", 362.8346457, 515.9055118 },
+ { "jb7", 257.9527559, 362.8346457 },
+ { "jb8", 181.4173228, 257.9527559 },
+ { "jb9", 127.5590551, 181.4173228 },
+ { "jb10", 90.70866142, 127.5590551 },
+
+ { "letter", 612.0, 792.0 },
+ { "legal", 612.0, 1008.0 },
+ { "tabloid", 792.0, 1224.0 },
+ { "ledger", 1224.0, 792.0 },
+
+ { 0, 0, 0 }
+};
+
+static CGAffineTransform horizontalMatrix = { 1.0, 0.0, 0.0, 1.0, 0.0, 0.0 };
+static CGAffineTransform verticalMatrix = { 0.0, 1.0, -1.0, 0.0, 0.0, 0.0 };
+
+struct nativeFont {
+ nativeFont()
+ : cgFont(0)
+ , atsFont(0)
+ , atsuStyleH(0)
+ , atsuStyleV(0)
+ , size(Long2Fix(10))
+ , isColored(false)
+ , isVertical(false)
+ {
+ }
+ CGFontRef cgFont;
+ ATSFontRef atsFont;
+ ATSUStyle atsuStyleH; // only present if isVertical is true
+ ATSUStyle atsuStyleV;
+ Fixed size;
+ CGColorRef color;
+ bool isColored;
+ bool isVertical;
+};
+
+static std::map<UInt32,nativeFont> sNativeFonts;
+
+static UInt32 sMag = 0;
+
+static bool sVerbose = false;
+
+static std::map<ATSFontRef,CGFontRef> sCGFonts;
+
+static CGFontRef
+getCGFontForATSFont(ATSFontRef fontRef)
+{
+ std::map<ATSFontRef,CGFontRef>::const_iterator i = sCGFonts.find(fontRef);
+ if (i != sCGFonts.end()) {
+ return i->second;
+ }
+ CGFontRef newFont;
+ if (fontRef == 0) {
+ ATSFontRef tmpRef = ATSFontFindFromPostScriptName(CFSTR("Helvetica"), kATSOptionFlagsDefault);
+ newFont = CGFontCreateWithPlatformFont(&tmpRef);
+ }
+ else
+ newFont = CGFontCreateWithPlatformFont(&fontRef);
+ sCGFonts[fontRef] = newFont;
+ return newFont;
+}
+
+inline UInt32
+SWAP(UInt32 v)
+{
+#ifdef WORDS_BIGENDIAN
+ return v;
+#else
+ return (v << 24)
+ + ((v << 8) & 0x00FF0000)
+ + ((v >> 8) & 0x0000FF00)
+ + (v >> 24);
+#endif
+}
+
+inline SInt32
+SWAP(SInt32 v)
+{
+#ifdef WORDS_BIGENDIAN
+ return v;
+#else
+ return (v << 24)
+ + ((v << 8) & 0x00FF0000)
+ + ((v >> 8) & 0x0000FF00)
+ + ((v >> 24) & 0x000000FF);
+#endif
+}
+
+inline UInt16
+SWAP(UInt16 v)
+{
+#ifdef WORDS_BIGENDIAN
+ return v;
+#else
+ return (v << 8) + (v >> 8);
+#endif
+}
+
+inline SInt16
+SWAP(SInt16 v)
+{
+#ifdef WORDS_BIGENDIAN
+ return v;
+#else
+ return (v << 8) + ((v >> 8) & 0xFF);
+#endif
+}
+
+/* precaution, just in case we apply these to byte-sized values! */
+inline UInt8
+SWAP(UInt8 v)
+{
+ return v;
+}
+
+inline SInt8
+SWAP(SInt8 v)
+{
+ return v;
+}
+
+void
+initAnnotBox()
+{
+ gAnnotBox.llx = gPageWd;
+ gAnnotBox.lly = 0;
+ gAnnotBox.urx = 0;
+ gAnnotBox.ury = gPageHt;
+}
+
+void
+flushAnnotBox()
+{
+ char buf[200];
+ sprintf(buf, "ABOX [%f %f %f %f]",
+ kDvi2Scr * gAnnotBox.llx + 72.0,
+ kDvi2Scr * (gPageHt - gAnnotBox.lly) - 72.0,
+ kDvi2Scr * gAnnotBox.urx + 72.0,
+ kDvi2Scr * (gPageHt - gAnnotBox.ury) - 72.0);
+ doPDFspecial(buf);
+ initAnnotBox();
+}
+
+static void
+mergeBox(const box& b)
+{
+ if (b.llx < gAnnotBox.llx)
+ gAnnotBox.llx = b.llx;
+ if (b.lly > gAnnotBox.lly)
+ gAnnotBox.lly = b.lly; // NB positive is downwards!
+ if (b.urx > gAnnotBox.urx)
+ gAnnotBox.urx = b.urx;
+ if (b.ury < gAnnotBox.ury)
+ gAnnotBox.ury = b.ury;
+}
+
+long
+readSigned(FILE* f, int k)
+{
+ long s = (long)(signed char)getc(f);
+ while (--k > 0) {
+ s <<= 8;
+ s += (getc(f) & 0xff);
+ }
+ return s;
+}
+
+unsigned long
+readUnsigned(FILE* f, int k)
+{
+ unsigned long u = getc(f);
+ while (--k > 0) {
+ u <<= 8;
+ u += (getc(f) & 0xff);
+ }
+ return u;
+}
+
+
+void
+setColor(CGColorRef color, bool force = false)
+{
+ if (force || !gCurrentColor || !CGColorEqualToColor(color, gCurrentColor))
+ CGContextSetFillColorWithColor(gCtx, color);
+ gCurrentColor = color;
+}
+
+inline double
+radians(double degrees)
+{
+ return degrees / 180.0 * M_PI;
+}
+
+void
+ensurePageStarted()
+{
+ if (gPageStarted)
+ return;
+
+ CGContextSaveGState(gCtx);
+ CGContextBeginPage(gCtx, &gMediaBox);
+
+ static CGAffineTransform sInitTextMatrix;
+ if (gPageIndex == 1) {
+ sInitTextMatrix = CGContextGetTextMatrix(gCtx);
+ }
+ else
+ CGContextSetTextMatrix(gCtx, sInitTextMatrix);
+
+ gCurrentColor = NULL; /* ensure color will get set by first drawing op */
+
+ CGContextTranslateCTM(gCtx, 72.0, -72.0);
+
+ gPageWd = kScr2Dvi * gMediaBox.size.width;
+ gPageHt = kScr2Dvi * gMediaBox.size.height;
+
+ cur_cgFont = kUndefinedFont;
+
+ gPageStarted = true;
+
+ paintBackground();
+}
+
+void
+paintBackground()
+{
+ if (gBackground != NULL) {
+ if (!gPageStarted)
+ ensurePageStarted(); /* this will call paintBackground() again */
+ else {
+ CGContextSaveGState(gCtx);
+ CGContextSetFillColorWithColor(gCtx, gBackground);
+ CGContextTranslateCTM(gCtx, -72.0, 72.0);
+ CGContextFillRect(gCtx, gMediaBox);
+ CGContextRestoreGState(gCtx);
+ }
+ }
+}
+
+#define MAX_BUFFERED_GLYPHS 1024
+int gGlyphCount = 0;
+CGGlyph gGlyphs[MAX_BUFFERED_GLYPHS];
+CGSize gAdvances[MAX_BUFFERED_GLYPHS+1];
+CGPoint gStartLoc;
+CGPoint gPrevLoc;
+
+static void
+myShowGlyphsWithAdvances(CGContextRef c, const CGGlyph* glyphs, const CGSize* advances, int count)
+{
+ CGPoint loc = CGContextGetTextPosition(c);
+ while (count > 0) {
+ CGContextShowGlyphsAtPoint(c, loc.x, loc.y, glyphs, 1);
+ loc.x += advances->width;
+ loc.y += advances->height;
+ ++advances;
+ ++glyphs;
+ --count;
+ }
+}
+
+void
+flushGlyphs()
+{
+ if (gGlyphCount > 0) {
+ CGContextSetTextPosition(gCtx, gStartLoc.x, gStartLoc.y);
+ gAdvances[gGlyphCount].width = 0;
+ gAdvances[gGlyphCount].height = 0;
+
+ if (sNativeFonts[cur_cgFont].isColored)
+ setColor(sNativeFonts[cur_cgFont].color);
+ else
+ setColor(gTextColor);
+ if (&CGContextShowGlyphsWithAdvances != NULL)
+ CGContextShowGlyphsWithAdvances(gCtx, gGlyphs, gAdvances, gGlyphCount);
+ else
+ myShowGlyphsWithAdvances(gCtx, gGlyphs, gAdvances, gGlyphCount);
+
+ gGlyphCount = 0;
+ }
+}
+
+void
+bufferGlyph(CGGlyph g)
+{
+ ensurePageStarted();
+
+ CGPoint curLoc;
+ curLoc.x = kDvi2Scr * dvi.h;
+ curLoc.y = kDvi2Scr * (gPageHt - dvi.v);
+ if (gGlyphCount == 0)
+ gStartLoc = curLoc;
+ else {
+ gAdvances[gGlyphCount-1].width = curLoc.x - gPrevLoc.x;
+ gAdvances[gGlyphCount-1].height = curLoc.y - gPrevLoc.y;
+ }
+ gPrevLoc = curLoc;
+ gGlyphs[gGlyphCount] = g;
+ if (++gGlyphCount == MAX_BUFFERED_GLYPHS)
+ flushGlyphs();
+}
+
+void
+setChar(UInt32 c, bool adv)
+{
+ OSStatus status;
+
+ ensurePageStarted();
+
+ if (f != cur_cgFont) {
+ flushGlyphs();
+ CGContextSetFont(gCtx, gTeXFonts[f].cgFont);
+ CGContextSetFontSize(gCtx, Fix2X(gTeXFonts[f].size) * gMagScale);
+ CGContextSetTextMatrix(gCtx, horizontalMatrix);
+ cur_cgFont = f;
+ }
+
+ CGGlyph g;
+ if (gTeXFonts[f].charMap != NULL) {
+ if (c < gTeXFonts[f].charMap->size())
+ g = (*gTeXFonts[f].charMap)[c];
+ else
+ g = c;
+ }
+ else
+ // default: glyph ID = char + 1, works for the majority of the CM PS fonts
+ g = c + 1;
+
+ bufferGlyph(g);
+
+ if (gTrackingBoxes) {
+ if (gTeXFonts[f].hasMetrics) {
+ box b = {
+ dvi.h,
+ dvi.v + gTeXFonts[f].depths[c],
+ dvi.h + gTeXFonts[f].widths[c],
+ dvi.v - gTeXFonts[f].heights[c] };
+ mergeBox(b);
+ }
+ }
+
+ if (adv && gTeXFonts[f].hasMetrics)
+ dvi.h += gTeXFonts[f].widths[c];
+}
+
+
+static FixedPoint
+getGlyphVOrg(const nativeFont& f, UInt16 gid)
+{
+ FixedPoint vorg;
+ ATSGlyphIdealMetrics metrics;
+ OSStatus status = ATSUGlyphGetIdealMetrics(f.atsuStyleV, 1, &gid, 0, &metrics);
+/*
+ fprintf(stderr, "\nmetrics(V) for glyph %d: adv = (%.3f, %.3f) sb = (%.3f, %.3f) os = (%.3f, %.3f)\n", gid,
+ metrics.advance.x, metrics.advance.y, metrics.sideBearing.x, metrics.sideBearing.y,
+ metrics.otherSideBearing.x, metrics.otherSideBearing.y);
+*/
+ vorg.y = X2Fix(-metrics.advance.y/* - metrics.otherSideBearing.y*/);
+ status = ATSUGlyphGetIdealMetrics(f.atsuStyleH, 1, &gid, 0, &metrics);
+/*
+ fprintf(stderr, "metrics(H) for glyph %d: adv = (%.3f, %.3f) sb = (%.3f, %.3f) os = (%.3f, %.3f)\n", gid,
+ metrics.advance.x, metrics.advance.y, metrics.sideBearing.x, metrics.sideBearing.y,
+ metrics.otherSideBearing.x, metrics.otherSideBearing.y);
+*/
+ vorg.x = X2Fix(metrics.advance.x / 2);
+ return vorg;
+}
+
+static void
+doGlyphArray(FILE* xdv, bool yLocs)
+{
+ static int maxGlyphs = 0;
+ static FixedPoint* locations = 0;
+ static CGSize* advances = 0;
+ static UInt16* glyphs = 0;
+
+ flushGlyphs();
+
+ Fixed wid = readUnsigned(xdv, 4);
+ int glyphCount = readUnsigned(xdv, 2);
+
+ if (glyphCount >= maxGlyphs) {
+ maxGlyphs = glyphCount + 100;
+ if (glyphs != 0)
+ delete[] glyphs;
+ glyphs = new UInt16[maxGlyphs];
+ if (locations != 0)
+ delete[] locations;
+ locations = new FixedPoint[maxGlyphs];
+ if (advances != 0)
+ delete[] advances;
+ advances = new CGSize[maxGlyphs];
+ }
+
+ for (int i = 0; i < glyphCount; ++i) {
+ locations[i].x = readSigned(xdv, 4);
+ locations[i].y = yLocs ? readSigned(xdv, 4) : 0;
+ }
+ for (int i = 0; i < glyphCount; ++i)
+ glyphs[i] = readUnsigned(xdv, 2);
+
+ if (f != cur_cgFont) {
+ CGContextSetFont(gCtx, sNativeFonts[f].cgFont);
+ CGContextSetFontSize(gCtx, Fix2X(sNativeFonts[f].size));
+ CGContextSetTextMatrix(gCtx, sNativeFonts[f].isVertical
+ ? verticalMatrix : horizontalMatrix);
+ cur_cgFont = f;
+ }
+
+ if (sNativeFonts[f].isColored)
+ setColor(sNativeFonts[f].color);
+ else
+ setColor(gTextColor);
+
+ if (sNativeFonts[f].isVertical) {
+ for (int i = 0; i < glyphCount; ++i) {
+ // need to adjust position to right by VOrg.y and down by VOrg.x
+ FixedPoint vorg = getGlyphVOrg(sNativeFonts[f], glyphs[i]);
+ locations[i].x += vorg.y;
+ locations[i].y += vorg.x;
+ }
+ }
+
+ CGContextSetTextPosition(gCtx, kDvi2Scr * dvi.h + Fix2X(locations[0].x) * 72 / 72.27,
+ kDvi2Scr * (gPageHt - dvi.v) - Fix2X(locations[0].y) * 72 / 72.27);
+
+ for (int i = 0; i < glyphCount - 1; ++i) {
+ advances[i].width = gMagScale * Fix2X(locations[i+1].x - locations[i].x) * 72 / 72.27;
+ advances[i].height = gMagScale * Fix2X(locations[i].y - locations[i+1].y) * 72 / 72.27;
+ }
+ advances[glyphCount-1].width = 0.0;
+ advances[glyphCount-1].height = 0.0;
+
+ if (&CGContextShowGlyphsWithAdvances != NULL)
+ CGContextShowGlyphsWithAdvances(gCtx, glyphs, advances, glyphCount);
+ else
+ myShowGlyphsWithAdvances(gCtx, glyphs, advances, glyphCount);
+
+ if (gTrackingBoxes) {
+ box b = {
+ dvi.h,
+ dvi.v + (sNativeFonts[f].size / 3),
+ dvi.h + wid,
+ dvi.v - (2 * sNativeFonts[f].size / 3) };
+ mergeBox(b);
+ }
+
+ dvi.h += wid;
+}
+
+/* code lifted almost verbatim from Apple sample "QTtoCG" */
+typedef struct {
+ size_t width;
+ size_t height;
+ size_t bitsPerComponent;
+ size_t bitsPerPixel;
+ size_t bytesPerRow;
+ size_t size;
+ CGImageAlphaInfo ai;
+ CGColorSpaceRef cs;
+ unsigned char *data;
+ CMProfileRef prof;
+} BitmapInfo;
+
+void readBitmapInfo(GraphicsImportComponent gi, BitmapInfo *bi)
+{
+ ComponentResult result;
+ ImageDescriptionHandle imageDescH = NULL;
+ ImageDescription *desc;
+ Handle profile = NULL;
+
+ result = GraphicsImportGetImageDescription(gi, &imageDescH);
+ if( noErr != result || imageDescH == NULL ) {
+ fprintf(stderr, "Error while retrieving image description");
+ exit(1);
+ }
+
+ desc = *imageDescH;
+
+ bi->width = desc->width;
+ bi->height = desc->height;
+ bi->bitsPerComponent = 8;
+ bi->bitsPerPixel = 32;
+ bi->bytesPerRow = (bi->bitsPerPixel * bi->width + 7)/8;
+ bi->ai = (desc->depth == 32) ? kCGImageAlphaFirst : kCGImageAlphaNoneSkipFirst;
+ bi->size = bi->bytesPerRow * bi->height;
+ bi->data = (unsigned char*)malloc(bi->size);
+
+ bi->cs = NULL;
+ bi->prof = NULL;
+ GraphicsImportGetColorSyncProfile(gi, &profile);
+ if( NULL != profile ) {
+ CMError err;
+ CMProfileLocation profLoc;
+ Boolean bValid, bPreferredCMMNotFound;
+
+ profLoc.locType = cmHandleBasedProfile;
+ profLoc.u.handleLoc.h = profile;
+
+ err = CMOpenProfile(&bi->prof, &profLoc);
+ if( err != noErr ) {
+ fprintf(stderr, "Cannot open profile");
+ exit(1);
+ }
+
+ /* Not necessary to validate profile, but good for debugging */
+ err = CMValidateProfile(bi->prof, &bValid, &bPreferredCMMNotFound);
+ if( err != noErr ) {
+ fprintf(stderr, "Cannot validate profile : Valid: %d, Preferred CMM not found : %d", bValid,
+ bPreferredCMMNotFound);
+ exit(1);
+ }
+
+ bi->cs = CGColorSpaceCreateWithPlatformColorSpace( bi->prof );
+
+ if( bi->cs == NULL ) {
+ fprintf(stderr, "Error creating cg colorspace from csync profile");
+ exit(1);
+ }
+ DisposeHandle(profile);
+ }
+
+ if( imageDescH != NULL)
+ DisposeHandle((Handle)imageDescH);
+}
+
+void getBitmapData(GraphicsImportComponent gi, BitmapInfo *bi)
+{
+ GWorldPtr gWorld;
+ QDErr err = noErr;
+ Rect boundsRect = { 0, 0, bi->height, bi->width };
+ ComponentResult result;
+
+ if( bi->data == NULL ) {
+ fprintf(stderr, "no bitmap buffer available");
+ exit(1);
+ }
+
+ err = NewGWorldFromPtr( &gWorld, k32ARGBPixelFormat, &boundsRect, NULL, NULL, 0,
+ (char*)bi->data, bi->bytesPerRow );
+ if (noErr != err) {
+ fprintf(stderr, "error creating new gworld - %d", err);
+ exit(1);
+ }
+
+ if( (result = GraphicsImportSetGWorld(gi, gWorld, NULL)) != noErr ) {
+ fprintf(stderr, "error while setting gworld");
+ exit(1);
+ }
+
+ if( (result = GraphicsImportDraw(gi)) != noErr ) {
+ fprintf(stderr, "error while drawing image through qt");
+ exit(1);
+ }
+
+ DisposeGWorld(gWorld);
+}
+/* end of code from "QTtoCG" */
+
+struct imageRec {
+ CGImageRef ref;
+ CGRect bounds;
+};
+std::map<std::string,imageRec> sCGImages;
+std::map<std::string,CGPDFDocumentRef> sPdfDocs;
+
+static void
+doPicFile(FILE* xdv, int pdfBoxType) // t[4][6] p[2] l[2] a[l]
+{
+ CGAffineTransform t;
+ t.a = Fix2X(readSigned(xdv, 4));
+ t.b = Fix2X(readSigned(xdv, 4));
+ t.c = Fix2X(readSigned(xdv, 4));
+ t.d = Fix2X(readSigned(xdv, 4));
+ t.tx = Fix2X(readSigned(xdv, 4));
+ t.ty = Fix2X(readSigned(xdv, 4));
+ if (sMag != 1000)
+ t = CGAffineTransformConcat(t, CGAffineTransformMakeScale(gMagScale, gMagScale));
+
+ int p = readSigned(xdv, 2);
+ int l = readUnsigned(xdv, 2);
+ unsigned char* pathname = new unsigned char[l + 1];
+ for (int i = 0; i < l; ++i)
+ pathname[i] = readUnsigned(xdv, 1);
+ pathname[l] = 0;
+
+ CFURLRef url = CFURLCreateFromFileSystemRepresentation(kCFAllocatorDefault, pathname, l, false);
+ if (url != NULL) {
+ std::string pathString((char*)pathname, l); // key for our map<>s of already-used images
+
+ // is it a \pdffile instance?
+ CGPDFDocumentRef document = NULL;
+ CGImageRef image = NULL;
+ CGRect bounds;
+ CGPDFBox cgPdfBoxType;
+ if (pdfBoxType > 0) {
+ std::map<std::string,CGPDFDocumentRef>::const_iterator i = sPdfDocs.find(pathString);
+ if (i != sPdfDocs.end())
+ document = i->second;
+ else {
+ document = CGPDFDocumentCreateWithURL(url);
+ sPdfDocs[pathString] = document;
+ }
+ if (document != NULL) {
+ int nPages = CGPDFDocumentGetNumberOfPages(document);
+ if (p < 0) p = nPages + 1 + p;
+ if (p > nPages) p = nPages;
+ if (p < 1) p = 1;
+#if MAC_OS_X_VERSION_MIN_REQUIRED < MAC_OS_X_VERSION_10_3
+ if (&CGPDFDocumentGetPage == NULL) {
+ switch (pdfBoxType) {
+ case pdfbox_crop:
+ default:
+ bounds = CGPDFDocumentGetCropBox(document, p);
+ break;
+ case pdfbox_media:
+ bounds = CGPDFDocumentGetMediaBox(document, p);
+ break;
+ case pdfbox_bleed:
+ bounds = CGPDFDocumentGetBleedBox(document, p);
+ break;
+ case pdfbox_trim:
+ bounds = CGPDFDocumentGetTrimBox(document, p);
+ break;
+ case pdfbox_art:
+ bounds = CGPDFDocumentGetArtBox(document, p);
+ break;
+ }
+ }
+ else
+#endif
+ {
+ CGPDFPageRef pageRef = CGPDFDocumentGetPage(document, p);
+ switch (pdfBoxType) {
+ case pdfbox_crop:
+ default:
+ cgPdfBoxType = kCGPDFCropBox;
+ break;
+ case pdfbox_media:
+ cgPdfBoxType = kCGPDFMediaBox;
+ break;
+ case pdfbox_bleed:
+ cgPdfBoxType = kCGPDFBleedBox;
+ break;
+ case pdfbox_trim:
+ cgPdfBoxType = kCGPDFTrimBox;
+ break;
+ case pdfbox_art:
+ cgPdfBoxType = kCGPDFArtBox;
+ break;
+ }
+ bounds = CGPDFPageGetBoxRect(pageRef, cgPdfBoxType);
+ }
+ }
+ }
+
+ // otherwise use GraphicsImport
+ else {
+ std::map<std::string,imageRec>::const_iterator i = sCGImages.find(pathString);
+ if (i != sCGImages.end()) {
+ image = i->second.ref;
+ bounds = i->second.bounds;
+ }
+ else {
+ FSRef ref;
+ if (CFURLGetFSRef(url, &ref)) {
+ FSSpec spec;
+ if (FSGetCatalogInfo(&ref, kFSCatInfoNone, NULL, NULL, &spec, NULL) == noErr) {
+ ComponentInstance gi;
+ OSErr result = GetGraphicsImporterForFile(&spec, &gi);
+ if (result == noErr) {
+ BitmapInfo bi;
+ readBitmapInfo(gi, &bi);
+ getBitmapData(gi, &bi);
+
+ if (bi.cs == NULL)
+ bi.cs = CGColorSpaceCreateDeviceRGB();
+ CGDataProviderRef provider = CGDataProviderCreateWithData(NULL, bi.data, bi.size, NULL);
+ image = CGImageCreate(bi.width, bi.height, bi.bitsPerComponent, bi.bitsPerPixel,
+ bi.bytesPerRow, bi.cs, bi.ai, provider, NULL, 0, kCGRenderingIntentDefault);
+ CGColorSpaceRelease(bi.cs);
+
+ ImageDescriptionHandle desc = NULL;
+ result = GraphicsImportGetImageDescription(gi, &desc);
+ bounds.origin.x = 0;
+ bounds.origin.y = 0;
+ bounds.size.width = (*desc)->width * 72.0 / Fix2X((*desc)->hRes);
+ bounds.size.height = (*desc)->height * 72.0 / Fix2X((*desc)->vRes);
+ DisposeHandle((Handle)desc);
+ result = CloseComponent(gi);
+ }
+ imageRec ir = { image, bounds };
+ sCGImages[pathString] = ir;
+ }
+ }
+ }
+ }
+
+ CGContextSaveGState(gCtx);
+
+ CGContextTranslateCTM(gCtx, kDvi2Scr * dvi.h, kDvi2Scr * (gPageHt - dvi.v));
+ CGContextConcatCTM(gCtx, t);
+
+ if (document != NULL) {
+#if MAC_OS_X_VERSION_MIN_REQUIRED < MAC_OS_X_VERSION_10_3
+ if (&CGPDFDocumentGetPage == NULL) {
+ CGRect mediaBox = CGPDFDocumentGetMediaBox(document, p);
+ CGRect cropBox = CGPDFDocumentGetCropBox(document, p);
+ CGContextClipToRect(gCtx, cropBox);
+ CGContextDrawPDFDocument(gCtx, mediaBox, document, p);
+ }
+ else
+#endif
+ {
+ CGPDFPageRef pageRef = CGPDFDocumentGetPage(document, p);
+ CGAffineTransform xf = CGPDFPageGetDrawingTransform(pageRef, cgPdfBoxType, bounds, 0, true);
+ CGContextConcatCTM(gCtx, xf);
+ CGRect cropBox = CGPDFPageGetBoxRect(pageRef, kCGPDFCropBox);
+ CGContextClipToRect(gCtx, cropBox);
+ CGContextDrawPDFPage(gCtx, pageRef);
+ }
+ }
+ else if (image != NULL)
+ CGContextDrawImage(gCtx, bounds, image);
+
+ if (gTrackingBoxes) {
+ // figure out the corners of the transformed and positioned image,
+ // and remember the lower left and upper right of the result
+ CGPoint p[4];
+ p[0].x = bounds.origin.x;
+ p[0].y = bounds.origin.y;
+ p[1].x = bounds.origin.x;
+ p[1].y = bounds.origin.y + bounds.size.height;
+ p[2].x = bounds.origin.x + bounds.size.width;
+ p[2].y = bounds.origin.y + bounds.size.height;
+ p[3].x = bounds.origin.x + bounds.size.width;
+ p[3].y = bounds.origin.y;
+
+ CGPoint ll = { MAXFLOAT, MAXFLOAT };
+ CGPoint ur = { -MAXFLOAT, -MAXFLOAT };
+
+ t = CGContextGetCTM(gCtx);
+ // now t is the CTM, including positioning as well as transformations of the image
+
+ for (int i = 0; i < 4; ++i) {
+ p[i] = CGPointApplyAffineTransform(p[i], t);
+ if (p[i].x < ll.x)
+ ll.x = p[i].x;
+ if (p[i].y < ll.y)
+ ll.y = p[i].y;
+ if (p[i].x > ur.x)
+ ur.x = p[i].x;
+ if (p[i].y > ur.y)
+ ur.y = p[i].y;
+ }
+
+ // convert back to dvi space and add to the annotation area
+ box b = {
+ (ll.x - 72.0) * kScr2Dvi,
+ gPageHt - (ur.y + 72.0) * kScr2Dvi,
+ (ur.x - 72.0) * kScr2Dvi,
+ gPageHt - (ll.y + 72.0) * kScr2Dvi
+ };
+ mergeBox(b);
+ }
+
+ CGContextRestoreGState(gCtx);
+
+ CFRelease(url);
+ }
+
+}
+
+/* declarations of KPATHSEARCH functions we use for finding TFMs and OTFs */
+extern "C" {
+ UInt8* kpse_find_file(const UInt8* name, int type, int must_exist);
+ UInt8* uppercasify(const UInt8* s);
+};
+extern unsigned kpathsea_debug;
+
+#include "xdv_kpse_formats.h"
+
+static void
+loadMetrics(struct texFont& font, UInt8* name, Fixed d, Fixed s)
+{
+ UInt8* pathname = kpse_find_file(name, xdv_kpse_tfm_format, true);
+ if (pathname) {
+ FILE* tfmFile = fopen((char*)pathname, "rb");
+ if (tfmFile != 0) {
+ enum { lf = 0, lh, bc, ec, nw, nh, nd, ni, nl, nk, ne, np };
+ SInt16 directory[12];
+ for (int i = 0; i < 12; ++i)
+ directory[i] = readSigned(tfmFile, 2);
+ fseek(tfmFile, directory[lh] * 4, SEEK_CUR);
+ int nChars = directory[ec] - directory[bc] + 1;
+ double_t factor = Fix2X(d) / 16.0;
+ if (s != d)
+ factor = factor * Fix2X(s) / Fix2X(d);
+ if (nChars > 0) {
+ struct s_charinfo {
+ UInt8 widthIndex;
+ UInt8 heightDepth;
+ UInt8 italicIndex;
+ UInt8 remainder;
+ };
+ s_charinfo* charInfo = new s_charinfo[nChars];
+ for (int i = 0; i < nChars; ++i) {
+ charInfo[i].widthIndex = readUnsigned(tfmFile, 1);
+ charInfo[i].heightDepth = readUnsigned(tfmFile, 1);
+ charInfo[i].italicIndex = readUnsigned(tfmFile, 1);
+ charInfo[i].remainder = readUnsigned(tfmFile, 1);
+ }
+ Fixed* widths = new Fixed[directory[nw]];
+ for (int i = 0; i < directory[nw]; ++i)
+ widths[i] = readSigned(tfmFile, 4);
+ Fixed* heights = new Fixed[directory[nh]];
+ for (int i = 0; i < directory[nh]; ++i)
+ heights[i] = readSigned(tfmFile, 4);
+ Fixed* depths = new Fixed[directory[nd]];
+ for (int i = 0; i < directory[nd]; ++i)
+ depths[i] = readSigned(tfmFile, 4);
+
+ font.widths.reserve(directory[ec] + 1);
+ font.heights.reserve(directory[ec] + 1);
+ font.depths.reserve(directory[ec] + 1);
+ font.charMap = new std::vector<UInt16>;
+ font.charMap->reserve(directory[ec] + 1);
+ for (int i = 0; i < directory[bc]; ++i) {
+ font.widths.push_back(0);
+ font.heights.push_back(0);
+ font.depths.push_back(0);
+ font.charMap->push_back(0);
+ }
+ int g = 0;
+ for (int i = 0; i < nChars; ++i) {
+ if (charInfo[i].widthIndex == 0) {
+ font.widths.push_back(0);
+ font.heights.push_back(0);
+ font.depths.push_back(0);
+ font.charMap->push_back(0);
+ }
+ else {
+ double_t wid = Fix2X(widths[charInfo[i].widthIndex]) * factor;
+ font.widths.push_back(X2Fix(wid));
+
+ int heightIndex = (charInfo[i].heightDepth >> 4);
+ double_t ht = Fix2X(heights[heightIndex]) * factor;
+ font.heights.push_back(X2Fix(ht));
+
+ int depthIndex = (charInfo[i].heightDepth & 0x0f);
+ double_t dp = Fix2X(depths[depthIndex]) * factor;
+ font.depths.push_back(X2Fix(dp));
+
+ font.charMap->push_back(++g);
+ }
+ }
+
+ delete[] widths;
+ delete[] heights;
+ delete[] depths;
+ delete[] charInfo;
+ }
+
+ font.hasMetrics = true;
+ fclose(tfmFile);
+ }
+ free(pathname);
+ }
+}
+
+typedef std::vector<std::string> encoding;
+std::map<std::string,encoding> sEncodings;
+
+class fontMapRec {
+public:
+ fontMapRec()
+ : cgFont(0)
+ , cmap(NULL)
+ , loaded(false)
+ { }
+
+ std::string psName;
+ std::string encName;
+ std::string pfbName;
+
+ CGFontRef cgFont;
+ std::vector<UInt16>* cmap;
+
+ bool loaded;
+};
+
+std::map<std::string,fontMapRec> psFontsMap;
+
+static void
+clearFontMap()
+{
+ std::map<std::string,fontMapRec>::iterator i;
+ for (i = psFontsMap.begin(); i != psFontsMap.end(); ++i) {
+ if (i->second.cmap != NULL)
+ delete i->second.cmap;
+ }
+ psFontsMap.clear();
+}
+
+void
+doPdfMapLine(const char* line, char mode)
+{
+ if (*line == '%')
+ return;
+ while (*line == ' ' || *line == '\t')
+ ++line;
+ if (*line < ' ')
+ return;
+
+ if (mode == 0) {
+ switch (*line) {
+ case '+':
+ case '-':
+ case '=':
+ mode = *line;
+ ++line;
+ while (*line == ' ' || *line == '\t')
+ ++line;
+ if (*line < ' ')
+ return;
+ break;
+ default:
+ clearFontMap();
+ mode = '+';
+ break;
+ }
+ }
+
+ const char* b = line;
+ const char* e = b;
+ while (*e > ' ')
+ ++e;
+ std::string tfmName(b, e - b);
+
+ if (mode == '-') {
+ psFontsMap.erase(tfmName);
+ return;
+ // erase existing entry
+ }
+
+ if ((mode == '+') && (psFontsMap.find(tfmName) != psFontsMap.end()))
+ return;
+ // don't add if entry already exists
+
+ while (*e && *e <= ' ')
+ ++e;
+ b = e;
+ while (*e > ' ')
+ ++e;
+ if (e > b) {
+ fontMapRec fr;
+ fr.psName.assign(b, e - b);
+ while (*e && *e <= ' ')
+ ++e;
+ if (*e == '"') { // skip quoted string; we don't do oblique, stretch, etc. (yet)
+ ++e;
+ while (*e && *e != '"')
+ ++e;
+ if (*e == '"')
+ ++e;
+ while (*e && *e <= ' ')
+ ++e;
+ }
+ while (*e == '<') {
+ ++e;
+ b = e;
+ while (*e > ' ')
+ ++e;
+ if (strncmp(e - 4, ".enc", 4) == 0) {
+ fr.encName.assign(b, e - b);
+ }
+ else if (strncmp(e - 4, ".pfb", 4) == 0) {
+ fr.pfbName.assign(b, e - b);
+ }
+/*
+ else if (strncmp(e - 4, ".pfa", 4) == 0) {
+ fr.otfName.assign(b, e - b - 4);
+ }
+*/
+ while (*e && *e <= ' ')
+ ++e;
+ }
+ psFontsMap[tfmName] = fr;
+ }
+}
+
+static bool sMapFileLoaded = false;
+
+void
+doPdfMapFile(const char* fileName)
+{
+ char mode = 0;
+
+ while (*fileName == ' ' || *fileName == '\t')
+ ++fileName;
+ if (*fileName < ' ')
+ return;
+
+ switch (*fileName) {
+ case '+':
+ case '-':
+ case '=':
+ mode = *fileName;
+ ++fileName;
+ break;
+ default:
+ clearFontMap();
+ mode = '+';
+ break;
+ }
+ while (*fileName == ' ' || *fileName == '\t')
+ ++fileName;
+ if (*fileName < ' ')
+ return;
+
+ bool loaded = false;
+ UInt8* pathname = kpse_find_file((UInt8*)fileName, xdv_kpse_font_map_format, true);
+ if (pathname) {
+ FILE* f = fopen((char*)pathname, "r");
+ if (f != NULL) {
+ char line[1000];
+ while (!feof(f)) {
+ if (fgets(line, 999, f) == 0)
+ break;
+ doPdfMapLine(line, mode);
+
+ }
+ loaded = true;
+ fclose(f);
+ if (sVerbose)
+ fprintf(stderr, "\n{fontmap: %s} ", pathname);
+ }
+ free(pathname);
+ }
+ if (!loaded)
+ fprintf(stderr, "\n*** fontmap %s not found; texmf.cnf may be broken\n", fileName);
+ else
+ sMapFileLoaded = true;
+}
+
+// return a pointer to the encoding vector with the given name, loading it if necessary
+// we don't really "parse" the .enc file
+static const encoding*
+getEncoding(const std::string& name)
+{
+ std::map<std::string,encoding>::iterator i = sEncodings.find(name);
+ if (i == sEncodings.end()) {
+ encoding enc;
+ UInt8* pathname = kpse_find_file((UInt8*)(name.c_str()), xdv_kpse_enc_format, true);
+ if (pathname != NULL) {
+ FILE* f = fopen((char*)pathname, "r");
+ if (f != NULL) {
+ int c;
+ bool inVector = false;
+ std::string str;
+ while ((c = getc(f)) != EOF) {
+ got_ch:
+ if (c == '%') { // comment: skip rest of line
+ while ((c = getc(f)) != EOF && c != '\r' && c != '\n')
+ ;
+ goto got_ch;
+ }
+ if (c == '[') {
+ inVector = true;
+ }
+ else if (c == '/' || c <= ' ' || c == ']' || c == EOF) {
+ if (inVector && str.length() > 0)
+ enc.push_back(str);
+ str.clear();
+ }
+ else if (inVector && c != EOF)
+ str.append(1, (char)c);
+ }
+ if (sVerbose)
+ fprintf(stderr, "\n{encoding: %s} ", pathname);
+ fclose(f);
+ }
+ free(pathname);
+ }
+ sEncodings[name] = enc;
+ return &(sEncodings[name]);
+ }
+
+ return &(i->second);
+}
+
+static ATSFontRef
+activateFromPath(const char* pathName)
+{
+ if (sVerbose)
+ fprintf(stderr, "\n{activate %s", pathName);
+ ItemCount count = 0;
+ FSRef fontFileRef;
+ bzero(&fontFileRef, sizeof(fontFileRef));
+ OSStatus status = FSPathMakeRef((UInt8*)pathName, &fontFileRef, 0);
+ if (status == noErr) {
+ ++count;
+ FSSpec fontFileSpec;
+ bzero(&fontFileSpec, sizeof(fontFileSpec));
+ status = FSGetCatalogInfo(&fontFileRef, 0, 0, 0, &fontFileSpec, 0);
+ if (status == noErr) {
+ ++count;
+ ATSFontContainerRef containerRef = 0;
+ status = ATSFontActivateFromFileSpecification(&fontFileSpec, kATSFontContextLocal,
+ kATSFontFormatUnspecified, 0, kATSOptionFlagsDefault, &containerRef);
+ if (status == noErr) {
+ ++count;
+ ATSFontRef fontRef;
+ status = ATSFontFindFromContainer(containerRef, 0, 1, &fontRef, &count);
+ if (status == noErr && count == 1) {
+ if (sVerbose)
+ fprintf(stderr, "}");
+ return fontRef;
+ }
+ // failed, or container contained multiple fonts(!) -- confused
+ ATSFontDeactivate(containerRef, 0, kATSOptionFlagsDefault);
+ }
+ }
+ }
+ if (sVerbose)
+ fprintf(stderr, "... failed: status=%d, count=%d}", status, count);
+ return 0;
+}
+
+struct postTable {
+ Fixed format;
+ Fixed italicAngle;
+ SInt16 underlinePosition;
+ SInt16 underlineThickness;
+ UInt16 isFixedPitch;
+ UInt16 reserved;
+ UInt32 minMemType42;
+ UInt32 maxMemType42;
+ UInt32 minMemType1;
+ UInt32 maxMemType1;
+};
+
+#include "appleGlyphNames.c"
+
+#define sfntCacheDir "/Library/Caches/Type1-sfnt-fonts/"
+#define sfntWrapCommand "T1Wrap"
+#define sfntWrapSuffix "-sfnt.otf"
+
+static ATSFontRef
+activatePFB(const char* pfbName)
+{
+ static std::map<std::string,ATSFontRef> sActivatedFonts;
+
+ std::map<std::string,ATSFontRef>::iterator i = sActivatedFonts.find(pfbName);
+ if (i != sActivatedFonts.end())
+ return i->second;
+
+ ATSFontRef fontRef = 0;
+ OSStatus status = noErr;
+
+ static int firstTime = 1;
+ if (firstTime) {
+ firstTime = 0;
+ status = mkdir(sfntCacheDir, S_IRWXU | S_IRWXG | S_IRWXO);
+ if (status != 0) {
+ if (errno == EEXIST) {
+ // clean up possible residue from past failed conversions
+ system("/usr/bin/find " sfntCacheDir " -maxdepth 1 -empty -type f -delete");
+ status = 0;
+ }
+ else
+ fprintf(stderr, "*** failed to create sfnt cache directory %s (errno = %d), cannot activate .pfb fonts\n",
+ sfntCacheDir, errno);
+ }
+ }
+
+ char* sfntName = new char[strlen(sfntCacheDir) + strlen(pfbName) + strlen(sfntWrapSuffix) + 1];
+ strcpy(sfntName, sfntCacheDir);
+ strcat(sfntName, pfbName);
+ strcat(sfntName, sfntWrapSuffix);
+
+ FSRef ref;
+ status = FSPathMakeRef((const UInt8*)sfntName, &ref, NULL);
+ if (status == fnfErr) {
+ char* pathName = (char*)kpse_find_file((UInt8*)pfbName, xdv_kpse_pfb_format, true);
+ if (pathName != NULL) {
+ char* cmd = new char[strlen(sfntWrapCommand) + strlen(pathName) + strlen(sfntName) + 6];
+ strcpy(cmd, sfntWrapCommand " ");
+ strcat(cmd, pathName);
+ strcat(cmd, " > ");
+ strcat(cmd, sfntName);
+ status = system(cmd);
+ delete[] cmd;
+ free(pathName);
+ }
+ }
+
+ if (status == noErr)
+ fontRef = activateFromPath(sfntName);
+
+ delete[] sfntName;
+
+ if (fontRef == 0) {
+ // try removing extension (.pfb) and looking for an .otf font...
+ char* baseName = new char[strlen(pfbName) + 1];
+ strcpy(baseName, pfbName);
+ char* dot = strrchr(baseName, '.');
+ if (dot != NULL)
+ *dot = 0;
+ char* pathName = (char*)kpse_find_file((UInt8*)baseName, xdv_kpse_otf_format, true);
+ delete[] baseName;
+ if (pathName != NULL) {
+ fontRef = activateFromPath(pathName);
+ free(pathName);
+ }
+ }
+
+ if (fontRef == 0)
+ fprintf(stderr, "\n*** font activation failed (status=%d): %s\n", status, pfbName);
+
+ sActivatedFonts[pfbName] = fontRef;
+ return fontRef;
+}
+
+static std::vector<UInt16>*
+readMacRomanCmap(ATSFontRef fontRef)
+{
+ std::vector<UInt16>* cmap = NULL;
+ ByteCount size;
+ OSStatus status = ATSFontGetTable(fontRef, 'cmap', 0, 0, 0, &size);
+ if (status == noErr) {
+ UInt8* buffer = new UInt8[size];
+ ATSFontGetTable(fontRef, 'cmap', 0, size, buffer, &size);
+
+ struct cmapHeader {
+ UInt16 version;
+ UInt16 numSubtables;
+ };
+ struct subtableHeader {
+ UInt16 platform;
+ UInt16 encoding;
+ UInt32 offset;
+ };
+ struct format0 {
+ UInt16 format;
+ UInt16 length;
+ UInt16 language;
+ UInt8 glyphIndex[256];
+ };
+ struct format6 {
+ UInt16 format;
+ UInt16 length;
+ UInt16 language;
+ UInt16 firstCode;
+ UInt16 entryCount;
+ UInt16 glyphIndex[1];
+ };
+
+ struct cmapHeader* h = (struct cmapHeader*)buffer;
+ struct subtableHeader* sh = (struct subtableHeader*)(buffer + sizeof(struct cmapHeader));
+ int subtable = 0;
+ cmap = new std::vector<UInt16>;
+ cmap->reserve(256);
+ while (subtable < SWAP(h->numSubtables)) {
+ if ((SWAP(sh->platform) == 1) && (SWAP(sh->encoding) == 0)) {
+ struct format0* f0 = (struct format0*)(buffer + SWAP(sh->offset));
+ if (SWAP(f0->format) == 0) {
+ for (int ch = 0; ch < 256; ++ch) {
+ cmap->push_back(SWAP(f0->glyphIndex[ch]));
+ }
+ }
+ else if (SWAP(f0->format) == 6) {
+ struct format6* f6 = (struct format6*)f0;
+ for (int ch = 0; ch < 256; ++ch) {
+ if ((ch < SWAP(f6->firstCode)) || (ch >= SWAP(f6->firstCode) + SWAP(f6->entryCount)))
+ cmap->push_back(0);
+ else
+ cmap->push_back(SWAP(f6->glyphIndex[ch - SWAP(f6->firstCode)]));
+ }
+ }
+ else {
+ // unsupported cmap subtable format
+ fprintf(stderr, "\n*** unsupported 'cmap' subtable format (%d)\n", SWAP(f0->format));
+ }
+ break;
+ }
+ ++subtable;
+ ++sh;
+ }
+
+ delete[] buffer;
+ }
+
+ return cmap;
+}
+
+static const fontMapRec*
+getFontRec(const std::string& name)
+{
+ if (!sMapFileLoaded)
+ doPdfMapFile("psfonts.map");
+
+ std::map<std::string,fontMapRec>::iterator i = psFontsMap.find(name);
+ if (i == psFontsMap.end())
+ return NULL;
+
+ fontMapRec& fr = i->second;
+ if (fr.loaded)
+ return &fr;
+
+ ATSFontRef fontRef = 0;
+ // if a filename is known, try to find and activate it
+
+ if (fr.pfbName.length() > 0)
+ fontRef = activatePFB(fr.pfbName.c_str());
+
+ // if no downloadable file, see if it's installed in the OS
+ if (fontRef == 0)
+ fontRef = ATSFontFindFromPostScriptName(CFStringCreateWithCStringNoCopy(kCFAllocatorDefault, fr.psName.c_str(),
+ CFStringGetSystemEncoding(), kCFAllocatorNull), kATSOptionFlagsDefault);
+
+ if (fontRef == 0)
+ fprintf(stderr, "\n*** font %s (%s: file '%s') not found\n", name.c_str(), fr.psName.c_str(), fr.pfbName.c_str());
+
+ if (fontRef != 0) {
+ // if re-encoding was called for, load the encoding vector and build a new cmap
+ if (fr.encName.length() > 0) {
+ const encoding* enc = getEncoding(fr.encName);
+ if (enc != 0) {
+ ByteCount size;
+ OSStatus status = ATSFontGetTable(fontRef, 'post', 0, 0, 0, &size);
+ if (status == noErr) {
+ UInt8* buffer = new UInt8[size];
+ ATSFontGetTable(fontRef, 'post', 0, size, buffer, &size);
+ postTable* p = (postTable*)&buffer[0];
+ std::map<std::string,UInt16> name2gid;
+ UInt16 g = 0;
+ switch (SWAP(p->format)) {
+ case 0x00010000:
+ {
+ char* cp;
+ while ((cp = appleGlyphNames[g]) != 0) {
+ name2gid[cp] = g;
+ ++g;
+ }
+ }
+ break;
+
+ case 0x00020000:
+ {
+ UInt16* n = (UInt16*)(p + 1);
+ UInt16 numGlyphs = SWAP(*n++);
+ UInt8* ps = (UInt8*)(n + numGlyphs);
+ std::vector<std::string> newNames;
+ while (ps < buffer + size) {
+ newNames.push_back(std::string((char*)ps + 1, *ps));
+ ps += *ps + 1;
+ }
+ for (g = 0; g < numGlyphs; ++g) {
+ if (SWAP(*n) < 258)
+ name2gid[appleGlyphNames[SWAP(*n)]] = g;
+ else
+ name2gid[newNames[SWAP(*n) - 258]] = g;
+ ++n;
+ }
+ }
+ break;
+
+ case 0x00028000:
+ fprintf(stderr, "\n*** format 2.5 'post' table not supported\n");
+ break;
+
+ case 0x00030000:
+ // TODO: see if it's a CFF OpenType font, and if so, get the glyph names from the CFF data
+ fprintf(stderr, "\n*** format 3 'post' table; cannot reencode font %s\n", name.c_str());
+ break;
+
+ case 0x00040000:
+ fprintf(stderr, "\n*** format 4 'post' table not supported\n");
+ break;
+
+ default:
+ fprintf(stderr, "\n*** unknown 'post' table format %08x\n");
+ break;
+ }
+ if (fr.cmap != NULL)
+ delete fr.cmap;
+ fr.cmap = new std::vector<UInt16>;
+ for (encoding::const_iterator i = enc->begin(); i != enc->end(); ++i) {
+ std::map<std::string,UInt16>::const_iterator g = name2gid.find(*i);
+ if (g == name2gid.end())
+ fr.cmap->push_back(0);
+ else
+ fr.cmap->push_back(g->second);
+ }
+ }
+ else {
+ fprintf(stderr, "\n*** no 'post' table found, unable to re-encode font %s\n", name.c_str());
+ }
+ }
+ }
+ else {
+ // read the MacOS/Roman cmap, if available
+ std::vector<UInt16>* cmap = readMacRomanCmap(fontRef);
+ if (fr.cmap != NULL)
+ delete fr.cmap;
+ fr.cmap = cmap;
+ }
+ }
+
+ if (fontRef == 0) {
+ fprintf(stderr, "\n*** font %s (%s) not found, will substitute Helvetica glyphs\n", name.c_str(), fr.pfbName.c_str());
+ fontRef = ATSFontFindFromPostScriptName(CFSTR("Helvetica"), kATSOptionFlagsDefault);
+ if (fr.cmap != NULL)
+ delete fr.cmap;
+ fr.cmap = readMacRomanCmap(fontRef);
+ }
+
+ fr.cgFont = getCGFontForATSFont(fontRef);
+ fr.loaded = true;
+
+ return &fr;
+}
+
+static void
+doFontDef(FILE* xdv, int k)
+{
+ OSStatus status;
+
+ UInt32 f = readUnsigned(xdv, k); // font number we're defining
+ UInt32 c = readUnsigned(xdv, 4); // TFM checksum
+ Fixed s = readUnsigned(xdv, 4); // at size
+ Fixed d = readUnsigned(xdv, 4); // design size
+
+ UInt16 alen = readUnsigned(xdv, 1); // area length
+ UInt16 nlen = readUnsigned(xdv, 1); // name length
+
+ UInt8* name = new UInt8[alen + nlen + 2];
+ if (alen > 0) {
+ fread(name, 1, alen, xdv);
+ name[alen] = '/';
+ fread(name + alen + 1, 1, nlen, xdv);
+ nlen += alen + 1;
+ }
+ else
+ fread(name, 1, nlen, xdv);
+ name[nlen] = 0;
+
+ texFont font;
+ loadMetrics(font, name, d, s);
+
+ if (alen > 0)
+ name = name + alen + 1; // point past the area to get the name by itself for the remaining operations
+
+ ATSFontRef fontRef;
+
+ // look for a map entry for this font name
+ std::string nameStr((char*)name);
+ const fontMapRec* fr = getFontRec(nameStr);
+ if (fr != NULL) {
+ font.cgFont = fr->cgFont;
+ if (fr->cmap->size() > 0) {
+ if (font.charMap != NULL)
+ delete font.charMap;
+ font.charMap = fr->cmap; // replacing map that was synthesized from the tfm coverage
+ }
+ }
+ else {
+ /* try to find the font without the benefit of psfonts.map...
+ first try the name as a pfb or otf filename
+ and then as the PS name of an installed font
+ */
+
+ // ****** FIXME ****** this needs re-working to read the 'cmap' properly
+
+ fontRef = ATSFontFindFromPostScriptName(CFStringCreateWithCStringNoCopy(kCFAllocatorDefault, (char*)name,
+ CFStringGetSystemEncoding(), kCFAllocatorNull), kATSOptionFlagsDefault);
+ UInt8* ucname = 0;
+ if (fontRef == 0) {
+ ucname = uppercasify(name);
+ fontRef = ATSFontFindFromPostScriptName(CFStringCreateWithCStringNoCopy(kCFAllocatorDefault, (char*)ucname,
+ CFStringGetSystemEncoding(), kCFAllocatorNull), kATSOptionFlagsDefault);
+ }
+
+ if (ucname != 0)
+ free(ucname);
+
+ if (fontRef == 0) {
+ fprintf(stderr, "\n*** font %s not found in psfonts.map or host system; will substitute Helvetica glyphs\n", (char*)name);
+ fontRef = ATSFontFindFromPostScriptName(CFSTR("Helvetica"), kATSOptionFlagsDefault);
+ if (font.charMap != NULL)
+ delete font.charMap;
+ font.charMap = readMacRomanCmap(fontRef);
+ }
+
+ font.cgFont = getCGFontForATSFont(fontRef);
+
+ delete[] name;
+ }
+
+ font.size = s;
+ gTeXFonts.insert(std::pair<const UInt32,texFont>(f, font));
+}
+
+static void
+variationWarning()
+{
+ static bool alreadyWarned = false;
+ if (alreadyWarned)
+ return;
+ fprintf(stderr, "\n"
+ "## xdv2pdf: AAT variation axes/multiple master fonts not supported on this system\n"
+ "## (at least Mac OS X 10.4 is required)\n");
+ alreadyWarned = true;
+}
+
+static void
+doNativeFontDef(FILE* xdv)
+{
+ UInt32 f = readUnsigned(xdv, 4); // font ID
+ Fixed s = X2Fix(Fix2X(readUnsigned(xdv, 4)) * 72 / 72.27); // size, converted to "big" points
+ if (sMag != 1000)
+ s = X2Fix(gMagScale * Fix2X(s));
+ UInt16 flags = readUnsigned(xdv, 2);
+
+ int psLen = readUnsigned(xdv, 1); // PSName length
+ int skipLen = readUnsigned(xdv, 1); // family name length
+ skipLen += readUnsigned(xdv, 1); // style name length
+
+ char* name = new char[psLen+1];
+ fread(name, 1, psLen, xdv);
+ name[psLen] = 0;
+
+ // don't use fseek, doesn't work when reading from a pipe!
+ while (skipLen-- > 0)
+ (void)readUnsigned(xdv, 1);
+
+ UInt32 rgba = 0x000000FF;
+ if (flags & XDV_FLAG_COLORED)
+ rgba = readUnsigned(xdv, 4);
+
+ ATSFontRef fontRef = 0;
+ ATSUFontID fontID = kATSUInvalidFontID;
+
+ if (name[0] == '[') { // uninstalled font specified by filename: not supported
+ fprintf(stderr, "\n"
+ "## xdv2pdf: use of uninstalled fonts (specified by filename) such as\n"
+ "## %s\n"
+ "## is not supported; try using the xdvipdfmx driver instead.\n", name);
+ }
+ else {
+ CFStringRef psNameStr = CFStringCreateWithCString(kCFAllocatorDefault, name, kCFStringEncodingMacRoman);
+ fontRef = ATSFontFindFromPostScriptName(psNameStr, kATSOptionFlagsDefault);
+ CFRelease(psNameStr);
+
+ fontID = FMGetFontFromATSFontRef(fontRef);
+ }
+
+ if (fontID == kATSUInvalidFontID)
+ rgba = 0xFF0000FF;
+
+ CGFontRef cgFont = getCGFontForATSFont(fontRef);
+
+ OSStatus status = noErr;
+
+ if (flags & XDV_FLAG_VARIATIONS) {
+ int n = readUnsigned(xdv, 2); // number of variations
+ if (n > 0) {
+ bool applyVars = true;
+ CFStringRef* axes = new CFStringRef[n];
+ for (int i = 0; i < n; ++i) {
+ UInt32 axis = readUnsigned(xdv, 4);
+ FontNameCode nameCode;
+ status = ATSUGetFontVariationNameCode(fontID, axis, &nameCode);
+ ByteCount len;
+ status = ATSUFindFontName(fontID, nameCode, kFontMacintoshPlatform, kFontRomanScript,
+ (FontLanguageCode)kFontNoLanguage, 0, NULL, &len, NULL);
+ if (status == noErr) {
+ char* buf = new char[len + 1];
+ status = ATSUFindFontName(fontID, nameCode, kFontMacintoshPlatform, kFontRomanScript,
+ (FontLanguageCode)kFontNoLanguage, len, buf, &len, NULL);
+ buf[len] = 0;
+ axes[i] = CFStringCreateWithCString(kCFAllocatorDefault, buf, kCFStringEncodingMacRoman);
+ delete[] buf;
+ }
+ else
+ applyVars = false;
+ }
+
+ CFNumberRef* values = new CFNumberRef[n];
+ for (int i = 0; i < n; ++i) {
+ SInt32 v = readSigned(xdv, 4);
+ Float32 f = Fix2X(v);
+ values[i] = CFNumberCreate(kCFAllocatorDefault, kCFNumberFloat32Type, &f);
+ }
+
+ if (&CGFontCreateCopyWithVariations != NULL) {
+ CFDictionaryRef variations = CFDictionaryCreate(kCFAllocatorDefault,
+ (const void**)axes, (const void**)values, n,
+ &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
+
+ cgFont = CGFontCreateCopyWithVariations(cgFont, variations);
+ CFRelease(variations);
+ }
+ else
+ variationWarning();
+
+ for (int i = 0; i < n; ++i) {
+ CFRelease(axes[i]);
+ CFRelease(values[i]);
+ }
+ delete[] values;
+ delete[] axes;
+ }
+ }
+
+ nativeFont fontRec;
+ fontRec.cgFont = cgFont;
+ fontRec.atsFont = fontRef;
+ fontRec.size = s;
+ fontRec.isColored = (flags & XDV_FLAG_COLORED) != 0 || fontID == kATSUInvalidFontID;
+ fontRec.isVertical = (flags & XDV_FLAG_VERTICAL) != 0;
+ if (fontRec.isColored) {
+ float values[4];
+ values[0] = (rgba >> 24) / 255.0;
+ values[1] = ((rgba >> 16) & 0xFF) / 255.0;
+ values[2] = ((rgba >> 8) & 0xFF) / 255.0;
+ values[3] = ( rgba & 0xFF) / 255.0;
+ fontRec.color = CGColorCreate(gRGBColorSpace, values);
+ }
+ else
+ fontRec.color = NULL;
+
+ if (fontRec.isVertical) {
+ ATSUStyle style;
+ status = ATSUCreateStyle(&style);
+ ATSUVerticalCharacterType vert = kATSUStronglyHorizontal;
+ ATSUAttributeTag tags[] = { kATSUFontTag, kATSUSizeTag, kATSUVerticalCharacterTag };
+ ByteCount sizes[] = { sizeof(fontID), sizeof(s), sizeof(vert) };
+ ATSUAttributeValuePtr values[] = { &fontID, &s, &vert };
+ ItemCount numTags = sizeof(tags) / sizeof(ATSUAttributeTag);
+ status = ATSUSetAttributes(style, numTags, tags, sizes, values);
+ fontRec.atsuStyleH = style;
+
+ status = ATSUCreateStyle(&style);
+ vert = kATSUStronglyVertical;
+ status = ATSUSetAttributes(style, numTags, tags, sizes, values);
+ fontRec.atsuStyleV = style;
+ }
+
+ sNativeFonts.insert(std::pair<const UInt32,nativeFont>(f, fontRec));
+}
+
+static void
+processPage(FILE* xdv)
+{
+ /* enter here having just read BOP from xdv file */
+ ++gPageIndex;
+
+ OSStatus status;
+ int i;
+
+ std::list<dviVars> stack;
+ gDviLevel = 0;
+
+ int j = 0;
+ for (i = 0; i < 10; ++i) { // read counts
+ gCounts[i] = readSigned(xdv, 4);
+ if (gCounts[i] != 0)
+ j = i;
+ }
+ if (sVerbose) {
+ fprintf(stderr, "[");
+ for (i = 0; i < j; ++i)
+ fprintf(stderr, "%d.", gCounts[i]);
+ fprintf(stderr, "%d", gCounts[j]);
+ }
+
+ (void)readUnsigned(xdv, 4); // skip prev-BOP pointer
+
+ cur_cgFont = kUndefinedFont;
+ dvi.h = dvi.v = 0;
+ dvi.w = dvi.x = dvi.y = dvi.z = 0;
+ f = 0;
+
+ unsigned int cmd = DVI_BOP;
+ while (cmd != DVI_EOP) {
+ UInt32 u;
+ SInt32 s;
+ SInt32 ht, wd;
+ int k = 1;
+
+ cmd = readUnsigned(xdv, 1);
+ switch (cmd) {
+ default:
+ if (cmd < DVI_SET1)
+ setChar(cmd, true);
+ else if (cmd >= DVI_FNTNUM0 && cmd <= DVI_FNTNUM0 + 63)
+ f = cmd - DVI_FNTNUM0;
+ else
+ goto ABORT_PAGE;
+ break;
+
+ case DVI_EOP:
+ flushGlyphs();
+ break;
+
+ case DVI_NOP:
+ break;
+
+ case DVI_FNT4:
+ ++k;
+ case DVI_FNT3:
+ ++k;
+ case DVI_FNT2:
+ ++k;
+ case DVI_FNT1:
+ f = readUnsigned(xdv, k); // that's it: just set |f|
+ break;
+
+ case DVI_XXX4:
+ ++k;
+ case DVI_XXX3:
+ ++k;
+ case DVI_XXX2:
+ ++k;
+ case DVI_XXX1:
+ flushGlyphs();
+ u = readUnsigned(xdv, k);
+ if (u > 0) {
+ char* special = new char[u+1];
+ fread(special, 1, u, xdv);
+ special[u] = '\0';
+
+ doSpecial(special);
+
+ delete[] special;
+ }
+ break;
+
+ case DVI_SETRULE:
+ case DVI_PUTRULE:
+ ensurePageStarted();
+ ht = readSigned(xdv, 4);
+ wd = readSigned(xdv, 4);
+ if (ht > 0 && wd > 0) {
+ CGRect r = CGRectMake(kDvi2Scr * dvi.h, kDvi2Scr * (gPageHt - dvi.v), kDvi2Scr * wd, kDvi2Scr * ht);
+ setColor(gRuleColor);
+ CGContextFillRect(gCtx, r);
+ if (gTrackingBoxes) {
+ box b = {
+ dvi.h,
+ dvi.v,
+ dvi.h + wd,
+ dvi.v - ht };
+ mergeBox(b);
+ }
+ }
+ if (cmd == DVI_SETRULE)
+ dvi.h += wd;
+ break;
+
+ case DVI_SET4:
+ ++k;
+ case DVI_SET3:
+ ++k;
+ case DVI_SET2:
+ ++k;
+ case DVI_SET1:
+ u = readUnsigned(xdv, k);
+ setChar(u, true);
+ break;
+
+ case DVI_PUT4:
+ ++k;
+ case DVI_PUT3:
+ ++k;
+ case DVI_PUT2:
+ ++k;
+ case DVI_PUT1:
+ u = readUnsigned(xdv, k);
+ setChar(u, false);
+ break;
+
+ case DVI_PUSH:
+ stack.push_back(dvi);
+ ++gDviLevel;
+ break;
+
+ case DVI_POP:
+ if (gDviLevel == gTagLevel)
+ flushAnnotBox();
+ --gDviLevel;
+ dvi = stack.back();
+ stack.pop_back();
+ break;
+
+ case DVI_RIGHT4:
+ ++k;
+ case DVI_RIGHT3:
+ ++k;
+ case DVI_RIGHT2:
+ ++k;
+ case DVI_RIGHT1:
+ s = readSigned(xdv, k);
+ dvi.h += s;
+ break;
+
+ case DVI_DOWN4:
+ ++k;
+ case DVI_DOWN3:
+ ++k;
+ case DVI_DOWN2:
+ ++k;
+ case DVI_DOWN1:
+ s = readSigned(xdv, k);
+ dvi.v += s;
+ break;
+
+ case DVI_W4:
+ ++k;
+ case DVI_W3:
+ ++k;
+ case DVI_W2:
+ ++k;
+ case DVI_W1:
+ s = readSigned(xdv, k);
+ dvi.w = s;
+ case DVI_W0:
+ dvi.h += dvi.w;
+ break;
+
+ case DVI_X4:
+ ++k;
+ case DVI_X3:
+ ++k;
+ case DVI_X2:
+ ++k;
+ case DVI_X1:
+ s = readSigned(xdv, k);
+ dvi.x = s;
+ case DVI_X0:
+ dvi.h += dvi.x;
+ break;
+
+ case DVI_Y4:
+ ++k;
+ case DVI_Y3:
+ ++k;
+ case DVI_Y2:
+ ++k;
+ case DVI_Y1:
+ s = readSigned(xdv, k);
+ dvi.y = s;
+ case DVI_Y0:
+ dvi.v += dvi.y;
+ break;
+
+ case DVI_Z4:
+ ++k;
+ case DVI_Z3:
+ ++k;
+ case DVI_Z2:
+ ++k;
+ case DVI_Z1:
+ s = readSigned(xdv, k);
+ dvi.z = s;
+ case DVI_Z0:
+ dvi.v += dvi.z;
+ break;
+
+ case XDV_GLYPH_ARRAY:
+ ensurePageStarted();
+ doGlyphArray(xdv, true);
+ break;
+
+ case XDV_GLYPH_STRING:
+ ensurePageStarted();
+ doGlyphArray(xdv, false);
+ break;
+
+ case XDV_PIC_FILE:
+ ensurePageStarted();
+ k = readUnsigned(xdv, 1); /* 0 for QT picfile, or PDF box code (1..5) */
+ doPicFile(xdv, k);
+ break;
+
+ case DVI_FNTDEF4:
+ ++k;
+ case DVI_FNTDEF3:
+ ++k;
+ case DVI_FNTDEF2:
+ ++k;
+ case DVI_FNTDEF1:
+ doFontDef(xdv, k);
+ break;
+
+ case XDV_NATIVE_FONT_DEF:
+ doNativeFontDef(xdv);
+ break;
+ }
+ }
+
+ ensurePageStarted(); // needed for completely blank pages!
+
+ if (sVerbose) {
+ fprintf(stderr, "]%s", (gPageIndex % 10) == 0 ? "\n" : "");
+ }
+
+ABORT_PAGE:
+
+ if (gPageStarted) {
+ CGContextEndPage(gCtx);
+ CGContextRestoreGState(gCtx);
+ }
+
+ gPageStarted = false;
+}
+
+#if MAC_OS_X_VERSION_MIN_REQUIRED < MAC_OS_X_VERSION_10_4
+/* From Apple Technical Q&A 1396, Listing 2 */
+static CGColorSpaceRef
+CreateICCColorSpaceFromPathToProfile(const char* iccProfilePath)
+{
+ CMProfileRef iccProfile = (CMProfileRef)0;
+ CGColorSpaceRef iccColorSpace = NULL;
+ CMProfileLocation loc;
+
+ // Specify that the location of the profile will be a POSIX path to the profile.
+ loc.locType = cmPathBasedProfile;
+
+ // Make sure the path is not larger then the buffer
+ if (strlen(iccProfilePath) > sizeof(loc.u.pathLoc.path))
+ return NULL;
+
+ // Copy the path the profile into the CMProfileLocation structure
+ strcpy(loc.u.pathLoc.path, iccProfilePath);
+
+ // Open the profile
+ if (CMOpenProfile(&iccProfile, &loc) != noErr) {
+ iccProfile = (CMProfileRef)0;
+ return NULL;
+ }
+
+ // Create the ColorSpace with the open profile.
+ iccColorSpace = CGColorSpaceCreateWithPlatformColorSpace(iccProfile);
+
+ // Close the profile now that we have what we need from it.
+ CMCloseProfile(iccProfile);
+
+ return iccColorSpace;
+}
+
+static CGColorSpaceRef
+CreateColorSpaceFromSystemICCProfileName(CFStringRef profileName)
+{
+ FSRef pathToProfilesFolder;
+ FSRef pathToProfile;
+
+ // Find the Systems Color Sync Profiles folder
+ if (FSFindFolder(kOnSystemDisk, kColorSyncProfilesFolderType,
+ kDontCreateFolder, &pathToProfilesFolder) == noErr) {
+
+ // Make a UniChar string of the profile name
+ UniChar uniBuffer[sizeof(CMPathLocation)];
+ CFStringGetCharacters(profileName, CFRangeMake(0, CFStringGetLength(profileName)), uniBuffer);
+
+ // Create a FSRef to the profile in the Systems Color Sync Profile folder
+ if (FSMakeFSRefUnicode(&pathToProfilesFolder, CFStringGetLength(profileName), uniBuffer,
+ kUnicodeUTF8Format, &pathToProfile) == noErr) {
+ char path[sizeof(CMPathLocation)];
+
+ // Write the posix path to the profile into our path buffer from the FSRef
+ if (FSRefMakePath(&pathToProfile, (UInt8*)path, sizeof(CMPathLocation)) == noErr)
+ return CreateICCColorSpaceFromPathToProfile(path);
+ }
+ }
+
+ return NULL;
+}
+/* end of Apple Q&A code */
+#endif
+
+static void
+processAllPages(FILE* xdv)
+{
+ // initialize some global variables that we use as CG "constants"
+#if MAC_OS_X_VERSION_MIN_REQUIRED < MAC_OS_X_VERSION_10_4
+ if (&kCGColorSpaceGenericRGB == NULL) {
+ gRGBColorSpace = CreateColorSpaceFromSystemICCProfileName(CFSTR("Generic RGB Profile.icc"));
+ gCMYKColorSpace = CreateColorSpaceFromSystemICCProfileName(CFSTR("Generic CMYK Profile.icc"));
+ gGrayColorSpace = CreateColorSpaceFromSystemICCProfileName(CFSTR("Generic Gray Profile.icc"));
+ }
+ else
+#endif
+ {
+ gRGBColorSpace = CGColorSpaceCreateWithName(kCGColorSpaceGenericRGB);
+ gCMYKColorSpace = CGColorSpaceCreateWithName(kCGColorSpaceGenericCMYK);
+ gGrayColorSpace = CGColorSpaceCreateWithName(kCGColorSpaceGenericGray);
+ }
+
+ if (gRGBColorSpace == NULL || gCMYKColorSpace == NULL || gGrayColorSpace == NULL) {
+ fprintf(stderr, "\n*** unable to initialize color spaces.... something's badly broken\n");
+ exit(1);
+ }
+
+ const float black[2] = { 0.0, 1.0 };
+ kBlackColor = CGColorCreate(gGrayColorSpace, black);
+
+ gPageIndex = 0;
+ unsigned int cmd;
+ while ((cmd = readUnsigned(xdv, 1)) != DVI_POST) {
+ switch (cmd) {
+ case DVI_BOP:
+ processPage(xdv);
+ break;
+
+ case DVI_NOP:
+ break;
+
+ default:
+ fprintf(stderr, "\n*** unexpected DVI command: %d\n", cmd);
+ exit(1);
+ }
+ }
+ if (sVerbose && (gPageIndex % 10) != 0)
+ fprintf(stderr, "\n");
+}
+
+const char* progName;
+static void
+usage()
+{
+ fprintf(stderr, "usage: %s [-m mag] [-p papersize[:landscape]] [-v] [-o pdfFile] xdvFile\n", progName);
+ fprintf(stderr, " papersize values: ");
+ paperSizeRec* ps = &gPaperSizes[0];
+ while (ps->name != 0) {
+ fprintf(stderr, "%s/", ps->name);
+ ++ps;
+ }
+ fprintf(stderr, "wd,ht [in 'big' points or with explicit units]\n");
+}
+
+int
+xdv2pdf(int argc, char** argv)
+{
+ OSStatus status;
+
+ progName = argv[0];
+
+ gMagScale = 1.0;
+ gRuleColor = kBlackColor;
+ gTextColor = gRuleColor;
+
+ gTagLevel = -1;
+
+ double_t paperWd = 0.0;
+ double_t paperHt = 0.0;
+
+ int ch;
+ while ((ch = getopt(argc, argv, "o:p:m:d:hv" /*r:*/)) != -1) {
+ switch (ch) {
+ case 'o':
+ {
+ CFStringRef outFileName = CFStringCreateWithCString(kCFAllocatorDefault, optarg, kCFStringEncodingUTF8);
+ gSaveURL = CFURLCreateWithFileSystemPath(kCFAllocatorDefault, outFileName, kCFURLPOSIXPathStyle, false);
+ CFRelease(outFileName);
+ }
+ break;
+
+ case 'p':
+ if (!getPaperSize(optarg, paperWd, paperHt)) {
+ fprintf(stderr, "*** unknown paper name: %s\n", optarg);
+ exit(1);
+ }
+ break;
+
+ case 'm':
+ sMag = atoi(optarg);
+ break;
+
+ case 'd':
+ kpathsea_debug |= atoi(optarg);
+ break;
+
+ case 'v':
+ sVerbose = true;
+ break;
+
+ case 'h':
+ usage();
+ exit(0);
+ }
+ }
+
+ if ((paperWd == 0.0) || (paperHt == 0.0)) {
+ // get default paper size from printing system
+ PMRect paperRect = { 0, 0, 792, 612 };
+ PMPrintSession printSession;
+ PMPrintSettings printSettings;
+ PMPageFormat pageFormat;
+ status = PMCreateSession(&printSession);
+ if (status == noErr) {
+ status = PMCreatePrintSettings(&printSettings);
+ if (status == noErr) {
+ status = PMSessionDefaultPrintSettings(printSession, printSettings);
+ status = PMCreatePageFormat(&pageFormat);
+ if (status == noErr) {
+ status = PMSessionDefaultPageFormat(printSession, pageFormat);
+ PMGetUnadjustedPaperRect(pageFormat, &paperRect);
+ status = PMRelease(pageFormat);
+ }
+ status = PMRelease(printSettings);
+ }
+ status = PMRelease(printSession);
+ }
+ paperWd = paperRect.right - paperRect.left;
+ paperHt = paperRect.bottom - paperRect.top;
+ }
+
+ // set the media box for PDF generation
+ gMediaBox = CGRectMake(0, 0, paperWd, paperHt);
+
+ argc -= optind;
+ argv += optind;
+ if (argc == 1 && gSaveURL == 0) {
+ CFStringRef inFileName = CFStringCreateWithCString(kCFAllocatorDefault, argv[0], CFStringGetSystemEncoding());
+ CFURLRef inURL = CFURLCreateWithFileSystemPath(kCFAllocatorDefault, inFileName, kCFURLPOSIXPathStyle, false);
+ CFRelease(inFileName);
+ CFURLRef tmpURL = CFURLCreateCopyDeletingPathExtension(kCFAllocatorDefault, inURL);
+ CFRelease(inURL);
+ gSaveURL = CFURLCreateCopyAppendingPathExtension(kCFAllocatorDefault, tmpURL, CFSTR("pdf"));
+ CFRelease(tmpURL);
+ }
+
+ if (argc > 1 || gSaveURL == 0) {
+ usage();
+ exit(1);
+ }
+
+ FILE* xdv;
+ if (argc == 1)
+ xdv = fopen(argv[0], "r");
+ else
+ xdv = stdin;
+
+ if (xdv != NULL) {
+ // read the preamble
+ unsigned b = readUnsigned(xdv, 1);
+ if (b != DVI_PRE) { fprintf(stderr, "*** bad XDV file: DVI_PRE not found, b=%d\n", b); exit(1); }
+ b = readUnsigned(xdv, 1);
+ if (b != XDV_ID) { fprintf(stderr, "*** bad XDV file: version=%d, expected %d\n", b, XDV_ID); exit(1); }
+ (void)readUnsigned(xdv, 4); // num
+ (void)readUnsigned(xdv, 4); // den
+ if (sMag == 0)
+ sMag = readUnsigned(xdv, 4); // sMag
+ else
+ (void)readUnsigned(xdv, 4);
+
+ unsigned numBytes = readUnsigned(xdv, 1); // length of output comment
+ UInt8* bytes = new UInt8[numBytes];
+ fread(bytes, 1, numBytes, xdv);
+
+ CFStringRef keys[2] = { CFSTR("Creator") , CFSTR("Title") /*, CFSTR("Author")*/ };
+ CFStringRef values[2] = { CFSTR("xdv2pdf") , 0 /*, 0*/ };
+ values[1] = CFStringCreateWithBytes(kCFAllocatorDefault, bytes, numBytes, CFStringGetSystemEncoding(), false);
+ delete[] bytes;
+// values[2] = CFStringCreateWithCString(kCFAllocatorDefault, getlogin(), CFStringGetSystemEncoding());
+ CFDictionaryRef auxInfo = CFDictionaryCreate(kCFAllocatorDefault, (const void**)keys, (const void**)values, sizeof(keys) / sizeof(keys[0]),
+ &kCFCopyStringDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
+ CFRelease(values[1]);
+// CFRelease(values[2]);
+
+ // create PDF graphics context
+ gCtx = CGPDFContextCreateWithURL(gSaveURL, &gMediaBox, auxInfo);
+
+ // handle magnification
+ if (sMag != 1000) {
+ gMagScale = sMag / 1000.0;
+ kScr2Dvi /= gMagScale;
+ kDvi2Scr *= gMagScale;
+ CGContextScaleCTM(gCtx, gMagScale, gMagScale);
+ }
+
+ // draw all the pages
+ processAllPages(xdv);
+
+ CGContextRelease(gCtx);
+
+ while (getc(xdv) != EOF)
+ ;
+ fclose(xdv);
+ }
+
+ if (gPdfMarkFile != NULL) {
+ fclose(gPdfMarkFile);
+ char pdfPath[_POSIX_PATH_MAX+1];
+ Boolean gotPath = CFURLGetFileSystemRepresentation(gSaveURL, true, (UInt8*)pdfPath, _POSIX_PATH_MAX);
+ CFRelease(gSaveURL);
+#if 0
+ if (gotPath) {
+ char* mergeMarks = "xdv2pdf_mergemarks";
+ execlp(mergeMarks, mergeMarks, pdfPath, gPdfMarkPath, 0); // should not return!
+ status = errno;
+ }
+ fprintf(stderr, "*** failed to run xdv2pdf_mergemarks: status = %d\n", status);
+#else
+ if (gotPath) {
+ char* mergeMarks = "xdv2pdf_mergemarks";
+ char cmd[_POSIX_PATH_MAX*2 + 100];
+ sprintf(cmd, "%s \"%s\" \"%s\"", mergeMarks, pdfPath, gPdfMarkPath);
+ status = system(cmd);
+ }
+ else
+ status = fnfErr;
+ if (status != 0)
+ fprintf(stderr, "*** failed to run xdv2pdf_mergemarks: status = %d\n", status);
+#endif
+ }
+ else
+ CFRelease(gSaveURL);
+
+ return status;
+}