freeeps.cpp

Go to the documentation of this file.
00001 // $Id: freeeps.cpp 1314 2006-06-14 08:58:56Z builder1 $
00002 /* @@tag:xara-cn@@ DO NOT MODIFY THIS LINE
00003 ================================XARAHEADERSTART===========================
00004  
00005                Xara LX, a vector drawing and manipulation program.
00006                     Copyright (C) 1993-2006 Xara Group Ltd.
00007        Copyright on certain contributions may be held in joint with their
00008               respective authors. See AUTHORS file for details.
00009 
00010 LICENSE TO USE AND MODIFY SOFTWARE
00011 ----------------------------------
00012 
00013 This file is part of Xara LX.
00014 
00015 Xara LX is free software; you can redistribute it and/or modify it
00016 under the terms of the GNU General Public License version 2 as published
00017 by the Free Software Foundation.
00018 
00019 Xara LX and its component source files are distributed in the hope
00020 that it will be useful, but WITHOUT ANY WARRANTY; without even the
00021 implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
00022 See the GNU General Public License for more details.
00023 
00024 You should have received a copy of the GNU General Public License along
00025 with Xara LX (see the file GPL in the root directory of the
00026 distribution); if not, write to the Free Software Foundation, Inc., 51
00027 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA
00028 
00029 
00030 ADDITIONAL RIGHTS
00031 -----------------
00032 
00033 Conditional upon your continuing compliance with the GNU General Public
00034 License described above, Xara Group Ltd grants to you certain additional
00035 rights. 
00036 
00037 The additional rights are to use, modify, and distribute the software
00038 together with the wxWidgets library, the wxXtra library, and the "CDraw"
00039 library and any other such library that any version of Xara LX relased
00040 by Xara Group Ltd requires in order to compile and execute, including
00041 the static linking of that library to XaraLX. In the case of the
00042 "CDraw" library, you may satisfy obligation under the GNU General Public
00043 License to provide source code by providing a binary copy of the library
00044 concerned and a copy of the license accompanying it.
00045 
00046 Nothing in this section restricts any of the rights you have under
00047 the GNU General Public License.
00048 
00049 
00050 SCOPE OF LICENSE
00051 ----------------
00052 
00053 This license applies to this program (XaraLX) and its constituent source
00054 files only, and does not necessarily apply to other Xara products which may
00055 in part share the same code base, and are subject to their own licensing
00056 terms.
00057 
00058 This license does not apply to files in the wxXtra directory, which
00059 are built into a separate library, and are subject to the wxWindows
00060 license contained within that directory in the file "WXXTRA-LICENSE".
00061 
00062 This license does not apply to the binary libraries (if any) within
00063 the "libs" directory, which are subject to a separate license contained
00064 within that directory in the file "LIBS-LICENSE".
00065 
00066 
00067 ARRANGEMENTS FOR CONTRIBUTION OF MODIFICATIONS
00068 ----------------------------------------------
00069 
00070 Subject to the terms of the GNU Public License (see above), you are
00071 free to do whatever you like with your modifications. However, you may
00072 (at your option) wish contribute them to Xara's source tree. You can
00073 find details of how to do this at:
00074   http://www.xaraxtreme.org/developers/
00075 
00076 Prior to contributing your modifications, you will need to complete our
00077 contributor agreement. This can be found at:
00078   http://www.xaraxtreme.org/developers/contribute/
00079 
00080 Please note that Xara will not accept modifications which modify any of
00081 the text between the start and end of this header (marked
00082 XARAHEADERSTART and XARAHEADEREND).
00083 
00084 
00085 MARKS
00086 -----
00087 
00088 Xara, Xara LX, Xara X, Xara X/Xtreme, Xara Xtreme, the Xtreme and Xara
00089 designs are registered or unregistered trademarks, design-marks, and/or
00090 service marks of Xara Group Ltd. All rights in these marks are reserved.
00091 
00092 
00093       Xara Group Ltd, Gaddesden Place, Hemel Hempstead, HP2 6EX, UK.
00094                         http://www.xara.com/
00095 
00096 =================================XARAHEADEREND============================
00097  */
00098 // ******** Aldus FreeHand EPS filter
00099 
00100 /*
00101 */
00102 
00103 #include "camtypes.h"
00104 #include "freeeps.h"
00105 //#include "oilfltrs.h" - in camtypes.h [AUTOMATICALLY REMOVED]
00106 //#include "filters.h" - in camtypes.h [AUTOMATICALLY REMOVED]
00107 //#include "ben.h"
00108 //#include "fixstr64.h" - in camtypes.h [AUTOMATICALLY REMOVED]
00109 #include "colcomp.h"
00110 #include "colourix.h"
00111 #include "collist.h"
00112 //#include "fixmem.h" - in camtypes.h [AUTOMATICALLY REMOVED]
00113 #include "nodepath.h"
00114 
00115 DECLARE_SOURCE("$Revision: 1314 $");
00116 
00117 CC_IMPLEMENT_DYNAMIC(FreeHandEPSFilter, EPSFilter)
00118 
00119 #define new CAM_DEBUG_NEW
00120 
00121 // All the commands used by AI EPS files.
00122 enum
00123 {
00124     // colours
00125     EPSC_Xa = EPSC_EOF + 1,
00126     EPSC_xa,
00127     EPSC_Ka,
00128     EPSC_ka,
00129 
00130     // fills
00131     EPSC_radfill,
00132     EPSC_recfill,
00133     EPSC_load,
00134 
00135     // colour list stuff
00136     EPSC_BeginSetup,
00137     EPSC_def,
00138     EPSC_newcmykcustomcolor,
00139     
00140     // text type stuff
00141     EPSC_makesetfont,
00142     EPSC_ts,
00143     EPSC_sts,
00144 
00145     // complex path stuff
00146     EPSC_eomode,
00147     EPSC_true,
00148     EPSC_false,
00149 
00150     // misc stuff which is ignored
00151     EPSC_concat,
00152     EPSC_vms,
00153     EPSC_vmr,
00154     EPSC_vmrs,
00155     EPSC_stob,
00156     EPSC_fhsetspreadallow,
00157     EPSC_FREEHAND_IGNOREDTOKEN      // a token used to ingore other tokens
00158 };
00159 
00160 // This is the array of AI EPS command/keyword names.
00161 CommandMap FreeHandEPSFilter::FHCommands[] =
00162 {
00163     // colours
00164     { EPSC_Xa,      _T("Xa") },
00165     { EPSC_xa,      _T("xa") },
00166     { EPSC_Ka,      _T("Ka") },
00167     { EPSC_ka,      _T("ka") },
00168 
00169     // fills
00170     { EPSC_radfill, _T("radfill") },
00171     { EPSC_recfill, _T("recfill") },
00172     { EPSC_load,    _T("load") },
00173 
00174     // colour list stuff
00175     { EPSC_BeginSetup, _T("%%BeginSetup")},
00176     { EPSC_def,     _T("def") },
00177     { EPSC_newcmykcustomcolor, _T("newcmykcustomcolor") },
00178 
00179     // text stuff
00180     { EPSC_makesetfont, _T("makesetfont") },
00181     { EPSC_ts,      _T("ts") },
00182     { EPSC_sts,     _T("sts") },
00183 
00184     // complex path stuff
00185     { EPSC_eomode,  _T("eomode") },
00186     { EPSC_true,    _T("true") },
00187     { EPSC_false,   _T("false") },
00188 
00189     // misc stuff
00190     { EPSC_concat,  _T("concat") },
00191     { EPSC_vms,     _T("vms") },
00192     { EPSC_vmr,     _T("vmr") },
00193     { EPSC_vmrs,    _T("vmrs") },
00194     { EPSC_stob,    _T("stob") },
00195     { EPSC_fhsetspreadallow, _T("fhsetspreadallow") },
00196 
00197     // Sentinel
00198     { EPSC_Invalid, _T("Invalid") }
00199 };
00200 
00201 /********************************************************************************************
00202 
00203 >   FreeHandEPSFilter::FreeHandEPSFilter()
00204 
00205     Author:     Ben_Summers (Xara Group Ltd) <camelotdev@xara.com>
00206     Created:    31/05/95
00207     Purpose:    Constructor for an FreeHandEPSFilter object.  The object should be 
00208                 initialised before use.
00209     SeeAlso:    EPSFilter::Init
00210 
00211 ********************************************************************************************/
00212 
00213 FreeHandEPSFilter::FreeHandEPSFilter()
00214 {
00215     // Set up filter descriptions.
00216     FilterNameID = _R(IDT_FREEHANDEPS_FILTERNAME);
00217     FilterInfoID = _R(IDT_FREEHANDEPS_FILTERINFO);
00218     ImportMsgID  = _R(IDT_IMPORTMSG_FREEHANDEPS);
00219 
00220     FilterID = FILTERID_FREEHAND_EPS;
00221 
00222     Flags.CanImport = TRUE;
00223     Flags.CanExport = FALSE;
00224 
00225     AdjustOrigin = FALSE;
00226 }
00227 
00228 /********************************************************************************************
00229 
00230 >   BOOL FreeHandEPSFilter::Init()
00231 
00232     Author:     Ben_Summers (Xara Group Ltd) <camelotdev@xara.com>
00233     Created:    31/05/95
00234     Returns:    TRUE if the filter was initialised ok, FALSE otherwise.
00235     Purpose:    Initialise an FreeHandEPSFilter object.
00236     Errors:     Will fail if not enough memory to initialise the EPS stack.
00237     SeeAlso:    EPSStack
00238 
00239 ********************************************************************************************/
00240 
00241 BOOL FreeHandEPSFilter::Init()
00242 {
00243     // Get the OILFilter object
00244     pOILFilter = new FreeHandEPSOILFilter(this);
00245     if (pOILFilter == NULL)
00246         return FALSE;
00247 
00248     // Load the description strings
00249     FilterName.Load(FilterNameID);
00250     FilterInfo.Load(FilterInfoID);
00251 
00252     // All ok
00253     return TRUE;
00254 }
00255 
00256 /********************************************************************************************
00257 
00258 >   BOOL FreeHandEPSFilter::PrepareToImport()
00259 
00260     Author:     Ben_Summers (Xara Group Ltd) <camelotdev@xara.com>
00261     Created:    02/06/95
00262     Returns:    error flag
00263     Purpose:    prepares to import a freehand EPS file
00264     SeeAlso:    EPSFilter
00265 
00266 ********************************************************************************************/
00267 
00268 BOOL FreeHandEPSFilter::PrepareToImport()
00269 {
00270     if(!EPSFilter::PrepareToImport())
00271         return FALSE;
00272 
00273     // set some nice variables
00274     HadhToken = FALSE;
00275     DoingGradFill = FALSE;
00276     OldFill = 0;
00277     InColours = FALSE;
00278     InText = FALSE;
00279     ComplexPathMode = FALSE;
00280     pLastPathSeen = 0;
00281     
00282     // set the clipping flags
00283     ClipRegion.SetClippingFlags(2);
00284 
00285     // get an importedcolours object
00286     pNewColours = new ImportedColours(pColours, FALSE);
00287     if(pNewColours == 0 || !pNewColours->Init())
00288         return FALSE;
00289 
00290     // get a colour array
00291     ColourArray = (IndexedColour **)CCMalloc(FHEF_COLOURARRAY_INITIALSIZE * sizeof(IndexedColour *));
00292     if(ColourArray == 0)
00293     {
00294         delete pNewColours;
00295         pNewColours = 0;
00296         return FALSE;
00297     }
00298     ColourArrayEntries = 0;
00299     ColourArraySize = FHEF_COLOURARRAY_INITIALSIZE;
00300 
00301     return TRUE;
00302 }
00303 
00304 /********************************************************************************************
00305 
00306 >   void FreeHandEPSFilter::CleanUpAfterImport(BOOL Successful)
00307 
00308     Author:     Ben_Summers (Xara Group Ltd) <camelotdev@xara.com>
00309     Created:    02/06/95
00310     Returns:    error flag
00311     Purpose:    clears up after importing a FreeHand 3.0 EPS file
00312     SeeAlso:    EPSFilter
00313 
00314 ********************************************************************************************/
00315 
00316 void FreeHandEPSFilter::CleanUpAfterImport(BOOL Successful)
00317 {
00318     // ensure that there's no old fill hanging around
00319     if(OldFill != 0)
00320         delete OldFill;
00321 
00322     OldFill = 0;
00323 
00324     // add new colours if successful, destroy them if not
00325     if(Successful)
00326     {
00327         pNewColours->AddColoursToDocument();
00328     }
00329     else
00330     {
00331         pNewColours->DestroyColours();
00332     }
00333 
00334     // delete the colour reference array
00335     if(ColourArray != 0)
00336     {
00337         CCFree(ColourArray);
00338         ColourArray = 0;
00339     }
00340 
00341     // get rid of any stuff in the stack
00342     Stack.DeleteAll ();
00343 
00344     EPSFilter::CleanUpAfterImport(Successful);
00345 }
00346 
00347 
00348 /********************************************************************************************
00349 
00350 >   INT32 FreeHandEPSFilter::EPSHeaderIsOk(ADDR pFileHeader, UINT32 HeaderSize)
00351 
00352     Author:     Ben_Summers (Xara Group Ltd) <camelotdev@xara.com>
00353     Created:    01/06/95
00354     Returns:    TRUE if the header is ok and import should proceed, FALSE if not.
00355     Purpose:    Checks to see if the EPS comment headers specify that this is an FreeHand 3.0
00356                 generated EPS file, as required.
00357 
00358 ********************************************************************************************/
00359 
00360 INT32 FreeHandEPSFilter::EPSHeaderIsOk(ADDR pFileHeader, UINT32 HeaderSize)
00361 {
00362     // Check the first line in EPS file
00363     if (strncmp((char *) pFileHeader, "%!PS-Adobe", 10) != 0)
00364     {
00365         // Incorrect version of EPS header line - we don't want this
00366         return 0;
00367     }
00368 
00369     // !PS-Adobe line is ok - check creator line...
00370     TCHAR *Buffer;
00371     CCMemTextFile HeaderFile((char *)pFileHeader, HeaderSize);
00372     if(HeaderFile.IsMemFileInited() == FALSE || HeaderFile.InitLexer() == FALSE)
00373     {
00374         HeaderFile.close();
00375         return 0;
00376     }
00377 
00378 
00379     BOOL HaveCreatorString = FALSE;
00380     
00381     UINT32 Lines = 0;
00382     while ((Lines < 20) && !HeaderFile.eof())
00383     {
00384         HeaderFile.GetLineToken();
00385         Buffer = (TCHAR *)HeaderFile.GetTokenBuf();
00386         ERROR2IF(Buffer == 0, 0, "Returned buffer from lex file == 0");
00387         Lines++;
00388 
00389         // Return TRUE if this file was created by Illustrator, or has been exported in 
00390         // Illustrator format.
00391         if (camStrncmp( (const TCHAR *)Buffer, _T("%%Creator: "), 11) == 0 && 
00392             camStrstr( (const TCHAR*)Buffer, _T("FreeHand")) != 0)
00393         {
00394             // found a plausible creator string - but it could be any version
00395             // (3.0 for the Mac gives it's version number here, but the PC one doesn't)
00396             HaveCreatorString = TRUE;
00397         }
00398 
00399         if (camStrncmp(Buffer, _T("%%DocumentProcSets: FreeHand_header 3 "), 38) == 0 && HaveCreatorString)
00400         {
00401             // I'll have that then.
00402             HeaderFile.close();
00403             return 10;
00404         }
00405 
00406         // If we find the compression token then stop the search as we don't want to start
00407         // looking in the compressed data!
00408         if (camStrncmp(Buffer, _T("%%Compression:"), 14)==0)
00409             break;
00410     }
00411 
00412     HeaderFile.close();
00413     
00414     return 0;
00415 }
00416 
00417 
00418 /********************************************************************************************
00419 
00420 >   void FreeHandEPSFilter::LookUpToken()
00421 
00422     Author:     Tim_Browse (Xara Group Ltd) <camelotdev@xara.com>
00423     Created:    25/02/94
00424     Returns:    TRUE if the token is an FreeHand EPS token; FALSE if not.
00425     Purpose:    Compare the current token against the AI keywords to see if it is
00426                 one of them.
00427     SeeAlso:    EPSFilter::LookUpToken; EPSFilter::DecodeToken
00428 
00429 ********************************************************************************************/
00430 
00431 void FreeHandEPSFilter::LookUpToken()
00432 {
00433     // Check to see if it is a keyword - cycle through the array of keyword names and
00434     // compare against our token (could use a hash table?)
00435     INT32 i = 0;
00436     while (FHCommands[i].Cmd != EPSC_Invalid)
00437     {
00438         if (camStrcmp(TokenBuf, FHCommands[i].CmdStr) == 0)
00439         {
00440             // Found the token - set the token variable and return success
00441             Token = FHCommands[i].Cmd;
00442             return;
00443         }
00444         // Try next command
00445         i++;
00446     }
00447 
00448     // Did not find this token - pass on to base class.
00449     EPSFilter::LookUpToken();
00450 }
00451 
00452 
00453 /********************************************************************************************
00454 
00455 >   BOOL AIEPSFilter::ProcessToken()
00456 
00457     Author:     Ben_Summers (Xara Group Ltd) <camelotdev@xara.com>
00458     Created:    01/06/95
00459     Returns:    TRUE if token understood and processed ok, FALSE if not.
00460     Purpose:    Processes EPS tokens that are not part of the standard Illustrator set, or
00461                 which need to be handled differently to the standard Illustrator meanings.
00462                 i.e. this is the function that handles all the FreeHand EPS operators.
00463     Errors:     Syntax error in EPS, Out of memory.
00464     SeeAlso:    EPSFilter::ProcessToken
00465 
00466 ********************************************************************************************/
00467 
00468 #define FHEF_SENDh { if(pPath != 0) {               \
00469         EPSCommand ThisToken = Token;               \
00470         Token = ComplexPathMode?EPSC_s:EPSC_h;      \
00471         if(!EPSFilter::ProcessToken())              \
00472             return FALSE;                           \
00473         Token = ThisToken;                          \
00474         HadhToken = FALSE; } }
00475 
00476 
00477 BOOL FreeHandEPSFilter::ProcessToken()
00478 {
00479     // Decode the command, and execute it...
00480     switch (Token)
00481     {
00482         // state saving
00483         case EPSC_vms:
00484             if(!Import_gsave())
00485                 return FALSE;
00486             break;
00487 
00488         case EPSC_vmr:
00489             if(!Import_grestore())
00490                 return FALSE;
00491             break;
00492 
00493         case EPSC_vmrs:
00494             if(!Import_grestore())
00495                 return FALSE;
00496             if(!Import_gsave())
00497                 return FALSE;
00498             break;
00499 
00500         // tokens to ignore
00501         case EPSC_FREEHAND_IGNOREDTOKEN:
00502             break;
00503 
00504         // tokens which should be ignored and one entry discarded
00505         case EPSC_load:         // the load part of a fill - discard the /clipper before it
00506         case EPSC_fhsetspreadallow:
00507             if(!Stack.Discard(1))
00508                 goto EPSError;
00509             break;
00510 
00511         case EPSC_concat:
00512             if(!Stack.DiscardArray())
00513                 goto EPSError;
00514             break;
00515         
00516         // complex paths...
00517         case EPSC_eomode:
00518             {
00519                 INT32 ComplexStart;
00520 
00521                 if(!Stack.Pop(&ComplexStart))
00522                     goto EPSError;
00523 
00524                 // is this a start of a complex path?
00525                 if(ComplexStart != TRUE)
00526                 {
00527                     ComplexPathMode = FALSE;
00528                 }
00529                 else
00530                 {
00531                     HadFirstOfComplexPath = FALSE;
00532                     ComplexPathMode = TRUE;
00533                 }
00534             }
00535             break;
00536 
00537         case EPSC_true:
00538             Stack.Push((INT32)TRUE);
00539             break;
00540 
00541         case EPSC_false:
00542             Stack.Push((INT32)FALSE);
00543             break;
00544 
00545         case EPSC_u:
00546             HadFirstOfComplexPath = FALSE;
00547             return EPSFilter::ProcessToken();
00548             break;
00549 
00550         // colours
00551         case EPSC_Ka:
00552         case EPSC_ka:
00553             {
00554                 DocColour Colour;
00555 
00556                 if(PopColour(&Colour))
00557                 {
00558                     // Remember this colour for future objects
00559                     if (Token == EPSC_ka)
00560                     {
00561                         if (!SetFillColour(Colour))
00562                             goto NoMemory;
00563                     }
00564                     else
00565                     {
00566                         if (!SetLineColour(Colour))
00567                             goto NoMemory;
00568                     }
00569                 }
00570                 else    
00571                     // Invalid colour operands
00572                     goto EPSError;
00573             }
00574             break;
00575 
00576         case EPSC_Xa:
00577         case EPSC_xa:
00578             {
00579                 DocColour Colour;
00580 
00581                 if(PopNamedColour(&Colour))
00582                 {
00583                     // Remember this colour for future objects
00584                     if (Token == EPSC_xa)
00585                     {
00586                         if (!SetFillColour(Colour))
00587                             goto NoMemory;
00588                     }
00589                     else
00590                     {
00591                         if (!SetLineColour(Colour))
00592                             goto NoMemory;
00593                     }
00594                 }
00595                 else    
00596                     // Invalid colour operands
00597                     goto EPSError;
00598             }
00599             break;
00600 
00601         case EPSC_H:
00602             if(ComplexPathMode)
00603             {
00604                 // in complex path mode - make this a filled one, not a discarded one
00605                 Token = EPSC_S; 
00606             }
00607             return EPSFilter::ProcessToken();
00608             break;
00609         
00610         case EPSC_h:
00611             if(ComplexPathMode)
00612             {
00613                 // in complex path mode - modify and process
00614                 Token = EPSC_s;
00615                 return EPSFilter::ProcessToken();
00616                 break;
00617             }
00618             // the hidden path closing operator - a grad fill thingy will follow shortly maybe...
00619             // this will prevent it being processed now, although it may get processed later on.
00620             HadhToken = TRUE;
00621             break;
00622 
00623         // for clipping masks, do some funky stuff
00624         case EPSC_q:
00625             // if there's a pending grad fill...
00626             if(DoingGradFill)
00627             {
00628                 if(pPath != 0)
00629                 {
00630                     // right then, make a copy of the path...
00631                     NodePath *pPathClone;
00632 
00633                     if(!pPath->NodeCopy((Node **)&pPathClone))
00634                     {
00635                         goto NoMemory;
00636                     }
00637 
00638                     // copy the flags
00639                     EPSFlagsDefn EPSFlagsClone = EPSFlags;
00640                 
00641                     // send a token to finish and fill the path...
00642                     Token = (pInkPath->IsFilled)?EPSC_f:EPSC_s;
00643                     if(!EPSFilter::ProcessToken())
00644                         return FALSE;
00645 
00646                     // restore the old fill
00647                     if(!RestoreCurrentFill())
00648                         goto NoMemory;
00649 
00650                     // restore the copy of the path
00651                     pPath = pPathClone;
00652                     pInkPath = &pPath->InkPath;
00653 
00654                     // restore the flags
00655                     EPSFlags = EPSFlagsClone;
00656 
00657                     // definately want to send an h
00658                     HadhToken = TRUE;
00659                 }
00660 
00661                 // done the grad fill
00662                 DoingGradFill = FALSE;
00663 
00664                 // restore the old token
00665                 Token = EPSC_q;
00666             }
00667 
00668             // clipping started - have we got an h token to send?
00669             if(HadhToken)
00670                 FHEF_SENDh
00671 
00672             // process this
00673             return EPSFilter::ProcessToken();
00674             break;
00675 
00676         // for now, if there's no path, don't return a W
00677         case EPSC_W:
00678             if(pPath == 0)
00679             {
00680                 // OK, now we want to get the last path we created, make a copy of it and then install it as the current one
00681                 if(pLastPathSeen == 0)
00682                     goto EPSError;
00683 
00684                 // make a copy of it
00685                 NodePath *pClone;
00686                 
00687                 if(!pLastPathSeen->NodeCopy((Node **)&pClone))
00688                     goto NoMemory;
00689 
00690                 // delete it's attributes
00691                 pClone->DeleteChildren(pClone->FindFirstChild());
00692 
00693                 // make it the current path
00694                 pPath = pClone;
00695                 pInkPath = &pPath->InkPath;
00696                 ThePathType = PATH_NORMAL;
00697                 EPSFlags.NoAttributes = TRUE;
00698             }
00699             if(pPath != 0)
00700                 return EPSFilter::ProcessToken();
00701             break;
00702 
00703         // we may need to modify path closing things if we're doing a grad fill
00704         case EPSC_s:
00705         case EPSC_S:
00706             if(Token == EPSC_S)
00707             {
00708                 // if we've had an h token but no grad fill, send the h now
00709                 if(HadhToken)
00710                     FHEF_SENDh
00711 
00712                 // if we've got a grad fill, modify the token we got
00713                 if(DoingGradFill)
00714                     Token = EPSC_b;
00715             }
00716             
00717             // process the possily modified token normally
00718             HadhToken = FALSE;
00719             return EPSFilter::ProcessToken();
00720             break;
00721 
00722         // modify path closing for grad fills.
00723         case EPSC_n:
00724             if(DoingGradFill)
00725             {
00726                 Token = EPSC_f;         // we want to fill the thing
00727                 HadhToken = FALSE;
00728                 return EPSFilter::ProcessToken();
00729                 break;
00730             }
00731             HadhToken = FALSE;          // ignore h's as this is another end path thingy...
00732 
00733             if(pPath != 0)
00734                 return EPSFilter::ProcessToken();
00735             break;
00736 
00737         // unset the had h token for other path closing things
00738         case EPSC_N:
00739         case EPSC_F:
00740         case EPSC_f:
00741         case EPSC_B:
00742         case EPSC_b:
00743             HadhToken = FALSE;
00744             return EPSFilter::ProcessToken();
00745             break;
00746         
00747         // interested in path element things to switch off grad fills
00748         case EPSC_m:
00749             if(InText)
00750             {
00751                 // if we're doing some text, discard the moveto command
00752                 if(!Stack.Discard(2))
00753                     goto EPSError;
00754 
00755                 break;
00756             }
00757         case EPSC_l:
00758         case EPSC_L:
00759         case EPSC_c:
00760         case EPSC_C:
00761         case EPSC_v:
00762         case EPSC_V:
00763         case EPSC_y:
00764         case EPSC_Y:
00765             // maybe we need an h token to be sent
00766             if(HadhToken)
00767                 FHEF_SENDh
00768             
00769             // stop grad fill
00770             if(DoingGradFill)
00771             {
00772                 // turn the grad fill state off
00773                 DoingGradFill = FALSE;
00774 
00775                 // restore the old fill type
00776                 RestoreCurrentFill();
00777             }
00778             return EPSFilter::ProcessToken();
00779             break;
00780 
00781         
00782         case EPSC_recfill:
00783             {
00784                 // get the colours
00785                 DocColour StartColour, EndColour;
00786 
00787                 if(!PopColour(&EndColour) ||
00788                         !PopColour(&StartColour))
00789                     goto EPSError;
00790 
00791                 // discard the fill type thingy - we can only do colours
00792                 if(!DiscardFillSubType())
00793                     goto EPSError;
00794 
00795                 // OK, now a few coords
00796                 DocCoord Centre;
00797                 double Angle;
00798                 DocRect BBox;
00799                 if(!Stack.PopCoordPair(&BBox.hi) ||
00800                         !Stack.PopCoordPair(&BBox.lo) ||
00801                         !Stack.Pop(&Angle) ||
00802                         !Stack.PopCoordPair(&Centre))
00803                     goto EPSError;
00804 
00805                 // munge the angle a little and get it into radians
00806                 Angle += 225;
00807                 Angle = (Angle * (2 * PI)) / 360;
00808 
00809                 // see if we can get a more accurate BBox
00810                 if(pPath != 0)
00811                 {
00812                     BBox = pPath->GetBoundingRect();
00813                     Centre.x = BBox.lo.x + (BBox.Width() / 2);
00814                     Centre.y = BBox.lo.y + (BBox.Height() / 2);
00815                 }
00816 
00817                 // OK, we've got all the stuff we need to do some niceness on it
00818                 BBox.Translate(0 - Centre.x, 0 - Centre.y);
00819                 DocCoord StartPoint, EndPoint;
00820 
00821                 StartPoint.x = Centre.x + (INT32)(((double)BBox.lo.x * cos(Angle)) - ((double)BBox.lo.y * sin(Angle)));
00822                 StartPoint.y = Centre.y + (INT32)(((double)BBox.lo.x * sin(Angle)) + ((double)BBox.lo.y * cos(Angle)));
00823                 EndPoint.x = Centre.x + (INT32)(((double)BBox.hi.x * cos(Angle)) - ((double)BBox.hi.y * sin(Angle)));
00824                 EndPoint.y = Centre.y + (INT32)(((double)BBox.hi.x * sin(Angle)) + ((double)BBox.hi.y * cos(Angle)));
00825         
00826                 // store current fill attribute
00827                 SaveCurrentFill();
00828 
00829                 // set the fill
00830                 if(!SetLinearFill(StartColour, EndColour, StartPoint, EndPoint))
00831                     goto NoMemory;
00832 
00833                 // say we're doing a grad fill
00834                 DoingGradFill = TRUE;
00835                 HadhToken = FALSE;          // absorb this
00836             }
00837             break;
00838 
00839         case EPSC_radfill:
00840             {
00841                 // get the colours
00842                 DocColour StartColour, EndColour;
00843 
00844                 if(!PopColour(&StartColour) ||
00845                         !PopColour(&EndColour))
00846                     goto EPSError;
00847 
00848                 // get the radius and centre coordinate
00849                 DocCoord Centre;
00850                 INT32 Radius;
00851                 if(!Stack.PopCoord(&Radius) ||
00852                         !Stack.PopCoordPair(&Centre))
00853                     goto EPSError;
00854 
00855                 // store current fill attribute
00856                 SaveCurrentFill();
00857 
00858                 // set the fill
00859                 DocCoord EndPoint(Centre.x + Radius, Centre.y);
00860                 if(!SetRadialFill(StartColour, EndColour, Centre, EndPoint))
00861                     goto NoMemory;
00862 
00863                 // say we're doing a grad fill
00864                 DoingGradFill = TRUE;
00865                 HadhToken = FALSE;
00866             }
00867             break;
00868 
00869         case EPSC_BeginSetup:
00870             // there's probably a colour list or something in that there setup thingy - search for the spots token
00871             {
00872                 BOOL Found = FALSE;
00873                 
00874                 while(Found == FALSE)
00875                 {
00876                     if(!EPSFile->GetToken())
00877                         return FALSE;
00878 
00879                     if(EPSFile->GetTokenType() == TOKEN_NORMAL)
00880                     {
00881                         if(camStrcmp(TokenBuf, _T("spots")) == 0)
00882                         {
00883                             // check to see if the array is about to start
00884                             if(!EPSFile->GetToken())
00885                                 return FALSE;
00886 
00887                             if(TokenBuf[0] == '[')
00888                             {
00889                                 TRACEUSER( "Ben", _T("Found spots\n"));
00890                                 Found = TRUE;
00891                             }
00892                         }
00893                     }
00894                 
00895                     if(camStrncmp(TokenBuf, _T("%%EndSetup"), 10) == 0)
00896                     {
00897                         TRACEUSER( "Ben", _T("Met end of setup without finding spots\n"));
00898                         break;
00899                     }
00900 
00901                     if(EPSFile->eof())
00902                         goto EPSError;
00903                 }
00904 
00905                 if(Found == TRUE)
00906                 {
00907                     InColours = TRUE;
00908                 }
00909             }
00910             break;
00911 
00912         case EPSC_def:
00913             if(InColours)
00914             {
00915                 // finished the colours...
00916                 TRACEUSER( "Ben", _T("Finished spot colours\n"));           
00917                 // scan for the end of the setup section
00918                 BOOL Found = FALSE;
00919                 
00920                 while(Found == FALSE)
00921                 {
00922                     if(!EPSFile->GetToken())
00923                         return FALSE;
00924 
00925                     if(EPSFile->GetTokenType() == TOKEN_COMMENT)
00926                     {
00927                         if(camStrncmp(TokenBuf, _T("%%EndSetup"), 10) == 0)
00928                         {
00929                             TRACEUSER( "Ben", _T("Found end of setup\n"));
00930                             Found = TRUE;
00931                         }
00932                     }
00933 
00934                     if(EPSFile->eof())
00935                         goto EPSError;
00936                 }
00937                 
00938                 // get the ] off the stack
00939                 EPSCommand Ignored;
00940                 Stack.PopCmd(&Ignored);
00941 
00942                 // empty it...
00943                 Stack.DeleteAll ();
00944                 InColours = FALSE;
00945             }
00946             else
00947             {
00948                 // probably a font type thingy - empty the stack including commands
00949                 Stack.DeleteAll ();
00950             }
00951             break;
00952 
00953         case EPSC_newcmykcustomcolor:
00954             // OK, here's a named colour... add it to those known
00955             {
00956                 // discard some random thingy
00957                 if(!Stack.Discard())
00958                     goto EPSError;
00959             
00960                 // get the name
00961                 String_64 ColourName;
00962                 if(!Stack.Pop(&ColourName))
00963                     goto EPSError;
00964 
00965                 // get the components
00966                 double C, M, Y, K;
00967                 if(!Stack.Pop(&K) ||
00968                         !Stack.Pop(&Y) ||
00969                         !Stack.Pop(&M) ||
00970                         !Stack.Pop(&C))
00971                     goto EPSError;
00972 
00973                 // make the new colour
00974                 ColourCMYK Colour;
00975                 Colour.Cyan = C;
00976                 Colour.Magenta = M;
00977                 Colour.Yellow = Y;
00978                 Colour.Key = K;
00979 
00980                 // add it
00981                 if(!pNewColours->AddColour(&ColourName, &Colour))
00982                     goto NoMemory;
00983 
00984                 // add it to the list of colours
00985                 // this is a bit of a bodge, but never mind. Shouldn't be that bad.
00986                 IndexedColour *TheNewColour = pNewColours->GetColour(ColourName);
00987 
00988                 if(TheNewColour == 0)
00989                     goto NoMemory;
00990 
00991                 // add it to the list of colours
00992                 // enough space?
00993                 if((ColourArrayEntries + 1) >= ColourArraySize)
00994                 {
00995                     TRACEUSER( "Ben", _T("Extening colour array\n"));
00996                     IndexedColour **NewPtr = (IndexedColour **)CCRealloc(ColourArray, (ColourArraySize + FHEF_COLOURARRAY_CHUNK) * sizeof(IndexedColour *));
00997 
00998                     if(NewPtr == 0)
00999                         goto NoMemory;
01000 
01001                     ColourArray = NewPtr;
01002                     ColourArraySize += FHEF_COLOURARRAY_CHUNK;
01003                 }
01004 
01005                 // add
01006                 ColourArray[ColourArrayEntries] = TheNewColour;
01007                 ColourArrayEntries++;
01008             }
01009             break;
01010 
01011         // ignore text stuff
01012         case EPSC_makesetfont:
01013             if(!Stack.DiscardArray())
01014                 goto EPSError;
01015             if(!Stack.Discard(1))
01016                 goto EPSError;
01017             InText = TRUE;
01018             break;
01019 
01020         case EPSC_ts:
01021             if(!Stack.Discard(6))
01022                 goto EPSError;
01023             break;
01024 
01025         case EPSC_stob:
01026         case EPSC_sts:
01027             Stack.DeleteAll ();
01028             InText = FALSE;
01029             break;
01030                 
01031         default:
01032             // Token not understood - pass on to base class
01033             return EPSFilter::ProcessToken();
01034     }
01035 
01036 
01037     // No errors encountered while parsing this token and its operands.
01038     return TRUE;
01039     
01040     
01041     // Error handlers:
01042 EPSError:
01043     HandleEPSError();
01044     return FALSE;
01045 
01046 NoMemory:
01047     HandleNoMemory();
01048     return FALSE;
01049 }
01050 
01051 
01052 /********************************************************************************************
01053 
01054 >   BOOL FreeHandEPSFilter::DiscardFillSubType()
01055 
01056     Author:     Ben_Summers (Xara Group Ltd) <camelotdev@xara.com>
01057     Created:    02/06/95
01058     Returns:    EPS error
01059     Purpose:    discards a fill sub type from the stack ('{}' or '{logtaper}' or something)
01060 
01061 ********************************************************************************************/
01062 
01063 BOOL FreeHandEPSFilter::DiscardFillSubType()
01064 {
01065     BOOL Done = FALSE;
01066     EPSType Type;
01067     while(Done == FALSE)
01068     {
01069         Type = Stack.GetType();
01070         if(Type == EPSTYPE_NONE)
01071         {
01072             // not enough stuff on the stack
01073             return FALSE;;
01074         }
01075         else if(Type == EPSTYPE_STRING)
01076         {
01077             // string - is this the ending?
01078             String_64 Str;
01079             if(!Stack.Pop(&Str))
01080                 return FALSE;
01081             if(camStrcmp(Str, _T("{")) == 0)
01082                 Done = TRUE;        // found end of this bit
01083         }
01084         else
01085         {
01086             // just something unimportant - discard it
01087             if(!Stack.Discard())
01088                 return FALSE;
01089         }
01090     }
01091     
01092     return TRUE;
01093 }
01094 
01095 
01096 /********************************************************************************************
01097 
01098 >   BOOL FreeHandEPSFilter::PopColour(DocColour *Col)
01099 
01100     Author:     Ben_Summers (Xara Group Ltd) <camelotdev@xara.com>
01101     Created:    01/06/95
01102     Returns:    TRUE if colour understood and processed ok, FALSE if not.
01103     Purpose:    Pops a FreeHand colour off the stack.
01104     Errors:     Syntax error in EPS, Out of memory.
01105     SeeAlso:    EPSFilter::ProcessToken
01106 
01107 ********************************************************************************************/
01108 
01109 BOOL FreeHandEPSFilter::PopColour(DocColour *Col)
01110 {
01111     // OK, we expect an array of four values in the order (of popping) K, Y, M, C
01112 
01113     // check that we've got a array end
01114     EPSCommand Cmd;
01115     if(!Stack.PopCmd(&Cmd))
01116         return FALSE;
01117     if(Cmd != EPSC_ArrayEnd)
01118         return FALSE;
01119 
01120     // pop the colours
01121     double C, M, Y, K;
01122     if(!Stack.Pop(&K) ||
01123             !Stack.Pop(&Y) ||
01124             !Stack.Pop(&M) ||
01125             !Stack.Pop(&C))
01126         return FALSE;
01127 
01128     // check that we've got a array start
01129     if(!Stack.PopCmd(&Cmd))
01130         return FALSE;
01131     if(Cmd != EPSC_ArrayStart)
01132         return FALSE;
01133 
01134     // knock up the colour...
01135     ColourCMYK Colour;
01136     
01137     Colour.Cyan = C;
01138     Colour.Magenta = M;
01139     Colour.Yellow = Y;
01140     Colour.Key = K;
01141     
01142     IndexedColour *NewCol = new IndexedColour(COLOURMODEL_CMYK, (ColourGeneric *)&Colour);
01143 
01144     if(NewCol == 0)
01145         return FALSE;
01146 
01147     pColours->GetColourList()->GetUnnamedColours()->AddTail(NewCol);
01148 
01149     Col->MakeRefToIndexedColour(NewCol);
01150 
01151     return TRUE;
01152 }
01153 
01154 
01155 /********************************************************************************************
01156 
01157 >   BOOL FreeHandEPSFilter::PopNamedColour(DocColour *Col)
01158 
01159     Author:     Ben_Summers (Xara Group Ltd) <camelotdev@xara.com>
01160     Created:    01/06/95
01161     Returns:    TRUE if colour understood and processed ok, FALSE if not.
01162     Purpose:    Pops a FreeHand named colour off the stack.
01163     Errors:     Syntax error in EPS, Out of memory.
01164     SeeAlso:    EPSFilter::ProcessToken
01165 
01166 ********************************************************************************************/
01167 
01168 BOOL FreeHandEPSFilter::PopNamedColour(DocColour *Col)
01169 {
01170     // OK, we expect an array of two values in the order (of popping) random, index
01171     // and we can use the index to look up the indexed colour we should use in our
01172     // nice list in ColourArray.
01173     // 'random'? Maybe it's a tint or something? Which doesn't seem to be used.
01174 
01175     // check that we've got a array end
01176     EPSCommand Cmd;
01177     if(!Stack.PopCmd(&Cmd))
01178         return FALSE;
01179     if(Cmd != EPSC_ArrayEnd)
01180         return FALSE;
01181 
01182     // pop the index
01183     double Random;
01184     INT32 Index;
01185     if(!Stack.Pop(&Index) ||
01186             !Stack.Pop(&Random))
01187         return FALSE;
01188 
01189     // check that we've got a array start
01190     if(!Stack.PopCmd(&Cmd))
01191         return FALSE;
01192     if(Cmd != EPSC_ArrayStart)
01193         return FALSE;
01194 
01195     // check that the index is valid
01196     if(Index < 0 || Index >= ColourArrayEntries)
01197         return FALSE;
01198 
01199     // knock up the colour...
01200     Col->MakeRefToIndexedColour(ColourArray[Index]);
01201 
01202     return TRUE;
01203 }
01204 
01205 
01206 /********************************************************************************************
01207 
01208 >   BOOL FreeHandEPSFilter::SaveCurrentFill()
01209 
01210     Author:     Ben_Summers (Xara Group Ltd) <camelotdev@xara.com>
01211     Created:    02/06/95
01212     Returns:    error flag
01213     Purpose:    stores the current fill attribute
01214     SeeAlso:    EPSFilter::ProcessToken
01215 
01216 ********************************************************************************************/
01217 
01218 BOOL FreeHandEPSFilter::SaveCurrentFill()
01219 {
01220     // get rid of any stored fill
01221     if(OldFill != 0)
01222         delete OldFill;
01223 
01224     // create a copy of the current fill attribute
01225     CCRuntimeClass* ObjectType = CurrentAttrs[ATTR_FILLGEOMETRY].pAttr->GetRuntimeClass();
01226     AttributeValue* AttrClone = (AttributeValue*)ObjectType->CreateObject();
01227 
01228     if(AttrClone == 0)
01229         return FALSE;
01230     
01231     AttrClone->SimpleCopy(CurrentAttrs[ATTR_FILLGEOMETRY].pAttr);
01232 
01233     // set the old fill pointer to the nice clone we've made
01234     OldFill = AttrClone;
01235 
01236     return TRUE;
01237 }
01238 
01239 
01240 /********************************************************************************************
01241 
01242 >   BOOL FreeHandEPSFilter::RestoreCurrentFill()
01243 
01244     Author:     Ben_Summers (Xara Group Ltd) <camelotdev@xara.com>
01245     Created:    02/06/95
01246     Returns:    error flag
01247     Purpose:    restores the current fill attribute from the stored one
01248     SeeAlso:    EPSFilter::ProcessToken
01249 
01250 ********************************************************************************************/
01251 
01252 BOOL FreeHandEPSFilter::RestoreCurrentFill()
01253 {
01254     if(OldFill == 0)
01255         return TRUE;            // no saved state
01256 
01257     // delete the old attribute
01258     if(CurrentAttrs[ATTR_FILLGEOMETRY].pAttr != 0)
01259         delete CurrentAttrs[ATTR_FILLGEOMETRY].pAttr;
01260 
01261     // make the current one the old one
01262     CurrentAttrs[ATTR_FILLGEOMETRY].pAttr = OldFill;
01263 
01264     // mark this as used so it doesn't get deleted
01265     OldFill = 0;
01266     
01267     return TRUE;
01268 }
01269 
01270 /********************************************************************************************
01271 
01272 >   BOOL FreeHandEPSFilter::ValidateGroup(Node *pGroup);
01273 
01274     Author:     Ben_Summers (Xara Group Ltd) <camelotdev@xara.com>
01275     Created:    02/06/95
01276     Returns:    error flag
01277     Purpose:    validates a group created by the filter. As all objects have a group around
01278                 then, this removes groups which only have one entry.
01279 
01280 ********************************************************************************************/
01281 
01282 BOOL FreeHandEPSFilter::ValidateGroup(Node *pGroup)
01283 {
01284     ERROR2IF(pGroup == 0, FALSE, "Group passed to validateGroup is null");
01285 
01286     Node *pFirstChild = pGroup->FindFirstChild();
01287 
01288     ERROR2IF(pFirstChild == 0, FALSE, "Group without children passed to ValidateGroup - should have been deleted by EndGroup"); 
01289 
01290     if(pFirstChild->FindNext() == 0)
01291     {
01292         // there was only one object - delete this group.
01293 
01294         // if pFirstNodeInRange is this node, set it to the child
01295         if(pFirstNodeInRange == pGroup)
01296             pFirstNodeInRange = pFirstChild;
01297         
01298         // move the child to the parent of the group
01299         if (!ImportInfo.pOp->DoMoveNode(pFirstChild, pNode, LASTCHILD))
01300             return FALSE;
01301 
01302         // see EPSFilter::EndGroup for the reason why it's hidden rather than deleted
01303         if (!ImportInfo.pOp->DoHideNode(pGroup, TRUE))
01304             return FALSE;
01305 
01306     }
01307 
01308     return TRUE;
01309 }
01310 
01311 /********************************************************************************************
01312 
01313 >   BOOL FreeHandEPSFilter::MaskedGroupEnding()
01314 
01315     Author:     Ben_Summers (Xara Group Ltd) <camelotdev@xara.com>
01316     Created:    02/06/95
01317     Returns:    EPS error flag
01318     Purpose:    Restores the graphic state after a masked group ends.
01319 
01320 ********************************************************************************************/
01321 
01322 BOOL FreeHandEPSFilter::MaskedGroupEnding()
01323 {
01324     // make the path type normal
01325     if(ThePathType == PATH_DISCARD || ThePathType == PATH_DISCARD_STICKY)
01326     {
01327         ThePathType = PATH_NORMAL;
01328     }
01329     
01330     return TRUE;
01331 }
01332 
01333 
01334 /********************************************************************************************
01335 
01336 >   BOOL FreeHandEPSFilter::RemoveLastSubPathIfNotUnique(Path *pIPath)
01337 
01338     Author:     Ben_Summers (Xara Group Ltd) <camelotdev@xara.com>
01339     Created:    15/06/95
01340     Returns:    error flag
01341     Purpose:    Removes the last subpath from a path if an sub path with it's exact coords
01342                 already exists.
01343 
01344 ********************************************************************************************/
01345 
01346 BOOL FreeHandEPSFilter::RemoveLastSubPathIfNotUnique(Path *pIPath)
01347 {
01348     // get coord arrays
01349     INT32 NCoords = pIPath->GetNumCoords();
01350     DocCoord *Coords = pIPath->GetCoordArray();
01351     PathVerb *Verbs = pIPath->GetVerbArray();
01352 
01353     // find the last moveto
01354     INT32 LastMoveTo = -1;
01355     INT32 c;
01356 
01357     for(c = 0; c < NCoords; c++)
01358     {
01359         if((Verbs[c] & ~PT_CLOSEFIGURE) == PT_MOVETO)
01360         {
01361             LastMoveTo = c;
01362         }
01363     }
01364 
01365     // check that there is a last moveto and it's not the first coord...
01366     if(LastMoveTo == -1 || LastMoveTo == 0)
01367         return TRUE;            // nothing more to do
01368     
01369     // go through the coords matching subpaths...
01370     for(c = 0; c < LastMoveTo; c++)
01371     {
01372         if((Verbs[c] & ~PT_CLOSEFIGURE) == PT_MOVETO)
01373         {
01374             // found a moveto... compare the subpaths
01375             INT32 n = 0;
01376 
01377             while(((Verbs[c+n] & ~PT_CLOSEFIGURE) == (Verbs[LastMoveTo+n] & ~PT_CLOSEFIGURE)) && (Coords[c+n] == Coords[LastMoveTo+n]))
01378             {
01379                 // this was ok... is the next a moveto and the subpath the right length?
01380 
01381                 if((LastMoveTo+n+1) == NCoords)
01382                 {
01383                     // yes, delete and return
01384                     return pIPath->DeleteFromElement(LastMoveTo);
01385                 }
01386                 
01387                 n++;
01388             }
01389         }
01390     }
01391 
01392     return TRUE;
01393 }
01394 
01395 
01396 /********************************************************************************************
01397 
01398 >   BOOL FreeHandEPSFilter::AddNewNode(Node *pNewNode)
01399 
01400     Author:     Ben_Summers (Xara Group Ltd) <camelotdev@xara.com>
01401     Created:    19/06/95
01402     Returns:    error flag
01403     Purpose:    checks to see if we're in complex path mode before passing on the node to
01404                 the base class. If we are, it joins it to the last one and fiddles about
01405                 with the attributes.
01406 
01407 ********************************************************************************************/
01408 
01409 BOOL FreeHandEPSFilter::AddNewNode(Node *pNewNode)
01410 {
01411     if(IS_A(pNewNode, NodePath))
01412         pLastPathSeen = (NodePath *)pNewNode;
01413     
01414     // check to see if we want to handle this
01415     if((ComplexPathMode == FALSE) || (pNewNode == 0) || (pNode == 0) || (!IS_A(pNewNode, NodePath)))
01416         return EPSFilter::AddNewNode(pNewNode);
01417 
01418     // check to see if this is the first...
01419     if(HadFirstOfComplexPath == FALSE)
01420     {
01421         HadFirstOfComplexPath = TRUE;
01422         return EPSFilter::AddNewNode(pNewNode);
01423     }
01424 
01425     // find the last child of the node
01426     Node *pLastChild = pNode->FindLastChild();
01427     if(pLastChild == 0 || !IS_A(pLastChild, NodePath))
01428         return EPSFilter::AddNewNode(pNewNode);
01429 
01430     // we know that both of these things are NodePaths.
01431     Path *pTarget = &((NodePath *)pLastChild)->InkPath;
01432     Path *pAddition = &((NodePath *)pNewNode)->InkPath;
01433 
01434     // work out the new flags for the target
01435     BOOL TargetFilled = pTarget->IsFilled;
01436     BOOL TargetStroked = pTarget->IsStroked;
01437     if(pAddition->IsFilled)     TargetFilled = TRUE;
01438     if(pAddition->IsStroked)    TargetStroked = TRUE;
01439 
01440     // add this new path to the old one...
01441     if(!pTarget->MergeTwoPaths(*pAddition))
01442         return FALSE;
01443 
01444     // check that the thing we just added isn't already there
01445     if(!RemoveLastSubPathIfNotUnique(pTarget))
01446         return FALSE;
01447 
01448     // set it's flags
01449     pTarget->IsFilled = TargetFilled;
01450     pTarget->IsStroked = TargetStroked;
01451 
01452     // vape it's attributes
01453     pLastChild->DeleteChildren(pLastChild->FindFirstChild());
01454 
01455     // apply some new ones
01456     SetPathFilled(TargetFilled);
01457     if(!AddAttributes((NodePath *)pLastChild, TargetStroked, TargetFilled))
01458         return FALSE;
01459 
01460     // hide the nice additional path
01461     //if(!ImportInfo.pOp->DoHideNode(pNewNode, TRUE))
01462     //  return FALSE;
01463     pNewNode->CascadeDelete();
01464     delete pNewNode;
01465 
01466     // set the last seen path bollox
01467     pLastPathSeen = (NodePath *)pLastChild;
01468 
01469     // done!
01470     return TRUE;
01471 }
01472 
01473 
01474 
01475 

Generated on Sat Nov 10 03:45:23 2007 for Camelot by  doxygen 1.4.4