epsfiltr.cpp

Go to the documentation of this file.
00001 // $Id: epsfiltr.cpp 1668 2006-08-04 11:45:17Z alex $
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 
00099 // An EPS import/export filter system for Camelot.
00100 
00101 
00102 #include "camtypes.h"
00103 //#include "epsfiltr.h" - in camtypes.h [AUTOMATICALLY REMOVED]
00104 
00105 #include <ctype.h>
00106 
00107 //#include "attrmgr.h" - in camtypes.h [AUTOMATICALLY REMOVED]
00108 #include "bitfilt.h"
00109 #include "ccdc.h"
00110 #include "colcomp.h"
00111 #include "csrstack.h"
00112 //#include "doccomp.h" - in camtypes.h [AUTOMATICALLY REMOVED]
00113 //#include "document.h" - in camtypes.h [AUTOMATICALLY REMOVED]
00114 //#include "docview.h" - in camtypes.h [AUTOMATICALLY REMOVED]
00115 //#include "errors.h" - in camtypes.h [AUTOMATICALLY REMOVED]
00116 //#include "ensure.h" - in camtypes.h [AUTOMATICALLY REMOVED]
00117 #include "prvwflt.h"
00118 #ifndef WEBSTER
00119 //#include "extfilts.h"
00120 #endif //WEBSTER
00121 //#include "fixmem.h" - in camtypes.h [AUTOMATICALLY REMOVED]
00122 //#include "group.h" - in camtypes.h [AUTOMATICALLY REMOVED]
00123 #include "layer.h"
00124 #include "lineattr.h"
00125 #include "nativeps.h"   // old native filter based on eps, used for version 1.1  
00126 #include "nodeelip.h"
00127 #include "nodepath.h"
00128 #include "noderect.h"
00129 //#include "oilfltrs.h" - in camtypes.h [AUTOMATICALLY REMOVED]
00130 #include "page.h"
00131 #include "paper.h"
00132 //#include "pathname.h" - in camtypes.h [AUTOMATICALLY REMOVED]
00133 //#include "paths.h" - in camtypes.h [AUTOMATICALLY REMOVED]
00134 #include "progress.h"        
00135 //#include "resource.h"
00136 //#include "rik.h"
00137 #include "saveeps.h"
00138 #include "sglayer.h"
00139 #include "sprdmsg.h"
00140 //#include "spread.h" - in camtypes.h [AUTOMATICALLY REMOVED]
00141 //#include "tim.h"
00142 //#include "trans2d.h" - in camtypes.h [AUTOMATICALLY REMOVED]
00143 #include "gclips.h"
00144 #include "nodetxts.h"
00145 #include "nodetxtl.h"
00146 #include "nodershp.h"
00147 //#include "app.h" - in camtypes.h [AUTOMATICALLY REMOVED]
00148 #include "insertnd.h"
00149 #include "fontman.h"
00150 #include "guides.h"
00151 //#include "nev.h"      // _R(IDN_USER_CANCELLED)
00152 //#include "will2.h"
00153 #include "moldshap.h"
00154 #include "ai_bmp.h"
00155 //#include "epsclist.h" - in camtypes.h [AUTOMATICALLY REMOVED]
00156 #include "epscdef.h"
00157 #include "nodetext.h"
00158 
00159 DECLARE_SOURCE("$Revision: 1668 $");
00160 
00161 CC_IMPLEMENT_DYNAMIC(EPSFilter, VectorFilter);
00162 CC_IMPLEMENT_MEMDUMP(RangeList, List);
00163 CC_IMPLEMENT_MEMDUMP(EPSClipContext, List);
00164 CC_IMPLEMENT_MEMDUMP(EPSClipContextItem, ListItem);
00165 
00166 #define new CAM_DEBUG_NEW
00167 
00168 // Job 10463: remove PS Level bits - default to Level 2
00169 INT32 EPSFilter::XSEPSExportPSType = 2;
00170 INT32 EPSFilter::XSEPSExportDPI  = 200;
00171 BOOL EPSFilter::XSEPSExportTextAsCurves = FALSE;
00172 
00173 /********************************************************************************************
00174 
00175 >   class RangeListItem : public ListItem
00176 
00177     Author:     Tim_Browse (Xara Group Ltd) <camelotdev@xara.com>
00178     Created:    28/09/94
00179     Purpose:    Hold a range that
00180     SeeAlso:    
00181 
00182 ********************************************************************************************/
00183 
00184 class RangeListItem : public ListItem
00185 {
00186     CC_DECLARE_MEMDUMP(RangeListItem)
00187 
00188 public:
00189     RangeListItem(Node *pFirst, Node *pLast);
00190     BOOL IsLayer;
00191     Range TheRange;
00192     Layer *TheLayer;
00193 };
00194 
00195 CC_IMPLEMENT_MEMDUMP(RangeListItem, ListItem)
00196 
00197 /********************************************************************************************
00198 
00199 >   RangeListItem::RangeListItem(Node *pFirst, Node *pLast)
00200 
00201     Author:     Tim_Browse (Xara Group Ltd) <camelotdev@xara.com>
00202     Created:    29/09/94
00203     Inputs:     pFirst, pLast - the first and last nodes in the range.
00204     Purpose:    Create a range list item using the specified node range.
00205     SeeAlso:    RangeListItem; RangeList
00206 
00207 ********************************************************************************************/
00208 
00209 RangeListItem::RangeListItem(Node *pFirst, Node *pLast)
00210 {
00211     // range or layer?
00212     if(pFirst == pLast && IS_A(pFirst, Layer))
00213     {
00214         // just a layer, so make a layer variant
00215         TheLayer = (Layer *)pFirst;
00216         IsLayer = TRUE;
00217     }
00218     else
00219     {
00220         // it's more than one, or not a layer
00221         
00222         // We want all nodes to be included in the range
00223         RangeControl Flags;
00224         Flags.Selected = TRUE;
00225         Flags.Unselected = TRUE;
00226 
00227         // Make the range and copy to where we want it (spot slight omission from Range class!)
00228         Range Tmp(pFirst, pLast, Flags);
00229         TheRange = Tmp;
00230         IsLayer = FALSE;
00231     }
00232 }
00233 
00234 /********************************************************************************************
00235 
00236 >   BOOL RangeList::AddRange(Node *pFirst, Node *pLast)
00237 
00238     Author:     Tim_Browse (Xara Group Ltd) <camelotdev@xara.com>
00239     Created:    28/09/94
00240     Inputs:     pFirst, pLast - the first and last nodes in the range.
00241     Returns:    TRUE if the range was added ok;
00242                 FALSE if not.
00243     Purpose:    Adds another range to the range list.  All nodes from pFirst to pLast
00244                 are inclusively added to the range.
00245     SeeAlso:    Range
00246 
00247 ********************************************************************************************/
00248 
00249 BOOL RangeList::AddRange(Node *pFirst, Node *pLast)
00250 {
00251     // Get a new range
00252     RangeListItem *pItem = new RangeListItem(pFirst, pLast);
00253     if (pItem == NULL)
00254         // Out of memory
00255         return FALSE;
00256 
00257     // Add it to the list and return success
00258     AddTail(pItem);
00259     return TRUE;
00260 }
00261 
00262 /********************************************************************************************
00263 
00264 >   DocRect RangeList::GetBoundingRect()
00265 
00266     Author:     Tim_Browse (Xara Group Ltd) <camelotdev@xara.com>
00267     Created:    29/09/94
00268     Returns:    The union of the bounding boxes of all nodes in this range list.
00269     Purpose:    Goes through all nodes in all the ranges in this RangeList, and unions
00270                 all the bounding boxes together.  It uses the simple algorithm, which does
00271                 not take account of the attributes applied to the nodes - i.e. it only
00272                 gives an approximation (hence this function can result in bounding boxes
00273                 being in the tree which are not entirely accurate).
00274     SeeAlso:    RangeList
00275 
00276 ********************************************************************************************/
00277 
00278 DocRect RangeList::GetBoundingRect()
00279 {
00280     // Start off with empty rectangle
00281     DocRect RangeBBox;
00282 
00283     // Do each range in turn
00284     RangeListItem *pItem = (RangeListItem *) GetHead();
00285 
00286     while (pItem != NULL)
00287     {
00288         // Do each node in turn
00289         if(pItem->IsLayer)
00290         {
00291             // layer based calcuations
00292 
00293             // see long note below
00294             RangeBBox = RangeBBox.Union(pItem->TheLayer->GetBoundingRect(TRUE));
00295         }
00296         else
00297         {
00298             // range based calculations
00299             Node *pNode = pItem->TheRange.FindFirst();
00300 
00301             while (pNode != NULL)
00302             {
00303                 // Does this node have a bounding rectangle?
00304                 if (pNode->IsBounded())
00305                 {
00306                     // Yes - add it to our union
00307                     NodeRenderableBounded *pNodeBounded = (NodeRenderableBounded *) pNode;
00308 
00309                     // Pass in TRUE so that we don't do a 'proper' attribute scan for the
00310                     // bounding box - we only need an approximate bounding rectangle
00311                     // for centering it anyway.
00312                     // NB. this results in inaccurate bounding boxes in the tree, but when
00313                     //     we call this we're about to transform them anyway, so they'll
00314                     //     be updated correctly afterwards.
00315                     RangeBBox = RangeBBox.Union(pNodeBounded->GetBoundingRect(TRUE));
00316                 }
00317                 else
00318                 {
00319                     ENSURE(FALSE, "Found a non-renderable node in the import range");
00320                 }
00321 
00322                 // Try the next node.
00323                 pNode = pItem->TheRange.FindNext(pNode);
00324             }
00325         }
00326 
00327         // Try the next range
00328         pItem = (RangeListItem *) GetNext(pItem);
00329     }
00330 
00331     // Return the accumulated bounds
00332     return RangeBBox;
00333 }
00334 
00335 /********************************************************************************************
00336 
00337 >   void RangeList::Transform(TransformBase& Trans)
00338 
00339     Author:     Tim_Browse (Xara Group Ltd) <camelotdev@xara.com>
00340     Created:    29/09/94
00341     Inputs:     Trans - the transform object to transform all the nodes by.
00342     Purpose:    Transforms all the nodes in all the ranges of this range list using the
00343                 transform object given.
00344     SeeAlso:    Range::Transform; TransformBase
00345 
00346 ********************************************************************************************/
00347 
00348 void RangeList::Transform(TransformBase& Trans)
00349 {
00350     // Do each range in turn
00351     RangeListItem *pItem = (RangeListItem *) GetHead();
00352 
00353     while (pItem != NULL)
00354     {
00355         if(pItem->IsLayer)
00356         {
00357             pItem->TheLayer->Transform(Trans);
00358         }
00359         else
00360         {
00361             // Transform all nodes in this range
00362             Node *pNode = pItem->TheRange.FindFirst();
00363 
00364             while (pNode != NULL)
00365             {
00366                 // Is this node renderable?
00367                 if (pNode->IS_KIND_OF(NodeRenderable))
00368                 {
00369                     // Yes - transform it.
00370                     NodeRenderable *pNodeRenderable = (NodeRenderable *) pNode;
00371                     pNodeRenderable->Transform(Trans);
00372                 }
00373                 else
00374                 {
00375                     ENSURE(FALSE, "Found a non-renderable node in the import range");
00376                 }
00377 
00378                 // Try the next node.
00379                 pNode = pItem->TheRange.FindNext(pNode);
00380             }
00381         }
00382 
00383         // Try the next range
00384         pItem = (RangeListItem *) GetNext(pItem);
00385     }
00386 }
00387 
00388 /********************************************************************************************
00389 
00390 >   EPSClipContextItem::EPSClipContextItem(INT32 TheContextLevel, InkPath *pNewClipPath)
00391 
00392     Author:     Tim_Browse (Xara Group Ltd) <camelotdev@xara.com>
00393     Created:    05/22/95
00394     Inputs:     TheContextLevel - the context level for this clip region.
00395     Purpose:    Initialise the clip region item to sensible values.
00396     SeeAlso:    EPSClipContext
00397 
00398 ********************************************************************************************/
00399 
00400 EPSClipContextItem::EPSClipContextItem(INT32 TheContextLevel, Path *pNewClipPath)
00401 {
00402     ContextLevel = TheContextLevel;
00403     pClipPath = pNewClipPath;
00404 
00405     // Defaults to not a complex region.
00406     ComplexRegionLevel = 0;
00407 }
00408 
00409 /********************************************************************************************
00410 
00411 >   EPSClipContextItem::~EPSClipContextItem()
00412 
00413     Author:     Tim_Browse (Xara Group Ltd) <camelotdev@xara.com>
00414     Created:    05/22/95
00415     Purpose:    Clean up the context item - this includes deleting the clipping path held
00416                 in the region item.
00417     SeeAlso:    EPSClipContext
00418 
00419 ********************************************************************************************/
00420 
00421 EPSClipContextItem::~EPSClipContextItem()
00422 {
00423     delete pClipPath;
00424 }
00425 
00426 /********************************************************************************************
00427 
00428 >   EPSClipContext::EPSClipContext()
00429 
00430     Author:     Tim_Browse (Xara Group Ltd) <camelotdev@xara.com>
00431     Created:    05/22/95
00432     Purpose:    Initialise the context to a sensible state, i.e. a zero context level.
00433 
00434 ********************************************************************************************/
00435 
00436 EPSClipContext::EPSClipContext()
00437 {
00438     ContextLevel = 0;
00439     ComplexRegionLevel = 0;
00440 
00441     // Initially there is no clip region.
00442     pCachedClipPath = NULL;
00443     CacheIsValid = TRUE;
00444 
00445     // set default clipping flags
00446     ClippingFlags = 2 | CLIPPING_CLIP_WINDING;
00447 }
00448 
00449 /********************************************************************************************
00450 
00451 >   void EPSClipContext::SaveContext()
00452 
00453     Author:     Tim_Browse (Xara Group Ltd) <camelotdev@xara.com>
00454     Created:    05/22/95
00455     Purpose:    Save the current clipping state for future use - no matter how many
00456                 clip regions are added to the current context, the exact state of the
00457                 current clipping context can be restore with a (balanced) call to
00458                 EPSClipContext::RestoreContext().
00459     SeeAlso:    EPSClipContext::RestoreContext
00460 
00461 ********************************************************************************************/
00462 
00463 void EPSClipContext::SaveContext()
00464 {
00465     ContextLevel++;
00466 }
00467 
00468 /********************************************************************************************
00469 
00470 >   BOOL EPSClipContext::RestoreContext()
00471 
00472     Author:     Tim_Browse (Xara Group Ltd) <camelotdev@xara.com>
00473     Created:    05/22/95
00474     Purpose:    Restores the clipping context to the state it was in the last time
00475                 SaveContext() was called on this clip context.
00476     Errors:     No call to SaveContext() has been made => ERROR2
00477     SeeAlso:    EPSClipContext::SaveContext
00478 
00479 ********************************************************************************************/
00480 
00481 BOOL EPSClipContext::RestoreContext()
00482 {
00483     // Invalidate cache
00484     CacheIsValid = FALSE;
00485 
00486     // Sanity check
00487     ERROR2IF(ContextLevel == 0, FALSE, "Unbalanced call to EPSClipContext::RestoreContext()");
00488 
00489     // Decrement context level
00490     ContextLevel--;
00491 
00492     // Delete all items added since save context was last called...
00493     EPSClipContextItem *pItem = (EPSClipContextItem *) GetTail();
00494 
00495     while ((pItem != NULL) && (pItem->ContextLevel > ContextLevel))
00496     {
00497         delete RemoveTail();
00498         
00499         // Get the next item
00500         pItem = (EPSClipContextItem *) GetTail();
00501     }
00502 
00503     // If we get here then it's ok
00504     return TRUE;
00505 }
00506 
00507 
00508 /********************************************************************************************
00509 
00510 >   BOOL EPSClipContext::StartComplexClipRegion()
00511 
00512     Author:     Tim_Browse (Xara Group Ltd) <camelotdev@xara.com>
00513     Created:    05/22/95
00514     Returns:    TRUE if ok;
00515                 FALSE if out of memory.
00516     Purpose:    Indicates that we are about to start constructing a complex clipping region.
00517                 All future calls to AddNewClippingPath() will be joined into the current
00518                 clipping region until EndComplexRegion is called.
00519                 Note that you cannot interleave these calls with Save/Restore context calls;
00520                 complex regions cannot be unbalanced across context levels.
00521     Errors:     Out of memory => ERROR1
00522     SeeAlso:    EPSClipContext::EndComplexClipRegion()
00523 
00524 ********************************************************************************************/
00525 
00526 BOOL EPSClipContext::StartComplexClipRegion()
00527 {
00528     // Update complex region count
00529     ComplexRegionLevel++;
00530 
00531     // We need to create a record to hold this complex region
00532     // (it has a NULL clipping region initially)
00533     EPSClipContextItem *pItem = new EPSClipContextItem(ContextLevel, NULL);
00534     if (pItem == NULL)
00535         // Out of memory
00536         return FALSE;
00537 
00538     // It's a complex region so save the level
00539     pItem->ComplexRegionLevel = ComplexRegionLevel;
00540 
00541     // Add to end of list
00542     AddTail(pItem);
00543 
00544     // All ok
00545     return TRUE;
00546 }
00547 
00548 /********************************************************************************************
00549 
00550 >   BOOL EPSClipContext::EndComplexClipRegion()
00551 
00552     Author:     Tim_Browse (Xara Group Ltd) <camelotdev@xara.com>
00553     Created:    05/22/95
00554     Returns:    TRUE if ok;
00555                 FALSE if no corresponding call to StartComplexClipRegion() has been 
00556                 encountered => ERROR2
00557     Purpose:    See StartComplexClipRegion().
00558     Errors:     If unbalanced call is made => ERROR2
00559     SeeAlso:    EPSClipContext::StartComplexClipRegion
00560 
00561 ********************************************************************************************/
00562 
00563 BOOL EPSClipContext::EndComplexClipRegion()
00564 {
00565     ERROR2IF(ComplexRegionLevel == 0, FALSE, 
00566              "Unbalanced call to EPSClipContext::EndComplexClipRegion()");
00567 
00568     // Adjust complex region level.
00569     ComplexRegionLevel--;
00570 
00571     // All ok
00572     return TRUE;
00573 }
00574 
00575 
00576 /********************************************************************************************
00577 
00578 >   BOOL EPSClipContext::AddNewClippingPath(Path *pNewClipPath)
00579 
00580     Author:     Tim_Browse (Xara Group Ltd) <camelotdev@xara.com>
00581     Created:    05/22/95
00582     Inputs:     The path to augment the current clipping region with.
00583     Returns:    TRUE if clipping region updated ok;
00584                 FALSE if not, i.e. out of memory => ERROR1
00585     Purpose:    Takes a copy of the path passed in and merges it with the current clipping
00586                 context.
00587     Errors:     Out of memory => ERROR1
00588 
00589 ********************************************************************************************/
00590 
00591 BOOL EPSClipContext::AddNewClippingPath(Path *pNewClipPath)
00592 {
00593     // Invalidate cache
00594     CacheIsValid = FALSE;
00595 
00596     // Flag to indicate if we just do a straightforward copy
00597     BOOL SimpleCopy = FALSE;
00598 
00599     EPSClipContextItem *pItem = NULL;
00600 
00601     if (ComplexRegionLevel > 0)
00602     {
00603         // We are building a complex region, so join the new path with the existing one.
00604 
00605         // First, make sure we have a clip path already?
00606         pItem = (EPSClipContextItem *) GetTail();
00607         if (pItem->pClipPath == NULL)
00608         {
00609             // No clip path - just copy the new path and use that.
00610             SimpleCopy = TRUE;
00611         }
00612         else
00613         {
00614             // Already have a clip path - join with new one to form a complex path.
00615             if (!pItem->pClipPath->MergeTwoPaths(*pNewClipPath))
00616                 // Error occured (i.e. out of memory)
00617                 return FALSE;
00618         }
00619     }
00620     else
00621     {
00622         // Just a simple clipping region
00623         pItem = new EPSClipContextItem(ContextLevel, NULL);
00624 
00625         if (pItem == NULL)
00626             // Out of memory
00627             return FALSE;
00628 
00629         // Add to end of list
00630         AddTail(pItem);
00631 
00632         // Just copy the path passed in
00633         SimpleCopy = TRUE;
00634     }
00635 
00636     // Should we just copy the path into the list item?
00637     if (SimpleCopy)
00638     {
00639         // Sanity check
00640         ERROR2IF(pItem == NULL, FALSE, "No clipping item found!");
00641 
00642         // (yes, this code is pretty tedious!)
00643         pItem->pClipPath = new Path;
00644 
00645         if (pItem->pClipPath == NULL)
00646             // Out of memory
00647             return FALSE;
00648 
00649         // Graeme (26/7/00) - Added a test for a NULL pointer to prevent access violations.
00650         if ( pNewClipPath != NULL &&
00651              !pItem->pClipPath->Initialise ( pNewClipPath->GetNumCoords () + 3, 12 ) )
00652         {
00653             // Failed to initialise - out of memory
00654             return FALSE;
00655         }
00656 
00657         if (!pItem->pClipPath->CopyPathDataFrom(pNewClipPath))
00658             // Failed to copy - out of memory - but this shouldn't happen as
00659             // we made sure we had enough space with the initialise call!
00660             ERROR2(FALSE, "Unexpected out-of-space error when copying clipping path");
00661     }
00662 
00663     // If we've got here, it must have worked...
00664     return TRUE;
00665 }
00666 
00667 
00668 /********************************************************************************************
00669 
00670 >   BOOL EPSClipContext::ClipPathToRegion(Path *pPathToClip)
00671 
00672     Author:     Tim_Browse (Xara Group Ltd) <camelotdev@xara.com>
00673     Created:    05/22/95
00674     Inputs:     pPathToClip - the path object that we want to clip
00675     Outputs:    pPathToClip - the clipped version of the path
00676     Returns:    TRUE if clipped ok;
00677                 FALSE if not.
00678     Purpose:    Given a path, clip it to the current clipping region specified by this
00679                 object.
00680     SeeAlso:    EPSClipContext::GetClipRegion
00681 
00682 ********************************************************************************************/
00683 
00684 BOOL EPSClipContext::ClipPathToRegion(Path *pPathToClip)
00685 {
00686     // Cope with NULL clip region first
00687     if (GetClipRegion() == NULL)
00688         // Path is already 'clipped'
00689         return TRUE;
00690 
00691     // Ensure GetClipRegion validated the cache
00692     ERROR2IF(!CacheIsValid, FALSE, 
00693              "Could not get valid clipping path in EPSClipContext::ClipPathToRegion()");
00694 
00695     // We now have a valid region, so use it to clip the given path
00696     // NB. we do an intersection using the cliping path's winding, with a tolerance of 0,
00697     //     and flatness of 128 millipoints (arbitrary).
00698     if (pCachedClipPath->ClipPathToPath(*pPathToClip, pPathToClip, ClippingFlags, 
00699                                         128, 256, 256) == -1)
00700     {
00701         // An error occured...
00702         return FALSE;
00703     }
00704 
00705     // Initialise flags so the bezier tool is happy 
00706     // [see comment for Path::ClipPathToPath()].
00707     pPathToClip->InitialiseFlags();
00708 
00709     // If we get this far, it worked!
00710     return TRUE;
00711 }
00712 
00713 /********************************************************************************************
00714 
00715 >   Path *EPSClipContext::GetClipRegion()
00716 
00717     Author:     Tim_Browse (Xara Group Ltd) <camelotdev@xara.com>
00718     Created:    05/22/95
00719     Returns:    Pointer to the clipping path, or
00720                 NULL if no clipping region is specified by this object.
00721     Purpose:    Get the path which defines the current clipping region ofheld by this
00722                 object.
00723     SeeAlso:    EPSClipContext::ClipPathToRegion
00724 
00725 ********************************************************************************************/
00726 
00727 Path *EPSClipContext::GetClipRegion()
00728 {
00729     if (!CacheIsValid)
00730     {
00731         // Cache is invalid - if there is no path in the cache, get a new one, otherwise
00732         // empty the one in the cache and use that.
00733         if (pCachedClipPath == NULL)
00734         {
00735             pCachedClipPath = new Path;
00736 
00737             if (pCachedClipPath == NULL)
00738                 // Out of memory
00739                 return NULL;
00740 
00741             if (!pCachedClipPath->Initialise(12, 12))
00742                 // Failed to initialise - out of memory
00743                 return NULL;
00744         }
00745         else
00746         {
00747             // Just clear this path out so we can use it to build a clipping region.
00748             pCachedClipPath->ClearPath();
00749         }
00750 
00751         // Calculate the current clipping path - we do this by going along the region
00752         // list and combining the clip regions.
00753         EPSClipContextItem *pItem = (EPSClipContextItem *) GetHead();
00754 
00755         while (pItem != NULL)
00756         {
00757             if (pItem->pClipPath != NULL)
00758             {
00759                 // Do we already have a clipping region?
00760                 if (pCachedClipPath->GetNumCoords() > 0)
00761                 {
00762                     // Combine this clipping region with the region being constructed...
00763                     if (pItem->pClipPath->ClipPathToPath(*pCachedClipPath, pCachedClipPath, 
00764                                                          2 | CLIPPING_CLIP_WINDING, 
00765                                                          128, 256, 256) == -1)
00766                     {
00767                         // An error occured...
00768                         return FALSE;
00769                     }
00770                 }
00771                 else
00772                 {
00773                     // No clipping region yet - just copy the data from this path
00774                     if (!pCachedClipPath->MakeSpaceInPath(pItem->pClipPath->GetNumCoords() + 3))
00775                         // Failed to initialise - out of memory
00776                         return FALSE;
00777 
00778                     if (!pCachedClipPath->CopyPathDataFrom(pItem->pClipPath))
00779                         // Failed to copy - out of memory - but this shouldn't happen as
00780                         // we made sure we had enough space with the initialise call!
00781                         ERROR2(FALSE, "Unexpected out-of-space error when copying clipping path");
00782                 }
00783             }
00784 
00785             // Get the next item
00786             pItem = (EPSClipContextItem *) GetNext(pItem);
00787         }
00788 
00789         // Did we get any clipping at all?
00790         if (pCachedClipPath->GetNumCoords() == 0)
00791         {
00792             // No clip region - delete and use NULL to indicate this.
00793             delete pCachedClipPath;
00794             pCachedClipPath = NULL;
00795         }
00796 
00797         // Validate cache
00798         CacheIsValid = TRUE;
00799     }
00800 
00801     // Cache must be valid, so return the contents of the cache.
00802     return pCachedClipPath;
00803 }
00804 
00805 
00806 // This is the array of ArtWorks EPS command/keyword names.
00807 CommandMap EPSFilter::Commands[] =
00808 {
00809     // Path Construction operators
00810     { EPSC_l,       _T("l") },
00811     { EPSC_c,       _T("c") },
00812     { EPSC_m,       _T("m") },
00813     { EPSC_L,       _T("L") },
00814     { EPSC_C,       _T("C") },
00815     { EPSC_v,       _T("v") },
00816     { EPSC_V,       _T("V") },
00817     { EPSC_y,       _T("y") },
00818     { EPSC_Y,       _T("Y") },
00819 
00820     // Colour operators
00821     { EPSC_g,       _T("g") },
00822     { EPSC_G,       _T("G") },
00823     { EPSC_k,       _T("k") },
00824     { EPSC_K,       _T("K") },
00825     { EPSC_x,       _T("x") },
00826     { EPSC_X,       _T("X") },
00827     { EPSC_p,       _T("p") },
00828     { EPSC_P,       _T("P") },
00829     
00830     // Path Painting operators
00831     { EPSC_N,       _T("N") },
00832     { EPSC_n,       _T("n") },
00833     { EPSC_F,       _T("F") },
00834     { EPSC_f,       _T("f") },
00835     { EPSC_S,       _T("S") },
00836     { EPSC_s,       _T("s") },
00837     { EPSC_B,       _T("B") },
00838     { EPSC_b,       _T("b") },
00839     { EPSC__u,      _T("*u") },
00840     { EPSC__U,      _T("*U") },
00841 
00842     // Group operators
00843     { EPSC_u,       _T("u") },
00844     { EPSC_U,       _T("U") },
00845     
00846     // Graphics state operators
00847     { EPSC_A,       _T("A") },
00848     { EPSC_d,       _T("d") },
00849     { EPSC_i,       _T("i") },
00850     { EPSC_D,       _T("D") },
00851     { EPSC_j,       _T("j") },
00852     { EPSC_J,       _T("J") },
00853     { EPSC_M,       _T("M") },
00854     { EPSC_w,       _T("w") },
00855 
00856     // Special operators
00857     { EPSC_ArrayStart,_T("[") },
00858     { EPSC_ArrayEnd,_T("]") },
00859     { EPSC_Slash,   _T("/") },
00860 
00861     // Overprint operators
00862     { EPSC_O,       _T("O") },
00863     { EPSC_R,       _T("R") },
00864 
00865     // Clipping operators
00866     { EPSC_q,       _T("q") },
00867     { EPSC_Q,       _T("Q") },
00868     { EPSC_H,       _T("H") },
00869     { EPSC_h,       _T("h") },
00870     { EPSC_W,       _T("W") },
00871     
00872     // Text operators
00873     { EPSC_To,      _T("To") },
00874     { EPSC_TO,      _T("TO") },
00875     { EPSC_Tp,      _T("Tp") },
00876     { EPSC_TP,      _T("TP") },
00877 
00878     // Matrix operators
00879     { EPSC_Tm,      _T("Tm") },
00880     { EPSC_Td,      _T("Td") },
00881     { EPSC_T_,      _T("T*") },
00882     { EPSC_TR,      _T("TR") },
00883 
00884     // Text Attribute operators
00885     { EPSC_Tr,      _T("Tr") },
00886     { EPSC_Tf,      _T("Tf") },
00887     { EPSC_Ta,      _T("Ta") },
00888     { EPSC_Tl,      _T("Tl") },
00889     { EPSC_Tt,      _T("Tt") },
00890     { EPSC_TW,      _T("TW") },
00891     { EPSC_Tw,      _T("Tw") },
00892     { EPSC_TC,      _T("TC") },
00893     { EPSC_Tc,      _T("Tc") },
00894     { EPSC_Ts,      _T("Ts") },
00895     { EPSC_Ti,      _T("Ti") },
00896     { EPSC_Tz,      _T("Tz") },
00897     { EPSC_TA,      _T("TA") },
00898     { EPSC_Tq,      _T("Tq") },
00899 
00900     // Text Body operators
00901     { EPSC_Tx,      _T("Tx") },
00902     { EPSC_Tj,      _T("Tj") },
00903     { EPSC_TX,      _T("TX") },
00904     { EPSC_Tk,      _T("Tk") },
00905     { EPSC_TK,      _T("TK") },
00906     { EPSC_Tplus,   _T("T+") },
00907     { EPSC_Tminus,  _T("T-") },
00908 
00909     // Misc
00910     { EPSC_showpage,_T("showpage") },
00911     { EPSC_end,     _T("end") },
00912     { EPSC_ctx,     _T("ctx") },
00913     { EPSC_ctex,    _T("ctex") },
00914 
00915     { EPSC_Invalid, _T("Invalid") },
00916 
00917     { EPSC_Name,    _T("Name") },
00918     { EPSC_Integer, _T("Integer") },
00919     { EPSC_Double,  _T("Double") },
00920     { EPSC_FixedPoint,_T("FixedPt") },
00921     { EPSC_String,  _T("String") },
00922     { EPSC_Comment, _T("Comment") },
00923     { EPSC_EOL,     _T("EOL") },
00924     { EPSC_EOF,     _T("EOF") }
00925 };
00926 
00927 
00928 RangeList EPSFilter::ImportRange;
00929 
00930 /********************************************************************************************
00931 
00932 >   EPSFilter::EPSFilter()
00933 
00934     Author:     Tim_Browse (Xara Group Ltd) <camelotdev@xara.com>
00935     Created:    28/10/93
00936     Purpose:    Constructor for an EPSFilter object.  The object should be initialised
00937                 before use.
00938     SeeAlso:    EPSFilter::Init
00939 
00940 ********************************************************************************************/
00941 
00942 EPSFilter::EPSFilter()
00943 {
00944     ImportMsgID = _R(IDT_IMPORTMSG_AI);
00945     Flags.CanImport = TRUE;
00946     //WEBSTER-Martin-27/01/97
00947 #ifdef WEBSTER
00948     Flags.CanExport = FALSE;
00949 #else
00950     Flags.CanExport = TRUE;
00951 #endif //WEBSTER
00952     FilterID = FILTERID_AIEPS;
00953 
00954 #ifndef DO_EXPORT
00955     Flags.CanExport = FALSE;
00956 #endif
00957 
00958     EPSFile = NULL;
00959     TokenBuf = NULL;
00960     pColours = NULL;
00961 
00962     ExportDCPtr = NULL;
00963     ExportRegion = NULL;
00964     ExportMsgID = _R(IDT_EXPORTMSG_AI);
00965 
00966     AdjustOrigin = TRUE;
00967     SupportsLayers = TRUE;
00968 
00969     // The offsets into the file for the Preview thing
00970     EPSStart = 0;
00971     EPSEnd = 0;
00972     PreviewStart = 0;
00973     PreviewEnd = 0;
00974 
00975     TextComment[0] = 0;
00976     TextComment[1] = 0;
00977     TextComment[2] = 1;
00978 
00979     // set the font extras block vars. These indicate to the
00980     // filter to do something special with the next set of font attributes
00981     FontFlags.Bold = FALSE;
00982     FontFlags.Italic = FALSE;
00983 
00984     // Set the font type to assume we are loading true type fonts unless
00985     // told otherwise.
00986     ClassOfFont = FC_TRUETYPE;
00987     
00988     // Define the mould threshold default value.
00989     NewMouldThreshold = MOULD_V1THRESHOLD;
00990 
00991     //  We're not in a group at first.
00992     EPS_Group   =   FALSE;
00993 
00994     // Clear the length of the file to the end of the EPS code.
00995     EndOfEPS    = 0;
00996 
00997     // Set up the comment list. These are the known DSC comments, and how to handle them.
00998     EPSComments.Add ( _T("%%BeginProlog"),          _T("%%EndProlog"),          TRUE,   0 );
00999     EPSComments.Add ( _T("%%BeginSetup"),           _T("%%EndSetup"),           TRUE,   0 );
01000     EPSComments.Add ( _T("%%BeginDocument"),        _T("%%EndDocument"),        FALSE,  0 );
01001     EPSComments.Add ( _T("%%BeginPageSetup"),       _T("%%EndPageSetup"),       FALSE,  0 );
01002     EPSComments.Add ( _T("%%BeginDefaults"),        _T("%%EndDefaults"),        FALSE,  0 );
01003     EPSComments.Add ( _T("%%BeginEmulation"),       _T("%%EndEmulation"),       FALSE,  0 );
01004     EPSComments.Add ( _T("%%BeginPreview"),         _T("%%EndPreview"),         FALSE,  0 );
01005     EPSComments.Add ( _T("%%BeginFeature"),         _T("%%EndFeature"),         FALSE,  0 );
01006     EPSComments.Add ( _T("%%BeginFile"),            _T("%%EndFile"),            FALSE,  0 );
01007     EPSComments.Add ( _T("%%BeginFont"),            _T("%%EndFont"),            FALSE,  0 );
01008     EPSComments.Add ( _T("%%BeginProcSet"),         _T("%%EndProcSet"),         FALSE,  0 );
01009     EPSComments.Add ( _T("%%BeginResource"),        _T("%%EndResource"),        FALSE,  0 );
01010     EPSComments.Add ( _T("%%BeginCustomColor"),     _T("%%EndCustomColor"),     FALSE,  0 );
01011     EPSComments.Add ( _T("%%BeginProcessColor"),    _T("%%EndProcessColor"),    FALSE,  0 );
01012 }
01013 
01014 /********************************************************************************************
01015 
01016 >   virtual BOOL EPSFilter::Init()
01017 
01018     Author:     Tim_Browse (Xara Group Ltd) <camelotdev@xara.com>
01019     Created:    28/10/93
01020     Returns:    FALSE => don't use this class for real filters!
01021     Purpose:    Initialise an EPSFilter object.  This is a base class and so should
01022                 never actually be initialised so this function will always return FALSE,
01023                 and also ENSURE in debug builds.
01024 
01025 ********************************************************************************************/
01026 
01027 BOOL EPSFilter::Init()
01028 {
01029     // This class is now used only as basis for deriving EPS filters - it should not
01030     // actually be used as a real filter.
01031     ENSURE(FALSE, "Base EPSFilter class called! Do not use this!");
01032     return FALSE;
01033 }
01034 
01035 /********************************************************************************************
01036 
01037 >   BOOL EPSFilter::InitPrefs()
01038 
01039     Author:     Tim_Browse (Xara Group Ltd) <camelotdev@xara.com>
01040     Created:    06/01/95
01041     Purpose:    Initialise preferences for EPS formats.  This is used by the Camelot
01042                 EPS export filter, and includes font mappings, DPI, PostScript language
01043                 level control, and exporting text as curves.
01044 
01045 ********************************************************************************************/
01046 
01047 BOOL EPSFilter::InitPrefs()
01048 {
01049 #ifndef STANDALONE
01050 
01051     // Register preferences for EPS output.
01052     Camelot.DeclareSection(_T("EPS"), 5);
01053     Camelot.DeclarePref(NULL, _T("ExportPSType"), &XSEPSExportPSType, 0, 2); 
01054     Camelot.DeclarePref(NULL, _T("ExportDPI"), &XSEPSExportDPI, 10, 600); 
01055     Camelot.DeclarePref(NULL, _T("ExportTextAsCurves"), &XSEPSExportTextAsCurves); 
01056 
01057     // Register preferences for font mappings from TrueType to PostScript names.
01058     // This is used by Camelot EPS (or any other filter or class that wants to use it).
01059     Camelot.DeclareSection( _T("EPSFontMapping"), 50);
01060 
01061     static const TCHAR FontMappings[][2][30] = 
01062     {
01063         // Standard PostScript font mappings
01064 
01065         //
01066         // TrueType name          =>    PostScript name
01067         //
01068         { _T("Times-New-Roman"),            _T("Times-Roman")                   },
01069         { _T("Times-New-Roman-Bold"),       _T("Times-Bold")                    },
01070         { _T("Times-New-Roman-Italic"),     _T("Times-Italic")                  },
01071         { _T("Times-New-Roman-BoldItalic"), _T("Times-BoldItalic")              },
01072         { _T("Arial"),                      _T("Helvetica")                     },
01073         { _T("Arial-Bold"),                 _T("Helvetica-Bold")                },
01074         { _T("Arial-Italic"),               _T("Helvetica-Oblique")             },
01075         { _T("Arial-BoldItalic"),           _T("Helvetica-BoldOblique")         },
01076         { _T("Courier-New"),                _T("Courier")                       },
01077         { _T("Courier-New-Bold"),           _T("Courier-Bold")                  },
01078         { _T("Courier-New-Italic"),         _T("Courier-Oblique")               },
01079         { _T("Courier-New-BoldItalic"),     _T("Courier-BoldOblique")           },
01080         { _T("Michael"),                    _T("Palatino-Roman")                },
01081         { _T("Michael-Bold"),               _T("Palatino-Bold")                 },
01082         { _T("Michael-Italic"),             _T("Palatino-Italic")               },
01083         { _T("Michael-BoldItalic"),         _T("Palatino-BoldItalic")           },
01084         { _T("NewSchbook"),                 _T("NewCenturySchlbk-Roman")        },
01085         { _T("NewSchbook-Bold"),            _T("NewCenturySchlbk-Bold")         },
01086         { _T("NewSchbook-Italic"),          _T("NewCenturySchlbk-Italic")       },
01087         { _T("NewSchbook-BoldItalic"),      _T("NewCenturySchlbk-BoldItalic")   },
01088         { _T("ZapfDingbatsBT"),             _T("ZapfDingbats")                  },
01089         { _T("Symbol"),                     _T("Symbol")                        },
01090 
01091 #if 0
01092         // Currently we have no TrueType equivalents for these fonts.
01093         { _T(""),                           _T("AvantGarde-Book")               },
01094         { _T(""),                           _T("AvantGarde-Demi")               },
01095         { _T(""),                           _T("AvantGarde-BookOblique")        },
01096         { _T(""),                           _T("AvantGarde-DemiOblique")        },
01097         { _T(""),                           _T("Bookman-Light")                 },
01098         { _T(""),                           _T("Bookman-Demi")                  },
01099         { _T(""),                           _T("Bookman-LightItalic")           },
01100         { _T(""),                           _T("Bookman-DemiItalic")            },
01101         { _T(""),                           _T("ZapfChancery-MediumItalic")     },
01102 #endif
01103 
01104         // Terminator
01105         { _T(""),                           _T("")                              }
01106     };
01107 
01108     // Loop to declare all of these font mappings...
01109     INT32 i = 0;
01110     while (camStrclen(FontMappings[i][0]) > 0)
01111     {
01112         Camelot.SetPrefDirect( _T("EPSFontMapping"), 
01113                               (TCHAR *) &FontMappings[i][0][0], 
01114                               &FontMappings[i][1][0]); 
01115         i++;
01116     }
01117 
01118 #endif
01119 
01120     // All ok
01121     return TRUE;
01122 }
01123 
01124 // The size of the buffer used to hold tokens
01125 const INT32 TokenBufSize = 256;
01126 
01127 /********************************************************************************************
01128 
01129 >   BOOL EPSFilter::PrepareToImport()
01130 
01131     Author:     Tim_Browse (Xara Group Ltd) <camelotdev@xara.com>
01132     Created:    22/02/94
01133     Returns:    TRUE if succeeded, FALSE if not (e.g. no memory for EPS stack)
01134     Purpose:    Prepare to import EPS data using this filter.  This sets up the filter
01135                 to a sensible state for reading.
01136     Errors:     Out of memory.
01137     SeeAlso:    EPSFilter::DoImport; EPSFilter::CleanUpAfterImport
01138     Scope:      Private
01139 
01140 ********************************************************************************************/
01141 
01142 BOOL EPSFilter::PrepareToImport()
01143 {
01144     if (!SetUpCurrentAttrs())
01145         return FALSE;
01146 
01147     // Start the selection operation.
01148     if (!ImportInfo.pOp->DoStartSelOp(FALSE))
01149         return FALSE;
01150 
01151     // Error if no file object.
01152     if (EPSFile == NULL)
01153         return FALSE;
01154 
01155     // Get ready to parse EPS tokens.
01156     EPSFile->InitLexer();
01157     EPSFile->SetDelimiters("()<>[]{}/%");
01158     EPSFile->SetCommentMarker('%');
01159     EPSFile->SetStringDelimiters("()");
01160 
01161     TokenBuf = EPSFile->GetTokenBuf();
01162     Token = EPSC_Invalid;
01163 
01164     EPSFlags.ComplexPath = 0;//FALSE;
01165     EPSFlags.StrokeComplexPath = FALSE;
01166     EPSFlags.NoAttributes = FALSE;
01167     EPSFlags.EPSErrorEncountered = FALSE;
01168     EPSFlags.AddToNewLayer = FALSE;
01169     EPSFlags.PathIsHidden = FALSE;
01170 
01171     ThePathType = PATH_NORMAL;
01172 
01173 
01174     // Decide whether we need to use layers or not
01175     // Neville 7/8/97 - Layers are bad in Webster as they conflict with frames
01176     // Now the same is true in Camelot
01177 #ifndef WEBSTER
01178