cameleps.cpp

Go to the documentation of this file.
00001 // $Id: cameleps.cpp 1431 2006-07-11 15:14:59Z 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 // Camelot Native EPS format filter
00100 
00101 /*
00102 */
00103 
00104 #include "camtypes.h"
00105 
00106 #include "cameleps.h"
00107 
00108 #include "nodepath.h"
00109 //#include "paths.h" - in camtypes.h [AUTOMATICALLY REMOVED]
00110 //#include "tim.h"
00111 //#include "oilfltrs.h" - in camtypes.h [AUTOMATICALLY REMOVED]
00112 #include "ccdc.h"
00113 //#include "spread.h" - in camtypes.h [AUTOMATICALLY REMOVED]
00114 #include "page.h"
00115 //#include "fillattr.h" - in camtypes.h [AUTOMATICALLY REMOVED]
00116 #include "bitmpinf.h"
00117 //#include "attrmgr.h" - in camtypes.h [AUTOMATICALLY REMOVED]
00118 #include "nodebmp.h"
00119 #include "fracfill.h"
00120 //#include "resource.h"
00121 #include "nodeblnd.h"
00122 #include "nodebldr.h"
00123 #include "nodershp.h"
00124 #include "nodemold.h"
00125 #include "ndmldgrp.h"
00126 #include "nodemldr.h"
00127 #include "ndmldpth.h"
00128 #include "nodeshap.h"
00129 //#include "txtattr.h" - in camtypes.h [AUTOMATICALLY REMOVED]
00130 #include "grndbmp.h"
00131 #include "camview.h"
00132 //#include "docview.h" - in camtypes.h [AUTOMATICALLY REMOVED]
00133 #include "opgrad.h"     // for AreLinesPerpendicular()
00134 #include "oilfiles.h"
00135 #include "ndoptmz.h"
00136 #include "ndtxtpth.h"
00137 #include "maskedrr.h"
00138 #include "oilbitmap.h"
00139 //#include "dibutil.h" - in camtypes.h [AUTOMATICALLY REMOVED]
00140 #include "native.h"         // The new designed native filter, used for v2
00141 #include "nativeps.h"       // The old style EPS native filter, used in v1.1
00142 #include "psdc.h"
00143 #include "osrndrgn.h"
00144 #include "prdlgctl.h"
00145 #include "progress.h"
00146 #include "textfuns.h"
00147 //#include "app.h" - in camtypes.h [AUTOMATICALLY REMOVED]
00148 //#include "will.h"     // for _R(IDE_UNKOWN_EPSOBJECT)
00149 //#include "fixmem.h" - in camtypes.h [AUTOMATICALLY REMOVED]
00150 //#include "ben.h"
00151 #include "fontman.h"
00152 #include "guides.h"
00153 #include "snap.h"
00154 //#include "jason.h"
00155 #include "layer.h"
00156 #include "colormgr.h"
00157 #include "attrmap.h"
00158 #include "colplate.h"
00159 
00160 //#include "will2.h"
00161 
00162 #include "xsepsops.h"       // export eps options
00163 #include "ctrlhelp.h"       // ControlHelper class
00164 
00165 #include "unicdman.h"       // For MBCS support
00166 
00167 DECLARE_SOURCE("$Revision: 1431 $");
00168 
00169 #define new CAM_DEBUG_NEW
00170 
00171 
00172 CC_IMPLEMENT_DYNAMIC(CamelotEPSFilter, ArtWorksEPSFilter)
00173 
00174 
00175 
00176 typedef enum
00177 {
00178     FILLEFFECT_FADE,
00179     FILLEFFECT_RAINBOW,
00180     FILLEFFECT_ALTRAINBOW
00181 } TranspFillEffectType;
00182 
00183 
00184 
00185 // This is the array of Camelot EPS command/keyword names.
00186 CommandMap CamelotEPSFilter::CamelotCommands[] =
00187 {
00188     // Line/Fill colours
00189     { EPSC_cx,      _T("cx") },
00190     { EPSC_cX,      _T("cX") },
00191     { EPSC_ck,      _T("ck") },
00192     { EPSC_cK,      _T("cK") },
00193 
00194     // Stroke transparency
00195     { EPSC_cst,     _T("cst") },
00196                       
00197     // Arrow heads
00198     { EPSC_csah,    _T("csah") },
00199     { EPSC_ceah,    _T("ceah") },
00200 
00201     // Dash Patterns
00202     { EPSC_cdp,     _T("cdp") },
00203 
00204     // Bitmap objects/bitmap fills
00205     { EPSC_cbm,     _T("cbm") },
00206     { EPSC_csbm,    _T("csbm") },
00207     { EPSC_cebm,    _T("cebm") },
00208 
00209     // Chromatic fill geometries
00210     { EPSC_caz,     _T("caz") },
00211     { EPSC_cax,     _T("cax") },
00212     
00213     // Transparent fill geometries
00214     { EPSC_cxt,     _T("cxt") },
00215 
00216     // Chromatic fill effects
00217     { EPSC_cxe,     _T("cxe") },
00218 
00219     // Chromatic fill mappings
00220     { EPSC_cxm,     _T("cxm") },
00221 
00222     // Transparent fill mappings
00223     { EPSC_cxmt,    _T("cxmt") },
00224 
00225     // Blends
00226     { EPSC_csbd,    _T("csbd") },
00227     { EPSC_cebd,    _T("cebd") },
00228     { EPSC_csbr,    _T("csbr") },
00229     { EPSC_cebr,    _T("cebr") },
00230 
00231     // Regular shapes
00232     { EPSC_csrs,    _T("csrs") },
00233     { EPSC_crsp,    _T("crsp") },
00234     { EPSC_crstm,   _T("crstm") },
00235     { EPSC_crsp1,   _T("crsp1") },
00236     { EPSC_crsp2,   _T("crsp2") },
00237     { EPSC_cers,    _T("cers") },
00238 
00239     // Mould commands
00240     { EPSC_csev,    _T("csev") },
00241     { EPSC_ceev,    _T("ceev") },
00242     { EPSC_cspr,    _T("cspr") },
00243     { EPSC_cepr,    _T("cepr") },
00244     { EPSC_csmp,    _T("csmp") },
00245     { EPSC_cemp,    _T("cemp") },
00246     { EPSC_csso,    _T("csso") },
00247     { EPSC_ceso,    _T("ceso") },
00248     { EPSC_csdo,    _T("csdo") },
00249     { EPSC_cedo,    _T("cedo") },
00250 
00251     // Text commands
00252     { EPSC_ctf,     _T("ctf") },
00253     { EPSC_ctb,     _T("ctb") },
00254     { EPSC_cti,     _T("cti") },
00255     { EPSC_cts,     _T("cts") },
00256     { EPSC_ctp,     _T("ctp") },
00257     { EPSC_ctls,    _T("ctls") },
00258 
00259     { EPSC_cso,     _T("cso") },
00260     { EPSC_ceo,     _T("ceo") },
00261     { EPSC_cfft,    _T("cfft") },
00262     { EPSC_cftf,    _T("cftf") },
00263     { EPSC_cbot,    _T("cbot") },
00264 
00265     { EPSC_cpal,    _T("cpal") },
00266 
00267     // Guide layer & guideline
00268     { EPSC_glyr,    _T("glyr") },
00269     { EPSC_glne,    _T("glne") },
00270 
00271     { EPSC_cmth,    _T("cmth") },
00272 
00273     { EPSC_cag,     _T("cag") },
00274 
00275     { EPSC_cbti,    _T("cbti") },
00276 
00277     // Sentinel
00278     { EPSC_Invalid, _T("Invalid") }
00279 };
00280 
00281 /********************************************************************************************
00282 
00283 >   CamelotEPSFilter::CamelotEPSFilter()
00284 
00285     Author:     Tim_Browse (Xara Group Ltd) <camelotdev@xara.com>
00286     Created:    28/10/93
00287     Purpose:    Constructor for an CamelotEPSFilter object.  The object should be 
00288                 initialised before use.
00289     SeeAlso:    EPSFilter::Init
00290 
00291 ********************************************************************************************/
00292 
00293 CamelotEPSFilter::CamelotEPSFilter()
00294 {
00295     // Set up filter descriptions.
00296     FilterNameID = _R(IDT_CAMEPS_FILTERNAME);
00297     FilterInfoID = _R(IDT_CAMEPS_FILTERINFO);
00298     ImportMsgID  = _R(IDT_IMPORTMSG_CAMELOT);
00299     ExportMsgID  = _R(IDT_EXPORTMSG_CAMEPS);
00300 
00301     FilterID = FILTERID_CAMELOT_EPS;
00302 
00303 #ifndef STANDALONE
00304     Flags.CanImport = TRUE;
00305     //WEBSTER-Martin-27/01/97
00306 #ifdef WEBSTER
00307     Flags.CanExport = FALSE;
00308 #else
00309     Flags.CanExport = TRUE;
00310 #endif //WEBSTER
00311 #else
00312     Flags.CanImport = FALSE;
00313     Flags.CanExport = FALSE;
00314 #endif
00315 
00316     // We don't want to re-position Camelot files because they should be ok and
00317     // Charles gets upset if his drawings move.
00318     AdjustOrigin = FALSE;
00319 
00320 }
00321 
00322 /********************************************************************************************
00323 
00324 >   BOOL CamelotEPSFilter::Init()
00325 
00326     Author:     Tim_Browse (Xara Group Ltd) <camelotdev@xara.com>
00327     Created:    28/02/94
00328     Returns:    TRUE if the filter was initialised ok, FALSE otherwise.
00329     Purpose:    Initialise an CamelotEPSFilter object.
00330     Errors:     Will fail if not enough memory to initialise the EPS stack.
00331     SeeAlso:    EPSStack
00332 
00333 ********************************************************************************************/
00334 
00335 BOOL CamelotEPSFilter::Init()
00336 {
00337     // Get the OILFilter object
00338     pOILFilter = new CamelotEPSOILFilter(this);
00339     if (pOILFilter == NULL)
00340         return FALSE;
00341 
00342     // Load the description strings
00343     FilterName.Load(FilterNameID);
00344     FilterInfo.Load(FilterInfoID);
00345 
00346     // All ok
00347     return TRUE;
00348 }
00349 
00350 /********************************************************************************************
00351 
00352 >   virtual BOOL CamelotEPSFilter::CanIncludePreviewBmp()
00353 
00354     Author:     Rik_Heywood (Xara Group Ltd) <camelotdev@xara.com>
00355     Created:    2/2/95
00356     Returns:    TRUE
00357     Purpose:    Admits that Camelot EPS is able to have a Preview Bitmap attached to the
00358                 document. To actually put a bitmap into the file, call the
00359                 IncludePreviewBmp(TRUE).
00360     SeeAlso:    Filters::CanIncludePreviewBmp
00361 
00362 ********************************************************************************************/
00363 
00364 BOOL CamelotEPSFilter::CanIncludePreviewBmp()
00365 {
00366     return TRUE;
00367 }
00368 
00369 /********************************************************************************************
00370 
00371 >   virtual BOOL CamelotEPSFilter::IsDefaultDocRequired(const TCHAR* pcszPathName)
00372 
00373     Author:     Neville_Humphrys (Xara Group Ltd) <camelotdev@xara.com>
00374     Created:    9/10/95
00375     Inputs:     pcszPathName    pointer to the pathname to check
00376     Returns:    TRUE if the filter requires a default document, FALSE if not.
00377     Purpose:    Works out if opening a file of this type requires a default document to be
00378                 loaded. If the file format supplies the document then return FALSE otherwise
00379                 return TRUE. An example would be opening a bitmap file. This has no document
00380                 defined in the file format and so we need to load the default document before
00381                 importing the bitmap into this file.
00382                 In this baseclass version return FALSE and hence assume that the filters that
00383                 need to will override this function to return TRUE.
00384     SeeAlso:    Filter; Filter::IsDefaultDocRequired; CCamDoc::OnOpenDocument;
00385     SeeAlso:    FilterFamily::DoImport; Filter::DoImport;
00386 
00387 ********************************************************************************************/
00388 
00389 BOOL CamelotEPSFilter::IsDefaultDocRequired(const TCHAR* pcszPathName)
00390 {
00391     // Return false here. All derived classes should return what they need
00392     return FALSE;
00393 }   
00394 
00395 
00396 /********************************************************************************************
00397 
00398 >   INT32 CamelotEPSFilter::EPSHeaderIsOk(ADDR pFileHeader, UINT32 HeaderSize)
00399 
00400     Author:     Tim_Browse (Xara Group Ltd) <camelotdev@xara.com>
00401     Created:    28/02/94
00402     Returns:    TRUE if the header is ok and import should proceed, FALSE if not.
00403     Purpose:    Checks to see if the EPS comment headers specify that this is an Camelot
00404                 generated EPS file, as required.
00405 
00406 ********************************************************************************************/
00407 
00408 INT32 CamelotEPSFilter::EPSHeaderIsOk(ADDR pFileHeader, UINT32 HeaderSize)
00409 {
00410     // THIS ROUTINE IS NOT UNICODE
00411     // Check the first line in EPS file
00412     if (strncmp((char *) pFileHeader, "%!PS-Adobe-2.0 EPSF-1.2", 23) != 0)
00413     {
00414         // Incorrect version of EPS header line - we don't want this
00415         return 0;
00416     }
00417 
00418     // !PS-Adobe line is ok - check creator line...
00419     istringstream HeaderFile( (char*)pFileHeader );
00420     char Buffer[200];
00421 
00422     UINT32 Lines = 0;
00423     while ((Lines < 20) && !HeaderFile.eof())
00424     {
00425         HeaderFile.getline(Buffer, 200);
00426         Lines++;
00427 
00428         // Return TRUE if this file was created by Camelot
00429         if (strncmp(Buffer, "%%Creator: Camelot", 18) == 0)
00430         {
00431             // Camelot is the creator.
00432             return 10;
00433         }
00434         // (ChrisG 8/11/00) Changed creator type to "Xara X")
00435         else if (strncmp (Buffer, "%%Creator: Xara X", 17) == 0)
00436         {
00437             // New Xara X string was set as the creator.
00438             return 10;
00439         }
00440 
00441         // If we find the compression token then stop the search as we don't want to start
00442         // looking in the compressed data!
00443         if (strncmp(Buffer, "%%Compression:", 14)==0)
00444             break;
00445     }
00446 
00447     // Didn't find a suitable Creator line, but we did find an EPS line, so indicate
00448     // that we're interested, but not sure.
00449     return 5;
00450 }
00451 
00452 /********************************************************************************************
00453 
00454 >   BOOL CamelotEPSFilter::PrepareToImport()
00455 
00456     Author:     Tim_Browse (Xara Group Ltd) <camelotdev@xara.com>
00457     Created:    23/08/94
00458     Returns:    TRUE if filter initialised ok;
00459                 FALSE if not.
00460     Purpose:    See base class.  This just sets up the bitmap fill flag for Camelot EPS.
00461     Errors:     See base class.
00462     SeeAlso:    EPSFilter::PrepareToImport;
00463 
00464 ********************************************************************************************/
00465 
00466 BOOL CamelotEPSFilter::PrepareToImport()
00467 {
00468     if(IS_A(this, CamelotEPSFilter))
00469     {
00470         // tell the user that we can't do it
00471         PathName Path;
00472         String_256 FileName;
00473         BOOL HaveName = FALSE;
00474         if(EPSFile->IsKindOf(CC_RUNTIME_CLASS(CCDiskFile)))
00475         {
00476             CCDiskFile *DF = (CCDiskFile *)EPSFile;
00477 
00478             Path = DF->GetPathName();
00479             FileName = Path.GetFileName();
00480             HaveName = TRUE;
00481         }
00482         String_256 ErrMsg;
00483         if(HaveName)
00484         {
00485             String_256 ErrMsg;
00486             ErrMsg.MakeMsg(_R(IDE_LOADCAMELOTEPSITHINKNOTNAMED), (TCHAR *)FileName);
00487             Error::SetError(0, ErrMsg, 0);
00488         }
00489         else
00490         {
00491             Error::SetError(_R(IDE_LOADCAMELOTEPSITHINKNOT));
00492         }
00493         return FALSE;
00494     }
00495     else
00496     {
00497         // Initialise base class first.
00498         if (!ArtWorksEPSFilter::PrepareToImport())
00499             return FALSE;
00500 
00501         // Not expecting a bitmap fill initially
00502         m_PendingBitmap = PENDING_BITMAP_NONE;
00503         BitmapTransparencyIndex = -1;
00504         pBitmap = FALSE;
00505         pRegularShape = NULL;
00506     }
00507 
00508     return TRUE;
00509 }
00510 
00511 /********************************************************************************************
00512 
00513 >   void CamelotEPSFilter::CleanUpAfterImport(BOOL Successful)
00514 
00515     Author:     Ben_Summers (Xara Group Ltd) <camelotdev@xara.com>
00516     Created:    15/07/95
00517     Inputs:     
00518     Returns:    -
00519     Purpose:    stops stuff happening after clearing up for a Camelot EPS import
00520     Errors:     -
00521 
00522 ********************************************************************************************/
00523 
00524 void CamelotEPSFilter::CleanUpAfterImport(BOOL Successful)
00525 {
00526     if(IS_A(this, CamelotEPSFilter))
00527         return;
00528 
00529     ArtWorksEPSFilter::CleanUpAfterImport(Successful);
00530 }
00531 
00532 /********************************************************************************************
00533 
00534 >   virtual BOOL CamelotEPSFilter::GetExportOptions( )
00535 
00536     Author:     Neville_Humphrys (Xara Group Ltd) <camelotdev@xara.com> HUmphrys
00537     Created:    22/12/95
00538     Inputs:     -
00539     Returns:    TRUE if OK, FALSE if user pressed Cancel.
00540     Purpose:    Allows the user to be prompted to get information for export.
00541                 In this Camelot EPS case, we are affectively exporting a printable EPS
00542                 and so require the user to set a few options.
00543                 Moves the code from OilFilters to a proper function.
00544     Scope:      Protected.
00545 
00546 ********************************************************************************************/
00547 
00548 BOOL CamelotEPSFilter::GetExportOptions( )
00549 {
00550 #if !defined(EXCLUDE_FROM_RALPH)
00551     OpDescriptor* pOpDesc = OpDescriptor::FindOpDescriptor(CC_RUNTIME_CLASS(XSEPSExportOptions)); 
00552     if ( pOpDesc )
00553         pOpDesc->Invoke();
00554     
00555     return !XSEPSExportOptions::Aborted;
00556 #endif
00557     return TRUE;
00558 }
00559 
00560 /********************************************************************************************
00561 
00562 >   BOOL CamelotEPSFilter::PrepareToExport(CCLexFile* pFile, Spread* pSpread)
00563 
00564     Author:     Tim_Browse (Xara Group Ltd) <camelotdev@xara.com>
00565     Created:    15/04/94
00566     Inputs:     pFile - The file to export to
00567                 pSpread - the spread to export
00568     Returns:    TRUE if succeeded, FALSE if not (e.g. no memory for EPS stack)
00569     Purpose:    Prepare to import EPS data using this filter.  This sets up the filter
00570                 to a sensible state for reading.
00571     Errors:     Out of memory.
00572     SeeAlso:    EPSFilter::DoImport; EPSFilter::CleanUpAfterImport
00573     Scope:      Private
00574 
00575 ********************************************************************************************/
00576 
00577 BOOL CamelotEPSFilter::PrepareToExport(CCLexFile* pFile, Spread* pSpread)
00578 {
00579 #if !defined(EXCLUDE_FROM_RALPH)
00580     // Use base class to do most of it
00581     if (!EPSFilter::PrepareToExport(pFile, pSpread))
00582         return FALSE;
00583 
00584     // Set export device to use full Camelot accuracy for user space values.
00585     ExportDCPtr->SetFullAccuracy(TRUE);
00586 
00587     // Create the region if it is this class
00588     if (IS_A(this, CamelotEPSFilter))
00589     {
00590         // Don't care about clip regions when exporting - create a null region.
00591         DocRect NullClipRect;
00592         NullClipRect.MakeEmpty();
00593 
00594         // Don't use rendering matrix when exporting EPS as it uses fractional coordinates.
00595         Matrix Identity;
00596 
00597         // Don't use view scale; set to 1
00598         FIXED16 Scale(1);
00599 
00600         ExportRegion = new CamelotEPSRenderRegion(NullClipRect, Identity, Scale);
00601         if (ExportRegion == NULL)
00602             return FALSE;
00603 
00604         // Attach to the right device.
00605         ExportRegion->AttachDevice(DocView::GetSelected(), ExportDCPtr->GetDC(), pSpread);
00606     }
00607 #endif
00608     // All ok
00609     return TRUE;
00610 };
00611 
00612 /********************************************************************************************
00613 
00614 >   void CamelotEPSFilter::LookUpToken()
00615 
00616     Author:     Tim_Browse (Xara Group Ltd) <camelotdev@xara.com>
00617     Created:    25/02/94
00618     Returns:    TRUE if the token is an Camelot EPS token; FALSE if not.
00619     Purpose:    Compare the current token against the Camelot keywords to see if it is
00620                 one of them.
00621     SeeAlso:    EPSFilter::LookUpToken; EPSFilter::DecodeToken
00622 
00623 ********************************************************************************************/
00624 
00625 void CamelotEPSFilter::LookUpToken()
00626 {
00627     // Not interested in comments
00628     if (Token == EPSC_Comment)
00629         return;
00630 
00631     // Check to see if it is a keyword - cycle through the array of keyword names and
00632     // compare against our token (could use a hash table?)
00633     INT32 i = 0;
00634     while (CamelotCommands[i].Cmd != EPSC_Invalid)
00635     {
00636         if (camStrcmp(TokenBuf, CamelotCommands[i].CmdStr) == 0)
00637         {
00638             // Found the token - set the token variable and return success
00639             Token = CamelotCommands[i].Cmd;
00640             return;
00641         }
00642         // Try next command
00643         i++;
00644     }
00645 
00646     // Did not find this token - pass on to base class.
00647     ArtWorksEPSFilter::LookUpToken();
00648 }
00649 
00650 /********************************************************************************************
00651 
00652 >   BOOL CamelotEPSFilter::ProcessToken()
00653 
00654     Author:     Tim_Browse (Xara Group Ltd) <camelotdev@xara.com>
00655     Created:    25/02/94
00656     Returns:    TRUE if token understood and processed ok, FALSE if not.
00657     Purpose:    Processes EPS tokens that are not part of the standard Illustrator set, or
00658                 which need to be handled differently to the standard Illustrator meanings.
00659                 i.e. this is the function that handles all the Camelot EPS operators.
00660     Errors:     Syntax error in EPS, Out of memory.
00661     SeeAlso:    EPSFilter::ProcessToken
00662 
00663 ********************************************************************************************/
00664 
00665 BOOL CamelotEPSFilter::ProcessToken()
00666 {
00667     // Variables used to extract operands from the stack
00668     String_64   ColName;
00669     PColourCMYK Col;
00670     TintType    Tint = TINT_NONE;
00671     FIXEDPOINT  TintVal;
00672     DocCoord    StartPoint, 
00673                 EndPoint;
00674     PathFlags   Flags;
00675     INT32       ObjectNesting = 0;
00676 
00677     DocCoord    BmpCoords[4];
00678     INT32           i;
00679 
00680     // Used to keep track of bitmaps loading them in.
00681     static      NodeBitmap *pBitmapObject = NULL;
00682 
00683     // Decode the command, and execute it...
00684     switch (Token)
00685     {
00686         case EPSC_cso:
00687             INT32 ObjectID;
00688             if (!Stack.Pop(&ObjectID))
00689                 goto EPSError;     // pop the ID
00690 
00691             switch (ObjectID)
00692             {
00693                 case EOTAG_FONTFLAGS:
00694                 {
00695                     if (!ProcessFontFlags())
00696                         goto EPSError;
00697                 }
00698                 break;
00699 
00700                 case EOTAG_BITMAPFLAGS:
00701                 {
00702                     if (!ProcessBitmapFlags())
00703                         goto EPSError;
00704                 }
00705                 break;
00706 
00707                 case EOTAG_TEXTWRAPPED:
00708                 {
00709                     if (!FindEndOfTag())
00710                         goto EPSError;
00711                 }
00712                 break;
00713 
00714                 case EOTAG_FONTTYPE:
00715                 {
00716                     if (!ProcessFontType())
00717                         goto EPSError;
00718                 }
00719                 break;
00720 
00721                 case EOTAG_GUIDELAYER:
00722                 case EOTAG_GUIDELINE:
00723                     break;
00724 
00725                 case EOTAG_MOULDTHRESHOLD:
00726                 {
00727                     if (!ProcessMouldThreshold())
00728                         goto EPSError;
00729                 }
00730                 break;
00731 
00732 
00733                 default:
00734                 {
00735                     // Report we're skipping an unknown object
00736                     ERROR1RAW(_R(IDE_UNKNOWN_EPSOBJECT));
00737                     ObjectNesting++;
00738                     do
00739                     {
00740                         if (!GetToken())
00741                             goto EPSError;
00742 
00743                         LexTokenType Type = EPSFile->GetTokenType();
00744                         if ((Type != TOKEN_EOL) && (Type != TOKEN_EOF))
00745                         {
00746                             if (camStrcmp(TokenBuf, _T("cso")) == 0)
00747                             {
00748                                 ObjectNesting++;
00749                             }
00750                             else if (camStrcmp(TokenBuf, _T("ceo")) == 0)
00751                             {
00752                                 ObjectNesting--;
00753                             }                           
00754                         }
00755                     }
00756                     while (ObjectNesting > 0);
00757                 }
00758                 break;
00759             }
00760 
00761             break;
00762 
00763         case EPSC_ceo:
00764             break;
00765 //          goto EPSError;
00766 
00767         // Line/Fill colours
00768         case EPSC_cx:
00769         case EPSC_cX:
00770         case EPSC_ck:
00771         case EPSC_cK:
00772             break;
00773 
00774         // Linear/radial fills
00775         case EPSC_cax:
00776             // Colours are described using a name and tint as well as CMYK.
00777             Tint = TINT_ILLUSTRATOR;
00778         case EPSC_caz:
00779         {
00780             DocColour StartColour, EndColour;
00781             DocCoord StartPoint, EndPoint, EndPoint2;
00782             CamelotEPSFillType FillType;
00783 
00784             // Used for fractal fills
00785             INT32 Seed;
00786             double Graininess, Gravity, Squash;
00787             UINT32 DPI;
00788             INT32 Tileable;
00789 
00790             // Get fill type
00791             if (!Stack.Pop((INT32*) &FillType))
00792                 goto EPSError;
00793 
00794             TRACEUSER( "Will", _T("Importing Fill, Type=%d\n"),FillType);
00795 
00796             if (FillType == CAMEPS_FILL_FRACTAL)
00797             {
00798                 // If it's a fractal fill type, discard the 'sub-type' parameter (should 
00799                 // always be zero currently!)
00800                 INT32 SubType;
00801 
00802                 if (!Stack.Pop(&SubType))
00803                     goto EPSError;
00804 
00805                 // Default to no tiling
00806                 Tileable = FALSE;
00807 
00808                 // Work out the sub-type: either it has no tileable flag, or it does,
00809                 // or it's a type that is not supported.
00810                 if (SubType != 0)
00811                 {
00812                     if (SubType == 1)
00813                     {
00814                         // Get the tileable flag
00815                         if (!Stack.Pop(&Tileable))
00816                             goto EPSError;
00817                     }
00818                     else
00819                     {
00820                         ERROR2RAW("Bad fractal fill sub-type");
00821                         goto EPSError;
00822                     }
00823                 }
00824 
00825                 // Now get the fractal parameters:
00826                 if (!Stack.Pop(&DPI) ||
00827                     !Stack.Pop(&Squash) ||
00828                     !Stack.Pop(&Gravity) ||
00829                     !Stack.Pop(&Graininess) ||
00830                     !Stack.Pop(&Seed))
00831                 {
00832                     // Error in fractal parameteres
00833                     goto EPSError;
00834                 }
00835             }
00836 
00837             // For elliptical & bitmap fills, get the second end-point of the fill
00838             if ((FillType == CAMEPS_FILL_ELLIPTICAL) ||
00839                 (FillType == CAMEPS_FILL_BITMAP) ||
00840                 (FillType == CAMEPS_FILL_FRACTAL) ||
00841                 (FillType == CAMEPS_FILL_NEWBITMAP) ||
00842                 (FillType == CAMEPS_FILL_NEWLINEAR))
00843             {
00844                 if (!Stack.PopCoordPair(&EndPoint2))
00845                     goto EPSError;
00846             }
00847             
00848             // Get start and end positions for grad-fills
00849             if (!Stack.PopCoordPair(&EndPoint) || !Stack.PopCoordPair(&StartPoint))
00850                 goto EPSError;
00851 
00852             // Get start and end colours for grad-fills (but not bitmap fills)
00853             if (FillType != CAMEPS_FILL_BITMAP)
00854             {
00855                 if (!Stack.PopColour(&Col, Tint, &TintVal, &ColName))
00856                     // Invalid colour operands
00857                     goto EPSError;
00858 
00859                 GetEPSColour(&EndColour, &Col, Tint, TintVal, &ColName);
00860 
00861                 if (!Stack.PopColour(&Col, Tint, &TintVal, &ColName))
00862                     // Invalid colour operands
00863                     goto EPSError;
00864 
00865                 GetEPSColour(&StartColour, &Col, Tint, TintVal, &ColName);
00866             }
00867                 
00868             switch (FillType)
00869             {
00870                 // Decode Camelot EPS grad fill codes
00871                 case CAMEPS_FILL_LINEAR:
00872                     if (!SetLinearFill(StartColour, EndColour, StartPoint, EndPoint))
00873                         goto NoMemory;
00874                     break;
00875 
00876                 case CAMEPS_FILL_NEWLINEAR:
00877                     if (!SetLinearFill(StartColour, EndColour, StartPoint, EndPoint, &EndPoint2))
00878                         goto NoMemory;
00879                     break;
00880 
00881                 case CAMEPS_FILL_ELLIPTICAL:
00882                     if (!SetRadialFill(StartColour, EndColour, StartPoint, EndPoint, EndPoint2))
00883                         goto NoMemory;
00884                     break;
00885 
00886                 case CAMEPS_FILL_CIRCULAR:
00887                     if (!SetRadialFill(StartColour, EndColour, StartPoint, EndPoint))
00888                         goto NoMemory;
00889                     break;
00890 
00891                 case CAMEPS_FILL_CONICAL:
00892                     if (!SetConicalFill(StartColour, EndColour, StartPoint, EndPoint))
00893                         goto NoMemory;
00894                     break;
00895 
00896                 case CAMEPS_FILL_BITMAP:
00897                     
00898                     // Is it really a bitmap fill ?
00899                     // It may be a new type bitmap object
00900                     if (m_PendingBitmap != PENDING_BITMAP_OBJECT_FILL)
00901                         m_PendingBitmap = PENDING_BITMAP_FILL;
00902 
00903                     BitmapAttrs.Coords[0] = StartPoint;
00904                     BitmapAttrs.Coords[1] = EndPoint;
00905                     BitmapAttrs.Coords[2] = EndPoint2;
00906 
00907                     BitmapAttrs.StartCol    = COLOUR_NONE;
00908                     BitmapAttrs.EndCol      = COLOUR_NONE;
00909                     break;
00910 
00911                 case CAMEPS_FILL_NEWBITMAP:
00912                     m_PendingBitmap = PENDING_BITMAP_FILL;
00913                     BitmapAttrs.Coords[0] = StartPoint;
00914                     BitmapAttrs.Coords[1] = EndPoint;
00915                     BitmapAttrs.Coords[2] = EndPoint2;
00916 
00917                     BitmapAttrs.StartCol    = StartColour;
00918                     BitmapAttrs.EndCol      = EndColour;
00919                     break;
00920 
00921                 case CAMEPS_FILL_FRACTAL:
00922                     if (!SetFractalFill(StartColour, EndColour, 
00923                                         StartPoint, EndPoint, EndPoint2,
00924                                         Seed, Graininess, Gravity, Squash, DPI, Tileable))
00925                         goto NoMemory;
00926                     break;
00927 
00928                 default:
00929                     ENSURE(FALSE, "Unknown fill type found!");
00930                     break;  // Don't know this fill type
00931             }
00932 
00933             break;
00934         }
00935 
00936 
00937         case EPSC_cxe:
00938         {
00939             INT32 EffectType;
00940 
00941             // Get fill effect type
00942             if (!Stack.Pop(&EffectType))
00943                 goto EPSError;
00944 
00945             // Call base class to use it
00946             switch (EffectType)
00947             {
00948                 case FILLEFFECT_FADE:
00949                     if (!SetFadeFillEffect())
00950                         goto NoMemory;
00951                     break;
00952 
00953                 case FILLEFFECT_RAINBOW:
00954                     if (!SetRainbowFillEffect())
00955                         goto NoMemory;
00956                     break;
00957 
00958                 case FILLEFFECT_ALTRAINBOW:
00959                     if (!SetAltRainbowFillEffect())
00960                         goto NoMemory;
00961                     break;
00962 
00963                 default:
00964                     ERROR3_PF(("Unknown fill effect type (%d) in EPS", EffectType));
00965             }
00966             break;
00967         }
00968 
00969         case EPSC_cxm:
00970         {
00971             INT32 MappingType;
00972             INT32 Repeat;
00973 
00974             // Get fill mapping type (should always be 0)
00975             if (!Stack.Pop(&MappingType))
00976                 goto EPSError;
00977 
00978             if (MappingType != 0)
00979             {
00980                 ERROR2RAW("Bad mapping type in EPS");
00981                 goto EPSError;
00982             }
00983 
00984             // Get proper fill mapping type (should be 1, 2 or 3)
00985             if (!Stack.Pop(&Repeat))
00986                 goto EPSError;
00987 
00988             if ((Repeat < 1) || (Repeat > 3))
00989             {
00990                 ERROR2RAW("Bad mapping type in EPS");
00991                 goto EPSError;
00992             }
00993 
00994             // Call base class to use it
00995             if (Token == EPSC_cxm)
00996             {
00997                 if (!SetLinearFillMapping(Repeat))
00998                     goto NoMemory;
00999             }
01000             else
01001             {
01002                 if (!SetLinearTranspFillMapping(Repeat))
01003                     goto NoMemory;
01004             }
01005 
01006             break;
01007         }
01008 
01009         case EPSC_csbm:
01010             // Try to read in a bitmap
01011             if (!ReadBitmap())
01012                 return FALSE;
01013             break;
01014 
01015         case EPSC_cebm:
01016             // Should have just finished reading a bitmap...what should we use for?
01017             switch (m_PendingBitmap)
01018             {
01019                 case PENDING_BITMAP_FILL:
01020                     // Use the bitmap to do a bitmap fill
01021 
01022                     if (!SetBitmapFill(pBitmap, 
01023                                        BitmapAttrs.Coords[0], 
01024                                        BitmapAttrs.Coords[1], 
01025                                        BitmapAttrs.Coords[2],
01026                                        &BitmapAttrs.StartCol,
01027                                        &BitmapAttrs.EndCol))
01028                         return FALSE;
01029 
01030                     // NULL the colours, so we don't get 'Index Colour in Use'
01031                     BitmapAttrs.StartCol    = COLOUR_NONE;
01032                     BitmapAttrs.EndCol      = COLOUR_NONE;
01033 
01034                     // Get rid of our KernelBitmap object (the bitmap itself is now
01035                     // attached to the current fill geometry attribute.
01036                     pBitmap = NULL;
01037                     break;
01038 
01039                 case PENDING_BITMAP_TRANSPFILL:
01040                     // Use the bitmap to do a transparent bitmap fill (texture)
01041 
01042                     if (!SetBitmapTranspFill(BitmapAttrs.TranspType, pBitmap, 
01043                                              BitmapAttrs.Coords[0], 
01044                                              BitmapAttrs.Coords[1], 
01045                                              BitmapAttrs.Coords[2],
01046                                              BitmapAttrs.Transp,
01047                                              BitmapAttrs.EndTransp))
01048                         return FALSE;
01049 
01050                     // Get rid of our KernelBitmap object (the bitmap itself is now
01051                     // attached to the current transparent fill geometry attribute.
01052                     pBitmap = NULL;
01053                     break;
01054 
01055                 case PENDING_BITMAP_OBJECT_FILL:
01056 
01057                     // This is the new format used to save 'Non-contoned' bitmaps,
01058                     // where it is saved as a bitmap filled path, so the Viewer
01059                     // can render it, and an extra token that tells this code that
01060                     // it's really a bitmap object.
01061 
01062                     // Bitmap object - make one ready to put into the tree.
01063                     pBitmapObject = new NodeBitmap;
01064                     if ((pBitmapObject == NULL) || (!pBitmapObject->SetUpPath(12,12)))
01065                         goto EPSError;
01066 
01067                     // Now we need to Skip over the path that is output for the
01068                     // Viewer to render ...
01069                     while (!EPSFile->eof())
01070                     {
01071                         GetToken();
01072 
01073                         // We'll remember the coords and use them to build the bitmap
01074                         switch (Token)
01075                         {
01076                             case EPSC_Integer:
01077                                 Stack.Push(TokenData.Long);
01078                                 break;
01079 
01080                             case EPSC_Double:
01081                                 Stack.Push(TokenData.Double);
01082                                 break;
01083 
01084                             case EPSC_FixedPoint:
01085                                 Stack.Push(TokenData.FixedPoint);
01086                                 break;
01087                         }   
01088 
01089                         // Look for the 'Filled Path' token...
01090                         if (Token == EPSC_F)
01091                             break;
01092                     }
01093 
01094                     Stack.Discard(2);   // Throw away the first coord pair
01095 
01096                     // Now read in reverse order because they're stacked
01097                     for (i = 3; i >= 0; i--)
01098                     {
01099                         if (!Stack.PopCoordPair(&BmpCoords[i]))
01100                             goto EPSError;
01101                         pBitmapObject->Parallel[i] = BmpCoords[i];
01102                     }
01103 
01104                     // Put them into the path.
01105                     pBitmapObject->InkPath.InsertMoveTo(BmpCoords[0]);
01106 
01107                     for (i = 1; i <= 3; i++)
01108                         pBitmapObject->InkPath.InsertLineTo(BmpCoords[i]);
01109 
01110                     pBitmapObject->InkPath.InsertLineTo(BmpCoords[0]);
01111                     pBitmapObject->InkPath.CloseSubPath();
01112 
01113                     // Make sure the bitmap has no fill colour applied
01114                     SetPathFilled(FALSE);
01115                     
01116                     // Now fall though to the Bitmap Object code .....
01117 
01118                 case PENDING_BITMAP_OBJECT:
01119                 {
01120                     // Use the bitmap to do a bitmap object
01121 
01122                     // Copy the bitmap into this node and delete the one we've just imported.
01123                     if (pBitmapObject == NULL)
01124                         // No bitmap node to load!
01125                         goto EPSError;
01126 
01127                     pBitmapObject->GetBitmapRef()->Attach(pBitmap, GetDocument());
01128                     if (pBitmapObject->GetBitmap() != pBitmap)
01129                     {
01130                         // It didn't use the bitmap we gave it, so we can delete it
01131                         delete pBitmap;
01132                     }
01133 
01134                     pBitmap = NULL;
01135 
01136                     AttributeValue* pOldLineColour = NULL;
01137                     AttributeValue* pOldLineWidth = NULL;
01138 
01139                     if (m_PendingBitmap == PENDING_BITMAP_OBJECT_FILL)
01140                     {
01141                         // This must be a non-contone bitmap, so make sure we
01142                         // don't apply any line colour.
01143 
01144                         // (The fill colour will already have been turned off
01145                         //  with a 'SetPathFilled(FALSE)' above)
01146                     
01147                         pOldLineColour = CurrentAttrs[ATTR_STROKECOLOUR].pAttr;
01148                         
01149                         // Force Line Colour be NONE.
01150                         StrokeColourAttribute* pLineCol = new StrokeColourAttribute;
01151                         if (pLineCol == NULL)
01152                             goto NoMemory;
01153 
01154                         DocColour   colorNone( COLOUR_NONE );
01155                         pLineCol->SetStartColour( &colorNone );
01156                             
01157                         CurrentAttrs[ATTR_STROKECOLOUR].pAttr = pLineCol;
01158                     }
01159 
01160                     pOldLineWidth = CurrentAttrs[ATTR_LINEWIDTH].pAttr;
01161                     
01162                     // Force the line width to be zero for Bitmap objects
01163                     LineWidthAttribute* pLineWidth = new LineWidthAttribute;
01164                     if (pLineWidth == NULL)
01165                         goto NoMemory;
01166 
01167                     pLineWidth->LineWidth = 0;
01168                         
01169                     CurrentAttrs[ATTR_LINEWIDTH].pAttr = pLineWidth;
01170 
01171                     // Add attributes to the path, if they are different from the default...
01172                     BOOL Success = AttributeManager::ApplyBasedOnDefaults(pBitmapObject, 
01173                                                                           CurrentAttrs);
01174 
01175                     // Did it work?
01176                     if (!Success)
01177                     {
01178                         // No clean up and report error
01179                         delete pBitmapObject;
01180                         pBitmapObject = NULL;
01181                         goto NoMemory;
01182                     }
01183 
01184                     // Finally, add it into the tree
01185                     pBitmapObject->InvalidateBoundingRect();
01186                     AddNewNode(pBitmapObject);
01187 
01188                     // Switch path filling back on again
01189                     SetPathFilled(TRUE);
01190 
01191                     if (pOldLineColour != NULL)
01192                     {
01193                         delete CurrentAttrs[ATTR_STROKECOLOUR].pAttr;
01194 
01195                         // Restore the old line colour attribute
01196                         CurrentAttrs[ATTR_STROKECOLOUR].pAttr = pOldLineColour;
01197                     }
01198 
01199                     if (pOldLineWidth != NULL)
01200                     {
01201                         delete CurrentAttrs[ATTR_LINEWIDTH].pAttr;
01202 
01203                         // Restore the old line width attribute
01204                         CurrentAttrs[ATTR_LINEWIDTH].pAttr = pOldLineWidth;
01205                     }
01206 
01207                     // Get rid of our KernelBitmap object (the bitmap itself is now
01208                     // attached to the current fill geometry attribute.
01209                     pBitmap = NULL;
01210                     pBitmapObject = NULL;
01211                     break;
01212                 }
01213 
01214                 case PENDING_BITMAP_POOLITEM:
01215                 {
01216                     // Attach the Bitmap to a reference so that it is kept around for things to use
01217                     TRACEUSER( "Rik", _T("What do you know, its a Pool Item\n"));
01218                     BitmapPoolAttach(pBitmap);
01219                     pBitmap = NULL;
01220                     break;
01221                 }
01222 
01223                 default:
01224                     ENSURE(FALSE, "Found a bitmap but don't know what to do with it!");
01225                     delete pBitmap;
01226                     pBitmap = NULL;
01227                     goto EPSError;
01228                     break;
01229             }
01230 
01231             m_PendingBitmap = PENDING_BITMAP_NONE;
01232             BitmapTransparencyIndex = -1;
01233             break;
01234 
01235         case EPSC_cbm:
01236         {
01237             // Bitmap object - make one ready to put into the tree.
01238             pBitmapObject = new NodeBitmap;
01239             if ((pBitmapObject == NULL) || (!pBitmapObject->SetUpPath(12,12)))
01240                 return FALSE;
01241 
01242             // First, try to read in the coordinates of the bitmap object
01243             DocCoord Coords[4];
01244 
01245             // NB. reverse order because they're stacked, remember!
01246             INT32 i;
01247             for ( i = 3; i >= 0; i--)
01248             {
01249                 if (!Stack.PopCoordPair(&Coords[i]))
01250                     goto EPSError;
01251                 pBitmapObject->Parallel[i] = Coords[i];
01252             }
01253 
01254             // Put them into the path.
01255             pBitmapObject->InkPath.InsertMoveTo(Coords[0]);
01256 
01257             for (i = 1; i <= 3; i++)
01258                 pBitmapObject->InkPath.InsertLineTo(Coords[i]);
01259 
01260             pBitmapObject->InkPath.InsertLineTo(Coords[0]);
01261             pBitmapObject->InkPath.CloseSubPath();
01262 
01263 //          pBitmapObject->InkPath.UpdateBoundingRect();
01264 
01265             // Flag to the code above that the next bitmap should be added to this object.
01266             m_PendingBitmap = PENDING_BITMAP_OBJECT;
01267 
01268             break;
01269         }
01270 
01271         case EPSC_cbmp:
01272             // A bitmap palette - should not ever get this in ART files.
01273             goto EPSError;
01274 
01275         // Blend related procedures
01276         case EPSC_csbd:
01277             return ProcessBlend();
01278             
01279         case EPSC_cebd:
01280             goto EPSError;
01281             
01282         case EPSC_csbr:
01283             return ProcessBlender();
01284             
01285         case EPSC_cebr:
01286             goto EPSError;
01287 
01288         // Regular shape related procedures         
01289         case EPSC_csrs:
01290             return ProcessRegularShape();
01291 
01292         case EPSC_cers:
01293             goto EPSError;
01294 
01295         case EPSC_crsp:
01296             return ProcessRegularShapeParams();
01297 
01298         case EPSC_crstm:
01299             {
01300                 if (pRegularShape == NULL)
01301                     goto EPSError;
01302 
01303                 // Get the shape's matrix from the stack and put it into the shape.
01304                 Matrix TheMatrix;
01305                 if (!Stack.Pop(&TheMatrix, FALSE))
01306                     goto EPSError;
01307 
01308                 pRegularShape->SetTransformMatrix(&TheMatrix);
01309                 break;
01310             }
01311 
01312         case EPSC_crsp1:
01313             {
01314                 if ((pRegularShape == NULL) || (pInkPath == NULL))
01315                     goto EPSError;
01316 
01317                 // Path is held in pInkPath - copy into shape and clear the pInkPath ready for
01318                 // the second edge's path.
01319                 pRegularShape->EdgePath1.ClearPath();
01320                 if (!pRegularShape->EdgePath1.CopyPathDataFrom(pInkPath))
01321                     goto NoMemory;
01322 
01323                 pInkPath->ClearPath();
01324                 break;
01325             }
01326 
01327         case EPSC_crsp2:
01328             {
01329                 if ((pRegularShape == NULL) || (pInkPath == NULL))
01330                     goto EPSError;
01331 
01332                 // Path is held in pInkPath - copy into shape's second edge path and delete the
01333                 // temporary path.
01334                 pRegularShape->EdgePath2.ClearPath();
01335                 if (!pRegularShape->EdgePath2.CopyPathDataFrom(pInkPath))
01336                     goto NoMemory;
01337 
01338                 // No more paths to read in.
01339                 delete pInkPath;
01340                 pInkPath = NULL;
01341                 break;
01342             }
01343 
01344         // Mould commands
01345         case EPSC_csev:
01346             return ProcessEnvelope();
01347 
01348         case EPSC_cspr:
01349             return ProcessPerspective();
01350 
01351         // Guide layers & guidelines
01352         case EPSC_glyr:
01353             return ProcessGuideLayer();
01354 
01355         case EPSC_glne:
01356             return ProcessGuideline();
01357 
01358         case EPSC_cag:
01359             // A separated grey fill - should not ever get this in ART files.
01360             goto EPSError;
01361         
01362         default:
01363             // Token not understood - pass on to base class
01364             return ArtWorksEPSFilter::ProcessToken();
01365     }
01366 
01367 
01368     // No errors encountered while parsing this token and its operands.
01369     return TRUE;
01370     
01371     
01372     // Error handlers:
01373     
01374 EPSError:
01375     HandleEPSError();
01376     return FALSE;
01377 
01378 NoMemory:
01379     HandleNoMemory();
01380     return FALSE;
01381 }
01382 
01383 
01384 
01385 
01386 /********************************************************************************************
01387 
01388 >   TCHAR *CamelotEPSFilter::GetEPSCommand(EPSCommand Cmd)
01389 
01390     Author:     Tim_Browse (Xara Group Ltd) <camelotdev@xara.com>
01391     Created:    28/02/94
01392     Inputs:     Cmd - the EPS token, e.g. EPSC_aoa
01393     Returns:    Pointer to the string representation of the token, e.g. "aoa"
01394     Purpose:    Given an EPS token, return the string representation of it; mainly for
01395                 debugging purposes.
01396 
01397 ********************************************************************************************/
01398 
01399 TCHAR *CamelotEPSFilter::GetEPSCommand(EPSCommand Cmd)
01400 {
01401     INT32 i = 0;
01402     while (CamelotCommands[i].Cmd != EPSC_Invalid)
01403     {
01404         if (CamelotCommands[i].Cmd == Cmd)
01405             return CamelotCommands[i].CmdStr;
01406 
01407         // Try next command
01408         i++;
01409     }
01410 
01411     // Couldn't find it - default to base class method
01412     return ArtWorksEPSFilter::GetEPSCommand(Cmd);
01413 }
01414 
01415 
01416 
01417 
01418 
01419 /********************************************************************************************
01420 
01421 >   BOOL CamelotEPSFilter::ReadBitmap()
01422 
01423     Author:     Tim_Browse (Xara Group Ltd) <camelotdev@xara.com>   (Will)
01424     Created:    25/1/95
01425     Returns:    TRUE if it worked, FALSE if it failed
01426     Purpose:    Reads the bitmap info from the eps stack and loads in the bitmap data
01427                 It loads a type 0 bitmap
01428     SeeAlso:    CamelotNativeEPSFilter::ReadBitmap
01429 
01430 ********************************************************************************************/
01431 
01432 BOOL CamelotEPSFilter::ReadBitmap()
01433 {
01434     // Find out what kind of bitmap it is - always type 0 at present in CamelotEPS (CamelotNative has a type 1 as well).
01435     INT32 BitmapType;
01436     if (!Stack.Pop(&BitmapType) || (BitmapType != 0))
01437     {
01438         // Error - not enough operands, or bitmap is wrong type
01439         ENSURE(BitmapType == 0, "Unknown bitmap type in EPS!");
01440         HandleEPSError();
01441         return FALSE;
01442     }
01443     
01444     // Read in info on the bitmap
01445     // Recommended size is stored as a user space value, i.e. in points, so we
01446     // use PopCoord() to get the automatic scaling to millipoints.
01447     BitmapInfo Info;
01448     String_256 BitmapName;
01449     if (!Stack.PopCoord(&Info.RecommendedHeight) || !Stack.PopCoord(&Info.RecommendedWidth) ||
01450         !Stack.Pop(&Info.NumPaletteEntries)      || !Stack.Pop(&Info.PixelDepth)            ||
01451         !Stack.Pop(&Info.PixelHeight)            || !Stack.Pop(&Info.PixelWidth)            ||
01452         !Stack.Pop(&BitmapName))
01453     {
01454         // Error - not enough operands
01455         HandleEPSError();
01456         return FALSE;
01457     }
01458 
01459     BOOL IsNew;
01460     BOOL ImportOk = KernelBitmap::ImportBitmap(this, &Info, BitmapType,
01461                                                 &pBitmap, &IsNew);
01462     if (ImportOk && IsNew)
01463     {
01464         // Set the name in it
01465         pBitmap->ActualBitmap->SetName(BitmapName);
01466 
01467         // Set the TransparencyIndex
01468         if (BitmapTransparencyIndex >= 0)
01469         {
01470             TRACEUSER( "Will", _T("Imported bitmap with Transparent Index %d\n"), BitmapTransparencyIndex);
01471         }
01472 
01473         pBitmap->SetTransparencyIndex(BitmapTransparencyIndex);
01474     }
01475 
01476     return ImportOk;
01477 }
01478 
01479 
01480 
01481 /********************************************************************************************
01482 
01483 >   virtual void CamelotEPSFilter::BitmapPoolAttach(KernelBitmap* pBitmap)
01484 
01485     Author:     Rik_Heywood (Xara Group Ltd) <camelotdev@xara.com>
01486     Created:    20/1/95
01487     Inputs:     pBitmap - The bitmap to attach
01488     Purpose:    Makes a reference to the bitmap so that it can be used throughout the eps
01489                 file with repeating the data. This base class version actually does nothing
01490                 but delete the bitmap. CamelotNativeEPS is where the real processing is done.
01491 
01492 ********************************************************************************************/
01493 
01494 void CamelotEPSFilter::BitmapPoolAttach(KernelBitmap* pBitmap)
01495 {
01496     // Base class version of the function just deletes the bitmap as it does not really know
01497     // what it needs to do with it
01498     if (pBitmap!=NULL)
01499         delete pBitmap;
01500 }
01501 
01502 
01503 /********************************************************************************************
01504 
01505 >   BOOL CamelotEPSFilter::ExportBitmap(KernelBitmap& TheBitmap)
01506 
01507     Author:     Tim_Browse (Xara Group Ltd) <camelotdev@xara.com>
01508     Created:    01/09/94
01509     Inputs:     TheBitmap - the bitmap to export
01510     Returns:    TRUE if the bitmap was exported ok (or if it wasn't exported because the
01511                      file format does not support it;
01512                 FALSE if an error occured.
01513     Purpose:    Export a bitmap to a Camelot EPS file.  This outputs the standard tokens
01514                 and data required to embed a bitmap in a Camelot EPS file.
01515     Errors:     Usual disk/file errors.
01516     SeeAlso:    Filter::ExportBitmap
01517 
01518 ********************************************************************************************/
01519 
01520 BOOL CamelotEPSFilter::ExportBitmap(KernelBitmap& TheBitmap)
01521 {
01522 #if !defined(EXCLUDE_FROM_RALPH)
01523     BitmapInfo Info;
01524     String_256 BitmapName;
01525 
01526     // Check to seed if the bitmap has a transparent Index, 
01527     // and output a special token if so
01528     if (TheBitmap.ActualBitmap->GetBPP() <= 8)
01529     {
01530         INT32 TransparencyIndex;
01531 
01532         if (TheBitmap.ActualBitmap->GetTransparencyIndex(&TransparencyIndex))
01533         {
01534             TRACEUSER( "Will", _T("Exporting bitmap with Transparent Index %d\n"), TransparencyIndex);
01535 
01536             ExportDCPtr->OutputValue((INT32) EOTAG_BITMAPFLAGS);
01537             ExportDCPtr->OutputToken(_T("cso"));    // Start of new object type
01538 
01539             ExportDCPtr->OutputValue((INT32) TransparencyIndex);
01540             ExportDCPtr->OutputToken(_T("cbti"));   // Index of the colour that is transparent
01541 
01542             ExportDCPtr->OutputToken(_T("ceo"));    // End of new object type
01543             ExportDCPtr->OutputNewLine();
01544         }
01545     }
01546 
01547     // Get details of the bitmap
01548     TheBitmap.ActualBitmap->GetInfo(&Info);
01549     BitmapName = TheBitmap.ActualBitmap->GetName();
01550 
01551     // Write them out to the EPS file.
01552     ExportDCPtr->OutputString((TCHAR*)BitmapName);
01553     ExportDCPtr->OutputValue((UINT32) Info.PixelWidth);
01554     ExportDCPtr->OutputValue((UINT32) Info.PixelHeight);
01555     ExportDCPtr->OutputValue((UINT32) Info.PixelDepth);
01556     ExportDCPtr->OutputValue((UINT32) Info.NumPaletteEntries);
01557     ExportDCPtr->OutputUserSpaceValue(Info.RecommendedWidth);
01558     ExportDCPtr->OutputUserSpaceValue(Info.RecommendedHeight);
01559 
01560     // Bitmap type is 0 as this is the type that exports all the bitmap data
01561     // Type 1 is used as a reference to a bitmap in the pool
01562     ExportDCPtr->OutputToken(_T("0"));
01563 
01564     // Write out the bitmap start token
01565     ExportDCPtr->OutputToken(_T("csbm"));
01566     ExportDCPtr->OutputNewLine();
01567 
01568     // Write out the bitmap data
01569     TheBitmap.ActualBitmap->ExportBitmap(ExportRegion);
01570 
01571     // Write out the bitmap end token
01572     ExportDCPtr->OutputToken(_T("cebm"));
01573     ExportDCPtr->OutputNewLine();
01574 #endif
01575     // All ok
01576     return TRUE;
01577 }
01578 
01579 
01580 
01581 /********************************************************************************************
01582 
01583 >   BOOL CamelotEPSFilter::ProcessFontFlags()
01584 
01585     Author:     Mike_Kenny (Xara Group Ltd) <camelotdev@xara.com>
01586     Created:    12/06/95
01587     Returns:    TRUE if the font flags were processed correctly, FALSE if not.
01588     Purpose:    Reads in all the elements of a font flags structure in the EPS file.
01589     Errors:     Syntax error in EPS file, Out of memory
01590 
01591 ********************************************************************************************/
01592 
01593 BOOL CamelotEPSFilter::ProcessFontFlags()
01594 {
01595     FontFlags.Bold = FALSE;
01596     FontFlags.Italic = FALSE;
01597     // Keep processing tokens until we find the end of the font flags tag object
01598     while (!EPSFile->eof())
01599     {
01600         GetToken();
01601 
01602         // Look for the only font tokens we're interrested in
01603         switch (Token)
01604         {
01605             case EPSC_ceo:
01606                 // found the end of the font flags block so return done.
01607                 return TRUE;
01608                 break;
01609 
01610             case EPSC_cfft:
01611                 // This flag indicates that the next bold/italic attributes are to
01612                 // be applied to the current font style rather than as physical attributes
01613                 FontFlags.Bold = TRUE;
01614                 FontFlags.Italic = TRUE;
01615                 break;
01616 
01617             default:
01618                 // try to handle whatever token this is.
01619                 if (!HandleToken()) 
01620                     return FALSE;
01621                 break;
01622         }
01623     }
01624 
01625     HandleEPSError();
01626     return FALSE;
01627 }
01628 
01629 
01630 
01631 /********************************************************************************************
01632 
01633 >   BOOL CamelotEPSFilter::ProcessFontType()
01634 
01635     Author:     Mike_Kenny (Xara Group Ltd) <camelotdev@xara.com>
01636     Created:    12/06/95
01637     Returns:    TRUE if the font type was processed correctly, FALSE if not.
01638     Purpose:    Reads in all the elements of a font type structure in the EPS file.
01639     Errors:     Syntax error in EPS file, Out of memory
01640 
01641 ********************************************************************************************/
01642 
01643 BOOL CamelotEPSFilter::ProcessFontType()
01644 {
01645     EPSFilter::ClassOfFont = FC_TRUETYPE;
01646     // Keep processing tokens until we find the end of the font flags tag object
01647     while (!EPSFile->eof())
01648     {
01649         GetToken();
01650 
01651         // Look for the only font tokens we're interrested in
01652         switch (Token)
01653         {
01654             case EPSC_ceo:
01655                 // found the end of the font flags block so return done.
01656                 return TRUE;
01657                 break;
01658 
01659             case EPSC_cftf:
01660             {
01661                 // This type indicates that the next font name is of a particular font type
01662                 INT32 Type;
01663                 FontClass actclass;
01664 
01665                 if (!Stack.Pop(&Type))
01666                 {
01667                     HandleEPSError();
01668                     return FALSE;
01669                 }
01670 
01671                 if (FONTMANAGER->LegalFontClass(Type, actclass))
01672                     EPSFilter::ClassOfFont = actclass;
01673                 // if we don't know what the class is assume Truetype.
01674             }
01675             break;
01676 
01677             default:
01678                 // try to handle whatever token this is.
01679                 if (!HandleToken()) 
01680                     return FALSE;
01681                 break;
01682         }
01683     }
01684 
01685     HandleEPSError();
01686     return FALSE;
01687 }
01688 
01689 
01690 
01691 /********************************************************************************************
01692 
01693 >   BOOL CamelotEPSFilter::FindEndOfTag()
01694 
01695     Author:     Mike_Kenny (Xara Group Ltd) <camelotdev@xara.com>
01696     Created:    17/06/95
01697     Returns:    TRUE if the end of tag token was found, FALSE if not.
01698     Purpose:    Continues reading tokens until the ceo token is found
01699     Errors:     Syntax error in EPS file, Out of memory
01700 
01701 ********************************************************************************************/
01702 
01703 BOOL CamelotEPSFilter::FindEndOfTag()
01704 {
01705     // Keep processing tokens until we find the 'end of tag' token
01706     while (!EPSFile->eof())
01707     {
01708         GetToken();
01709         switch (Token)
01710         {
01711             case EPSC_ceo:
01712                 return TRUE;
01713                 break;
01714 
01715             default:
01716                 if (!HandleToken())
01717                     return FALSE;
01718                 break;
01719         }
01720     }
01721     HandleEPSError();
01722     return FALSE;
01723 }
01724 
01725 
01726 
01727 
01728 
01729 
01730 
01731 
01732 
01733 /********************************************************************************************
01734 
01735 >   BOOL CamelotEPSFilter::ProcessBitmapFlags()
01736 
01737     Author:     Will_Cowling (Xara Group Ltd) <camelotdev@xara.com>
01738     Created:    23/06/95
01739     Returns:    TRUE if the bitmap flags were processed correctly, FALSE if not.
01740     Purpose:    Reads in all the elements of a bitmap flags structure in the EPS file.
01741     Errors:     Syntax error in EPS file, Out of memory
01742 
01743 ********************************************************************************************/
01744 
01745 BOOL CamelotEPSFilter::ProcessBitmapFlags()
01746 {
01747     // Keep processing tokens until we find the end of the bitmap flags tag object
01748     while (!EPSFile->eof())
01749     {
01750         GetToken();
01751 
01752         // Look for the only bitmap tokens we're interested in
01753         switch (Token)
01754         {
01755             case EPSC_ceo:
01756                 // found the end of the bitmap flags block so return done.
01757                 return TRUE;
01758                 break;
01759 
01760             case EPSC_cbot:
01761                 // This token indicates that the next bitmap fill is really 
01762                 // a bitmap object
01763                 m_PendingBitmap = PENDING_BITMAP_OBJECT_FILL;
01764                 break;
01765 
01766             case EPSC_cbti:
01767                 // The next bitmap must have a transparency Index
01768                 INT32 Index;
01769 
01770                 if (!Stack.Pop(&Index))
01771                 {
01772                     HandleEPSError();
01773                     return FALSE;
01774                 }
01775 
01776                 BitmapTransparencyIndex = Index;
01777                 break;
01778 
01779             default:
01780                 // try to handle whatever token this is.
01781                 if (!HandleToken()) 
01782                     return FALSE;
01783                 break;
01784         }
01785     }
01786 
01787     HandleEPSError();
01788     return FALSE;
01789 }
01790 
01791 /********************************************************************************************
01792 
01793 >   BOOL CamelotEPSFilter::ProcessBlend()
01794 
01795     Author:     Mark_Neves (Xara Group Ltd) <camelotdev@xara.com>
01796     Created:    29/11/94
01797     Returns:    TRUE if the blend was processed correctly, FALSE if not.
01798     Purpose:    Reads in all the elements of a blend structure in the EPS file.
01799     Errors:     Syntax error in EPS file, Out of memory
01800 
01801 ********************************************************************************************/
01802 
01803 BOOL CamelotEPSFilter::ProcessBlend()
01804 {
01805     if (!StartBlend())
01806         return FALSE;
01807 
01808     // Keep processing tokens until we find the end of the blend
01809     do
01810     {
01811         GetToken();
01812 
01813         // Look for the end of the blend token...
01814         if (Token == EPSC_cebd)
01815         {
01816             return EndBlend();
01817         }
01818     }
01819     // Otherwise keep going until an error or eof is encountered
01820     while (HandleToken() && (!EPSFile->eof()));
01821 
01822     if (EPSFile->eof())
01823     {
01824         // Didn't find end of blend - syntax error; deal with it
01825         HandleEPSError();
01826     }
01827 
01828     // If we're here, something went wrong
01829     return FALSE;
01830 }
01831 
01832 /********************************************************************************************
01833 
01834 >   BOOL CamelotEPSFilter::StartBlend()
01835 
01836     Author:     Mark_Neves (Xara Group Ltd) <camelotdev@xara.com>
01837     Created:    29/11/94
01838     Returns:    TRUE if the new blend was created ok;
01839                 FALSE if not.
01840     Purpose:    Used when a blend structure needs to be created - after this is called,
01841                 all new nodes added with AddNewNode() will be added as children of this
01842                 new blend, until EndBlend is called.
01843     Errors:     Out of memory
01844     SeeAlso:    CamelotEPSFilter::EndBlend
01845 
01846 ********************************************************************************************/
01847 
01848 BOOL CamelotEPSFilter::StartBlend()
01849 {
01850     INT32 NumBlendSteps,Version,OneToOne,NotAntialiased,ColBlendType;
01851     INT32 Reserved3,Reserved2,Reserved1;
01852     
01853     // The last param before the token is the version.
01854     if (!Stack.Pop(&Version))
01855         return FALSE;
01856 
01857     // Error if the version number is not correct
01858     ERROR1IF(Version != 1,FALSE,_R(IDT_EPS_BADSYNTAX));
01859 
01860     // Read in the blend's params
01861     if (!Stack.Pop(&NumBlendSteps)  ||
01862         !Stack.Pop(&NotAntialiased) ||
01863         !Stack.Pop(&OneToOne)       ||
01864         !Stack.Pop(&ColBlendType)   ||
01865         !Stack.Pop(&Reserved3)      ||
01866         !Stack.Pop(&Reserved2)      ||
01867         !Stack.Pop(&Reserved1))
01868     {
01869         return FALSE;
01870     }
01871 
01872     // Make a new blend node for this blend
01873     NodeBlend *pNodeBlend = new NodeBlend;
01874     ERROR1IF(pNodeBlend == NULL, FALSE, _R(IDT_EPS_NOMEMORY));
01875 
01876     // Add it into the tree
01877     if (!AddNewNode(pNodeBlend))
01878         return FALSE;
01879 
01880     // Set the blend's params
01881     pNodeBlend->SetOneToOne(OneToOne);
01882     pNodeBlend->SetNotAntialiased(NotAntialiased);
01883     pNodeBlend->SetNumBlendSteps(NumBlendSteps);
01884     pNodeBlend->SetColourBlendType((ColourBlendType)ColBlendType);
01885 
01886     // Make sure new objects are added as children of the node
01887     pNode = pNodeBlend;
01888 
01889     // All ok
01890     return TRUE;
01891 }
01892 
01893 /********************************************************************************************
01894 
01895 >   BOOL CamelotEPSFilter::EndBlend()
01896 
01897     Author:     Mark_Neves (Xara Group Ltd) <camelotdev@xara.com>
01898     Created:    29/11/94
01899     Returns:    TRUE if the blend was ended ok;
01900                 FALSE if not.
01901     Purpose:    Used when a blend has finished being constructed and we want to return to
01902                 normal node positioning.  The blend is added to the document when this
01903                 function is called (although that depending on the current filter mode, 
01904                 i.e. whether this is a new or existing layer, it may not be added
01905                 directly to the documnent tree straight away - it may be deferred until
01906                 the next layer is found, or the import has ended).
01907     Errors:     Out of memory
01908     SeeAlso:    CamelotEPSFilter::StartBlend
01909 
01910 ********************************************************************************************/
01911 
01912 BOOL CamelotEPSFilter::EndBlend()
01913 {
01914     // Sanity check
01915     ENSURE(pNode->IsKindOf(CC_RUNTIME_CLASS(NodeBlend)), "No blend in CamelotEPSFilter::EndBlend");
01916 
01917     // Keep the blend ptr and find the first child of the node before we reset pNode.
01918     Node* pNodeBlend = pNode;
01919     Node* pChildNode = pNode->FindFirstChild();
01920 
01921     // Get the parent of the blend node, and use that to add new objects to
01922     pNode = pNodeBlend->FindParent();
01923     ENSURE(pNode != NULL, "Blend has no parent in ProcessBlend()");
01924 
01925     // Initialise all the child blender nodes
01926     UINT32 BlenderCount=0;
01927     while (pChildNode != NULL)
01928     {
01929         if (IS_A(pChildNode,NodeBlender))
01930         {
01931             NodeBlender* pNodeBlender = (NodeBlender*)pChildNode;
01932 
01933             // Get the imported blender params
01934             INT32 PathIndexStart = pNodeBlender->GetPathIndexStart();
01935             INT32 PathIndexEnd   = pNodeBlender->GetPathIndexEnd();
01936             INT32 ObjIndexStart     = pNodeBlender->GetObjIndexStart();
01937             INT32 ObjIndexEnd       = pNodeBlender->GetObjIndexEnd();
01938 
01939             // Find the nodes this blender is to blend together
01940             NodeRenderableInk* pNodeStart = pNodeBlender->FindObjIndexedNode(ObjIndexStart);
01941             NodeRenderableInk* pNodeEnd   = pNodeBlender->FindObjIndexedNode(ObjIndexEnd);
01942 
01943             // They should not be NULL
01944             if (pNodeStart == NULL || pNodeEnd == NULL)
01945                 return FALSE;
01946 
01947             // Init the blender with the start and end nodes.
01948             if (!pNodeBlender->Initialise(pNodeStart,pNodeEnd,PathIndexStart,PathIndexEnd,ImportInfo.pOp,NULL,TRUE))
01949                 return FALSE;
01950 
01951             BlenderCount++;
01952         }
01953         pChildNode = pChildNode->FindNext();
01954     }
01955 
01956     if (BlenderCount == 0)
01957     {
01958         ERROR3("Imported Blend node doesn't contain any blender nodes");
01959         if (!ImportInfo.pOp->DoHideNode(pNodeBlend, TRUE))
01960             return FALSE;
01961     }
01962 
01963     return TRUE;
01964 }
01965 
01966 
01967 /********************************************************************************************
01968 
01969 >   BOOL CamelotEPSFilter::ProcessBlender()
01970 
01971     Author:     Mark_Neves (Xara Group Ltd) <camelotdev@xara.com>
01972     Created:    29/11/94
01973     Returns:    TRUE if the blender was processed correctly, FALSE if not.
01974     Purpose:    Reads in all the elements of a blender structure in the EPS file.
01975     Errors:     Syntax error in EPS file, Out of memory
01976 
01977 ********************************************************************************************/
01978 
01979 BOOL CamelotEPSFilter::ProcessBlender()
01980 {
01981     if (!StartBlender())
01982         return FALSE;
01983 
01984     // Keep processing tokens until we find the end of the blender
01985     do
01986     {
01987         GetToken();
01988 
01989         // Look for the end of the blender token...
01990         if (Token == EPSC_cebr)
01991         {
01992             return EndBlender();
01993         }
01994     }
01995     // Otherwise keep going until an error or eof is encountered
01996     while (HandleToken() && (!EPSFile->eof()));
01997 
01998     if (EPSFile->eof())
01999     {
02000         // Didn't find end of blender - syntax error; deal with it
02001         HandleEPSError();
02002     }
02003 
02004     // If we're here, something went wrong
02005     return FALSE;
02006 }
02007 
02008 /********************************************************************************************
02009 
02010 >   BOOL CamelotEPSFilter::StartBlender()
02011 
02012     Author:     Mark_Neves (Xara Group Ltd) <camelotdev@xara.com>
02013     Created:    29/11/94
02014     Returns:    TRUE if the new blender was created ok;
02015                 FALSE if not.
02016     Purpose:    Used when a blender structure needs to be created - after this is called,
02017                 all paths will be discarded, until EndBlender is called.
02018     Errors:     Out of memory
02019     SeeAlso:    CamelotEPSFilter::EndBlender
02020 
02021 ********************************************************************************************/
02022 
02023 BOOL CamelotEPSFilter::StartBlender()
02024 {
02025     INT32 ObjIndexStart,ObjIndexEnd,PathIndexStart,PathIndexEnd;
02026     INT32 Reserved1,Reserved2,Reserved3,Reserved4;
02027     
02028     // Read the blender's params
02029     if (!Stack.Pop(&PathIndexEnd)   ||
02030         !Stack.Pop(&PathIndexStart) ||
02031         !Stack.Pop(&ObjIndexEnd)    ||
02032         !Stack.Pop(&ObjIndexStart)  ||
02033         !Stack.Pop(&Reserved4)      ||
02034         !Stack.Pop(&Reserved3)      ||
02035         !Stack.Pop(&Reserved2)      ||
02036         !Stack.Pop(&Reserved1))
02037     {
02038         return FALSE;
02039     }
02040 
02041     // Make a new blender node for this blender
02042     NodeBlender *pNodeBlender = new NodeBlender;
02043     ERRORIF(pNodeBlender == NULL, _R(IDT_EPS_NOMEMORY), FALSE);
02044 
02045     // Add it into the tree
02046     if (!AddNewNode(pNodeBlender))
02047         return FALSE;
02048 
02049     // Set the blender params
02050     pNodeBlender->SetObjIndexStart(ObjIndexStart);
02051     pNodeBlender->SetObjIndexEnd  (ObjIndexEnd);
02052     pNodeBlender->SetPathIndexStart(PathIndexStart);
02053     pNodeBlender->SetPathIndexEnd  (PathIndexEnd);
02054     
02055     // Discard all subsequent paths - until we get an "end blender" token
02056     ThePathType = PATH_DISCARD_STICKY;
02057 
02058     // All ok
02059     return TRUE;
02060 }
02061 
02062 /********************************************************************************************
02063 
02064 >   BOOL CamelotEPSFilter::EndBlender()
02065 
02066     Author:     Mark_Neves (Xara Group Ltd) <camelotdev@xara.com>
02067     Created:    29/11/94
02068     Returns:    TRUE if the blender was ended ok;
02069                 FALSE if not.
02070     Purpose:    Used when a blender has finished being constructed.
02071                 Path importing is returned to normal (i.e. they're no longer discarded)
02072     Errors:     -
02073     SeeAlso:    CamelotEPSFilter::StartBlender
02074 
02075 ********************************************************************************************/
02076 
02077 BOOL CamelotEPSFilter::EndBlender()
02078 {
02079     ThePathType = PATH_NORMAL;
02080     return TRUE;
02081 }
02082 
02083 
02084 
02085 /********************************************************************************************
02086 
02087 >   BOOL CamelotEPSFilter::ProcessRegularShape()
02088 
02089     Author:     Mark_Neves (Xara Group Ltd) <camelotdev@xara.com>
02090     Created:    29/11/94
02091     Returns:    TRUE if the blend was processed correctly, FALSE if not.
02092     Purpose:    Reads in all the elements of a blend structure in the EPS file.
02093     Errors:     Syntax error in EPS file, Out of memory
02094 
02095 ********************************************************************************************/
02096 
02097 BOOL CamelotEPSFilter::ProcessRegularShape()
02098 {
02099     // Make a regular shape node, and use this as a temporary context for storing path
02100     // data before passing it into the shape object.
02101     pRegularShape = new NodeRegularShape;
02102     if (pRegularShape == NULL)
02103         return FALSE;
02104 
02105     if (!pRegularShape->SetUpShape())
02106     {
02107         delete pRegularShape;            
02108         pRegularShape = NULL;
02109         return FALSE;
02110     }
02111 
02112     // Add it into the tree
02113     if (!AddNewNode(pRegularShape))
02114     {
02115         pRegularShape = NULL;
02116         return FALSE;
02117     }
02118     
02119     // Get a new ink path to store the shape's edge paths in.
02120     ERROR3IF(pInkPath != NULL, "Already creating an InkPath object in regular shape");
02121     pInkPath = new Path;
02122     if (pInkPath == NULL)
02123         return FALSE;
02124 
02125     // Set up the shape's path - this is currently 4 elements long - and 
02126     // move to the start of the path
02127     if (!pInkPath->Initialise())
02128     {
02129         delete pInkPath;
02130         pInkPath = NULL;
02131         delete pRegularShape;
02132         pRegularShape = NULL;
02133         return FALSE;
02134     }   
02135 
02136     pInkPath->FindStartOfPath();
02137 
02138     // Keep processing tokens until we find the end of the regular shape
02139     do
02140     {
02141         GetToken();
02142 
02143         // Look for the end of the regular shape token...
02144         if (Token == EPSC_cers)
02145         {
02146             if (pInkPath != NULL)
02147             {
02148                 // Shape has not been saved correctly if we haven't read in both paths by now.
02149                 HandleEPSError();
02150                 return FALSE;
02151             }
02152 
02153             // Apply current attributes to the shape
02154             if (!AddAttributes(pRegularShape, RegularShapeIsStroked, RegularShapeIsFilled))
02155             {
02156                 HandleNoMemory();
02157                 return FALSE;
02158             }
02159 
02160             // Force shape's path to be reconstructed
02161             pRegularShape->InvalidateCache();
02162             pRegularShape->InvalidateBoundingRect();
02163 
02164             pRegularShape = NULL;
02165             return TRUE;
02166         }
02167     }
02168     // Otherwise keep going until an error or eof is encountered
02169     while (HandleToken() && (!EPSFile->eof()));
02170 
02171     if (EPSFile->eof())
02172     {
02173         // Didn't find end of regular - syntax error; deal with it
02174         HandleEPSError();
02175     }
02176 
02177     // If we're here, something went wrong
02178     return FALSE;
02179 }
02180 
02181 
02182 /********************************************************************************************
02183 
02184 >   BOOL CamelotEPSFilter::ProcessRegularShapeParams()
02185 
02186     Author:     Peter_Arnold (Xara Group Ltd) <camelotdev@xara.com>
02187     Created:    2/2/95
02188     Returns:    TRUE if the regular shape was processed correctly, FALSE if not.
02189     Purpose:    Reads in all the parameters of a regular shape structure in the EPS file.
02190     Errors:     Syntax error in EPS file, Out of memory
02191 
02192 ********************************************************************************************/
02193 
02194 BOOL CamelotEPSFilter::ProcessRegularShapeParams()
02195 {
02196     // Check we are actually making a regular shape at the moment.
02197     if (pRegularShape == NULL)
02198     {
02199         HandleEPSError();
02200         return FALSE;
02201     }
02202 
02203     UINT32 NumSides;
02204     INT32   Circular;
02205     INT32   Stellated;
02206     INT32   PrimaryCurvature;
02207     INT32   StellationCurvature;
02208     double  StellRadiusToPrimary;
02209     double  PrimaryCurveToPrimary;
02210     double  StellCurveToStell;
02211     double  StellOffsetRatio;
02212     DocCoord    UTCentrePoint;
02213     DocCoord    UTMajorAxes;
02214     DocCoord    UTMinorAxes;
02215 
02216 //Peter was here
02217     INT32 StrokedFlag;
02218     INT32 FilledFlag;
02219 
02220     // Try to get all the parameters off the EPS stack.
02221     if (!Stack.Pop(&StrokedFlag) ||
02222         !Stack.Pop(&FilledFlag) ||
02223         !Stack.PopCoordPair(&UTMinorAxes) ||
02224         !Stack.PopCoordPair(&UTMajorAxes) ||
02225         !Stack.PopCoordPair(&UTCentrePoint) ||
02226         !Stack.Pop(&StellOffsetRatio) ||
02227         !Stack.Pop(&StellCurveToStell) ||
02228         !Stack.Pop(&PrimaryCurveToPrimary) ||
02229         !Stack.Pop(&StellRadiusToPrimary) ||
02230         !Stack.Pop(&StellationCurvature) ||
02231         !Stack.Pop(&PrimaryCurvature) ||
02232         !Stack.Pop(&Stellated) ||
02233         !Stack.Pop(&Circular) ||
02234         !Stack.Pop(&NumSides))
02235     {
02236         HandleEPSError();
02237         return FALSE;
02238     }
02239     else
02240     {
02241         RegularShapeIsStroked = StrokedFlag;
02242         RegularShapeIsFilled = FilledFlag;
02243     }
02244 
02245     // Now set the flag specifying whether the path is filled
02246     SetPathFilled(RegularShapeIsFilled);
02247 
02248     // Set up the shape
02249     pRegularShape->SetNumSides(NumSides);
02250     pRegularShape->SetCircular(Circular);
02251     pRegularShape->SetStellated(Stellated);
02252     pRegularShape->SetPrimaryCurvature(PrimaryCurvature);
02253     pRegularShape->SetStellationCurvature(StellationCurvature);
02254     pRegularShape->SetStellRadiusToPrimary(StellRadiusToPrimary);
02255     pRegularShape->SetPrimaryCurveToPrimary(PrimaryCurveToPrimary);
02256     pRegularShape->SetStellCurveToStell(StellCurveToStell);
02257     pRegularShape->SetStellationRatio(StellOffsetRatio);
02258     pRegularShape->SetCentrePoint(UTCentrePoint);
02259     pRegularShape->SetMajorAxes(UTMajorAxes);
02260     pRegularShape->SetMinorAxes(UTMinorAxes);
02261 
02262     // All ok
02263     return TRUE;
02264 }
02265 
02266 
02267 /********************************************************************************************
02268 
02269 >   BOOL CamelotEPSFilter::ProcessEnvelope()
02270 
02271     Author:     Mike_Kenny (Xara Group Ltd) <camelotdev@xara.com>
02272     Created:    14/03/95
02273     Returns:    TRUE if the envelope object was processed correctly, 
02274                 FALSE if not.
02275     Purpose:    Reads in all the elements of an envelope structure in the EPS file.
02276     Errors:     Syntax error in EPS file, Out of memory
02277 
02278 ********************************************************************************************/
02279 
02280 BOOL CamelotEPSFilter::ProcessEnvelope()
02281 {
02282     // NOT IMPLEMENTED
02283     if (!StartMould(MOULDSPACE_ENVELOPE))
02284         return FALSE;
02285     
02286     if (ProcessMould())
02287         // If we've completed then all is well
02288         return EndMould();
02289 
02290     if (EPSFile->eof())
02291     {
02292         // Didn't find end of envelope - syntax error; deal with it
02293         HandleEPSError();
02294     }
02295 
02296     // if made it here all is not well
02297     return FALSE;
02298 }
02299 
02300 /********************************************************************************************
02301 
02302 >   BOOL CamelotEPSFilter::ProcessPerspective()
02303 
02304     Author:     Mike_Kenny (Xara Group Ltd) <camelotdev@xara.com>
02305     Created:    14/03/95
02306     Returns:    TRUE if the perspective object was processed correctly, 
02307                 FALSE if not.
02308     Purpose:    Reads in all the elements of an perspective structure in the EPS file.
02309     Errors:     Syntax error in EPS file, Out of memory
02310     SeeAlso:    ProcessEnvelope() for furher details
02311 
02312 ********************************************************************************************/
02313 
02314 BOOL CamelotEPSFilter::ProcessPerspective()
02315 {
02316     // NOT IMPLEMENTED
02317     if (!StartMould(MOULDSPACE_PERSPECTIVE))
02318         return FALSE;
02319 
02320     // If we've completed then all is well
02321     if (ProcessMould())
02322         return EndMould();
02323 
02324     if (EPSFile->eof())
02325     {
02326         // Didn't find end of perspective - syntax error; deal with it
02327         HandleEPSError();
02328     }
02329 
02330     // if made it here all is not well
02331     return FALSE;
02332 }
02333 
02334 
02335 
02336 
02337 /********************************************************************************************
02338 
02339 >   BOOL CamelotEPSFilter::StartMould(MouldSpace mSpace)
02340 
02341     Author:     Mike_Kenny (Xara Group Ltd) <camelotdev@xara.com> 
02342     Created:    14/03/95
02343     Inputs:     mSpace = Mould geometry to define, either envelope or perspective
02344     Returns:    TRUE if the new mould object was created ok;
02345                 FALSE if not.
02346     Purpose:    Used when an mould structure needs to be created - after this is called,
02347                 all new nodes added with AddNewNode() will be added as children of this
02348                 new mould, until EndMould is called.
02349     Errors:     Out of memory
02350     SeeAlso:    CamelotEPSFilter::EndMould
02351 
02352 ********************************************************************************************/
02353 
02354 BOOL CamelotEPSFilter::StartMould(MouldSpace mSpace)
02355 {
02356     // Read in the version number of this camelot mould
02357     INT32 version;
02358     if (!Stack.Pop(&version))
02359         return FALSE;
02360 
02361     // if the version number's not one we recognise then return an error
02362     if (version!=100)
02363         return FALSE;   
02364 
02365     // create a mould parent and insert it in the tree
02366     NodeMould* pMouldParent = new NodeMould;
02367     ERROR1IF(pMouldParent == NULL, FALSE, _R(IDT_EPS_NOMEMORY));
02368 
02369     // give the parent mould object a shape and mould space to work with and stick it in the tree
02370     if (!pMouldParent->CreateGeometry(mSpace))
02371     {
02372         delete pMouldParent;
02373         return FALSE;
02374     }
02375 
02376     if (!AddNewNode(pMouldParent))
02377     {
02378         HandleNoMemory();
02379         return FALSE;
02380     }
02381 
02382     // Make sure new objects are added as children of the mould
02383     pNode = pMouldParent;
02384 
02385     // Make sure the default mould threshold is set correctly
02386     EPSFilter::NewMouldThreshold = MOULD_V1THRESHOLD;
02387 
02388     // All ok
02389     return TRUE;
02390 }
02391 
02392 
02393 
02394 /********************************************************************************************
02395 
02396 >   BOOL CamelotEPSFilter::EndMould()
02397 
02398     Author:     Mike_Kenny (Xara Group Ltd) <camelotdev@xara.com>
02399     Created:    06/03/95
02400     Returns:    TRUE if the envelope was ended ok;
02401                 FALSE if not.
02402     Purpose:    Used when a mould has finished being constructed and we want to return to
02403                 normal node positioning.
02404     SeeAlso:    CamelotEPSFilter::StartMould()
02405 
02406 ********************************************************************************************/
02407 
02408 BOOL CamelotEPSFilter::EndMould()
02409 {
02410     // Sanity check
02411     ERROR3IF(!pNode->IsKindOf(CC_RUNTIME_CLASS(NodeMould)), "No MouldParent in CamelotEPSFilter::EndMould");
02412 
02413     // Get a pointer to the mould object
02414     NodeMould* pNodeMould = (NodeMould*)pNode;
02415     // move pNode up one level
02416     pNode = pNode->FindParent();
02417     ERROR3IF(pNode==NULL, "No parent of mould in CamelotEPSFilter::EndMould");
02418 
02419     // for loaded docs we need to set the default threshold to the
02420     // old 1024 threshold until such time as it changes by way of parsing
02421     // the threshold token. This avoids blends from remapping!
02422     pNodeMould->GetGeometry()->SetThreshold(EPSFilter::NewMouldThreshold);
02423 
02424     // We should really have these items, otherwise well, we have nothing whatsoever
02425     NodeMouldPath* pNodeMouldPath = (NodeMouldPath*)pNodeMould->FindFirstChild(CC_RUNTIME_CLASS(NodeMouldPath));
02426     NodeMouldGroup* pNodeMouldGroup = (NodeMouldGroup*)pNodeMould->FindFirstChild(CC_RUNTIME_CLASS(NodeMouldGroup));
02427 
02428     if (pNodeMouldPath==NULL || pNodeMouldGroup==NULL)
02429     {
02430         // delete the object and all its children
02431         pNodeMould->CascadeDelete();
02432     }
02433 
02434     // Finally trash any unwanted attributes which may have been added
02435     // by SnapShotCurrentAttributes.
02436     RemoveUnwantedAttributes(pNodeMould);
02437 
02438     // All is well and lovely
02439     return TRUE;
02440 }
02441 
02442 
02443 /********************************************************************************************
02444     
02445     BOOL CamelotEPSFilter::RemoveUnwantedAttributes(NodeMould* pNodeMould)
02446 
02447     Author:     Mike_Kenny (Xara Group Ltd) <camelotdev@xara.com>
02448     Created:    18/10/95
02449     Inputs:     pNodeMould = a pointer to a node mould object
02450     Returns:    TRUE if all unwanted attributes were removed ok.
02451                 FALSE if none have been removed
02452     Purpose:    Removes unwanted attributes added as first children of the node mould.
02453                 It does this by scanning the children of the nodemouldgroup and determining
02454                 what attributes are applied here.
02455 
02456 ********************************************************************************************/
02457 
02458 BOOL CamelotEPSFilter::RemoveUnwantedAttributes(NodeMould* pNodeMould)
02459 {
02460     /* Ok, at this stage, we have a list of attributes applied to the
02461        first level of the node mould. All these atts have been applied to
02462        avoid them being applied to the mould source objects which would
02463        then be moulded (not what we want). Unfortunately we now need to check
02464        all attributes applied to the source objects. This is unfortunately
02465        a side effect of the method we are using. If we find that there
02466        are attributes applied in the source object tree which have been
02467        applied to the root, then we delete those at the root. Groan!
02468     */
02469 
02470     // grab the node mould group.
02471     NodeMouldGroup* pNdMldGrp = (NodeMouldGroup*)pNodeMould->FindFirstChild(CC_RUNTIME_CLASS(NodeMouldGroup));
02472     if (pNdMldGrp==NULL)
02473         return FALSE;
02474 
02475     // ok, build a list of attribute pointers.
02476     CCAttrMap AttrMap(30);
02477     CCRuntimeClass* pTypeInfo;
02478     void* pDummy;
02479 
02480     BuildSubtreeAttrMap(&AttrMap, pNdMldGrp);
02481     
02482     Node *qNode, *pNode = pNodeMould->FindFirstChild();
02483     while (pNode)
02484     {
02485         qNode = pNode->FindNext();
02486         if (pNode->IsAnAttribute())
02487         {
02488             NodeAttribute* pAttrib = (NodeAttribute*)pNode;
02489             pTypeInfo = pAttrib->GetAttributeType();
02490             if (AttrMap.Lookup(pTypeInfo, pDummy))
02491             {
02492                 pNode->UnlinkNodeFromTree();
02493                 delete pNode;
02494             }
02495         }
02496         pNode = qNode;
02497     }
02498 
02499     return TRUE;
02500 }
02501 
02502 
02503 
02504 /********************************************************************************************
02505 
02506 >   void CamelotEPSFilter::BuildSubtreeAttrMap(CCAttrMap *pAttribMap, Node* pParent)
02507 
02508     Author:     Mike_Kenny (Xara Group Ltd) <camelotdev@xara.com>
02509     Created:    18/10/95
02510     Inputs:     pAttribMap  = a pointer to an attribute map
02511                 pParent     = a pointer to the parent node
02512     Returns:    -
02513     Purpose:    Adds all attribute pointers to the pAttribMap which it finds as children
02514                 of pParent.
02515     Notes:      Told off by MarkN for not commenting ... oopps
02516     
02517 ********************************************************************************************/
02518 
02519 void CamelotEPSFilter::BuildSubtreeAttrMap(CCAttrMap *pAttribMap, Node* pParent)
02520 {
02521     CCRuntimeClass* pTypeInfo;
02522     void* pDummy;
02523 
02524     Node* qNode = pParent->FindFirstChild();
02525     while (qNode)
02526     {
02527         if (qNode->IsAnAttribute())
02528         {
02529             NodeAttribute* pAttrib = (NodeAttribute*)qNode;
02530             pTypeInfo = pAttrib->GetAttributeType();
02531             if (!pAttribMap->Lookup(pTypeInfo, pDummy))
02532                 pAttribMap->SetAt(pTypeInfo, qNode);
02533         }
02534 
02535         if (qNode->FindFirstChild())
02536         {
02537             BuildSubtreeAttrMap(pAttribMap, qNode);
02538         }
02539 
02540         qNode = qNode->FindNext();
02541     }
02542 }
02543 
02544 
02545 
02546 
02547 /********************************************************************************************
02548 
02549 >   BOOL CamelotEPSFilter::ProcessMould()
02550 
02551     Author:     Mike_Kenny (Xara Group Ltd) <camelotdev@xara.com>
02552     Created:    06/03/95
02553     Returns:    TRUE if the mould was processed ok;
02554                 FALSE if not.
02555     Purpose:    Processes tokens inside a mould object. Tokens specific to mould objects
02556                 are processed along with some overridding of default tokens such as groups
02557     SeeAlso:
02558     
02559 ********************************************************************************************/
02560 
02561 BOOL CamelotEPSFilter::ProcessMould()
02562 {
02563     // Try to make a snap shot of the current attribute state
02564     SnapShotCurrentAttrs();
02565 
02566     // process the mould
02567     BOOL success=ProcessMouldTokens();
02568 
02569     // Get rid of the snap shot again.
02570     DeleteSnapShot();
02571 
02572     return success;
02573 }
02574 
02575 
02576 
02577 
02578 /********************************************************************************************
02579 
02580 >   BOOL CamelotEPSFilter::ProcessMouldTokens()
02581 
02582     Author:     Mike_Kenny (Xara Group Ltd) <camelotdev@xara.com>
02583     Created:    06/03/95
02584     Returns:    TRUE if the mould was processed ok;
02585                 FALSE if not.
02586     Purpose:    Processes tokens inside a mould object. Tokens specific to mould objects
02587                 are processed along with some overridding of default tokens such as groups
02588     SeeAlso:
02589     
02590 ********************************************************************************************/
02591 
02592 BOOL CamelotEPSFilter::ProcessMouldTokens()
02593 {   
02594     // Keep processing tokens until we find the end of the envelope
02595     while (!EPSFile->eof())
02596     {
02597         GetToken();
02598 
02599         // Look for the end of the mould token...
02600         switch (Token)
02601         {
02602             case EPSC_ceev:
02603             case EPSC_cepr:
02604                 // found the end of the mould so return done.
02605                 return TRUE;
02606                 break;
02607 
02608             case EPSC_csmp:
02609                 // if we've found the envelope/perspective end then
02610                 // all is well.
02611                 if (!ProcessMouldShape())
02612                     return FALSE;
02613                 break;
02614 
02615             case EPSC_csso:
02616             {
02617                 // Remove all current attributes which don't match snap shot
02618                 // saving their values on the stack. (These should not be applied)
02619                 // (See ApplyChangedAttrs call below)
02620                 PushCurrentAttrsBasedOnSnapShot();
02621 
02622                 // Process all mould source objects
02623                 BOOL Success = ProcessMouldSourceObjs();
02624             
02625                 // Put back the current attrs how they were.                
02626                 PopCurrentAttrsBasedOnSnapShot();
02627 
02628                 if (!Success)
02629                     return FALSE;
02630             }
02631             break;
02632 
02633             case EPSC_csdo:
02634                 // Create next object as a child
02635                 if (!ProcessMouldDestinObjs())
02636                     return FALSE;
02637                 break;
02638 
02639             default:
02640                 // try to handle whatever token this is.
02641                 if (!HandleToken()) 
02642                     return FALSE;
02643                 break;
02644         }
02645     }
02646 
02647     HandleEPSError();
02648     return FALSE;
02649 }
02650 
02651 
02652 
02653 /********************************************************************************************
02654 
02655 >   BOOL CamelotEPSFilter::ProcessMouldThreshold()
02656 
02657     Author:     Mike_Kenny (Xara Group Ltd) <camelotdev@xara.com>
02658     Created:    12/06/95
02659     Returns:    TRUE if the threshold was processed correctly, FALSE if not.
02660     Purpose:    Reads in all the elements of a mould threshold structure in the EPS file.
02661                 The threshold structure is defined as an extension token as it only came into
02662                 existance after version 1.1 documents. This function is called from the
02663                 extension token parser. We read the threshold value and set the EPSFilter
02664                 variable NewMouldThreshold. Having read in the mould, the threshold will be
02665                 read from this variable and overwrite the default constructed threshold.
02666 
02667     Errors:     Syntax error in EPS file, Out of memory
02668 
02669 ********************************************************************************************/
02670 
02671 BOOL CamelotEPSFilter::ProcessMouldThreshold()
02672 {
02673     EPSFilter::NewMouldThreshold = MOULD_V1THRESHOLD;
02674 
02675     while (!EPSFile->eof())
02676     {
02677         GetToken();
02678         switch (Token)
02679         {
02680             case EPSC_ceo:
02681             {
02682                 // found the end of the threshold block so return done.
02683                 return TRUE;
02684             }
02685             break;
02686 
02687             case EPSC_cmth:
02688             {
02689                 INT32 Threshold;
02690                 if (!Stack.Pop(&Threshold))
02691                 {
02692                     HandleEPSError();
02693                     return FALSE;
02694                 }
02695                 EPSFilter::NewMouldThreshold = Threshold;
02696             }
02697             break;
02698 
02699             default:
02700             {
02701                 // try to handle whatever token this is.
02702                 if (!HandleToken()) 
02703                     return FALSE;
02704             }   
02705             break;
02706         }
02707     }
02708 
02709     HandleEPSError();
02710     return FALSE;
02711 }
02712 
02713 
02714 /********************************************************************************************
02715 
02716 >   BOOL CamelotEPSFilter::ProcessMouldShape()
02717 
02718     Author:     Mike_Kenny (Xara Group Ltd) <camelotdev@xara.com>
02719     Created:    06/03/94
02720     Returns:    TRUE if the shape has been read correctly, 
02721                 FALSE if not.
02722     Purpose:    Reads in all the elements of a mould geometry held in the EPS file.
02723                 The description of the geometry for this particular file type is that of
02724                 a path. 
02725                 Input Stack = coord, coord
02726     Errors:     Syntax error in EPS file, Out of memory
02727 
02728 ********************************************************************************************/
02729 
02730 BOOL CamelotEPSFilter::ProcessMouldShape()
02731 {
02732     // We really should be inside a mould object at this stage
02733     if (!IS_A(pNode,NodeMould)) return FALSE;
02734     NodeMould* pNodeMould = (NodeMould*)pNode;
02735 
02736     Path TempPath;
02737     if (!TempPath.Initialise(24,12))
02738         return FALSE;
02739 
02740     BOOL done = FALSE;
02741     // Keep processing tokens until we find the end of the mould path
02742     while (!done && (!EPSFile->eof()))
02743     {
02744         GetToken();
02745 
02746         switch (Token)
02747         {
02748 
02749             case EPSC_m:
02750                 if (!ReadSimplePath(&TempPath))
02751                     return FALSE;
02752                 break;
02753 
02754             case EPSC_cemp:
02755                 done=TRUE;
02756                 break;
02757 
02758             default:
02759                 // try to handle whatever token this is.
02760                 if (!HandleToken())
02761                     return FALSE;
02762                 break;
02763         }
02764     }
02765 
02766     if (EPSFile->eof())
02767     {
02768         HandleEPSError();
02769         return FALSE;
02770     }
02771 
02772     // We have read the path so now lets try and build the geometry
02773     // check the shape we've been given is fine and lovely  
02774     UINT32 errID;
02775     if (!pNodeMould->GetGeometry()->Validate(&TempPath,errID))
02776     {
02777         // ok the path we read is invalid so lets try to build a valid one
02778         Path* pPath = NULL;
02779         INT32 Corners[4] = {0, 3, 6, 9}; 
02780         if (!pNodeMould->GetGeometry()->MakeValidFrom(&pPath, &TempPath, Corners))
02781             return FALSE;
02782         if (!TempPath.CloneFrom(*pPath))
02783         {
02784             delete pPath;
02785             return FALSE;
02786         }
02787         delete pPath;
02788     }
02789 
02790     // Build and if necessary fit the geometry
02791     NodeMouldPath* pNodeMPath = pNodeMould->CreateNewMouldShape(&TempPath,NULL,ImportInfo.pOp);
02792     if (pNodeMPath==NULL) 
02793         return FALSE;
02794 
02795     // having created the shape bung it in the tree.
02796     if (!AddNewNode(pNodeMPath))
02797     {
02798         delete pNodeMPath;
02799         return FALSE;
02800     }
02801 
02802     /* Ok, what we need to do now is apply the current set of attributes
02803        as first children of the pNodeMould. This will apply all none default
02804        attributes at this level. We need to do this as we dont want the current
02805        attributes as they are now to be applied to objects within the source
02806        mould object tree. If they do get applied there, they will be moulded
02807        and will look wrong.
02808        Background:
02809        There is a problem I'm fixing here. Currently you can apply attributes
02810        to moulds eg dragging a linear fill over a selected mould. This creates
02811        an attribute as a first child of the mould, outside the source object
02812        group which gets moulded. So the att is not moulded. Right, when you
02813        save, these atts are put into the file before the nodemouldpath.
02814        On loading currently all atts are gathered and applied to each object
02815        as it is created. This means atts that weren't moulded migrate onto
02816        objects which will be moulded and get moulded. yuk. So what I am doing
02817        is to save the CurrentAttrs state when we find a start mould token.
02818        Then when we find the nodemouldpath, apply all the current atts and
02819        reset the current set back to the saved set. That means all the atts
02820        that appear before the nodemouldpath dont get applied to the source objects
02821        and only those that appear later do.
02822     */
02823 
02824     if (!ApplyChangedAttrs(pNodeMould))
02825         return FALSE;
02826     
02827     // All is well.
02828     return TRUE;
02829 }
02830 
02831 
02832 
02833 
02834 
02835 
02836 
02837 /********************************************************************************************
02838 
02839 >   BOOL CamelotEPSFilter::ReadSimplePath(Path* TempPath)
02840 
02841     Author:     Mike_Kenny (Xara Group Ltd) <camelotdev@xara.com>
02842     Created:    14/03/95
02843     Returns:    TRUE if the path shape has been read
02844                 FALSE if not.
02845     Purpose:    Reads in the defined mould path shape.
02846     Errors:     Syntax error in EPS file, Out of memory
02847 
02848 ********************************************************************************************/
02849 
02850 BOOL CamelotEPSFilter::ReadSimplePath(Path* TempPath)
02851 {
02852     DocCoord Ta,Tb,Tc;
02853     PathFlags flags;
02854     flags.IsSelected = FALSE;
02855     BOOL ok, done = FALSE;
02856 
02857     // ok, pop off the move stacked coordinate
02858     if (!Stack.PopCoordPair(&Ta))
02859         return FALSE;
02860 
02861     // Make sure there's absolutely no elements in this path
02862     TempPath->ClearPath(FALSE);
02863 
02864     // Add this first element as a move to
02865     if (!TempPath->AddMoveTo(Ta, &flags))
02866         return FALSE;
02867 
02868     // Keep processing tokens until we find the end of the mould path
02869     while (!done && (!EPSFile->eof()))
02870     {
02871         GetToken();
02872 
02873         switch (Token)
02874         {
02875             case EPSC_l:
02876                 // lineto (x,y) found
02877                         ok = Stack.PopCoordPair(&Ta);
02878                 if (ok) ok = TempPath->AddLineTo(Ta, &flags);
02879                 break;
02880                                                 
02881             case EPSC_c:
02882                 // curveto (x0,y0,x1,y1,x2,y2) found
02883                         ok = Stack.PopCoordPair(&Tc);
02884                 if (ok) ok = Stack.PopCoordPair(&Tb);
02885                 if (ok) ok = Stack.PopCoordPair(&Ta);
02886                 if (ok) ok = TempPath->AddCurveTo(Ta,Tb,Tc,&flags);
02887                 break;
02888 
02889             case EPSC_s: case EPSC_f: case EPSC_b: case EPSC_h: case EPSC_n:
02890             case EPSC_S: case EPSC_F: case EPSC_B: case EPSC_H: case EPSC_N:
02891                 // found a stroked close path. That should be it for path elements
02892                 ok = TempPath->CloseSubPath();
02893                 done = TRUE;
02894                 break;
02895 
02896             default:
02897                 // try to handle whatever token this is.
02898                 ok = HandleToken();
02899                 break;
02900         }
02901 
02902         // if someones failed return.
02903         if (!ok) return FALSE;
02904     }
02905 
02906     // All is well
02907     return TRUE;
02908 }
02909 
02910 
02911 /********************************************************************************************
02912 
02913 >   BOOL CamelotEPSFilter::ProcessMouldSourceObjs()
02914 
02915     Author:     Mike_Kenny (Xara Group Ltd) <camelotdev@xara.com>
02916     Created:    14/03/95
02917     Returns:    TRUE if the source objects have been read correctly, 
02918                 FALSE if not.
02919     Purpose:    Reads in all the objects within the mould source grouping.
02920     Errors:     Syntax error in EPS file, Out of memory
02921 
02922 ********************************************************************************************/
02923 
02924 BOOL CamelotEPSFilter::ProcessMouldSourceObjs()
02925 {
02926     // Sanity check
02927     ERROR3IF(!pNode->IsKindOf(CC_RUNTIME_CLASS(NodeMould)), "No MouldParent in CamelotEPSFilter::ProcessMouldSourceObjs");
02928     NodeMould* pNodeMould = (NodeMould*)pNode;
02929 
02930     // Create a mould group object
02931     NodeMouldGroup* pMouldGroup = pNodeMould->CreateNewMouldGroup(ImportInfo.pOp);
02932     if (pMouldGroup==NULL) 
02933         return FALSE;
02934 
02935     if (!AddNewNode(pMouldGroup))
02936     {
02937         delete pMouldGroup;
02938         return FALSE;
02939     }
02940 
02941     // save the old insert position and position to our group
02942     Node* pOldPos = pNode;
02943     pNode = pMouldGroup;
02944 
02945     // scan the tokens until we find the one we want
02946     BOOL done = FALSE;
02947     while (!done && (!EPSFile->eof()))
02948     {
02949         GetToken();
02950         if (!(done=(Token==EPSC_ceso)))
02951         {
02952             if (!HandleToken())
02953             {   
02954                 pNode=pOldPos;
02955                 return FALSE;
02956             }
02957         }
02958     }
02959 
02960     // reposition our magic insert position 
02961     pNode = pOldPos;
02962 
02963     if (EPSFile->eof())
02964     {
02965         HandleEPSError();
02966         return FALSE;
02967     }
02968 
02969     return TRUE;
02970 }
02971 
02972 
02973 
02974 
02975 /********************************************************************************************
02976 
02977 >   BOOL CamelotEPSFilter::ProcessMouldDestinObjs()
02978 
02979     Author:     Mike_Kenny (Xara Group Ltd) <camelotdev@xara.com>
02980     Created:    14/03/95
02981     Returns:    TRUE if the source objects have been read correctly, 
02982                 FALSE if not.
02983     Purpose:    Reads in all the objects within the mould destination grouping.
02984     Errors:     Syntax error in EPS file, Out of memory
02985 
02986 ********************************************************************************************/
02987 
02988 BOOL CamelotEPSFilter::ProcessMouldDestinObjs()
02989 {
02990     // Sanity check
02991     ERROR3IF(!pNode->IsKindOf(CC_RUNTIME_CLASS(NodeMould)), "No MouldParent in CamelotEPSFilter::ProcessMouldDestinObjs");
02992     NodeMould* pNodeMould = (NodeMould*)pNode;
02993 
02994     // create a new moulder object
02995     NodeMoulder* pMoulder = pNodeMould->CreateNewMoulder(ImportInfo.pOp);
02996     if (!pMoulder)
02997         return FALSE;
02998 
02999     // Add it into the tree as the last child of the mould
03000     if (!AddNewNode(pMoulder))
03001         return FALSE;
03002 
03003     // save the old insert position
03004     Node* pOldPos=pNode;
03005     pNode=pMoulder;
03006 
03007     // scan the tokens until we find the one we want
03008     BOOL done=FALSE;
03009     while (!done && (!EPSFile->eof()))
03010     {
03011         GetToken();
03012         if (!(done=(Token==EPSC_cedo)))
03013         {
03014             if (!HandleToken())
03015             {   
03016                 pNode=pOldPos;
03017                 return FALSE;
03018             }
03019         }
03020     }
03021 
03022     // reposition our magic insert position 
03023     pNode=pOldPos;
03024 
03025     if (EPSFile->eof())
03026     {
03027         HandleEPSError();
03028         return FALSE;
03029     }
03030 
03031     return TRUE;
03032 }
03033 
03034 
03035 /********************************************************************************************
03036 
03037 >   BOOL CamelotEPSFilter::ProcessGuideLayer()
03038 
03039     Author:     Mark_Neves (Xara Group Ltd) <camelotdev@xara.com>
03040     Created:    11/10/95
03041     Returns:    TRUE if the guide layer was processed correctly, FALSE if not.
03042     Purpose:    Reads in a guide layer object
03043     Errors:     Syntax error in EPS file, Out of memory
03044 
03045 ********************************************************************************************/
03046 
03047 BOOL CamelotEPSFilter::ProcessGuideLayer()
03048 {
03049     String_64 GuideColourName;
03050     PColourCMYK CMYK;
03051     FIXEDPOINT TintVal;
03052 
03053     String_256 LayerName;
03054     INT32 Visible;
03055     INT32 Locked;
03056     INT32 Foreground;
03057     INT32 Printable;
03058 
03059     // Read in the guide layer params
03060     if (!Stack.PopColour(&CMYK, TINT_ILLUSTRATOR, &TintVal, &GuideColourName)   ||  
03061         !Stack.Pop(&Locked)     || 
03062         !Stack.Pop(&Printable)  || 
03063         !Stack.Pop(&Visible)    || 
03064         !Stack.Pop(&Foreground) || 
03065         !Stack.Pop(&LayerName))
03066     {
03067         return FALSE;
03068     }
03069 
03070 #if defined(EXCLUDE_FROM_RALPH)
03071     // In ralph we dont want to import guideline layers
03072 #else
03073     if (Filter::ImportWithLayers)
03074     {
03075         // We are importing layers, so put all new nodes on this layer.
03076         UseLayer(LayerName,TRUE);
03077 
03078         // Try to set the visible/locked flags on this layer, but only if
03079         // we created a new one (i.e. don't change the flags of existing layers.
03080         if (EPSFlags.AddToNewLayer)
03081         {
03082             // Here we force layers to be printable if they are in the foreground - just in case
03083             // This will have to change if the UI allows Printable & Foreground to be specified
03084             // independantly.
03085             Printable = Foreground;
03086 
03087             // Visible flag
03088             // Only relevant for non-guide layers. Guide layer visibility is stored in the DocView
03089             // and is saved in the document comments header
03090 
03091             // Locked flag
03092             pLayer->SetLocked(!(Locked == 0));
03093 
03094             // Printable flag
03095             pLayer->SetPrintable(!(Printable == 0));
03096 
03097             // Background flag
03098             pLayer->SetBackground(Foreground == 0);
03099 
03100             DocColour Col;
03101 
03102             GetEPSColour(&Col,&CMYK,TINT_ILLUSTRATOR,TintVal,&GuideColourName);
03103             IndexedColour* pIndexedColour = Col.FindParentIndexedColour();
03104             pLayer->SetGuideColour(pIndexedColour);
03105         }
03106     }
03107 #endif
03108     return TRUE;
03109 }
03110 
03111 /********************************************************************************************
03112 
03113 >   BOOL CamelotEPSFilter::ProcessGuideline()
03114 
03115     Author:     Mark_Neves (Xara Group Ltd) <camelotdev@xara.com>
03116     Created:    11/10/95
03117     Returns:    TRUE if the guideline was processed correctly, FALSE if not.
03118     Purpose:    Reads in a guide line object
03119     Errors:     Syntax error in EPS file, Out of memory
03120 
03121 ********************************************************************************************/
03122 
03123 BOOL CamelotEPSFilter::ProcessGuideline()
03124 {
03125     INT32 Type, Ordinate;
03126 
03127     // Read in the guideline's params
03128     if (!Stack.Pop(&Type)   ||
03129         !Stack.Pop(&Ordinate))
03130     {
03131         return FALSE;
03132     }
03133 
03134 #ifdef WEBSTER
03135     // Neville 8/8/97
03136     // If we aren't importing with layers into Webster, which we wont by default,
03137     // then ignore any guidelines, as these should only be loaded onto guide layers
03138     if (!Filter::ImportWithLayers)
03139         return TRUE;
03140 #endif
03141 
03142     NodeGuideline* pGuideline = new NodeGuideline;
03143     ERROR1IF(pGuideline == NULL, FALSE, _R(IDT_EPS_NOMEMORY));
03144 
03145     // Add it into the tree
03146     if (!AddNewNode(pGuideline))
03147         return FALSE;
03148 
03149     pGuideline->SetType(GuidelineType(Type));
03150     pGuideline->SetOrdinate(Ordinate);
03151 
03152     return TRUE;
03153 }
03154 
03156 // EPSFontCache methods.
03157 
03158 /********************************************************************************************
03159 
03160 >   EPSFontCache::EPSFontCache()
03161 
03162     Author:     Tim_Browse (Xara Group Ltd) <camelotdev@xara.com>
03163     Created:    05/30/95
03164     Purpose:    Set up the EPS font cache to be initially invalid.
03165     SeeAlso:    CamelotEPSRenderRegion
03166 
03167 ********************************************************************************************/
03168 
03169 EPSFontCache::EPSFontCache()
03170 {
03171     // Font cache is invalid
03172     Valid = FALSE;
03173 };
03174 
03175 #if !defined(EXCLUDE_FROM_RALPH)
03176 
03178 // CamelotEPSRenderRegion methods.
03179 
03180 CC_IMPLEMENT_DYNAMIC(CamelotEPSRenderRegion, ArtWorksEPSRenderRegion)
03181 
03182 /********************************************************************************************
03183 
03184 >   CamelotEPSRenderRegion(DocRect ClipRect, Matrix ConvertMatrix, FIXED16 ViewScale)
03185 
03186     Author:     Tim_Browse (Xara Group Ltd) <camelotdev@xara.com>
03187     Created:    15/04/94
03188     Purpose:    Initialise a render region for exporting Camelot EPS. Sets up the
03189                 string to put in the %%Creator comment.
03190     SeeAlso:    EPSRenderRegion::EPSRenderRegion
03191 
03192 ********************************************************************************************/
03193 
03194 CamelotEPSRenderRegion::CamelotEPSRenderRegion(DocRect ClipRect,
03195                                                  Matrix ConvertMatrix, 
03196                                                  FIXED16 ViewScale) 
03197     : ArtWorksEPSRenderRegion(ClipRect, ConvertMatrix, ViewScale)
03198 {
03199     CreatorString = _T("Xara X");
03200 
03201     SepTables = NULL;
03202 }
03203 
03204 
03205 
03206 /********************************************************************************************
03207 
03208 >   CamelotEPSRenderRegion::~CamelotEPSRenderRegion()
03209 
03210     Author:     Jason_Williams (Xara Group Ltd) <camelotdev@xara.com>
03211     Created:    27/6/96
03212     Purpose:    destructor
03213 
03214 ********************************************************************************************/
03215 
03216 CamelotEPSRenderRegion::~CamelotEPSRenderRegion()
03217 {
03218     if (SepTables != NULL)
03219     {
03220         CCFree(SepTables);
03221         SepTables = NULL;
03222     }
03223 }
03224 
03225 
03226 
03227 /********************************************************************************************
03228 
03229 >   BOOL CamelotEPSRenderRegion::StartRender()
03230 
03231     Author:     Tim_Browse (Xara Group Ltd) <camelotdev@xara.com>
03232     Created:    30/03/94
03233     Returns:    TRUE if worked, FALSE if failed.
03234     Purpose:    Prepare the render region for rendering (exporting).
03235     SeeAlso:    EPSRenderRegion::Initialise; EPSRenderRegion::StopRender
03236 
03237 ********************************************************************************************/
03238 
03239 BOOL CamelotEPSRenderRegion::StartRender()
03240 {
03241     // (ChrisG 3/1/01) - We no longer write out the restore/save pair, so that multiple objects
03242     //  (both simple and complex) can be clipped together. This now works the same as the 
03243     //  printing (see PrintPSRenderRegion::StartRender)
03244 
03245     // This should only be done around areas where there is a possibility of the VM being 
03246     //  modified without our knowledge, e.g. when including EPS files inside this file, using
03247     //  third-party functions, etc..., and the save should be done immediately before the 
03248     //  suspect block, and the restore immediately after.
03249 
03250     // Call base class first
03251     if (!EPSRenderRegion::StartRender())
03252         return FALSE;
03253 
03254     InitAttributes ();
03255 
03256     return TRUE;
03257 }
03258 
03259 /********************************************************************************************
03260 
03261 >   BOOL CamelotEPSRenderRegion::StopRender()
03262 
03263     Author:     Tim_Browse (Xara Group Ltd) <camelotdev@xara.com>
03264     Created:    30/03/94
03265     Purpose:    Deinitialise the render region after rendering (exporting).
03266     SeeAlso:    EPSRenderRegion::Deinitialise; EPSRenderRegion::StartRender
03267 
03268 ********************************************************************************************/
03269 
03270 BOOL CamelotEPSRenderRegion::StopRender()
03271 {
03272     // (ChrisG 3/1/01) - We no longer write out the restore/save pair, so that multiple objects
03273     //  (both simple and complex) can be clipped together. This now works the same as the 
03274     //  printing (see CamelotEPSRenderRegion::StartRender for more info)
03275 
03276     if (!IS_A(this, NativeRenderRegion))
03277     {
03278         // Since we're clearing the the current attributes in the postscript file (using 
03279         //  restore), we'd better clear them in the exporter, so that they are re-exported
03280         //  if needed. NOTE: This only seems to affect font info.
03281 
03282         FontInfo.Valid = 0;
03283     }
03284 
03285     if (SepTables != NULL)
03286     {
03287         CCFree(SepTables);
03288         SepTables = NULL;
03289     }
03290 
03291     DeInitAttributes ();
03292 
03293     return TRUE;
03294 }
03295 
03296 
03297 /********************************************************************************************
03298 
03299 >   virtual void CamelotEPSRenderRegion::GetRenderRegionCaps(RRCaps* pCaps)
03300 
03301     Author:     Tim_Browse (Xara Group Ltd) <camelotdev@xara.com>
03302     Created:    10/4/95
03303     Outputs:    pCaps - The details about what types of thing this render region can render
03304     Purpose:    This function allows render regions to admit to what they can and can not
03305                 render. This allows other areas of the program to come in and help render
03306                 regions out in some situations, if they are unable to render everything.
03307                 eg. an OSRenderRegion can not render transparancy.
03308 
03309 ********************************************************************************************/
03310 
03311 void CamelotEPSRenderRegion::GetRenderRegionCaps(RRCaps* pCaps)
03312 {
03313     // We can do grad fills, and most kinds of bitmaps.
03314     pCaps->CanDoNothing();
03315     pCaps->GradFills = TRUE;
03316     pCaps->SimpleBitmaps = TRUE;
03317     pCaps->ArbitraryBitmaps = TRUE;
03318     pCaps->ClippedSimpleBitmaps = TRUE;
03319     pCaps->ClippedArbitraryBitmaps = TRUE;
03320     pCaps->ClippedOutput = TRUE;
03321 }
03322 
03323 /********************************************************************************************
03324 
03325 >   void CamelotEPSRenderRegion::GetValidPathAttributes()
03326 
03327     Author:     Tim_Browse (Xara Group Ltd) <camelotdev@xara.com>
03328     Created:    30/03/94
03329     Purpose:    See EPSRenderRegion::GetValidPathAttributes.
03330                 This version checks and handles grad fill colours before calling the
03331                 base class version.
03332     SeeAlso:    EPSRenderRegion::GetValidPathAttributes
03333 
03334 ********************************************************************************************/
03335 
03336 void CamelotEPSRenderRegion::GetValidPathAttributes()
03337 {
03338     KernelDC *pDC = (KernelDC*)CCDC::ConvertFromNativeDC(RenderDC);
03339 
03340     FillGeometryAttribute *pFillAttr = 
03341         (FillGeometryAttribute *) CurrentAttrs[ATTR_FILLGEOMETRY].pAttr;
03342 
03343     if (pFillAttr->IsKindOf(CC_RUNTIME_CLASS(GradFillAttribute)) &&
03344         SetLastOutputAttribute(ATTR_FILLGEOMETRY))
03345     {
03346         // If the fill is square
03347         if (!pFillAttr->IsASquareFill() && !pFillAttr->IsAThreeColFill())
03348         {
03349             // Get the correct brush - may be a grad fill.
03350             if (//pFillAttr->IsKindOf(CC_RUNTIME_CLASS(GradFillAttribute)) &&
03351                 !pFillAttr->IsKindOf(CC_RUNTIME_CLASS(BitmapFillAttribute)) )
03352             {
03353                 // Output a grad fill command...
03354 
03355                 // Get the start colour and end colour in CMYK, and output them.
03356                 GradFillAttribute *pGradFillAttr = (GradFillAttribute *) pFillAttr;
03357 
03358                 // Check if the colours are named or not.
03359                 BOOL UnnamedColours = FALSE;
03360                 BOOL GreyFill = FALSE;
03361 
03362                 // During a normal CamEPS export we will not be separating, hence
03363                 // pSeparation will always return NULL here. If however we are
03364                 // currently printing we need to perform some giggery pokery with
03365                 // grad fills in order to separate them correctly. 
03366                 
03367                 // We have two fill operators we can use in Postscript, these
03368                 // are cax, a colour fill op and cag a grey fill op. 
03369                 // There are three items of interest to us which we need to consider
03370                 // before choosing to use one of the postscript fill operators, these
03371                 // are
03372                 // (1) the end colours of the fill (the model etc)
03373                 // (2) the fill effect across the grad fill
03374                 // (3) the output plate we're rendering to.
03375                 //
03376                 // We need to work out what happens on both cmyk and spot plates when
03377                 // using one of these operators. For instance when using a grey fill
03378                 // operator, the fill effect will not be used. It may go out in the 
03379                 // output stream, but it will be ignored in Postscript (because you
03380                 // are using a grey fill of course!).
03381                 // You also need to consider that if you use the colour fill operator
03382                 // you are in effect restricting output to the process colour plates.
03383                 // Nothing will appear on spot plates. The colour operator works by
03384                 // performing a separation itself, using the current fill effect and
03385                 // wandering about in rgb space.
03386                 // You also need to know the rules for interaction between mixed colours
03387                 // during a separation, ie if we have a rgb colour to spot colour fill
03388                 // with a rainbow fill effect, question which should immediately leap/
03389                 // to mind are
03390                 // (1) do we use the fill effect
03391                 // (2) what fill is used on the spot plate
03392                 // (3) what fill is used on the process plates
03393                 // (4) how will this all look, is it sensible?
03394 
03395                             
03396                 // We should really define a virtual function for this operation and
03397                 // override it in PrintPSRenderRegion.
03398 
03399                 ColourContext* pContext;
03400                 ColourPlate* pSeparation;
03401                 GetOutputColourPlate(COLOURMODEL_CMYK, &pContext, &pSeparation);
03402 
03403                 if (pSeparation!=NULL)
03404                 {
03405                     DocColour *Start, *End;
03406                     Start = &(pGradFillAttr->Colour);
03407                     End   = &(pGradFillAttr->EndColour);
03408                     BOOL StartIsSpot = (Start->GetSpotParent() != NULL);
03409                     BOOL EndIsSpot   = (End->GetSpotParent() != NULL);
03410 
03411                     // If we are rendering a spot plate 
03412                     GreyFill = (pSeparation->GetType() == COLOURPLATE_SPOT);
03413                     // or either end of this fill are spot... use the gray fill operator
03414                     // This will mean process colour will be separated and provide a linear
03415                     // fill to white on none spot plates.
03416                     GreyFill = (GreyFill || StartIsSpot || EndIsSpot);
03417 
03418                     if (!GreyFill)
03419                     {
03420                         // ok we're on a process plate, and both ends are not spot
03421                         // are both ends CMYK colours with a simple fill effect?
03422                         GreyFill = ( Start->GetColourModel() == COLOURMODEL_CMYK ) && 
03423                                    (   End->GetColourModel() == COLOURMODEL_CMYK );
03424 
03425                         if (GreyFill)
03426                         {
03427                             // Is there a crazy fill effect pending?
03428                             UINT32 filleffect;
03429                             BOOL exists = GetCurrFillEffect(filleffect);
03430                             GreyFill = (!exists || filleffect == FILLEFFECT_FADE);
03431                         }
03432                     }
03433                 }
03434 
03435                 // if GreyFill is true we output this fill as a single plate gray fill
03436                 // otherwise we splunge it across the process plates.
03437                     
03438                 if (GreyFill)
03439                 {
03440                     UnnamedColours = TRUE;
03441                     // both of these colours are cmyk so we need to handle the separation
03442                     // slightly differently. The user will expect these colours to spread
03443                     // without going through rgb=>devcmyk to do so.
03444                     PColourCMYK sCMYK, eCMYK;
03445 
03446                     pGradFillAttr->Colour.GetCMYKValue(pContext, &sCMYK);
03447                     pGradFillAttr->EndColour.GetCMYKValue(pContext, &eCMYK);
03448 
03449                     // Sanity check on this plate
03450                     ERROR3IF( (sCMYK.Cyan!=0 || sCMYK.Magenta!=0 || sCMYK.Yellow!=0 ||
03451                                eCMYK.Cyan!=0 || eCMYK.Magenta!=0 || eCMYK.Yellow!=0) , 
03452                              "Not a mono plate during export of CMYK gradfills (CAMEPSRndRgn::GetValidPathAttributes())" );
03453 
03454                     // output a linear key fill.
03455                     BOOL ok = pDC->OutputColourValue(255 - sCMYK.Key);
03456                     ok = ok && pDC->OutputColourValue(255 - eCMYK.Key);
03457                     // This entire function seems to ignore disc failure which is a bit rampant!
03458                     ERROR3IF(!ok, "Disc buffer failure during gradfill output in GetValidPathAttributes");
03459                 }
03460                 else
03461                 {
03462                     // This is a mixture of colour spaces so simply output the
03463                     // who lot as normal. Postscript will spread the colour
03464                     UnnamedColours = OutputGradFillColours( &(pGradFillAttr->Colour), &(pGradFillAttr->EndColour) );
03465                 }
03466 
03467                 // Output the start and end points of the grad fill
03468                 pDC->OutputCoord(pGradFillAttr->StartPoint);
03469                 pDC->OutputCoord(pGradFillAttr->EndPoint);
03470 
03471 
03472                 // Output the fill type
03473                 if (pFillAttr->IsKindOf(CC_RUNTIME_CLASS(LinearFillAttribute)))
03474                 {
03475                     LinearFillAttribute *pLinearFillAttr = (LinearFillAttribute *) pFillAttr;
03476 
03477                     DocCoord* Start = pFillAttr->GetStartPoint();
03478                     DocCoord* End   = pFillAttr->GetEndPoint();
03479                     DocCoord* End2  = pFillAttr->GetEndPoint2();
03480 
03481                     if (AreLinesPerpendicular(Start, End, End2))
03482                     {
03483                         TRACEUSER( "Will", _T("Exporting Simple Linear Fill\n"));
03484                         pDC->OutputValue((INT32) CAMEPS_FILL_LINEAR);
03485                     }
03486                     else
03487                     {
03488                         TRACEUSER( "Will", _T("Exporting New Style Linear Fill\n"));
03489                         pDC->OutputCoord(pLinearFillAttr->EndPoint2);
03490                         pDC->OutputValue((INT32) CAMEPS_FILL_NEWLINEAR);
03491                     }
03492                 }
03493                 else if (pFillAttr->IsKindOf(CC_RUNTIME_CLASS(RadialFillAttribute)))
03494                 {
03495                     // Is it circular or elliptical?
03496                     RadialFillAttribute *pRadialFillAttr = (RadialFillAttribute *) pFillAttr;
03497                     if (pRadialFillAttr->IsElliptical())
03498                     {
03499                         // Elliptical fill - output the second end point.
03500                         pDC->OutputCoord(pRadialFillAttr->EndPoint2);
03501                         pDC->OutputValue((INT32) CAMEPS_FILL_ELLIPTICAL);
03502                     }
03503                     else
03504                     {
03505                         // Circular fill
03506                         pDC->OutputValue((INT32) CAMEPS_FILL_CIRCULAR);
03507                     }
03508                 }
03509                 else if (pFillAttr->IsKindOf(CC_RUNTIME_CLASS(ConicalFillAttribute)))
03510                 {
03511                     pDC->OutputValue((INT32) CAMEPS_FILL_CONICAL);
03512                 }
03513                 else
03514                 {
03515                     // Bodge: ought to handle this more gracefully soon...
03516                     ENSURE(FALSE, "Unsupported grad fill encountered while exporting");
03517                     EPSRenderRegion::GetValidPathAttributes();
03518                     return;
03519                 }
03520 
03521                 // Output the grad fill token
03522                 if (UnnamedColours)
03523                 {
03524                     if (GreyFill)
03525                         pDC->OutputToken(_T("cag"));
03526                     else
03527                         pDC->OutputToken(_T("caz"));
03528                 }
03529                 else
03530                 {
03531                     pDC->OutputToken(_T("cax"));
03532                 }
03533                 pDC->OutputNewLine();
03534             }
03535             else if (   // Utterly awful code by WillC
03536                         (pFillAttr->IsKindOf(CC_RUNTIME_CLASS(FractalFillAttribute)) ||
03537                          pFillAttr->IsKindOf(CC_RUNTIME_CLASS(NoiseFillAttribute))) &&
03538                          Caps.BitmapFills
03539                     )
03540             {
03541                 // Fractal fill - output fractal fill command...
03542 
03543                 //*************************************************************************
03544                 // this piece of code is copied from the bitmap export but differs
03545                 // only very slightly. Is this difference a bug or intended? We will never
03546                 // know.
03547 
03548                 // first output the base class fill stuff...
03549                 BitmapFillAttribute *pFill = (BitmapFillAttribute *) pFillAttr;
03550 
03551                 // Get the start colour and end colour in CMYK, and output them.
03552                 // Check if the colours are named or not.
03553                 BOOL UnnamedColours = FALSE;
03554                 ColourContext *cc = NULL;
03555                 
03556                 // (ChrisG 8/12/00) All non-grey grad fill colours are now exported by the 
03557                 // OutputGradFillColours function.
03558                 if ((pFill->Colour.FindParentIndexedColour() == NULL) &&
03559                     (pFill->EndColour.FindParentIndexedColour() == NULL))
03560                 {
03561                     // Neither colour is named...use unnamed colour syntax.
03562                     cc = ColourManager::GetColourContext(COLOURMODEL_CMYK, GetRenderView());
03563                     ERROR3IF(cc == NULL, "No CMYK colour context - somethings' seriously broken");
03564                 }
03565                 else
03566                 {
03567                     cc = NULL;
03568                 }
03569 
03570                 // Output the colours (and names)
03571                 UnnamedColours = OutputGradFillColours (&(pFill->Colour), &(pFill->EndColour), cc);
03572 
03573                 // Next the coords of the fill
03574                 pDC->OutputCoord(pFill->StartPoint);
03575                 pDC->OutputCoord(pFill->EndPoint);
03576                 pDC->OutputCoord(pFill->EndPoint2);
03577 
03578                 //*************************************************************************
03579                 // ok NoiseFillAttributes are saved as FractalFillAttributes. Strange you
03580                 // might say, aha! but NoiseFillAttributes are new objects not supported by
03581                 // camelot 1.1 so what else should we do? Hmm not sure at this point.
03582 
03583                 // Save the fractal parameters out.
03584                 pDC->OutputValue(pFill->GetSeed());
03585                 pDC->OutputReal(pFill->GetGraininess().MakeDouble());
03586                 pDC->OutputReal(pFill->GetGravity().MakeDouble());
03587                 pDC->OutputReal(pFill->GetSquash().MakeDouble());
03588 
03589                 // And now the DPI of the fractal bitmap
03590                 UINT32 DPI = pFill->GetFractalDPI();
03591                 pDC->OutputValue(DPI);
03592 
03593                 // And now the 'tileable' flag
03594                 pDC->OutputValue((INT32) (pFill->GetTileable()));
03595 
03596                 // Always Fractal type 1 at present
03597                 pDC->OutputToken(_T("1"));
03598 
03599                 // This is a fractal fill
03600                 pDC->OutputValue((INT32) CAMEPS_FILL_FRACTAL);
03601 
03602                 if (UnnamedColours)
03603                     pDC->OutputToken(_T("caz"));
03604                 else
03605                     pDC->OutputToken(_T("cax"));
03606                 pDC->OutputNewLine();
03607             }
03608             else if (pFillAttr->IsKindOf(CC_RUNTIME_CLASS(BitmapFillAttribute)) && Caps.BitmapFills)
03609             {
03610                 // Bitmap fill - output bitmap fill command...
03611                 BitmapFillAttribute *pBitmapFill = (BitmapFillAttribute *) pFillAttr;
03612                 ERROR3IF(pBitmapFill->GetBitmap() == NULL, "Bitmap fill has no bitmap");
03613 
03614                 if (pBitmapFill->GetBitmap())
03615                 {
03616                     // Get the start colour and end colour in CMYK, and output them.
03617                     // Check if the colours are named or not.
03618                     BOOL UnnamedColours = FALSE;
03619                     BOOL HasColours = ((pBitmapFill->GetStartColour() != NULL) &&
03620                                        (pBitmapFill->GetEndColour() != NULL));
03621 
03622                     if (HasColours)
03623                     {
03624                         // (ChrisG 8/12/00) All non-grey grad fill colours are now exported by the 
03625                         // OutputGradFillColours function.
03626                         ColourContext *cc = NULL;
03627 
03628                         if ((pBitmapFill->Colour.FindParentIndexedColour() == NULL) &&
03629                             (pBitmapFill->EndColour.FindParentIndexedColour() == NULL))
03630                         {
03631                             // Neither colour is named...use unnamed colour syntax.
03632                             cc = ColourManager::GetColourContext(COLOURMODEL_CMYK, GetRenderView());
03633                             ERROR3IF(cc == NULL, "No CMYK colour context - somethings' seriously broken");
03634                         }
03635                         else
03636                         {
03637                             // One or two of the colours are named, so use the named colour syntax.
03638                             cc = NULL;
03639                         }
03640 
03641                         // Write out the colours themselves.
03642                         UnnamedColours = OutputGradFillColours (&(pBitmapFill->Colour),
03643                                                                 &(pBitmapFill->EndColour),
03644                                                                 cc);
03645                     }
03646 
03647                     pDC->OutputCoord(pBitmapFill->StartPoint);
03648                     pDC->OutputCoord(pBitmapFill->EndPoint);
03649                     pDC->OutputCoord(pBitmapFill->EndPoint2);
03650 
03651                     if (HasColours)
03652                         pDC->OutputValue((INT32) CAMEPS_FILL_NEWBITMAP);
03653                     else
03654                         pDC->OutputValue((INT32) CAMEPS_FILL_BITMAP);
03655 
03656                     if (UnnamedColours)
03657                         pDC->OutputToken(_T("caz"));
03658                     else
03659                         pDC->OutputToken(_T("cax"));
03660 
03661                     pDC->OutputNewLine();
03662 
03663                     // ...and then the bitmap itself.
03664                     ExportDC *pExportDC = (ExportDC *) pDC;
03665                     pExportDC->GetParentFilter()->ExportBitmap(*pBitmapFill->GetBitmap());
03666                 }
03667             }
03668         }
03669     }
03670 
03671     if (SetLastOutputAttribute(ATTR_FILLEFFECT))
03672     {
03673         UINT32 EffectType;
03674         GetCurrFillEffect(EffectType);
03675 
03676         // Output fill effect
03677         pDC->OutputValue(EffectType);
03678     
03679         // Output the fill effect token
03680         pDC->OutputToken(_T("cxe"));
03681         pDC->OutputNewLine();
03682     }
03683 
03684     if (SetLastOutputAttribute(ATTR_FILLMAPPING))
03685     {
03686         // Now do the chromatic fill mapping
03687         FillMappingAttribute *pFillAttr = 
03688             (FillMappingAttribute *) CurrentAttrs[ATTR_FILLMAPPING].pAttr;
03689 
03690         // Get the correct mapping
03691         INT32 MappingType = pFillAttr->Repeat;
03692 //Mark Howitt, 9/10/97. If we are exporting, make sure that repeating grad fill is saved as simple.
03693 #ifdef _DEBUG
03694         if(MappingType == 4) MappingType = 2;
03695 #endif
03696         ERROR3IF((MappingType < 1) || (MappingType > 3), "Illegal fill mapping value!");
03697         
03698         // Output fill mapping
03699         pDC->OutputValue((UINT32) MappingType);
03700 
03701         // Allow for future extension of fill mappings. 
03702         pDC->OutputToken(_T("0"));
03703 
03704         // Output the fill mapping token
03705         pDC->OutputToken(_T("cxm"));
03706         pDC->OutputNewLine();
03707     }
03708 
03709     if (SetLastOutputAttribute(ATTR_WINDINGRULE))
03710     {
03711         WindingRuleAttribute *pWindingRuleAttr = 
03712             (WindingRuleAttribute *) CurrentAttrs[ATTR_WINDINGRULE].pAttr;
03713 
03714         switch (pWindingRuleAttr->WindingRule)
03715         {
03716             case NonZeroWinding:
03717                 // Change winding rule to 0, which means non-zero in Camelot EPS.
03718                 pDC->OutputToken(_T("0"));
03719                 pDC->OutputToken(_T("awr"));
03720                 pDC->OutputNewLine();
03721                 break;
03722 
03723             case EvenOddWinding:
03724                 // Change winding rule to 1, which means even-odd in Camelot EPS.
03725                 pDC->OutputToken(_T("1"));
03726                 pDC->OutputToken(_T("awr"));
03727                 pDC->OutputNewLine();
03728                 break;
03729 
03730             case NegativeWinding:
03731             case PositiveWinding:
03732                 // Not supported in Camelot EPS - ignore.
03733                 break;
03734 
03735             default:
03736                 ERROR3("Unknown winding rule encountered while exporting.");
03737                 break;
03738                 
03739         }
03740     }
03741 
03742     // Leave arrows/dash patterns to the base class.
03743 
03744     // Handle usual pens/brushes
03745     EPSRenderRegion::GetValidPathAttributes();
03746 }
03747 
03748 
03749 
03750 /********************************************************************************************
03751 
03752 >   BOOL CamelotEPSRenderRegion::OutputGradFillColours(DocColour* StartCol, 
03753                                                        DocColour* EndCol
03754                                                        ColourContext pContext = NULL)
03755     Author:     Mike_Kenny (Xara Group Ltd) <camelotdev@xara.com>
03756     Created:    24/06/96
03757     Inputs:     StartCol = the grad fill start colour
03758                 EndCol   = the grad fill end colour
03759                 pContext = a pointer to a local colour context (defaults to NULL, in
03760                             which case a global default context will be used instead)
03761                             This context MUST be a CMYK context
03762     Outputs:    -
03763     Returns:    TRUE if both colours were unnamed colours;
03764                 FALSE if not
03765     Purpose:    Output the colour definitions of the start and end colours to be used
03766                 in a graduated fill.
03767 
03768 ********************************************************************************************/
03769 
03770 BOOL CamelotEPSRenderRegion::OutputGradFillColours(DocColour* StartCol, DocColour* EndCol, ColourContext* pContext)
03771 {
03772     KernelDC *pDC = (KernelDC*)CCDC::ConvertFromNativeDC(RenderDC);
03773     bool outputNames = FALSE;
03774 
03775     if ((StartCol->FindParentIndexedColour() == NULL) &&
03776         (EndCol->FindParentIndexedColour() == NULL))
03777     {
03778         // Neither colour is named...use unnamed colour syntax.
03779         outputNames = FALSE;
03780     }
03781     else
03782     {
03783         // One or two of the colours are named, so use the named colour syntax.
03784         outputNames = TRUE;
03785     }
03786 
03787     // Start writing stuff to the EPS file.
03788     INT32 red = 0;
03789     INT32 green = 0;
03790     INT32 blue = 0;
03791 
03792     // Write out the start colour
03793     StartCol->GetRGBValue (&red, &green, &blue);
03794     pDC->OutputColourValue (red);
03795     pDC->OutputColourValue (green);
03796     pDC->OutputColourValue (blue);
03797 
03798     if (outputNames)
03799     {
03800         // Write out the name
03801         pDC->OutputColourName (StartCol);
03802 
03803         // Write out the tint value. This isn't actually used at the moment, but is left 
03804         //  for possible future expansion
03805         pDC->OutputValue ((INT32)0);
03806     }
03807 
03808     // And the End colour.
03809     EndCol->GetRGBValue (&red, &green, &blue);
03810     pDC->OutputColourValue(red);
03811     pDC->OutputColourValue(green);
03812     pDC->OutputColourValue(blue);
03813 
03814     if (outputNames)
03815     {
03816         // Write out the name
03817         pDC->OutputColourName (EndCol);
03818 
03819         // Write out the tint value. This isn't actually used at the moment, but is left 
03820         //  for possible future expansion
03821         pDC->OutputValue ((INT32)0);
03822     }
03823 
03824     // Return TRUE for no names used, FALSE for names used.
03825     return (!outputNames);
03826 }
03827 
03828 
03829 
03830 /********************************************************************************************
03831 
03832 >   UINT32 CamelotEPSRenderRegion::GetCurrFillEffect(UINT32& EffectType)
03833 
03834     Author:     Mike_Kenny (Xara Group Ltd) <camelotdev@xara.com>
03835     Created:    24/06/96
03836     Inputs:     -
03837     Outputs:    The current type of fill effect
03838     Returns:    TRUE if a fill effect exists in the current attrs stack
03839                 FALSE if not
03840     Purpose:    Return the state of the current fill effect in the EPS attributes stack
03841 
03842 ********************************************************************************************/
03843 
03844 BOOL CamelotEPSRenderRegion::GetCurrFillEffect(UINT32& EffectType)
03845 {
03846     // set default
03847     EffectType = FILLEFFECT_FADE;
03848 
03849     FillEffectAttribute *pFillAttr = (FillEffectAttribute *) CurrentAttrs[ATTR_FILLEFFECT].pAttr;
03850     if (pFillAttr==NULL)
03851         return FALSE;   
03852 
03853     if (pFillAttr->IsKindOf(CC_RUNTIME_CLASS(FillEffectFadeAttribute)))
03854     {
03855         EffectType = FILLEFFECT_FADE;
03856     }
03857     else if (pFillAttr->IsKindOf(CC_RUNTIME_CLASS(FillEffectRainbowAttribute)))
03858     {
03859         EffectType = FILLEFFECT_RAINBOW;
03860     }
03861     else if (pFillAttr->IsKindOf(CC_RUNTIME_CLASS(FillEffectAltRainbowAttribute)))
03862     {
03863         EffectType = FILLEFFECT_ALTRAINBOW;
03864     }
03865     else
03866     {
03867         ERROR3("Illegal fill effect type!");
03868     }
03869     
03870     return TRUE;
03871 }
03872 
03873 /********************************************************************************************
03874 
03875 >   BOOL CamelotEPSRenderRegion::WriteEPSBoundingBox ( void )
03876 
03877     Author:     Graeme_Sutherland (Xara Group Ltd) <camelotdev@xara.com>
03878     Created:    19/5/00
03879     Inputs:     -
03880     Returns:    TRUE if ok;
03881                 FALSE if error (e.g. file/disk error or printer driver error)
03882     Purpose:    Calls the base EPS bounding box function.
03883     SeeAlso:    EPSRenderRegion::WriteEPSBoundingBox
03884 
03885 ********************************************************************************************/
03886 
03887 BOOL CamelotEPSRenderRegion::WriteEPSBoundingBox ( void )
03888 {
03889     // Call the base class function.
03890     return EPSRenderRegion::WriteEPSBoundingBox ();
03891 }
03892 
03893 /********************************************************************************************
03894 
03895 >   BOOL CamelotEPSRenderRegion::WriteProlog(KernelDC *pDC)
03896 
03897     Author:     Tim_Browse (Xara Group Ltd) <camelotdev@xara.com>
03898     Created:    24/04/95
03899     Inputs:     pDC - the device context to output to.
03900     Outputs:    -
03901     Returns:    TRUE if ok;
03902                 FALSE if error (e.g. file/disk error or printer driver error)
03903     Purpose:    Output any PostScript prolog for this render region.  For EPS and printing,
03904                 this means output of our PostScript rendering procedures; for Native
03905                 files we do nothing.
03906     SeeAlso:    EPSRenderRegion::WriteSetup
03907 
03908 ********************************************************************************************/
03909 
03910 BOOL CamelotEPSRenderRegion::WriteProlog(KernelDC *pDC)
03911 {
03912     // Get hold of our PostScript prolog resource...
03913     CCResTextFile PrologFile;
03914 
03915     // Open the file
03916     if (!PrologFile.open(_R(IDM_PS_PROLOG), _R(IDT_PS_RES)))
03917     {
03918         // Failed to open the file...
03919         ERROR2(FALSE, "Could not get at PostScript resource!");
03920     } 
03921 
03922     // Put some comments out to mark our procset
03923     pDC->OutputNewLine();
03924     pDC->OutputToken(_T("%%BeginResource: procset XaraStudio1Dict"));
03925     pDC->OutputNewLine();
03926     pDC->OutputToken(_T("% Copyright (c) 1995,1996 Xara Ltd"));
03927     pDC->OutputNewLine();
03928 
03929     // Start a new dictionary to avoid clashes...
03930     pDC->OutputToken(_T("/XaraStudio1Dict 300 dict def XaraStudio1Dict begin"));
03931     pDC->OutputNewLine();
03932 
03933     // Read each line from the file and output it to the DC.
03934     String_256 LineBuf;
03935     TCHAR *pBuf = (TCHAR *) LineBuf;
03936 
03937     while (!PrologFile.read(&LineBuf).eof())
03938     {
03939         // Copy this line to output.
03940         pDC->OutputTCHARAsChar(pBuf, LineBuf.Length());
03941         pDC->OutputNewLine();
03942     }
03943 
03944     // All done
03945     PrologFile.close();
03946 
03947     // Write out any separation functions in this prolog if we
03948     // need to. Note this is a bool function
03949     WriteSepFunctions(pDC);
03950 
03951     // Put some comments/code out to mark our procset end
03952     pDC->OutputToken(_T("end"));
03953     pDC->OutputNewLine();
03954     pDC->OutputToken(_T("%%EndResource"));
03955     pDC->OutputNewLine();
03956 
03957     return TRUE;
03958 }
03959 
03960 /********************************************************************************************
03961 
03962 >   BOOL CamelotEPSRenderRegion::WriteSetup(KernelDC *pDC)
03963 
03964     Author:     Tim_Browse (Xara Group Ltd) <camelotdev@xara.com>
03965     Created:    24/04/95
03966     Inputs:     pDC - the device context to output to.
03967     Outputs:    -
03968     Returns:    TRUE if ok;
03969                 FALSE if error (e.g. file/disk error or printer driver error)
03970     Purpose:    Output any PostScript setup for this render region.  For EPS and printing,
03971                 this means output of our PostScript code to initialise the context for
03972                 rendering; for Native files we do nothing.
03973     SeeAlso:    EPSRenderRegion::WriteSetup
03974 
03975 ********************************************************************************************/
03976 
03977 BOOL CamelotEPSRenderRegion::WriteSetup(KernelDC *pDC)
03978 {
03979     // Get hold of our PostScript setup resource...
03980     CCResTextFile SetupFile;
03981 
03982     // Open the file
03983     if (!SetupFile.open(_R(IDM_PS_SETUP), _R(IDT_PS_RES)))
03984     {
03985         // Failed to open the file...
03986         ERROR2(FALSE, "Could not get at PostScript resource!");
03987     } 
03988 
03989     // Output some code so that we use our dictionary
03990     pDC->OutputToken(_T("save XaraStudio1Dict begin"));
03991     pDC->OutputNewLine();
03992 
03993     // Read each line from the file and output it to the DC.
03994     String_256 LineBuf;
03995     TCHAR *pBuf = (TCHAR *) LineBuf;
03996 
03997     while (!SetupFile.read(&LineBuf).eof())
03998     {
03999         // Copy this line to output.
04000         pDC->OutputTCHARAsChar(pBuf, LineBuf.Length());
04001         pDC->OutputNewLine();
04002     }
04003 
04004     // All done
04005     SetupFile.close();
04006 
04007     return TRUE;
04008 }
04009 
04010 /********************************************************************************************
04011 
04012 >   BOOL CamelotEPSRenderRegion::WriteEPSTrailerComments ( void )
04013 
04014     Author:     Graeme_Sutherland (Xara Group Ltd) <camelotdev@xara.com>
04015     Created:    22/5/000
04016     Inputs:     -
04017     Returns:    TRUE if ok;
04018                 FALSE if error (e.g. file/disk error or printer driver error)
04019     Purpose:    Writes out an EPS trailer by calling the base class function.
04020     SeeAlso:    EPSRenderRegion::CloseDown
04021 
04022 ********************************************************************************************/
04023 
04024 BOOL CamelotEPSRenderRegion::WriteEPSTrailerComments ( void )
04025 {
04026     // (ChrisG 3/1/01) - Output some code so that we stop using our dictionary, Since
04027     //  this is no longer controlled by Start- and StopRender, we have restore here. This should 
04028     //  be done to close the 'save-restore' block started in WriteSetup.
04029     KernelDC *pDC = (KernelDC*)CCDC::ConvertFromNativeDC(RenderDC);
04030     pDC->OutputToken(_T("end restore"));
04031     pDC->OutputNewLine();
04032 
04033     // Call the base class to over-ride the native render region version.
04034     return EPSRenderRegion::WriteEPSTrailerComments ();
04035 }
04036 
04037 /********************************************************************************************
04038 
04039 >   void CamelotEPSRenderRegion::ColourSeparateScanline24(BYTE *DestBuffer, BYTE *SrcBuffer,
04040                                                             INT32 PixelWidth)
04041 
04042     Author:     Jason_Williams (Xara Group Ltd) <camelotdev@xara.com>
04043     Created:    27/6/96
04044 
04045     Inputs:     DestBuffer  - points to a destination 24bpp RGB scanline buffer
04046                 SrcBuffer   - points to a source 24bpp RGB scanline buffer
04047                 PixelWidth  - The number of pixels to copy from Src to Dest
04048 
04049     Purpose:    Copies the scanline of 24bpp RGB values from SrcBuffer to DestBuffer,
04050                 running the pixels through colour separation tables en route.
04051 
04052                 If you're not colour-separating, then you should replace this function
04053                 call with a simple memcpy.
04054 
04055     Notes:      ONLY handles 24bpp RGB scanlines, and expects that SepTables points
04056                 at the separation tables. The pixel bytes must be in the order R,G,B.
04057 
04058                 NOTE that it handles data in RGB (postscript) format, rather than the 
04059                 normal Bitmap BGR format!
04060 
04061                 All deep bitmap data separates to white on spot plates.
04062 
04063                 The CMYK separation plates could have been written generically, but 
04064                 I have separated out the cases in order to squeeze a bit of speed out
04065                 of the inner pixel conversion loop. The conversion algorithm is basically
04066                 like this, only optimised to move as much work out of the loop as
04067                 possible:
04068 
04069                 MonoOn
04070                     Cyan    = SepTables + 0     // Find the look up tables
04071                     Magenta = SepTables + 256
04072                     Yellow  = SepTables + 512
04073                     UCR     = SepTables + 768
04074                     BGT     = SepTables + 1024
04075 
04076                     c = 255-r;
04077                     m = 255-g;
04078                     y = 255-b;
04079 
04080                     k = min(c, m, y);
04081 
04082                     kr = UCR[k];
04083                     ok = BGT[k];
04084 
04085                     c -= kr;
04086                     m -= kr;
04087                     y -= kr;
04088 
04089                     if (c<0) c=0;
04090                     if (m<0) m=0;
04091                     if (y<0) y=0;
04092 
04093                     oc = Cyan[c];
04094                     om = Magenta[m];
04095                     oy = Yellow[y];
04096             MonoOff
04097 
04098 ********************************************************************************************/
04099 
04100 void CamelotEPSRenderRegion::ColourSeparateScanline24(BYTE *DestBuffer, BYTE *SrcBuffer,
04101                                                         INT32 PixelWidth)
04102 {
04103     ERROR3IF(DestBuffer == NULL || SrcBuffer == NULL || PixelWidth < 1, _T("Illegal params"));
04104     ERROR3IF(SepTables == NULL, _T("No separation tables!?"));
04105     ERROR3IF(CurrentColContext->GetColourPlate() == NULL,
04106                 _T("The separation ColourPlate has gone missing!"));
04107 
04108     // --- A wee macro to find the Maximum of R,G,B, and place it into a variable called 
04109     // Temp, which we define right now in function scope so it's always available
04110     BYTE Temp;
04111     #define TEMPMAX(R,G,B)                  \
04112     {                                       \
04113         Temp = ((R) > (G))  ? (R) : (G);    \
04114         Temp = (Temp > (B)) ? Temp : (B);   \
04115     }
04116 
04117 
04118     // --- OK, let'ts get busy
04119     INT32 i;                                // Loop i
04120     BYTE Ink;                               // Temporary variable for storing the ink value in
04121 
04122     BYTE *UCR = SepTables + 768 + 255;      // UCR is the 4th table, but this points at the
04123                                             // END of the table for optimisation - precalcs (255 - Temp)
04124 
04125 
04126 PORTNOTE("cms", "Disabled colour separations");
04127 #ifndef EXCLUDE_FROM_XARALX
04128     BOOL PrintRGBBlackAsKey=XaraCMS::PrintRGBBlackAsKey;
04129 #else
04130     BOOL PrintRGBBlackAsKey=TRUE;
04131 #endif
04132 
04133     // I've unrolled the shared code for each plate in order to optimise the inner loop
04134     switch (CurrentColContext->GetColourPlate()->GetType())
04135     {
04136         case COLOURPLATE_CYAN:
04137             {
04138                 BYTE *LUT = SepTables + 0;                  // Cyan table is the 1st table
04139 
04140                 for (i = 0; i < PixelWidth * 3; i += 3)
04141                 {
04142                     TEMPMAX(SrcBuffer[i], SrcBuffer[i+1], SrcBuffer[i+2]);
04143 
04144                     if (!PrintRGBBlackAsKey || Temp > 0)
04145                     {
04146                         Ink = 255 - SrcBuffer[i];           // Cyan ink is (255 - Red)
04147 
04148                         // Look up UCR(Key), which is UCR(255 - Temp), but the 255 is precalculated
04149                         // into the base address above, so we i with (-Temp)!
04150                         // Make Ink the ink value for the given plate. Crop at 0
04151                         if ((UINT32)Ink>(UINT32)UCR[-Temp])
04152                             Ink -= UCR[-Temp];
04153                         else
04154                             Ink = 0;
04155 
04156                         Ink = LUT[Ink];
04157 
04158                         DestBuffer[i] = DestBuffer[i+1] = DestBuffer[i+2] = 255 - Ink;
04159                     }
04160                     else
04161                     {
04162                         DestBuffer[i] = DestBuffer[i+1] = DestBuffer[i+2] = 255;
04163                     }
04164                 }
04165             }
04166             break;
04167 
04168 
04169         case COLOURPLATE_MAGENTA:
04170             {
04171                 BYTE *LUT = SepTables + 256;                // Magenta table is the 2nd table
04172 
04173                 for (i = 0; i < PixelWidth * 3; i += 3)
04174                 {
04175                     TEMPMAX(SrcBuffer[i], SrcBuffer[i+1], SrcBuffer[i+2]);
04176 
04177                     if (!PrintRGBBlackAsKey || Temp > 0)
04178                     {
04179                         Ink = 255 - SrcBuffer[i+1];         // Magenta ink is (255 - Green)
04180 
04181                         // Look up UCR(Key), which is UCR(255 - Temp), but the 255 is precalculated
04182                         // into the base address above, so we i with (-Temp)!
04183                         // Make Ink the ink value for the given plate. Crop at 0
04184                         if ((UINT32)Ink>(UINT32)UCR[-Temp])
04185                             Ink -= UCR[-Temp];
04186                         else
04187                             Ink = 0;
04188 
04189                         Ink = LUT[Ink];
04190 
04191                         DestBuffer[i] = DestBuffer[i+1] = DestBuffer[i+2] = 255 - Ink;
04192                     }
04193                     else
04194                     {
04195                         DestBuffer[i] = DestBuffer[i+1] = DestBuffer[i+2] = 255;
04196                     }
04197                 }
04198             }
04199             break;
04200 
04201 
04202         case COLOURPLATE_YELLOW:
04203             {
04204                 BYTE *LUT = SepTables + 512;                // Yellow table is the 3rd table
04205 
04206                 for (i = 0; i < PixelWidth * 3; i += 3)
04207                 {
04208                     TEMPMAX(SrcBuffer[i], SrcBuffer[i+1], SrcBuffer[i+2]);
04209 
04210                     if (!PrintRGBBlackAsKey || Temp > 0)
04211                     {
04212                         Ink = 255 - SrcBuffer[i+2];         // Yellow ink is (255 - Blue)
04213 
04214                         // Look up UCR(Key), which is UCR(255 - Temp), but the 255 is precalculated
04215                         // into the base address above, so we i with (-Temp)!
04216                         // Make Ink the ink value for the given plate. Crop at 0
04217                         if ((UINT32)Ink>(UINT32)UCR[-Temp])
04218                             Ink -= UCR[-Temp];
04219                         else
04220                             Ink = 0;
04221 
04222                         Ink = LUT[Ink];
04223 
04224                         DestBuffer[i] = DestBuffer[i+1] = DestBuffer[i+2] = 255 - Ink;
04225                     }
04226                     else
04227                     {
04228                         DestBuffer[i] = DestBuffer[i+1] = DestBuffer[i+2] = 255;
04229                     }
04230                 }
04231             }
04232             break;
04233 
04234 
04235         case COLOURPLATE_KEY:
04236             {
04237                 BYTE *BGT = SepTables + 1024;           // Black generation is the 5th table
04238 
04239                 for (i = 0; i < PixelWidth * 3; i += 3)
04240                 {
04241                     // Get the maximum of R,G,B into Temp
04242                     TEMPMAX(SrcBuffer[i], SrcBuffer[i+1], SrcBuffer[i+2]);
04243 
04244                     if (!PrintRGBBlackAsKey || Temp > 0)
04245                     {
04246                         // Key = 255 - Temp. Look up Key in the Black Generation table
04247                         Ink = BGT[255 - Temp];
04248 
04249                         DestBuffer[i] = DestBuffer[i+1] = DestBuffer[i+2] = 255 - Ink;
04250                     }
04251                     else
04252                     {
04253                         DestBuffer[i] = DestBuffer[i+1] = DestBuffer[i+2] = 0;
04254                     }
04255                 }
04256             }
04257             break;
04258 
04259 
04260         case COLOURPLATE_SPOT:
04261             // Deep colour bitmaps cannot include spot colours, so they always separate out
04262             // to white...
04263             for (i = 0; i < PixelWidth * 3; i += 3)
04264                 DestBuffer[i] = DestBuffer[i+1] = DestBuffer[i+2] = 255;
04265             break;
04266 
04267 
04268         default:
04269             ERROR3(_T("Unsupported separation plate!"));
04270             break;
04271     }
04272 
04273     // And finally, vape our helper macro
04274     #undef TEMPMAX
04275 }
04276 
04277 
04278 
04279 /********************************************************************************************
04280 
04281 >   SlowJobResult CamelotEPSRenderRegion::DrawMaskedBitmap(const DocRect &Rect, 
04282                                                           KernelBitmap* pBitmap, 
04283                                                           MaskedRenderRegion* pMask,
04284                                                           ProgressDisplay *Progress)
04285 
04286     Author:     Tim_Browse (Xara Group Ltd) <camelotdev@xara.com>
04287     Created:    5/5/95
04288     Inputs:     Point - the position to plot the bitmap
04289                 pBitmap - The bitmap that needs plotting
04290                 pMask - The mask render region to use to indicate which bits of pBitmap
04291                 needs to be plotted
04292 
04293     Purpose:    Plots the bitmap using the mask supplied.
04294 
04295     Notes:      
04296 
04297 ********************************************************************************************/
04298 
04299 SlowJobResult CamelotEPSRenderRegion::DrawMaskedBitmap(const DocRect &Rect, KernelBitmap* pBitmap, 
04300                                                        MaskedRenderRegion* pMask, 
04301                                                        ProgressDisplay *Progress)
04302 {
04303 #ifndef STANDALONE
04304 
04305     KernelDC *pDC = (KernelDC*)CCDC::ConvertFromNativeDC(RenderDC);
04306 
04307     // Make sure the world is in one piece
04308     if ((pBitmap==NULL) || (pMask==NULL))
04309         return SLOWJOB_FAILURE;
04310 
04311     // Are we doing actual EPS? If so, then we need to do some special stuff
04312     BOOL IsCamelotEPS = IS_A(this, CamelotEPSRenderRegion);
04313 
04314     // If mask coverage is 0% we do not need to do anything. The TRUE param indicates
04315     // that we don't care about exact coverage, which makes it return as soon as it realises
04316     // that there are pixel(s) that we'll have to plot
04317     if (pMask->FindCoverage(TRUE) == 0)
04318         return SLOWJOB_SUCCESS;
04319 
04320     // Guess we will need to be doing something...
04321     if (pBitmap->ActualBitmap == NULL)
04322         return SLOWJOB_FAILURE;
04323 
04324     // Get the 'Actual' windows Bitmap
04325     CWxBitmap *WinBM = (CWxBitmap*)pBitmap->ActualBitmap;
04326 
04327     // Is it valid ?
04328     if ((WinBM->BMInfo==NULL) || (WinBM->BMBytes==NULL))
04329         return SLOWJOB_FAILURE;
04330 
04331     // Remember the Size of the Bitmap (in pixels)
04332     INT32 Width  = WinBM->GetWidth();
04333     INT32 Height = WinBM->GetHeight();
04334 
04335     if ((Width == 0) || (Height == 0))
04336         // Error - bitmap has no dimension
04337         return SLOWJOB_FAILURE;
04338 
04339     // Hideous WINOILY code in the kernel. I get the feeling this was a rush job!
04340     // Convert the bitmap from a 32bpp bitmap to a 24bpp bitmap
04341     INT32 BitmapDepth = WinBM->GetBPP();
04342     if (BitmapDepth == 32)
04343     {
04344         // Can't plot 32bpp bitmaps to GDI as 16-bit GDI doesn't understand them,
04345         // so we convert to 24bpp bitmap in-situ and render that...
04346 
04347         // How many bytes to a source scanline?
04348         const INT32 ScanlineBytes = WinBM->GetScanlineSize();
04349 
04350         // How many bytes to a destination scanline
04351         const INT32 DestlineBytes = DIBUtil::ScanlineSize(Width, 24);
04352 
04353         // Now convert the bitmap in-situ
04354         LPBYTE OriginalBuffer  = WinBM->BMBytes;
04355         LPBYTE ConvertedBuffer = WinBM->BMBytes;
04356 
04357         INT32 i;
04358         for (i=0; i<Height; i++)
04359         {
04360             DIBUtil::Convert32to24(Width, OriginalBuffer, ConvertedBuffer);
04361             OriginalBuffer += ScanlineBytes;
04362             ConvertedBuffer += DestlineBytes;
04363         }
04364 
04365         // Update bitmap info to show it is now a 24bpp bitmap...
04366         WinBM->BMInfo->bmiHeader.biSizeImage = DestlineBytes * Height;
04367         WinBM->BMInfo->bmiHeader.biBitCount  = 24;
04368         BitmapDepth = 24;
04369 
04370         // Now swap the R and B bytes around as they are in BGR format in DIBS, and
04371         // we need RGB format for PostScript.
04372         OriginalBuffer = WinBM->BMBytes;
04373 
04374         for (i=0; i<Height; i++)
04375         {
04376             ConvertedBuffer = OriginalBuffer;
04377             for (INT32 j=0; j<Width; j++)
04378             {
04379                 // Swap R and B bytes around
04380                 BYTE Temp = ConvertedBuffer[0];
04381                 ConvertedBuffer[0] = ConvertedBuffer[2];
04382                 ConvertedBuffer[2] = Temp;
04383                 ConvertedBuffer += 3;
04384             }
04385 
04386             OriginalBuffer += DestlineBytes;
04387         }
04388     }
04389 
04390     // make sure we have a 24bpp bitmap
04391     ERROR3IF(BitmapDepth!=24, _T("Non 24bpp bitmap found in DrawMaskedBitmap"));
04392 
04393     // Work out the coords to blit to, taking into acount the differnt dpis of the
04394     // source and destination bitmaps.
04395     INT32 SrcDpi = pMask->FindMaskDpi();
04396     INT32 DestDpi = SrcDpi;
04397     BOOL UseLevel2 = FALSE;
04398 
04399     if (IsCamelotEPS)
04400     {
04401         // EPS is output in points, so it's always 72dpi.
04402         DestDpi = 72000;
04403 
04404         // Find out if we should do level 2 output.
04405         if (EPSFilter::XSEPSExportPSType == 2)
04406             UseLevel2 = TRUE;
04407     }
04408     else
04409     {
04410         // Find destination device resolution (e.g. PostScript printer)
04411         if (RenderDC!=NULL)
04412             DestDpi = RenderDC->GetPPI().GetWidth();
04413 //  WEBSTER-ranbirr-12/11/96
04414 #ifndef WEBSTER
04415         // If printing, then see if we should do level 2.
04416         if (IsPrinting())
04417         {
04418             // Get the print info for this job.
04419             CCPrintInfo *pInfo = CCPrintInfo::GetCurrent();
04420             if (pInfo != NULL)
04421             {
04422                 PrintControl *pPrCtrl = pInfo->GetPrintControl();
04423                 ERROR3IF(pPrCtrl == NULL, _T("Unexpected NULL pointer"));
04424 
04425                 if (pPrCtrl->GetPSLevel() == PSLEVEL_2)
04426                     // Printing to a level 2 device - party on!
04427                     UseLevel2 = TRUE;
04428             }
04429         }
04430 #endif  //webster
04431     }
04432 
04433     // Work out the ratio between to two
04434     double Ratio = DestDpi;
04435     Ratio = Ratio/SrcDpi;
04436     TRACEUSER( "Rik", _T("Src = %ld, Dest = %ld, Ratio = %f\n"), SrcDpi, DestDpi, Ratio);
04437 
04438     // Convert the DocCoord into a windows coord
04439     DocRect ClipRect = pMask->GetClipRect();
04440     POINT Origin;
04441 
04442     if (IsCamelotEPS)
04443     {
04444         // No GDI conversion needed for EPS - we need to convert to points later on.
04445         Origin.x = ClipRect.lo.x;
04446         Origin.y = ClipRect.lo.y;
04447     }
04448     else
04449     {
04450         // Usual GDI conversion (used for PostScript printers)
04451         WinRect WinClipRect = OSRenderRegion::DocRectToWin(RenderMatrix, ClipRect, DestDpi);
04452         Origin.x = WinClipRect.GetLeft();
04453         Origin.y = WinClipRect.GetBottom();
04454     }
04455 
04456     // Inform progress object how high this band is
04457     if (Progress!=NULL)
04458         Progress->StartBitmapPhaseBand(Height);
04459 
04460     // We need to create a tempory bitmap that we use to render each scan line
04461     LPBITMAPINFO    TempBitmapInfo = NULL;
04462     LPBYTE          TempBitmapBytes = NULL;
04463 
04464     // Get some memory for a tempory bmp 1 pixel high
04465     TempBitmapInfo = AllocDIB(Width, 1, BitmapDepth, &TempBitmapBytes);
04466     if (TempBitmapInfo==NULL)
04467         return SLOWJOB_FAILURE;
04468 
04469     // Get a buffer for RGB channel separation (for level 2) and colour separation
04470     BYTE *pRGBBuf = (BYTE *) CCMalloc(Width);
04471     ERROR2IF(pRGBBuf == NULL, SLOWJOB_FAILURE, _T("No memory for RGB buffer"));
04472 
04473     // How many bytes to a destination scanline
04474     INT32 ScanLineBytes = DIBUtil::ScanlineSize(Width, BitmapDepth);
04475     INT32  BytesPerPixel = 3;
04476 
04477     // Now convert the bitmap in-situ
04478     LPBYTE SrcBuffer  = WinBM->BMBytes;
04479     LPBYTE DestBuffer = TempBitmapBytes;
04480 
04481     // If we're colour separating, make sure we've got some sep tables to work with.
04482     // We cache them once we've used them. I would do this sort of set-up in StartRender
04483     // but of course all the derived classes just override that behaviour and we're stuffed
04484     if (SepTables == NULL && RenderView->GetColourPlate() != NULL && !RenderView->GetColourPlate()->IsDisabled())
04485     {
04486         // We are doing a colour separation - find the sep tables
04487 
04488         ColourContextCMYK *cc = (ColourContextCMYK *)RenderView->GetColourContext(COLOURMODEL_CMYK);
04489         if (cc != NULL)
04490         {
04491             SepTables = (BYTE *) CCMalloc(5 * 256 * sizeof(BYTE));
04492             if (SepTables != NULL)
04493             {
04494                 if (!cc->GetProfileTables(SepTables))
04495                 {
04496                     CCFree(SepTables);
04497                     SepTables = NULL;
04498                 }
04499             }
04500         }
04501 
04502         ERROR3IF(SepTables == NULL, _T("Can't generate separation tables in CamelotEPSRenderRegion"));
04503     }
04504 
04505     // Now copy the Bits from our Kernel Bitmap into the Memory DC, scan line at a time
04506     MaskRegion MaskInfo;
04507     pMask->GetFirstMaskRegion(&MaskInfo);
04508     try
04509     {
04510         while (MaskInfo.Length!=0)
04511         {
04512             // Calculate the source buffer address from the x and y position
04513             SrcBuffer = WinBM->BMBytes;
04514             SrcBuffer += ScanLineBytes * MaskInfo.y;
04515             SrcBuffer += MaskInfo.x*BytesPerPixel;
04516             INT32 RegionWidth = MaskInfo.Length;
04517 
04518             // Update bitmap info to show the new scan line size
04519             TempBitmapInfo->bmiHeader.biWidth = RegionWidth;
04520             TempBitmapInfo->bmiHeader.biSizeImage = (RegionWidth*BytesPerPixel);
04521 
04522             // copy the scan line into the temporary bmp
04523             if (SepTables == NULL)
04524             {
04525                 // We are not colour separating, so just do a quick memcpy
04526                 memcpy(DestBuffer, SrcBuffer, RegionWidth*BytesPerPixel);
04527             }
04528             else
04529             {
04530                 // We are doing some sort of colour separation, so copy the data across, colour
04531                 // separating it as we go
04532                 ERROR3IF(BytesPerPixel != 3, _T("non-24bpp scanline unsupported"));
04533                 ColourSeparateScanline24(DestBuffer, SrcBuffer, RegionWidth);
04534             }
04535 
04536             // Work out the coords of the destination rectangle
04537             INT32 DestX = Origin.x + INT32(ceil(MaskInfo.x*Ratio));
04538             INT32 DestY = Origin.y;
04539 
04540             // Y direction is different for our EPS and GDI PostScript, as GDI preserves
04541             // the Y +ve <=> downwards model of windows.
04542             if (IsCamelotEPS)
04543                 DestY += INT32(ceil((MaskInfo.y+1)*Ratio));
04544             else
04545                 DestY -= INT32(ceil((MaskInfo.y+1)*Ratio));
04546 
04547             INT32 DestWidth = INT32(ceil(RegionWidth*Ratio));
04548             INT32 DestHeight = INT32(ceil(1*Ratio));
04549 
04550             pDC->OutputToken(_T("sv"));
04551             pDC->OutputValue(RegionWidth);
04552             pDC->OutputToken(_T("1"));
04553 
04554 
04555             if (IsCamelotEPS)
04556             {
04557                 // Convert to points.
04558                 pDC->OutputUserSpaceValue(DestWidth);
04559                 pDC->OutputUserSpaceValue(DestHeight);
04560 
04561                 DocCoord Dest(DestX, DestY);
04562                 pDC->OutputCoord(Dest);
04563             }
04564             else
04565             {
04566                 // Pass straight through
04567                 pDC->OutputValue(DestWidth);
04568                 pDC->OutputValue(DestHeight);
04569                 pDC->OutputValue(DestX);
04570                 pDC->OutputValue(DestY);
04571             }
04572 
04573             // Tell the proc whether this is L1 or L2 compatible data
04574             if (UseLevel2)
04575                 pDC->OutputToken(_T("2"));
04576             else
04577                 pDC->OutputToken(_T("1"));
04578 
04579             // Rendering primitive.
04580             if (SepTables == NULL)
04581                 pDC->OutputToken(_T("cbsl"));       // 24-bpp colour scanline
04582             else
04583                 pDC->OutputToken(_T("gbsl"));       // 8-bpp greyscale scanline
04584 
04585 
04586             // Output bitmap data - format depends on whether we can do Level 2 or not
04587             if (UseLevel2)
04588             {
04589                 // Separate into 3 RGB streams (because it is much more likely to compress well
04590                 // with RLE if we compress each component individually), and send as RLE
04591                 // compressed ASCII85 data.
04592 
04593                 // If we're colour separating, then it's greyscale (R=G=B) data, so we just dump
04594                 // only the first channel to the gbsl operator
04595                 const INT32 NumStreams = (SepTables == NULL) ? 3 : 1;
04596 
04597                 for (INT32 i = 0; i < NumStreams; i++)
04598                 {
04599                     ERROR3IF(RegionWidth > Width, _T("I didn't allocate enough memory!"));
04600 
04601                     // Copy this component into the buffer
04602                     for (INT32 j = 0; j < RegionWidth; j++)
04603                     {
04604                         pRGBBuf[j] = TempBitmapBytes[(j * 3) + i];
04605                     }
04606 
04607                     // We are going to do ASCII85, so get ready for it.
04608                     if (!pDC->StartASCII85Output(TRUE))  // TRUE => RLE please
04609                         // Error
04610                         return SLOWJOB_FAILURE;
04611 
04612                     // Send to output stream as RLE encoded ASCII85 data.
04613                     /*INT32 nBytes =*/ pDC->OutputASCII85(pRGBBuf, RegionWidth);
04614 
04615                     if (!pDC->EndASCII85Output())
04616                         // Error
04617                         return SLOWJOB_FAILURE;
04618                 }
04619             }
04620             else
04621             {
04622                 // Just output as L1 ASCIIHex encoded data
04623                 if (SepTables != NULL)
04624                 {
04625                     // Colour separating, so reduce the 24-bit (R=G=B) greyscale to 8bpp by getting the _T("red") channel
04626                     for (INT32 j = 0; j < RegionWidth; j++)
04627                         pRGBBuf[j] = TempBitmapBytes[j * 3];
04628                     pDC->OutputRawBinary(pRGBBuf, RegionWidth, 1);
04629                 }
04630                 else
04631                 {
04632                     // Normal 24bpp colour output
04633                     pDC->OutputRawBinary(TempBitmapBytes, RegionWidth * 3, 1);
04634                 }
04635             }
04636 
04637             pDC->OutputToken(_T("rs"));
04638             pDC->OutputNewLine();
04639 
04640             // Update the progress display if necessary.
04641             if ((Progress!=NULL) && (!Progress->BitmapPhaseBandRenderedTo(MaskInfo.y)))
04642             {
04643                 // Free up the tempory DIB I made
04644                 FreeDIB(TempBitmapInfo, TempBitmapBytes);
04645 
04646                 return SLOWJOB_USERABORT;
04647             }
04648 
04649             // Find the next bit of scan line to plot
04650             pMask->GetNextMaskRegion(&MaskInfo);
04651         }
04652     }
04653     catch(CFileException)
04654     {
04655         FreeDIB(TempBitmapInfo, TempBitmapBytes);
04656 
04657         if (UseLevel2)
04658             CCFree(pRGBBuf);
04659 
04660         return SLOWJOB_FAILURE;
04661     }
04662 
04663 
04664     // Update bitmap info to what it was before we started
04665     TempBitmapInfo->bmiHeader.biSizeImage = (Width*3);
04666     TempBitmapInfo->bmiHeader.biWidth = Width;
04667 
04668     // Free up the tempory DIB I made
04669     FreeDIB(TempBitmapInfo, TempBitmapBytes);
04670 
04671     // Free up the buffer used for separating RGB components
04672     CCFree(pRGBBuf);
04673 #endif
04674 
04675     // All ok
04676     return SLOWJOB_SUCCESS;
04677 }
04678 
04679 
04680 
04681 /********************************************************************************************
04682 
04683 >   BOOL CamelotEPSRenderRegion::RenderChar(WCHAR ch, Matrix* pMatrix)
04684 
04685     Author:     Tim_Browse (Xara Group Ltd) <camelotdev@xara.com>
04686     Created:    30/4/95
04687     Inputs:     ch      - unicode value of char
04688                 pMatrix - matrix specifying transforms to place char correctly in document
04689     Returns:    FALSE if fails
04690     Purpose:    Render a character,
04691                 using the specified transform and current attributes in the render region.
04692 
04693 ********************************************************************************************/
04694 
04695 BOOL CamelotEPSRenderRegion::RenderChar(WCHAR ch, Matrix* pMatrix)
04696 {
04697 #ifndef STANDALONE
04698 
04699     // Should we render it as curves? (We do for non-ASCII characters)
04700     if ((ch > 255) || EPSFilter::XSEPSExportTextAsCurves)
04701         return RenderRegion::RenderChar(ch, pMatrix);
04702 
04703     // Make sure we have the right attributes for rendering as paths.
04704     GetValidPathAttributes();
04705 
04706     // get overall matrix - attribute matrix concatenated with given matrix if supplied
04707     Matrix matrix;
04708     if (GetCharAttributeMatrix(&matrix)==FALSE)
04709         return FALSE;
04710     if (pMatrix)
04711         matrix*=*pMatrix;
04712 
04713     // Can we do this using a GDI font?
04714     // We can if the matrix only specifies scaling and translation, with no aspect ratio.
04715     FIXED16 abcd[4];
04716     INT32   ef[2];
04717     matrix.GetComponents(abcd, ef);
04718 
04719     if ((abcd[1] == FIXED16(0)) && (abcd[2] == FIXED16(0)) && (abcd[0] == abcd[3]))
04720     {
04721         // Simple transformation - we can do this with an untransformed PostScript font
04722 
04723         // Work out required height of the font
04724         MILLIPOINT ReferenceSize = TextManager::GetDefaultHeight();
04725         MILLIPOINT Height = ReferenceSize * abcd[3];
04726 
04727         if (!SelectNewFont(RR_TXTFONTTYPEFACE(), RR_TXTBOLD(), RR_TXTITALIC(), Height))
04728         {
04729             // Could not select font
04730             return FALSE;
04731         }
04732     }
04733     else
04734     {
04735         // Transformation is complex - we need to use a matrix when selecting the font.
04736         if (!SelectNewFont(RR_TXTFONTTYPEFACE(), RR_TXTBOLD(), RR_TXTITALIC(), abcd))
04737         {
04738             // Could not select font
04739             return FALSE;
04740         }
04741     }
04742 
04743     // Render the character in the specified position...
04744     KernelDC *pKernelDC = (KernelDC*)CCDC::ConvertFromNativeDC(RenderDC);
04745 
04746     // Output character
04747 
04748     // Graham 5/8/96: Changed this to work with MBCS
04749     // Set up a string buffer for the output character
04750     char cBuffer[3];
04751 
04752     //Convert the character over to a MBCS index number
04753     UINT32 uiCharNumber = UnicodeManager::UnicodeToMultiByte(ch);
04754 
04755     //DecomposeMultiBytes takes that index number and creates a
04756     //two-byte character from it. We put that two-byte character
04757     //in cBuffer.
04758     UnicodeManager::DecomposeMultiBytes(uiCharNumber, (BYTE*) &cBuffer[0], (BYTE*) &cBuffer[1]);
04759     
04760     //And let's null-terminate cBuffer
04761     cBuffer[2]=0;
04762 
04763     //Now, if the character was a standard one-byte ASCII character, the ASCII
04764     //value will be in the second byte of cBuffer - cBuffer[1] - and cBuffer[0]
04765     //will be zero.
04766 
04767     //Note that we have set cBuffer[2] to zero. That means that cBuffer is null
04768     //terminated whether it contains a one or a two byte character.
04769 
04770     //So let's check if we've got a one-byte or a two-byte character. We do this
04771     //by saying...is cBuffer[0] zero?
04772 
04773     // First we copy it byte by byte into a TCHAR buffer
04774     TCHAR tcBuffer[3];
04775     tcBuffer[0]=cBuffer[0];
04776     tcBuffer[1]=cBuffer[1];
04777     tcBuffer[2]=cBuffer[2];
04778 
04779     if (tcBuffer[0]==0)
04780         //It's a standard ASCII character, one byte long.
04781         //So we only want to pass the second byte of cBuffer - cBuffer[1] -
04782         //to OutputString
04783         pKernelDC->OutputString(&tcBuffer[1]);
04784     else
04785         //The character is two bytes long (that is, it's a foreign character).
04786         //So we want to pass the whole of cBuffer - starting from cBuffer[0] -
04787         //to OutputString
04788         pKernelDC->OutputString(&tcBuffer[0]);
04789 
04790     //And that's the end of Graham's MBCS code. Now back to Tim.
04791 
04792     // Output position
04793     DocCoord DocPos(ef[0], ef[1]);
04794     pKernelDC->OutputCoord(DocPos);
04795 
04796     // Work out how to render the character..
04797     BOOL FlatFill = IS_A(CurrentAttrs[ATTR_FILLGEOMETRY].pAttr, FlatFillAttribute);
04798     BOOL Stroked = !RR_STROKECOLOUR().IsTransparent();
04799 
04800     // The character is filled if the fill is not a simple flat fill, or if it is,
04801     // but the fill colour is not transparent.
04802     BOOL Filled = !FlatFill || (!RR_FILLCOLOUR().IsTransparent());
04803 
04804     if (FlatFill && Filled && !Stroked)
04805     {
04806         // Simple flat filled text with no outline, so render normally.
04807         pKernelDC->OutputToken(_T("t"));
04808     }
04809     else
04810     {
04811         // Need to do something special...
04812         if (Stroked)
04813         {
04814             if (Filled)
04815                 // Stroke and fill it
04816                 pKernelDC->OutputToken(_T("tb"));
04817             else
04818                 // Just stroke it
04819                 pKernelDC->OutputToken(_T("ts"));
04820         }
04821         else
04822         {
04823             // Just fill it
04824             pKernelDC->OutputToken(_T("tf"));
04825         }
04826     }
04827 
04828 #endif
04829 
04830     return TRUE;
04831 }
04832 
04833 /********************************************************************************************
04834 
04835 >   virtual void CamelotEPSRenderRegion::ConditionalSuicide ( void )
04836 
04837     Author:     Graeme_Sutherland (Xara Group Ltd) <camelotdev@xara.com>
04838     Created:    24/2/00
04839     Inputs:     -
04840     Returns:    -
04841     Purpose:    Doesn't delete the object when invoked. This is to get around using a few
04842                 if IS_A calls elsewhere in Camelot.
04843 
04844 ********************************************************************************************/
04845 
04846 void CamelotEPSRenderRegion::ConditionalSuicide ( void )
04847 {
04848     // Don't delete this!
04849 }
04850 
04851 /********************************************************************************************
04852 
04853 >   virtual BOOL CamelotEPSRenderRegion::NeedsPrintComponents ( void )
04854 
04855     Author:     Graeme_Sutherland (Xara Group Ltd) <camelotdev@xara.com>
04856     Created:    24/2/00
04857     Inputs:     -
04858     Returns:    TRUE - the print components are needed.
04859     Purpose:    Lets the print component class know that they are needed in this format's
04860                 export.
04861 
04862 ********************************************************************************************/
04863 
04864 BOOL CamelotEPSRenderRegion::NeedsPrintComponents ( void )
04865 {
04866     // We want print components!
04867     return TRUE;
04868 }
04869 
04870 /********************************************************************************************
04871 
04872 >   BOOL CamelotEPSRenderRegion::SelectNewFont ( WORD       Typeface,
04873                                                  BOOL       Bold,
04874                                                  BOOL       Italic,
04875                                                  MILLIPOINT Size )
04876 
04877     Author:     Tim_Browse (Xara Group Ltd) <camelotdev@xara.com>
04878     Created:    30/4/95
04879     Inputs:     Typeface    - The font's ID.
04880                 Bold        - Is it bold?
04881                 Italic      - Is it italic?
04882                 Size        - The font size in millipoints.
04883     Returns:    TRUE        - Success.
04884                 FALSE       - Failure / an error occured.
04885     Purpose:    Get the font name being used by the Camelot EPS exporter.
04886 
04887 ********************************************************************************************/
04888 
04889 BOOL CamelotEPSRenderRegion::SelectNewFont ( WORD       Typeface,
04890                                              BOOL       Bold,
04891                                              BOOL       Italic,
04892                                              MILLIPOINT Size )
04893 {
04894 #ifndef STANDALONE
04895     // BODGE: This is mingled with horrible OIL code that should be moved to the OIL RSN...
04896 
04897     // Check to see if it is cached
04898     if ((FontInfo.Valid && !FontInfo.Complex) &&
04899         (FontInfo.Typeface == Typeface) &&
04900         (FontInfo.Bold == Bold) &&
04901         (FontInfo.Italic == Italic) &&
04902         (FontInfo.Size == Size))
04903     {
04904         // It is the same as the currently selected/cached font
04905         TRACEUSER( "Tim", _T("Using the cached font\n"));
04906         return TRUE;
04907     }
04908 
04909     // Cache is invalid - map the TrueType name to a PostScript font.
04910 
04911     // Get the typeface name of the font
04912     String_64 FontName;
04913     if (!FONTMANAGER->GetFontName(Typeface, FontName))
04914         return FALSE;
04915 
04916     // Transform to a INI-file friendly format
04917     String_64 Encoded;
04918     INT32 Styles = 0;
04919     if (Bold)
04920         Styles += 1;
04921     if (Italic)
04922         Styles += 2;
04923     FONTMANAGER->EncodeFontName(FontName, Encoded, Styles);
04924 
04925     // Look up font in INI file.
04926     String_256 MappedFontName;
04927     if (!Camelot.GetPrefDirect(_T("EPSFontMapping"), (TCHAR *) Encoded, &MappedFontName))
04928     {
04929         // No preference - map to Times-Roman variant
04930         if (Bold)
04931         {
04932             if (Italic)
04933                 MappedFontName.Load(_R(IDS_K_CAMEPS_MAPFNAMEBI));
04934             else
04935                 MappedFontName.Load(_R(IDS_K_CAMEPS_MAPFNAMEB));
04936         }
04937         else
04938         {
04939             if (Italic)
04940                 MappedFontName.Load(_R(IDS_K_CAMEPS_MAPFNAMEI));
04941             else
04942                 MappedFontName.Load(_R(IDS_K_CAMEPS_MAPFNAME));
04943         }           
04944     }
04945 
04946     // Due to inaccuracies, point sizes like 16pt come out as 15.999pt, so we check
04947     // for trailing digit errors and round to nearest integer point size if necessary.
04948     // (NB we have to use another variable for this otherwise the cache values don't match)
04949     MILLIPOINT CorrectedSize = Size;
04950     MILLIPOINT Remainder = Size % 1000;
04951     if ((Remainder <= 2) || (Remainder >= 998))
04952         // Lose the inaccuracy.
04953         CorrectedSize = ((Size + 500) / 1000) * 1000;
04954 
04955     // Select this font by prefixing with a forward slash to make it into a name.
04956     KernelDC *pKernelDC = (KernelDC*)CCDC::ConvertFromNativeDC(RenderDC);
04957     String_8 Slash(TEXT("/"));
04958     MappedFontName.Insert(Slash, 0);
04959     pKernelDC->OutputToken((TCHAR *) MappedFontName);
04960     pKernelDC->OutputUserSpaceValue(CorrectedSize);
04961     pKernelDC->OutputToken(_T("sf"));
04962 
04963     // Font cache is now valid
04964     FontInfo.Valid    = TRUE;
04965     FontInfo.Complex  = FALSE;
04966     FontInfo.Typeface = Typeface;
04967     FontInfo.Bold     = Bold;
04968     FontInfo.Italic   = Italic;
04969     FontInfo.Size     = Size;
04970     
04971 #endif
04972 
04973     // All ok
04974     return TRUE;    
04975 }
04976 
04977 
04978 
04979 BOOL CamelotEPSRenderRegion::SelectNewFont(WORD Typeface, BOOL Bold, BOOL Italic, FIXED16 *abcd)
04980 {
04981 #ifndef STANDALONE
04982 
04983     // BODGE: This is mingled with horrible OIL code that should be moved to the OIL RSN...
04984 
04985     // Check to see if it is cached
04986     if ((FontInfo.Valid && FontInfo.Complex) &&
04987         (FontInfo.Typeface == Typeface) &&
04988         (FontInfo.Bold == Bold) &&
04989         (FontInfo.Italic == Italic) &&
04990         (FontInfo.a == abcd[0]) &&
04991         (FontInfo.b == abcd[1]) &&
04992         (FontInfo.c == abcd[2]) &&
04993         (FontInfo.d == abcd[3]))
04994     {
04995         // It is the same as the currently selected/cached font
04996         TRACEUSER( "Tim", _T("Using the cached font\n"));
04997         return TRUE;
04998     }
04999 
05000     // Cache is invalid - map the TrueType name to a PostScript font.
05001 
05002     // Get the typeface name of the font
05003     //  ENUMLOGFONT *pEnumLogFont = TextInfoBarOp::GetCachedLogFont(Typeface);
05004     //  String_64 FontName(pEnumLogFont->elfLogFont.lfFaceName);
05005     // Changed by Mike (2/8/95)
05006     String_64 FontName;
05007     if (!FONTMANAGER->GetFontName(Typeface, FontName))
05008         return FALSE;
05009 
05010     // Transform to a INI-file friendly format
05011     String_64 Encoded;
05012     INT32 Styles = 0;
05013     if (Bold)
05014         Styles += 1;
05015     if (Italic)
05016         Styles += 2;
05017     FONTMANAGER->EncodeFontName(FontName, Encoded, Styles);
05018 
05019     // Look up font in INI file.
05020     String_256 MappedFontName;
05021     if (!Camelot.GetPrefDirect(_T("EPSFontMapping"), (TCHAR *) Encoded, &MappedFontName))
05022     {
05023         // No preference - map to Times-Roman variant
05024         if (Bold)
05025         {
05026             if (Italic)
05027                 MappedFontName.Load(_R(IDS_K_CAMEPS_MAPFNAMEBI2));
05028             else
05029                 MappedFontName.Load(_R(IDS_K_CAMEPS_MAPFNAMEB2));
05030         }
05031         else
05032         {
05033             if (Italic)
05034                 MappedFontName.Load(_R(IDS_K_CAMEPS_MAPFNAMEI2));
05035             else
05036                 MappedFontName.Load(_R(IDS_K_CAMEPS_MAPFNAME2));
05037         }           
05038     }
05039 
05040     // Select this font by prefixing with a forward slash to make it into a name.
05041     KernelDC *pKernelDC = (KernelDC*)CCDC::ConvertFromNativeDC(RenderDC);
05042     String_8 Slash(TEXT("/"));
05043     MappedFontName.Insert(Slash, 0);
05044     pKernelDC->OutputToken((TCHAR *) MappedFontName);
05045 
05046     // Get the default text height, and work out how to scale to PostScript's default of
05047     // 1 user space unit (i.e. 1 pt)
05048     double ScaleFactor = TextManager::GetDefaultHeight() / 1000.0;
05049     TCHAR MatrixBuf[100];
05050     camSprintf(MatrixBuf, _T("[%.3f %.3f %.3f %.3f 0 0]"), 
05051             (double) (abcd[0].MakeDouble() * ScaleFactor),
05052             (double) (abcd[1].MakeDouble() * ScaleFactor),
05053             (double) (abcd[2].MakeDouble() * ScaleFactor),
05054             (double) (abcd[3].MakeDouble() * ScaleFactor));
05055 
05056     pKernelDC->OutputToken(MatrixBuf);
05057     
05058     // Select the complex font
05059     pKernelDC->OutputToken(_T("sf"));
05060 
05061     // Font cache is now valid
05062     FontInfo.Valid    = TRUE;
05063     FontInfo.Complex  = TRUE;
05064     FontInfo.Typeface = Typeface;
05065     FontInfo.Bold     = Bold;
05066     FontInfo.Italic   = Italic;
05067     FontInfo.a = abcd[0];
05068     FontInfo.b = abcd[1];
05069     FontInfo.c = abcd[2];
05070     FontInfo.d = abcd[3];
05071 
05072 #endif
05073     
05074     // All ok
05075     return TRUE;    
05076 }
05077 
05078 
05079 /********************************************************************************************
05080 
05081 >   BOOL CamelotEPSRenderRegion::DrawTransformedBitmap(NodeBitmap *pNodeBitmap))
05082 
05083     Author:     Tim_Browse (Xara Group Ltd) <camelotdev@xara.com>
05084     Created:    28/6/95
05085     Inputs:     pNodeBitmap - the bitmap node to render!
05086     Returns:    TRUE if the bitmap could be rendered as specified;
05087                 FALSE if it could not be (and hence wasn't).
05088     Purpose:    Plot an arbitrarily transformed bitmap using a matrix and an offset.
05089                 The bitmap is assumed to occupy a rectangle at the origin, which has pixels 1 
05090                 point across, and the matrix supplied transforms this area to the correct 
05091                 place.  The Point parameter supplies an optional offset which can be applied 
05092                 after the transform.
05093                 If the offset is 0,0, this means there is no offset (surprise, surprise!)
05094     SeeAlso:    NodeBitmap::Render
05095 
05096 ********************************************************************************************/
05097 
05098 BOOL CamelotEPSRenderRegion::DrawTransformedBitmap(NodeBitmap *pNodeBitmap)
05099 {
05100     // If we are not drawing complex shapes and this shape is, then return
05101     if ((!RenderComplexShapes) && (TestForComplexShape(&Caps)))
05102         return TRUE;
05103 
05104     if ((!RenderComplexShapes) && (pNodeBitmap->NeedsTransparency()))
05105         return TRUE;
05106 
05107     DocCoord *Coords = pNodeBitmap->InkPath.GetCoordArray();
05108 
05109     // Get the 'actual' OIL Bitmap
05110     OILBitmap *TheBitmap = pNodeBitmap->GetBitmap()->ActualBitmap;
05111 
05112     // Is it contoned?
05113     DocColour *StartCol = NULL, 
05114               *EndCol =   NULL;
05115 
05116     if (!RR_STROKECOLOUR().IsTransparent())
05117     {
05118         // It has a line colour, so it is contoned!
05119         StartCol = &(RR_STROKECOLOUR());
05120         EndCol   = &(RR_FILLCOLOUR());
05121     }
05122 
05123     // Render it
05124     return DrawParallelogramBitmap(Coords, TheBitmap, GetFillEffect(), StartCol, EndCol);
05125 }
05126 
05127 
05128 /********************************************************************************************
05129 
05130 >   BOOL CamelotEPSRenderRegion::DrawParallelogramBitmap(DocCoord *Coords, 
05131                                                          OILBitmap *pBitmap,
05132                                                          EFFECTTYPE Effect = EFFECT_RGB,
05133                                                          DocColour *StartCol = NULL, 
05134                                                          DocColour *EndCol = NULL)
05135 
05136     Author:     Tim_Browse (Xara Group Ltd) <camelotdev@xara.com>
05137     Created:    3/7/95
05138     Inputs:     Coords - the parallelogram to plot the bitmap into (should be an array of
05139                          4 co-ordinates)
05140                 pBitmap - the bitmap to render!
05141                 Effect - if contone colours are supplied, this is how the colours should fade.
05142                 StartCol, EndCol - optional colours, if non-NULL, this indicates that the
05143                                    bitmap is a contoned bitmap and should be rendered
05144                                    using these colours.
05145     Returns:    TRUE if the bitmap could be rendered as specified;
05146                 FALSE if it could not be (and hence wasn't).
05147     Purpose:    Plot an arbitrarily transformed bitmap in the position specified.
05148 
05149                 For a normal untransformed rectangle, the Coords array should start specify
05150                 the parallelogram as follows:
05151 
05152                     Coords[0]       Coords[1]
05153                        +---------------+
05154                        |               |
05155                        |               |
05156                        |               |
05157                        |               |
05158                        +---------------+
05159                     Coords[3]       Coords[2]
05160 
05161     SeeAlso:    NodeBitmap::Render; CamelotEPSRenderRegion::DrawTransformedBitmap
05162 
05163 ********************************************************************************************/
05164 
05165 BOOL CamelotEPSRenderRegion::DrawParallelogramBitmap(DocCoord *Coords, OILBitmap *pBitmap,
05166                                                      EFFECTTYPE Effect,
05167                                                      DocColour *StartCol, DocColour *EndCol)
05168 {
05169     // If we are not drawing complex shapes and this shape is, then return
05170     if ((!RenderComplexShapes) && (TestForComplexShape(&Caps)))
05171         return TRUE;
05172 
05173     // If it's contoned between two no-colours, it is invisible (on this plate) so don't render anything
05174     if (StartCol != NULL && StartCol->IsTransparent() && EndCol != NULL && EndCol->IsTransparent())
05175         return(TRUE);
05176 
05177     // Get information about the bitmap to be rendered.
05178     BitmapInfo BMInfo;
05179     if (!pBitmap->GetInfo(&BMInfo))
05180     {
05181         ERROR3("Bitmap GetInfo failed");
05182         // Error getting bitmap info
05183         return FALSE;
05184     }
05185 
05186     // Remember the Size of the Bitmap (in pixels)
05187     INT32 Width  = BMInfo.PixelWidth;
05188     INT32 Height = BMInfo.PixelHeight;
05189 
05190     if ((Width == 0) || (Height == 0))
05191     {
05192         // Error - bitmap has no dimension
05193         ERROR3("Bitmap width/height is 0");
05194         return FALSE;
05195     }
05196 
05197     // Find out if this is real renderable EPS.
05198     BOOL IsCamelotEPS = IS_A(this, CamelotEPSRenderRegion);
05199 
05200     // Is it contoned?
05201     BOOL IsContoned = (StartCol != NULL) && (EndCol != NULL);
05202     if (IsContoned)
05203     {
05204         if (pBitmap->GetGreyscaleVersion() != NULL)
05205         {
05206             pBitmap = pBitmap->GetGreyscaleVersion();
05207             if (!pBitmap->GetInfo(&BMInfo))
05208             {
05209                 ERROR3("Bitmap GetInfo failed");
05210                 // Error getting bitmap info
05211                 return FALSE;
05212             }
05213         }
05214     }
05215 
05216     // Find out if we can use Level 2 features.
05217     BOOL UseLevel2 = FALSE;
05218 
05219     if (IsCamelotEPS)
05220     {
05221         // Use the EPS preference.
05222         if (EPSFilter::XSEPSExportPSType == 2)
05223             UseLevel2 = TRUE;
05224     }
05225 //  WEBSTER-ranbirr-12/11/96
05226 #ifndef WEBSTER
05227     else if (IsPrinting())
05228     {
05229 #ifndef STANDALONE
05230         // Get the print info for this job.
05231         CCPrintInfo *pInfo = CCPrintInfo::GetCurrent();
05232         if (pInfo != NULL)
05233         {
05234             PrintControl *pPrCtrl = pInfo->GetPrintControl();
05235             if (pPrCtrl->GetPSLevel() == PSLEVEL_2)
05236                 // Printing to a level 2 device - party on!
05237                 UseLevel2 = TRUE;
05238         }
05239 #else
05240     ERROR2(FALSE,"CamelotEPSRenderRegion::DrawParallelogramBitmap trying to print in Viewer version!")
05241 #endif
05242     }
05243 
05244 #endif //webster
05245 
05246     // Work out the coords of the destination rectangle
05247 //  INT32 DestX = 0;
05248 //  INT32 DestY = 0;
05249 
05250     KernelDC *pDC = (KernelDC*)CCDC::ConvertFromNativeDC(RenderDC);
05251 
05252     pDC->OutputToken(_T("sv"));
05253 
05254     // Dimensions of bitmap
05255     pDC->OutputValue(Width);
05256     pDC->OutputValue(Height);
05257 
05258     // Number of bits per component sample
05259     INT32 BytesPerScanline;
05260 
05261     // Are we colour separating? Get SepTables if we need them
05262     // NOTE that we get them even if we have a <= 8bpp bitmap, as the pointer is also used 
05263     // as a "we are separating" flag. Note also that we don't try to separate contoned bitmaps
05264     // via the normal system- we just let the old contone bitmap code output it, and separate the palette
05265     if (!IsContoned && SepTables == NULL && RenderView->GetColourPlate() != NULL)
05266     {
05267         ColourContextCMYK *cc = (ColourContextCMYK *)RenderView->GetColourContext(COLOURMODEL_CMYK);
05268         if (cc != NULL)
05269         {
05270             SepTables = (BYTE *) CCMalloc(5 * 256 * sizeof(BYTE));
05271             if (SepTables != NULL)
05272             {
05273                 if (!cc->GetProfileTables(SepTables))
05274                 {
05275                     CCFree(SepTables);
05276                     SepTables = NULL;
05277                 }
05278             }
05279         }
05280 
05281         ERROR3IF(SepTables == NULL, "Can't generate separation tables in CamelotEPSRenderRegion");
05282     }
05283 
05284     if (SepTables != NULL && !IsContoned)
05285     {
05286         // ALL colour separated bitmaps go out as simple 8bpp greyscales (yay! no special cases!)
05287         pDC->OutputValue((INT32)8);
05288         BytesPerScanline = Width;
05289     }
05290     else
05291     {
05292         // It's not colour separated, so output the right values for the bitmap's BPP.
05293         switch (BMInfo.PixelDepth)
05294         {
05295             case 1:
05296             case 8:
05297                 pDC->OutputValue((INT32) BMInfo.PixelDepth);
05298                 BytesPerScanline = ((BMInfo.PixelDepth * Width) + 7) / 8;   // Round 1bpp up to multiple of bytes
05299                 break;
05300 
05301             case 4:
05302                 // 4bpp images get converted to 8bpp because Adobe SDK image library can't
05303                 // do 4bpp on some PS printers (e.g. Canon LBP4-PS).
05304                 pDC->OutputValue((INT32) 8);
05305                 BytesPerScanline = Width;
05306                 break;
05307 
05308             case 24:
05309             case 32:            // 32bpp images are output as 24bpp to postscript
05310                 // Output will be standard 24bpp
05311                 pDC->OutputValue((INT32) 24);
05312                 BytesPerScanline = 3 * Width;
05313                 break;
05314 
05315             default:
05316                 // Unknown bitmap depth
05317                 ERROR3("Unsupported bitmap depth in CamelotEPSRenderRegion::DrawParallelogramBitmap");
05318                 return FALSE;
05319         }
05320     }
05321 
05322     // Now, output how many bytes per scanline will be used.
05323     pDC->OutputValue(BytesPerScanline);
05324 
05325     // Now output the matrix - colorimage expects the inverse of this matrix, i.e.
05326     // it needs the matrix which maps the destination area to the 1-1 rectangle at
05327     // the origin (cos PostScript is weird like that).
05328     pDC->OutputToken(_T("["));
05329 
05330     // NB. We have to do some 'bespoke' matrix maths here, because we need to work
05331     //     in points, and our Matrix class is not accurate enough (notably the e and 
05332     //     f components are integer only, and we need fractional points).
05333     double a[2],b[2],c[2],d[2], e[2], f[2];
05334 
05335     // Find out the size of one unit, in MILLIPOINTS.
05336     double UnitSize;
05337     if (IsCamelotEPS)
05338     {
05339         // Unit is 1000 millipoints, as we always use 1 point as unit in user space
05340         // in Camelot EPS.
05341         UnitSize = 1000;
05342     }
05343     else
05344     {
05345         // Find the size of a pixel, because Windows maps a unit to a pixel.
05346         FIXED16 fxPixelSize;
05347         RenderView->GetPixelSize(&fxPixelSize, &fxPixelSize);
05348         UnitSize = fxPixelSize.MakeDouble();
05349     }
05350 
05351     // Work out the width and height of the bitmap, assuming each pixel corresponds to
05352     // one unit.
05353     double dWidth = (double) Width;
05354     double dHeight = (double) Height;
05355 
05356     DocCoord DCOrigin = pDC->GetOrigin();
05357 
05358     if (IsCamelotEPS)
05359     {
05360         dWidth *= UnitSize;
05361         dHeight *= UnitSize;
05362         a[0] = ((double) (Coords[2].x - Coords[3].x)) / dWidth;
05363         b[0] = ((double) (Coords[2].y - Coords[3].y)) / dWidth;
05364         c[0] = ((double) (Coords[0].x - Coords[3].x)) / dHeight;
05365         d[0] = ((double) (Coords[0].y - Coords[3].y)) / dHeight;
05366         e[0] = ((double) (Coords[3].x - DCOrigin.x)) / UnitSize;
05367         f[0] = ((double) (Coords[3].y - DCOrigin.y)) / UnitSize;
05368     }
05369     else
05370     {
05371         ERROR3IF(!pDC->IsKindOf(CC_RUNTIME_CLASS(PSPrintDC)), "KernelDC is not a PSPrintDC - how did that happen?");
05372         // Printing to PostScript - convert to Window co-ordinates, using the DC.
05373         PSPrintDC *pPSDC = (PSPrintDC *) pDC;
05374 
05375         WinCoord PSCoords[3];
05376         for (INT32 i = 0; i <= 3; i++)
05377             PSCoords[i] = pPSDC->TransformCoord(Coords[i]);
05378 
05379         a[0] = ((double) (PSCoords[2].x - PSCoords[3].x)) / dWidth;
05380         b[0] = ((double) (PSCoords[2].y - PSCoords[3].y)) / dWidth;
05381         c[0] = ((double) (PSCoords[0].x - PSCoords[3].x)) / dHeight;
05382         d[0] = ((double) (PSCoords[0].y - PSCoords[3].y)) / dHeight;
05383         e[0] = (double) PSCoords[3].x;
05384         f[0] = (double) PSCoords[3].y;
05385     }
05386 
05387     // Calculate the inverse of the above.
05388     double Det = a[0] * d[0] - b[0] * c[0];
05389 
05390     if (Det==0.0)
05391     {
05392         // There is no inversion of this matrix
05393         // return the identity matrix
05394         ERROR3("Matrix Inversion Failed - Tried to Invert a non-invertable matrix" );
05395         return FALSE;
05396     }
05397 
05398     // this section calculates the inverse of the matrix. As it takes into
05399     // account that our 3x3 matrix always has 0,0,1 down its right hand side
05400     // it has been greatly simplified. The operations combine the calculation
05401     // of the adjoint matrix and scaling it by the Determinent with a matrix
05402     // transpose (the inverse is the transpose of the adjoint scaled by the 
05403     // determinent)
05404     a[1] =  d[0] / Det;
05405     b[1] = -b[0] / Det;
05406     c[1] = -c[0] / Det;
05407     d[1] =  a[0] / Det;
05408     e[1] =   ((c[0] * f[0]) - (e[0] * d[0])) / Det ;
05409     f[1] = -(((a[0] * f[0]) - (e[0] * b[0])) / Det);
05410 
05411     pDC->OutputReal(a[1]);
05412     pDC->OutputReal(b[1]);
05413     pDC->OutputReal(c[1]);
05414     pDC->OutputReal(d[1]);
05415 
05416     if (IsCamelotEPS)
05417     {
05418         // Convert to points.
05419         pDC->OutputUserSpaceValue((MILLIPOINT) (e[1] * 1000.0));
05420         pDC->OutputUserSpaceValue((MILLIPOINT) (f[1] * 1000.0));
05421     }
05422     else
05423     {
05424         // Pass straight through
05425         pDC->OutputReal(e[1]);
05426         pDC->OutputReal(f[1]);
05427     }
05428 
05429     pDC->OutputToken(_T("]"));
05430 
05431     // Default values for smoothing and polarity
05432     pDC->OutputToken(_T("false"));
05433     pDC->OutputToken(_T("false"));
05434 
05435     // Normally we use ASCII Hex encoding, but for Level 2 specific output, we use ASCII85.
05436     if (UseLevel2)
05437         // Run length encoded and ASCII85 encoded
05438         pDC->OutputToken(_T("3"));
05439     else
05440         // Simple level 1 compatible ASCII Hex encoding.
05441         pDC->OutputToken(_T("1"));
05442 
05443     // Set up the image procs
05444     pDC->OutputToken(_T("beginimage"));
05445 
05446     if (SepTables != NULL && !IsContoned)
05447     {
05448         // If we are colour-separating, then output always goes out as an 8bpp greyscale image
05449         // This may seem like a gross thing to do (especially for 1/4bpp), but as 4bpp is always
05450         // expanded to 8bpp anyway, it is much better to treat all images in a nice generic manner
05451         // and save ourselves lots of work, lots of confusion, and the potential for lots of
05452         // special cases which don't work (for examples of code like this, see the old composite
05453         // printing code below).
05454         if (!pBitmap->ExportSeparatedPalette(this))
05455         {
05456             ERROR3("ExportSeparatedPalette failed");
05457             return FALSE;
05458         }
05459 
05460         pDC->OutputToken(_T("doclutimage"));        // Colour separation to 8bpp greyscale image
05461 
05462         pDC->OutputNewLine();
05463 
05464         // Output bitmap (scanline) data as an 8bpp greyscale
05465         if (!pBitmap->ExportSeparatedData(this, SepTables))
05466         {
05467             ERROR3("ExportSeparatedData failed");
05468             return FALSE;
05469         }
05470     }
05471     else
05472     {
05473         // We've specified the size, depth and position of the bitmap, so now we work 
05474         // out which PostScript procedure we should render the image with.
05475         switch (BMInfo.PixelDepth)
05476         {
05477             case 1:
05478                 // It's a 1bpp image - should we output a palette with it?
05479                 if (IsContoned)
05480                 {
05481                     // (Jason here: I believe the postscript routine 1bitcopyimage doesn't work.
05482                     // This is probably OK, as I think all contoned bitmaps in camelot are made 8bpp.
05483                     // I've left this code in place, however, just in case it would break something)
05484 
05485                     // Convert colours to RGB and output for use with the special
05486                     // 1bpp contone operator.
05487                     Document *pDoc = RenderView->GetDoc();
05488                     if (pDoc == NULL)
05489                     {
05490                         ERROR3("No document when rendering");
05491                         // Panic!
05492                         return FALSE;
05493                     }
05494 
05495                     // Use the output context to convert the colours, and then output them...
05496                     ColourContext *pContext = CurrentColContext;
05497                     if (pContext == NULL)
05498                     {
05499                         ERROR3("No Colour context when rendering");
05500                         return(FALSE);      // Panic!
05501                     }
05502 
05503                     ERROR3IF(pContext->GetColourModel() != COLOURMODEL_RGBT,
05504                                 "RGB output context expected in EPS rendering");
05505                     ColourRGBT Result;
05506 
05507                     if (IsContoned)
05508                         pContext->ConvertColour((DocColour *) StartCol, (ColourGeneric *)&Result);
05509                     else
05510                     {
05511                         DocColour Black(0L, 0L, 0L);
05512                         pContext->ConvertColour(&Black, (ColourGeneric *)&Result);
05513                     }
05514 
05515                     pDC->OutputReal(Result.Red.MakeDouble());
05516                     pDC->OutputReal(Result.Green.MakeDouble());
05517                     pDC->OutputReal(Result.Blue.MakeDouble());
05518 
05519                     if (IsContoned)
05520                         pContext->ConvertColour((DocColour *) EndCol, (ColourGeneric *)&Result);
05521                     else
05522                     {
05523                         DocColour White(255L, 255L, 255L);
05524                         pContext->ConvertColour(&White, (ColourGeneric *)&Result);
05525                     }
05526 
05527                     pDC->OutputReal(Result.Red.MakeDouble());
05528                     pDC->OutputReal(Result.Green.MakeDouble());
05529                     pDC->OutputReal(Result.Blue.MakeDouble());
05530 
05531                     // Use special colour 1bpp operator
05532                     pDC->OutputToken(_T("1bitcopyimage"));
05533                 }
05534                 else
05535                 {
05536                     // Normal black and white image
05537                     pDC->OutputToken(_T("1bitbwcopyimage"));
05538                 }
05539                 break;
05540 
05541             case 4:
05542             case 8:
05543                 if (IsContoned)
05544                 {
05545                     // Contoned bitmap - generate and output the contone palette.
05546                     if (!pBitmap->BuildContonePalette(*StartCol, *EndCol, Effect, RenderView))
05547                     {
05548                         ERROR3("Contoned palette failed");
05549                         // Error while building palette (out of memory?)
05550                         return FALSE;
05551                     }
05552 
05553                     BOOL Success = pBitmap->ExportContonePalette(this);
05554 
05555                     // Clean up contone palette
05556                     pBitmap->DestroyContonePalette();
05557                 
05558                     // Did it work?
05559                     if (!Success)
05560                     {
05561                         ERROR3("Contoned palette export failed");
05562                         return FALSE;
05563                     }
05564 
05565                     // Use the procedure for rendering paletted images.
05566                     pDC->OutputToken(_T("doclutimage"));
05567                 }
05568                 else if (BMInfo.NumPaletteEntries > 0)
05569                 {
05570                     // It's a paletted image - export the palette
05571                     if (!pBitmap->ExportBitmapPalette(this))
05572                     {
05573                         ERROR3("ExportBitmapPalette failed");
05574                         return FALSE;
05575                     }
05576 
05577                     // Use the procedure for rendering paletted images.
05578                     pDC->OutputToken(_T("doclutimage"));
05579                 }
05580                 else
05581                 {
05582                     // It's a greyscale image - use the image operator.
05583                     // (Jason here: I reckon the postscript for doimage doesn't work. This is
05584                     // probably OK, because camelot can't theoretically have a non-paletted image?)
05585                     pDC->OutputToken(_T("doimage"));
05586                 }
05587                 break;
05588 
05589             case 24:
05590             case 32:                                    // 32bpp is output as 24bpp data
05591                 pDC->OutputToken(_T("do24image"));          // Normal composite colour 24bpp image
05592                 break;
05593 
05594             default:
05595                 // Unknown bitmap depth
05596                 ERROR3("Unsupported bitmap depth");
05597                 return FALSE;
05598         }
05599 
05600         pDC->OutputNewLine();
05601 
05602         // Output bitmap (scanline) data
05603         if (!pBitmap->ExportBitmapData(this))
05604         {
05605             ERROR3("ExportBitmapData failed");
05606             return FALSE;
05607         }
05608     }
05609 
05610     // Clean up image stuff
05611     pDC->OutputNewLine();
05612     pDC->OutputToken(_T("endimage"));
05613 
05614     pDC->OutputToken(_T("rs"));
05615     pDC->OutputNewLine();
05616 
05617     return TRUE;
05618 }
05619 
05620 
05621 /********************************************************************************************
05622 
05623 >   void CamelotEPSRenderRegion::DrawPathToOutputDevice(Path *DrawPath)
05624 
05625     Author:     Tim_Browse (Xara Group Ltd) <camelotdev@xara.com>
05626     Created:    30/03/94
05627     Inputs:     DrawPath - the path to render.
05628     Purpose:    Output all the commands required to render this path to the EPS file.
05629     SeeAlso:    RenderRegion::DrawPath; EPSRenderRegion::GetValidPathAttributes
05630                 EPSRenderRegion::ExportPath
05631 
05632 ********************************************************************************************/
05633 
05634 void CamelotEPSRenderRegion::DrawPathToOutputDevice(Path *DrawPath, PathShape)
05635 {
05636     // If we are not drawing complex shapes and this shape is, then return
05637     if ((!RenderComplexShapes) && (TestForComplexShape(&Caps)))
05638         return;
05639 
05640     // Work out if we need to be renderable.
05641     BOOL Renderable = (IS_A(this, CamelotEPSRenderRegion) || IsPrinting());
05642 
05643     if (Renderable)
05644     {
05645         // Check for clipped (non-repeating) bitmaps...
05646 
05647         // Get the current fill attr
05648         ColourFillAttribute* pFillAttr = (ColourFillAttribute*) CurrentAttrs[ATTR_FILLGEOMETRY].pAttr;
05649 
05650         // See if it is a simple non-repeating bitmap fill...
05651         if (pFillAttr->IS_KIND_OF(BitmapFillAttribute))
05652         {
05653             FillMappingAttribute* pMapAttr = (FillMappingAttribute*) CurrentAttrs[ATTR_FILLMAPPING].pAttr;
05654             if (pMapAttr->Repeat == 1)
05655             {
05656                 // Simple non-repeating clipped bitmap - we can do this!
05657                 DrawClippedBitmap(DrawPath);
05658                 return;
05659             }
05660         }
05661     }
05662 
05663     ExportPath(DrawPath, FALSE);
05664 
05665     // Now do the arrow heads if the render region can't do them directly
05666     if (!Caps.ArrowHeads)
05667     {
05668         // Doesn't support arrow heads directly so we render them as paths.
05669         DrawPathArrowHeads(DrawPath->GetCoordArray(),
05670                            DrawPath->GetVerbArray(),
05671                            DrawPath->GetNumCoords());
05672     }
05673 }
05674 
05675 
05676 /********************************************************************************************
05677 
05678 >   BOOL CamelotEPSRenderRegion::DrawClippedBitmap(Path *DrawPath)
05679 
05680     Author:     Tim_Browse (Xara Group Ltd) <camelotdev@xara.com>
05681     Created:    3/7/95
05682     Inputs:     DrawPath - the path to use to clip the bitmap fill.
05683     Returns:    TRUE if rendered ok;    
05684                 FALSE if not.
05685     Purpose:    Render a clipped bitmap using the given path.  This is because we can do
05686                 clipped bitmap rendering in PostScript, and it is much cheaper to do it
05687                 via PostScript instead of via the 'transparency' mask (via GDraw).
05688 
05689 ********************************************************************************************/
05690 
05691 BOOL CamelotEPSRenderRegion::DrawClippedBitmap(Path *DrawPath)
05692 {
05693     // Get a device context
05694     KernelDC *pDC = (KernelDC*)CCDC::ConvertFromNativeDC(RenderDC);
05695 
05696     // What we do is: export the path, save the graphics context, use the path as a clipping 
05697     // region, render the bitmap, restore the graphics context, and optionally stroke the
05698     // path, if necessary.
05699 
05700     // make sure the eps-rr is set up with the correct path attributes,
05701     // as ExportPath() will only be rendering pure path information.
05702     GetValidPathAttributes();
05703 
05704     // export our path.
05705     ExportPath(DrawPath, TRUE, TRUE);
05706 
05707     // Save the context
05708     pDC->OutputToken(_T("gs"));
05709 
05710     // export a 'clip' command. note that if our current winding rule is for even-odd
05711     // winding, we must export an 'eoclip' command instead.
05712     if (RR_WINDINGRULE() == EvenOddWinding)
05713         pDC->OutputToken(TEXT("eoclip"));
05714     else
05715         pDC->OutputToken(TEXT("clip"));
05716 
05717     // Render the bitmap - first, set up the parallelogram to render into.
05718     BitmapFillAttribute* pFillAttr = (BitmapFillAttribute*) CurrentAttrs[ATTR_FILLGEOMETRY].pAttr;
05719     DocCoord Coords[4];
05720     Coords[3] = *(pFillAttr->GetStartPoint());
05721     Coords[2] = *(pFillAttr->GetEndPoint());
05722     Coords[0] = *(pFillAttr->GetEndPoint2());
05723 
05724     // Deduce the other co-ordinate (not used by DrawParalleogramBitmap(), but calculate
05725     // it just in case...)
05726     Coords[1].x = Coords[0].x + (Coords[2].x - Coords[3].x);
05727     Coords[1].y = Coords[0].y + (Coords[2].y - Coords[3].y);
05728 
05729     // Get the bitmap
05730     KernelBitmap *pKernelBitmap = pFillAttr->GetBitmap();
05731     if (pKernelBitmap == NULL)
05732         return FALSE;
05733 
05734     // Render it.
05735     if (!DrawParallelogramBitmap(Coords, pKernelBitmap->ActualBitmap, GetFillEffect(),
05736                                  pFillAttr->GetStartColour(), pFillAttr->GetEndColour()))
05737         return FALSE;
05738 
05739     // Restore the context
05740     pDC->OutputToken(_T("gr"));
05741 
05742     // Work out if we should stroke the path
05743     TCHAR PathType[2] = _T("N");
05744 
05745     if (!RR_STROKECOLOUR().IsTransparent())
05746     {
05747         // Stroke path, leaving it open
05748         PathType[0] = 'S';
05749     }
05750 
05751     PathVerb* Verbs = DrawPath->GetVerbArray();
05752     INT32 NumCoords = DrawPath->GetNumCoords();
05753 
05754     if (Verbs[NumCoords - 1] & PT_CLOSEFIGURE)
05755         // Path should be closed
05756         PathType[0] = tolower(PathType[0]);
05757 
05758     // Do the stroke command
05759     pDC->OutputToken(PathType);
05760     pDC->OutputNewLine();
05761 
05762     // Ok if we got to here
05763     return TRUE;
05764 }
05765 
05766 
05767 /********************************************************************************************
05768 
05769 >   BOOL CamelotEPSRenderRegion::WriteNewLine ( void )
05770 
05771     Author:     Chris_Gallimore (Xara Group Ltd) <camelotdev@xara.com>
05772     Created:    15/11/00
05773     Inputs:     -
05774     Returns:    TRUE    - Success.
05775                 FALSE   - Failure.
05776     Purpose:    Outputs a new line tag to the file.
05777 
05778 ********************************************************************************************/
05779 
05780 BOOL CamelotEPSRenderRegion::WriteNewLine ( void )
05781 {
05782     // Since the positions of the characters are explicitly given in the EPS file, newline
05783     //  (/r) characters are not required. And they 
05784     return TRUE;
05785 }
05786 
05787 
05788 /********************************************************************************************
05789 
05790 >   virtual void CamelotEPSRenderRegion::OutputStrokeColour ()
05791 
05792     Author:     Chris_Gallimore (Xara Group Ltd) <camelotdev@xara.com>
05793     Created:    8/12/00
05794     Inputs:     -
05795     Returns:    -
05796     Purpose:    This simply calls EPSRenderRegion::OutputStrokeColour, as otherwise the 
05797                 ArtWorks version is used (which can't cope with the RGB colour tokens)
05798     SeeAlso:    EPSRenderRegion::OutputStrokeColour
05799 
05800 ********************************************************************************************/
05801 
05802 void CamelotEPSRenderRegion::OutputStrokeColour ()
05803 {
05804     EPSRenderRegion::OutputStrokeColour ();
05805 }
05806 
05807 
05808 /********************************************************************************************
05809 
05810 >   virtual void CamelotEPSRenderRegion::OutputFillColour ()
05811 
05812     Author:     Chris_Gallimore (Xara Group Ltd) <camelotdev@xara.com>
05813     Created:    8/12/00
05814     Inputs:     -
05815     Returns:    -
05816     Purpose:    This simply calls EPSRenderRegion::OutputFillColour, otherwise this render
05817                 region uses the overridden version found in ArtWorksEPSRenderRegion.
05818     SeeAlso:    EPSRenderRegion::OutputStrokeColour
05819 
05820 ********************************************************************************************/
05821 
05822 void CamelotEPSRenderRegion::OutputFillColour ()
05823 {
05824     EPSRenderRegion::OutputFillColour ();
05825 }
05826 
05827 #endif

Generated on Sat Nov 10 03:44:36 2007 for Camelot by  doxygen 1.4.4