// $Id: cameleps.cpp 1431 2006-07-11 15:14:59Z alex $ /* @@tag:xara-cn@@ DO NOT MODIFY THIS LINE ================================XARAHEADERSTART=========================== Xara LX, a vector drawing and manipulation program. Copyright (C) 1993-2006 Xara Group Ltd. Copyright on certain contributions may be held in joint with their respective authors. See AUTHORS file for details. LICENSE TO USE AND MODIFY SOFTWARE ---------------------------------- This file is part of Xara LX. Xara LX is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License version 2 as published by the Free Software Foundation. Xara LX and its component source files are distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with Xara LX (see the file GPL in the root directory of the distribution); if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA ADDITIONAL RIGHTS ----------------- Conditional upon your continuing compliance with the GNU General Public License described above, Xara Group Ltd grants to you certain additional rights. The additional rights are to use, modify, and distribute the software together with the wxWidgets library, the wxXtra library, and the "CDraw" library and any other such library that any version of Xara LX relased by Xara Group Ltd requires in order to compile and execute, including the static linking of that library to XaraLX. In the case of the "CDraw" library, you may satisfy obligation under the GNU General Public License to provide source code by providing a binary copy of the library concerned and a copy of the license accompanying it. Nothing in this section restricts any of the rights you have under the GNU General Public License. SCOPE OF LICENSE ---------------- This license applies to this program (XaraLX) and its constituent source files only, and does not necessarily apply to other Xara products which may in part share the same code base, and are subject to their own licensing terms. This license does not apply to files in the wxXtra directory, which are built into a separate library, and are subject to the wxWindows license contained within that directory in the file "WXXTRA-LICENSE". This license does not apply to the binary libraries (if any) within the "libs" directory, which are subject to a separate license contained within that directory in the file "LIBS-LICENSE". ARRANGEMENTS FOR CONTRIBUTION OF MODIFICATIONS ---------------------------------------------- Subject to the terms of the GNU Public License (see above), you are free to do whatever you like with your modifications. However, you may (at your option) wish contribute them to Xara's source tree. You can find details of how to do this at: http://www.xaraxtreme.org/developers/ Prior to contributing your modifications, you will need to complete our contributor agreement. This can be found at: http://www.xaraxtreme.org/developers/contribute/ Please note that Xara will not accept modifications which modify any of the text between the start and end of this header (marked XARAHEADERSTART and XARAHEADEREND). MARKS ----- Xara, Xara LX, Xara X, Xara X/Xtreme, Xara Xtreme, the Xtreme and Xara designs are registered or unregistered trademarks, design-marks, and/or service marks of Xara Group Ltd. All rights in these marks are reserved. Xara Group Ltd, Gaddesden Place, Hemel Hempstead, HP2 6EX, UK. http://www.xara.com/ =================================XARAHEADEREND============================ */ // Camelot Native EPS format filter /* */ #include "camtypes.h" #include "cameleps.h" #include "nodepath.h" //#include "paths.h" - in camtypes.h [AUTOMATICALLY REMOVED] //#include "tim.h" //#include "oilfltrs.h" - in camtypes.h [AUTOMATICALLY REMOVED] #include "ccdc.h" //#include "spread.h" - in camtypes.h [AUTOMATICALLY REMOVED] #include "page.h" //#include "fillattr.h" - in camtypes.h [AUTOMATICALLY REMOVED] #include "bitmpinf.h" //#include "attrmgr.h" - in camtypes.h [AUTOMATICALLY REMOVED] #include "nodebmp.h" #include "fracfill.h" //#include "resource.h" #include "nodeblnd.h" #include "nodebldr.h" #include "nodershp.h" #include "nodemold.h" #include "ndmldgrp.h" #include "nodemldr.h" #include "ndmldpth.h" #include "nodeshap.h" //#include "txtattr.h" - in camtypes.h [AUTOMATICALLY REMOVED] #include "grndbmp.h" #include "camview.h" //#include "docview.h" - in camtypes.h [AUTOMATICALLY REMOVED] #include "opgrad.h" // for AreLinesPerpendicular() #include "oilfiles.h" #include "ndoptmz.h" #include "ndtxtpth.h" #include "maskedrr.h" #include "oilbitmap.h" //#include "dibutil.h" - in camtypes.h [AUTOMATICALLY REMOVED] #include "native.h" // The new designed native filter, used for v2 #include "nativeps.h" // The old style EPS native filter, used in v1.1 #include "psdc.h" #include "osrndrgn.h" #include "prdlgctl.h" #include "progress.h" #include "textfuns.h" //#include "app.h" - in camtypes.h [AUTOMATICALLY REMOVED] //#include "will.h" // for _R(IDE_UNKOWN_EPSOBJECT) //#include "fixmem.h" - in camtypes.h [AUTOMATICALLY REMOVED] //#include "ben.h" #include "fontman.h" #include "guides.h" #include "snap.h" //#include "jason.h" #include "layer.h" #include "colormgr.h" #include "attrmap.h" #include "colplate.h" //#include "will2.h" #include "xsepsops.h" // export eps options #include "ctrlhelp.h" // ControlHelper class #include "unicdman.h" // For MBCS support DECLARE_SOURCE("$Revision: 1431 $"); #define new CAM_DEBUG_NEW CC_IMPLEMENT_DYNAMIC(CamelotEPSFilter, ArtWorksEPSFilter) typedef enum { FILLEFFECT_FADE, FILLEFFECT_RAINBOW, FILLEFFECT_ALTRAINBOW } TranspFillEffectType; // This is the array of Camelot EPS command/keyword names. CommandMap CamelotEPSFilter::CamelotCommands[] = { // Line/Fill colours { EPSC_cx, _T("cx") }, { EPSC_cX, _T("cX") }, { EPSC_ck, _T("ck") }, { EPSC_cK, _T("cK") }, // Stroke transparency { EPSC_cst, _T("cst") }, // Arrow heads { EPSC_csah, _T("csah") }, { EPSC_ceah, _T("ceah") }, // Dash Patterns { EPSC_cdp, _T("cdp") }, // Bitmap objects/bitmap fills { EPSC_cbm, _T("cbm") }, { EPSC_csbm, _T("csbm") }, { EPSC_cebm, _T("cebm") }, // Chromatic fill geometries { EPSC_caz, _T("caz") }, { EPSC_cax, _T("cax") }, // Transparent fill geometries { EPSC_cxt, _T("cxt") }, // Chromatic fill effects { EPSC_cxe, _T("cxe") }, // Chromatic fill mappings { EPSC_cxm, _T("cxm") }, // Transparent fill mappings { EPSC_cxmt, _T("cxmt") }, // Blends { EPSC_csbd, _T("csbd") }, { EPSC_cebd, _T("cebd") }, { EPSC_csbr, _T("csbr") }, { EPSC_cebr, _T("cebr") }, // Regular shapes { EPSC_csrs, _T("csrs") }, { EPSC_crsp, _T("crsp") }, { EPSC_crstm, _T("crstm") }, { EPSC_crsp1, _T("crsp1") }, { EPSC_crsp2, _T("crsp2") }, { EPSC_cers, _T("cers") }, // Mould commands { EPSC_csev, _T("csev") }, { EPSC_ceev, _T("ceev") }, { EPSC_cspr, _T("cspr") }, { EPSC_cepr, _T("cepr") }, { EPSC_csmp, _T("csmp") }, { EPSC_cemp, _T("cemp") }, { EPSC_csso, _T("csso") }, { EPSC_ceso, _T("ceso") }, { EPSC_csdo, _T("csdo") }, { EPSC_cedo, _T("cedo") }, // Text commands { EPSC_ctf, _T("ctf") }, { EPSC_ctb, _T("ctb") }, { EPSC_cti, _T("cti") }, { EPSC_cts, _T("cts") }, { EPSC_ctp, _T("ctp") }, { EPSC_ctls, _T("ctls") }, { EPSC_cso, _T("cso") }, { EPSC_ceo, _T("ceo") }, { EPSC_cfft, _T("cfft") }, { EPSC_cftf, _T("cftf") }, { EPSC_cbot, _T("cbot") }, { EPSC_cpal, _T("cpal") }, // Guide layer & guideline { EPSC_glyr, _T("glyr") }, { EPSC_glne, _T("glne") }, { EPSC_cmth, _T("cmth") }, { EPSC_cag, _T("cag") }, { EPSC_cbti, _T("cbti") }, // Sentinel { EPSC_Invalid, _T("Invalid") } }; /******************************************************************************************** > CamelotEPSFilter::CamelotEPSFilter() Author: Tim_Browse (Xara Group Ltd) Created: 28/10/93 Purpose: Constructor for an CamelotEPSFilter object. The object should be initialised before use. SeeAlso: EPSFilter::Init ********************************************************************************************/ CamelotEPSFilter::CamelotEPSFilter() { // Set up filter descriptions. FilterNameID = _R(IDT_CAMEPS_FILTERNAME); FilterInfoID = _R(IDT_CAMEPS_FILTERINFO); ImportMsgID = _R(IDT_IMPORTMSG_CAMELOT); ExportMsgID = _R(IDT_EXPORTMSG_CAMEPS); FilterID = FILTERID_CAMELOT_EPS; #ifndef STANDALONE Flags.CanImport = TRUE; //WEBSTER-Martin-27/01/97 #ifdef WEBSTER Flags.CanExport = FALSE; #else Flags.CanExport = TRUE; #endif //WEBSTER #else Flags.CanImport = FALSE; Flags.CanExport = FALSE; #endif // We don't want to re-position Camelot files because they should be ok and // Charles gets upset if his drawings move. AdjustOrigin = FALSE; } /******************************************************************************************** > BOOL CamelotEPSFilter::Init() Author: Tim_Browse (Xara Group Ltd) Created: 28/02/94 Returns: TRUE if the filter was initialised ok, FALSE otherwise. Purpose: Initialise an CamelotEPSFilter object. Errors: Will fail if not enough memory to initialise the EPS stack. SeeAlso: EPSStack ********************************************************************************************/ BOOL CamelotEPSFilter::Init() { // Get the OILFilter object pOILFilter = new CamelotEPSOILFilter(this); if (pOILFilter == NULL) return FALSE; // Load the description strings FilterName.Load(FilterNameID); FilterInfo.Load(FilterInfoID); // All ok return TRUE; } /******************************************************************************************** > virtual BOOL CamelotEPSFilter::CanIncludePreviewBmp() Author: Rik_Heywood (Xara Group Ltd) Created: 2/2/95 Returns: TRUE Purpose: Admits that Camelot EPS is able to have a Preview Bitmap attached to the document. To actually put a bitmap into the file, call the IncludePreviewBmp(TRUE). SeeAlso: Filters::CanIncludePreviewBmp ********************************************************************************************/ BOOL CamelotEPSFilter::CanIncludePreviewBmp() { return TRUE; } /******************************************************************************************** > virtual BOOL CamelotEPSFilter::IsDefaultDocRequired(const TCHAR* pcszPathName) Author: Neville_Humphrys (Xara Group Ltd) Created: 9/10/95 Inputs: pcszPathName pointer to the pathname to check Returns: TRUE if the filter requires a default document, FALSE if not. Purpose: Works out if opening a file of this type requires a default document to be loaded. If the file format supplies the document then return FALSE otherwise return TRUE. An example would be opening a bitmap file. This has no document defined in the file format and so we need to load the default document before importing the bitmap into this file. In this baseclass version return FALSE and hence assume that the filters that need to will override this function to return TRUE. SeeAlso: Filter; Filter::IsDefaultDocRequired; CCamDoc::OnOpenDocument; SeeAlso: FilterFamily::DoImport; Filter::DoImport; ********************************************************************************************/ BOOL CamelotEPSFilter::IsDefaultDocRequired(const TCHAR* pcszPathName) { // Return false here. All derived classes should return what they need return FALSE; } /******************************************************************************************** > INT32 CamelotEPSFilter::EPSHeaderIsOk(ADDR pFileHeader, UINT32 HeaderSize) Author: Tim_Browse (Xara Group Ltd) Created: 28/02/94 Returns: TRUE if the header is ok and import should proceed, FALSE if not. Purpose: Checks to see if the EPS comment headers specify that this is an Camelot generated EPS file, as required. ********************************************************************************************/ INT32 CamelotEPSFilter::EPSHeaderIsOk(ADDR pFileHeader, UINT32 HeaderSize) { // THIS ROUTINE IS NOT UNICODE // Check the first line in EPS file if (strncmp((char *) pFileHeader, "%!PS-Adobe-2.0 EPSF-1.2", 23) != 0) { // Incorrect version of EPS header line - we don't want this return 0; } // !PS-Adobe line is ok - check creator line... istringstream HeaderFile( (char*)pFileHeader ); char Buffer[200]; UINT32 Lines = 0; while ((Lines < 20) && !HeaderFile.eof()) { HeaderFile.getline(Buffer, 200); Lines++; // Return TRUE if this file was created by Camelot if (strncmp(Buffer, "%%Creator: Camelot", 18) == 0) { // Camelot is the creator. return 10; } // (ChrisG 8/11/00) Changed creator type to "Xara X") else if (strncmp (Buffer, "%%Creator: Xara X", 17) == 0) { // New Xara X string was set as the creator. return 10; } // If we find the compression token then stop the search as we don't want to start // looking in the compressed data! if (strncmp(Buffer, "%%Compression:", 14)==0) break; } // Didn't find a suitable Creator line, but we did find an EPS line, so indicate // that we're interested, but not sure. return 5; } /******************************************************************************************** > BOOL CamelotEPSFilter::PrepareToImport() Author: Tim_Browse (Xara Group Ltd) Created: 23/08/94 Returns: TRUE if filter initialised ok; FALSE if not. Purpose: See base class. This just sets up the bitmap fill flag for Camelot EPS. Errors: See base class. SeeAlso: EPSFilter::PrepareToImport; ********************************************************************************************/ BOOL CamelotEPSFilter::PrepareToImport() { if(IS_A(this, CamelotEPSFilter)) { // tell the user that we can't do it PathName Path; String_256 FileName; BOOL HaveName = FALSE; if(EPSFile->IsKindOf(CC_RUNTIME_CLASS(CCDiskFile))) { CCDiskFile *DF = (CCDiskFile *)EPSFile; Path = DF->GetPathName(); FileName = Path.GetFileName(); HaveName = TRUE; } String_256 ErrMsg; if(HaveName) { String_256 ErrMsg; ErrMsg.MakeMsg(_R(IDE_LOADCAMELOTEPSITHINKNOTNAMED), (TCHAR *)FileName); Error::SetError(0, ErrMsg, 0); } else { Error::SetError(_R(IDE_LOADCAMELOTEPSITHINKNOT)); } return FALSE; } else { // Initialise base class first. if (!ArtWorksEPSFilter::PrepareToImport()) return FALSE; // Not expecting a bitmap fill initially m_PendingBitmap = PENDING_BITMAP_NONE; BitmapTransparencyIndex = -1; pBitmap = FALSE; pRegularShape = NULL; } return TRUE; } /******************************************************************************************** > void CamelotEPSFilter::CleanUpAfterImport(BOOL Successful) Author: Ben_Summers (Xara Group Ltd) Created: 15/07/95 Inputs: Returns: - Purpose: stops stuff happening after clearing up for a Camelot EPS import Errors: - ********************************************************************************************/ void CamelotEPSFilter::CleanUpAfterImport(BOOL Successful) { if(IS_A(this, CamelotEPSFilter)) return; ArtWorksEPSFilter::CleanUpAfterImport(Successful); } /******************************************************************************************** > virtual BOOL CamelotEPSFilter::GetExportOptions( ) Author: Neville_Humphrys (Xara Group Ltd) HUmphrys Created: 22/12/95 Inputs: - Returns: TRUE if OK, FALSE if user pressed Cancel. Purpose: Allows the user to be prompted to get information for export. In this Camelot EPS case, we are affectively exporting a printable EPS and so require the user to set a few options. Moves the code from OilFilters to a proper function. Scope: Protected. ********************************************************************************************/ BOOL CamelotEPSFilter::GetExportOptions( ) { #if !defined(EXCLUDE_FROM_RALPH) OpDescriptor* pOpDesc = OpDescriptor::FindOpDescriptor(CC_RUNTIME_CLASS(XSEPSExportOptions)); if ( pOpDesc ) pOpDesc->Invoke(); return !XSEPSExportOptions::Aborted; #endif return TRUE; } /******************************************************************************************** > BOOL CamelotEPSFilter::PrepareToExport(CCLexFile* pFile, Spread* pSpread) Author: Tim_Browse (Xara Group Ltd) Created: 15/04/94 Inputs: pFile - The file to export to pSpread - the spread to export Returns: TRUE if succeeded, FALSE if not (e.g. no memory for EPS stack) Purpose: Prepare to import EPS data using this filter. This sets up the filter to a sensible state for reading. Errors: Out of memory. SeeAlso: EPSFilter::DoImport; EPSFilter::CleanUpAfterImport Scope: Private ********************************************************************************************/ BOOL CamelotEPSFilter::PrepareToExport(CCLexFile* pFile, Spread* pSpread) { #if !defined(EXCLUDE_FROM_RALPH) // Use base class to do most of it if (!EPSFilter::PrepareToExport(pFile, pSpread)) return FALSE; // Set export device to use full Camelot accuracy for user space values. ExportDCPtr->SetFullAccuracy(TRUE); // Create the region if it is this class if (IS_A(this, CamelotEPSFilter)) { // Don't care about clip regions when exporting - create a null region. DocRect NullClipRect; NullClipRect.MakeEmpty(); // Don't use rendering matrix when exporting EPS as it uses fractional coordinates. Matrix Identity; // Don't use view scale; set to 1 FIXED16 Scale(1); ExportRegion = new CamelotEPSRenderRegion(NullClipRect, Identity, Scale); if (ExportRegion == NULL) return FALSE; // Attach to the right device. ExportRegion->AttachDevice(DocView::GetSelected(), ExportDCPtr->GetDC(), pSpread); } #endif // All ok return TRUE; }; /******************************************************************************************** > void CamelotEPSFilter::LookUpToken() Author: Tim_Browse (Xara Group Ltd) Created: 25/02/94 Returns: TRUE if the token is an Camelot EPS token; FALSE if not. Purpose: Compare the current token against the Camelot keywords to see if it is one of them. SeeAlso: EPSFilter::LookUpToken; EPSFilter::DecodeToken ********************************************************************************************/ void CamelotEPSFilter::LookUpToken() { // Not interested in comments if (Token == EPSC_Comment) return; // Check to see if it is a keyword - cycle through the array of keyword names and // compare against our token (could use a hash table?) INT32 i = 0; while (CamelotCommands[i].Cmd != EPSC_Invalid) { if (camStrcmp(TokenBuf, CamelotCommands[i].CmdStr) == 0) { // Found the token - set the token variable and return success Token = CamelotCommands[i].Cmd; return; } // Try next command i++; } // Did not find this token - pass on to base class. ArtWorksEPSFilter::LookUpToken(); } /******************************************************************************************** > BOOL CamelotEPSFilter::ProcessToken() Author: Tim_Browse (Xara Group Ltd) Created: 25/02/94 Returns: TRUE if token understood and processed ok, FALSE if not. Purpose: Processes EPS tokens that are not part of the standard Illustrator set, or which need to be handled differently to the standard Illustrator meanings. i.e. this is the function that handles all the Camelot EPS operators. Errors: Syntax error in EPS, Out of memory. SeeAlso: EPSFilter::ProcessToken ********************************************************************************************/ BOOL CamelotEPSFilter::ProcessToken() { // Variables used to extract operands from the stack String_64 ColName; PColourCMYK Col; TintType Tint = TINT_NONE; FIXEDPOINT TintVal; DocCoord StartPoint, EndPoint; PathFlags Flags; INT32 ObjectNesting = 0; DocCoord BmpCoords[4]; INT32 i; // Used to keep track of bitmaps loading them in. static NodeBitmap *pBitmapObject = NULL; // Decode the command, and execute it... switch (Token) { case EPSC_cso: INT32 ObjectID; if (!Stack.Pop(&ObjectID)) goto EPSError; // pop the ID switch (ObjectID) { case EOTAG_FONTFLAGS: { if (!ProcessFontFlags()) goto EPSError; } break; case EOTAG_BITMAPFLAGS: { if (!ProcessBitmapFlags()) goto EPSError; } break; case EOTAG_TEXTWRAPPED: { if (!FindEndOfTag()) goto EPSError; } break; case EOTAG_FONTTYPE: { if (!ProcessFontType()) goto EPSError; } break; case EOTAG_GUIDELAYER: case EOTAG_GUIDELINE: break; case EOTAG_MOULDTHRESHOLD: { if (!ProcessMouldThreshold()) goto EPSError; } break; default: { // Report we're skipping an unknown object ERROR1RAW(_R(IDE_UNKNOWN_EPSOBJECT)); ObjectNesting++; do { if (!GetToken()) goto EPSError; LexTokenType Type = EPSFile->GetTokenType(); if ((Type != TOKEN_EOL) && (Type != TOKEN_EOF)) { if (camStrcmp(TokenBuf, _T("cso")) == 0) { ObjectNesting++; } else if (camStrcmp(TokenBuf, _T("ceo")) == 0) { ObjectNesting--; } } } while (ObjectNesting > 0); } break; } break; case EPSC_ceo: break; // goto EPSError; // Line/Fill colours case EPSC_cx: case EPSC_cX: case EPSC_ck: case EPSC_cK: break; // Linear/radial fills case EPSC_cax: // Colours are described using a name and tint as well as CMYK. Tint = TINT_ILLUSTRATOR; case EPSC_caz: { DocColour StartColour, EndColour; DocCoord StartPoint, EndPoint, EndPoint2; CamelotEPSFillType FillType; // Used for fractal fills INT32 Seed; double Graininess, Gravity, Squash; UINT32 DPI; INT32 Tileable; // Get fill type if (!Stack.Pop((INT32*) &FillType)) goto EPSError; TRACEUSER( "Will", _T("Importing Fill, Type=%d\n"),FillType); if (FillType == CAMEPS_FILL_FRACTAL) { // If it's a fractal fill type, discard the 'sub-type' parameter (should // always be zero currently!) INT32 SubType; if (!Stack.Pop(&SubType)) goto EPSError; // Default to no tiling Tileable = FALSE; // Work out the sub-type: either it has no tileable flag, or it does, // or it's a type that is not supported. if (SubType != 0) { if (SubType == 1) { // Get the tileable flag if (!Stack.Pop(&Tileable)) goto EPSError; } else { ERROR2RAW("Bad fractal fill sub-type"); goto EPSError; } } // Now get the fractal parameters: if (!Stack.Pop(&DPI) || !Stack.Pop(&Squash) || !Stack.Pop(&Gravity) || !Stack.Pop(&Graininess) || !Stack.Pop(&Seed)) { // Error in fractal parameteres goto EPSError; } } // For elliptical & bitmap fills, get the second end-point of the fill if ((FillType == CAMEPS_FILL_ELLIPTICAL) || (FillType == CAMEPS_FILL_BITMAP) || (FillType == CAMEPS_FILL_FRACTAL) || (FillType == CAMEPS_FILL_NEWBITMAP) || (FillType == CAMEPS_FILL_NEWLINEAR)) { if (!Stack.PopCoordPair(&EndPoint2)) goto EPSError; } // Get start and end positions for grad-fills if (!Stack.PopCoordPair(&EndPoint) || !Stack.PopCoordPair(&StartPoint)) goto EPSError; // Get start and end colours for grad-fills (but not bitmap fills) if (FillType != CAMEPS_FILL_BITMAP) { if (!Stack.PopColour(&Col, Tint, &TintVal, &ColName)) // Invalid colour operands goto EPSError; GetEPSColour(&EndColour, &Col, Tint, TintVal, &ColName); if (!Stack.PopColour(&Col, Tint, &TintVal, &ColName)) // Invalid colour operands goto EPSError; GetEPSColour(&StartColour, &Col, Tint, TintVal, &ColName); } switch (FillType) { // Decode Camelot EPS grad fill codes case CAMEPS_FILL_LINEAR: if (!SetLinearFill(StartColour, EndColour, StartPoint, EndPoint)) goto NoMemory; break; case CAMEPS_FILL_NEWLINEAR: if (!SetLinearFill(StartColour, EndColour, StartPoint, EndPoint, &EndPoint2)) goto NoMemory; break; case CAMEPS_FILL_ELLIPTICAL: if (!SetRadialFill(StartColour, EndColour, StartPoint, EndPoint, EndPoint2)) goto NoMemory; break; case CAMEPS_FILL_CIRCULAR: if (!SetRadialFill(StartColour, EndColour, StartPoint, EndPoint)) goto NoMemory; break; case CAMEPS_FILL_CONICAL: if (!SetConicalFill(StartColour, EndColour, StartPoint, EndPoint)) goto NoMemory; break; case CAMEPS_FILL_BITMAP: // Is it really a bitmap fill ? // It may be a new type bitmap object if (m_PendingBitmap != PENDING_BITMAP_OBJECT_FILL) m_PendingBitmap = PENDING_BITMAP_FILL; BitmapAttrs.Coords[0] = StartPoint; BitmapAttrs.Coords[1] = EndPoint; BitmapAttrs.Coords[2] = EndPoint2; BitmapAttrs.StartCol = COLOUR_NONE; BitmapAttrs.EndCol = COLOUR_NONE; break; case CAMEPS_FILL_NEWBITMAP: m_PendingBitmap = PENDING_BITMAP_FILL; BitmapAttrs.Coords[0] = StartPoint; BitmapAttrs.Coords[1] = EndPoint; BitmapAttrs.Coords[2] = EndPoint2; BitmapAttrs.StartCol = StartColour; BitmapAttrs.EndCol = EndColour; break; case CAMEPS_FILL_FRACTAL: if (!SetFractalFill(StartColour, EndColour, StartPoint, EndPoint, EndPoint2, Seed, Graininess, Gravity, Squash, DPI, Tileable)) goto NoMemory; break; default: ENSURE(FALSE, "Unknown fill type found!"); break; // Don't know this fill type } break; } case EPSC_cxe: { INT32 EffectType; // Get fill effect type if (!Stack.Pop(&EffectType)) goto EPSError; // Call base class to use it switch (EffectType) { case FILLEFFECT_FADE: if (!SetFadeFillEffect()) goto NoMemory; break; case FILLEFFECT_RAINBOW: if (!SetRainbowFillEffect()) goto NoMemory; break; case FILLEFFECT_ALTRAINBOW: if (!SetAltRainbowFillEffect()) goto NoMemory; break; default: ERROR3_PF(("Unknown fill effect type (%d) in EPS", EffectType)); } break; } case EPSC_cxm: { INT32 MappingType; INT32 Repeat; // Get fill mapping type (should always be 0) if (!Stack.Pop(&MappingType)) goto EPSError; if (MappingType != 0) { ERROR2RAW("Bad mapping type in EPS"); goto EPSError; } // Get proper fill mapping type (should be 1, 2 or 3) if (!Stack.Pop(&Repeat)) goto EPSError; if ((Repeat < 1) || (Repeat > 3)) { ERROR2RAW("Bad mapping type in EPS"); goto EPSError; } // Call base class to use it if (Token == EPSC_cxm) { if (!SetLinearFillMapping(Repeat)) goto NoMemory; } else { if (!SetLinearTranspFillMapping(Repeat)) goto NoMemory; } break; } case EPSC_csbm: // Try to read in a bitmap if (!ReadBitmap()) return FALSE; break; case EPSC_cebm: // Should have just finished reading a bitmap...what should we use for? switch (m_PendingBitmap) { case PENDING_BITMAP_FILL: // Use the bitmap to do a bitmap fill if (!SetBitmapFill(pBitmap, BitmapAttrs.Coords[0], BitmapAttrs.Coords[1], BitmapAttrs.Coords[2], &BitmapAttrs.StartCol, &BitmapAttrs.EndCol)) return FALSE; // NULL the colours, so we don't get 'Index Colour in Use' BitmapAttrs.StartCol = COLOUR_NONE; BitmapAttrs.EndCol = COLOUR_NONE; // Get rid of our KernelBitmap object (the bitmap itself is now // attached to the current fill geometry attribute. pBitmap = NULL; break; case PENDING_BITMAP_TRANSPFILL: // Use the bitmap to do a transparent bitmap fill (texture) if (!SetBitmapTranspFill(BitmapAttrs.TranspType, pBitmap, BitmapAttrs.Coords[0], BitmapAttrs.Coords[1], BitmapAttrs.Coords[2], BitmapAttrs.Transp, BitmapAttrs.EndTransp)) return FALSE; // Get rid of our KernelBitmap object (the bitmap itself is now // attached to the current transparent fill geometry attribute. pBitmap = NULL; break; case PENDING_BITMAP_OBJECT_FILL: // This is the new format used to save 'Non-contoned' bitmaps, // where it is saved as a bitmap filled path, so the Viewer // can render it, and an extra token that tells this code that // it's really a bitmap object. // Bitmap object - make one ready to put into the tree. pBitmapObject = new NodeBitmap; if ((pBitmapObject == NULL) || (!pBitmapObject->SetUpPath(12,12))) goto EPSError; // Now we need to Skip over the path that is output for the // Viewer to render ... while (!EPSFile->eof()) { GetToken(); // We'll remember the coords and use them to build the bitmap switch (Token) { case EPSC_Integer: Stack.Push(TokenData.Long); break; case EPSC_Double: Stack.Push(TokenData.Double); break; case EPSC_FixedPoint: Stack.Push(TokenData.FixedPoint); break; } // Look for the 'Filled Path' token... if (Token == EPSC_F) break; } Stack.Discard(2); // Throw away the first coord pair // Now read in reverse order because they're stacked for (i = 3; i >= 0; i--) { if (!Stack.PopCoordPair(&BmpCoords[i])) goto EPSError; pBitmapObject->Parallel[i] = BmpCoords[i]; } // Put them into the path. pBitmapObject->InkPath.InsertMoveTo(BmpCoords[0]); for (i = 1; i <= 3; i++) pBitmapObject->InkPath.InsertLineTo(BmpCoords[i]); pBitmapObject->InkPath.InsertLineTo(BmpCoords[0]); pBitmapObject->InkPath.CloseSubPath(); // Make sure the bitmap has no fill colour applied SetPathFilled(FALSE); // Now fall though to the Bitmap Object code ..... case PENDING_BITMAP_OBJECT: { // Use the bitmap to do a bitmap object // Copy the bitmap into this node and delete the one we've just imported. if (pBitmapObject == NULL) // No bitmap node to load! goto EPSError; pBitmapObject->GetBitmapRef()->Attach(pBitmap, GetDocument()); if (pBitmapObject->GetBitmap() != pBitmap) { // It didn't use the bitmap we gave it, so we can delete it delete pBitmap; } pBitmap = NULL; AttributeValue* pOldLineColour = NULL; AttributeValue* pOldLineWidth = NULL; if (m_PendingBitmap == PENDING_BITMAP_OBJECT_FILL) { // This must be a non-contone bitmap, so make sure we // don't apply any line colour. // (The fill colour will already have been turned off // with a 'SetPathFilled(FALSE)' above) pOldLineColour = CurrentAttrs[ATTR_STROKECOLOUR].pAttr; // Force Line Colour be NONE. StrokeColourAttribute* pLineCol = new StrokeColourAttribute; if (pLineCol == NULL) goto NoMemory; DocColour colorNone( COLOUR_NONE ); pLineCol->SetStartColour( &colorNone ); CurrentAttrs[ATTR_STROKECOLOUR].pAttr = pLineCol; } pOldLineWidth = CurrentAttrs[ATTR_LINEWIDTH].pAttr; // Force the line width to be zero for Bitmap objects LineWidthAttribute* pLineWidth = new LineWidthAttribute; if (pLineWidth == NULL) goto NoMemory; pLineWidth->LineWidth = 0; CurrentAttrs[ATTR_LINEWIDTH].pAttr = pLineWidth; // Add attributes to the path, if they are different from the default... BOOL Success = AttributeManager::ApplyBasedOnDefaults(pBitmapObject, CurrentAttrs); // Did it work? if (!Success) { // No clean up and report error delete pBitmapObject; pBitmapObject = NULL; goto NoMemory; } // Finally, add it into the tree pBitmapObject->InvalidateBoundingRect(); AddNewNode(pBitmapObject); // Switch path filling back on again SetPathFilled(TRUE); if (pOldLineColour != NULL) { delete CurrentAttrs[ATTR_STROKECOLOUR].pAttr; // Restore the old line colour attribute CurrentAttrs[ATTR_STROKECOLOUR].pAttr = pOldLineColour; } if (pOldLineWidth != NULL) { delete CurrentAttrs[ATTR_LINEWIDTH].pAttr; // Restore the old line width attribute CurrentAttrs[ATTR_LINEWIDTH].pAttr = pOldLineWidth; } // Get rid of our KernelBitmap object (the bitmap itself is now // attached to the current fill geometry attribute. pBitmap = NULL; pBitmapObject = NULL; break; } case PENDING_BITMAP_POOLITEM: { // Attach the Bitmap to a reference so that it is kept around for things to use TRACEUSER( "Rik", _T("What do you know, its a Pool Item\n")); BitmapPoolAttach(pBitmap); pBitmap = NULL; break; } default: ENSURE(FALSE, "Found a bitmap but don't know what to do with it!"); delete pBitmap; pBitmap = NULL; goto EPSError; break; } m_PendingBitmap = PENDING_BITMAP_NONE; BitmapTransparencyIndex = -1; break; case EPSC_cbm: { // Bitmap object - make one ready to put into the tree. pBitmapObject = new NodeBitmap; if ((pBitmapObject == NULL) || (!pBitmapObject->SetUpPath(12,12))) return FALSE; // First, try to read in the coordinates of the bitmap object DocCoord Coords[4]; // NB. reverse order because they're stacked, remember! INT32 i; for ( i = 3; i >= 0; i--) { if (!Stack.PopCoordPair(&Coords[i])) goto EPSError; pBitmapObject->Parallel[i] = Coords[i]; } // Put them into the path. pBitmapObject->InkPath.InsertMoveTo(Coords[0]); for (i = 1; i <= 3; i++) pBitmapObject->InkPath.InsertLineTo(Coords[i]); pBitmapObject->InkPath.InsertLineTo(Coords[0]); pBitmapObject->InkPath.CloseSubPath(); // pBitmapObject->InkPath.UpdateBoundingRect(); // Flag to the code above that the next bitmap should be added to this object. m_PendingBitmap = PENDING_BITMAP_OBJECT; break; } case EPSC_cbmp: // A bitmap palette - should not ever get this in ART files. goto EPSError; // Blend related procedures case EPSC_csbd: return ProcessBlend(); case EPSC_cebd: goto EPSError; case EPSC_csbr: return ProcessBlender(); case EPSC_cebr: goto EPSError; // Regular shape related procedures case EPSC_csrs: return ProcessRegularShape(); case EPSC_cers: goto EPSError; case EPSC_crsp: return ProcessRegularShapeParams(); case EPSC_crstm: { if (pRegularShape == NULL) goto EPSError; // Get the shape's matrix from the stack and put it into the shape. Matrix TheMatrix; if (!Stack.Pop(&TheMatrix, FALSE)) goto EPSError; pRegularShape->SetTransformMatrix(&TheMatrix); break; } case EPSC_crsp1: { if ((pRegularShape == NULL) || (pInkPath == NULL)) goto EPSError; // Path is held in pInkPath - copy into shape and clear the pInkPath ready for // the second edge's path. pRegularShape->EdgePath1.ClearPath(); if (!pRegularShape->EdgePath1.CopyPathDataFrom(pInkPath)) goto NoMemory; pInkPath->ClearPath(); break; } case EPSC_crsp2: { if ((pRegularShape == NULL) || (pInkPath == NULL)) goto EPSError; // Path is held in pInkPath - copy into shape's second edge path and delete the // temporary path. pRegularShape->EdgePath2.ClearPath(); if (!pRegularShape->EdgePath2.CopyPathDataFrom(pInkPath)) goto NoMemory; // No more paths to read in. delete pInkPath; pInkPath = NULL; break; } // Mould commands case EPSC_csev: return ProcessEnvelope(); case EPSC_cspr: return ProcessPerspective(); // Guide layers & guidelines case EPSC_glyr: return ProcessGuideLayer(); case EPSC_glne: return ProcessGuideline(); case EPSC_cag: // A separated grey fill - should not ever get this in ART files. goto EPSError; default: // Token not understood - pass on to base class return ArtWorksEPSFilter::ProcessToken(); } // No errors encountered while parsing this token and its operands. return TRUE; // Error handlers: EPSError: HandleEPSError(); return FALSE; NoMemory: HandleNoMemory(); return FALSE; } /******************************************************************************************** > TCHAR *CamelotEPSFilter::GetEPSCommand(EPSCommand Cmd) Author: Tim_Browse (Xara Group Ltd) Created: 28/02/94 Inputs: Cmd - the EPS token, e.g. EPSC_aoa Returns: Pointer to the string representation of the token, e.g. "aoa" Purpose: Given an EPS token, return the string representation of it; mainly for debugging purposes. ********************************************************************************************/ TCHAR *CamelotEPSFilter::GetEPSCommand(EPSCommand Cmd) { INT32 i = 0; while (CamelotCommands[i].Cmd != EPSC_Invalid) { if (CamelotCommands[i].Cmd == Cmd) return CamelotCommands[i].CmdStr; // Try next command i++; } // Couldn't find it - default to base class method return ArtWorksEPSFilter::GetEPSCommand(Cmd); } /******************************************************************************************** > BOOL CamelotEPSFilter::ReadBitmap() Author: Tim_Browse (Xara Group Ltd) (Will) Created: 25/1/95 Returns: TRUE if it worked, FALSE if it failed Purpose: Reads the bitmap info from the eps stack and loads in the bitmap data It loads a type 0 bitmap SeeAlso: CamelotNativeEPSFilter::ReadBitmap ********************************************************************************************/ BOOL CamelotEPSFilter::ReadBitmap() { // Find out what kind of bitmap it is - always type 0 at present in CamelotEPS (CamelotNative has a type 1 as well). INT32 BitmapType; if (!Stack.Pop(&BitmapType) || (BitmapType != 0)) { // Error - not enough operands, or bitmap is wrong type ENSURE(BitmapType == 0, "Unknown bitmap type in EPS!"); HandleEPSError(); return FALSE; } // Read in info on the bitmap // Recommended size is stored as a user space value, i.e. in points, so we // use PopCoord() to get the automatic scaling to millipoints. BitmapInfo Info; String_256 BitmapName; if (!Stack.PopCoord(&Info.RecommendedHeight) || !Stack.PopCoord(&Info.RecommendedWidth) || !Stack.Pop(&Info.NumPaletteEntries) || !Stack.Pop(&Info.PixelDepth) || !Stack.Pop(&Info.PixelHeight) || !Stack.Pop(&Info.PixelWidth) || !Stack.Pop(&BitmapName)) { // Error - not enough operands HandleEPSError(); return FALSE; } BOOL IsNew; BOOL ImportOk = KernelBitmap::ImportBitmap(this, &Info, BitmapType, &pBitmap, &IsNew); if (ImportOk && IsNew) { // Set the name in it pBitmap->ActualBitmap->SetName(BitmapName); // Set the TransparencyIndex if (BitmapTransparencyIndex >= 0) { TRACEUSER( "Will", _T("Imported bitmap with Transparent Index %d\n"), BitmapTransparencyIndex); } pBitmap->SetTransparencyIndex(BitmapTransparencyIndex); } return ImportOk; } /******************************************************************************************** > virtual void CamelotEPSFilter::BitmapPoolAttach(KernelBitmap* pBitmap) Author: Rik_Heywood (Xara Group Ltd) Created: 20/1/95 Inputs: pBitmap - The bitmap to attach Purpose: Makes a reference to the bitmap so that it can be used throughout the eps file with repeating the data. This base class version actually does nothing but delete the bitmap. CamelotNativeEPS is where the real processing is done. ********************************************************************************************/ void CamelotEPSFilter::BitmapPoolAttach(KernelBitmap* pBitmap) { // Base class version of the function just deletes the bitmap as it does not really know // what it needs to do with it if (pBitmap!=NULL) delete pBitmap; } /******************************************************************************************** > BOOL CamelotEPSFilter::ExportBitmap(KernelBitmap& TheBitmap) Author: Tim_Browse (Xara Group Ltd) Created: 01/09/94 Inputs: TheBitmap - the bitmap to export Returns: TRUE if the bitmap was exported ok (or if it wasn't exported because the file format does not support it; FALSE if an error occured. Purpose: Export a bitmap to a Camelot EPS file. This outputs the standard tokens and data required to embed a bitmap in a Camelot EPS file. Errors: Usual disk/file errors. SeeAlso: Filter::ExportBitmap ********************************************************************************************/ BOOL CamelotEPSFilter::ExportBitmap(KernelBitmap& TheBitmap) { #if !defined(EXCLUDE_FROM_RALPH) BitmapInfo Info; String_256 BitmapName; // Check to seed if the bitmap has a transparent Index, // and output a special token if so if (TheBitmap.ActualBitmap->GetBPP() <= 8) { INT32 TransparencyIndex; if (TheBitmap.ActualBitmap->GetTransparencyIndex(&TransparencyIndex)) { TRACEUSER( "Will", _T("Exporting bitmap with Transparent Index %d\n"), TransparencyIndex); ExportDCPtr->OutputValue((INT32) EOTAG_BITMAPFLAGS); ExportDCPtr->OutputToken(_T("cso")); // Start of new object type ExportDCPtr->OutputValue((INT32) TransparencyIndex); ExportDCPtr->OutputToken(_T("cbti")); // Index of the colour that is transparent ExportDCPtr->OutputToken(_T("ceo")); // End of new object type ExportDCPtr->OutputNewLine(); } } // Get details of the bitmap TheBitmap.ActualBitmap->GetInfo(&Info); BitmapName = TheBitmap.ActualBitmap->GetName(); // Write them out to the EPS file. ExportDCPtr->OutputString((TCHAR*)BitmapName); ExportDCPtr->OutputValue((UINT32) Info.PixelWidth); ExportDCPtr->OutputValue((UINT32) Info.PixelHeight); ExportDCPtr->OutputValue((UINT32) Info.PixelDepth); ExportDCPtr->OutputValue((UINT32) Info.NumPaletteEntries); ExportDCPtr->OutputUserSpaceValue(Info.RecommendedWidth); ExportDCPtr->OutputUserSpaceValue(Info.RecommendedHeight); // Bitmap type is 0 as this is the type that exports all the bitmap data // Type 1 is used as a reference to a bitmap in the pool ExportDCPtr->OutputToken(_T("0")); // Write out the bitmap start token ExportDCPtr->OutputToken(_T("csbm")); ExportDCPtr->OutputNewLine(); // Write out the bitmap data TheBitmap.ActualBitmap->ExportBitmap(ExportRegion); // Write out the bitmap end token ExportDCPtr->OutputToken(_T("cebm")); ExportDCPtr->OutputNewLine(); #endif // All ok return TRUE; } /******************************************************************************************** > BOOL CamelotEPSFilter::ProcessFontFlags() Author: Mike_Kenny (Xara Group Ltd) Created: 12/06/95 Returns: TRUE if the font flags were processed correctly, FALSE if not. Purpose: Reads in all the elements of a font flags structure in the EPS file. Errors: Syntax error in EPS file, Out of memory ********************************************************************************************/ BOOL CamelotEPSFilter::ProcessFontFlags() { FontFlags.Bold = FALSE; FontFlags.Italic = FALSE; // Keep processing tokens until we find the end of the font flags tag object while (!EPSFile->eof()) { GetToken(); // Look for the only font tokens we're interrested in switch (Token) { case EPSC_ceo: // found the end of the font flags block so return done. return TRUE; break; case EPSC_cfft: // This flag indicates that the next bold/italic attributes are to // be applied to the current font style rather than as physical attributes FontFlags.Bold = TRUE; FontFlags.Italic = TRUE; break; default: // try to handle whatever token this is. if (!HandleToken()) return FALSE; break; } } HandleEPSError(); return FALSE; } /******************************************************************************************** > BOOL CamelotEPSFilter::ProcessFontType() Author: Mike_Kenny (Xara Group Ltd) Created: 12/06/95 Returns: TRUE if the font type was processed correctly, FALSE if not. Purpose: Reads in all the elements of a font type structure in the EPS file. Errors: Syntax error in EPS file, Out of memory ********************************************************************************************/ BOOL CamelotEPSFilter::ProcessFontType() { EPSFilter::ClassOfFont = FC_TRUETYPE; // Keep processing tokens until we find the end of the font flags tag object while (!EPSFile->eof()) { GetToken(); // Look for the only font tokens we're interrested in switch (Token) { case EPSC_ceo: // found the end of the font flags block so return done. return TRUE; break; case EPSC_cftf: { // This type indicates that the next font name is of a particular font type INT32 Type; FontClass actclass; if (!Stack.Pop(&Type)) { HandleEPSError(); return FALSE; } if (FONTMANAGER->LegalFontClass(Type, actclass)) EPSFilter::ClassOfFont = actclass; // if we don't know what the class is assume Truetype. } break; default: // try to handle whatever token this is. if (!HandleToken()) return FALSE; break; } } HandleEPSError(); return FALSE; } /******************************************************************************************** > BOOL CamelotEPSFilter::FindEndOfTag() Author: Mike_Kenny (Xara Group Ltd) Created: 17/06/95 Returns: TRUE if the end of tag token was found, FALSE if not. Purpose: Continues reading tokens until the ceo token is found Errors: Syntax error in EPS file, Out of memory ********************************************************************************************/ BOOL CamelotEPSFilter::FindEndOfTag() { // Keep processing tokens until we find the 'end of tag' token while (!EPSFile->eof()) { GetToken(); switch (Token) { case EPSC_ceo: return TRUE; break; default: if (!HandleToken()) return FALSE; break; } } HandleEPSError(); return FALSE; } /******************************************************************************************** > BOOL CamelotEPSFilter::ProcessBitmapFlags() Author: Will_Cowling (Xara Group Ltd) Created: 23/06/95 Returns: TRUE if the bitmap flags were processed correctly, FALSE if not. Purpose: Reads in all the elements of a bitmap flags structure in the EPS file. Errors: Syntax error in EPS file, Out of memory ********************************************************************************************/ BOOL CamelotEPSFilter::ProcessBitmapFlags() { // Keep processing tokens until we find the end of the bitmap flags tag object while (!EPSFile->eof()) { GetToken(); // Look for the only bitmap tokens we're interested in switch (Token) { case EPSC_ceo: // found the end of the bitmap flags block so return done. return TRUE; break; case EPSC_cbot: // This token indicates that the next bitmap fill is really // a bitmap object m_PendingBitmap = PENDING_BITMAP_OBJECT_FILL; break; case EPSC_cbti: // The next bitmap must have a transparency Index INT32 Index; if (!Stack.Pop(&Index)) { HandleEPSError(); return FALSE; } BitmapTransparencyIndex = Index; break; default: // try to handle whatever token this is. if (!HandleToken()) return FALSE; break; } } HandleEPSError(); return FALSE; } /******************************************************************************************** > BOOL CamelotEPSFilter::ProcessBlend() Author: Mark_Neves (Xara Group Ltd) Created: 29/11/94 Returns: TRUE if the blend was processed correctly, FALSE if not. Purpose: Reads in all the elements of a blend structure in the EPS file. Errors: Syntax error in EPS file, Out of memory ********************************************************************************************/ BOOL CamelotEPSFilter::ProcessBlend() { if (!StartBlend()) return FALSE; // Keep processing tokens until we find the end of the blend do { GetToken(); // Look for the end of the blend token... if (Token == EPSC_cebd) { return EndBlend(); } } // Otherwise keep going until an error or eof is encountered while (HandleToken() && (!EPSFile->eof())); if (EPSFile->eof()) { // Didn't find end of blend - syntax error; deal with it HandleEPSError(); } // If we're here, something went wrong return FALSE; } /******************************************************************************************** > BOOL CamelotEPSFilter::StartBlend() Author: Mark_Neves (Xara Group Ltd) Created: 29/11/94 Returns: TRUE if the new blend was created ok; FALSE if not. Purpose: Used when a blend structure needs to be created - after this is called, all new nodes added with AddNewNode() will be added as children of this new blend, until EndBlend is called. Errors: Out of memory SeeAlso: CamelotEPSFilter::EndBlend ********************************************************************************************/ BOOL CamelotEPSFilter::StartBlend() { INT32 NumBlendSteps,Version,OneToOne,NotAntialiased,ColBlendType; INT32 Reserved3,Reserved2,Reserved1; // The last param before the token is the version. if (!Stack.Pop(&Version)) return FALSE; // Error if the version number is not correct ERROR1IF(Version != 1,FALSE,_R(IDT_EPS_BADSYNTAX)); // Read in the blend's params if (!Stack.Pop(&NumBlendSteps) || !Stack.Pop(&NotAntialiased) || !Stack.Pop(&OneToOne) || !Stack.Pop(&ColBlendType) || !Stack.Pop(&Reserved3) || !Stack.Pop(&Reserved2) || !Stack.Pop(&Reserved1)) { return FALSE; } // Make a new blend node for this blend NodeBlend *pNodeBlend = new NodeBlend; ERROR1IF(pNodeBlend == NULL, FALSE, _R(IDT_EPS_NOMEMORY)); // Add it into the tree if (!AddNewNode(pNodeBlend)) return FALSE; // Set the blend's params pNodeBlend->SetOneToOne(OneToOne); pNodeBlend->SetNotAntialiased(NotAntialiased); pNodeBlend->SetNumBlendSteps(NumBlendSteps); pNodeBlend->SetColourBlendType((ColourBlendType)ColBlendType); // Make sure new objects are added as children of the node pNode = pNodeBlend; // All ok return TRUE; } /******************************************************************************************** > BOOL CamelotEPSFilter::EndBlend() Author: Mark_Neves (Xara Group Ltd) Created: 29/11/94 Returns: TRUE if the blend was ended ok; FALSE if not. Purpose: Used when a blend has finished being constructed and we want to return to normal node positioning. The blend is added to the document when this function is called (although that depending on the current filter mode, i.e. whether this is a new or existing layer, it may not be added directly to the documnent tree straight away - it may be deferred until the next layer is found, or the import has ended). Errors: Out of memory SeeAlso: CamelotEPSFilter::StartBlend ********************************************************************************************/ BOOL CamelotEPSFilter::EndBlend() { // Sanity check ENSURE(pNode->IsKindOf(CC_RUNTIME_CLASS(NodeBlend)), "No blend in CamelotEPSFilter::EndBlend"); // Keep the blend ptr and find the first child of the node before we reset pNode. Node* pNodeBlend = pNode; Node* pChildNode = pNode->FindFirstChild(); // Get the parent of the blend node, and use that to add new objects to pNode = pNodeBlend->FindParent(); ENSURE(pNode != NULL, "Blend has no parent in ProcessBlend()"); // Initialise all the child blender nodes UINT32 BlenderCount=0; while (pChildNode != NULL) { if (IS_A(pChildNode,NodeBlender)) { NodeBlender* pNodeBlender = (NodeBlender*)pChildNode; // Get the imported blender params INT32 PathIndexStart = pNodeBlender->GetPathIndexStart(); INT32 PathIndexEnd = pNodeBlender->GetPathIndexEnd(); INT32 ObjIndexStart = pNodeBlender->GetObjIndexStart(); INT32 ObjIndexEnd = pNodeBlender->GetObjIndexEnd(); // Find the nodes this blender is to blend together NodeRenderableInk* pNodeStart = pNodeBlender->FindObjIndexedNode(ObjIndexStart); NodeRenderableInk* pNodeEnd = pNodeBlender->FindObjIndexedNode(ObjIndexEnd); // They should not be NULL if (pNodeStart == NULL || pNodeEnd == NULL) return FALSE; // Init the blender with the start and end nodes. if (!pNodeBlender->Initialise(pNodeStart,pNodeEnd,PathIndexStart,PathIndexEnd,ImportInfo.pOp,NULL,TRUE)) return FALSE; BlenderCount++; } pChildNode = pChildNode->FindNext(); } if (BlenderCount == 0) { ERROR3("Imported Blend node doesn't contain any blender nodes"); if (!ImportInfo.pOp->DoHideNode(pNodeBlend, TRUE)) return FALSE; } return TRUE; } /******************************************************************************************** > BOOL CamelotEPSFilter::ProcessBlender() Author: Mark_Neves (Xara Group Ltd) Created: 29/11/94 Returns: TRUE if the blender was processed correctly, FALSE if not. Purpose: Reads in all the elements of a blender structure in the EPS file. Errors: Syntax error in EPS file, Out of memory ********************************************************************************************/ BOOL CamelotEPSFilter::ProcessBlender() { if (!StartBlender()) return FALSE; // Keep processing tokens until we find the end of the blender do { GetToken(); // Look for the end of the blender token... if (Token == EPSC_cebr) { return EndBlender(); } } // Otherwise keep going until an error or eof is encountered while (HandleToken() && (!EPSFile->eof())); if (EPSFile->eof()) { // Didn't find end of blender - syntax error; deal with it HandleEPSError(); } // If we're here, something went wrong return FALSE; } /******************************************************************************************** > BOOL CamelotEPSFilter::StartBlender() Author: Mark_Neves (Xara Group Ltd) Created: 29/11/94 Returns: TRUE if the new blender was created ok; FALSE if not. Purpose: Used when a blender structure needs to be created - after this is called, all paths will be discarded, until EndBlender is called. Errors: Out of memory SeeAlso: CamelotEPSFilter::EndBlender ********************************************************************************************/ BOOL CamelotEPSFilter::StartBlender() { INT32 ObjIndexStart,ObjIndexEnd,PathIndexStart,PathIndexEnd; INT32 Reserved1,Reserved2,Reserved3,Reserved4; // Read the blender's params if (!Stack.Pop(&PathIndexEnd) || !Stack.Pop(&PathIndexStart) || !Stack.Pop(&ObjIndexEnd) || !Stack.Pop(&ObjIndexStart) || !Stack.Pop(&Reserved4) || !Stack.Pop(&Reserved3) || !Stack.Pop(&Reserved2) || !Stack.Pop(&Reserved1)) { return FALSE; } // Make a new blender node for this blender NodeBlender *pNodeBlender = new NodeBlender; ERRORIF(pNodeBlender == NULL, _R(IDT_EPS_NOMEMORY), FALSE); // Add it into the tree if (!AddNewNode(pNodeBlender)) return FALSE; // Set the blender params pNodeBlender->SetObjIndexStart(ObjIndexStart); pNodeBlender->SetObjIndexEnd (ObjIndexEnd); pNodeBlender->SetPathIndexStart(PathIndexStart); pNodeBlender->SetPathIndexEnd (PathIndexEnd); // Discard all subsequent paths - until we get an "end blender" token ThePathType = PATH_DISCARD_STICKY; // All ok return TRUE; } /******************************************************************************************** > BOOL CamelotEPSFilter::EndBlender() Author: Mark_Neves (Xara Group Ltd) Created: 29/11/94 Returns: TRUE if the blender was ended ok; FALSE if not. Purpose: Used when a blender has finished being constructed. Path importing is returned to normal (i.e. they're no longer discarded) Errors: - SeeAlso: CamelotEPSFilter::StartBlender ********************************************************************************************/ BOOL CamelotEPSFilter::EndBlender() { ThePathType = PATH_NORMAL; return TRUE; } /******************************************************************************************** > BOOL CamelotEPSFilter::ProcessRegularShape() Author: Mark_Neves (Xara Group Ltd) Created: 29/11/94 Returns: TRUE if the blend was processed correctly, FALSE if not. Purpose: Reads in all the elements of a blend structure in the EPS file. Errors: Syntax error in EPS file, Out of memory ********************************************************************************************/ BOOL CamelotEPSFilter::ProcessRegularShape() { // Make a regular shape node, and use this as a temporary context for storing path // data before passing it into the shape object. pRegularShape = new NodeRegularShape; if (pRegularShape == NULL) return FALSE; if (!pRegularShape->SetUpShape()) { delete pRegularShape; pRegularShape = NULL; return FALSE; } // Add it into the tree if (!AddNewNode(pRegularShape)) { pRegularShape = NULL; return FALSE; } // Get a new ink path to store the shape's edge paths in. ERROR3IF(pInkPath != NULL, "Already creating an InkPath object in regular shape"); pInkPath = new Path; if (pInkPath == NULL) return FALSE; // Set up the shape's path - this is currently 4 elements long - and // move to the start of the path if (!pInkPath->Initialise()) { delete pInkPath; pInkPath = NULL; delete pRegularShape; pRegularShape = NULL; return FALSE; } pInkPath->FindStartOfPath(); // Keep processing tokens until we find the end of the regular shape do { GetToken(); // Look for the end of the regular shape token... if (Token == EPSC_cers) { if (pInkPath != NULL) { // Shape has not been saved correctly if we haven't read in both paths by now. HandleEPSError(); return FALSE; } // Apply current attributes to the shape if (!AddAttributes(pRegularShape, RegularShapeIsStroked, RegularShapeIsFilled)) { HandleNoMemory(); return FALSE; } // Force shape's path to be reconstructed pRegularShape->InvalidateCache(); pRegularShape->InvalidateBoundingRect(); pRegularShape = NULL; return TRUE; } } // Otherwise keep going until an error or eof is encountered while (HandleToken() && (!EPSFile->eof())); if (EPSFile->eof()) { // Didn't find end of regular - syntax error; deal with it HandleEPSError(); } // If we're here, something went wrong return FALSE; } /******************************************************************************************** > BOOL CamelotEPSFilter::ProcessRegularShapeParams() Author: Peter_Arnold (Xara Group Ltd) Created: 2/2/95 Returns: TRUE if the regular shape was processed correctly, FALSE if not. Purpose: Reads in all the parameters of a regular shape structure in the EPS file. Errors: Syntax error in EPS file, Out of memory ********************************************************************************************/ BOOL CamelotEPSFilter::ProcessRegularShapeParams() { // Check we are actually making a regular shape at the moment. if (pRegularShape == NULL) { HandleEPSError(); return FALSE; } UINT32 NumSides; INT32 Circular; INT32 Stellated; INT32 PrimaryCurvature; INT32 StellationCurvature; double StellRadiusToPrimary; double PrimaryCurveToPrimary; double StellCurveToStell; double StellOffsetRatio; DocCoord UTCentrePoint; DocCoord UTMajorAxes; DocCoord UTMinorAxes; //Peter was here INT32 StrokedFlag; INT32 FilledFlag; // Try to get all the parameters off the EPS stack. if (!Stack.Pop(&StrokedFlag) || !Stack.Pop(&FilledFlag) || !Stack.PopCoordPair(&UTMinorAxes) || !Stack.PopCoordPair(&UTMajorAxes) || !Stack.PopCoordPair(&UTCentrePoint) || !Stack.Pop(&StellOffsetRatio) || !Stack.Pop(&StellCurveToStell) || !Stack.Pop(&PrimaryCurveToPrimary) || !Stack.Pop(&StellRadiusToPrimary) || !Stack.Pop(&StellationCurvature) || !Stack.Pop(&PrimaryCurvature) || !Stack.Pop(&Stellated) || !Stack.Pop(&Circular) || !Stack.Pop(&NumSides)) { HandleEPSError(); return FALSE; } else { RegularShapeIsStroked = StrokedFlag; RegularShapeIsFilled = FilledFlag; } // Now set the flag specifying whether the path is filled SetPathFilled(RegularShapeIsFilled); // Set up the shape pRegularShape->SetNumSides(NumSides); pRegularShape->SetCircular(Circular); pRegularShape->SetStellated(Stellated); pRegularShape->SetPrimaryCurvature(PrimaryCurvature); pRegularShape->SetStellationCurvature(StellationCurvature); pRegularShape->SetStellRadiusToPrimary(StellRadiusToPrimary); pRegularShape->SetPrimaryCurveToPrimary(PrimaryCurveToPrimary); pRegularShape->SetStellCurveToStell(StellCurveToStell); pRegularShape->SetStellationRatio(StellOffsetRatio); pRegularShape->SetCentrePoint(UTCentrePoint); pRegularShape->SetMajorAxes(UTMajorAxes); pRegularShape->SetMinorAxes(UTMinorAxes); // All ok return TRUE; } /******************************************************************************************** > BOOL CamelotEPSFilter::ProcessEnvelope() Author: Mike_Kenny (Xara Group Ltd) Created: 14/03/95 Returns: TRUE if the envelope object was processed correctly, FALSE if not. Purpose: Reads in all the elements of an envelope structure in the EPS file. Errors: Syntax error in EPS file, Out of memory ********************************************************************************************/ BOOL CamelotEPSFilter::ProcessEnvelope() { // NOT IMPLEMENTED if (!StartMould(MOULDSPACE_ENVELOPE)) return FALSE; if (ProcessMould()) // If we've completed then all is well return EndMould(); if (EPSFile->eof()) { // Didn't find end of envelope - syntax error; deal with it HandleEPSError(); } // if made it here all is not well return FALSE; } /******************************************************************************************** > BOOL CamelotEPSFilter::ProcessPerspective() Author: Mike_Kenny (Xara Group Ltd) Created: 14/03/95 Returns: TRUE if the perspective object was processed correctly, FALSE if not. Purpose: Reads in all the elements of an perspective structure in the EPS file. Errors: Syntax error in EPS file, Out of memory SeeAlso: ProcessEnvelope() for furher details ********************************************************************************************/ BOOL CamelotEPSFilter::ProcessPerspective() { // NOT IMPLEMENTED if (!StartMould(MOULDSPACE_PERSPECTIVE)) return FALSE; // If we've completed then all is well if (ProcessMould()) return EndMould(); if (EPSFile->eof()) { // Didn't find end of perspective - syntax error; deal with it HandleEPSError(); } // if made it here all is not well return FALSE; } /******************************************************************************************** > BOOL CamelotEPSFilter::StartMould(MouldSpace mSpace) Author: Mike_Kenny (Xara Group Ltd) Created: 14/03/95 Inputs: mSpace = Mould geometry to define, either envelope or perspective Returns: TRUE if the new mould object was created ok; FALSE if not. Purpose: Used when an mould structure needs to be created - after this is called, all new nodes added with AddNewNode() will be added as children of this new mould, until EndMould is called. Errors: Out of memory SeeAlso: CamelotEPSFilter::EndMould ********************************************************************************************/ BOOL CamelotEPSFilter::StartMould(MouldSpace mSpace) { // Read in the version number of this camelot mould INT32 version; if (!Stack.Pop(&version)) return FALSE; // if the version number's not one we recognise then return an error if (version!=100) return FALSE; // create a mould parent and insert it in the tree NodeMould* pMouldParent = new NodeMould; ERROR1IF(pMouldParent == NULL, FALSE, _R(IDT_EPS_NOMEMORY)); // give the parent mould object a shape and mould space to work with and stick it in the tree if (!pMouldParent->CreateGeometry(mSpace)) { delete pMouldParent; return FALSE; } if (!AddNewNode(pMouldParent)) { HandleNoMemory(); return FALSE; } // Make sure new objects are added as children of the mould pNode = pMouldParent; // Make sure the default mould threshold is set correctly EPSFilter::NewMouldThreshold = MOULD_V1THRESHOLD; // All ok return TRUE; } /******************************************************************************************** > BOOL CamelotEPSFilter::EndMould() Author: Mike_Kenny (Xara Group Ltd) Created: 06/03/95 Returns: TRUE if the envelope was ended ok; FALSE if not. Purpose: Used when a mould has finished being constructed and we want to return to normal node positioning. SeeAlso: CamelotEPSFilter::StartMould() ********************************************************************************************/ BOOL CamelotEPSFilter::EndMould() { // Sanity check ERROR3IF(!pNode->IsKindOf(CC_RUNTIME_CLASS(NodeMould)), "No MouldParent in CamelotEPSFilter::EndMould"); // Get a pointer to the mould object NodeMould* pNodeMould = (NodeMould*)pNode; // move pNode up one level pNode = pNode->FindParent(); ERROR3IF(pNode==NULL, "No parent of mould in CamelotEPSFilter::EndMould"); // for loaded docs we need to set the default threshold to the // old 1024 threshold until such time as it changes by way of parsing // the threshold token. This avoids blends from remapping! pNodeMould->GetGeometry()->SetThreshold(EPSFilter::NewMouldThreshold); // We should really have these items, otherwise well, we have nothing whatsoever NodeMouldPath* pNodeMouldPath = (NodeMouldPath*)pNodeMould->FindFirstChild(CC_RUNTIME_CLASS(NodeMouldPath)); NodeMouldGroup* pNodeMouldGroup = (NodeMouldGroup*)pNodeMould->FindFirstChild(CC_RUNTIME_CLASS(NodeMouldGroup)); if (pNodeMouldPath==NULL || pNodeMouldGroup==NULL) { // delete the object and all its children pNodeMould->CascadeDelete(); } // Finally trash any unwanted attributes which may have been added // by SnapShotCurrentAttributes. RemoveUnwantedAttributes(pNodeMould); // All is well and lovely return TRUE; } /******************************************************************************************** BOOL CamelotEPSFilter::RemoveUnwantedAttributes(NodeMould* pNodeMould) Author: Mike_Kenny (Xara Group Ltd) Created: 18/10/95 Inputs: pNodeMould = a pointer to a node mould object Returns: TRUE if all unwanted attributes were removed ok. FALSE if none have been removed Purpose: Removes unwanted attributes added as first children of the node mould. It does this by scanning the children of the nodemouldgroup and determining what attributes are applied here. ********************************************************************************************/ BOOL CamelotEPSFilter::RemoveUnwantedAttributes(NodeMould* pNodeMould) { /* Ok, at this stage, we have a list of attributes applied to the first level of the node mould. All these atts have been applied to avoid them being applied to the mould source objects which would then be moulded (not what we want). Unfortunately we now need to check all attributes applied to the source objects. This is unfortunately a side effect of the method we are using. If we find that there are attributes applied in the source object tree which have been applied to the root, then we delete those at the root. Groan! */ // grab the node mould group. NodeMouldGroup* pNdMldGrp = (NodeMouldGroup*)pNodeMould->FindFirstChild(CC_RUNTIME_CLASS(NodeMouldGroup)); if (pNdMldGrp==NULL) return FALSE; // ok, build a list of attribute pointers. CCAttrMap AttrMap(30); CCRuntimeClass* pTypeInfo; void* pDummy; BuildSubtreeAttrMap(&AttrMap, pNdMldGrp); Node *qNode, *pNode = pNodeMould->FindFirstChild(); while (pNode) { qNode = pNode->FindNext(); if (pNode->IsAnAttribute()) { NodeAttribute* pAttrib = (NodeAttribute*)pNode; pTypeInfo = pAttrib->GetAttributeType(); if (AttrMap.Lookup(pTypeInfo, pDummy)) { pNode->UnlinkNodeFromTree(); delete pNode; } } pNode = qNode; } return TRUE; } /******************************************************************************************** > void CamelotEPSFilter::BuildSubtreeAttrMap(CCAttrMap *pAttribMap, Node* pParent) Author: Mike_Kenny (Xara Group Ltd) Created: 18/10/95 Inputs: pAttribMap = a pointer to an attribute map pParent = a pointer to the parent node Returns: - Purpose: Adds all attribute pointers to the pAttribMap which it finds as children of pParent. Notes: Told off by MarkN for not commenting ... oopps ********************************************************************************************/ void CamelotEPSFilter::BuildSubtreeAttrMap(CCAttrMap *pAttribMap, Node* pParent) { CCRuntimeClass* pTypeInfo; void* pDummy; Node* qNode = pParent->FindFirstChild(); while (qNode) { if (qNode->IsAnAttribute()) { NodeAttribute* pAttrib = (NodeAttribute*)qNode; pTypeInfo = pAttrib->GetAttributeType(); if (!pAttribMap->Lookup(pTypeInfo, pDummy)) pAttribMap->SetAt(pTypeInfo, qNode); } if (qNode->FindFirstChild()) { BuildSubtreeAttrMap(pAttribMap, qNode); } qNode = qNode->FindNext(); } } /******************************************************************************************** > BOOL CamelotEPSFilter::ProcessMould() Author: Mike_Kenny (Xara Group Ltd) Created: 06/03/95 Returns: TRUE if the mould was processed ok; FALSE if not. Purpose: Processes tokens inside a mould object. Tokens specific to mould objects are processed along with some overridding of default tokens such as groups SeeAlso: ********************************************************************************************/ BOOL CamelotEPSFilter::ProcessMould() { // Try to make a snap shot of the current attribute state SnapShotCurrentAttrs(); // process the mould BOOL success=ProcessMouldTokens(); // Get rid of the snap shot again. DeleteSnapShot(); return success; } /******************************************************************************************** > BOOL CamelotEPSFilter::ProcessMouldTokens() Author: Mike_Kenny (Xara Group Ltd) Created: 06/03/95 Returns: TRUE if the mould was processed ok; FALSE if not. Purpose: Processes tokens inside a mould object. Tokens specific to mould objects are processed along with some overridding of default tokens such as groups SeeAlso: ********************************************************************************************/ BOOL CamelotEPSFilter::ProcessMouldTokens() { // Keep processing tokens until we find the end of the envelope while (!EPSFile->eof()) { GetToken(); // Look for the end of the mould token... switch (Token) { case EPSC_ceev: case EPSC_cepr: // found the end of the mould so return done. return TRUE; break; case EPSC_csmp: // if we've found the envelope/perspective end then // all is well. if (!ProcessMouldShape()) return FALSE; break; case EPSC_csso: { // Remove all current attributes which don't match snap shot // saving their values on the stack. (These should not be applied) // (See ApplyChangedAttrs call below) PushCurrentAttrsBasedOnSnapShot(); // Process all mould source objects BOOL Success = ProcessMouldSourceObjs(); // Put back the current attrs how they were. PopCurrentAttrsBasedOnSnapShot(); if (!Success) return FALSE; } break; case EPSC_csdo: // Create next object as a child if (!ProcessMouldDestinObjs()) return FALSE; break; default: // try to handle whatever token this is. if (!HandleToken()) return FALSE; break; } } HandleEPSError(); return FALSE; } /******************************************************************************************** > BOOL CamelotEPSFilter::ProcessMouldThreshold() Author: Mike_Kenny (Xara Group Ltd) Created: 12/06/95 Returns: TRUE if the threshold was processed correctly, FALSE if not. Purpose: Reads in all the elements of a mould threshold structure in the EPS file. The threshold structure is defined as an extension token as it only came into existance after version 1.1 documents. This function is called from the extension token parser. We read the threshold value and set the EPSFilter variable NewMouldThreshold. Having read in the mould, the threshold will be read from this variable and overwrite the default constructed threshold. Errors: Syntax error in EPS file, Out of memory ********************************************************************************************/ BOOL CamelotEPSFilter::ProcessMouldThreshold() { EPSFilter::NewMouldThreshold = MOULD_V1THRESHOLD; while (!EPSFile->eof()) { GetToken(); switch (Token) { case EPSC_ceo: { // found the end of the threshold block so return done. return TRUE; } break; case EPSC_cmth: { INT32 Threshold; if (!Stack.Pop(&Threshold)) { HandleEPSError(); return FALSE; } EPSFilter::NewMouldThreshold = Threshold; } break; default: { // try to handle whatever token this is. if (!HandleToken()) return FALSE; } break; } } HandleEPSError(); return FALSE; } /******************************************************************************************** > BOOL CamelotEPSFilter::ProcessMouldShape() Author: Mike_Kenny (Xara Group Ltd) Created: 06/03/94 Returns: TRUE if the shape has been read correctly, FALSE if not. Purpose: Reads in all the elements of a mould geometry held in the EPS file. The description of the geometry for this particular file type is that of a path. Input Stack = coord, coord Errors: Syntax error in EPS file, Out of memory ********************************************************************************************/ BOOL CamelotEPSFilter::ProcessMouldShape() { // We really should be inside a mould object at this stage if (!IS_A(pNode,NodeMould)) return FALSE; NodeMould* pNodeMould = (NodeMould*)pNode; Path TempPath; if (!TempPath.Initialise(24,12)) return FALSE; BOOL done = FALSE; // Keep processing tokens until we find the end of the mould path while (!done && (!EPSFile->eof())) { GetToken(); switch (Token) { case EPSC_m: if (!ReadSimplePath(&TempPath)) return FALSE; break; case EPSC_cemp: done=TRUE; break; default: // try to handle whatever token this is. if (!HandleToken()) return FALSE; break; } } if (EPSFile->eof()) { HandleEPSError(); return FALSE; } // We have read the path so now lets try and build the geometry // check the shape we've been given is fine and lovely UINT32 errID; if (!pNodeMould->GetGeometry()->Validate(&TempPath,errID)) { // ok the path we read is invalid so lets try to build a valid one Path* pPath = NULL; INT32 Corners[4] = {0, 3, 6, 9}; if (!pNodeMould->GetGeometry()->MakeValidFrom(&pPath, &TempPath, Corners)) return FALSE; if (!TempPath.CloneFrom(*pPath)) { delete pPath; return FALSE; } delete pPath; } // Build and if necessary fit the geometry NodeMouldPath* pNodeMPath = pNodeMould->CreateNewMouldShape(&TempPath,NULL,ImportInfo.pOp); if (pNodeMPath==NULL) return FALSE; // having created the shape bung it in the tree. if (!AddNewNode(pNodeMPath)) { delete pNodeMPath; return FALSE; } /* Ok, what we need to do now is apply the current set of attributes as first children of the pNodeMould. This will apply all none default attributes at this level. We need to do this as we dont want the current attributes as they are now to be applied to objects within the source mould object tree. If they do get applied there, they will be moulded and will look wrong. Background: There is a problem I'm fixing here. Currently you can apply attributes to moulds eg dragging a linear fill over a selected mould. This creates an attribute as a first child of the mould, outside the source object group which gets moulded. So the att is not moulded. Right, when you save, these atts are put into the file before the nodemouldpath. On loading currently all atts are gathered and applied to each object as it is created. This means atts that weren't moulded migrate onto objects which will be moulded and get moulded. yuk. So what I am doing is to save the CurrentAttrs state when we find a start mould token. Then when we find the nodemouldpath, apply all the current atts and reset the current set back to the saved set. That means all the atts that appear before the nodemouldpath dont get applied to the source objects and only those that appear later do. */ if (!ApplyChangedAttrs(pNodeMould)) return FALSE; // All is well. return TRUE; } /******************************************************************************************** > BOOL CamelotEPSFilter::ReadSimplePath(Path* TempPath) Author: Mike_Kenny (Xara Group Ltd) Created: 14/03/95 Returns: TRUE if the path shape has been read FALSE if not. Purpose: Reads in the defined mould path shape. Errors: Syntax error in EPS file, Out of memory ********************************************************************************************/ BOOL CamelotEPSFilter::ReadSimplePath(Path* TempPath) { DocCoord Ta,Tb,Tc; PathFlags flags; flags.IsSelected = FALSE; BOOL ok, done = FALSE; // ok, pop off the move stacked coordinate if (!Stack.PopCoordPair(&Ta)) return FALSE; // Make sure there's absolutely no elements in this path TempPath->ClearPath(FALSE); // Add this first element as a move to if (!TempPath->AddMoveTo(Ta, &flags)) return FALSE; // Keep processing tokens until we find the end of the mould path while (!done && (!EPSFile->eof())) { GetToken(); switch (Token) { case EPSC_l: // lineto (x,y) found ok = Stack.PopCoordPair(&Ta); if (ok) ok = TempPath->AddLineTo(Ta, &flags); break; case EPSC_c: // curveto (x0,y0,x1,y1,x2,y2) found ok = Stack.PopCoordPair(&Tc); if (ok) ok = Stack.PopCoordPair(&Tb); if (ok) ok = Stack.PopCoordPair(&Ta); if (ok) ok = TempPath->AddCurveTo(Ta,Tb,Tc,&flags); break; case EPSC_s: case EPSC_f: case EPSC_b: case EPSC_h: case EPSC_n: case EPSC_S: case EPSC_F: case EPSC_B: case EPSC_H: case EPSC_N: // found a stroked close path. That should be it for path elements ok = TempPath->CloseSubPath(); done = TRUE; break; default: // try to handle whatever token this is. ok = HandleToken(); break; } // if someones failed return. if (!ok) return FALSE; } // All is well return TRUE; } /******************************************************************************************** > BOOL CamelotEPSFilter::ProcessMouldSourceObjs() Author: Mike_Kenny (Xara Group Ltd) Created: 14/03/95 Returns: TRUE if the source objects have been read correctly, FALSE if not. Purpose: Reads in all the objects within the mould source grouping. Errors: Syntax error in EPS file, Out of memory ********************************************************************************************/ BOOL CamelotEPSFilter::ProcessMouldSourceObjs() { // Sanity check ERROR3IF(!pNode->IsKindOf(CC_RUNTIME_CLASS(NodeMould)), "No MouldParent in CamelotEPSFilter::ProcessMouldSourceObjs"); NodeMould* pNodeMould = (NodeMould*)pNode; // Create a mould group object NodeMouldGroup* pMouldGroup = pNodeMould->CreateNewMouldGroup(ImportInfo.pOp); if (pMouldGroup==NULL) return FALSE; if (!AddNewNode(pMouldGroup)) { delete pMouldGroup; return FALSE; } // save the old insert position and position to our group Node* pOldPos = pNode; pNode = pMouldGroup; // scan the tokens until we find the one we want BOOL done = FALSE; while (!done && (!EPSFile->eof())) { GetToken(); if (!(done=(Token==EPSC_ceso))) { if (!HandleToken()) { pNode=pOldPos; return FALSE; } } } // reposition our magic insert position pNode = pOldPos; if (EPSFile->eof()) { HandleEPSError(); return FALSE; } return TRUE; } /******************************************************************************************** > BOOL CamelotEPSFilter::ProcessMouldDestinObjs() Author: Mike_Kenny (Xara Group Ltd) Created: 14/03/95 Returns: TRUE if the source objects have been read correctly, FALSE if not. Purpose: Reads in all the objects within the mould destination grouping. Errors: Syntax error in EPS file, Out of memory ********************************************************************************************/ BOOL CamelotEPSFilter::ProcessMouldDestinObjs() { // Sanity check ERROR3IF(!pNode->IsKindOf(CC_RUNTIME_CLASS(NodeMould)), "No MouldParent in CamelotEPSFilter::ProcessMouldDestinObjs"); NodeMould* pNodeMould = (NodeMould*)pNode; // create a new moulder object NodeMoulder* pMoulder = pNodeMould->CreateNewMoulder(ImportInfo.pOp); if (!pMoulder) return FALSE; // Add it into the tree as the last child of the mould if (!AddNewNode(pMoulder)) return FALSE; // save the old insert position Node* pOldPos=pNode; pNode=pMoulder; // scan the tokens until we find the one we want BOOL done=FALSE; while (!done && (!EPSFile->eof())) { GetToken(); if (!(done=(Token==EPSC_cedo))) { if (!HandleToken()) { pNode=pOldPos; return FALSE; } } } // reposition our magic insert position pNode=pOldPos; if (EPSFile->eof()) { HandleEPSError(); return FALSE; } return TRUE; } /******************************************************************************************** > BOOL CamelotEPSFilter::ProcessGuideLayer() Author: Mark_Neves (Xara Group Ltd) Created: 11/10/95 Returns: TRUE if the guide layer was processed correctly, FALSE if not. Purpose: Reads in a guide layer object Errors: Syntax error in EPS file, Out of memory ********************************************************************************************/ BOOL CamelotEPSFilter::ProcessGuideLayer() { String_64 GuideColourName; PColourCMYK CMYK; FIXEDPOINT TintVal; String_256 LayerName; INT32 Visible; INT32 Locked; INT32 Foreground; INT32 Printable; // Read in the guide layer params if (!Stack.PopColour(&CMYK, TINT_ILLUSTRATOR, &TintVal, &GuideColourName) || !Stack.Pop(&Locked) || !Stack.Pop(&Printable) || !Stack.Pop(&Visible) || !Stack.Pop(&Foreground) || !Stack.Pop(&LayerName)) { return FALSE; } #if defined(EXCLUDE_FROM_RALPH) // In ralph we dont want to import guideline layers #else if (Filter::ImportWithLayers) { // We are importing layers, so put all new nodes on this layer. UseLayer(LayerName,TRUE); // Try to set the visible/locked flags on this layer, but only if // we created a new one (i.e. don't change the flags of existing layers. if (EPSFlags.AddToNewLayer) { // Here we force layers to be printable if they are in the foreground - just in case // This will have to change if the UI allows Printable & Foreground to be specified // independantly. Printable = Foreground; // Visible flag // Only relevant for non-guide layers. Guide layer visibility is stored in the DocView // and is saved in the document comments header // Locked flag pLayer->SetLocked(!(Locked == 0)); // Printable flag pLayer->SetPrintable(!(Printable == 0)); // Background flag pLayer->SetBackground(Foreground == 0); DocColour Col; GetEPSColour(&Col,&CMYK,TINT_ILLUSTRATOR,TintVal,&GuideColourName); IndexedColour* pIndexedColour = Col.FindParentIndexedColour(); pLayer->SetGuideColour(pIndexedColour); } } #endif return TRUE; } /******************************************************************************************** > BOOL CamelotEPSFilter::ProcessGuideline() Author: Mark_Neves (Xara Group Ltd) Created: 11/10/95 Returns: TRUE if the guideline was processed correctly, FALSE if not. Purpose: Reads in a guide line object Errors: Syntax error in EPS file, Out of memory ********************************************************************************************/ BOOL CamelotEPSFilter::ProcessGuideline() { INT32 Type, Ordinate; // Read in the guideline's params if (!Stack.Pop(&Type) || !Stack.Pop(&Ordinate)) { return FALSE; } #ifdef WEBSTER // Neville 8/8/97 // If we aren't importing with layers into Webster, which we wont by default, // then ignore any guidelines, as these should only be loaded onto guide layers if (!Filter::ImportWithLayers) return TRUE; #endif NodeGuideline* pGuideline = new NodeGuideline; ERROR1IF(pGuideline == NULL, FALSE, _R(IDT_EPS_NOMEMORY)); // Add it into the tree if (!AddNewNode(pGuideline)) return FALSE; pGuideline->SetType(GuidelineType(Type)); pGuideline->SetOrdinate(Ordinate); return TRUE; } //////////////////////////////////////////////////////////////////////////////////////////// // EPSFontCache methods. /******************************************************************************************** > EPSFontCache::EPSFontCache() Author: Tim_Browse (Xara Group Ltd) Created: 05/30/95 Purpose: Set up the EPS font cache to be initially invalid. SeeAlso: CamelotEPSRenderRegion ********************************************************************************************/ EPSFontCache::EPSFontCache() { // Font cache is invalid Valid = FALSE; }; #if !defined(EXCLUDE_FROM_RALPH) //////////////////////////////////////////////////////////////////////////////////////////// // CamelotEPSRenderRegion methods. CC_IMPLEMENT_DYNAMIC(CamelotEPSRenderRegion, ArtWorksEPSRenderRegion) /******************************************************************************************** > CamelotEPSRenderRegion(DocRect ClipRect, Matrix ConvertMatrix, FIXED16 ViewScale) Author: Tim_Browse (Xara Group Ltd) Created: 15/04/94 Purpose: Initialise a render region for exporting Camelot EPS. Sets up the string to put in the %%Creator comment. SeeAlso: EPSRenderRegion::EPSRenderRegion ********************************************************************************************/ CamelotEPSRenderRegion::CamelotEPSRenderRegion(DocRect ClipRect, Matrix ConvertMatrix, FIXED16 ViewScale) : ArtWorksEPSRenderRegion(ClipRect, ConvertMatrix, ViewScale) { CreatorString = _T("Xara X"); SepTables = NULL; } /******************************************************************************************** > CamelotEPSRenderRegion::~CamelotEPSRenderRegion() Author: Jason_Williams (Xara Group Ltd) Created: 27/6/96 Purpose: destructor ********************************************************************************************/ CamelotEPSRenderRegion::~CamelotEPSRenderRegion() { if (SepTables != NULL) { CCFree(SepTables); SepTables = NULL; } } /******************************************************************************************** > BOOL CamelotEPSRenderRegion::StartRender() Author: Tim_Browse (Xara Group Ltd) Created: 30/03/94 Returns: TRUE if worked, FALSE if failed. Purpose: Prepare the render region for rendering (exporting). SeeAlso: EPSRenderRegion::Initialise; EPSRenderRegion::StopRender ********************************************************************************************/ BOOL CamelotEPSRenderRegion::StartRender() { // (ChrisG 3/1/01) - We no longer write out the restore/save pair, so that multiple objects // (both simple and complex) can be clipped together. This now works the same as the // printing (see PrintPSRenderRegion::StartRender) // This should only be done around areas where there is a possibility of the VM being // modified without our knowledge, e.g. when including EPS files inside this file, using // third-party functions, etc..., and the save should be done immediately before the // suspect block, and the restore immediately after. // Call base class first if (!EPSRenderRegion::StartRender()) return FALSE; InitAttributes (); return TRUE; } /******************************************************************************************** > BOOL CamelotEPSRenderRegion::StopRender() Author: Tim_Browse (Xara Group Ltd) Created: 30/03/94 Purpose: Deinitialise the render region after rendering (exporting). SeeAlso: EPSRenderRegion::Deinitialise; EPSRenderRegion::StartRender ********************************************************************************************/ BOOL CamelotEPSRenderRegion::StopRender() { // (ChrisG 3/1/01) - We no longer write out the restore/save pair, so that multiple objects // (both simple and complex) can be clipped together. This now works the same as the // printing (see CamelotEPSRenderRegion::StartRender for more info) if (!IS_A(this, NativeRenderRegion)) { // Since we're clearing the the current attributes in the postscript file (using // restore), we'd better clear them in the exporter, so that they are re-exported // if needed. NOTE: This only seems to affect font info. FontInfo.Valid = 0; } if (SepTables != NULL) { CCFree(SepTables); SepTables = NULL; } DeInitAttributes (); return TRUE; } /******************************************************************************************** > virtual void CamelotEPSRenderRegion::GetRenderRegionCaps(RRCaps* pCaps) Author: Tim_Browse (Xara Group Ltd) Created: 10/4/95 Outputs: pCaps - The details about what types of thing this render region can render Purpose: This function allows render regions to admit to what they can and can not render. This allows other areas of the program to come in and help render regions out in some situations, if they are unable to render everything. eg. an OSRenderRegion can not render transparancy. ********************************************************************************************/ void CamelotEPSRenderRegion::GetRenderRegionCaps(RRCaps* pCaps) { // We can do grad fills, and most kinds of bitmaps. pCaps->CanDoNothing(); pCaps->GradFills = TRUE; pCaps->SimpleBitmaps = TRUE; pCaps->ArbitraryBitmaps = TRUE; pCaps->ClippedSimpleBitmaps = TRUE; pCaps->ClippedArbitraryBitmaps = TRUE; pCaps->ClippedOutput = TRUE; } /******************************************************************************************** > void CamelotEPSRenderRegion::GetValidPathAttributes() Author: Tim_Browse (Xara Group Ltd) Created: 30/03/94 Purpose: See EPSRenderRegion::GetValidPathAttributes. This version checks and handles grad fill colours before calling the base class version. SeeAlso: EPSRenderRegion::GetValidPathAttributes ********************************************************************************************/ void CamelotEPSRenderRegion::GetValidPathAttributes() { KernelDC *pDC = (KernelDC*)CCDC::ConvertFromNativeDC(RenderDC); FillGeometryAttribute *pFillAttr = (FillGeometryAttribute *) CurrentAttrs[ATTR_FILLGEOMETRY].pAttr; if (pFillAttr->IsKindOf(CC_RUNTIME_CLASS(GradFillAttribute)) && SetLastOutputAttribute(ATTR_FILLGEOMETRY)) { // If the fill is square if (!pFillAttr->IsASquareFill() && !pFillAttr->IsAThreeColFill()) { // Get the correct brush - may be a grad fill. if (//pFillAttr->IsKindOf(CC_RUNTIME_CLASS(GradFillAttribute)) && !pFillAttr->IsKindOf(CC_RUNTIME_CLASS(BitmapFillAttribute)) ) { // Output a grad fill command... // Get the start colour and end colour in CMYK, and output them. GradFillAttribute *pGradFillAttr = (GradFillAttribute *) pFillAttr; // Check if the colours are named or not. BOOL UnnamedColours = FALSE; BOOL GreyFill = FALSE; // During a normal CamEPS export we will not be separating, hence // pSeparation will always return NULL here. If however we are // currently printing we need to perform some giggery pokery with // grad fills in order to separate them correctly. // We have two fill operators we can use in Postscript, these // are cax, a colour fill op and cag a grey fill op. // There are three items of interest to us which we need to consider // before choosing to use one of the postscript fill operators, these // are // (1) the end colours of the fill (the model etc) // (2) the fill effect across the grad fill // (3) the output plate we're rendering to. // // We need to work out what happens on both cmyk and spot plates when // using one of these operators. For instance when using a grey fill // operator, the fill effect will not be used. It may go out in the // output stream, but it will be ignored in Postscript (because you // are using a grey fill of course!). // You also need to consider that if you use the colour fill operator // you are in effect restricting output to the process colour plates. // Nothing will appear on spot plates. The colour operator works by // performing a separation itself, using the current fill effect and // wandering about in rgb space. // You also need to know the rules for interaction between mixed colours // during a separation, ie if we have a rgb colour to spot colour fill // with a rainbow fill effect, question which should immediately leap/ // to mind are // (1) do we use the fill effect // (2) what fill is used on the spot plate // (3) what fill is used on the process plates // (4) how will this all look, is it sensible? // We should really define a virtual function for this operation and // override it in PrintPSRenderRegion. ColourContext* pContext; ColourPlate* pSeparation; GetOutputColourPlate(COLOURMODEL_CMYK, &pContext, &pSeparation); if (pSeparation!=NULL) { DocColour *Start, *End; Start = &(pGradFillAttr->Colour); End = &(pGradFillAttr->EndColour); BOOL StartIsSpot = (Start->GetSpotParent() != NULL); BOOL EndIsSpot = (End->GetSpotParent() != NULL); // If we are rendering a spot plate GreyFill = (pSeparation->GetType() == COLOURPLATE_SPOT); // or either end of this fill are spot... use the gray fill operator // This will mean process colour will be separated and provide a linear // fill to white on none spot plates. GreyFill = (GreyFill || StartIsSpot || EndIsSpot); if (!GreyFill) { // ok we're on a process plate, and both ends are not spot // are both ends CMYK colours with a simple fill effect? GreyFill = ( Start->GetColourModel() == COLOURMODEL_CMYK ) && ( End->GetColourModel() == COLOURMODEL_CMYK ); if (GreyFill) { // Is there a crazy fill effect pending? UINT32 filleffect; BOOL exists = GetCurrFillEffect(filleffect); GreyFill = (!exists || filleffect == FILLEFFECT_FADE); } } } // if GreyFill is true we output this fill as a single plate gray fill // otherwise we splunge it across the process plates. if (GreyFill) { UnnamedColours = TRUE; // both of these colours are cmyk so we need to handle the separation // slightly differently. The user will expect these colours to spread // without going through rgb=>devcmyk to do so. PColourCMYK sCMYK, eCMYK; pGradFillAttr->Colour.GetCMYKValue(pContext, &sCMYK); pGradFillAttr->EndColour.GetCMYKValue(pContext, &eCMYK); // Sanity check on this plate ERROR3IF( (sCMYK.Cyan!=0 || sCMYK.Magenta!=0 || sCMYK.Yellow!=0 || eCMYK.Cyan!=0 || eCMYK.Magenta!=0 || eCMYK.Yellow!=0) , "Not a mono plate during export of CMYK gradfills (CAMEPSRndRgn::GetValidPathAttributes())" ); // output a linear key fill. BOOL ok = pDC->OutputColourValue(255 - sCMYK.Key); ok = ok && pDC->OutputColourValue(255 - eCMYK.Key); // This entire function seems to ignore disc failure which is a bit rampant! ERROR3IF(!ok, "Disc buffer failure during gradfill output in GetValidPathAttributes"); } else { // This is a mixture of colour spaces so simply output the // who lot as normal. Postscript will spread the colour UnnamedColours = OutputGradFillColours( &(pGradFillAttr->Colour), &(pGradFillAttr->EndColour) ); } // Output the start and end points of the grad fill pDC->OutputCoord(pGradFillAttr->StartPoint); pDC->OutputCoord(pGradFillAttr->EndPoint); // Output the fill type if (pFillAttr->IsKindOf(CC_RUNTIME_CLASS(LinearFillAttribute))) { LinearFillAttribute *pLinearFillAttr = (LinearFillAttribute *) pFillAttr; DocCoord* Start = pFillAttr->GetStartPoint(); DocCoord* End = pFillAttr->GetEndPoint(); DocCoord* End2 = pFillAttr->GetEndPoint2(); if (AreLinesPerpendicular(Start, End, End2)) { TRACEUSER( "Will", _T("Exporting Simple Linear Fill\n")); pDC->OutputValue((INT32) CAMEPS_FILL_LINEAR); } else { TRACEUSER( "Will", _T("Exporting New Style Linear Fill\n")); pDC->OutputCoord(pLinearFillAttr->EndPoint2); pDC->OutputValue((INT32) CAMEPS_FILL_NEWLINEAR); } } else if (pFillAttr->IsKindOf(CC_RUNTIME_CLASS(RadialFillAttribute))) { // Is it circular or elliptical? RadialFillAttribute *pRadialFillAttr = (RadialFillAttribute *) pFillAttr; if (pRadialFillAttr->IsElliptical()) { // Elliptical fill - output the second end point. pDC->OutputCoord(pRadialFillAttr->EndPoint2); pDC->OutputValue((INT32) CAMEPS_FILL_ELLIPTICAL); } else { // Circular fill pDC->OutputValue((INT32) CAMEPS_FILL_CIRCULAR); } } else if (pFillAttr->IsKindOf(CC_RUNTIME_CLASS(ConicalFillAttribute))) { pDC->OutputValue((INT32) CAMEPS_FILL_CONICAL); } else { // Bodge: ought to handle this more gracefully soon... ENSURE(FALSE, "Unsupported grad fill encountered while exporting"); EPSRenderRegion::GetValidPathAttributes(); return; } // Output the grad fill token if (UnnamedColours) { if (GreyFill) pDC->OutputToken(_T("cag")); else pDC->OutputToken(_T("caz")); } else { pDC->OutputToken(_T("cax")); } pDC->OutputNewLine(); } else if ( // Utterly awful code by WillC (pFillAttr->IsKindOf(CC_RUNTIME_CLASS(FractalFillAttribute)) || pFillAttr->IsKindOf(CC_RUNTIME_CLASS(NoiseFillAttribute))) && Caps.BitmapFills ) { // Fractal fill - output fractal fill command... //************************************************************************* // this piece of code is copied from the bitmap export but differs // only very slightly. Is this difference a bug or intended? We will never // know. // first output the base class fill stuff... BitmapFillAttribute *pFill = (BitmapFillAttribute *) pFillAttr; // Get the start colour and end colour in CMYK, and output them. // Check if the colours are named or not. BOOL UnnamedColours = FALSE; ColourContext *cc = NULL; // (ChrisG 8/12/00) All non-grey grad fill colours are now exported by the // OutputGradFillColours function. if ((pFill->Colour.FindParentIndexedColour() == NULL) && (pFill->EndColour.FindParentIndexedColour() == NULL)) { // Neither colour is named...use unnamed colour syntax. cc = ColourManager::GetColourContext(COLOURMODEL_CMYK, GetRenderView()); ERROR3IF(cc == NULL, "No CMYK colour context - somethings' seriously broken"); } else { cc = NULL; } // Output the colours (and names) UnnamedColours = OutputGradFillColours (&(pFill->Colour), &(pFill->EndColour), cc); // Next the coords of the fill pDC->OutputCoord(pFill->StartPoint); pDC->OutputCoord(pFill->EndPoint); pDC->OutputCoord(pFill->EndPoint2); //************************************************************************* // ok NoiseFillAttributes are saved as FractalFillAttributes. Strange you // might say, aha! but NoiseFillAttributes are new objects not supported by // camelot 1.1 so what else should we do? Hmm not sure at this point. // Save the fractal parameters out. pDC->OutputValue(pFill->GetSeed()); pDC->OutputReal(pFill->GetGraininess().MakeDouble()); pDC->OutputReal(pFill->GetGravity().MakeDouble()); pDC->OutputReal(pFill->GetSquash().MakeDouble()); // And now the DPI of the fractal bitmap UINT32 DPI = pFill->GetFractalDPI(); pDC->OutputValue(DPI); // And now the 'tileable' flag pDC->OutputValue((INT32) (pFill->GetTileable())); // Always Fractal type 1 at present pDC->OutputToken(_T("1")); // This is a fractal fill pDC->OutputValue((INT32) CAMEPS_FILL_FRACTAL); if (UnnamedColours) pDC->OutputToken(_T("caz")); else pDC->OutputToken(_T("cax")); pDC->OutputNewLine(); } else if (pFillAttr->IsKindOf(CC_RUNTIME_CLASS(BitmapFillAttribute)) && Caps.BitmapFills) { // Bitmap fill - output bitmap fill command... BitmapFillAttribute *pBitmapFill = (BitmapFillAttribute *) pFillAttr; ERROR3IF(pBitmapFill->GetBitmap() == NULL, "Bitmap fill has no bitmap"); if (pBitmapFill->GetBitmap()) { // Get the start colour and end colour in CMYK, and output them. // Check if the colours are named or not. BOOL UnnamedColours = FALSE; BOOL HasColours = ((pBitmapFill->GetStartColour() != NULL) && (pBitmapFill->GetEndColour() != NULL)); if (HasColours) { // (ChrisG 8/12/00) All non-grey grad fill colours are now exported by the // OutputGradFillColours function. ColourContext *cc = NULL; if ((pBitmapFill->Colour.FindParentIndexedColour() == NULL) && (pBitmapFill->EndColour.FindParentIndexedColour() == NULL)) { // Neither colour is named...use unnamed colour syntax. cc = ColourManager::GetColourContext(COLOURMODEL_CMYK, GetRenderView()); ERROR3IF(cc == NULL, "No CMYK colour context - somethings' seriously broken"); } else { // One or two of the colours are named, so use the named colour syntax. cc = NULL; } // Write out the colours themselves. UnnamedColours = OutputGradFillColours (&(pBitmapFill->Colour), &(pBitmapFill->EndColour), cc); } pDC->OutputCoord(pBitmapFill->StartPoint); pDC->OutputCoord(pBitmapFill->EndPoint); pDC->OutputCoord(pBitmapFill->EndPoint2); if (HasColours) pDC->OutputValue((INT32) CAMEPS_FILL_NEWBITMAP); else pDC->OutputValue((INT32) CAMEPS_FILL_BITMAP); if (UnnamedColours) pDC->OutputToken(_T("caz")); else pDC->OutputToken(_T("cax")); pDC->OutputNewLine(); // ...and then the bitmap itself. ExportDC *pExportDC = (ExportDC *) pDC; pExportDC->GetParentFilter()->ExportBitmap(*pBitmapFill->GetBitmap()); } } } } if (SetLastOutputAttribute(ATTR_FILLEFFECT)) { UINT32 EffectType; GetCurrFillEffect(EffectType); // Output fill effect pDC->OutputValue(EffectType); // Output the fill effect token pDC->OutputToken(_T("cxe")); pDC->OutputNewLine(); } if (SetLastOutputAttribute(ATTR_FILLMAPPING)) { // Now do the chromatic fill mapping FillMappingAttribute *pFillAttr = (FillMappingAttribute *) CurrentAttrs[ATTR_FILLMAPPING].pAttr; // Get the correct mapping INT32 MappingType = pFillAttr->Repeat; //Mark Howitt, 9/10/97. If we are exporting, make sure that repeating grad fill is saved as simple. #ifdef _DEBUG if(MappingType == 4) MappingType = 2; #endif ERROR3IF((MappingType < 1) || (MappingType > 3), "Illegal fill mapping value!"); // Output fill mapping pDC->OutputValue((UINT32) MappingType); // Allow for future extension of fill mappings. pDC->OutputToken(_T("0")); // Output the fill mapping token pDC->OutputToken(_T("cxm")); pDC->OutputNewLine(); } if (SetLastOutputAttribute(ATTR_WINDINGRULE)) { WindingRuleAttribute *pWindingRuleAttr = (WindingRuleAttribute *) CurrentAttrs[ATTR_WINDINGRULE].pAttr; switch (pWindingRuleAttr->WindingRule) { case NonZeroWinding: // Change winding rule to 0, which means non-zero in Camelot EPS. pDC->OutputToken(_T("0")); pDC->OutputToken(_T("awr")); pDC->OutputNewLine(); break; case EvenOddWinding: // Change winding rule to 1, which means even-odd in Camelot EPS. pDC->OutputToken(_T("1")); pDC->OutputToken(_T("awr")); pDC->OutputNewLine(); break; case NegativeWinding: case PositiveWinding: // Not supported in Camelot EPS - ignore. break; default: ERROR3("Unknown winding rule encountered while exporting."); break; } } // Leave arrows/dash patterns to the base class. // Handle usual pens/brushes EPSRenderRegion::GetValidPathAttributes(); } /******************************************************************************************** > BOOL CamelotEPSRenderRegion::OutputGradFillColours(DocColour* StartCol, DocColour* EndCol ColourContext pContext = NULL) Author: Mike_Kenny (Xara Group Ltd) Created: 24/06/96 Inputs: StartCol = the grad fill start colour EndCol = the grad fill end colour pContext = a pointer to a local colour context (defaults to NULL, in which case a global default context will be used instead) This context MUST be a CMYK context Outputs: - Returns: TRUE if both colours were unnamed colours; FALSE if not Purpose: Output the colour definitions of the start and end colours to be used in a graduated fill. ********************************************************************************************/ BOOL CamelotEPSRenderRegion::OutputGradFillColours(DocColour* StartCol, DocColour* EndCol, ColourContext* pContext) { KernelDC *pDC = (KernelDC*)CCDC::ConvertFromNativeDC(RenderDC); bool outputNames = FALSE; if ((StartCol->FindParentIndexedColour() == NULL) && (EndCol->FindParentIndexedColour() == NULL)) { // Neither colour is named...use unnamed colour syntax. outputNames = FALSE; } else { // One or two of the colours are named, so use the named colour syntax. outputNames = TRUE; } // Start writing stuff to the EPS file. INT32 red = 0; INT32 green = 0; INT32 blue = 0; // Write out the start colour StartCol->GetRGBValue (&red, &green, &blue); pDC->OutputColourValue (red); pDC->OutputColourValue (green); pDC->OutputColourValue (blue); if (outputNames) { // Write out the name pDC->OutputColourName (StartCol); // Write out the tint value. This isn't actually used at the moment, but is left // for possible future expansion pDC->OutputValue ((INT32)0); } // And the End colour. EndCol->GetRGBValue (&red, &green, &blue); pDC->OutputColourValue(red); pDC->OutputColourValue(green); pDC->OutputColourValue(blue); if (outputNames) { // Write out the name pDC->OutputColourName (EndCol); // Write out the tint value. This isn't actually used at the moment, but is left // for possible future expansion pDC->OutputValue ((INT32)0); } // Return TRUE for no names used, FALSE for names used. return (!outputNames); } /******************************************************************************************** > UINT32 CamelotEPSRenderRegion::GetCurrFillEffect(UINT32& EffectType) Author: Mike_Kenny (Xara Group Ltd) Created: 24/06/96 Inputs: - Outputs: The current type of fill effect Returns: TRUE if a fill effect exists in the current attrs stack FALSE if not Purpose: Return the state of the current fill effect in the EPS attributes stack ********************************************************************************************/ BOOL CamelotEPSRenderRegion::GetCurrFillEffect(UINT32& EffectType) { // set default EffectType = FILLEFFECT_FADE; FillEffectAttribute *pFillAttr = (FillEffectAttribute *) CurrentAttrs[ATTR_FILLEFFECT].pAttr; if (pFillAttr==NULL) return FALSE; if (pFillAttr->IsKindOf(CC_RUNTIME_CLASS(FillEffectFadeAttribute))) { EffectType = FILLEFFECT_FADE; } else if (pFillAttr->IsKindOf(CC_RUNTIME_CLASS(FillEffectRainbowAttribute))) { EffectType = FILLEFFECT_RAINBOW; } else if (pFillAttr->IsKindOf(CC_RUNTIME_CLASS(FillEffectAltRainbowAttribute))) { EffectType = FILLEFFECT_ALTRAINBOW; } else { ERROR3("Illegal fill effect type!"); } return TRUE; } /******************************************************************************************** > BOOL CamelotEPSRenderRegion::WriteEPSBoundingBox ( void ) Author: Graeme_Sutherland (Xara Group Ltd) Created: 19/5/00 Inputs: - Returns: TRUE if ok; FALSE if error (e.g. file/disk error or printer driver error) Purpose: Calls the base EPS bounding box function. SeeAlso: EPSRenderRegion::WriteEPSBoundingBox ********************************************************************************************/ BOOL CamelotEPSRenderRegion::WriteEPSBoundingBox ( void ) { // Call the base class function. return EPSRenderRegion::WriteEPSBoundingBox (); } /******************************************************************************************** > BOOL CamelotEPSRenderRegion::WriteProlog(KernelDC *pDC) Author: Tim_Browse (Xara Group Ltd) Created: 24/04/95 Inputs: pDC - the device context to output to. Outputs: - Returns: TRUE if ok; FALSE if error (e.g. file/disk error or printer driver error) Purpose: Output any PostScript prolog for this render region. For EPS and printing, this means output of our PostScript rendering procedures; for Native files we do nothing. SeeAlso: EPSRenderRegion::WriteSetup ********************************************************************************************/ BOOL CamelotEPSRenderRegion::WriteProlog(KernelDC *pDC) { // Get hold of our PostScript prolog resource... CCResTextFile PrologFile; // Open the file if (!PrologFile.open(_R(IDM_PS_PROLOG), _R(IDT_PS_RES))) { // Failed to open the file... ERROR2(FALSE, "Could not get at PostScript resource!"); } // Put some comments out to mark our procset pDC->OutputNewLine(); pDC->OutputToken(_T("%%BeginResource: procset XaraStudio1Dict")); pDC->OutputNewLine(); pDC->OutputToken(_T("% Copyright (c) 1995,1996 Xara Ltd")); pDC->OutputNewLine(); // Start a new dictionary to avoid clashes... pDC->OutputToken(_T("/XaraStudio1Dict 300 dict def XaraStudio1Dict begin")); pDC->OutputNewLine(); // Read each line from the file and output it to the DC. String_256 LineBuf; TCHAR *pBuf = (TCHAR *) LineBuf; while (!PrologFile.read(&LineBuf).eof()) { // Copy this line to output. pDC->OutputTCHARAsChar(pBuf, LineBuf.Length()); pDC->OutputNewLine(); } // All done PrologFile.close(); // Write out any separation functions in this prolog if we // need to. Note this is a bool function WriteSepFunctions(pDC); // Put some comments/code out to mark our procset end pDC->OutputToken(_T("end")); pDC->OutputNewLine(); pDC->OutputToken(_T("%%EndResource")); pDC->OutputNewLine(); return TRUE; } /******************************************************************************************** > BOOL CamelotEPSRenderRegion::WriteSetup(KernelDC *pDC) Author: Tim_Browse (Xara Group Ltd) Created: 24/04/95 Inputs: pDC - the device context to output to. Outputs: - Returns: TRUE if ok; FALSE if error (e.g. file/disk error or printer driver error) Purpose: Output any PostScript setup for this render region. For EPS and printing, this means output of our PostScript code to initialise the context for rendering; for Native files we do nothing. SeeAlso: EPSRenderRegion::WriteSetup ********************************************************************************************/ BOOL CamelotEPSRenderRegion::WriteSetup(KernelDC *pDC) { // Get hold of our PostScript setup resource... CCResTextFile SetupFile; // Open the file if (!SetupFile.open(_R(IDM_PS_SETUP), _R(IDT_PS_RES))) { // Failed to open the file... ERROR2(FALSE, "Could not get at PostScript resource!"); } // Output some code so that we use our dictionary pDC->OutputToken(_T("save XaraStudio1Dict begin")); pDC->OutputNewLine(); // Read each line from the file and output it to the DC. String_256 LineBuf; TCHAR *pBuf = (TCHAR *) LineBuf; while (!SetupFile.read(&LineBuf).eof()) { // Copy this line to output. pDC->OutputTCHARAsChar(pBuf, LineBuf.Length()); pDC->OutputNewLine(); } // All done SetupFile.close(); return TRUE; } /******************************************************************************************** > BOOL CamelotEPSRenderRegion::WriteEPSTrailerComments ( void ) Author: Graeme_Sutherland (Xara Group Ltd) Created: 22/5/000 Inputs: - Returns: TRUE if ok; FALSE if error (e.g. file/disk error or printer driver error) Purpose: Writes out an EPS trailer by calling the base class function. SeeAlso: EPSRenderRegion::CloseDown ********************************************************************************************/ BOOL CamelotEPSRenderRegion::WriteEPSTrailerComments ( void ) { // (ChrisG 3/1/01) - Output some code so that we stop using our dictionary, Since // this is no longer controlled by Start- and StopRender, we have restore here. This should // be done to close the 'save-restore' block started in WriteSetup. KernelDC *pDC = (KernelDC*)CCDC::ConvertFromNativeDC(RenderDC); pDC->OutputToken(_T("end restore")); pDC->OutputNewLine(); // Call the base class to over-ride the native render region version. return EPSRenderRegion::WriteEPSTrailerComments (); } /******************************************************************************************** > void CamelotEPSRenderRegion::ColourSeparateScanline24(BYTE *DestBuffer, BYTE *SrcBuffer, INT32 PixelWidth) Author: Jason_Williams (Xara Group Ltd) Created: 27/6/96 Inputs: DestBuffer - points to a destination 24bpp RGB scanline buffer SrcBuffer - points to a source 24bpp RGB scanline buffer PixelWidth - The number of pixels to copy from Src to Dest Purpose: Copies the scanline of 24bpp RGB values from SrcBuffer to DestBuffer, running the pixels through colour separation tables en route. If you're not colour-separating, then you should replace this function call with a simple memcpy. Notes: ONLY handles 24bpp RGB scanlines, and expects that SepTables points at the separation tables. The pixel bytes must be in the order R,G,B. NOTE that it handles data in RGB (postscript) format, rather than the normal Bitmap BGR format! All deep bitmap data separates to white on spot plates. The CMYK separation plates could have been written generically, but I have separated out the cases in order to squeeze a bit of speed out of the inner pixel conversion loop. The conversion algorithm is basically like this, only optimised to move as much work out of the loop as possible: MonoOn Cyan = SepTables + 0 // Find the look up tables Magenta = SepTables + 256 Yellow = SepTables + 512 UCR = SepTables + 768 BGT = SepTables + 1024 c = 255-r; m = 255-g; y = 255-b; k = min(c, m, y); kr = UCR[k]; ok = BGT[k]; c -= kr; m -= kr; y -= kr; if (c<0) c=0; if (m<0) m=0; if (y<0) y=0; oc = Cyan[c]; om = Magenta[m]; oy = Yellow[y]; MonoOff ********************************************************************************************/ void CamelotEPSRenderRegion::ColourSeparateScanline24(BYTE *DestBuffer, BYTE *SrcBuffer, INT32 PixelWidth) { ERROR3IF(DestBuffer == NULL || SrcBuffer == NULL || PixelWidth < 1, _T("Illegal params")); ERROR3IF(SepTables == NULL, _T("No separation tables!?")); ERROR3IF(CurrentColContext->GetColourPlate() == NULL, _T("The separation ColourPlate has gone missing!")); // --- A wee macro to find the Maximum of R,G,B, and place it into a variable called // Temp, which we define right now in function scope so it's always available BYTE Temp; #define TEMPMAX(R,G,B) \ { \ Temp = ((R) > (G)) ? (R) : (G); \ Temp = (Temp > (B)) ? Temp : (B); \ } // --- OK, let'ts get busy INT32 i; // Loop i BYTE Ink; // Temporary variable for storing the ink value in BYTE *UCR = SepTables + 768 + 255; // UCR is the 4th table, but this points at the // END of the table for optimisation - precalcs (255 - Temp) PORTNOTE("cms", "Disabled colour separations"); #ifndef EXCLUDE_FROM_XARALX BOOL PrintRGBBlackAsKey=XaraCMS::PrintRGBBlackAsKey; #else BOOL PrintRGBBlackAsKey=TRUE; #endif // I've unrolled the shared code for each plate in order to optimise the inner loop switch (CurrentColContext->GetColourPlate()->GetType()) { case COLOURPLATE_CYAN: { BYTE *LUT = SepTables + 0; // Cyan table is the 1st table for (i = 0; i < PixelWidth * 3; i += 3) { TEMPMAX(SrcBuffer[i], SrcBuffer[i+1], SrcBuffer[i+2]); if (!PrintRGBBlackAsKey || Temp > 0) { Ink = 255 - SrcBuffer[i]; // Cyan ink is (255 - Red) // Look up UCR(Key), which is UCR(255 - Temp), but the 255 is precalculated // into the base address above, so we i with (-Temp)! // Make Ink the ink value for the given plate. Crop at 0 if ((UINT32)Ink>(UINT32)UCR[-Temp]) Ink -= UCR[-Temp]; else Ink = 0; Ink = LUT[Ink]; DestBuffer[i] = DestBuffer[i+1] = DestBuffer[i+2] = 255 - Ink; } else { DestBuffer[i] = DestBuffer[i+1] = DestBuffer[i+2] = 255; } } } break; case COLOURPLATE_MAGENTA: { BYTE *LUT = SepTables + 256; // Magenta table is the 2nd table for (i = 0; i < PixelWidth * 3; i += 3) { TEMPMAX(SrcBuffer[i], SrcBuffer[i+1], SrcBuffer[i+2]); if (!PrintRGBBlackAsKey || Temp > 0) { Ink = 255 - SrcBuffer[i+1]; // Magenta ink is (255 - Green) // Look up UCR(Key), which is UCR(255 - Temp), but the 255 is precalculated // into the base address above, so we i with (-Temp)! // Make Ink the ink value for the given plate. Crop at 0 if ((UINT32)Ink>(UINT32)UCR[-Temp]) Ink -= UCR[-Temp]; else Ink = 0; Ink = LUT[Ink]; DestBuffer[i] = DestBuffer[i+1] = DestBuffer[i+2] = 255 - Ink; } else { DestBuffer[i] = DestBuffer[i+1] = DestBuffer[i+2] = 255; } } } break; case COLOURPLATE_YELLOW: { BYTE *LUT = SepTables + 512; // Yellow table is the 3rd table for (i = 0; i < PixelWidth * 3; i += 3) { TEMPMAX(SrcBuffer[i], SrcBuffer[i+1], SrcBuffer[i+2]); if (!PrintRGBBlackAsKey || Temp > 0) { Ink = 255 - SrcBuffer[i+2]; // Yellow ink is (255 - Blue) // Look up UCR(Key), which is UCR(255 - Temp), but the 255 is precalculated // into the base address above, so we i with (-Temp)! // Make Ink the ink value for the given plate. Crop at 0 if ((UINT32)Ink>(UINT32)UCR[-Temp]) Ink -= UCR[-Temp]; else Ink = 0; Ink = LUT[Ink]; DestBuffer[i] = DestBuffer[i+1] = DestBuffer[i+2] = 255 - Ink; } else { DestBuffer[i] = DestBuffer[i+1] = DestBuffer[i+2] = 255; } } } break; case COLOURPLATE_KEY: { BYTE *BGT = SepTables + 1024; // Black generation is the 5th table for (i = 0; i < PixelWidth * 3; i += 3) { // Get the maximum of R,G,B into Temp TEMPMAX(SrcBuffer[i], SrcBuffer[i+1], SrcBuffer[i+2]); if (!PrintRGBBlackAsKey || Temp > 0) { // Key = 255 - Temp. Look up Key in the Black Generation table Ink = BGT[255 - Temp]; DestBuffer[i] = DestBuffer[i+1] = DestBuffer[i+2] = 255 - Ink; } else { DestBuffer[i] = DestBuffer[i+1] = DestBuffer[i+2] = 0; } } } break; case COLOURPLATE_SPOT: // Deep colour bitmaps cannot include spot colours, so they always separate out // to white... for (i = 0; i < PixelWidth * 3; i += 3) DestBuffer[i] = DestBuffer[i+1] = DestBuffer[i+2] = 255; break; default: ERROR3(_T("Unsupported separation plate!")); break; } // And finally, vape our helper macro #undef TEMPMAX } /******************************************************************************************** > SlowJobResult CamelotEPSRenderRegion::DrawMaskedBitmap(const DocRect &Rect, KernelBitmap* pBitmap, MaskedRenderRegion* pMask, ProgressDisplay *Progress) Author: Tim_Browse (Xara Group Ltd) Created: 5/5/95 Inputs: Point - the position to plot the bitmap pBitmap - The bitmap that needs plotting pMask - The mask render region to use to indicate which bits of pBitmap needs to be plotted Purpose: Plots the bitmap using the mask supplied. Notes: ********************************************************************************************/ SlowJobResult CamelotEPSRenderRegion::DrawMaskedBitmap(const DocRect &Rect, KernelBitmap* pBitmap, MaskedRenderRegion* pMask, ProgressDisplay *Progress) { #ifndef STANDALONE KernelDC *pDC = (KernelDC*)CCDC::ConvertFromNativeDC(RenderDC); // Make sure the world is in one piece if ((pBitmap==NULL) || (pMask==NULL)) return SLOWJOB_FAILURE; // Are we doing actual EPS? If so, then we need to do some special stuff BOOL IsCamelotEPS = IS_A(this, CamelotEPSRenderRegion); // If mask coverage is 0% we do not need to do anything. The TRUE param indicates // that we don't care about exact coverage, which makes it return as soon as it realises // that there are pixel(s) that we'll have to plot if (pMask->FindCoverage(TRUE) == 0) return SLOWJOB_SUCCESS; // Guess we will need to be doing something... if (pBitmap->ActualBitmap == NULL) return SLOWJOB_FAILURE; // Get the 'Actual' windows Bitmap CWxBitmap *WinBM = (CWxBitmap*)pBitmap->ActualBitmap; // Is it valid ? if ((WinBM->BMInfo==NULL) || (WinBM->BMBytes==NULL)) return SLOWJOB_FAILURE; // Remember the Size of the Bitmap (in pixels) INT32 Width = WinBM->GetWidth(); INT32 Height = WinBM->GetHeight(); if ((Width == 0) || (Height == 0)) // Error - bitmap has no dimension return SLOWJOB_FAILURE; // Hideous WINOILY code in the kernel. I get the feeling this was a rush job! // Convert the bitmap from a 32bpp bitmap to a 24bpp bitmap INT32 BitmapDepth = WinBM->GetBPP(); if (BitmapDepth == 32) { // Can't plot 32bpp bitmaps to GDI as 16-bit GDI doesn't understand them, // so we convert to 24bpp bitmap in-situ and render that... // How many bytes to a source scanline? const INT32 ScanlineBytes = WinBM->GetScanlineSize(); // How many bytes to a destination scanline const INT32 DestlineBytes = DIBUtil::ScanlineSize(Width, 24); // Now convert the bitmap in-situ LPBYTE OriginalBuffer = WinBM->BMBytes; LPBYTE ConvertedBuffer = WinBM->BMBytes; INT32 i; for (i=0; iBMInfo->bmiHeader.biSizeImage = DestlineBytes * Height; WinBM->BMInfo->bmiHeader.biBitCount = 24; BitmapDepth = 24; // Now swap the R and B bytes around as they are in BGR format in DIBS, and // we need RGB format for PostScript. OriginalBuffer = WinBM->BMBytes; for (i=0; iFindMaskDpi(); INT32 DestDpi = SrcDpi; BOOL UseLevel2 = FALSE; if (IsCamelotEPS) { // EPS is output in points, so it's always 72dpi. DestDpi = 72000; // Find out if we should do level 2 output. if (EPSFilter::XSEPSExportPSType == 2) UseLevel2 = TRUE; } else { // Find destination device resolution (e.g. PostScript printer) if (RenderDC!=NULL) DestDpi = RenderDC->GetPPI().GetWidth(); // WEBSTER-ranbirr-12/11/96 #ifndef WEBSTER // If printing, then see if we should do level 2. if (IsPrinting()) { // Get the print info for this job. CCPrintInfo *pInfo = CCPrintInfo::GetCurrent(); if (pInfo != NULL) { PrintControl *pPrCtrl = pInfo->GetPrintControl(); ERROR3IF(pPrCtrl == NULL, _T("Unexpected NULL pointer")); if (pPrCtrl->GetPSLevel() == PSLEVEL_2) // Printing to a level 2 device - party on! UseLevel2 = TRUE; } } #endif //webster } // Work out the ratio between to two double Ratio = DestDpi; Ratio = Ratio/SrcDpi; TRACEUSER( "Rik", _T("Src = %ld, Dest = %ld, Ratio = %f\n"), SrcDpi, DestDpi, Ratio); // Convert the DocCoord into a windows coord DocRect ClipRect = pMask->GetClipRect(); POINT Origin; if (IsCamelotEPS) { // No GDI conversion needed for EPS - we need to convert to points later on. Origin.x = ClipRect.lo.x; Origin.y = ClipRect.lo.y; } else { // Usual GDI conversion (used for PostScript printers) WinRect WinClipRect = OSRenderRegion::DocRectToWin(RenderMatrix, ClipRect, DestDpi); Origin.x = WinClipRect.GetLeft(); Origin.y = WinClipRect.GetBottom(); } // Inform progress object how high this band is if (Progress!=NULL) Progress->StartBitmapPhaseBand(Height); // We need to create a tempory bitmap that we use to render each scan line LPBITMAPINFO TempBitmapInfo = NULL; LPBYTE TempBitmapBytes = NULL; // Get some memory for a tempory bmp 1 pixel high TempBitmapInfo = AllocDIB(Width, 1, BitmapDepth, &TempBitmapBytes); if (TempBitmapInfo==NULL) return SLOWJOB_FAILURE; // Get a buffer for RGB channel separation (for level 2) and colour separation BYTE *pRGBBuf = (BYTE *) CCMalloc(Width); ERROR2IF(pRGBBuf == NULL, SLOWJOB_FAILURE, _T("No memory for RGB buffer")); // How many bytes to a destination scanline INT32 ScanLineBytes = DIBUtil::ScanlineSize(Width, BitmapDepth); INT32 BytesPerPixel = 3; // Now convert the bitmap in-situ LPBYTE SrcBuffer = WinBM->BMBytes; LPBYTE DestBuffer = TempBitmapBytes; // If we're colour separating, make sure we've got some sep tables to work with. // We cache them once we've used them. I would do this sort of set-up in StartRender // but of course all the derived classes just override that behaviour and we're stuffed if (SepTables == NULL && RenderView->GetColourPlate() != NULL && !RenderView->GetColourPlate()->IsDisabled()) { // We are doing a colour separation - find the sep tables ColourContextCMYK *cc = (ColourContextCMYK *)RenderView->GetColourContext(COLOURMODEL_CMYK); if (cc != NULL) { SepTables = (BYTE *) CCMalloc(5 * 256 * sizeof(BYTE)); if (SepTables != NULL) { if (!cc->GetProfileTables(SepTables)) { CCFree(SepTables); SepTables = NULL; } } } ERROR3IF(SepTables == NULL, _T("Can't generate separation tables in CamelotEPSRenderRegion")); } // Now copy the Bits from our Kernel Bitmap into the Memory DC, scan line at a time MaskRegion MaskInfo; pMask->GetFirstMaskRegion(&MaskInfo); try { while (MaskInfo.Length!=0) { // Calculate the source buffer address from the x and y position SrcBuffer = WinBM->BMBytes; SrcBuffer += ScanLineBytes * MaskInfo.y; SrcBuffer += MaskInfo.x*BytesPerPixel; INT32 RegionWidth = MaskInfo.Length; // Update bitmap info to show the new scan line size TempBitmapInfo->bmiHeader.biWidth = RegionWidth; TempBitmapInfo->bmiHeader.biSizeImage = (RegionWidth*BytesPerPixel); // copy the scan line into the temporary bmp if (SepTables == NULL) { // We are not colour separating, so just do a quick memcpy memcpy(DestBuffer, SrcBuffer, RegionWidth*BytesPerPixel); } else { // We are doing some sort of colour separation, so copy the data across, colour // separating it as we go ERROR3IF(BytesPerPixel != 3, _T("non-24bpp scanline unsupported")); ColourSeparateScanline24(DestBuffer, SrcBuffer, RegionWidth); } // Work out the coords of the destination rectangle INT32 DestX = Origin.x + INT32(ceil(MaskInfo.x*Ratio)); INT32 DestY = Origin.y; // Y direction is different for our EPS and GDI PostScript, as GDI preserves // the Y +ve <=> downwards model of windows. if (IsCamelotEPS) DestY += INT32(ceil((MaskInfo.y+1)*Ratio)); else DestY -= INT32(ceil((MaskInfo.y+1)*Ratio)); INT32 DestWidth = INT32(ceil(RegionWidth*Ratio)); INT32 DestHeight = INT32(ceil(1*Ratio)); pDC->OutputToken(_T("sv")); pDC->OutputValue(RegionWidth); pDC->OutputToken(_T("1")); if (IsCamelotEPS) { // Convert to points. pDC->OutputUserSpaceValue(DestWidth); pDC->OutputUserSpaceValue(DestHeight); DocCoord Dest(DestX, DestY); pDC->OutputCoord(Dest); } else { // Pass straight through pDC->OutputValue(DestWidth); pDC->OutputValue(DestHeight); pDC->OutputValue(DestX); pDC->OutputValue(DestY); } // Tell the proc whether this is L1 or L2 compatible data if (UseLevel2) pDC->OutputToken(_T("2")); else pDC->OutputToken(_T("1")); // Rendering primitive. if (SepTables == NULL) pDC->OutputToken(_T("cbsl")); // 24-bpp colour scanline else pDC->OutputToken(_T("gbsl")); // 8-bpp greyscale scanline // Output bitmap data - format depends on whether we can do Level 2 or not if (UseLevel2) { // Separate into 3 RGB streams (because it is much more likely to compress well // with RLE if we compress each component individually), and send as RLE // compressed ASCII85 data. // If we're colour separating, then it's greyscale (R=G=B) data, so we just dump // only the first channel to the gbsl operator const INT32 NumStreams = (SepTables == NULL) ? 3 : 1; for (INT32 i = 0; i < NumStreams; i++) { ERROR3IF(RegionWidth > Width, _T("I didn't allocate enough memory!")); // Copy this component into the buffer for (INT32 j = 0; j < RegionWidth; j++) { pRGBBuf[j] = TempBitmapBytes[(j * 3) + i]; } // We are going to do ASCII85, so get ready for it. if (!pDC->StartASCII85Output(TRUE)) // TRUE => RLE please // Error return SLOWJOB_FAILURE; // Send to output stream as RLE encoded ASCII85 data. /*INT32 nBytes =*/ pDC->OutputASCII85(pRGBBuf, RegionWidth); if (!pDC->EndASCII85Output()) // Error return SLOWJOB_FAILURE; } } else { // Just output as L1 ASCIIHex encoded data if (SepTables != NULL) { // Colour separating, so reduce the 24-bit (R=G=B) greyscale to 8bpp by getting the _T("red") channel for (INT32 j = 0; j < RegionWidth; j++) pRGBBuf[j] = TempBitmapBytes[j * 3]; pDC->OutputRawBinary(pRGBBuf, RegionWidth, 1); } else { // Normal 24bpp colour output pDC->OutputRawBinary(TempBitmapBytes, RegionWidth * 3, 1); } } pDC->OutputToken(_T("rs")); pDC->OutputNewLine(); // Update the progress display if necessary. if ((Progress!=NULL) && (!Progress->BitmapPhaseBandRenderedTo(MaskInfo.y))) { // Free up the tempory DIB I made FreeDIB(TempBitmapInfo, TempBitmapBytes); return SLOWJOB_USERABORT; } // Find the next bit of scan line to plot pMask->GetNextMaskRegion(&MaskInfo); } } catch(CFileException) { FreeDIB(TempBitmapInfo, TempBitmapBytes); if (UseLevel2) CCFree(pRGBBuf); return SLOWJOB_FAILURE; } // Update bitmap info to what it was before we started TempBitmapInfo->bmiHeader.biSizeImage = (Width*3); TempBitmapInfo->bmiHeader.biWidth = Width; // Free up the tempory DIB I made FreeDIB(TempBitmapInfo, TempBitmapBytes); // Free up the buffer used for separating RGB components CCFree(pRGBBuf); #endif // All ok return SLOWJOB_SUCCESS; } /******************************************************************************************** > BOOL CamelotEPSRenderRegion::RenderChar(WCHAR ch, Matrix* pMatrix) Author: Tim_Browse (Xara Group Ltd) Created: 30/4/95 Inputs: ch - unicode value of char pMatrix - matrix specifying transforms to place char correctly in document Returns: FALSE if fails Purpose: Render a character, using the specified transform and current attributes in the render region. ********************************************************************************************/ BOOL CamelotEPSRenderRegion::RenderChar(WCHAR ch, Matrix* pMatrix) { #ifndef STANDALONE // Should we render it as curves? (We do for non-ASCII characters) if ((ch > 255) || EPSFilter::XSEPSExportTextAsCurves) return RenderRegion::RenderChar(ch, pMatrix); // Make sure we have the right attributes for rendering as paths. GetValidPathAttributes(); // get overall matrix - attribute matrix concatenated with given matrix if supplied Matrix matrix; if (GetCharAttributeMatrix(&matrix)==FALSE) return FALSE; if (pMatrix) matrix*=*pMatrix; // Can we do this using a GDI font? // We can if the matrix only specifies scaling and translation, with no aspect ratio. FIXED16 abcd[4]; INT32 ef[2]; matrix.GetComponents(abcd, ef); if ((abcd[1] == FIXED16(0)) && (abcd[2] == FIXED16(0)) && (abcd[0] == abcd[3])) { // Simple transformation - we can do this with an untransformed PostScript font // Work out required height of the font MILLIPOINT ReferenceSize = TextManager::GetDefaultHeight(); MILLIPOINT Height = ReferenceSize * abcd[3]; if (!SelectNewFont(RR_TXTFONTTYPEFACE(), RR_TXTBOLD(), RR_TXTITALIC(), Height)) { // Could not select font return FALSE; } } else { // Transformation is complex - we need to use a matrix when selecting the font. if (!SelectNewFont(RR_TXTFONTTYPEFACE(), RR_TXTBOLD(), RR_TXTITALIC(), abcd)) { // Could not select font return FALSE; } } // Render the character in the specified position... KernelDC *pKernelDC = (KernelDC*)CCDC::ConvertFromNativeDC(RenderDC); // Output character // Graham 5/8/96: Changed this to work with MBCS // Set up a string buffer for the output character char cBuffer[3]; //Convert the character over to a MBCS index number UINT32 uiCharNumber = UnicodeManager::UnicodeToMultiByte(ch); //DecomposeMultiBytes takes that index number and creates a //two-byte character from it. We put that two-byte character //in cBuffer. UnicodeManager::DecomposeMultiBytes(uiCharNumber, (BYTE*) &cBuffer[0], (BYTE*) &cBuffer[1]); //And let's null-terminate cBuffer cBuffer[2]=0; //Now, if the character was a standard one-byte ASCII character, the ASCII //value will be in the second byte of cBuffer - cBuffer[1] - and cBuffer[0] //will be zero. //Note that we have set cBuffer[2] to zero. That means that cBuffer is null //terminated whether it contains a one or a two byte character. //So let's check if we've got a one-byte or a two-byte character. We do this //by saying...is cBuffer[0] zero? // First we copy it byte by byte into a TCHAR buffer TCHAR tcBuffer[3]; tcBuffer[0]=cBuffer[0]; tcBuffer[1]=cBuffer[1]; tcBuffer[2]=cBuffer[2]; if (tcBuffer[0]==0) //It's a standard ASCII character, one byte long. //So we only want to pass the second byte of cBuffer - cBuffer[1] - //to OutputString pKernelDC->OutputString(&tcBuffer[1]); else //The character is two bytes long (that is, it's a foreign character). //So we want to pass the whole of cBuffer - starting from cBuffer[0] - //to OutputString pKernelDC->OutputString(&tcBuffer[0]); //And that's the end of Graham's MBCS code. Now back to Tim. // Output position DocCoord DocPos(ef[0], ef[1]); pKernelDC->OutputCoord(DocPos); // Work out how to render the character.. BOOL FlatFill = IS_A(CurrentAttrs[ATTR_FILLGEOMETRY].pAttr, FlatFillAttribute); BOOL Stroked = !RR_STROKECOLOUR().IsTransparent(); // The character is filled if the fill is not a simple flat fill, or if it is, // but the fill colour is not transparent. BOOL Filled = !FlatFill || (!RR_FILLCOLOUR().IsTransparent()); if (FlatFill && Filled && !Stroked) { // Simple flat filled text with no outline, so render normally. pKernelDC->OutputToken(_T("t")); } else { // Need to do something special... if (Stroked) { if (Filled) // Stroke and fill it pKernelDC->OutputToken(_T("tb")); else // Just stroke it pKernelDC->OutputToken(_T("ts")); } else { // Just fill it pKernelDC->OutputToken(_T("tf")); } } #endif return TRUE; } /******************************************************************************************** > virtual void CamelotEPSRenderRegion::ConditionalSuicide ( void ) Author: Graeme_Sutherland (Xara Group Ltd) Created: 24/2/00 Inputs: - Returns: - Purpose: Doesn't delete the object when invoked. This is to get around using a few if IS_A calls elsewhere in Camelot. ********************************************************************************************/ void CamelotEPSRenderRegion::ConditionalSuicide ( void ) { // Don't delete this! } /******************************************************************************************** > virtual BOOL CamelotEPSRenderRegion::NeedsPrintComponents ( void ) Author: Graeme_Sutherland (Xara Group Ltd) Created: 24/2/00 Inputs: - Returns: TRUE - the print components are needed. Purpose: Lets the print component class know that they are needed in this format's export. ********************************************************************************************/ BOOL CamelotEPSRenderRegion::NeedsPrintComponents ( void ) { // We want print components! return TRUE; } /******************************************************************************************** > BOOL CamelotEPSRenderRegion::SelectNewFont ( WORD Typeface, BOOL Bold, BOOL Italic, MILLIPOINT Size ) Author: Tim_Browse (Xara Group Ltd) Created: 30/4/95 Inputs: Typeface - The font's ID. Bold - Is it bold? Italic - Is it italic? Size - The font size in millipoints. Returns: TRUE - Success. FALSE - Failure / an error occured. Purpose: Get the font name being used by the Camelot EPS exporter. ********************************************************************************************/ BOOL CamelotEPSRenderRegion::SelectNewFont ( WORD Typeface, BOOL Bold, BOOL Italic, MILLIPOINT Size ) { #ifndef STANDALONE // BODGE: This is mingled with horrible OIL code that should be moved to the OIL RSN... // Check to see if it is cached if ((FontInfo.Valid && !FontInfo.Complex) && (FontInfo.Typeface == Typeface) && (FontInfo.Bold == Bold) && (FontInfo.Italic == Italic) && (FontInfo.Size == Size)) { // It is the same as the currently selected/cached font TRACEUSER( "Tim", _T("Using the cached font\n")); return TRUE; } // Cache is invalid - map the TrueType name to a PostScript font. // Get the typeface name of the font String_64 FontName; if (!FONTMANAGER->GetFontName(Typeface, FontName)) return FALSE; // Transform to a INI-file friendly format String_64 Encoded; INT32 Styles = 0; if (Bold) Styles += 1; if (Italic) Styles += 2; FONTMANAGER->EncodeFontName(FontName, Encoded, Styles); // Look up font in INI file. String_256 MappedFontName; if (!Camelot.GetPrefDirect(_T("EPSFontMapping"), (TCHAR *) Encoded, &MappedFontName)) { // No preference - map to Times-Roman variant if (Bold) { if (Italic) MappedFontName.Load(_R(IDS_K_CAMEPS_MAPFNAMEBI)); else MappedFontName.Load(_R(IDS_K_CAMEPS_MAPFNAMEB)); } else { if (Italic) MappedFontName.Load(_R(IDS_K_CAMEPS_MAPFNAMEI)); else MappedFontName.Load(_R(IDS_K_CAMEPS_MAPFNAME)); } } // Due to inaccuracies, point sizes like 16pt come out as 15.999pt, so we check // for trailing digit errors and round to nearest integer point size if necessary. // (NB we have to use another variable for this otherwise the cache values don't match) MILLIPOINT CorrectedSize = Size; MILLIPOINT Remainder = Size % 1000; if ((Remainder <= 2) || (Remainder >= 998)) // Lose the inaccuracy. CorrectedSize = ((Size + 500) / 1000) * 1000; // Select this font by prefixing with a forward slash to make it into a name. KernelDC *pKernelDC = (KernelDC*)CCDC::ConvertFromNativeDC(RenderDC); String_8 Slash(TEXT("/")); MappedFontName.Insert(Slash, 0); pKernelDC->OutputToken((TCHAR *) MappedFontName); pKernelDC->OutputUserSpaceValue(CorrectedSize); pKernelDC->OutputToken(_T("sf")); // Font cache is now valid FontInfo.Valid = TRUE; FontInfo.Complex = FALSE; FontInfo.Typeface = Typeface; FontInfo.Bold = Bold; FontInfo.Italic = Italic; FontInfo.Size = Size; #endif // All ok return TRUE; } BOOL CamelotEPSRenderRegion::SelectNewFont(WORD Typeface, BOOL Bold, BOOL Italic, FIXED16 *abcd) { #ifndef STANDALONE // BODGE: This is mingled with horrible OIL code that should be moved to the OIL RSN... // Check to see if it is cached if ((FontInfo.Valid && FontInfo.Complex) && (FontInfo.Typeface == Typeface) && (FontInfo.Bold == Bold) && (FontInfo.Italic == Italic) && (FontInfo.a == abcd[0]) && (FontInfo.b == abcd[1]) && (FontInfo.c == abcd[2]) && (FontInfo.d == abcd[3])) { // It is the same as the currently selected/cached font TRACEUSER( "Tim", _T("Using the cached font\n")); return TRUE; } // Cache is invalid - map the TrueType name to a PostScript font. // Get the typeface name of the font // ENUMLOGFONT *pEnumLogFont = TextInfoBarOp::GetCachedLogFont(Typeface); // String_64 FontName(pEnumLogFont->elfLogFont.lfFaceName); // Changed by Mike (2/8/95) String_64 FontName; if (!FONTMANAGER->GetFontName(Typeface, FontName)) return FALSE; // Transform to a INI-file friendly format String_64 Encoded; INT32 Styles = 0; if (Bold) Styles += 1; if (Italic) Styles += 2; FONTMANAGER->EncodeFontName(FontName, Encoded, Styles); // Look up font in INI file. String_256 MappedFontName; if (!Camelot.GetPrefDirect(_T("EPSFontMapping"), (TCHAR *) Encoded, &MappedFontName)) { // No preference - map to Times-Roman variant if (Bold) { if (Italic) MappedFontName.Load(_R(IDS_K_CAMEPS_MAPFNAMEBI2)); else MappedFontName.Load(_R(IDS_K_CAMEPS_MAPFNAMEB2)); } else { if (Italic) MappedFontName.Load(_R(IDS_K_CAMEPS_MAPFNAMEI2)); else MappedFontName.Load(_R(IDS_K_CAMEPS_MAPFNAME2)); } } // Select this font by prefixing with a forward slash to make it into a name. KernelDC *pKernelDC = (KernelDC*)CCDC::ConvertFromNativeDC(RenderDC); String_8 Slash(TEXT("/")); MappedFontName.Insert(Slash, 0); pKernelDC->OutputToken((TCHAR *) MappedFontName); // Get the default text height, and work out how to scale to PostScript's default of // 1 user space unit (i.e. 1 pt) double ScaleFactor = TextManager::GetDefaultHeight() / 1000.0; TCHAR MatrixBuf[100]; camSprintf(MatrixBuf, _T("[%.3f %.3f %.3f %.3f 0 0]"), (double) (abcd[0].MakeDouble() * ScaleFactor), (double) (abcd[1].MakeDouble() * ScaleFactor), (double) (abcd[2].MakeDouble() * ScaleFactor), (double) (abcd[3].MakeDouble() * ScaleFactor)); pKernelDC->OutputToken(MatrixBuf); // Select the complex font pKernelDC->OutputToken(_T("sf")); // Font cache is now valid FontInfo.Valid = TRUE; FontInfo.Complex = TRUE; FontInfo.Typeface = Typeface; FontInfo.Bold = Bold; FontInfo.Italic = Italic; FontInfo.a = abcd[0]; FontInfo.b = abcd[1]; FontInfo.c = abcd[2]; FontInfo.d = abcd[3]; #endif // All ok return TRUE; } /******************************************************************************************** > BOOL CamelotEPSRenderRegion::DrawTransformedBitmap(NodeBitmap *pNodeBitmap)) Author: Tim_Browse (Xara Group Ltd) Created: 28/6/95 Inputs: pNodeBitmap - the bitmap node to render! Returns: TRUE if the bitmap could be rendered as specified; FALSE if it could not be (and hence wasn't). Purpose: Plot an arbitrarily transformed bitmap using a matrix and an offset. The bitmap is assumed to occupy a rectangle at the origin, which has pixels 1 point across, and the matrix supplied transforms this area to the correct place. The Point parameter supplies an optional offset which can be applied after the transform. If the offset is 0,0, this means there is no offset (surprise, surprise!) SeeAlso: NodeBitmap::Render ********************************************************************************************/ BOOL CamelotEPSRenderRegion::DrawTransformedBitmap(NodeBitmap *pNodeBitmap) { // If we are not drawing complex shapes and this shape is, then return if ((!RenderComplexShapes) && (TestForComplexShape(&Caps))) return TRUE; if ((!RenderComplexShapes) && (pNodeBitmap->NeedsTransparency())) return TRUE; DocCoord *Coords = pNodeBitmap->InkPath.GetCoordArray(); // Get the 'actual' OIL Bitmap OILBitmap *TheBitmap = pNodeBitmap->GetBitmap()->ActualBitmap; // Is it contoned? DocColour *StartCol = NULL, *EndCol = NULL; if (!RR_STROKECOLOUR().IsTransparent()) { // It has a line colour, so it is contoned! StartCol = &(RR_STROKECOLOUR()); EndCol = &(RR_FILLCOLOUR()); } // Render it return DrawParallelogramBitmap(Coords, TheBitmap, GetFillEffect(), StartCol, EndCol); } /******************************************************************************************** > BOOL CamelotEPSRenderRegion::DrawParallelogramBitmap(DocCoord *Coords, OILBitmap *pBitmap, EFFECTTYPE Effect = EFFECT_RGB, DocColour *StartCol = NULL, DocColour *EndCol = NULL) Author: Tim_Browse (Xara Group Ltd) Created: 3/7/95 Inputs: Coords - the parallelogram to plot the bitmap into (should be an array of 4 co-ordinates) pBitmap - the bitmap to render! Effect - if contone colours are supplied, this is how the colours should fade. StartCol, EndCol - optional colours, if non-NULL, this indicates that the bitmap is a contoned bitmap and should be rendered using these colours. Returns: TRUE if the bitmap could be rendered as specified; FALSE if it could not be (and hence wasn't). Purpose: Plot an arbitrarily transformed bitmap in the position specified. For a normal untransformed rectangle, the Coords array should start specify the parallelogram as follows: Coords[0] Coords[1] +---------------+ | | | | | | | | +---------------+ Coords[3] Coords[2] SeeAlso: NodeBitmap::Render; CamelotEPSRenderRegion::DrawTransformedBitmap ********************************************************************************************/ BOOL CamelotEPSRenderRegion::DrawParallelogramBitmap(DocCoord *Coords, OILBitmap *pBitmap, EFFECTTYPE Effect, DocColour *StartCol, DocColour *EndCol) { // If we are not drawing complex shapes and this shape is, then return if ((!RenderComplexShapes) && (TestForComplexShape(&Caps))) return TRUE; // If it's contoned between two no-colours, it is invisible (on this plate) so don't render anything if (StartCol != NULL && StartCol->IsTransparent() && EndCol != NULL && EndCol->IsTransparent()) return(TRUE); // Get information about the bitmap to be rendered. BitmapInfo BMInfo; if (!pBitmap->GetInfo(&BMInfo)) { ERROR3("Bitmap GetInfo failed"); // Error getting bitmap info return FALSE; } // Remember the Size of the Bitmap (in pixels) INT32 Width = BMInfo.PixelWidth; INT32 Height = BMInfo.PixelHeight; if ((Width == 0) || (Height == 0)) { // Error - bitmap has no dimension ERROR3("Bitmap width/height is 0"); return FALSE; } // Find out if this is real renderable EPS. BOOL IsCamelotEPS = IS_A(this, CamelotEPSRenderRegion); // Is it contoned? BOOL IsContoned = (StartCol != NULL) && (EndCol != NULL); if (IsContoned) { if (pBitmap->GetGreyscaleVersion() != NULL) { pBitmap = pBitmap->GetGreyscaleVersion(); if (!pBitmap->GetInfo(&BMInfo)) { ERROR3("Bitmap GetInfo failed"); // Error getting bitmap info return FALSE; } } } // Find out if we can use Level 2 features. BOOL UseLevel2 = FALSE; if (IsCamelotEPS) { // Use the EPS preference. if (EPSFilter::XSEPSExportPSType == 2) UseLevel2 = TRUE; } // WEBSTER-ranbirr-12/11/96 #ifndef WEBSTER else if (IsPrinting()) { #ifndef STANDALONE // Get the print info for this job. CCPrintInfo *pInfo = CCPrintInfo::GetCurrent(); if (pInfo != NULL) { PrintControl *pPrCtrl = pInfo->GetPrintControl(); if (pPrCtrl->GetPSLevel() == PSLEVEL_2) // Printing to a level 2 device - party on! UseLevel2 = TRUE; } #else ERROR2(FALSE,"CamelotEPSRenderRegion::DrawParallelogramBitmap trying to print in Viewer version!") #endif } #endif //webster // Work out the coords of the destination rectangle // INT32 DestX = 0; // INT32 DestY = 0; KernelDC *pDC = (KernelDC*)CCDC::ConvertFromNativeDC(RenderDC); pDC->OutputToken(_T("sv")); // Dimensions of bitmap pDC->OutputValue(Width); pDC->OutputValue(Height); // Number of bits per component sample INT32 BytesPerScanline; // Are we colour separating? Get SepTables if we need them // NOTE that we get them even if we have a <= 8bpp bitmap, as the pointer is also used // as a "we are separating" flag. Note also that we don't try to separate contoned bitmaps // via the normal system- we just let the old contone bitmap code output it, and separate the palette if (!IsContoned && SepTables == NULL && RenderView->GetColourPlate() != NULL) { ColourContextCMYK *cc = (ColourContextCMYK *)RenderView->GetColourContext(COLOURMODEL_CMYK); if (cc != NULL) { SepTables = (BYTE *) CCMalloc(5 * 256 * sizeof(BYTE)); if (SepTables != NULL) { if (!cc->GetProfileTables(SepTables)) { CCFree(SepTables); SepTables = NULL; } } } ERROR3IF(SepTables == NULL, "Can't generate separation tables in CamelotEPSRenderRegion"); } if (SepTables != NULL && !IsContoned) { // ALL colour separated bitmaps go out as simple 8bpp greyscales (yay! no special cases!) pDC->OutputValue((INT32)8); BytesPerScanline = Width; } else { // It's not colour separated, so output the right values for the bitmap's BPP. switch (BMInfo.PixelDepth) { case 1: case 8: pDC->OutputValue((INT32) BMInfo.PixelDepth); BytesPerScanline = ((BMInfo.PixelDepth * Width) + 7) / 8; // Round 1bpp up to multiple of bytes break; case 4: // 4bpp images get converted to 8bpp because Adobe SDK image library can't // do 4bpp on some PS printers (e.g. Canon LBP4-PS). pDC->OutputValue((INT32) 8); BytesPerScanline = Width; break; case 24: case 32: // 32bpp images are output as 24bpp to postscript // Output will be standard 24bpp pDC->OutputValue((INT32) 24); BytesPerScanline = 3 * Width; break; default: // Unknown bitmap depth ERROR3("Unsupported bitmap depth in CamelotEPSRenderRegion::DrawParallelogramBitmap"); return FALSE; } } // Now, output how many bytes per scanline will be used. pDC->OutputValue(BytesPerScanline); // Now output the matrix - colorimage expects the inverse of this matrix, i.e. // it needs the matrix which maps the destination area to the 1-1 rectangle at // the origin (cos PostScript is weird like that). pDC->OutputToken(_T("[")); // NB. We have to do some 'bespoke' matrix maths here, because we need to work // in points, and our Matrix class is not accurate enough (notably the e and // f components are integer only, and we need fractional points). double a[2],b[2],c[2],d[2], e[2], f[2]; // Find out the size of one unit, in MILLIPOINTS. double UnitSize; if (IsCamelotEPS) { // Unit is 1000 millipoints, as we always use 1 point as unit in user space // in Camelot EPS. UnitSize = 1000; } else { // Find the size of a pixel, because Windows maps a unit to a pixel. FIXED16 fxPixelSize; RenderView->GetPixelSize(&fxPixelSize, &fxPixelSize); UnitSize = fxPixelSize.MakeDouble(); } // Work out the width and height of the bitmap, assuming each pixel corresponds to // one unit. double dWidth = (double) Width; double dHeight = (double) Height; DocCoord DCOrigin = pDC->GetOrigin(); if (IsCamelotEPS) { dWidth *= UnitSize; dHeight *= UnitSize; a[0] = ((double) (Coords[2].x - Coords[3].x)) / dWidth; b[0] = ((double) (Coords[2].y - Coords[3].y)) / dWidth; c[0] = ((double) (Coords[0].x - Coords[3].x)) / dHeight; d[0] = ((double) (Coords[0].y - Coords[3].y)) / dHeight; e[0] = ((double) (Coords[3].x - DCOrigin.x)) / UnitSize; f[0] = ((double) (Coords[3].y - DCOrigin.y)) / UnitSize; } else { ERROR3IF(!pDC->IsKindOf(CC_RUNTIME_CLASS(PSPrintDC)), "KernelDC is not a PSPrintDC - how did that happen?"); // Printing to PostScript - convert to Window co-ordinates, using the DC. PSPrintDC *pPSDC = (PSPrintDC *) pDC; WinCoord PSCoords[3]; for (INT32 i = 0; i <= 3; i++) PSCoords[i] = pPSDC->TransformCoord(Coords[i]); a[0] = ((double) (PSCoords[2].x - PSCoords[3].x)) / dWidth; b[0] = ((double) (PSCoords[2].y - PSCoords[3].y)) / dWidth; c[0] = ((double) (PSCoords[0].x - PSCoords[3].x)) / dHeight; d[0] = ((double) (PSCoords[0].y - PSCoords[3].y)) / dHeight; e[0] = (double) PSCoords[3].x; f[0] = (double) PSCoords[3].y; } // Calculate the inverse of the above. double Det = a[0] * d[0] - b[0] * c[0]; if (Det==0.0) { // There is no inversion of this matrix // return the identity matrix ERROR3("Matrix Inversion Failed - Tried to Invert a non-invertable matrix" ); return FALSE; } // this section calculates the inverse of the matrix. As it takes into // account that our 3x3 matrix always has 0,0,1 down its right hand side // it has been greatly simplified. The operations combine the calculation // of the adjoint matrix and scaling it by the Determinent with a matrix // transpose (the inverse is the transpose of the adjoint scaled by the // determinent) a[1] = d[0] / Det; b[1] = -b[0] / Det; c[1] = -c[0] / Det; d[1] = a[0] / Det; e[1] = ((c[0] * f[0]) - (e[0] * d[0])) / Det ; f[1] = -(((a[0] * f[0]) - (e[0] * b[0])) / Det); pDC->OutputReal(a[1]); pDC->OutputReal(b[1]); pDC->OutputReal(c[1]); pDC->OutputReal(d[1]); if (IsCamelotEPS) { // Convert to points. pDC->OutputUserSpaceValue((MILLIPOINT) (e[1] * 1000.0)); pDC->OutputUserSpaceValue((MILLIPOINT) (f[1] * 1000.0)); } else { // Pass straight through pDC->OutputReal(e[1]); pDC->OutputReal(f[1]); } pDC->OutputToken(_T("]")); // Default values for smoothing and polarity pDC->OutputToken(_T("false")); pDC->OutputToken(_T("false")); // Normally we use ASCII Hex encoding, but for Level 2 specific output, we use ASCII85. if (UseLevel2) // Run length encoded and ASCII85 encoded pDC->OutputToken(_T("3")); else // Simple level 1 compatible ASCII Hex encoding. pDC->OutputToken(_T("1")); // Set up the image procs pDC->OutputToken(_T("beginimage")); if (SepTables != NULL && !IsContoned) { // If we are colour-separating, then output always goes out as an 8bpp greyscale image // This may seem like a gross thing to do (especially for 1/4bpp), but as 4bpp is always // expanded to 8bpp anyway, it is much better to treat all images in a nice generic manner // and save ourselves lots of work, lots of confusion, and the potential for lots of // special cases which don't work (for examples of code like this, see the old composite // printing code below). if (!pBitmap->ExportSeparatedPalette(this)) { ERROR3("ExportSeparatedPalette failed"); return FALSE; } pDC->OutputToken(_T("doclutimage")); // Colour separation to 8bpp greyscale image pDC->OutputNewLine(); // Output bitmap (scanline) data as an 8bpp greyscale if (!pBitmap->ExportSeparatedData(this, SepTables)) { ERROR3("ExportSeparatedData failed"); return FALSE; } } else { // We've specified the size, depth and position of the bitmap, so now we work // out which PostScript procedure we should render the image with. switch (BMInfo.PixelDepth) { case 1: // It's a 1bpp image - should we output a palette with it? if (IsContoned) { // (Jason here: I believe the postscript routine 1bitcopyimage doesn't work. // This is probably OK, as I think all contoned bitmaps in camelot are made 8bpp. // I've left this code in place, however, just in case it would break something) // Convert colours to RGB and output for use with the special // 1bpp contone operator. Document *pDoc = RenderView->GetDoc(); if (pDoc == NULL) { ERROR3("No document when rendering"); // Panic! return FALSE; } // Use the output context to convert the colours, and then output them... ColourContext *pContext = CurrentColContext; if (pContext == NULL) { ERROR3("No Colour context when rendering"); return(FALSE); // Panic! } ERROR3IF(pContext->GetColourModel() != COLOURMODEL_RGBT, "RGB output context expected in EPS rendering"); ColourRGBT Result; if (IsContoned) pContext->ConvertColour((DocColour *) StartCol, (ColourGeneric *)&Result); else { DocColour Black(0L, 0L, 0L); pContext->ConvertColour(&Black, (ColourGeneric *)&Result); } pDC->OutputReal(Result.Red.MakeDouble()); pDC->OutputReal(Result.Green.MakeDouble()); pDC->OutputReal(Result.Blue.MakeDouble()); if (IsContoned) pContext->ConvertColour((DocColour *) EndCol, (ColourGeneric *)&Result); else { DocColour White(255L, 255L, 255L); pContext->ConvertColour(&White, (ColourGeneric *)&Result); } pDC->OutputReal(Result.Red.MakeDouble()); pDC->OutputReal(Result.Green.MakeDouble()); pDC->OutputReal(Result.Blue.MakeDouble()); // Use special colour 1bpp operator pDC->OutputToken(_T("1bitcopyimage")); } else { // Normal black and white image pDC->OutputToken(_T("1bitbwcopyimage")); } break; case 4: case 8: if (IsContoned) { // Contoned bitmap - generate and output the contone palette. if (!pBitmap->BuildContonePalette(*StartCol, *EndCol, Effect, RenderView)) { ERROR3("Contoned palette failed"); // Error while building palette (out of memory?) return FALSE; } BOOL Success = pBitmap->ExportContonePalette(this); // Clean up contone palette pBitmap->DestroyContonePalette(); // Did it work? if (!Success) { ERROR3("Contoned palette export failed"); return FALSE; } // Use the procedure for rendering paletted images. pDC->OutputToken(_T("doclutimage")); } else if (BMInfo.NumPaletteEntries > 0) { // It's a paletted image - export the palette if (!pBitmap->ExportBitmapPalette(this)) { ERROR3("ExportBitmapPalette failed"); return FALSE; } // Use the procedure for rendering paletted images. pDC->OutputToken(_T("doclutimage")); } else { // It's a greyscale image - use the image operator. // (Jason here: I reckon the postscript for doimage doesn't work. This is // probably OK, because camelot can't theoretically have a non-paletted image?) pDC->OutputToken(_T("doimage")); } break; case 24: case 32: // 32bpp is output as 24bpp data pDC->OutputToken(_T("do24image")); // Normal composite colour 24bpp image break; default: // Unknown bitmap depth ERROR3("Unsupported bitmap depth"); return FALSE; } pDC->OutputNewLine(); // Output bitmap (scanline) data if (!pBitmap->ExportBitmapData(this)) { ERROR3("ExportBitmapData failed"); return FALSE; } } // Clean up image stuff pDC->OutputNewLine(); pDC->OutputToken(_T("endimage")); pDC->OutputToken(_T("rs")); pDC->OutputNewLine(); return TRUE; } /******************************************************************************************** > void CamelotEPSRenderRegion::DrawPathToOutputDevice(Path *DrawPath) Author: Tim_Browse (Xara Group Ltd) Created: 30/03/94 Inputs: DrawPath - the path to render. Purpose: Output all the commands required to render this path to the EPS file. SeeAlso: RenderRegion::DrawPath; EPSRenderRegion::GetValidPathAttributes EPSRenderRegion::ExportPath ********************************************************************************************/ void CamelotEPSRenderRegion::DrawPathToOutputDevice(Path *DrawPath, PathShape) { // If we are not drawing complex shapes and this shape is, then return if ((!RenderComplexShapes) && (TestForComplexShape(&Caps))) return; // Work out if we need to be renderable. BOOL Renderable = (IS_A(this, CamelotEPSRenderRegion) || IsPrinting()); if (Renderable) { // Check for clipped (non-repeating) bitmaps... // Get the current fill attr ColourFillAttribute* pFillAttr = (ColourFillAttribute*) CurrentAttrs[ATTR_FILLGEOMETRY].pAttr; // See if it is a simple non-repeating bitmap fill... if (pFillAttr->IS_KIND_OF(BitmapFillAttribute)) { FillMappingAttribute* pMapAttr = (FillMappingAttribute*) CurrentAttrs[ATTR_FILLMAPPING].pAttr; if (pMapAttr->Repeat == 1) { // Simple non-repeating clipped bitmap - we can do this! DrawClippedBitmap(DrawPath); return; } } } ExportPath(DrawPath, FALSE); // Now do the arrow heads if the render region can't do them directly if (!Caps.ArrowHeads) { // Doesn't support arrow heads directly so we render them as paths. DrawPathArrowHeads(DrawPath->GetCoordArray(), DrawPath->GetVerbArray(), DrawPath->GetNumCoords()); } } /******************************************************************************************** > BOOL CamelotEPSRenderRegion::DrawClippedBitmap(Path *DrawPath) Author: Tim_Browse (Xara Group Ltd) Created: 3/7/95 Inputs: DrawPath - the path to use to clip the bitmap fill. Returns: TRUE if rendered ok; FALSE if not. Purpose: Render a clipped bitmap using the given path. This is because we can do clipped bitmap rendering in PostScript, and it is much cheaper to do it via PostScript instead of via the 'transparency' mask (via GDraw). ********************************************************************************************/ BOOL CamelotEPSRenderRegion::DrawClippedBitmap(Path *DrawPath) { // Get a device context KernelDC *pDC = (KernelDC*)CCDC::ConvertFromNativeDC(RenderDC); // What we do is: export the path, save the graphics context, use the path as a clipping // region, render the bitmap, restore the graphics context, and optionally stroke the // path, if necessary. // make sure the eps-rr is set up with the correct path attributes, // as ExportPath() will only be rendering pure path information. GetValidPathAttributes(); // export our path. ExportPath(DrawPath, TRUE, TRUE); // Save the context pDC->OutputToken(_T("gs")); // export a 'clip' command. note that if our current winding rule is for even-odd // winding, we must export an 'eoclip' command instead. if (RR_WINDINGRULE() == EvenOddWinding) pDC->OutputToken(TEXT("eoclip")); else pDC->OutputToken(TEXT("clip")); // Render the bitmap - first, set up the parallelogram to render into. BitmapFillAttribute* pFillAttr = (BitmapFillAttribute*) CurrentAttrs[ATTR_FILLGEOMETRY].pAttr; DocCoord Coords[4]; Coords[3] = *(pFillAttr->GetStartPoint()); Coords[2] = *(pFillAttr->GetEndPoint()); Coords[0] = *(pFillAttr->GetEndPoint2()); // Deduce the other co-ordinate (not used by DrawParalleogramBitmap(), but calculate // it just in case...) Coords[1].x = Coords[0].x + (Coords[2].x - Coords[3].x); Coords[1].y = Coords[0].y + (Coords[2].y - Coords[3].y); // Get the bitmap KernelBitmap *pKernelBitmap = pFillAttr->GetBitmap(); if (pKernelBitmap == NULL) return FALSE; // Render it. if (!DrawParallelogramBitmap(Coords, pKernelBitmap->ActualBitmap, GetFillEffect(), pFillAttr->GetStartColour(), pFillAttr->GetEndColour())) return FALSE; // Restore the context pDC->OutputToken(_T("gr")); // Work out if we should stroke the path TCHAR PathType[2] = _T("N"); if (!RR_STROKECOLOUR().IsTransparent()) { // Stroke path, leaving it open PathType[0] = 'S'; } PathVerb* Verbs = DrawPath->GetVerbArray(); INT32 NumCoords = DrawPath->GetNumCoords(); if (Verbs[NumCoords - 1] & PT_CLOSEFIGURE) // Path should be closed PathType[0] = tolower(PathType[0]); // Do the stroke command pDC->OutputToken(PathType); pDC->OutputNewLine(); // Ok if we got to here return TRUE; } /******************************************************************************************** > BOOL CamelotEPSRenderRegion::WriteNewLine ( void ) Author: Chris_Gallimore (Xara Group Ltd) Created: 15/11/00 Inputs: - Returns: TRUE - Success. FALSE - Failure. Purpose: Outputs a new line tag to the file. ********************************************************************************************/ BOOL CamelotEPSRenderRegion::WriteNewLine ( void ) { // Since the positions of the characters are explicitly given in the EPS file, newline // (/r) characters are not required. And they return TRUE; } /******************************************************************************************** > virtual void CamelotEPSRenderRegion::OutputStrokeColour () Author: Chris_Gallimore (Xara Group Ltd) Created: 8/12/00 Inputs: - Returns: - Purpose: This simply calls EPSRenderRegion::OutputStrokeColour, as otherwise the ArtWorks version is used (which can't cope with the RGB colour tokens) SeeAlso: EPSRenderRegion::OutputStrokeColour ********************************************************************************************/ void CamelotEPSRenderRegion::OutputStrokeColour () { EPSRenderRegion::OutputStrokeColour (); } /******************************************************************************************** > virtual void CamelotEPSRenderRegion::OutputFillColour () Author: Chris_Gallimore (Xara Group Ltd) Created: 8/12/00 Inputs: - Returns: - Purpose: This simply calls EPSRenderRegion::OutputFillColour, otherwise this render region uses the overridden version found in ArtWorksEPSRenderRegion. SeeAlso: EPSRenderRegion::OutputStrokeColour ********************************************************************************************/ void CamelotEPSRenderRegion::OutputFillColour () { EPSRenderRegion::OutputFillColour (); } #endif