// $Id: freeeps.cpp 1314 2006-06-14 08:58:56Z builder1 $ /* @@tag:xara-cn@@ DO NOT MODIFY THIS LINE ================================XARAHEADERSTART=========================== Xara LX, a vector drawing and manipulation program. Copyright (C) 1993-2006 Xara Group Ltd. Copyright on certain contributions may be held in joint with their respective authors. See AUTHORS file for details. LICENSE TO USE AND MODIFY SOFTWARE ---------------------------------- This file is part of Xara LX. Xara LX is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License version 2 as published by the Free Software Foundation. Xara LX and its component source files are 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 Xara LX (see the file GPL in the root directory of the distribution); if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA ADDITIONAL RIGHTS ----------------- Conditional upon your continuing compliance with the GNU General Public License described above, Xara Group Ltd grants to you certain additional rights. The additional rights are to use, modify, and distribute the software together with the wxWidgets library, the wxXtra library, and the "CDraw" library and any other such library that any version of Xara LX relased by Xara Group Ltd requires in order to compile and execute, including the static linking of that library to XaraLX. In the case of the "CDraw" library, you may satisfy obligation under the GNU General Public License to provide source code by providing a binary copy of the library concerned and a copy of the license accompanying it. Nothing in this section restricts any of the rights you have under the GNU General Public License. SCOPE OF LICENSE ---------------- This license applies to this program (XaraLX) and its constituent source files only, and does not necessarily apply to other Xara products which may in part share the same code base, and are subject to their own licensing terms. This license does not apply to files in the wxXtra directory, which are built into a separate library, and are subject to the wxWindows license contained within that directory in the file "WXXTRA-LICENSE". This license does not apply to the binary libraries (if any) within the "libs" directory, which are subject to a separate license contained within that directory in the file "LIBS-LICENSE". ARRANGEMENTS FOR CONTRIBUTION OF MODIFICATIONS ---------------------------------------------- Subject to the terms of the GNU Public License (see above), you are free to do whatever you like with your modifications. However, you may (at your option) wish contribute them to Xara's source tree. You can find details of how to do this at: http://www.xaraxtreme.org/developers/ Prior to contributing your modifications, you will need to complete our contributor agreement. This can be found at: http://www.xaraxtreme.org/developers/contribute/ Please note that Xara will not accept modifications which modify any of the text between the start and end of this header (marked XARAHEADERSTART and XARAHEADEREND). MARKS ----- Xara, Xara LX, Xara X, Xara X/Xtreme, Xara Xtreme, the Xtreme and Xara designs are registered or unregistered trademarks, design-marks, and/or service marks of Xara Group Ltd. All rights in these marks are reserved. Xara Group Ltd, Gaddesden Place, Hemel Hempstead, HP2 6EX, UK. http://www.xara.com/ =================================XARAHEADEREND============================ */ // ******** Aldus FreeHand EPS filter /* */ #include "camtypes.h" #include "freeeps.h" //#include "oilfltrs.h" - in camtypes.h [AUTOMATICALLY REMOVED] //#include "filters.h" - in camtypes.h [AUTOMATICALLY REMOVED] //#include "ben.h" //#include "fixstr64.h" - in camtypes.h [AUTOMATICALLY REMOVED] #include "colcomp.h" #include "colourix.h" #include "collist.h" //#include "fixmem.h" - in camtypes.h [AUTOMATICALLY REMOVED] #include "nodepath.h" DECLARE_SOURCE("$Revision: 1314 $"); CC_IMPLEMENT_DYNAMIC(FreeHandEPSFilter, EPSFilter) #define new CAM_DEBUG_NEW // All the commands used by AI EPS files. enum { // colours EPSC_Xa = EPSC_EOF + 1, EPSC_xa, EPSC_Ka, EPSC_ka, // fills EPSC_radfill, EPSC_recfill, EPSC_load, // colour list stuff EPSC_BeginSetup, EPSC_def, EPSC_newcmykcustomcolor, // text type stuff EPSC_makesetfont, EPSC_ts, EPSC_sts, // complex path stuff EPSC_eomode, EPSC_true, EPSC_false, // misc stuff which is ignored EPSC_concat, EPSC_vms, EPSC_vmr, EPSC_vmrs, EPSC_stob, EPSC_fhsetspreadallow, EPSC_FREEHAND_IGNOREDTOKEN // a token used to ingore other tokens }; // This is the array of AI EPS command/keyword names. CommandMap FreeHandEPSFilter::FHCommands[] = { // colours { EPSC_Xa, _T("Xa") }, { EPSC_xa, _T("xa") }, { EPSC_Ka, _T("Ka") }, { EPSC_ka, _T("ka") }, // fills { EPSC_radfill, _T("radfill") }, { EPSC_recfill, _T("recfill") }, { EPSC_load, _T("load") }, // colour list stuff { EPSC_BeginSetup, _T("%%BeginSetup")}, { EPSC_def, _T("def") }, { EPSC_newcmykcustomcolor, _T("newcmykcustomcolor") }, // text stuff { EPSC_makesetfont, _T("makesetfont") }, { EPSC_ts, _T("ts") }, { EPSC_sts, _T("sts") }, // complex path stuff { EPSC_eomode, _T("eomode") }, { EPSC_true, _T("true") }, { EPSC_false, _T("false") }, // misc stuff { EPSC_concat, _T("concat") }, { EPSC_vms, _T("vms") }, { EPSC_vmr, _T("vmr") }, { EPSC_vmrs, _T("vmrs") }, { EPSC_stob, _T("stob") }, { EPSC_fhsetspreadallow, _T("fhsetspreadallow") }, // Sentinel { EPSC_Invalid, _T("Invalid") } }; /******************************************************************************************** > FreeHandEPSFilter::FreeHandEPSFilter() Author: Ben_Summers (Xara Group Ltd) Created: 31/05/95 Purpose: Constructor for an FreeHandEPSFilter object. The object should be initialised before use. SeeAlso: EPSFilter::Init ********************************************************************************************/ FreeHandEPSFilter::FreeHandEPSFilter() { // Set up filter descriptions. FilterNameID = _R(IDT_FREEHANDEPS_FILTERNAME); FilterInfoID = _R(IDT_FREEHANDEPS_FILTERINFO); ImportMsgID = _R(IDT_IMPORTMSG_FREEHANDEPS); FilterID = FILTERID_FREEHAND_EPS; Flags.CanImport = TRUE; Flags.CanExport = FALSE; AdjustOrigin = FALSE; } /******************************************************************************************** > BOOL FreeHandEPSFilter::Init() Author: Ben_Summers (Xara Group Ltd) Created: 31/05/95 Returns: TRUE if the filter was initialised ok, FALSE otherwise. Purpose: Initialise an FreeHandEPSFilter object. Errors: Will fail if not enough memory to initialise the EPS stack. SeeAlso: EPSStack ********************************************************************************************/ BOOL FreeHandEPSFilter::Init() { // Get the OILFilter object pOILFilter = new FreeHandEPSOILFilter(this); if (pOILFilter == NULL) return FALSE; // Load the description strings FilterName.Load(FilterNameID); FilterInfo.Load(FilterInfoID); // All ok return TRUE; } /******************************************************************************************** > BOOL FreeHandEPSFilter::PrepareToImport() Author: Ben_Summers (Xara Group Ltd) Created: 02/06/95 Returns: error flag Purpose: prepares to import a freehand EPS file SeeAlso: EPSFilter ********************************************************************************************/ BOOL FreeHandEPSFilter::PrepareToImport() { if(!EPSFilter::PrepareToImport()) return FALSE; // set some nice variables HadhToken = FALSE; DoingGradFill = FALSE; OldFill = 0; InColours = FALSE; InText = FALSE; ComplexPathMode = FALSE; pLastPathSeen = 0; // set the clipping flags ClipRegion.SetClippingFlags(2); // get an importedcolours object pNewColours = new ImportedColours(pColours, FALSE); if(pNewColours == 0 || !pNewColours->Init()) return FALSE; // get a colour array ColourArray = (IndexedColour **)CCMalloc(FHEF_COLOURARRAY_INITIALSIZE * sizeof(IndexedColour *)); if(ColourArray == 0) { delete pNewColours; pNewColours = 0; return FALSE; } ColourArrayEntries = 0; ColourArraySize = FHEF_COLOURARRAY_INITIALSIZE; return TRUE; } /******************************************************************************************** > void FreeHandEPSFilter::CleanUpAfterImport(BOOL Successful) Author: Ben_Summers (Xara Group Ltd) Created: 02/06/95 Returns: error flag Purpose: clears up after importing a FreeHand 3.0 EPS file SeeAlso: EPSFilter ********************************************************************************************/ void FreeHandEPSFilter::CleanUpAfterImport(BOOL Successful) { // ensure that there's no old fill hanging around if(OldFill != 0) delete OldFill; OldFill = 0; // add new colours if successful, destroy them if not if(Successful) { pNewColours->AddColoursToDocument(); } else { pNewColours->DestroyColours(); } // delete the colour reference array if(ColourArray != 0) { CCFree(ColourArray); ColourArray = 0; } // get rid of any stuff in the stack Stack.DeleteAll (); EPSFilter::CleanUpAfterImport(Successful); } /******************************************************************************************** > INT32 FreeHandEPSFilter::EPSHeaderIsOk(ADDR pFileHeader, UINT32 HeaderSize) Author: Ben_Summers (Xara Group Ltd) Created: 01/06/95 Returns: TRUE if the header is ok and import should proceed, FALSE if not. Purpose: Checks to see if the EPS comment headers specify that this is an FreeHand 3.0 generated EPS file, as required. ********************************************************************************************/ INT32 FreeHandEPSFilter::EPSHeaderIsOk(ADDR pFileHeader, UINT32 HeaderSize) { // Check the first line in EPS file if (strncmp((char *) pFileHeader, "%!PS-Adobe", 10) != 0) { // Incorrect version of EPS header line - we don't want this return 0; } // !PS-Adobe line is ok - check creator line... TCHAR *Buffer; CCMemTextFile HeaderFile((char *)pFileHeader, HeaderSize); if(HeaderFile.IsMemFileInited() == FALSE || HeaderFile.InitLexer() == FALSE) { HeaderFile.close(); return 0; } BOOL HaveCreatorString = FALSE; UINT32 Lines = 0; while ((Lines < 20) && !HeaderFile.eof()) { HeaderFile.GetLineToken(); Buffer = (TCHAR *)HeaderFile.GetTokenBuf(); ERROR2IF(Buffer == 0, 0, "Returned buffer from lex file == 0"); Lines++; // Return TRUE if this file was created by Illustrator, or has been exported in // Illustrator format. if (camStrncmp( (const TCHAR *)Buffer, _T("%%Creator: "), 11) == 0 && camStrstr( (const TCHAR*)Buffer, _T("FreeHand")) != 0) { // found a plausible creator string - but it could be any version // (3.0 for the Mac gives it's version number here, but the PC one doesn't) HaveCreatorString = TRUE; } if (camStrncmp(Buffer, _T("%%DocumentProcSets: FreeHand_header 3 "), 38) == 0 && HaveCreatorString) { // I'll have that then. HeaderFile.close(); return 10; } // If we find the compression token then stop the search as we don't want to start // looking in the compressed data! if (camStrncmp(Buffer, _T("%%Compression:"), 14)==0) break; } HeaderFile.close(); return 0; } /******************************************************************************************** > void FreeHandEPSFilter::LookUpToken() Author: Tim_Browse (Xara Group Ltd) Created: 25/02/94 Returns: TRUE if the token is an FreeHand EPS token; FALSE if not. Purpose: Compare the current token against the AI keywords to see if it is one of them. SeeAlso: EPSFilter::LookUpToken; EPSFilter::DecodeToken ********************************************************************************************/ void FreeHandEPSFilter::LookUpToken() { // Check to see if it is a keyword - cycle through the array of keyword names and // compare against our token (could use a hash table?) INT32 i = 0; while (FHCommands[i].Cmd != EPSC_Invalid) { if (camStrcmp(TokenBuf, FHCommands[i].CmdStr) == 0) { // Found the token - set the token variable and return success Token = FHCommands[i].Cmd; return; } // Try next command i++; } // Did not find this token - pass on to base class. EPSFilter::LookUpToken(); } /******************************************************************************************** > BOOL AIEPSFilter::ProcessToken() Author: Ben_Summers (Xara Group Ltd) Created: 01/06/95 Returns: TRUE if token understood and processed ok, FALSE if not. Purpose: Processes EPS tokens that are not part of the standard Illustrator set, or which need to be handled differently to the standard Illustrator meanings. i.e. this is the function that handles all the FreeHand EPS operators. Errors: Syntax error in EPS, Out of memory. SeeAlso: EPSFilter::ProcessToken ********************************************************************************************/ #define FHEF_SENDh { if(pPath != 0) { \ EPSCommand ThisToken = Token; \ Token = ComplexPathMode?EPSC_s:EPSC_h; \ if(!EPSFilter::ProcessToken()) \ return FALSE; \ Token = ThisToken; \ HadhToken = FALSE; } } BOOL FreeHandEPSFilter::ProcessToken() { // Decode the command, and execute it... switch (Token) { // state saving case EPSC_vms: if(!Import_gsave()) return FALSE; break; case EPSC_vmr: if(!Import_grestore()) return FALSE; break; case EPSC_vmrs: if(!Import_grestore()) return FALSE; if(!Import_gsave()) return FALSE; break; // tokens to ignore case EPSC_FREEHAND_IGNOREDTOKEN: break; // tokens which should be ignored and one entry discarded case EPSC_load: // the load part of a fill - discard the /clipper before it case EPSC_fhsetspreadallow: if(!Stack.Discard(1)) goto EPSError; break; case EPSC_concat: if(!Stack.DiscardArray()) goto EPSError; break; // complex paths... case EPSC_eomode: { INT32 ComplexStart; if(!Stack.Pop(&ComplexStart)) goto EPSError; // is this a start of a complex path? if(ComplexStart != TRUE) { ComplexPathMode = FALSE; } else { HadFirstOfComplexPath = FALSE; ComplexPathMode = TRUE; } } break; case EPSC_true: Stack.Push((INT32)TRUE); break; case EPSC_false: Stack.Push((INT32)FALSE); break; case EPSC_u: HadFirstOfComplexPath = FALSE; return EPSFilter::ProcessToken(); break; // colours case EPSC_Ka: case EPSC_ka: { DocColour Colour; if(PopColour(&Colour)) { // Remember this colour for future objects if (Token == EPSC_ka) { if (!SetFillColour(Colour)) goto NoMemory; } else { if (!SetLineColour(Colour)) goto NoMemory; } } else // Invalid colour operands goto EPSError; } break; case EPSC_Xa: case EPSC_xa: { DocColour Colour; if(PopNamedColour(&Colour)) { // Remember this colour for future objects if (Token == EPSC_xa) { if (!SetFillColour(Colour)) goto NoMemory; } else { if (!SetLineColour(Colour)) goto NoMemory; } } else // Invalid colour operands goto EPSError; } break; case EPSC_H: if(ComplexPathMode) { // in complex path mode - make this a filled one, not a discarded one Token = EPSC_S; } return EPSFilter::ProcessToken(); break; case EPSC_h: if(ComplexPathMode) { // in complex path mode - modify and process Token = EPSC_s; return EPSFilter::ProcessToken(); break; } // the hidden path closing operator - a grad fill thingy will follow shortly maybe... // this will prevent it being processed now, although it may get processed later on. HadhToken = TRUE; break; // for clipping masks, do some funky stuff case EPSC_q: // if there's a pending grad fill... if(DoingGradFill) { if(pPath != 0) { // right then, make a copy of the path... NodePath *pPathClone; if(!pPath->NodeCopy((Node **)&pPathClone)) { goto NoMemory; } // copy the flags EPSFlagsDefn EPSFlagsClone = EPSFlags; // send a token to finish and fill the path... Token = (pInkPath->IsFilled)?EPSC_f:EPSC_s; if(!EPSFilter::ProcessToken()) return FALSE; // restore the old fill if(!RestoreCurrentFill()) goto NoMemory; // restore the copy of the path pPath = pPathClone; pInkPath = &pPath->InkPath; // restore the flags EPSFlags = EPSFlagsClone; // definately want to send an h HadhToken = TRUE; } // done the grad fill DoingGradFill = FALSE; // restore the old token Token = EPSC_q; } // clipping started - have we got an h token to send? if(HadhToken) FHEF_SENDh // process this return EPSFilter::ProcessToken(); break; // for now, if there's no path, don't return a W case EPSC_W: if(pPath == 0) { // OK, now we want to get the last path we created, make a copy of it and then install it as the current one if(pLastPathSeen == 0) goto EPSError; // make a copy of it NodePath *pClone; if(!pLastPathSeen->NodeCopy((Node **)&pClone)) goto NoMemory; // delete it's attributes pClone->DeleteChildren(pClone->FindFirstChild()); // make it the current path pPath = pClone; pInkPath = &pPath->InkPath; ThePathType = PATH_NORMAL; EPSFlags.NoAttributes = TRUE; } if(pPath != 0) return EPSFilter::ProcessToken(); break; // we may need to modify path closing things if we're doing a grad fill case EPSC_s: case EPSC_S: if(Token == EPSC_S) { // if we've had an h token but no grad fill, send the h now if(HadhToken) FHEF_SENDh // if we've got a grad fill, modify the token we got if(DoingGradFill) Token = EPSC_b; } // process the possily modified token normally HadhToken = FALSE; return EPSFilter::ProcessToken(); break; // modify path closing for grad fills. case EPSC_n: if(DoingGradFill) { Token = EPSC_f; // we want to fill the thing HadhToken = FALSE; return EPSFilter::ProcessToken(); break; } HadhToken = FALSE; // ignore h's as this is another end path thingy... if(pPath != 0) return EPSFilter::ProcessToken(); break; // unset the had h token for other path closing things case EPSC_N: case EPSC_F: case EPSC_f: case EPSC_B: case EPSC_b: HadhToken = FALSE; return EPSFilter::ProcessToken(); break; // interested in path element things to switch off grad fills case EPSC_m: if(InText) { // if we're doing some text, discard the moveto command if(!Stack.Discard(2)) goto EPSError; break; } case EPSC_l: case EPSC_L: case EPSC_c: case EPSC_C: case EPSC_v: case EPSC_V: case EPSC_y: case EPSC_Y: // maybe we need an h token to be sent if(HadhToken) FHEF_SENDh // stop grad fill if(DoingGradFill) { // turn the grad fill state off DoingGradFill = FALSE; // restore the old fill type RestoreCurrentFill(); } return EPSFilter::ProcessToken(); break; case EPSC_recfill: { // get the colours DocColour StartColour, EndColour; if(!PopColour(&EndColour) || !PopColour(&StartColour)) goto EPSError; // discard the fill type thingy - we can only do colours if(!DiscardFillSubType()) goto EPSError; // OK, now a few coords DocCoord Centre; double Angle; DocRect BBox; if(!Stack.PopCoordPair(&BBox.hi) || !Stack.PopCoordPair(&BBox.lo) || !Stack.Pop(&Angle) || !Stack.PopCoordPair(&Centre)) goto EPSError; // munge the angle a little and get it into radians Angle += 225; Angle = (Angle * (2 * PI)) / 360; // see if we can get a more accurate BBox if(pPath != 0) { BBox = pPath->GetBoundingRect(); Centre.x = BBox.lo.x + (BBox.Width() / 2); Centre.y = BBox.lo.y + (BBox.Height() / 2); } // OK, we've got all the stuff we need to do some niceness on it BBox.Translate(0 - Centre.x, 0 - Centre.y); DocCoord StartPoint, EndPoint; StartPoint.x = Centre.x + (INT32)(((double)BBox.lo.x * cos(Angle)) - ((double)BBox.lo.y * sin(Angle))); StartPoint.y = Centre.y + (INT32)(((double)BBox.lo.x * sin(Angle)) + ((double)BBox.lo.y * cos(Angle))); EndPoint.x = Centre.x + (INT32)(((double)BBox.hi.x * cos(Angle)) - ((double)BBox.hi.y * sin(Angle))); EndPoint.y = Centre.y + (INT32)(((double)BBox.hi.x * sin(Angle)) + ((double)BBox.hi.y * cos(Angle))); // store current fill attribute SaveCurrentFill(); // set the fill if(!SetLinearFill(StartColour, EndColour, StartPoint, EndPoint)) goto NoMemory; // say we're doing a grad fill DoingGradFill = TRUE; HadhToken = FALSE; // absorb this } break; case EPSC_radfill: { // get the colours DocColour StartColour, EndColour; if(!PopColour(&StartColour) || !PopColour(&EndColour)) goto EPSError; // get the radius and centre coordinate DocCoord Centre; INT32 Radius; if(!Stack.PopCoord(&Radius) || !Stack.PopCoordPair(&Centre)) goto EPSError; // store current fill attribute SaveCurrentFill(); // set the fill DocCoord EndPoint(Centre.x + Radius, Centre.y); if(!SetRadialFill(StartColour, EndColour, Centre, EndPoint)) goto NoMemory; // say we're doing a grad fill DoingGradFill = TRUE; HadhToken = FALSE; } break; case EPSC_BeginSetup: // there's probably a colour list or something in that there setup thingy - search for the spots token { BOOL Found = FALSE; while(Found == FALSE) { if(!EPSFile->GetToken()) return FALSE; if(EPSFile->GetTokenType() == TOKEN_NORMAL) { if(camStrcmp(TokenBuf, _T("spots")) == 0) { // check to see if the array is about to start if(!EPSFile->GetToken()) return FALSE; if(TokenBuf[0] == '[') { TRACEUSER( "Ben", _T("Found spots\n")); Found = TRUE; } } } if(camStrncmp(TokenBuf, _T("%%EndSetup"), 10) == 0) { TRACEUSER( "Ben", _T("Met end of setup without finding spots\n")); break; } if(EPSFile->eof()) goto EPSError; } if(Found == TRUE) { InColours = TRUE; } } break; case EPSC_def: if(InColours) { // finished the colours... TRACEUSER( "Ben", _T("Finished spot colours\n")); // scan for the end of the setup section BOOL Found = FALSE; while(Found == FALSE) { if(!EPSFile->GetToken()) return FALSE; if(EPSFile->GetTokenType() == TOKEN_COMMENT) { if(camStrncmp(TokenBuf, _T("%%EndSetup"), 10) == 0) { TRACEUSER( "Ben", _T("Found end of setup\n")); Found = TRUE; } } if(EPSFile->eof()) goto EPSError; } // get the ] off the stack EPSCommand Ignored; Stack.PopCmd(&Ignored); // empty it... Stack.DeleteAll (); InColours = FALSE; } else { // probably a font type thingy - empty the stack including commands Stack.DeleteAll (); } break; case EPSC_newcmykcustomcolor: // OK, here's a named colour... add it to those known { // discard some random thingy if(!Stack.Discard()) goto EPSError; // get the name String_64 ColourName; if(!Stack.Pop(&ColourName)) goto EPSError; // get the components double C, M, Y, K; if(!Stack.Pop(&K) || !Stack.Pop(&Y) || !Stack.Pop(&M) || !Stack.Pop(&C)) goto EPSError; // make the new colour ColourCMYK Colour; Colour.Cyan = C; Colour.Magenta = M; Colour.Yellow = Y; Colour.Key = K; // add it if(!pNewColours->AddColour(&ColourName, &Colour)) goto NoMemory; // add it to the list of colours // this is a bit of a bodge, but never mind. Shouldn't be that bad. IndexedColour *TheNewColour = pNewColours->GetColour(ColourName); if(TheNewColour == 0) goto NoMemory; // add it to the list of colours // enough space? if((ColourArrayEntries + 1) >= ColourArraySize) { TRACEUSER( "Ben", _T("Extening colour array\n")); IndexedColour **NewPtr = (IndexedColour **)CCRealloc(ColourArray, (ColourArraySize + FHEF_COLOURARRAY_CHUNK) * sizeof(IndexedColour *)); if(NewPtr == 0) goto NoMemory; ColourArray = NewPtr; ColourArraySize += FHEF_COLOURARRAY_CHUNK; } // add ColourArray[ColourArrayEntries] = TheNewColour; ColourArrayEntries++; } break; // ignore text stuff case EPSC_makesetfont: if(!Stack.DiscardArray()) goto EPSError; if(!Stack.Discard(1)) goto EPSError; InText = TRUE; break; case EPSC_ts: if(!Stack.Discard(6)) goto EPSError; break; case EPSC_stob: case EPSC_sts: Stack.DeleteAll (); InText = FALSE; break; default: // Token not understood - pass on to base class return EPSFilter::ProcessToken(); } // No errors encountered while parsing this token and its operands. return TRUE; // Error handlers: EPSError: HandleEPSError(); return FALSE; NoMemory: HandleNoMemory(); return FALSE; } /******************************************************************************************** > BOOL FreeHandEPSFilter::DiscardFillSubType() Author: Ben_Summers (Xara Group Ltd) Created: 02/06/95 Returns: EPS error Purpose: discards a fill sub type from the stack ('{}' or '{logtaper}' or something) ********************************************************************************************/ BOOL FreeHandEPSFilter::DiscardFillSubType() { BOOL Done = FALSE; EPSType Type; while(Done == FALSE) { Type = Stack.GetType(); if(Type == EPSTYPE_NONE) { // not enough stuff on the stack return FALSE;; } else if(Type == EPSTYPE_STRING) { // string - is this the ending? String_64 Str; if(!Stack.Pop(&Str)) return FALSE; if(camStrcmp(Str, _T("{")) == 0) Done = TRUE; // found end of this bit } else { // just something unimportant - discard it if(!Stack.Discard()) return FALSE; } } return TRUE; } /******************************************************************************************** > BOOL FreeHandEPSFilter::PopColour(DocColour *Col) Author: Ben_Summers (Xara Group Ltd) Created: 01/06/95 Returns: TRUE if colour understood and processed ok, FALSE if not. Purpose: Pops a FreeHand colour off the stack. Errors: Syntax error in EPS, Out of memory. SeeAlso: EPSFilter::ProcessToken ********************************************************************************************/ BOOL FreeHandEPSFilter::PopColour(DocColour *Col) { // OK, we expect an array of four values in the order (of popping) K, Y, M, C // check that we've got a array end EPSCommand Cmd; if(!Stack.PopCmd(&Cmd)) return FALSE; if(Cmd != EPSC_ArrayEnd) return FALSE; // pop the colours double C, M, Y, K; if(!Stack.Pop(&K) || !Stack.Pop(&Y) || !Stack.Pop(&M) || !Stack.Pop(&C)) return FALSE; // check that we've got a array start if(!Stack.PopCmd(&Cmd)) return FALSE; if(Cmd != EPSC_ArrayStart) return FALSE; // knock up the colour... ColourCMYK Colour; Colour.Cyan = C; Colour.Magenta = M; Colour.Yellow = Y; Colour.Key = K; IndexedColour *NewCol = new IndexedColour(COLOURMODEL_CMYK, (ColourGeneric *)&Colour); if(NewCol == 0) return FALSE; pColours->GetColourList()->GetUnnamedColours()->AddTail(NewCol); Col->MakeRefToIndexedColour(NewCol); return TRUE; } /******************************************************************************************** > BOOL FreeHandEPSFilter::PopNamedColour(DocColour *Col) Author: Ben_Summers (Xara Group Ltd) Created: 01/06/95 Returns: TRUE if colour understood and processed ok, FALSE if not. Purpose: Pops a FreeHand named colour off the stack. Errors: Syntax error in EPS, Out of memory. SeeAlso: EPSFilter::ProcessToken ********************************************************************************************/ BOOL FreeHandEPSFilter::PopNamedColour(DocColour *Col) { // OK, we expect an array of two values in the order (of popping) random, index // and we can use the index to look up the indexed colour we should use in our // nice list in ColourArray. // 'random'? Maybe it's a tint or something? Which doesn't seem to be used. // check that we've got a array end EPSCommand Cmd; if(!Stack.PopCmd(&Cmd)) return FALSE; if(Cmd != EPSC_ArrayEnd) return FALSE; // pop the index double Random; INT32 Index; if(!Stack.Pop(&Index) || !Stack.Pop(&Random)) return FALSE; // check that we've got a array start if(!Stack.PopCmd(&Cmd)) return FALSE; if(Cmd != EPSC_ArrayStart) return FALSE; // check that the index is valid if(Index < 0 || Index >= ColourArrayEntries) return FALSE; // knock up the colour... Col->MakeRefToIndexedColour(ColourArray[Index]); return TRUE; } /******************************************************************************************** > BOOL FreeHandEPSFilter::SaveCurrentFill() Author: Ben_Summers (Xara Group Ltd) Created: 02/06/95 Returns: error flag Purpose: stores the current fill attribute SeeAlso: EPSFilter::ProcessToken ********************************************************************************************/ BOOL FreeHandEPSFilter::SaveCurrentFill() { // get rid of any stored fill if(OldFill != 0) delete OldFill; // create a copy of the current fill attribute CCRuntimeClass* ObjectType = CurrentAttrs[ATTR_FILLGEOMETRY].pAttr->GetRuntimeClass(); AttributeValue* AttrClone = (AttributeValue*)ObjectType->CreateObject(); if(AttrClone == 0) return FALSE; AttrClone->SimpleCopy(CurrentAttrs[ATTR_FILLGEOMETRY].pAttr); // set the old fill pointer to the nice clone we've made OldFill = AttrClone; return TRUE; } /******************************************************************************************** > BOOL FreeHandEPSFilter::RestoreCurrentFill() Author: Ben_Summers (Xara Group Ltd) Created: 02/06/95 Returns: error flag Purpose: restores the current fill attribute from the stored one SeeAlso: EPSFilter::ProcessToken ********************************************************************************************/ BOOL FreeHandEPSFilter::RestoreCurrentFill() { if(OldFill == 0) return TRUE; // no saved state // delete the old attribute if(CurrentAttrs[ATTR_FILLGEOMETRY].pAttr != 0) delete CurrentAttrs[ATTR_FILLGEOMETRY].pAttr; // make the current one the old one CurrentAttrs[ATTR_FILLGEOMETRY].pAttr = OldFill; // mark this as used so it doesn't get deleted OldFill = 0; return TRUE; } /******************************************************************************************** > BOOL FreeHandEPSFilter::ValidateGroup(Node *pGroup); Author: Ben_Summers (Xara Group Ltd) Created: 02/06/95 Returns: error flag Purpose: validates a group created by the filter. As all objects have a group around then, this removes groups which only have one entry. ********************************************************************************************/ BOOL FreeHandEPSFilter::ValidateGroup(Node *pGroup) { ERROR2IF(pGroup == 0, FALSE, "Group passed to validateGroup is null"); Node *pFirstChild = pGroup->FindFirstChild(); ERROR2IF(pFirstChild == 0, FALSE, "Group without children passed to ValidateGroup - should have been deleted by EndGroup"); if(pFirstChild->FindNext() == 0) { // there was only one object - delete this group. // if pFirstNodeInRange is this node, set it to the child if(pFirstNodeInRange == pGroup) pFirstNodeInRange = pFirstChild; // move the child to the parent of the group if (!ImportInfo.pOp->DoMoveNode(pFirstChild, pNode, LASTCHILD)) return FALSE; // see EPSFilter::EndGroup for the reason why it's hidden rather than deleted if (!ImportInfo.pOp->DoHideNode(pGroup, TRUE)) return FALSE; } return TRUE; } /******************************************************************************************** > BOOL FreeHandEPSFilter::MaskedGroupEnding() Author: Ben_Summers (Xara Group Ltd) Created: 02/06/95 Returns: EPS error flag Purpose: Restores the graphic state after a masked group ends. ********************************************************************************************/ BOOL FreeHandEPSFilter::MaskedGroupEnding() { // make the path type normal if(ThePathType == PATH_DISCARD || ThePathType == PATH_DISCARD_STICKY) { ThePathType = PATH_NORMAL; } return TRUE; } /******************************************************************************************** > BOOL FreeHandEPSFilter::RemoveLastSubPathIfNotUnique(Path *pIPath) Author: Ben_Summers (Xara Group Ltd) Created: 15/06/95 Returns: error flag Purpose: Removes the last subpath from a path if an sub path with it's exact coords already exists. ********************************************************************************************/ BOOL FreeHandEPSFilter::RemoveLastSubPathIfNotUnique(Path *pIPath) { // get coord arrays INT32 NCoords = pIPath->GetNumCoords(); DocCoord *Coords = pIPath->GetCoordArray(); PathVerb *Verbs = pIPath->GetVerbArray(); // find the last moveto INT32 LastMoveTo = -1; INT32 c; for(c = 0; c < NCoords; c++) { if((Verbs[c] & ~PT_CLOSEFIGURE) == PT_MOVETO) { LastMoveTo = c; } } // check that there is a last moveto and it's not the first coord... if(LastMoveTo == -1 || LastMoveTo == 0) return TRUE; // nothing more to do // go through the coords matching subpaths... for(c = 0; c < LastMoveTo; c++) { if((Verbs[c] & ~PT_CLOSEFIGURE) == PT_MOVETO) { // found a moveto... compare the subpaths INT32 n = 0; while(((Verbs[c+n] & ~PT_CLOSEFIGURE) == (Verbs[LastMoveTo+n] & ~PT_CLOSEFIGURE)) && (Coords[c+n] == Coords[LastMoveTo+n])) { // this was ok... is the next a moveto and the subpath the right length? if((LastMoveTo+n+1) == NCoords) { // yes, delete and return return pIPath->DeleteFromElement(LastMoveTo); } n++; } } } return TRUE; } /******************************************************************************************** > BOOL FreeHandEPSFilter::AddNewNode(Node *pNewNode) Author: Ben_Summers (Xara Group Ltd) Created: 19/06/95 Returns: error flag Purpose: checks to see if we're in complex path mode before passing on the node to the base class. If we are, it joins it to the last one and fiddles about with the attributes. ********************************************************************************************/ BOOL FreeHandEPSFilter::AddNewNode(Node *pNewNode) { if(IS_A(pNewNode, NodePath)) pLastPathSeen = (NodePath *)pNewNode; // check to see if we want to handle this if((ComplexPathMode == FALSE) || (pNewNode == 0) || (pNode == 0) || (!IS_A(pNewNode, NodePath))) return EPSFilter::AddNewNode(pNewNode); // check to see if this is the first... if(HadFirstOfComplexPath == FALSE) { HadFirstOfComplexPath = TRUE; return EPSFilter::AddNewNode(pNewNode); } // find the last child of the node Node *pLastChild = pNode->FindLastChild(); if(pLastChild == 0 || !IS_A(pLastChild, NodePath)) return EPSFilter::AddNewNode(pNewNode); // we know that both of these things are NodePaths. Path *pTarget = &((NodePath *)pLastChild)->InkPath; Path *pAddition = &((NodePath *)pNewNode)->InkPath; // work out the new flags for the target BOOL TargetFilled = pTarget->IsFilled; BOOL TargetStroked = pTarget->IsStroked; if(pAddition->IsFilled) TargetFilled = TRUE; if(pAddition->IsStroked) TargetStroked = TRUE; // add this new path to the old one... if(!pTarget->MergeTwoPaths(*pAddition)) return FALSE; // check that the thing we just added isn't already there if(!RemoveLastSubPathIfNotUnique(pTarget)) return FALSE; // set it's flags pTarget->IsFilled = TargetFilled; pTarget->IsStroked = TargetStroked; // vape it's attributes pLastChild->DeleteChildren(pLastChild->FindFirstChild()); // apply some new ones SetPathFilled(TargetFilled); if(!AddAttributes((NodePath *)pLastChild, TargetStroked, TargetFilled)) return FALSE; // hide the nice additional path //if(!ImportInfo.pOp->DoHideNode(pNewNode, TRUE)) // return FALSE; pNewNode->CascadeDelete(); delete pNewNode; // set the last seen path bollox pLastPathSeen = (NodePath *)pLastChild; // done! return TRUE; }