This is PictConverter.m in view mode; [Download] [Up]
/***********************************************************************\ Converter class for Convert PICT which converts graphics from PICT to eps formats. Copyright (C) 1993 David John Burrowes This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 1, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. The author, David John Burrowes, can be reached at: davidjohn@kira.net.netcom.com David John Burrowes 1926 Ivy #10 San Mateo, CA 94403-1367 \***********************************************************************/ #define VERSIONSTRING "Convert PICT 1.2" /* ==================================================================== This is $Revision: 1.10 $ of this file It was last modified by $Author: death $ on $Date: 93/04/04 23:29:57 $ Note that this file was created while using the New Century Schoolbook Roman typeface. You may find that some things line up strangely if you don't use that family. History: 93.07.18 djb Added support for the user's having a choice of either writing out the piccomments as before, or having them ommitted. 93.08.01 djb Finished Objective-C support for PicComments defined in tech notes. $Log: PictConverter.m,v $ Revision 1.10 93/04/04 23:29:57 death Sun Apr 4 23:29:57 PDT 1993 Revision 1.9 93/01/09 21:07:09 death Sat Jan 9 21:07:09 PST 1993 Revision 1.8 93/01/01 11:51:17 death Fri Jan 1 11:51:17 PST 1993 Revision 1.7 92/12/31 15:33:55 death Thu Dec 31 15:33:55 PST 1992 Revision 1.6 92/12/05 23:06:28 death Sat Dec 5 23:06:28 PST 1992 ==================================================================== */ #import "PictConverter.h" #import "PICTFile.h" #import "PSFile.h" #import <stdio.h> #import <string.h> #import <strings.h> #import <stdlib.h> #include <time.h> #import <architecture/byte_order.h> // for big/little endian conversins #import "RegionConverter.h" #import "MacToNeXTText.h" @implementation PictConverter /* Layout of the groups of routines in this file (in order) Administrative/ setup High level opcode parsers General opcode processing Processing utilities (e.g. WriteRect) Primary data conversion routines Bitmap and pattern processing (about 25-30% of the file) Notes: Warning. In bitmap routines, we routinely write out > and 80 as terminators for the various PS filters. Make any changes to these parts with caution, since PS lvl 1 code depends on precisely the number of bytes they are (including end of lines) In PS code, if you remove rapping for oval sizes that are too big, you can get neat results. =) Enhancements: The percent indicator on the conversion window is not updated when you'd think it should be (display is always one step behind what it thinks it's drawn , Ithink) Convert packed color bitmaps straight through, rather than unpack and then repack? (Might be able to do similar for B&W, by inverting in place) Warning: The signs of arguments of some opcodes, esp. in Miscellaneous group, have not been thoroughly tested. Enhance overall error checking, including providing gracefull aborting (what if pixmap has unfamiliar values? My treatment of RGB color values may be bad, in that I'm assuming they are 8 bit values in a 16 space. This may not be accurate. */ ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // Administrative/setup methods ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // Method: init // Parameters: none // Returns: self // Stores: none // Description: // This initializes the instance variables of the created object. // History: // 93.07.18 djb Added initializing of the new instance variable PicCommentOp... // Bugs: ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - init { sourceFile = NullInstance; destFile = NullInstance; [super init]; // // The following are sorted by opcode number, basically // UsedPatterns = NO; UsedColors = NO; UsedLines = NO; UsedText = NO; UsedRectangles = NO; UsedRoundRectangles = NO; UsedOvals = NO; UsedArcs = NO; UsedPolygons = NO; UsedRegions = NO; UsedBitmaps = NO; UsedComments = NO; UsedMiscellaneous = NO; CodeDir = NewCString(0); PackingSetting = YES; textConverter = [[MacToNeXTText alloc] init]; ConvertCurrentCharacters = YES; UsersCharConvertChoice = NO; PicCommentOperation = ConvertPicComments; return self; } ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // Method: free // Parameters: none // Returns: self // Stores: none // Description: // This disposes of the CodeDir instance variable // Bugs: ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - free { FreeCString(CodeDir); [textConverter free]; [super free]; return self; } ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // Method: SetImagePacking:CLUTIncluding:AndCodeSet: // Parameters: three boolean values // Returns: self // Stores: none // Description: // This routine allows a caller to change two settings that affect the behavior of // an instance of this class. The three values are: // - whether we pack bitmap when we write them out, or leave them unpacked // - Where we should locate the PS code to include with the code we generate. // Bugs: ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - SetImagePacking: (Boolean) packimages UsingPSIn: (CString) newCodeDirectory { PackingSetting = packimages; if (newCodeDirectory != NullCString) { FreeCString(CodeDir); CodeDir = NewCString(strlen(newCodeDirectory)); strcpy(CodeDir, newCodeDirectory); } return self; } ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // Method: SetConvertAllChars: // Parameters: boolean value // Returns: self // Stores: none // Description: // Tweaks one aspect of this application's character conversion. If YES, then // chars in unfamiliar families will be converted. If NO, then they won't. // History: // 93.07.18 djb Removed a semicolon from the end of the method name. // Bugs: ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - SetConvertAllChars: (Boolean) convertAll { UsersCharConvertChoice = convertAll; return self; } ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // Method: SetPicCommentConversion: // Parameters: An enumerated value indicating what kind of piccomment conversion // should be done hereafter. // Returns: self // Stores: none // Description: // This sets my instance variable PicCommentOperation to the specified // value. This allows someone else to dictate how I should deal with PicComments // until this setting is reset. // History: // 93.07.18 djb Created // Bugs: // This doesn't assure that the enumerated parameter is a 'good' value. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - SetPicCommentConversion: (PicCommentOpType) picConversion { PicCommentOperation = picConversion; return self; } ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // Method: isThisAGoodFile: // Parameters: A Pict File instance // Returns: YES if it is, NO if it isn't. // Stores: none // Description: // Get the pict file's version number. If it's 1 or 2, then return that it is a good file. // This should never answer 'NO', because the pict file itself will have failed to open // fail to open if it thinks the file is not legit. // Bugs: ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - (Boolean) isThisAGoodFile: theFile { Integer version; Boolean result = NO; version = [theFile GetVersion]; if ((version== 2) || (version == 1)) result = YES; else { [self PutCString: "This doesn't look like a PICT file to me (could not find the `version' opcode). Proceed with extreme caution." Into: SECOND_RESULT]; result = NO; } return result; } ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // Method: ConvertPICTfile:ToEPSfile: // Parameters: // The PICT file to convert from // the eps file to store into // Returns: errors // Stores: none // Description: // Ths is the main conversion routine. It converts a pict file into the specified // postscript file. After initializations, this involves a couple steps: convert all // the PICT opcodes into a temporary file. While doing this, keep track of what // types of opcodes we have used. For instance, if we get a call to the FrameRect // opcode, we will set UsedRectangles to YES. When this is done, write a header // onto the eps file, and then copy over all the PS groups that were actually used // (this allows the destination PS file to be smaller than if we copied everything). // After this, copy the contents of the temporary file to the PS file (the stuff was // written to the temporary file earlier, because we needed to get the header // and group definitions written before it). Finally, write the trailing restore, etc. // Bugs: ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - ConvertPICTfile: pictFile ToEPSfile: epsfile { Boolean finished = NO; Integer opcodeCount = 0; Real fileSize = [pictFile FileSize] * 1.0; CString tempString = NewCString(100); PICTRect* bounds = [pictFile GetBounds]; // // Initalize the PS group flags, so we don't include PS code we don't want. // UsedPatterns = NO; UsedColors = NO; UsedLines = NO; UsedText = NO; UsedRectangles = NO; UsedRoundRectangles = NO; UsedOvals = NO; UsedArcs = NO; UsedPolygons = NO; UsedRegions = NO; UsedComments = NO; UsedMiscellaneous = NO; UsedBitmaps = NO; // // // ConvertCurrentCharacters = YES; // // Set the instance variable 'sourceFile' to be the pict file, // set the destFile to be a temporary file, for now. // sourceFile = pictFile; destFile = [[PSFile alloc] initAndUseTemporary]; if ([destFile GetErrorCode] == ERR_OK) [destFile CreateAndOpenFor: FILE_READWRITE]; if ([destFile GetErrorCode] != ERR_OK) { [self StoreErrorCode: ERR_CREATEFAILED AndText: "Could not create a necessary, temporary, file"]; [destFile free]; destFile = NullInstance; } else { // // Write the initiating 'startPict' which will be balanced, later, by the endPict opcode 0xFF. // [destFile WriteInteger: [sourceFile GetVersion]]; [self WritePSProcedureName: "startPict"]; // // With source and destination file established, convert all the opcodes.. // (Let out manager know how the conversion process is going every 10 opcodes) // while (finished == NO) { finished = [self ParseOpcode: [sourceFile GetOpcode]]; if ((opcodeCount % 10) == 0) if ( [myManager respondsTo:@selector(SetPercentageDone:)] ) [myManager SetPercentageDone: ([sourceFile GetCurrentPosition] * 100.0) / fileSize]; } // // If no error occurred, then proceed // if ([self GetErrorCode] >= ERR_OK) { // // Write the PS Header and prolog of group ps code Add the 'save' line. // [self BuildHeaderInto: epsfile]; [self BuildPrologInto: epsfile]; [epsfile WritePSLine: "save"]; [epsfile ForceNewLine]; // // Write the matrix to compensate for the PICT and PS coordinate systems // [epsfile WriteTextUsing: tempString WithFormat: "[1 0 0 -1 %d %d] concat\n", -1*bounds->left, bounds->bottom]; // // Write all the data from the conversion of the opcodes, and then the terminating 'restore'. // [epsfile AppendFrom: destFile]; [epsfile ForceNewLine]; [epsfile WritePSLine: "restore"]; } [destFile CloseAndDelete]; // Remember that this is the temp file. The 'destination' of just the opcode conversion [destFile free]; } FreeCString(tempString); FreeByteString((ByteString)bounds); return self; } ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // Method: SimpleStringConvert: // Parameters: A CString structure // Returns: A converted CString // Stores: none // Description: // This is a quick hack that takes a string, and turns it into another string with // non-printable characters escaped, and (\) all escaped as well. The resulting string // is returned (truncated to 255 chars if necessary) // Bugs: ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - (CString) SimpleStringConvert: (CString) theString { CString filteredString; CString tempString; PositiveInteger filterIndex; PositiveInteger index; PositiveInteger newLength = strlen(theString); // // Now, filter this string, replacing certain troublesome characters with // escaped equivalents. // filteredString = NewCString(newLength*4); // worst case each all will become \ddd filterIndex = 0; for (index = 0; index < newLength; index++) { if ( ( (PositiveInteger) theString[index] < 32 ) || (PositiveInteger) theString[index] > 126 ) { tempString = NewCString(4); // // 93.01.31 djb OOPS! Had been using %.3u, not octal! // sprintf(tempString, "\\%.3o", (unsigned int) theString[index]); strcat(filteredString, tempString); FreeCString(tempString); filterIndex += 4; } else { switch (theString[index]) { case '\\' : strcat(filteredString, "\\\\"); filterIndex += 2; break; case '(' : strcat(filteredString, "\\("); filterIndex += 2; break; case ')' : strcat(filteredString, "\\)"); filterIndex += 2; break; default : filteredString[filterIndex] = theString[index]; filterIndex ++; filteredString[filterIndex] = EndOfCString; // put a null back on. break; } } } // // Don't let the string be longer than 255 chars (p. 639 of Big fat red ps book) // if (strlen(filteredString) > 255) filteredString[255] = EndOfCString; FreeCString(theString); return filteredString; } ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // Method: BuildHeaderInto: // Parameters: a PSfile object. // Returns: errors // Stores: none // Description: // When we're ready to start building the output PS file. // We build a nice standard PS header here. // Bugs: ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - BuildHeaderInto: thisFile { CString tempString = NewCString(255); CString tempBasename; PICTRect* bounds = [sourceFile GetBounds]; Integer thetime; CString timeString; // // Write out inital lines. // [thisFile WriteComment: "!PS-Adobe-3.0 EPSF-3.0"]; // // Write as an EPS bounding box // [thisFile WriteDSCCommentUsing: tempString WithFormat: "BoundingBox: %d %d %d %d",0, 0, (bounds->right - bounds->left), (bounds->bottom)-(bounds->top)]; // // Record the name of the file, and close off the beginning comments. // tempBasename = [self SimpleStringConvert: [sourceFile GetBasename]]; [thisFile WriteDSCCommentUsing: tempString WithFormat: "Title: (%s)", tempBasename]; [thisFile WriteDSCCommentUsing: tempString WithFormat: "Creator: (%s)", VERSIONSTRING]; thetime = time(NULL); timeString = (CString) ctime(&thetime); sprintf(tempString, "CreationDate: (%s", timeString); tempString[strlen(tempString)-1] = EndOfCString; // ctime puts a \n on which we don't want strcat(tempString,")"); [thisFile WriteDSCComment: tempString]; [thisFile WriteDSCComment: "EndComments"]; FreeCString(tempBasename); FreeCString(tempString); FreeByteString((ByteString)bounds); return self; } ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // Method: AppendFile: // Parameters: the pathname of a file, and a file instance to append it to. // Returns: errors // Stores: none // Description: // Given a file, and the path to a file, open the path, and append its contents // to the instance. If we fail to do this, report an error. // Bugs: ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - AppendFile: (CString) codeFile To: thisFile { Instance tempFile; tempFile = [[PSFile alloc] initAndUse: codeFile]; if ([tempFile GetErrorCode] == ERR_OK) [tempFile OpenExistingFor: FILE_READ]; if ([tempFile GetErrorCode] != ERR_OK) [self StoreErrorCode: ERR_OPENFAILED AndText: "Could not open a file of necessary PS code"]; else { [thisFile AppendFrom: tempFile]; if ([thisFile GetErrorCode] != ERR_OK) [self StoreErrorCode: ERR_APPENDFAILED AndText: "Could not append a file of necessary PS code"]; [tempFile Close]; } [tempFile free]; return self; } ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // Method: BuildPrologInto: // Parameters: A text file object. // Returns: errors // Stores: none // Description: // When we're ready to start building the output PS file, we need to stick all the // PostScript code and procedures into the file. This routine checks each group // flag, and if it is set, dumps the appropriate PS out. // Note: we find the path that leads us to the files of PS code that we might // want to append. For each of these, if the relevant flag indicates we // should, we add the file name to the path, call the routine to append // that file, and then strip the name off the path so we can reuse it. // Note that we set no errors, but rely on AppendFile: to do so. // Bugs: // We should report when the PS code directory isn't even there. however, // this requires more work than I'm willing to do at the moment. (stat() is not // as reliable as I'd feel comfortable, since the man page says it barfs if there is // an 8 bit character in the path (gads)). ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - BuildPrologInto: thisFile { CString fileName = NewCString(strlen(CodeDir)+20); Integer pathlength; [self ResetResults]; [thisFile WriteDSCComment: "Begin Prolog"]; strcpy(fileName, CodeDir); pathlength = strlen (fileName); // // Append all the common code // strcat(fileName, "Common"); [self AppendFile: fileName To: thisFile]; fileName[pathlength] = EndOfCString; // // Miscellaneous // if (UsedMiscellaneous == YES) { strcat(fileName, "Miscellaneous"); [self AppendFile: fileName To: thisFile]; fileName[pathlength] = EndOfCString; } // // Patterns // if (UsedPatterns == YES) { strcat(fileName, "Patterns"); [self AppendFile: fileName To: thisFile]; fileName[pathlength] = EndOfCString; } // // Colors // if (UsedColors == YES) { strcat(fileName, "Colors"); [self AppendFile: fileName To: thisFile]; fileName[pathlength] = EndOfCString; } // // lines // if (UsedLines == YES) { strcat(fileName, "Lines"); [self AppendFile: fileName To: thisFile]; fileName[pathlength] = EndOfCString; } // // text // if (UsedText == YES) { strcat(fileName, "Text"); [self AppendFile: fileName To: thisFile]; fileName[pathlength] = EndOfCString; } // // Rects // if (UsedRectangles == YES) { strcat(fileName, "Rectangles"); [self AppendFile: fileName To: thisFile]; fileName[pathlength] = EndOfCString; } // // Roundrects // if (UsedRoundRectangles == YES) { strcat(fileName, "RoundRectangles"); [self AppendFile: fileName To: thisFile]; fileName[pathlength] = EndOfCString; } // // ovals // if (UsedOvals == YES) { strcat(fileName, "Ovals"); [self AppendFile: fileName To: thisFile]; fileName[pathlength] = EndOfCString; } // // arcs // if (UsedArcs == YES) { strcat(fileName, "Arcs"); [self AppendFile: fileName To: thisFile]; fileName[pathlength] = EndOfCString; } // // polygons // if (UsedPolygons == YES) { strcat(fileName, "Polygons"); [self AppendFile: fileName To: thisFile]; fileName[pathlength] = EndOfCString; } // // regions // if (UsedRegions == YES) { strcat(fileName, "Regions"); [self AppendFile: fileName To: thisFile]; fileName[pathlength] = EndOfCString; } // // bitmaps // if (UsedBitmaps == YES) { strcat(fileName, "Bitmaps"); [self AppendFile: fileName To: thisFile]; fileName[pathlength] = EndOfCString; } // // Comments // if (UsedComments == YES) { strcat(fileName, "Comments"); [self AppendFile: fileName To: thisFile]; fileName[pathlength] = EndOfCString; } FreeCString(fileName); [thisFile WriteDSCComment: "End Prolog"]; return self; } ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // HIGH LEVEL OPCODE PROCESSING ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // Method: ParseOpcode // Parameters: A PICT opcode // Returns: YES if something went wrong or we reached the end of the pict file // Stores: none // Description: // This method takes an opcode, and passes it on to a particular method that deal // with processing a particular group of opcodes. This routine, and the ones it // calls, then, essentially make up a giant switch statement that has been // broken up for legibility's sake. // These methods are the heard of this object. Given an opcode, it reads the appropriate // arguments, and writes appropriate postscript calls out to the output file. Note that // this does not generate the actual postscript procedures that will be needed to image // this, but rather just the calls to some procedures. Those PS procedures must // be created independantly of this object. // NOTE: The following code basically presumes you know something about PICT files. // It's organization is basically: throw the specified opcode through a giant switch // statement. If, for some reason, it isn't one of the defined opcodes, a default entry // will try to consume its data so no harm is done. // If you know nothing about PICT stuff, these are the sources that I've used to figure out // what is in one (aside from mucking around with occasional PICT files by hand): Inside // Mac V p.84-105, The Pict.r file which comes with MPW (I think the partial path is // :MPW:Includes:RIncludes:Pict.r), Tech Note 21, and a bit of guessing. =) // History: // 93.07.18 djb Changed Compiler told me that ((opCode >=0x00) && (opCode <=0x1F)) // : comparison is always 1 due to limited range of data type. I said 'huh? // opCode is 16bits! There should be plenty of room'. So, I poked and poked, // and eventualy it dawned on my thick head that since opCode is an // unsigned quantity, it would always be >=0. Duh. Kinda funny, for me, // though. Needless to say, I pulled that bit out! // Also, moved comment processing to a separate routine call. // History: // 93.08.21 djb used 'endFound' flag to catch error flag from parsebitmap // definitely a hack, but for now we'll live. // Bugs: // Ignoring error flags ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -(Boolean) ParseOpcode: (PICTOpcode) opCode { Boolean endFound = NO; // // Now, try to parse the message. // if (opCode == 0xFF) endFound = YES; if ( (opCode <=0x1F) || (opCode == 0xFF) || (opCode == 0x0C00)) [self ParseMiscOpcode: opCode]; else if ( (opCode >= 0x20) && (opCode <=0x27)) [self ParseLineOpcode: opCode]; else if ( (opCode >= 0x28) && (opCode <=0x2F) ) [self ParseTextOpcode: opCode]; else if ( (opCode >= 0x30) && (opCode <=0x3F) ) [self ParseRectangleOpcode: opCode]; else if ( (opCode >= 0x40) && (opCode <=0x4F) ) [self ParseRoundRectangleOpcode: opCode]; else if ( (opCode >= 0x50) && (opCode <=0x5F) ) [self ParseOvalOpcode: opCode]; else if ( (opCode >= 0x60) && (opCode <=0x6F) ) [self ParseArcOpcode: opCode]; else if ( (opCode >= 0x70) && (opCode <=0x7F) ) [self ParsePolygonOpcode: opCode]; else if ( (opCode >= 0x80) && (opCode <=0x8F) ) [self ParseRegionOpcode: opCode]; else if ( (opCode >= 0x90) && (opCode <=0x9F) ) endFound = [self ParseBitmapOpcode: opCode]; else if ((opCode == 0xA0) || (opCode == 0xA1)) [self ParseCommentOpcode: opCode]; else if ((opCode >= 0x00A2) && (opCode != 0x0C00) && (opCode != 0x00FF)) { // // There are a bunch of reserved opcodes defined by apple. On the off chance // we get a picture that makes use of one of them, the following will just // skip over its data and ignore it. // // // Opcodes 0x8000 to 0x80FF and 0x00B0 to 0x00CF have no data to // consume. // if (opCode >= 0x8100) [self Skip4PlusData]; // // Opcodes 0x8000 to 0x80FF have no data to consume. // if ((opCode >= 0x7F00) && (opCode <= 0x7FFF)) [sourceFile AdvanceBytes: 254]; // // It is not clear what values follow these next opcodes. I'm guessing 4 // if ((opCode >= 0x0C01) && (opCode < 0x7F00)) [sourceFile AdvanceBytes: 4]; if ((opCode >= 0x0200) && (opCode < 0xBFF)) [sourceFile AdvanceBytes: 4]; if ((opCode >= 0x0100) && (opCode < 0x1FF)) [sourceFile AdvanceBytes: 2]; if ((opCode >= 0x00D0) && (opCode <= 0x00FE)) [self Skip4PlusData]; // // Opcodes 0x00B0 to 0x00CF have no data to consume. // if ((opCode >= 0x00A2) && (opCode <= 0x00AF)) [self Skip2PlusData]; } else { // // Something is very weird if we get here, because all opcodes should have // been consumed above. // endFound = YES; } return endFound; } ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // OPCODE PROCESSING ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // Method: ParseMiscOpcode: // Parameters: // A PICT opcode code // Returns: true if anything goes wrong // Stores: none explicitly (subcalls may) // Description: // This is one segment of the giant switch that parses pict opcodes. // This one deals with all the opcodes that do miscellaneous things // (that is, that aren't part of some well defined greater group like the text // opcodes, or the region ones, etc). IT also includes opcodes (e.g. txFace) // which logically belong to others, but weren't worth making specialized tests for // above just so we could call this like that. // History: // 93.07.18 djb Moved PicComment processing into another routine. // 93.08.15 djb Added HeaderOp for IM v. 6 format. // Bugs: // The rectangle in a -2 headerop should be being used for the ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - (Boolean) ParseMiscOpcode: (PICTOpcode) opcode { Boolean error = NO; CString buffer; Integer tempInt; PICTPoint* numerator; PICTPoint* denominator; Integer version; Signed32Bits temp2; Signed32Bits tempsize; // // Process the opcode // switch (opcode) { case 0x00 : // NOP opcode. Do nothing break; case 0x01 : // Define clip region. // Uses common definitions. [self ConvertRegion]; [self WritePSProcedureName: "QDclip"]; break; case 0x02 : // bk Pat UsedPatterns = YES; [self ConvertPict1Pattern]; [self WritePSProcedureName: "bkPat"]; break; case 0x03 : // Set type family to use UsedText = YES; [self ConvertFamily]; [self WritePSProcedureName: "txFont" ]; break; case 0x04 : // Set type style to use UsedText = YES; [self ConvertFace]; [self WritePSProcedureName: "txFace" ]; break; case 0x05 : // Set type mode UsedText = YES; [self ConvertMode]; [self WritePSProcedureName: "txMode" ]; break; case 0x06 : // Set space extra // // a fixed won't have more than 9 digits after the decimal place, // (by my calculator ( 1/(2**16))) // UsedText = YES; buffer = NewCString(15); [destFile WriteTextUsing: buffer WithFormat: "%.9f ", [sourceFile GetFixedNumber]]; [self WritePSProcedureName: "spExtra" ]; FreeCString(buffer); break; case 0x07 : // Set pen size UsedMiscellaneous = YES; tempInt = [sourceFile GetINTEGER]; // can it be negative? [destFile WriteInteger: [sourceFile GetINTEGER]]; // width [destFile WriteInteger: tempInt]; // height [self WritePSProcedureName: "pnSize" ]; break; case 0x08 : // Set pen mode UsedMiscellaneous = YES; [self ConvertMode]; [self WritePSProcedureName: "pnMode" ]; break; case 0x09 : // Pen Pat UsedPatterns = YES; [self ConvertPict1Pattern]; [self WritePSProcedureName: "pnPat"]; break; case 0x0a : // fill Pat UsedPatterns = YES; [self ConvertPict1Pattern]; [self WritePSProcedureName: "fillPat"]; break; case 0x0B : // Oval size (x and y) UsedRoundRectangles = YES; [self WritePoint: [sourceFile GetPoint] ]; [self WritePSProcedureName: "ovSize" ]; break; case 0x0C : // Origin (dh and dv) UsedMiscellaneous = YES; [destFile WriteInteger: -1* [sourceFile GetINTEGER]]; // dh [destFile WriteInteger: -1*[sourceFile GetINTEGER]]; // dv [self WritePSProcedureName: "origin" ]; break; case 0x0D : // TextSize // // I can't tell if parameter should be signed integers or not // UsedText = YES; [destFile WriteInteger: [sourceFile GetINTEGER]]; [self WritePSProcedureName: "txSize" ]; break; case 0x0E : // FgColor UsedColors = YES; [self ConvertOldColor]; [self WritePSProcedureName: "fgColor"]; break; case 0x0F : // BkColor UsedColors = YES; [self ConvertOldColor]; [self WritePSProcedureName: "bkColor"]; break; case 0x10: // Text ratio UsedText = YES; // // presumably these are unsigned, but again, Apple calls them points. // numerator = [sourceFile GetPoint]; denominator = [sourceFile GetPoint]; [destFile WriteReal: numerator->x / denominator->x ]; // numerator [destFile WriteReal: numerator->y / denominator->y ]; // numerator FreeByteString((ByteString) numerator); FreeByteString((ByteString) denominator); [self WritePSProcedureName: "txRatio" ]; break; case 0x11 : // Version opcode // // We should actually never end up here, I think, because the initial version // opcode is read in when the pict file opens. // buffer = NewCString(31); [destFile WriteCommentUsing: buffer WithFormat: "PictVersion %d", [sourceFile GetByte]]; FreeCString(buffer); break; case 0x12 : // Background patten (color) UsedPatterns = YES; [self ConvertPict2Pattern]; [self WritePSProcedureName: "bkPixPat"]; break; case 0x13 : // Color pen pattern UsedPatterns = YES; [self ConvertPict2Pattern]; [self WritePSProcedureName: "pnPixPat"]; break; case 0x14 : // color fill pattern UsedPatterns = YES; [self ConvertPict2Pattern]; [self WritePSProcedureName: "fillPixPat"]; break; case 0x15 : // 'fractional pen position' UsedMiscellaneous = YES; [destFile WriteInteger: [sourceFile GetINTEGER]]; // the param... (pos? neg??) [self WritePSProcedureName: "pnLocHFrac"]; break; case 0x16 : // extra for each non-space character UsedText = YES; // // Dunno what this does. for sure. IM suggested param is in a // special format... // [destFile WriteInteger: [sourceFile GetINTEGER]]; // the param... [self WritePSProcedureName: "chExtra"]; break; case 0x017: // Several 'reserved for Apple use' case 0x018: case 0x019: break; case 0x1A : // RGB foreground color UsedColors = YES; [self ConvertRGBColor]; [self WritePSProcedureName: "RGBFgCol"]; break; case 0x1B : // RGB background color UsedColors = YES; [self ConvertRGBColor]; [self WritePSProcedureName: "RGBBkCol"]; break; case 0x1C : // Use hilite mode flag UsedMiscellaneous = YES; [self WritePSProcedureName: "hiliteMode"]; break; case 0x1D : // RGB highlighting color UsedColors = YES; [self ConvertRGBColor]; [self WritePSProcedureName: "hiliteColor"]; break; case 0x1E : // Use default hilite color UsedColors = YES; [self WritePSProcedureName: "defHilite"]; break; case 0x1F : // RGB OpColor for 'arithmetic modes' UsedColors = YES; [self ConvertRGBColor]; [self WritePSProcedureName: "hiliteColor"]; break; case 0xFF: // End of picture // // Inside Mac (V5 P102) says 2 bytes follow this. Pict.r for MPW doesn't agree. // I'm ignoring IMV5. Can't imagine what they are.. // (This routine is defined in the common code, so I don't set a Use* flag here) // error = YES; // hacked way to get out... [self WritePSProcedureName: "endPict"]; break; case 0x0C00: // Header op // // 93.08.15 djb Turns out that Inside Mac 6 redefined the HeaderOp's // contents. So, we try to distinguish between the two and // write them out appropriately between []'s because of the // variable number of parameters. // Note that theoretically IVM volume V said all args were -1 // // (uses common) // [destFile WriteText: "[ "]; version = [sourceFile GetINTEGER]; if (version == -2) // As defined by IM volume 6 { [destFile WriteInteger: version]; [destFile WriteInteger: [sourceFile GetINTEGER]]; // reserved buffer = NewCString(63); [destFile WriteTextUsing: buffer WithFormat: "%.9f ", [sourceFile GetFixedNumber]]; // hres [destFile WriteTextUsing: buffer WithFormat: "%.9f ", [sourceFile GetFixedNumber]]; // vres FreeCString(buffer); [destFile WriteText: "[ "]; [self WriteRect: [sourceFile GetRect]]; // source rectangle [destFile WriteText: "] "]; [destFile WriteInteger: [sourceFile GetLONGINT]]; // reserved } else { // // We must reconstruct the first longint. ick. Is my logic good? // temp2 = [sourceFile GetINTEGER]; tempsize = version << 16; tempsize |= temp2; [destFile WriteInteger: tempsize]; [destFile WriteInteger: [sourceFile GetLONGINT]]; [destFile WriteInteger: [sourceFile GetLONGINT]]; [destFile WriteInteger: [sourceFile GetLONGINT]]; [destFile WriteInteger: [sourceFile GetLONGINT]]; [destFile WriteInteger: [sourceFile GetLONGINT]]; } [destFile WriteText: "] "]; [self WritePSProcedureName: "headerOp"]; break; default: error = YES; break; } return error; } ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // Method: ParseLineOpcode: // Parameters: // A PICT opcode code // Returns: true if anything goes wrong // Stores: none explicitly (subcalls may) // Description: // This is one segment of the giant switch that parses pict opcodes. // This one deals with all the opcodes that draw lines. // Bugs: ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - (Boolean) ParseLineOpcode: (PICTOpcode) opcode { Boolean error = NO; // // Set the type flag // UsedLines = YES; // // Process the opcode // switch (opcode) { case 0x020: [self WritePoint: [sourceFile GetPoint] ]; // From [self WritePoint: [sourceFile GetPoint] ]; // To [self WritePSProcedureName: "line"]; break; case 0x021: // Starts from 'current' point? [self WritePoint: [sourceFile GetPoint] ]; // To [self WritePSProcedureName: "lineFrom"]; break; case 0x022: //point to dh,dv [self WritePoint: [sourceFile GetPoint] ]; // From [destFile WriteInteger: [sourceFile GetSignedByte] ]; // dh [destFile WriteInteger: [sourceFile GetSignedByte] ]; // dv [self WritePSProcedureName: "shortLine"]; break; case 0x023: [destFile WriteInteger: [sourceFile GetSignedByte] ]; // dh [destFile WriteInteger: [sourceFile GetSignedByte] ]; // dv [self WritePSProcedureName: "shortLineFrom"]; break; case 0x024: // Several 'reserved for Apple use' case 0x025: case 0x026: case 0x027: [self Skip2PlusData]; break; default: error = YES; break; } return error; } ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // Method: ParseTextOpcode: // Parameters: // A PICT opcode code // Returns: true if anything goes wrong // Stores: none explicitly (subcalls may) // Description: // This is one segment of the giant switch that parses pict opcodes. // This one deals with all the opcodes that deal with text. // History: // 93.08.16 djb Added 0x2C code. // 93.08.18 djb Added 0x2D opcode // Bugs: ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - (Boolean) ParseTextOpcode: (PICTOpcode) opcode { Boolean error = NO; CString fontName; CString newName; CString buffer; // // Set the type flag // UsedText = YES; // // Process the opcode // switch (opcode) { case 0x028: [self WritePoint: [sourceFile GetPoint] ]; // From [self WriteString: [sourceFile GetPString] ]; [self WritePSProcedureName: "longText" ]; break; case 0x029: [destFile WriteInteger: [sourceFile GetByte] ]; // dh [self WriteString: [sourceFile GetPString] ]; [self WritePSProcedureName: "DHText" ]; break; case 0x02A: [destFile WriteInteger: [sourceFile GetByte] ]; // dv [self WriteString: [sourceFile GetPString] ]; [self WritePSProcedureName: "DVText"]; break; case 0x02B: [destFile WriteInteger: [sourceFile GetByte] ]; // dh [destFile WriteInteger: [sourceFile GetByte] ]; // dv [self WriteString: [sourceFile GetPString] ]; [self WritePSProcedureName: "DHDVText" ]; break; case 0x02C: [sourceFile GetINTEGER]; [sourceFile GetINTEGER]; fontName = [sourceFile GetPString]; // // Convert type family names // newName = NullCString; if (strcmp(fontName, "Times") == 0) newName = "Times-Roman"; else if (strcmp(fontName, "Avant Garde") == 0) newName = "AvantGarde-Book"; else if (strcmp(fontName, "Bookman") == 0) newName = "Bookman-Light"; else if (strcmp(fontName, "Helvetica Narrow") == 0) newName = "Helvetica-Narrow"; else if (strcmp(fontName, "New Century Schlbk") == 0) newName = "NewCenturySchlbk-Roman"; else if (strcmp(fontName, "Palatino") == 0) newName = "Palatino-Roman"; else if (strcmp(fontName, "Zapf Chancery") == 0) newName = "ZapfChancery-MediumItalic"; else if (strcmp(fontName, "New York") == 0) newName = "NewYork"; else if (strcmp(fontName, "Los Angeles") == 0) newName = "LosAngeles"; else if (strcmp(fontName, "San Francisco") == 0) newName = "SanFrancisco"; if (newName != NullCString) { FreeCString(fontName); fontName = newName; } [destFile WriteText: "/"]; [destFile WriteText: fontName]; [self WritePSProcedureName: " fontName" ]; break; case 0x02D: [sourceFile GetINTEGER]; // Skip the length. We don't care. buffer = NewCString(63); [destFile WriteTextUsing: buffer WithFormat: "%.9f ", [sourceFile GetFixedNumber]]; // intercharacter spacing [destFile WriteTextUsing: buffer WithFormat: "%.9f ", [sourceFile GetFixedNumber]]; // total extra space for justification FreeCString(buffer); [self WritePSProcedureName: "lineJustify" ]; break; case 0x02E: // Several 'reserved for Apple use' case 0x02F: [self Skip2PlusData]; break; default: error = YES; break; } return error; } ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // Method: ParseRectangleOpcode: // Parameters: // A PICT opcode code // Returns: true if anything goes wrong // Stores: none explicitly (subcalls may) // Description: // This is one segment of the giant switch that parses pict opcodes. // This one deals with all the opcodes that deal with Rectangles. // Bugs: ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - (Boolean) ParseRectangleOpcode: (PICTOpcode) opcode { Boolean error = NO; // // Set the type flag // UsedRectangles = YES; // // Process the opcode // switch (opcode) { case 0x30 : // frameRect . [self WriteRect: [sourceFile GetRect] ]; [self WritePSProcedureName: "frameRect"]; break; case 0x31 : // paintRect . [self WriteRect: [sourceFile GetRect] ]; [self WritePSProcedureName: "paintRect"]; break; case 0x32 : // eraseRect . [self WriteRect: [sourceFile GetRect] ]; [self WritePSProcedureName: "eraseRect"]; break; case 0x33 : // invertRect . [self WriteRect: [sourceFile GetRect] ]; [self WritePSProcedureName: "invertRect"]; break; case 0x34 : // fillRect [self WriteRect: [sourceFile GetRect] ]; [self WritePSProcedureName: "fillRect"]; break; case 0x035: // Several 'reserved for Apple use' each require 8 bytes case 0x036: case 0x037: [sourceFile AdvanceBytes: 8]; break; case 0x38 : // frame same Rect [self WritePSProcedureName: "frameSameRect"]; break; case 0x39 : // paint same Rect [self WritePSProcedureName: "paintSameRect"]; break; case 0x3A : // erase same Rect [self WritePSProcedureName: "eraseSameRect"]; break; case 0x3B : // invert same Rect [self WritePSProcedureName: "invertSameRect"]; break; case 0x3C : // fill same Rect [self WritePSProcedureName: "fillSameRect"]; break; case 0x03D: // Several 'reserved for Apple use'. Each takes no bytes of parameters. case 0x03E: case 0x03F: break; default: error = YES; break; } return error; } ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // Method: ParseRoundRectangleOpcode: // Parameters: // A PICT opcode code // Returns: true if anything goes wrong // Stores: none explicitly (subcalls may) // Description: // This is one segment of the giant switch that parses pict opcodes. // This one deals with all the opcodes that deal with Rectangles. // Bugs: ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - (Boolean) ParseRoundRectangleOpcode: (PICTOpcode) opcode { Boolean error = NO; // // Set the type flag // UsedRoundRectangles = YES; // // Process the opcode // switch (opcode) { case 0x40 : // frameRRect [self WriteRect: [sourceFile GetRect] ]; [self WritePSProcedureName: "frameRRect"]; break; case 0x41 : // paintRRec [self WriteRect: [sourceFile GetRect] ]; [self WritePSProcedureName: "paintRRect"]; break; case 0x42 : // eraseRRect [self WriteRect: [sourceFile GetRect] ]; [self WritePSProcedureName: "eraseRRect"]; break; case 0x43 : // invertRRect [self WriteRect: [sourceFile GetRect] ]; [self WritePSProcedureName: "invertRRect"]; break; case 0x44 : // fillRRect [self WriteRect: [sourceFile GetRect] ]; [self WritePSProcedureName: "fillRRect"]; break; case 0x045: // Several 'reserved for Apple use' each require 8 bytes case 0x046: case 0x047: [sourceFile AdvanceBytes: 8]; break; case 0x48 : // frame same RRect [self WritePSProcedureName: "frameSameRRect"]; break; case 0x49 : // paint same RRect [self WritePSProcedureName: "paintSameRRect"]; break; case 0x4A : // erase same RRect [self WritePSProcedureName: "eraseSameRRect"]; break; case 0x4B : // invert same RRect [self WritePSProcedureName: "invertSameRRect"]; break; case 0x4C : // fill same RRect [self WritePSProcedureName: "fillSameRRect"]; break; case 0x04D: // Several 'reserved for Apple use'. Each takes no bytes of parameters. case 0x04E: case 0x04F: break; default: error = YES; break; } return error; } ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // Method: ParseOvalOpcode: // Parameters: // A PICT opcode code // Returns: true if anything goes wrong // Stores: none explicitly (subcalls may) // Description: // This is one segment of the giant switch that parses pict opcodes. // This one deals with all the opcodes that deal with Ovals. // Bugs: ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - (Boolean) ParseOvalOpcode: (PICTOpcode) opcode { Boolean error = NO; // // Set the type flag // UsedOvals = YES; // // Process the opcode // switch (opcode) { case 0x50 : // frameOval [self WriteRect: [sourceFile GetRect] ]; [self WritePSProcedureName: "frameOval"]; break; case 0x51 : // paintOval [self WriteRect: [sourceFile GetRect] ]; [self WritePSProcedureName: "paintOval"]; break; case 0x52 : // eraseOval [self WriteRect: [sourceFile GetRect] ]; [self WritePSProcedureName: "eraseOval"]; break; case 0x53 : // invertOval [self WriteRect: [sourceFile GetRect] ]; [self WritePSProcedureName: "invertOval"]; break; case 0x54 : // fillOval [self WriteRect: [sourceFile GetRect] ]; [self WritePSProcedureName: "fillOval"]; break; case 0x055: // Several 'reserved for Apple use' each require 8 bytes case 0x056: case 0x057: [sourceFile AdvanceBytes: 8]; break; case 0x58 : // frame same Oval [self WritePSProcedureName: "frameSameOval"]; break; case 0x59 : // paint same Oval [self WritePSProcedureName: "paintSameOval"]; break; case 0x5A : // erase same Oval [self WritePSProcedureName: "eraseSameOval"]; break; case 0x5B : // invert same Oval [self WritePSProcedureName: "invertSameOval"]; break; case 0x5C : // fill same Oval [self WritePSProcedureName: "fillSameOval"]; break; case 0x05D: // Several 'reserved for Apple use'. Each takes no bytes of parameters. case 0x05E: case 0x05F: break; default: error = YES; break; } return error; } ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // Method: ParseArcOpcode: // Parameters: // A PICT opcode code // Returns: true if anything goes wrong // Stores: none explicitly (subcalls may) // Description: // This is one segment of the giant switch that parses pict opcodes. // This one deals with all the opcodes that deal with Arcs. // Bugs: ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - (Boolean) ParseArcOpcode: (PICTOpcode) opcode { Boolean error = NO; // // Set the type flag // UsedArcs = YES; // // Process the opcode // switch (opcode) { case 0x60 : // frameArc [self ConvertArc]; [self WritePSProcedureName: "frameArc" ]; break; case 0x61 : // paintArc [self ConvertArc]; [self WritePSProcedureName: "paintArc" ]; break; case 0x62 : // eraseArc [self ConvertArc]; [self WritePSProcedureName: "eraseArc" ]; break; case 0x63 : // invertArc . [self ConvertArc]; [self WritePSProcedureName: "invertArc" ]; break; case 0x64 : // fillArc [self ConvertArc]; [self WritePSProcedureName: "fillArc" ]; break; case 0x065: // Several 'reserved for Apple use' each require 12 bytes case 0x066: case 0x067: [sourceFile AdvanceBytes: 12]; break; // // The following 5 opcodes take angle measurements, but no rect // case 0x68 : // frame same Arc [self ConvertAngles]; [self WritePSProcedureName: "frameSameArc" ]; break; case 0x69 : // paint same Arc [self ConvertAngles]; [self WritePSProcedureName: "paintSameArc" ]; break; case 0x6A : // erase same Arc [self ConvertAngles]; [self WritePSProcedureName: "eraseSameArc" ]; break; case 0x6B : // invert same Arc [self ConvertAngles]; [self WritePSProcedureName: "invertSameArc" ]; break; case 0x6C : // fill same Arc [self ConvertAngles]; [self WritePSProcedureName: "fillSameArc" ]; break; case 0x06D: // Several 'reserved for Apple use' case 0x06E: case 0x06F: [sourceFile AdvanceBytes: 4]; break; default: error = YES; break; } return error; } ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // Method: ParsePolygonOpcode: // Parameters: // A PICT opcode code // Returns: true if anything goes wrong // Stores: none explicitly (subcalls may) // Description: // This is one segment of the giant switch that parses pict opcodes. // This one deals with all the opcodes that deal with Polygon. // Bugs: ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - (Boolean) ParsePolygonOpcode: (PICTOpcode) opcode { Boolean error = NO; // // Set the type flag // UsedPolygons = YES; UsedLines = YES; // A cheat because we know that a routine in the Lines file is one we need // // Process the opcode // switch (opcode) { case 0x70 : // framePoly . [self ConvertPolygon]; [self WritePSProcedureName: "framePoly"]; break; case 0x71 : // paintPoly . . [self ConvertPolygon]; [self WritePSProcedureName: "paintPoly"]; break; case 0x72 : // erasePoly . . [self ConvertPolygon]; [self WritePSProcedureName: "erasePoly"]; break; case 0x73 : // invertPoly . . [self ConvertPolygon]; [self WritePSProcedureName: "invertPoly"]; break; case 0x74 : // fillPoly . . [self ConvertPolygon]; [self WritePSProcedureName: "fillPoly"]; break; case 0x075: // Several 'reserved for Apple use' All are followed by a Polygon case 0x076: case 0x077: [self SkipSizedObject]; break; case 0x78 : // frame same Poly (not yet actually implemented by apple) [self WritePSProcedureName: "frameSamePoly" ]; break; case 0x79 : // paint same Poly (not yet actually implemented by apple) [self WritePSProcedureName: "paintSamePoly" ]; break; case 0x7A : // erase same Poly (not yet actually implemented by apple) [self WritePSProcedureName: "eraseSamePoly" ]; break; case 0x7B : // invert same Poly (not yet actually implemented by apple) [self WritePSProcedureName: "invertSamePoly" ]; break; case 0x7C : // fill same Poly (not yet actually implemented by apple) [self WritePSProcedureName: "fillSamePoly" ]; break; case 0x07D: // Several 'reserved for Apple use' None have data bytes case 0x07E: case 0x07F: break; default: error = YES; break; } return error; } ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // Method: ParseRegionOpcode: // Parameters: // A PICT opcode code // Returns: true if anything goes wrong // Stores: none explicitly (subcalls may) // Description: // This is one segment of the giant switch that parses pict opcodes. // This one deals with all the opcodes that deal with Regions. // Bugs: ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - (Boolean) ParseRegionOpcode: (PICTOpcode) opcode { Boolean error = NO; // // Set the type flag // UsedRegions = YES; // // Process the opcode // switch (opcode) { case 0x80 : // frameRgn [self ConvertRegion]; [self WritePSProcedureName: "frameRgn"]; break; case 0x81 : // paintRgn [self ConvertRegion]; [self WritePSProcedureName: "paintRgn"]; break; case 0x82 : // eraseRgn [self ConvertRegion]; [self WritePSProcedureName: "eraseRgn"]; break; case 0x83 : // invertRgn [self ConvertRegion]; [self WritePSProcedureName: "invertRgn"]; break; case 0x84 : // fillRgn [self ConvertRegion]; [self WritePSProcedureName: "fillRgn"]; break; case 0x085: // Several 'reserved for Apple use' All are followed by a region case 0x086: case 0x087: [self SkipSizedObject]; break; case 0x88 : // frame same rgn (not yet actually implemented by apple) [self WritePSProcedureName: "frameSameRgn" ]; break; case 0x89 : // paint same rgn (not yet actually implemented by apple) [self WritePSProcedureName: "paintSameRgn" ]; break; case 0x8A : // erase same rgn (not yet actually implemented by apple) [self WritePSProcedureName: "eraseSameRgn" ]; break; case 0x8B : // invert same rgn (not yet actually implemented by apple) [self WritePSProcedureName: "invertSameRgn" ]; break; case 0x8C : // fill same rgn (not yet actually implemented by apple) [self WritePSProcedureName: "fillSameRgn" ]; break; case 0x08D: // Several 'reserved for Apple use' None have data bytes case 0x08E: case 0x08F: break; default: error = YES; break; } return error; } ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // Method: ParseBitmapOpcode: // Parameters: // A PICT opcode code // Returns: true if anything goes wrong // Stores: none explicitly (subcalls may) // Description: // This is one segment of the giant switch that parses pict opcodes. // This one deals with all the opcodes that deal with Bitmaps. // History: // 93.08.21 djb Added branches for the direct bitmaps 9a and 9b // Added a crude error code returning capability. // Bugs: ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - (Boolean) ParseBitmapOpcode: (PICTOpcode) opcode { Boolean error = NO; // // Set the type flag // UsedBitmaps = YES; // // Process the opcode // switch (opcode) { case 0x90 : [self ConvertBitmapWithRegion: NO PackedData: NO]; break; case 0x91 : [self ConvertBitmapWithRegion: YES PackedData: NO]; break; case 0x092: // Several 'reserved for Apple use' case 0x093: case 0x094: case 0x095: case 0x096: case 0x097: [self Skip2PlusData]; break; case 0x98 : [self ConvertBitmapWithRegion: NO PackedData: YES]; break; case 0x99 : [self ConvertBitmapWithRegion: YES PackedData: YES]; break; case 0x09A: [self ConvertDirectBitmapWithRegion: NO]; break; case 0x09B: [self ConvertDirectBitmapWithRegion: YES]; break; case 0x09C: // Several 'reserved for Apple use' case 0x09D: case 0x09E: case 0x09F: [self Skip2PlusData]; break; default: error = YES; break; } if ([self GetErrorCode] < ERR_OK) { error = YES; } return error; } ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // Method: ParseCommentOpcode: // Parameters: // A PICT opcode code // Returns: true if anything goes wrong // Stores: none explicitly (subcalls may) // Description: // This routine process the two picture comment opcodes. One opcode's data is merely // a number indicating it's 'kind'. The other opcode's data is a kind and a length of // data. It happens that there are lots of opcodes out there, and many of them don't // seem to be documented, meaning they're basically proprietary. There used to be // a publicly defined set that MacDraw used, but that info is now Claris', and isn't // as redily available. So, what this routine does is it will do what little processing it // can for the opcodes defined in the TechNotes or Inside Mac #1, and for the others // write them out in a generic form. That is, Inside Mac or TN ones will be written out // in the form: // [args] /name longComment // While unknown ones will be written out as // <hex-data> kind longComment // (short comments the same, without args or hex-data). // Note these points: the kind and args have reversed position from earlier versions // of the converter. Our arguments are different types depending on what we know // about them. Also, the appspecific one will go out as a hex string and with a name. // Naturally, if the user has asked to remove all pic comments, nothing is written out. // ² These comments are obsolete. // # These comments are not recommended. // History: // 93.07.18 Started doing some Picture comment stuff (added kinds 0, 1) // Bugs: ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - (Boolean) ParseCommentOpcode: (PICTOpcode) opcode { Boolean error = NO; CString buffer = NewCString(1024); PositiveInteger kind; PositiveInteger length; CString tempString; TPolyVerbRec polyBitfield; Byte tempByte; SignedByte tempSignedByte; SignedByte numIntervals; PositiveInteger index; kind = [sourceFile GetPositiveINTEGER]; if (PicCommentOperation == DiscardPicComments) { if (opcode == 0xA1) [self Skip2PlusData]; } else { UsedComments = YES; if (opcode == 0xA0) [self ParseShortComment: kind]; else { length = [sourceFile GetPositiveINTEGER]; switch (kind) { case 100: [self ConvertHexBytes: length]; // // Extract copies of MYAP, and 2 byte number? // [destFile WriteText: "/AppComment "]; break; case 150: //Begin text function if (length != 10) { // // Doc gives conflicting sizes. if it's not the one we want do generic. // [destFile WriteCommentUsing: buffer WithFormat: "%s (%d)", "WARNING: Unexpected datalength for TextBegin. doing nothing!", length]; [self ConvertHexBytes: length]; [destFile WritePositiveInteger: kind]; } else { UsedText = YES; [destFile WriteText: "[ "]; // // Read tJus // tempByte = [sourceFile GetByte]; switch (tempByte) { case tJusNone: [destFile WriteText: "/tJusNone "]; break; case tJusLeft: [destFile WriteText: "/tJusLeft "]; break; case tJusCenter: [destFile WriteText: "/tJusCenter "]; break; case tJusRight: [destFile WriteText: "/tJusRight "]; break; case tJusFull: [destFile WriteText: "/tJusFull "]; break; } // // Read tFlip // tempByte = [sourceFile GetByte]; switch (tempByte) { case tFlipNone: [destFile WriteText: "/tFlipNone "]; break; case tFlipHorizontal: [destFile WriteText: "/tFlipHorizontal "]; break; case tFlipVertical: [destFile WriteText: "/tFlipVertical "]; break; } // // tAngle (clockwise rotation in degrees 0..360) // [destFile WriteInteger: [sourceFile GetINTEGER]]; // // Skip these unused and reserved bytes (tLine and tCmnt) // [sourceFile GetByte]; [sourceFile GetByte]; // // tAngleFixed (same as "tAngle" in Fixed precision) // [destFile WriteTextUsing: buffer WithFormat: "%.9f ", [sourceFile GetFixedNumber]]; [destFile WriteText: "]"]; [destFile WriteText: " /TextBegin "]; } break; case 154: //Offset to center of rotation [destFile WriteText: "[ "]; [destFile WriteTextUsing: buffer WithFormat: "%.9f ", [sourceFile GetFixedNumber]]; [destFile WriteTextUsing: buffer WithFormat: "%.9f ", [sourceFile GetFixedNumber]]; [destFile WriteText: "]"]; [destFile WriteText: " /TextCenter "]; break; case 157: //Customize line layout error distribution # [destFile WriteText: "[ "]; // // Parameters are, respecitvely, // chCount Apply for so many characters. // major percentage of line layout error to be distributed // among space characters. // spcChar code of character that is to absorb // the "major" line layout error // minor percentage of intercharacter distrib. // ulLength underline length. // [destFile WriteInteger: [sourceFile GetINTEGER]]; // Apply this # chars [destFile WriteTextUsing: buffer WithFormat: "%.9f ", [sourceFile GetFixedNumber]]; [destFile WriteInteger: [sourceFile GetINTEGER]]; [destFile WriteTextUsing: buffer WithFormat: "%.9f ", [sourceFile GetFixedNumber]]; [destFile WriteTextUsing: buffer WithFormat: "%.9f ", [sourceFile GetFixedNumber]]; [destFile WriteText: "]"]; [destFile WriteText: " /ClientLineLayout "]; break; case 164: //Close, Fill, Frame // // Hey. Since we only care about 3 of the fields, we could probably // just read this in as a byte, and look for the values from 0 to 8 (or the // reverse if the byte is reversed). Just a thought. // polyBitfield = [sourceFile GetPolyVerbs]; [destFile WriteText: "[ "]; if (polyBitfield.fPolyClose == YES) [destFile WriteText: "/Close "]; if (polyBitfield.fPolyFill == YES) [destFile WriteText: "/Fill "]; if (polyBitfield.fPolyFrame == YES) [destFile WriteText: "/Frame "]; [destFile WriteText: "]"]; [destFile WriteText: " /PolySmooth "]; break; case 180: //Draw following lines as dashed tempSignedByte = (SignedByte) [sourceFile GetByte]; [destFile WriteText: "[ "]; [destFile WriteInteger: tempSignedByte]; // // Skip the centered argument // [sourceFile GetByte]; // // Write out the dash info, unless our length is way wrong // numIntervals = (SignedByte) [sourceFile GetByte]; if ((numIntervals + 3) != length) { [destFile WriteCommentUsing: buffer WithFormat: "%s (%d expected %d)", "WARNING: Unexpected datalength for DashedLine. doing nothing!", length, numIntervals+3]; [destFile WriteInteger: numIntervals]; [self ConvertHexBytes: length-3]; [destFile WriteText: "]"]; [destFile WritePositiveInteger: kind]; } else { // // All is well, so write out the offset and dash info. // (put this info in additional braces for easier processing. // [destFile WriteText: " [ "]; for (index = 1; index <= numIntervals; index++) { tempSignedByte = (SignedByte) [sourceFile GetByte]; [destFile WriteInteger: tempSignedByte]; } [destFile WriteText: "]"]; [destFile WriteText: " ]"]; [destFile WriteText: " /DashedLine "]; } break; case 182: // Set fractional line widths // // the arguments are v/h which become a fraction to scale the pen in both // dimensions by. // Output: v h div // [destFile WriteInteger: [sourceFile GetINTEGER]]; [destFile WriteInteger: [sourceFile GetINTEGER]]; [destFile WriteText: " div "]; [destFile WriteText: "/SetLineWidth "]; break; case 192: // PostScript data in handle tempString = NewCString(length); [sourceFile Read: length BytesInto: tempString]; tempString[length] = 0; [self WriteString: tempString]; [destFile WriteText: " /PostScriptHandle "]; FreeCString(tempString); break; case 193: // FileName in data handle ² tempString = NewCString(length); [sourceFile Read: length BytesInto: tempString]; tempString[length] = 0; [self WriteString: tempString]; [destFile WriteText: " /PostScriptFile "]; FreeCString(tempString); break; case 195: // PostScript data in a resource ² // // Should be doing some length error checking? // [destFile WriteComment: "WARNING: The ResourcePS values are quite possibly wrong."]; [destFile WriteText: "[ "]; // // Read in the type // tempString = NewCString(4); [sourceFile Read: 4 BytesInto: tempString]; tempString[4] = 0; //93.11.06 Fixed. Was setting tempString[length] to 0, but length is 8 in this case. oops. [self WriteString: tempString]; // // Convert the ID and Index // [destFile WriteInteger: [sourceFile GetINTEGER]]; [destFile WriteInteger: [sourceFile GetINTEGER]]; [destFile WriteText: "]"]; [destFile WriteText: " /ResourcePS "]; FreeCString(tempString); break; case 197: // Call PostScript's setgray operator # [destFile WriteTextUsing: buffer WithFormat: "%.9f ", [sourceFile GetFixedNumber]]; [destFile WriteText: " /SetGrayLevel "]; break; case 200: //Begin rotated port if (length != 8) { [destFile WriteCommentUsing: buffer WithFormat: "%s (%d)", "WARNING: Unexpected datalength for RotateBegin. doing nothing!", length]; [self ConvertHexBytes: length]; [destFile WritePositiveInteger: kind]; } else { [destFile WriteText: "[ "]; [destFile WriteInteger: [sourceFile GetINTEGER]]; //flip [destFile WriteInteger: [sourceFile GetINTEGER]]; //angle [destFile WriteTextUsing: buffer WithFormat: "%.9f ", [sourceFile GetFixedNumber]]; //fixed angle [destFile WriteText: "]"]; [destFile WriteText: " /RotateBegin "]; } break; case 202: //Offset to center of rotation [destFile WriteText: "[ "]; [destFile WriteTextUsing: buffer WithFormat: "%.9f ", [sourceFile GetFixedNumber]]; [destFile WriteTextUsing: buffer WithFormat: "%.9f ", [sourceFile GetFixedNumber]]; [destFile WriteText: "]"]; [destFile WriteText: " /RotateCenter "]; break; default: // read in a length, then that # bytes and hex out. [self ConvertHexBytes: length]; [destFile WritePositiveInteger: kind]; break; } [self WritePSProcedureName: "longComment"]; } } FreeCString(buffer); return error; } ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // Method: ParseShortComment: // Parameters: // A kind of short comment // Returns: self // Stores: none explicitly (subcalls may) // Description: // This routine process short pict comments. At the moment, this merely means // writing out a name or a number for the specified kind of short comment. Names // are written out when we know what about this kind of comment, numbers are // written out otherwise. // ² These comments are obsolete. // # These comments are not recommended. // History: // 93.08.01 Split off from the PicComment conversion code. // Bugs: ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - ParseShortComment: (Integer) kind { switch (kind) { // // Defined in Inside Mac V 1 // case 0: [destFile WriteText: "/picLParen "]; break; case 1: [destFile WriteText: "/picRParen "]; break; // // Mentioned in Tech note as being ImageWriter specific // case 1000: [destFile WriteText: "/BitMapThinningOff "]; break; case 1001: [destFile WriteText: "/BitMapThinningOn "]; break; // // Mainly LaserWriter/PS specific. Defined in Tech Note // case 151: // End text function [destFile WriteText: "/TextEnd "]; break; case 152: // Begin string delimitation [destFile WriteText: "/StringBegin "]; break; case 153: // End string delimitation [destFile WriteText: "/StringEnd "]; break; case 155: // Turn LaserWriter line layout off [destFile WriteText: "/LineLayoutOff "]; break; case 156: // Turn LaserWriter line layout on [destFile WriteText: "/LineLayoutOn "]; break; case 160: // Begin special polygon [destFile WriteText: "/PolyBegin "]; break; case 161: // End special polygon [destFile WriteText: "/PolyEnd "]; break; case 163: // Ignore following polygon data [destFile WriteText: "/PolyIgnore "]; break; case 165: // Close the polygon [destFile WriteText: "/PolyClose "]; break; case 181: // End dashed lines [destFile WriteText: "/DashedStop "]; break; case 190: //Set driver state to PostScript [destFile WriteText: "/PostScriptBegin "]; break; case 191: //Restore QuickDraw state [destFile WriteText: "/PostScriptEnd "]; break; case 194: //QuickDraw text is sent as PostScript ² UsedText = YES; [destFile WriteText: "/TextIsPostScript "]; break; case 196: //Set driver state to PostScript [destFile WriteText: "/PSBeginNoSave "]; break; case 201: //End rotation [destFile WriteText: "/RotateEnd "]; break; case 210: //Don't clear print buffer after each page # [destFile WriteText: "/FormsPrinting "]; break; case 211: // End forms printing after PrClosePage # [destFile WriteText: "/EndFormsPrinting "]; break; default: [destFile WritePositiveInteger: kind]; break; } [self WritePSProcedureName: "shortComment"]; return self; } ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // PROCESSING UTILITIES ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // Method: Skip2PlusData: // Parameters: none // Returns: self // Stores: none explicitly (subcalls may) // Description: // This reads a 16 bit number in, and then moves forward as many bytes as // that specifies in the source file. This is usefull because if we run into an unknown // opcode, it may be of the form 2 bytes of size, and that many bytes of data, and this // allows us to skip over it gracefully. // Bugs: ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - Skip2PlusData { PositiveInteger numBytes; numBytes = [sourceFile GetPositiveINTEGER]; [sourceFile AdvanceBytes: numBytes]; return self; } ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // Method: Skip4PlusData: // Parameters: none // Returns: self // Stores: none explicitly (subcalls may) // Description: // This reads a 32 bit number in, and then moves forward as many bytes as // that specifies. Useful for the same reason as Skip2PlusData:; // Bugs: ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - Skip4PlusData { PositiveInteger numBytes; numBytes = [sourceFile GetPositiveLONGINT]; [sourceFile AdvanceBytes: numBytes]; return self; } ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // Method: SkipSizedObject: // Parameters: none // Returns: self // Stores: none explicitly (subcalls may) // Description: // Read in a size value (2 bytes), and use it's value to skip over all that many remaining // bytes. This differs from Skip2PlusData in that the byte count is part of the number // of bytes we are skipping. // Bugs: ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - SkipSizedObject { PositiveInteger size; size = [sourceFile GetPositiveINTEGER]; [sourceFile AdvanceBytes: size-2]; return self; } ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // Method: ConvertHexBytes: // Parameters: The number of bytes to convert. // Returns: self // Stores: none explicitly (subcalls may) // Description: // read the specified number of bytes from the source file, and write them out as hex // digits in the eps file. use a slow brute force algorithm. =) // Bugs: ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - ConvertHexBytes: (PositiveInteger) theBytes { PositiveInteger byteCounter; CString buffer = NewCString(2); [destFile WriteText: "<"]; for (byteCounter = 0; byteCounter < theBytes; byteCounter++) [destFile WriteTextUsing: buffer WithFormat: "%.2X", (Byte)[sourceFile GetByte]]; [destFile WriteText: "> "]; FreeCString(buffer); return self; } ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // Method: WritePoint: // Parameters: // A Point structure // Returns: self // Stores: none explicitly (subcalls may) // Description: // This simply writes out the pict's x and y coordinates, and then it assumes it // may free the point structure, since that's how it's always used. =) // Bugs: ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - WritePoint: (PICTPoint*) thePoint { [destFile WriteInteger: thePoint->x]; [destFile WriteInteger: thePoint->y]; FreeByteString( (ByteString) thePoint); return self; } ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // Method: WriteRect: // Parameters: // A Rect structure // Returns: self // Stores: none explicitly (subcalls may) // Description: // This simply writes out the rectangle, and then it assumes it // may free the rectangle structure, since that's how it's always used. =) // Bugs: ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - WriteRect: (PICTRect*) theRect { [destFile WriteInteger: theRect->top]; [destFile WriteInteger: theRect->left]; [destFile WriteInteger: theRect->bottom ]; [destFile WriteInteger: theRect->right]; FreeByteString((ByteString)theRect); return self; } ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // Method: WritePSProcedureName: // Parameters: // A CString // Returns: self // Stores: none explicitly (subcalls may) // Description: // This merely writes a procedure name that we are passed, and follows it // with a newline. Any time we are writing one of our procedure names, it'll // be the last thing on a line, so, we hard-code this behavior. // Bugs: ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - WritePSProcedureName: (CString) theName { [destFile WriteText: theName]; [destFile WriteText: "\n"]; return self; } ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // PRIMARY DATA CONVERSION ROUTINES ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // Method: ConvertAngles: // Parameters: none // Returns: self // Stores: none explicitly (subcalls may) // Description: // This reads the information about the angles of a pict arc in from the current position // in the source file, and then converts the information to a form that can be used // easily in the resulting postscript file // The angles for a PICT arc is stored as an initial angle, and the number of degrees from // the start angle to the end of the arc. We write it out as a start angle and a finish // angle. // Bugs: ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - ConvertAngles { Integer startAng, deltaAng, finishAng; // // Read in angles, and compensate for the different position of 0deg in PICT and PS. // then, compute the finish position, given the start and the delta. // startAng = [sourceFile GetINTEGER]; deltaAng = [sourceFile GetINTEGER]; startAng -= 90; // compensate for the fact that PICT 90 is PS 0 (I think)) if (deltaAng > 0) finishAng = startAng + deltaAng; else { finishAng = startAng; startAng = finishAng + deltaAng; } // // To make things prettier, check if the numbers are negative, and if so, add 360. // no promises that they will not still be negative afterwards. but most of the time they // will, and this will make the code easier to understand // @@ Is there a chance that these will write out misleading numbers? That is, it used // to say go from x to x+y. now we say, go from x to z, but can we end up somehow with // z being behind x when it shouldn't? // if (startAng < 0) startAng += 360; if (finishAng < 0) finishAng += 360; [destFile WriteInteger: startAng]; [destFile WriteInteger: finishAng]; return self; } ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // Method: ConvertArc: // Parameters: none // Returns: self // Stores: none explicitly (subcalls may) // Description: // This reads the information about an arc in from the current position in the // source file, and then converts the information to a form that can be used // easily in the resulting postscript file // A PICT arc is stored as a bounding rectangle, which defines overall size and // overall circle-ness/ovalness of the circle/oval we are drawing the arc in. After // this rectangle, the start and finish angles of the arc are stored. // Bugs: ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - ConvertArc { [self WriteRect: [sourceFile GetRect] ]; [self ConvertAngles]; return self; } ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // Method: ConvertPolygon: // Parameters: // Returns: self // Stores: none explicitly (subcalls may) // Description: // A Mac polygon consists of the following: 2 bytes of size, 8 bytes of bounding rect, // and a sequence of points that describe the vertices (including start and end points) of // a polygon. Note that a polygon need not be a closed object. This takes said object, and // writes out the points of the polygon as a series of numbers, these are followed by the // number of poits that we have written and finally the bounding rectangle is written // Bugs: ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - ConvertPolygon { PICTRect* bounds; Integer numPoints, pointsOnLine = 0; Integer RemainingPoints; PICTPoint* thePoint; PositiveInteger size; // // Get the size, and the bounding rectangle, anc compute the number of vertices // using the total size of the object // size = [sourceFile GetPositiveINTEGER]; bounds = [sourceFile GetRect]; numPoints = 0; RemainingPoints = (size-10) / 4; // // For each vertex, write out a little PS array of the form [x y]. Don't put more than // 8 on a line so things will remain readable for any browsing humans. // thePoint = [sourceFile GetPoint]; RemainingPoints--; while ( RemainingPoints >= 0 ) { numPoints++; [destFile WriteText: "["]; [self WritePoint: thePoint]; [destFile WriteText: "]"]; if (pointsOnLine == 8) { pointsOnLine = 0; [destFile ForceNewLine]; } else pointsOnLine++; if (RemainingPoints > 0) thePoint = [sourceFile GetPoint]; RemainingPoints--; // if it was 0, this will drop it below, which will fall out of the main loop } // // Well. That was easy. Now, just write out the number of points, so the ps routine // will be able to consume all the data properly, and write out the bounding rectangle // [destFile WriteInteger: numPoints]; [self WriteRect: bounds]; return self; } ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // Method: ConvertRegion: // Parameters: none // Returns: self // Stores: none explicitly (subcalls may) // Description: // A Mac region is arranged like so: 2 bytes of size of the whole region, 8 bytes of // bounding rectangle, then N groups of data about segments of lins. Each group // of segments has a y coordinate, and then a start and stop x coordinate for // each segment. Anyway, this creates a converter instance, and asks it to // convert the region waiting in the source file, to the destination file. // Bugs: ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - ConvertRegion { Instance regConverter; regConverter= [[regionConverter alloc] init]; [regConverter ConvertRegionFrom: sourceFile To: destFile]; [regConverter free]; return self; } ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // Method: ConvertMode // Parameters: none // Returns: self // Stores: none explicitly (subcalls may) // Description: // This takes a Mac mode type value, and writes out a PS name corresponding to it // History: // 93.07.18 djb added magicMode which is a mysterious pen mode defined in a tech note. // It's effect is, it seems, to be to cause QuickDraw to process stuff, but not // actually draw anything. // Bugs: // This gets called by a routine which wants only src, and one that wants // only pat. Because we do no checking of this, there's no way to keep us from // writing out an illegal mode for that routin. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - ConvertMode { PositiveInteger modeNum; CString buffer; modeNum = [sourceFile GetPositiveINTEGER]; switch (modeNum) { case macSrcCopy: [destFile WriteText: "/srcCopy "]; break; case macSrcOr: [destFile WriteText: "/srcOr "]; break; case macSrcXor: [destFile WriteText: "/srcXor "]; break; case macSrcBic: [destFile WriteText: "/srcBic "]; break; case macNotSrcCopy: [destFile WriteText: "/notSrcCopy "]; break; case macNotSrcXor: [destFile WriteText: "/notSrcXor "]; break; case macNotSrcOr: [destFile WriteText: "/notSrcOr "]; break; case macNotSrcBic: [destFile WriteText: "/notSrcBic "]; break; case macPatCopy: [destFile WriteText: "/patCopy "]; break; case macPatOr: [destFile WriteText: "/patOr "]; break; case macPatXor: [destFile WriteText: "/patXor "]; break; case macPatBic: [destFile WriteText: "/patBic "]; break; case macNotPatCopy: [destFile WriteText: "/notPatCopy "]; break; case macNotPatOr: [destFile WriteText: "/notPatOr "]; break; case macNotPatXor: [destFile WriteText: "/notPatXor "]; break; case macNotPatBic: [destFile WriteText: "/notPatBic "]; break; case blend: [destFile WriteText: "/blend "]; break; case addPin: [destFile WriteText: "/addPin "]; break; case addOver: [destFile WriteText: "/addOver "]; break; case subPin: [destFile WriteText: "/subPin "]; break; case addMax: [destFile WriteText: "/addMax "]; break; case subOver: [destFile WriteText: "/subOver "]; break; case adMin: [destFile WriteText: "/adMin "]; break; case transparent: [destFile WriteText: "/transparent "]; break; case magicMode: [destFile WriteText: "/magicMode "]; break; default: buffer = NewCString(64); [destFile WriteTextUsing: buffer WithFormat: "/badMode%d ", modeNum]; FreeCString(buffer); break; } return self; } ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // Method: ConvertFamily: // Parameters: none // Returns: self // Stores: none explicitly (subcalls may) // Description: // This reads a number in from the pict file. from this number, we either write out the // name of a standard mac type face, or we write out a number. We also set the // ConvertCurrentCharacters flag, which is used when writing out strings // from the text opcodes later. If this is set to NO, then we will not attempt to // convert the text. Otherwise we will. // Bugs: // I dunno whether this is really a positive or a signed integer. No tests were done, // and no doc seems to indicate for sure. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - ConvertFamily { PositiveInteger familyNum; familyNum = [sourceFile GetPositiveINTEGER]; switch (familyNum) { case 0: [destFile WriteText: "/Chicago "]; ConvertCurrentCharacters = YES; break; case 2: [destFile WriteText: "/NewYork "]; ConvertCurrentCharacters = YES; break; case 3: [destFile WriteText: "/Geneva "]; ConvertCurrentCharacters = YES; break; case 4: [destFile WriteText: "/Monaco "]; ConvertCurrentCharacters = YES; break; case 5: [destFile WriteText: "/Venice "]; ConvertCurrentCharacters = YES; break; case 6: [destFile WriteText: "/London "]; ConvertCurrentCharacters = YES; break; case 7: [destFile WriteText: "/Athens "]; ConvertCurrentCharacters = YES; break; case 8: ConvertCurrentCharacters = YES; [destFile WriteText: "/SanFrancisco "]; break; case 9: [destFile WriteText: "/Toronto "]; ConvertCurrentCharacters = YES; break; case 10: [destFile WriteText: "/Seattle "]; ConvertCurrentCharacters = YES; break; case 11: [destFile WriteText: "/Cairo "]; ConvertCurrentCharacters = NO; break; case 12: [destFile WriteText: "/LosAngeles "]; ConvertCurrentCharacters = YES; break; case 13: [destFile WriteText: "/ZapfDingbats "]; ConvertCurrentCharacters = NO; break; case 14: [destFile WriteText: "/Bookman-Light "]; ConvertCurrentCharacters = YES; break; case 15: [destFile WriteText: "/Helvetica-Narrow "]; ConvertCurrentCharacters = YES; break; case 16: [destFile WriteText: "/Palatino-Roman "]; ConvertCurrentCharacters = YES; break; case 18: [destFile WriteText: "/ZapfChancery-MediumItalic "]; ConvertCurrentCharacters = YES; break; case 20: [destFile WriteText: "/Times-Roman "]; ConvertCurrentCharacters = YES; break; case 21: [destFile WriteText: "/Helvetica "]; ConvertCurrentCharacters = YES; break; case 22: [destFile WriteText: "/Courier "]; ConvertCurrentCharacters = YES; break; case 23: [destFile WriteText: "/Symbol "]; ConvertCurrentCharacters = NO; break; case 24: [destFile WriteText: "/Mobile "]; // Also known as Taliesin ConvertCurrentCharacters = NO; break; case 33: [destFile WriteText: "/AvantGarde-Book "]; ConvertCurrentCharacters = YES; break; case 34: [destFile WriteText: "/NewCenturySchlbk-Roman "]; ConvertCurrentCharacters = YES; break; default: [destFile WriteInteger: familyNum]; ConvertCurrentCharacters = UsersCharConvertChoice; break; } return self; } ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // Method: ConvertFace: // Parameters: none // Returns: self // Stores: none explicitly (subcalls may) // Description: // This converts the byte of data describing typeface attributes and writes it // out as a PS array describing the various attributes. // Bugs: ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - ConvertFace { bits8 theFace; theFace = [sourceFile GetByte]; [destFile WriteText:"\[ "]; if (boldBit & theFace) [destFile WriteText:"/bold "]; if (italicBit & theFace) [destFile WriteText:"/italic "]; if (underlineBit & theFace) [destFile WriteText:"/underline "]; if (outlineBit & theFace) [destFile WriteText:"/outline "]; if (shadowBit & theFace) [destFile WriteText:"/shadow "]; if (condenseBit & theFace) [destFile WriteText:"/condense "]; if (extendBit & theFace) [destFile WriteText:"/extend "]; [destFile WriteText:"] "]; return self; } ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // Method: WriteString: // Parameters: A CString structure // Returns: self // Stores: none explicitly (subcalls may) // Description: // This simply writes out specified string as a PS string, in parens, follows it with // a space, and then free's the string. // Bugs: ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - WriteString: (CString) theString { CString convertedString; CString filteredString; CString tempString; PositiveInteger filterIndex; PositiveInteger index; PositiveInteger newLength = strlen(theString); if (ConvertCurrentCharacters == YES) { // // Convert the Mac characters to the NeXT equivalents // convertedString = [textConverter ConvertString: theString WithLength: strlen(theString)]; newLength = [textConverter GetPositiveIntegerFrom: SECOND_RESULT]; FreeCString(theString); theString = convertedString; } // // Now, filter this string, replacing certain troublesome characters with // escaped equivalents. // filteredString = NewCString(newLength*4); // worst case each all will become \ddd filterIndex = 0; for (index = 0; index < newLength; index++) { if ( (PositiveInteger) theString[index] < (PositiveInteger) ' ') { tempString = NewCString(4); // // 93.01.31 djb OOPS! Had been using %.3u, not octal! // sprintf(tempString, "\\%.3o", (unsigned int) theString[index]); strcat(filteredString, tempString); FreeCString(tempString); filterIndex += 4; } else { switch (theString[index]) { case '\\' : strcat(filteredString, "\\\\"); filterIndex += 2; break; case '(' : strcat(filteredString, "\\("); filterIndex += 2; break; case ')' : strcat(filteredString, "\\)"); filterIndex += 2; break; default : filteredString[filterIndex] = theString[index]; filterIndex ++; filteredString[filterIndex] = EndOfCString; // put a null back on. break; } } } // // write out the new string // [destFile WriteText: "("]; [destFile WriteText: filteredString]; [destFile WriteText: ") "]; FreeCString(filteredString); FreeCString(theString); return self; } ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // Method: ConvertOldColor // Parameters: none. // Returns: self // Stores: none explicitly (subcalls may) // Description: // This reads in a four byte old style Mac color, and writes it out as an array of // RGB values. If the color isn't one of the pre-defined ones (in theory, this shouldn't // happen, I think), we instead write out an integer with the number of the color so // the PS code can perhaps deal with it. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - ConvertOldColor { PositiveInteger colorNum; colorNum = [sourceFile GetPositiveLONGINT]; switch (colorNum) { case oldBlackColor: [destFile WriteText: "[0 0 0] "]; break; case oldWhiteColor: [destFile WriteText: "[1 1 1] "]; break; case oldRedColor: [destFile WriteText: "[1 0 0] "]; break; case oldGreenColor: [destFile WriteText: "[0 1 0] "]; break; case oldBlueColor: [destFile WriteText: "[0 0 1] "]; break; case oldCyanColor: [destFile WriteText: "[0 1 1] "]; break; case oldMagentaColor: [destFile WriteText: "[1 0 1] "]; break; case oldYellowColor: [destFile WriteText: "[1 1 0] "]; break; default: [destFile WritePositiveInteger: colorNum]; break; } return self; } ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // Method: ConvertRGBColor // Parameters: none. // Returns: self // Stores: none explicitly (subcalls may) // Description: // read a mac rgb value in, write out as an array of 3 numbers from 0 to 1 for PS // Bugs: // I really can't tell whether to treat these colors as 8 or 16 bit values. I'm assuming // 8 for the moment. change maxMACCOLOR to 65535.0 if // it should be 16. (read in with GetPositiveINTEGER) ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// #define maxMACCOLOR 255.0 - ConvertRGBColor { CString tempString = NewCString(12); [destFile WriteText: "["]; // // Read the red component // [destFile WriteTextUsing: tempString WithFormat: "%.9f ", ( [sourceFile GetByte] / maxMACCOLOR)]; [sourceFile AdvanceBytes: 1]; // // Read the green component // [destFile WriteTextUsing: tempString WithFormat: "%.9f ", ( [sourceFile GetByte] / maxMACCOLOR)]; [sourceFile AdvanceBytes: 1]; // // Read the blue component // [destFile WriteTextUsing: tempString WithFormat: "%.9f ", ( [sourceFile GetByte] / maxMACCOLOR)]; [sourceFile AdvanceBytes: 1]; [destFile WriteText: "] "]; FreeCString(tempString); return self; } ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // BITMAP AND PATTERN PROCESSING ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // Method: ConvertInvertedHexBytes: // Parameters: The number of bytes to convert. // Returns: self // Stores: none explicitly (subcalls may) // Description: // read the specified number of bytes from the source file, and write them out as hex // digits in the eps file, inverting as we go. use a slow brute force algorithm. =) // Bugs: ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - ConvertInvertedHexBytes: (PositiveInteger) theBytes { PositiveInteger byteCounter; CString buffer = NewCString(2); [destFile WriteText: "<"]; for (byteCounter = 0; byteCounter < theBytes; byteCounter++) [destFile WriteTextUsing: buffer WithFormat: "%.2X", (Byte) ~[sourceFile GetByte]]; [destFile WriteText: "> "]; FreeCString(buffer); return self; } ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // Method: ConvertPict1Pattern // Parameters: none. // Returns: self // Stores: none explicitly (subcalls may) // Description: // This merely reads an 'old-style' 8 byte B&W pattern in from the pict file, and writes // it out as a binary string to the PS file. We also write a type (0) of pattern // out so the parser can distinguish this from the other two types. // Bugs: ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - ConvertPict1Pattern { [self ConvertInvertedHexBytes: 8]; [destFile WritePositiveInteger: 0]; return self; } ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // Method: GetPixMapFrom: // Parameters: the file to read a Mac pixmap record structure from // A pixmap structure to read into. // Returns: self // Stores: none // Description: // This reads in a PixMap structure that was written out on a Macintosh. // Because it is from a Mac, it is made up of lots of big endian integers. Because // we may be running on an Intel or other platform, we need to convert these // integers so they are of the right form for the native endianness. We do this in // a brute force method: read in the mac form, create a second structure, and // effectively copy over all the fields, flipping the bytes if necessary as we go. // History: // 93.08.01 djb Created // 93.08.15 djb Argh! I need to be casting the results!!! // 93.08.16 djb Argh! again! Wasn't casting properly in all cases. // Bugs: // I consider it icy design that we are being passed the structuer to fill in, and aren't // just returning on our own (the latter allows more flexibility). However, this // routine was added long after it's callers were, and they were already depending on // their pixmaps structures being allocated on the stack. I didn't want to risk changing // all the foo.bar's to foo->bar's. So, this gets passed a map structure instead. =( ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - GetPixMapFrom: SourceFile Into: (modPixMap*) nativeMap { [sourceFile Read: sizeof(modPixMap) BytesInto: (ByteString) nativeMap]; // // Now, swap all the fields of the pixmap structure from the mac format to whatever // the native one is. . // nativeMap->bounds.top = (INTEGER)NXSwapBigShortToHost(nativeMap->bounds.top); nativeMap->bounds.right = (INTEGER)NXSwapBigShortToHost(nativeMap->bounds.right); nativeMap->bounds.bottom = (INTEGER)NXSwapBigShortToHost(nativeMap->bounds.bottom); nativeMap->bounds.left = (INTEGER)NXSwapBigShortToHost(nativeMap->bounds.left); nativeMap->pmVersion = (INTEGER)NXSwapBigShortToHost(nativeMap->pmVersion); nativeMap->packType = (INTEGER)NXSwapBigShortToHost(nativeMap->packType); nativeMap->packSize = (LONGINT)NXSwapBigLongToHost(nativeMap->packSize); nativeMap->hRes = (FIXED)NXSwapBigLongToHost(nativeMap->hRes); nativeMap->vRes = (FIXED)NXSwapBigLongToHost(nativeMap->vRes); nativeMap->pixelType = (INTEGER)NXSwapBigShortToHost(nativeMap->pixelType); nativeMap->pixelSize = (INTEGER)NXSwapBigShortToHost(nativeMap->pixelSize); nativeMap->cmpCount = (INTEGER)NXSwapBigShortToHost(nativeMap->cmpCount); nativeMap->cmpSize = (INTEGER)NXSwapBigShortToHost(nativeMap->cmpSize); nativeMap->planeBytes = (LONGINT)NXSwapBigLongToHost(nativeMap->planeBytes); nativeMap->pmTable = (LONGINT)NXSwapBigLongToHost(nativeMap->pmTable); nativeMap->pmReserved = (LONGINT)NXSwapBigLongToHost(nativeMap->pmReserved); // // dispose of the source one, and return the destination. // // FreeByteString((ByteString)macMap); return self; } ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // Method: ConvertPict2Pattern // Parameters: none. // Returns: self // Stores: none explicitly (subcalls may) // Description: // This reads in a PICT 2 pattern, massages the data, and writes it out for the // PS file. Inside Mac V. 5 only documents two types (1&2) on p. 103. // Type 2: 8byte B&W pattern, and RGB color. IM V suggests that the pattern is // there only for things to use if they are on a B&W machine. Because PS // can deal with a color, and I can see no reason why it should ever need the // pattern in this case, I am discarding this data! This may be a mistake, though. // Type 1: 'arbitrary' bitmap used as pattern... // Bugs: ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - ConvertPict2Pattern { PositiveInteger patType; PICTRect* bounds = (PICTRect*)NewByteString(sizeof(PICTRect)); modPixMap theMap; PositiveInteger dataHigh, dataWide; PositiveInteger rowBytes; Integer bitsDeep; Real hScale, vScale; CString tempString = NewCString(30); Boolean isPacked; Boolean tempPacking; patType = [sourceFile GetPositiveINTEGER]; [self ConvertInvertedHexBytes: 8]; switch (patType) { case 1 : // // Clear the high bit of rowBytes, read in the pixmap data, and convert // the color table that follows it. (same basic flow of events as in the bitmap // converter below. // rowBytes =0x7FFF & [sourceFile GetPositiveINTEGER]; [self GetPixMapFrom: sourceFile Into: &theMap]; [self ConvertColorTable]; // // move the rectangle bounding data into the bounds rect.. // bounds->left = theMap.bounds.left; bounds->top = theMap.bounds.top; bounds->bottom = theMap.bounds.bottom; bounds->right = theMap.bounds.right; bitsDeep = theMap.pixelSize; [destFile WriteInteger: bitsDeep]; dataHigh = bounds->bottom - bounds->top; dataWide = bounds->right - bounds->left; if (rowBytes < 8) isPacked = NO; else isPacked = YES; // // Start writing out data to the PS file. // [destFile WriteInteger: bounds->left]; [destFile WriteInteger: bounds->top]; // because bottom in pict is top in ps.. [destFile WriteInteger: dataWide]; [destFile WriteInteger: dataHigh]; [destFile ForceNewLine]; // make sure the following data starts on a new line. // // compute the relative scaling ratios of the bitmap (72dpi = 1) // Convert first from fixedpoint data. // hScale = (theMap.hRes / 65535.0) / 72; vScale = (theMap.vRes / 65535.0) / 72; [destFile WriteInteger: (dataWide * hScale)]; [destFile WriteInteger: (dataHigh * vScale)]; [destFile ForceNewLine]; // make sure the following data starts on a new line. // // Write whether the data is packed or not. (for now, only pack because I // could not figure out how to get filters to work inside a pattern proc. // [destFile WriteTextLine: "false"]; // // Now, write out the bitmap data itself converted. // [destFile WriteText: "<"]; // // A hack to wait until we can figure out how to unpack images in PS in // indexed color spaces set packing so we never write out packed images // tempPacking = PackingSetting; PackingSetting = NO; [self ExtractImageDataUsingDataWidth: rowBytes Scanlines: dataHigh ScanlineLength: rowBytes ScanlineStart: 0 AndPixelDepth: bitsDeep IsCompressed: isPacked UsesColor: YES]; // // Write a trailing > which an ASCIIHexEncoding filter understands as EOF. // If needed, also write an 80 which is eof for the RLE filter. // if (PackingSetting == YES) [destFile WriteTextLine: "80>"]; else [destFile WriteTextLine: ">"]; [destFile WritePositiveInteger: 1]; PackingSetting = tempPacking; break; case 2 : // // The 'pattern' is really halftone of the specified color. // That is, you specify color XYZ, but device can't do this, so // QuickDraw will build a raster using colors it can draw, that // will approximate the color your want (specifed by the rgb value) // [self ConvertRGBColor]; [destFile WritePositiveInteger: 2]; break; default : // Should set some kind big error here! break; } FreeCString(tempString); return self; } ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // Method: ConvertBitmapWithRegion:PackedData: // Parameters: Boolean flag // Returns: self // Stores: none explicitly // Description: // This reads a bitmap that may or may not have a clipping region, and may or // may not be packed. It determines whether it is a B&W or color image, and then // extracts the appropriate data structures and writes them out. // Bugs: // We don't examine the data nearly closely enough to assure that it is good. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -ConvertBitmapWithRegion: (Boolean) hasRegion PackedData: (Boolean) isPacked { PICTRect* bounds; PICTRect* sourceRect; PICTRect* destRect; modPixMap theMap; PositiveInteger dataHigh, dataWide; PositiveInteger rowBytes; Integer bytesOut; Integer diff; Integer bitmapOffset; Integer rasterType; Integer bitsDeep; Boolean colorUse; rowBytes = [sourceFile GetPositiveINTEGER]; // Retrieves a Mac INTEGER (16 bits) // // Inside Mac V says that if the high bit of rowBytes is set, then the picture is // essentially one with multiple bits per pixel. SO. Check the high bit (mask the // rowBytes and check if zero or not), and also check for Pict version number. // if the version is 1, then ignore that the high bit is set. // In either case, process all the leading information up to the bitmap data. // if (((rowBytes & 0x8000) == 0) || ([sourceFile GetVersion] == 1)) { // // Write the two 'dummy' pieces of data so that the output from the two pict // types of bitmaps have the same parameters, allowing one more flexibility in // writing PS code. // [destFile WritePositiveInteger: 1]; // num colors -1 [destFile WriteTextLine: "< 000000 FFFFFF >"]; bounds = [sourceFile GetRect]; rasterType = 1; bitsDeep = 1; } else { // // Clear the high bit of rowBytes, read in the pixmap data, and convert // the color table that follows it. // rowBytes = 0x7FFF & rowBytes; [self GetPixMapFrom: sourceFile Into: &theMap]; [self ConvertColorTable]; // // move the rectangle bounding data into the bounds rect.. // bounds = (PICTRect*)NewByteString(sizeof(PICTRect)); bounds->left = theMap.bounds.left; bounds->top = theMap.bounds.top; bounds->bottom = theMap.bounds.bottom; bounds->right = theMap.bounds.right; rasterType = 2; bitsDeep = theMap.pixelSize; } [destFile WriteInteger: bitsDeep]; // // Read the source and dest rectangles in from the bitmap // sourceRect = [sourceFile GetRect]; destRect = [sourceFile GetRect]; // // Start writing out data to the PS file. // Convert and write out the transver mode // Write the left and bottom coordinates of the destination rectangle // (note that bottom in PS coords is top in PICT) // Write the height and width of the destination rectangle // Write the height and width of the source rectangle // IF there is a region, convert it. In either case, write a flag indicating whether // there is a region written out. // [self ConvertMode]; [destFile WriteInteger: destRect->left]; [destFile WriteInteger: destRect->top]; // because bottom right in pict is top in ps.. [destFile WriteInteger: destRect->right - destRect->left]; [destFile WriteInteger: destRect->bottom - destRect->top]; [destFile WriteInteger: sourceRect->right - sourceRect->left]; [destFile WriteInteger: sourceRect->bottom - sourceRect->top]; [destFile ForceNewLine]; // make sure the following data starts on a new line. // // Write whether the data is packed or not. // if (PackingSetting == YES) [destFile WriteTextLine: "true"]; else [destFile WriteTextLine: "false"]; if (hasRegion == YES) { [self ConvertRegion]; [destFile WriteTextLine: "true"]; } else [destFile WriteTextLine: "false"]; // // Compute data needed for consumption of the bitmap. Specifically: // Determine how many pixels high and wide the image rectangle is. // This figure very likely differs from the rowBytes width, so figure out exactly // how many BYTES are in the width of the bitmap to go out. // Then, figure out what the offset of the start of this bitmap in the raw data is, // since strangely a bitmapped image is not always left justified in a pict bitmap... // dataHigh = bounds->bottom - bounds->top; dataWide = sourceRect->right - sourceRect->left; diff = ( (rowBytes*8) - (dataWide *bitsDeep) ) / 8; bytesOut = rowBytes -diff; // remove any 'fluf' bytes. bitmapOffset = sourceRect->left - bounds->left; // there may be a strange offset // // Now, write out the command to PS, and then have the bitmap data itself converted. // if (rasterType == 1) { colorUse = NO; [destFile WriteTextLine: "bitmap"]; } else { colorUse = YES; [destFile WriteTextLine: "colorbitmap"]; } [self ExtractImageDataUsingDataWidth: rowBytes Scanlines: dataHigh ScanlineLength: bytesOut ScanlineStart: bitmapOffset AndPixelDepth: bitsDeep IsCompressed: isPacked UsesColor: colorUse]; // // Write a trailing > which an ASCIIHexEncoding filter understands as EOF. // If needed, also write an 80 which is eof for the RLE filter. // if (PackingSetting == YES) [destFile WriteTextLine: "80>"]; else [destFile WriteTextLine: ">"]; FreeByteString((ByteString)bounds); FreeByteString((ByteString)sourceRect); FreeByteString((ByteString)destRect); return self; } ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // Method: ExtractPackedImageDataUsingDataWidth:Scanlines:ScanlineLength: // ScanlineStart:AndPixelDepth:IsCompressed:UsesColor: // Parameters: // A count of the number of bytes per row of raw bit data // A count of the number of scanlines in the file // The number of bytes per scanline // The number of bits offset into the raw scanline data that the bitmap starts // The number of bits that represent a pixel // A flag indicating whether the data is compressed // A flag indicating whether color was used. // Returns: self // Stores: none // Description: // This consumes the packed raw data from the sourcefile, interpreting it as bitmap // data using the parameters given. This raw data can be viewed as a rectangle of // DataWidth bytes wide and Scanlines rows tall. The interesting part of the bitmap // is in the sub-rectangle that is scanlines rows tall and scanlinelength bytes wide. // This subrectangle may be identical to the first, or smaller (in which case, it may be // inset on the left by scanlinesstart bits.) If the data is packed, then we unpack it // one scanline at a time, and write it out, inverting it if it is B&W, and packing it if // the master object has told us to. // The failing of this routine, perhaps, is that it attempts to do everything at once // (it used to be at least two that did a lot of similar, and a bit of different things), so // it gets a bit complex near the end... // Bugs: // It should be possible to bypass the unpacking and repacking for color images. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - ExtractImageDataUsingDataWidth: (PositiveInteger) rawWidth Scanlines: (Integer) numLines ScanlineLength: (Integer) bytesPerScanline ScanlineStart: (Integer) bitOffset AndPixelDepth: (Integer) numBits IsCompressed: (Boolean) sourcePacked UsesColor: (Boolean) ColorUsed { PositiveInteger index; Integer byteCount; Integer lastSize = rawWidth; ByteString preLine = NewByteString(rawWidth); ByteString destLine = NewByteString (rawWidth); ByteString sourceLine = NewByteString (rawWidth); ByteString packedLine = NewByteString(rawWidth*1.5); // crude worst pack case Integer numOut; ByteString temp; Real fileSize = ( [sourceFile FileSize] * 1.0); // divide the above by 100 now so later division will yeild number between 0 and 100 for (index = 0; index < numLines; index ++) { // // Let the user know that we're actually doing something. // if ((index % 35) == 0) { if ( [myManager respondsTo:@selector(SetPercentageDone:)] ) [myManager SetPercentageDone: ([sourceFile GetCurrentPosition] * 100.0) / fileSize]; } // // Read in the source bitmap line, unpacking it if necessary // if (sourcePacked == NO) [sourceFile Read: rawWidth BytesInto: sourceLine]; else { // // Get the length of the packed record. (if rawWidth > 250, we use a 16 bit // integer to determine the size. This is documented on p. 105 of IM V. // I do NOT know if this applies to PICT 1 data, but I'm assuming so.) // if (rawWidth > 250) byteCount = [sourceFile GetINTEGER]; else byteCount = [sourceFile GetByte]; // // If the lastpreLine buffer won't hold the whole packed data, then free it and // allocate a new one (this prevents needing to reallocate *every* time, // without our needing to necessarily allocate a buffer of maximum size // (whatever that would be) // if (byteCount > lastSize) { FreeByteString(preLine); preLine = NewByteString (byteCount); lastSize = byteCount; } // // Read in the packed data and unpack it. // [sourceFile Read: byteCount BytesInto: preLine]; [self Unpack: rawWidth BytesFrom: preLine Into: sourceLine]; } ////// // At this point, we should have our raw non-packed scanline data. ////// // // If the bitdata is offset, then shift it left appropiately, and reverse pointers from // dest and source lines (this allows the following routine to use the same variables // regardless of whether we did this or not. // if (bitOffset != 0) { [self Copy: (bytesPerScanline*8) BitsAtOffset: bitOffset From: sourceLine Into: destLine]; temp = destLine; destLine = sourceLine; sourceLine = temp; } // // Invert the data if it is B&W, and then pack the data if the manager has asked us // to (set temp to point to final buffer, and numOut to be it's length) // if ( (numBits == 1) && (ColorUsed == NO) ) [self Invert: bytesPerScanline In: sourceLine]; if (PackingSetting == YES) { numOut = [self Pack: bytesPerScanline BytesFrom: sourceLine Into: packedLine]; temp = packedLine; } else { numOut = bytesPerScanline; temp = sourceLine; } // // write out the scanline of bits as PS hex data. // [destFile Write: numOut BytesOfHexDataFrom: temp]; } FreeByteString (sourceLine); FreeByteString (destLine); FreeByteString (packedLine); FreeByteString (preLine); return self; } ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // Method: ConvertColorTable // Parameters: none // Returns: self // Stores: none // Description: // This reads a PICT2 color table from the source file (it assumes that the // source file is positioned at the start of one), and converts it into a PS Level 2 // indexed colorspace definition. // Note: PICT Color tables use 16 bits per color component, in terms of storage. // Still, I've not seen any using more than the 8 bits (to get a total of 24bit color). // I'm sure it's possible there are 48 bit color values somewhere out there, but I // also can't find a definition of how to determine this. It probably has to do with // some of the other values in the pixmap which IM V defines as being set to 0 // Note: I did run across a pixel paint file which use only 8 bits, and then wrote a // number (e.g. AA01 A301 B201 for a color value of AA A3 B2, and incremented to // 02 for next, etc), so it seems that one must assume 8 at least for now... // Bugs: ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - ConvertColorTable { PositiveInteger numEntries; PositiveInteger index; CString buffer = NewCString(40); Byte Red, Green, Blue; // // Skip the color table's seed and flag values // [sourceFile AdvanceBytes: 6]; // // Get the number of color entries in the table (0 origin) // numEntries = [sourceFile GetPositiveINTEGER]; // // Start off the PS color table with the start of the indexed color space array // [destFile WritePositiveInteger: numEntries]; [destFile WriteText: "<"]; // // Write out the rgb color data. Put a limited number of color values on an output line // for (index = 0; index <= numEntries; index++) { [sourceFile AdvanceBytes: 2]; // skip over the index (assure they are sequential?) // // Read the color components in. Note that PICT uses 16 bits per color component, // however, we can only use 8 here due to the indexed color space strategy I'm using // in PS. This actually seems to be not loose anything as far as I've seen so far. // Red = [sourceFile GetByte]; [sourceFile AdvanceBytes: 1]; Green = [sourceFile GetByte]; [sourceFile AdvanceBytes: 1]; Blue = [sourceFile GetByte]; [sourceFile AdvanceBytes: 1]; // // If we've not written a nultiple of 9 colors, just write out the color in FF4502 form // (FF red, 45 green, 02 blue). If we have written 9, then put a newline afterwards // if (((index+1) % 9) != 0) [destFile WriteTextUsing: buffer WithFormat: "%.2x%.2x%.2x ", Red, Green, Blue]; else [destFile WriteTextUsing: buffer WithFormat: "%.2x%.2x%.2x\n", Red, Green, Blue]; } // // Close the array and quit // [destFile WriteTextLine: ">"]; FreeCString(buffer); return self; } ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // Method: ConvertDirectBitmapWithRegion: // Parameters: Boolean flag // Returns: self // Stores: none explicitly // Description: // This routines reads in and processes one of the 'direct' bitmap structres in a // pict file (opcodes 9a and 9b). These are bitmaps that have no clut's, but are direct // representations of color values in the pixels. // History: // 93.08.21 djb Created. // Bugs: // 16bit images are completely ignored. I'm not convinced I've got all // 32bit cases dealt with. Too little resources to test with. // Yep, properly rowbytes should be part of the pixmap. However, it had to be // examind in advance in the other bitmap conversion routine, since the pixmap // isn't there for older bitmaps, and I didn't figure it was worth a second pixmap // reading routine given that. (similar for the baseaddress field) ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -ConvertDirectBitmapWithRegion: (Boolean) hasRegion { PICTRect* sourceRect = NULL; PICTRect* destRect = NULL; modPixMap theMap; PositiveInteger dataHigh, dataWide; PositiveInteger rowBytes; Integer bytesOut; Integer diff; Integer bitmapOffset; Integer bitsDeep; Integer index; Integer count; Integer decider; // // Skip the baseaddress field. It should just contain 0x000000FF, anyway. // [sourceFile GetLONGINT]; // // Read in rowbytes, but do nothing with it for now, and then read in the pixmap. // rowBytes = [sourceFile GetPositiveINTEGER]; rowBytes = 0x7FFF & rowBytes; [self GetPixMapFrom: sourceFile Into: &theMap]; // // Insert a real hack. Someday we may indeed want to process 16 bit // images. But, for now the ambiguity in Inside mac, coupled with a lack // of samples here, coupled with the fact that I have no personal interest in // getting it done just now (someday, but not just now), really does motivate // me to put this hack in. // if (theMap.pixelSize == 16) { [self StoreErrorCode: ERR_NOSIXTEEN AndText: "Convert PICT found a 16bit image. Alas, this version is not able to process those. Your image can not be converted here."]; goto HACKGOTO; } // // Read the source and dest rectangles in from the bitmap // sourceRect = [sourceFile GetRect]; destRect = [sourceFile GetRect]; // // Start writing out data to the PS file. // Convert and write out the transfer mode // Write the left and bottom coordinates of the destination rectangle // (note that bottom in PS coords is top in PICT) // Write the height and width of the destination rectangle // Write the height and width of the source rectangle // IF there is a region, convert it. In either case, write a flag indicating whether // there is a region written out. // [self ConvertMode]; [destFile WriteInteger: destRect->left]; [destFile WriteInteger: destRect->top]; // because bottom right in pict is top in ps.. [destFile WriteInteger: destRect->right - destRect->left]; [destFile WriteInteger: destRect->bottom - destRect->top]; [destFile WriteInteger: sourceRect->right - sourceRect->left]; [destFile WriteInteger: sourceRect->bottom - sourceRect->top]; [destFile ForceNewLine]; // make sure the following data starts on a new line. if (PackingSetting == YES) [destFile WriteTextLine: "true"]; else [destFile WriteTextLine: "false"]; if (hasRegion == YES) { [self ConvertRegion]; [destFile WriteTextLine: "true"]; } else [destFile WriteTextLine: "false"]; // // Compute data needed for consumption of the bitmap. Specifically: // Determine how many pixels high and wide the image rectangle is. // This figure very likely differs from the rowBytes width, so figure out exactly // how many BYTES are in the width of the bitmap to go out. // Then, figure out what the offset of the start of this bitmap in the raw data is, // since strangely a bitmapped image is not always left justified in a pict bitmap... // dataHigh = theMap.bounds.bottom - theMap.bounds.top; dataWide = sourceRect->right - sourceRect->left; diff = ( (rowBytes*8) - (dataWide *bitsDeep) ) / 8; bytesOut = rowBytes -diff; // remove any 'fluf' bytes. bitmapOffset = sourceRect->left - theMap.bounds.left; // there may be a strange offset // // Now, write out the command to PS, and then have the bitmap data itself converted. // [destFile WriteTextLine: "24bitBitmap"]; // // Set up a value, and then switch on it to determine what packing type we are using. // decider = theMap.packType; if (rowBytes < 8) decider = 1; switch (decider) { case 1: [self ConvertPackType1WithHeight: dataHigh AndWidth: rowBytes AndOffiset: bitmapOffset]; break; case 2: [self ConvertPackType2WithHeight: dataHigh AndWidth: rowBytes AndOffiset: bitmapOffset]; break; case 3: // 16 bit image. Skipped by thing above break; case 4: [self ConvertPackType4WithHeight: dataHigh AndWidth: rowBytes AndOffiset: bitmapOffset]; break; default: if (theMap.packType < 0) { [self StoreErrorCode: ERR_BADPACK AndText: "Encountered an unknown packType when converting an image. Your PICT file may be bad."]; } else { for (index = 1; index <= dataHigh; index++) { if (rowBytes > 250) count = [sourceFile GetINTEGER]; else count = [sourceFile GetByte]; [sourceFile AdvanceBytes: count]; } [self StoreErrorCode: ERR_UNKNOWNPACK AndText: "Encountered an unknown packType when converting an image. Unable to convert it."]; } break; } HACKGOTO: FreeByteString((ByteString)sourceRect); FreeByteString((ByteString)destRect); return self; } ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // Method: ConvertPackType1WithHeight:AndWidth:AndOffset: // Parameters: // height of bitmap (in pixels) // width of bitmap (pixels) // count of bitmap offset. // Returns: self // Stores: none explicitly // Description: // This extracts a 'direct' bitmap that was stored with packing type 1. // Apparently, this is used only with 16 and 32bit color images. // In this packing scheme, the pixel values are not altered at all. So, we do // nothing with them but write them out in hex, and potentially rle // encode them.. // History: // 93.08.21 djb Created. // Bugs: // We ignore the offset value. It's conceivable, but I don't know if it's probable, // that the bitmap would not be left aligned. If not, we would need the offset to // fix the problem. This probably only occurrs with bitmaps whose pixel size is // less than a byte. // This will fail to work for 16 bit graphics, actually. However, since our // caller discards 16 bit values for now, we can also ignore them. // Warning! Rowbytes may need to be multiplied by .75! if this is 32 bit ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - ConvertPackType1WithHeight: (Integer) dataHigh AndWidth: (Integer) rowBytes AndOffiset: (Integer) bitmapOffset { Integer index; Integer bytesPerRow; ByteString scanline; ByteString packedline; Integer packlength; bytesPerRow = rowBytes; scanline = NewByteString(bytesPerRow); packedline = NewByteString(bytesPerRow*2); for (index = 1; index <= dataHigh; index++) { [sourceFile Read: bytesPerRow BytesInto: scanline]; if (PackingSetting == YES) { packlength = [self Pack: bytesPerRow BytesFrom: scanline Into: packedline]; [destFile Write: packlength BytesOfHexDataFrom: packedline ]; } else [destFile Write: bytesPerRow BytesOfHexDataFrom: scanline]; } FreeByteString(packedline); FreeByteString(scanline); return self; } ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // Method: ConvertPackType2WithHeight:AndWidth:AndOffset: // Parameters: // height of bitmap (in pixels) // width of bitmap (pixels) // count of bitmap offset. // Returns: self // Stores: none explicitly // Description: // This extracts a 'direct' bitmap that was stored with packing type 2. // This packing type is used only for 32bit images where each pixel is // represented by 3 componets of 8 bytes each (RGB) and one spare 8 byte component. // In this packing scheme, that spare component is removed. We simply read the // bitmap data in, and turn around and write it back out. // History: // 93.08.21 djb Created. // Bugs: // We ignore the offset value. It's conceivable, but I don't know if it's probable, // that the bitmap would not be left aligned. If not, we would need the offset to // fix the problem. This probably only occurrs with bitmaps whose pixel size is // less than a byte. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - ConvertPackType2WithHeight: (Integer) dataHigh AndWidth: (Integer) rowBytes AndOffiset: (Integer) bitmapOffset { Integer bytesPerRow; ByteString scanline; ByteString packedline; Integer packlength; bytesPerRow = rowBytes * .75; // so sayeth the color quickdraw section of inside mac VI scanline = NewByteString(bytesPerRow); packedline = NewByteString(bytesPerRow*2); [sourceFile Read: bytesPerRow BytesInto: scanline]; if (PackingSetting == YES) { packlength = [self Pack: bytesPerRow BytesFrom: scanline Into: packedline]; [destFile Write: packlength BytesOfHexDataFrom: packedline]; } else [destFile Write: bytesPerRow BytesOfHexDataFrom: scanline]; FreeByteString(packedline); return self; } ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // Method: ConvertPackType4WithHeight:AndWidth:AndOffset: // Parameters: // height of bitmap (in pixels) // width of bitmap (pixels) // count of bitmap offset. // Returns: self // Stores: none explicitly // Description: // This extracts a 'direct' bitmap that was stored with packing type 4. // This packing type is used only for 32bit images where there are 3 components used // per pixel. The scheme is simple. Each scanline is compressed using packbits. // History: // 93.08.21 djb Created. // Bugs: // We ignore the offset value. It's conceivable, but I don't know if it's probable, // that the bitmap would not be left aligned. If not, we would need the offset to // fix the problem. This probably only occurrs with bitmaps whose pixel size is // less than a byte. // If the image happens to have 4 components per pixel, we are going to die. // The doc says nothing about manipulating rowbytes, explicitly. Yet, the example // of this type that i have had it multiplied by .75 (to take into account the lack of // a fourth component. It may be simplistic to assume I should always be doing this? ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - ConvertPackType4WithHeight: (Integer) dataHigh AndWidth: (Integer) rowBytes AndOffiset: (Integer) bitmapOffset { Integer index; Integer bytesPerRow; ByteString weirdline; ByteString inputline; ByteString sortedline; PositiveInteger count; PositiveInteger numPixels; PositiveInteger pixel; ByteString redAddr; ByteString greenAddr; ByteString blueAddr; bytesPerRow = .75 * rowBytes; // See above. numPixels = .25 * rowBytes; // get length of one set of components inputline = NewByteString(bytesPerRow*2); weirdline = NewByteString(bytesPerRow); sortedline = NewByteString(bytesPerRow); for (index = 1; index <= dataHigh; index++) { if (rowBytes > 250) count = [sourceFile GetPositiveINTEGER]; else count = [sourceFile GetByte]; [sourceFile Read: count BytesInto: inputline]; // // Now, we must unpack the data, because as it stands, it is made up of // the RGB values, with all the R values first, then all the G, etc... // So, we unpack it, and then reorganize the bytes that make up the components // of each pixel, sorting them into an RGB order for the postscript routine. // [self Unpack: bytesPerRow BytesFrom: inputline Into: weirdline]; redAddr = weirdline; greenAddr = &weirdline[numPixels]; blueAddr = &weirdline[numPixels * 2]; for (pixel = 0; pixel < numPixels; pixel++) { sortedline[pixel*3] = redAddr[pixel]; sortedline[(pixel*3)+1] = greenAddr[pixel]; sortedline[(pixel*3)+2] = blueAddr[pixel]; } // // Whew! Now that that is done, we turn around and pack the line (if needed) // and write it out. // if (PackingSetting == YES) { // // Reuse inputline and count. What the heck. // count = [self Pack: bytesPerRow BytesFrom: sortedline Into: inputline]; [destFile Write: count BytesOfHexDataFrom: inputline]; } else { [destFile Write: bytesPerRow BytesOfHexDataFrom: sortedline]; } } FreeByteString(sortedline); FreeByteString(weirdline); FreeByteString(inputline); return self; } ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // Method: Unpack:BytesFrom:Into: // Parameters: // A count of the number of bytes to unpack // A source string // A destination string. // Returns: self // Stores: none // Description: // This repeatedly unpacks runs from the source line, and stores them into // destline. It stops only when numBytes have been written to destLine. // NOTE: This, then, watches for no stop marker in the sourceLine. If data is // malformed, or numBytes is bad, this may careen into space that doesn't belong to it. // It is also assumed that destLine is large enough for whatever gets unpacked. // This algorithm is, I believe, documented in, Adobe's PS level 2 reference manual // reference manual as their simple RLE filter and in an Apple tech note on PackBits // Bugs: ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - Unpack: (PositiveInteger) numBytes BytesFrom: (ByteString) sourceLine Into: (ByteString) destLine { Byte flag; Byte dataByte; Integer index; Integer sourceIndex = 0; Integer destIndex = 0; do { flag = sourceLine[sourceIndex]; sourceIndex++; if (flag < 128) { // // Copy one more than the number of bytes specified by flag litterally. // flag++; for (index = 0; index < flag; index++) { destLine[destIndex] = sourceLine[sourceIndex]; sourceIndex++; destIndex++; } } else { // // Copy the next byte 257-flag times // dataByte =sourceLine[sourceIndex]; sourceIndex++; for (index = 0; index < (257 -flag); index ++) { destLine[destIndex] = dataByte; destIndex++; } } } while (destIndex < numBytes); return self; } ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // Method: Pack:BytesFrom:Into: // Parameters: // A count of the number of bytes to pack // A source string // A destination string. // Returns: the size of the packed data // Stores: none // Description: // This will pack the source line into the dest line, stopping only after it has written // lineLength bytes from source. This assumes that the length parameter is good, // and that the source buffer has that many bytes, and that dest buffer has enough // space for the packing (I really should calculate a worst case scenario some time) // This data format is, I believe, documented in, Adobe's PS level 2 reference manual // reference manual as their simple RLE filter and in an Apple tech note on PackBits // Bugs: // This routine is not commented enough. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -(PositiveInteger) Pack: (PositiveInteger) lineLength BytesFrom: (ByteString) source Into: (ByteString) dest { Boolean runs = NO; Integer numTraversedBytes; Integer startByte = 0; Integer index = 0; Integer destIndex = 0; Integer ctr; index = 0; do { startByte = index; // // first Byte is always assumed to be OK. Safe assumption. Either // we are starting (Byte 0 should always be good), or we just dumped // some data, at which index points to the first Byte that did not match // that pattern, and so is good to use for starting with the next // index++; // // Try to accumulate a run // while ((source[index-1] == source[index]) && ((index-startByte) < 128) && (index < lineLength)) { index++; } if ((index-startByte) > 2) runs = YES; else { // // not enough to justify a run, so carry on. as a litteral sequence of Bytes // while ((source[index-1] != source[index]) && ((index-startByte) < 128) && (index < lineLength)) { index++; } if (index < lineLength) // We ran into a match, not the end { index--; } // Thus, back up over 1 of the 2 Bytes. runs = NO; } numTraversedBytes = index-startByte; if (runs == YES) { dest[destIndex] = 257-numTraversedBytes; destIndex++; dest[destIndex] = source[startByte]; destIndex++; } else { dest[destIndex] = numTraversedBytes - 1; destIndex++; for (ctr = startByte; ctr < startByte+numTraversedBytes; ctr++) { dest[destIndex] = source[ctr]; destIndex++; } } } while (index < lineLength); return destIndex; //return a number that is the num of bytes in the destination } ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // Method: Invert:In // Parameters: A number of bytes to convert, and the buffer to do it in. // Returns: self // Stores: none // Description: // This simply inverts numBytes bytes in theBuffer. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - Invert: (Integer) numBytes In: (ByteString) theBuffer; { Integer index; for (index = 0; index < numBytes; index++) theBuffer[index] = ~(theBuffer[index]); return self; } ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // Method: Copy:BitsAtOffset:From:Into: // Parameters: The number of bits in the source data to be copied // The number of bits, offset from the start of source, where source data starts // The source byte string to copy from // The destination byte string to copy into // Returns: The destination pointer (identical to the one passed) // Stores: error code // 1 The destination pointer (as a Pointer) // 2 The count of the number of bytes stored in the destination // Description: // This routine can be used to extract arbitrary bit patterns out of a source string. // It was created to serve two different purposes: to allow one to get a bitmap from // a PICT image so it was left justified in its storage space, rather than offset some // number of bits in. The other use was to extract the characters of a Mac font, // one scanline at a time, from the single bitmap tht makes up a Mac font. // Ultimately, the work is simple: take a source string, and an offset into it where // the data resides (and the length of the source data). Copy the relevant bits from // the source to the dest. Note that this aligns the left of the source data to the // beginning of a byte boundrary in dest. Also note that it fills up an integral number // of bytes in dest, filling the last out with 0's if needed. // Bugs: ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - (ByteString) Copy: (PositiveInteger) sourceSize BitsAtOffset: (PositiveInteger) offset From: (ByteString) source Into: (ByteString) dest { PositiveInteger firstByte, lastByte; // First and last bytes in source string to be copied PositiveInteger index, destIndex; // indices into source and dest strings PositiveInteger offsetInByte; // Number of bits into a byte a dest byte starts Byte byte1, byte2; // Temporary bytes to hold info as copied Byte mask; PositiveInteger bitpos, bitsMoved, finalBits; [self ResetResults]; if (sourceSize == 0) { [self StoreErrorCode: ERR_BADSOURCESIZE AndText: "You requested for 0 bytes. This has been fulfilled, weird as it is."]; dest[0] = 0; [self StorePointer: (Pointer) dest]; [self PutPositiveInteger: 0 Into: SECOND_RESULT]; } else { // // Computer our start and end position in source string, in bytes, and compute what // bit offset in the first byte we should start working at // firstByte = offset / 8; lastByte = ((offset+sourceSize-1) / 8); offsetInByte = offset % 8; // Zero origin... // // Bear in mind that it's most likely that the destination byte will straddle two bytes // in the source string. So. Get two bytes at a time from the source, and bitshift // so they each contain only the bits that are relevant to the destination byte (get its // upper and lower parts), then fuse the two parts together into a single destination // byte. Increment our destination index, and store the upper of the two bytes, for // it will be the lower one the next time around. // Following this, deal with any remaining bits that might belong to the final // dest byte, and pad them with zero. // destIndex = 0; // // Two major cases: our data resides in only a single byte, or it spans // two or more bytes: // if (firstByte == lastByte) { // // First, shift the data in the byte down so it is aligned with the start // of the byte. // Then Build a mask to clear out any extraneous bits that were above the // source data. We're too lazy to actully determine whether there is any // junk (there would be if the offset to the data + the length < 8 bits), and // so, build a mask of sourceSize length, and use it. // byte1 = source[firstByte] << offsetInByte; mask = 0x00; for (bitpos = 0; bitpos < sourceSize; bitpos ++) { mask = mask >> 1; mask |= 0x80; } dest[destIndex] = byte1 & mask; destIndex++; } else { // // Start off by pulling off contiguous groups of 8 bits from // the source, and storing them as bytes in the destination. // for (index = firstByte; index < lastByte; index++) { byte1 = source[index] << offsetInByte; byte2 = source[index+1] >> ( 8 - offsetInByte); dest[destIndex] = byte1 | byte2; destIndex++; } // // There are 3 possible states at this point: // - We happen to have copied exactly the number of source bits to the dest. // - We have copied too many bits to the dest (i.e. our final byte2 group had // too many source bits in it) // - We have not copied enough bits (i.e. there were bits in source[lastByte] that // were not moved with those in byte2). // Process each of these accordingly. // // Calculate: // Bits copied to dest in byte2: // Bits source bits actually in source[lastbyte] // bitsMoved = offsetInByte; finalBits = (offset+sourceSize) - (lastByte*8); // // There are a few bits which we have not yet moved. // if (finalBits > bitsMoved) { byte1 = source[index] << bitsMoved; mask = 0x00; for (bitpos = 0; bitpos < (finalBits - bitsMoved); bitpos ++) { mask = mask >> 1; mask |= 0x80; } dest[destIndex] = byte1 & mask; destIndex++; } // // Check if we moved more bits than we should have // else if (finalBits < bitsMoved) { mask = 0x00; for (bitpos = 0; bitpos < finalBits+( 8 - offsetInByte); bitpos ++) { mask = mask >> 1; mask |= 0x80; } dest[destIndex-1] = dest[destIndex-1] & mask; } } [self StoreErrorCode: ERR_OK AndText: "No errors (big surprise)"]; [self StorePointer: (Pointer) dest]; [self PutPositiveInteger: destIndex Into: SECOND_RESULT]; } return dest; } @end
These are the contents of the former NiCE NeXT User Group NeXTSTEP/OpenStep software archive, currently hosted by Netfuture.ch.