// $Id: ai_epsrr.cpp 1336 2006-06-17 16:18:41Z alex $ // Implementation of Adobe Illustrator EPS filter. /* @@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============================ */ #include "camtypes.h" #include "ai_epsrr.h" #include #include #include "vectrndr.h" #include "ccdc.h" //#include "colmodel.h" - in camtypes.h [AUTOMATICALLY REMOVED] //#include "fillattr.h" - in camtypes.h [AUTOMATICALLY REMOVED] #include "opbevel.h" #include "swfexpdc.h" #include "swfrndr.h" #include "slicehelper.h" #include "fillramp.h" //#include "spread.h" - in camtypes.h [AUTOMATICALLY REMOVED] #include "nodebmp.h" //#include "bitmap.h" - in camtypes.h [AUTOMATICALLY REMOVED] //#include "range.h" - in camtypes.h [AUTOMATICALLY REMOVED] //#include "rendsel.h" //#include "dibutil.h" - in camtypes.h [AUTOMATICALLY REMOVED] #include "fontman.h" // for FontManager - for writing font changes to overflow text #include "colplate.h" // for ColourPlate - in overflow text functions #include "colourix.h" // for Indexed Colour - for writing colour changes to overflow #include "nodebev.h" #include "layer.h" #include "clipattr.h" CC_IMPLEMENT_DYNAMIC(AIEPSRenderRegion, EPSRenderRegion) /******************************************************************************************** > AIEPSRenderRegion::AIEPSRenderRegion ( DocRect ClipRect, Matrix ConvertMatrix, FIXED16 ViewScale ) Author: Graeme_Sutherland (Xara Group Ltd) Created: 29/3/00 Purpose: Create and initialise a render region for exporting AI EPS. Sets up the string to put in the %%Creator comment. SeeAlso: EPSRenderRegion::EPSRenderRegion () ********************************************************************************************/ AIEPSRenderRegion::AIEPSRenderRegion ( DocRect ClipRect, Matrix ConvertMatrix, FIXED16 ViewScale ) : EPSRenderRegion ( ClipRect, ConvertMatrix, ViewScale ) { // Set up member variables. m_a = 1; m_b = 0; m_c = 0; m_d = 1; m_T = DocCoord ( 0, 0 ); m_ActiveLayer = FALSE; m_LayerColour = 0; m_fpOverflowText = NULL; m_bInTextGap = FALSE; m_bTextAsShapes = FALSE; m_pLinearGradList = NULL; m_pRadialGradList = NULL; // Initialise the creator string to show that it's an AI file. CreatorString = _T("Adobe Illustrator(TM) 7.0 by Xara."); } /******************************************************************************************** > AIEPSRenderRegion::~AIEPSRenderRegion Author: Chris_Gallimore (Xara Group Ltd) Created: 30/3/2001 Purpose: Destructor for an AI render region. All this really does is dispose of the gradient fill cache (if there is one) SeeAlso: EPSRenderRegion::EPSRenderRegion () ********************************************************************************************/ AIEPSRenderRegion::~AIEPSRenderRegion () { ClearGradientCache (); } /******************************************************************************************** > BOOL AIEPSRenderRegion::ExportBitmap ( NodeBitmap *pNodeBMP ) Author: Graeme_Sutherland (Xara Group Ltd) Created: 31/3/00 Inputs: pNodeBMP - A pointer to the NodeBitmap being exported. Returns: TRUE - If successful. FALSE - If an error is encountered. Purpose: Kicks off the NodeBitmap export process.. SeeAlso: NodeBitmap::ExportRender () ********************************************************************************************/ BOOL AIEPSRenderRegion::ExportBitmap ( NodeBitmap *pNodeBMP ) { // Set up the local variables. KernelBitmap *pBitmap = pNodeBMP->GetBitmap (); // Set up the bitmap's transformation matrix. LoadBitmapMatrix ( pNodeBMP ); // Write out the bitmap record. WriteBitmapRecord ( pBitmap->GetActualBitmap () ); return TRUE; } /******************************************************************************************** > BOOL AIEPSRenderRegion::ExportBevel ( NodeBevel *pBevel ) Author: Graeme_Sutherland (Xara Group Ltd) Created: 6/4/00 Inputs: pBevel - A pointer to a bevel node. Returns: TRUE - Success. FALSE - An error occured. Purpose: Renders a bevel into a DIB, and exports this as a bitmap to the AI file. SeeAlso: NodeBevel::ExportRender ********************************************************************************************/ BOOL AIEPSRenderRegion::ExportBevel ( NodeBevel *pBevel ) { // Set up the local variables. KernelDC *pDC = static_cast ( CCDC::ConvertFromNativeDC(RenderDC) ); RangeControl ControlFlags ( TRUE, TRUE ); Range ToRender ( pBevel, pBevel, ControlFlags ); KernelBitmap *pBitmap = NULL; Path *pSourcePath = &( pBevel->InkPath ); DocRect Bounds = pBevel->GetBoundingRect (); DocCoord Position ( Bounds.lo.x, Bounds.hi.y ); double Width = static_cast ( Bounds.Width () ); double Height = static_cast ( Bounds.Height () ); // Create and render the bitmap. pBitmap = pBevel->CreateBitmapCopy(-1.0,FALSE); if(!pBitmap) return FALSE; // Get the width and height of the bitmap. double BMPWidth = static_cast ( pBitmap->GetWidth () ); double BMPHeight = static_cast ( pBitmap->GetHeight () ); // Write the path. (ChrisG 16/01/01) If we have one. if (pSourcePath->GetNumCoords () != 0) { WriteMask ( pSourcePath, TRUE ); } // Set up the bitmap translation matrix. LoadTranslationMatrix ( Position ); // Scale width and height to be valid value for AI co-ordinates. Width /= 1000; Height /= 1000; // And calcuate the scale values for placing the bitmap. m_a = Width / BMPWidth; m_d = Height / BMPHeight; // Write the bitmap record. WriteBitmapRecord ( pBitmap->ActualBitmap ); // Write the end of mask operator. (ChrisG 16/01/01) If we've written a mask. if (pSourcePath->GetNumCoords () != 0) { pDC->OutputToken ( _T("Q") ); pDC->OutputNewLine (); } if(pBitmap) { pBitmap->DestroyGreyscaleVersion(); delete pBitmap; } // Success! return TRUE; } /******************************************************************************************** > BOOL AIEPSRenderRegion::ExportShadow ( OILBitmap *pBitmap, UINT32 Darkness, DocRect &Bounds ) Author: Graeme_Sutherland (Xara Group Ltd) Created: 11/4/00 Inputs: pShadow - A pointer to a shadow node. Returns: TRUE - Success. FALSE - An error occured. Purpose: Renders a shadow into a DIB, and exports this as a bitmap to the AI file. SeeAlso: NodeBevel::ExportRender ********************************************************************************************/ BOOL AIEPSRenderRegion::ExportShadow ( OILBitmap *pBitmap, UINT32 Darkness, DocRect &Bounds ) { // Set up the local variables. // KernelDC *pDC = (KernelDC*)CCDC::ConvertFromNativeDC(RenderDC); DocCoord Position ( Bounds.lo.x, Bounds.hi.y ); double Width = static_cast ( Bounds.Width () ) / 1000; double Height = static_cast ( Bounds.Height () ) / 1000; double BMPWidth = static_cast ( pBitmap->GetWidth () ); double BMPHeight = static_cast ( pBitmap->GetHeight () ); DocColour ShadowColour = *( static_cast ( GetCurrentAttribute ( ATTR_FILLGEOMETRY ) ) ->GetStartColour () ); DocColour BlendedShadow = AlphaBlend ( ShadowColour, mBackgroundColour, Darkness ); // And use the shadow colour to build a contone bitmap palette. pBitmap->BuildContonePalette ( BlendedShadow, mBackgroundColour, EFFECT_RGB, RenderView ); // Set up the bitmap translation matrix. LoadTranslationMatrix ( Position ); // And calcuate the scale values for placing the bitmap. m_a = Width / BMPWidth; m_d = Height / BMPHeight; // Write out the bitmap header. WriteBitmapHeader ( (INT32)BMPWidth, (INT32)BMPHeight ); // Export the bitmap. WriteContoneBody ( pBitmap ); // Finish off the bitmap record. WriteBitmapTail (); return TRUE; } /******************************************************************************************** > BOOL AIEPSRenderRegion::ExportLayer ( Layer *pLayer ) Author: Graeme_Sutherland (Xara Group Ltd) Created: 11/4/00 Inputs: pLayer - A pointer to a layer object. Returns: TRUE - Success. FALSE - An error occured. Purpose: If there is an existing layer, it writes the end of layer tags, before creating a new layer record. SeeAlso: AIEPSRenderRegion::EndLayer () ********************************************************************************************/ BOOL AIEPSRenderRegion::ExportLayer ( Layer *pLayer ) { // Tidy up the existing layer, if it exists. EndLayer (); // And write out the next layer. return StartLayer ( static_cast ( pLayer->FindNext ( CC_RUNTIME_CLASS ( Layer ) ) ) ); } /******************************************************************************************** > BOOL AIEPSRenderRegion::StartLayer ( Layer *pLayer ) Author: Graeme_Sutherland (Xara Group Ltd) Created: 11/4/00 Inputs: pLayer - A pointer to a layer object. Returns: TRUE - Success. FALSE - An error occured. Purpose: Writes a layer record to the file. SeeAlso: AIEPSRenderRegion::EndLayer () ********************************************************************************************/ BOOL AIEPSRenderRegion::StartLayer ( Layer *pLayer ) { // Catch null pointers to layers. if ( pLayer != NULL ) { KernelDC *pDC = static_cast ( CCDC::ConvertFromNativeDC(RenderDC) ); TCHAR LayerName [258]; // Set up the layer's name. camSprintf ( LayerName, _T("(%s)"), ( TCHAR* ) pLayer->GetLayerID () ); // Start a layer definition. pDC->OutputToken ( _T("%AI5_BeginLayer") ); pDC->OutputNewLine (); // Set the layer's properties. pDC->OutputValue ( ( UINT32 ) pLayer->IsVisible () ); // Is the layer visible? pDC->OutputValue ( ( UINT32 ) 1 ); // Is the layer previewed? pDC->OutputValue ( ( UINT32 ) !pLayer->IsLocked () ); // Is the layer enabled? pDC->OutputValue ( ( UINT32 ) pLayer->IsPrintable () );//Is the layer printable? pDC->OutputValue ( ( UINT32 ) 0 ); // Is the layer dimmed? pDC->OutputValue ( ( UINT32 ) 0 ); // No multilayer masks. // The ID colour. pDC->OutputValue ( ( UINT32 ) m_LayerColour % 27 ); // Set the layer's ID colour. m_LayerColour ++; // Increment colour ID. // Colour values - I don't know what they're used for. pDC->OutputValue ( ( UINT32 ) 0 ); // Red intensity. pDC->OutputValue ( ( UINT32 ) 0 ); // Blue intensity. pDC->OutputValue ( ( UINT32 ) 0 ); // Green intensity. // Write out the Lb tag. pDC->OutputToken ( _T("Lb") ); pDC->OutputNewLine (); // Set the layer's name. pDC->OutputToken ( LayerName ); pDC->OutputToken ( _T("Ln") ); pDC->OutputNewLine (); // Tell Camelot that there is an active layer. m_ActiveLayer = TRUE; } // Success! return TRUE; } /******************************************************************************************** > void AIEPSRenderRegion::RenderChar (WCHAR ch, Matrix * pMatrix) Author: Chris_Gallimore (Xara Group Ltd) Created: 1/11/00 Purpose: Overrides the default behaviour for rendering a single character. Used to store additional information on text blocks needed for AI text. i.e. the overflow text block ((xxxxxxxxx) TX) which occurs after the unjustified ((x) Tx\n (x) Tx\n) block. Both of these are needed for correct Illustrator export of text. SeeAlso: EPSRenderRegion::RenderChar ********************************************************************************************/ BOOL AIEPSRenderRegion::RenderChar(WCHAR ch, Matrix* pMatrix) { TCHAR buf[20]; BOOL result = FALSE; // assume failure if nothing given. if (m_bTextAsShapes) { result = RenderCharAsShape (ch, pMatrix); } else { // Output matrix (a b c d e f Tm\n) - a,b,c and d are in the range 1.0 to -1.0 // Only do this for text on paths if (ExportingOnPath ()) { KernelDC *pDC = (KernelDC*)CCDC::ConvertFromNativeDC(RenderDC); pDC->OutputMatrix(pMatrix); pDC->OutputToken(_T("Tm")); pDC->OutputNewLine(); } // call the base class first, so that the attributes are exported BEFORE the character result = EPSRenderRegion::RenderChar (ch, pMatrix);; // store any extra information, closing an attribute gap if one is open. if (ExportingOnPath ()) { OverflowTextFinishGap (); camSprintf (buf,_T("%c"),ch); OverflowTextWrite (buf); } } // Call the base class' function to do the standard export. return result; } /******************************************************************************************** > void AIEPSRenderRegion::RenderCharAsShape (WCHAR ch, Matrix * pMatrix) Author: Chris_Gallimore (Xara Group Ltd) Created: 1/11/00 Purpose: Allows text to be drawn as shapes, not characters, so that AI can cope with gradient filled text. SeeAlso: AIEPSRenderRegion::RenderChar, EPSRenderRegion::RenderChar ********************************************************************************************/ BOOL AIEPSRenderRegion::RenderCharAsShape (WCHAR ch, Matrix* pMatrix) { BOOL result = FALSE; // create the char's path Path* pCharPath=CreateCharPath(ch,pMatrix); // if the path got created correctly, then we done good... if (pCharPath!=NULL) { // if at least one point, draw path using current attibutes in render region if (pCharPath->GetNumCoords()!=0) DrawPath(pCharPath); // clean up delete pCharPath; result = TRUE; } return result; } /******************************************************************************************** > void AIEPSRenderRegion::Initialise() Author: Graeme_Sutherland (Xara Group Ltd) Created: 29/3/00 Purpose: Sets up render region - it outputs the AIEPS file header comments, and intialises the rendering attributes. SeeAlso: EPSRenderRegion::Initialise, EPSRenderRegion::InitAttributes ********************************************************************************************/ void AIEPSRenderRegion::Initialise() { // Set up member variables. m_a = 1; m_b = 0; m_c = 0; m_d = 1; m_T = DocCoord ( 0, 0 ); m_ActiveLayer = FALSE; m_LayerColour = 0; ClearGradientCache (); // Call the EPSRenderRegion method to re-enter the export loop. EPSRenderRegion::InitAttributes (); } /******************************************************************************************** > BOOL AIEPSRenderRegion::WriteEPSVersion ( void ) Author: Graeme_Sutherland (Xara Group Ltd) Created: 28/3/000 Inputs: pDC - The device context to output to. Returns: TRUE if ok; FALSE if error (e.g. file/disk error or printer driver error) Purpose: Writes the relevant EPS version declaration. SeeAlso: EPSRenderRegion::WriteEPSVersion ********************************************************************************************/ BOOL AIEPSRenderRegion::WriteEPSVersion ( void ) { // Cast the pointer to an appropriate DC. KernelDC *pDC = static_cast ( CCDC::ConvertFromNativeDC(RenderDC) ); // Output the special AIEPS header start token. pDC->OutputToken ( _T("%!PS-Adobe-3.0") ); pDC->OutputNewLine (); return TRUE; } /******************************************************************************************** > BOOL AIEPSRenderRegion::WriteEPSProcessColours ( void ) Author: Graeme_Sutherland (Xara Group Ltd) Created: 28/3/000 Inputs: pDC - The device context to output to. Returns: TRUE if ok; FALSE if error (e.g. file/disk error or printer driver error) Purpose: Writes the colour type used to disk. SeeAlso: EPSRenderRegion::WriteEPSProcessColours ********************************************************************************************/ BOOL AIEPSRenderRegion::WriteEPSProcessColours ( void ) { // Cast a pointer to the appropriate DC. KernelDC *pDC = (KernelDC*)CCDC::ConvertFromNativeDC(RenderDC); // Output the process colours pDC->OutputToken ( _T("%%DocumentProcessColors: Cyan Magenta Yellow Black") ); pDC->OutputNewLine (); return TRUE; } /******************************************************************************************** > BOOL AIEPSRenderRegion::WriteEPSResources ( EPSFilter *pFilter, Document *pDocument ) Author: Graeme_Sutherland (Xara Group Ltd) Created: 28/3/00 Inputs: pDC - The device context to output to. Returns: TRUE if ok; FALSE if error (e.g. file/disk error or printer driver error) Purpose: This function writes out the resource inclusion string required for the AI file. SeeAlso: EPSRenderRegion::WriteSetup ********************************************************************************************/ BOOL AIEPSRenderRegion::WriteEPSResources ( EPSFilter *pFilter, Document *pDocument ) { // Cast a pointer to the appropriate DC. KernelDC *pDC = (KernelDC*)CCDC::ConvertFromNativeDC(RenderDC); // Call the export method in the document. pDocument->WriteEPSResources ( pFilter ); // Add a few things necessary for the AI file format. pDC->OutputToken ( _T("%AI3_ColorUsage: Color") ); pDC->OutputNewLine (); pDC->OutputToken ( _T("%AI5_FileFormat 2.0") ); pDC->OutputNewLine (); // All done. return TRUE; } /******************************************************************************************** > BOOL AIEPSRenderRegion::WriteEPSProlog ( EPSFilter *pFilter, Document *pDocument ) Author: Graeme_Sutherland (Xara Group Ltd) Created: 28/3/00 Inputs: pDC - The device context to output to. Returns: TRUE if ok; FALSE if error (e.g. file/disk error or printer driver error) Purpose: This method writes out the initialisation code for the included resources. SeeAlso: EPSRenderRegion::WriteSetup ********************************************************************************************/ BOOL AIEPSRenderRegion::WriteEPSProlog ( EPSFilter *pFilter, Document *pDocument ) { // Call the export method in the document. pDocument->WriteEPSProlog ( pFilter ); // All done. return TRUE; } /******************************************************************************************** > virtual BOOL AIEPSRenderRegion::WriteGradientFills ( Document *pDocument ) Author: Graeme_Sutherland (Xara Group Ltd) Created: 1/3/00 Inputs: pDocument - The document to be written. Outputs: - Returns: TRUE if successful. FALSE if error (e.g. file/disk error) Purpose: Parses through the tree, identifies any gradient fills, and writes them to the export DC's file. ********************************************************************************************/ BOOL AIEPSRenderRegion::WriteGradientFills ( Document *pDocument ) { // Get the spread from the document. Spread *pSpread = pDocument->FindFirstSpread (); // Build the gradient cache up from all the fills under the spread, after cleaning any // junk out of it (there shouldn't be any there, but that ain't the point) ClearGradientCache (); BuildGradientCache (pSpread); // Write out the number of fills and their definition. WriteGradientCount (); WriteGradientDefinitions (); // The fill has been successfully saved, so return TRUE. return TRUE; } /******************************************************************************************** > BOOL AIEPSRenderRegion::WriteLinearFill ( FillGeometryAttribute *pFill, INT32 id ) Author: Graeme_Sutherland (Xara Group Ltd) Created: 1/3/00 Inputs: pFill - A pointer to the fill attribute record. id - the id number for the fill Returns: TRUE - If successful. FALSE - The was an error (e.g. file/disk error). Purpose: Writes a linear fill to the disk file. ********************************************************************************************/ BOOL AIEPSRenderRegion::WriteLinearFill ( FillGeometryAttribute * pFill, EFFECTTYPE effect, INT32 id ) { // Extract the fill attribute - this is necessary for processing the fill type. KernelDC *pDC = static_cast ( CCDC::ConvertFromNativeDC(RenderDC) ); INT32 Colours = 2; TCHAR GradientName [32]; DocColour StartColour = *( pFill->GetStartColour () ); DocColour EndColour; ColourRamp *pRamp = pFill->GetColourRamp (); // The fill is, or can be approximated by, a linear fill. camSprintf ( GradientName, _T("(%s %d)"), LinearGradient, id ); // (ChrisG 16/01/01) Four colour fill MUST come first, as it's derived from Three // colour fill, and returns valid for IsThreeColFill(). if ( pFill->IsAFourColFill () ) { // I'm going to use a version of the below here, but for now I'll just // set up the end colour as the most distant end colour. EndColour = *( pFill->GetEndColour3 () ); } else if ( pFill->IsAThreeColFill () ) { // AI doesn't support three colour fills, so I'm going to fake it. This // code merges the two end colours to approximate the fill with a two // colour linear fill. EndColour.Mix ( pFill->GetEndColour (), pFill->GetEndColour2(), 0.5f, NULL, FALSE, NULL ); } else { // Set up the end colour value appropriately. EndColour = *( pFill->GetEndColour () ); } // Count the number of colours. if ( pRamp != NULL ) { UINT32 First = 0; UINT32 Last = 0; // Extract the indices from the colour ramp. pRamp->GetIndexRange ( &First, &Last ); // And add their difference to the colour count. Note, you need to add one // to the result so that you don't register having no colours. There really // should be a count number of elements method in the ramp code, though. Colours += 1 + Last - First; } // (ChrisG 15/01/01) If this is a four colour fill, since we will be adding a mid-colour // below, then we need to include it in the definition if (pFill->IsAFourColFill ()) { Colours ++; } // (ChrisG 4/4/2001) Add in the extra colours given from the HSV fill effect emulation if (effect == EFFECT_HSV_SHORT || effect == EFFECT_HSV_LONG) { Colours += (Colours - 1) * NumFillEmulationSteps; } // Write the gradient's header. pDC->OutputToken ( _T("%AI5_BeginGradient:") ); pDC->OutputToken ( GradientName ); pDC->OutputNewLine (); // Then the type definition. pDC->OutputToken ( GradientName ); pDC->OutputValue ( static_cast ( LinearFill ) ); pDC->OutputValue ( static_cast ( Colours ) ); pDC->OutputToken ( _T("Bd") ); pDC->OutputNewLine (); pDC->OutputToken ( _T("[") ); pDC->OutputNewLine (); // (ChrisG 3/4/2001) Get bias and convert to 0-100 range (from -1 to +1) // This will be used later on to offset the positions CProfileBiasGain profile = pFill->GetProfile (); INT32 bias = (INT32) ((1 - (double) profile.GetBias ()) * 50); // Write out the last colour. WriteGradientEntry ( &EndColour, 100, 50 ); // set up the last colour and position (for the fill effect emulation. DocColour * pLastColour = &EndColour; INT32 lastPos = 100; // Mid colour for four colour fills - this needs to be stored longer so that it can be // re-used in the case of HSV fill effects. DocColour MidColour; // If the fill is a four colour fill, add a mid-colour that's a blend of the // second and third colours. if ( pFill->IsAFourColFill () ) { MidColour.Mix ( pFill->GetEndColour (), pFill->GetEndColour2 (), 0.5f, NULL, FALSE, NULL ); // Write out the extra fill steps, if there are any. if (effect != EFFECT_RGB) { WriteFillEffectSteps (pLastColour, lastPos, &MidColour, 50, bias, effect); pLastColour = &MidColour; } // And set the value. WriteGradientEntry ( &MidColour, 50, bias); } // Otherwise, process the colour ramp. else if ( pRamp != NULL ) { ColRampItem *pItem = pRamp->GetLastCol (); // Write in the ramp's colour values. while ( pItem != NULL ) { // Get the position of the colour in the fill. INT32 Ratio = static_cast ( 100 * pItem->GetPosition () ); DocColour *pCol = pItem->GetColourAddr (); // Write out the extra fill steps, if there are any. if (effect != EFFECT_RGB) { WriteFillEffectSteps (pLastColour, lastPos, pCol, Ratio, bias, effect); pLastColour = pCol; lastPos = Ratio; } // And set the value. WriteGradientEntry ( pCol, Ratio, 50 ); // Increment the pointer onto the next item. pItem = pRamp->GetPrevCol ( pItem ); } } // Write out the extra fill steps, if there are any. if (effect != EFFECT_RGB) { WriteFillEffectSteps (pLastColour, lastPos, &StartColour, 0, bias, effect); } // Write out the start colour. WriteGradientEntry ( &StartColour, 0, bias ); // Write out the end of gradient tokens. pDC->OutputToken ( _T("BD") ); pDC->OutputNewLine (); pDC->OutputToken ( _T("%AI5_EndGradient") ); pDC->OutputNewLine (); // It worked! return TRUE; } /******************************************************************************************** > BOOL AIEPSRenderRegion::WriteRadialFill ( AttrFillGeometry *pFill, INT32 id ) Author: Graeme_Sutherland (Xara Group Ltd) Created: 1/3/00 Inputs: pFill - A pointer to the fill attribute record. id - The fill's id number Returns: TRUE - If successful. FALSE - The was an error (e.g. file/disk error). Purpose: Writes a Radial fill to the disk file. ********************************************************************************************/ BOOL AIEPSRenderRegion::WriteRadialFill (FillGeometryAttribute * pFill, EFFECTTYPE effect, INT32 id) { // Extract the fill attribute - this is necessary for processing the fill type. KernelDC *pDC = static_cast ( CCDC::ConvertFromNativeDC(RenderDC) ); INT32 Colours = 2; TCHAR GradientName [32]; DocColour StartColour = *( pFill->GetStartColour () ); DocColour EndColour = *( pFill->GetEndColour () ); ColourRamp *pRamp = pFill->GetColourRamp (); // The fill is, or can be approximated by, a Radial fill. camSprintf ( GradientName, _T("(%s %d)"), RadialGradient, id ); // Count the number of colours. if ( pRamp != NULL ) { UINT32 First = 0; UINT32 Last = 0; // Extract the indices from the colour ramp. pRamp->GetIndexRange ( &First, &Last ); // And add their difference to the colour count. Note, you need to add one // to the result so that you don't register having no colours. There really // should be a count number of elements method in the ramp code, though. Colours += 1 + Last - First; if (effect != EFFECT_RGB) { // Add in the extra steps for the fill effect (Five extra steps between each // pair of colours). Colours += (Colours - 1) * NumFillEmulationSteps; } } // Write the gradient's header. pDC->OutputToken ( _T("%AI5_BeginGradient:") ); pDC->OutputToken ( GradientName ); pDC->OutputNewLine (); // Then the type definition. pDC->OutputToken ( GradientName ); pDC->OutputValue ( static_cast ( RadialFill ) ); pDC->OutputValue ( static_cast ( Colours ) ); pDC->OutputToken ( _T("Bd") ); pDC->OutputNewLine (); pDC->OutputToken ( _T("[") ); pDC->OutputNewLine (); // (ChrisG 3/4/2001) Get bias and convert to 0-100 range (from -1 to +1) CProfileBiasGain profile = pFill->GetProfile (); INT32 bias = (INT32) ((1 - (double) profile.GetBias ()) * 50); // Write out the start colour. WriteGradientEntry ( &StartColour, 0, bias ); DocColour * pLastCol = &StartColour; INT32 lastPos = 0; // Process the colour ramp. if ( pRamp != NULL ) { ColRampItem *pItem = pRamp->GetFirstCol (); // Write in the ramp's colour values. while ( pItem != NULL ) { // Get the position of the colour in the fill. INT32 Ratio = static_cast ( 100 * pItem->GetPosition () ); DocColour *pCol = pItem->GetColourAddr (); // Write out the intermediate fill steps (if there are any). if (effect != EFFECT_RGB) { WriteFillEffectSteps (pLastCol, lastPos, pCol, Ratio, bias, effect); pLastCol = pCol; lastPos = Ratio; } // And set the value. WriteGradientEntry ( pCol, Ratio, 50 ); // Increment the pointer onto the next item. pItem = pRamp->GetNextCol ( pItem ); } } // Write out the intermediate fill steps (if there are any). if (effect != EFFECT_RGB) { WriteFillEffectSteps (pLastCol, lastPos, &EndColour, 100, bias, effect); } // Write out the last colour. WriteGradientEntry ( &EndColour, 100, 50 ); // Write out the end of gradient tokens. pDC->OutputToken ( _T("BD") ); pDC->OutputNewLine (); pDC->OutputToken ( _T("%AI5_EndGradient") ); pDC->OutputNewLine (); // It worked! return TRUE; } /******************************************************************************************** > virtual BOOL AIEPSRenderRegion::WriteGradientEntry ( DocColour *pColour, INT32 Position, INT32 Midpoint ) Author: Graeme_Sutherland (Xara Group Ltd) Created: 24/3/00 Inputs: pColour - A pointer to a Camelot colour. Position - The position of the colour within the fill. Midpoint - The midpoint of the fill. Returns: TRUE if successful. FALSE if error (e.g. file/disk error) Purpose: Writes a line into the definition of a gradient fill. ********************************************************************************************/ BOOL AIEPSRenderRegion::WriteGradientEntry ( DocColour *pColour, INT32 Position, INT32 Midpoint ) { INT32 red; // INT32 green; // RGB colour values INT32 blue; // PColourCMYK CMYKColour; // The colour to be stored. KernelDC *pDC = (KernelDC*)CCDC::ConvertFromNativeDC(RenderDC); TCHAR Line [80]; // Contains the string to be stored. // (ChrisG 3/4/2001) if (Midpoint < 13) Midpoint = 13; else if (Midpoint > 87) Midpoint = 87; pColour->GetCMYKValue ( &CMYKColour ); // Extract the CMYK value. if (pColour->GetColourModel () == COLOURMODEL_RGBT || pColour->GetColourModel () == COLOURMODEL_HSVT) { // Use RGB syntax. pColour->GetRGBValue ( &red, &green, &blue ); camSprintf ( Line, _T("2 %d %d%%_Bs"), Midpoint, Position ); pDC->OutputColour ( &CMYKColour ); pDC->OutputColourValue ( red ); pDC->OutputColourValue ( green ); pDC->OutputColourValue ( blue ); pDC->OutputToken ( Line ); pDC->OutputNewLine (); } else { // It is necessary to build the output line like this, so that there isn't a space // between the position of the colour, and the %_Bs operator. camSprintf ( Line, _T("1 %d %d%%_Bs"), Midpoint, Position ); // Build the output string. pDC->OutputColour ( &CMYKColour ); // Write the colour to the file. pDC->OutputToken ( Line ); // Write the output string out. pDC->OutputNewLine (); // And write a new-line tag. } return TRUE; // All done successfully. } /******************************************************************************************** > virtual BOOL AIEPSRenderRegion::WriteFillEffectSteps ( DocColour * pColour1, INT32 pos1, DocColour * pColour2, INT32 pos2, INT32 bias, EFFECTTYPE effect) Author: Chris_Gallimore (Xara Group Ltd) Created: 4/4/2001 Inputs: pColour1 - The colour to start from. pos1 - The position of the start colour within the fill (0-100%). pColour2 - The colour to end at. pos2 - The position of the end colour within the entire fill (0-100%) bias - The fill's bias part of any profile (0-100%). effect - The type of fill effect (EFFECT_HSV_SHORT or EFFECT_HSV_LONG) Returns: TRUE if successful. FALSE if error (e.g. file/disk error) Purpose: Writes the additional entries required for a special fill effect (rainbow or alt-rainbow) to the gradient definition, as a series of steps. See Also: AIEPSRenderRegion::WriteGradientEntry AIEPSRenderRegion::WriteLinearFill AIEPSRenderRegion::WriteRadialFill ********************************************************************************************/ BOOL AIEPSRenderRegion::WriteFillEffectSteps ( DocColour *pColour1, INT32 pos1, DocColour *pColour2, INT32 pos2, INT32 bias, EFFECTTYPE effect) { ERROR3IF (effect == EFFECT_RGB, "AIEPSRenderRegion::WriteFillEffectSteps, an attempt was made to write HSV fill effect steps on an RGB fade fill"); // RGB fades are the 'standard' fill, so we don't want to do any special processing on them if (effect != EFFECT_RGB) { // Start and end colour values INT32 h1 = 0; INT32 h2 = 0; INT32 s1 = 0; INT32 s2 = 0; INT32 v1 = 0; INT32 v2 = 0; // intermediate colour values / increments DocColour midColour; double hAdd = 0; double sAdd = 0; double vAdd = 0; double posAdd = 0; // First get the start and end colour components pColour1->GetHSVValue (&h1, &s1, &v1); pColour2->GetHSVValue (&h2, &s2, &v2); // Calculate the increments // // NOTE: if the hue changes by more than 180 deg. in a short HSV (rainbow) fill // (or not more than 180 deg. in a long HSV (alt-rainbow) fill), then the direction // must be reversed. This is done by changing the hue increment value. hAdd = h2-h1; if (effect == EFFECT_HSV_SHORT) { if (hAdd > 180) hAdd -= 360; else if (hAdd < -180) hAdd += 360; } else { if ((hAdd > 0) && (hAdd <= 180)) hAdd -= 360; else if ((hAdd <= 0) && (hAdd >= -180)) hAdd += 360; } hAdd = hAdd / (NumFillEmulationSteps + 1); sAdd = (s2-s1) / (NumFillEmulationSteps + 1); vAdd = (v2-v1) / (NumFillEmulationSteps + 1); posAdd = ((double) pos2-pos1) / (NumFillEmulationSteps + 1); // Cycle through the stops calcuating each stop's colour and position, before // writing them out. for (INT32 i=1; i=360) h2 -= 360; else if (h2<0) h2 += 360; // create the colour and export. midColour.SetHSVValue (h2, s2, v2); WriteGradientEntry (&midColour, pos2, 50); } } // End of is fill effect not an RGB fade. return TRUE; } /******************************************************************************************** > virtual BOOL AIEPSRenderRegion::WriteDocumentSetup ( Document *pDocument, EPSFilter *pFilter ) Author: Graeme_Sutherland (Xara Group Ltd) Created: 24/3/00 Inputs: pDocument - The document being outputted. pFilter - A pointer to the export filter. Outputs: - Returns: TRUE if successful. FALSE if error (e.g. file/disk error) Purpose: Browse through the tree, and if a text story is found invoke the document setup function. By only exporting the font setup when necessary, the file size produced is smaller (which makes it easier for me to interpret :) ), and it should hopefully make the filter run a bit faster too. ********************************************************************************************/ BOOL AIEPSRenderRegion::WriteDocumentSetup ( Document *pDocument, EPSFilter *pFilter ) { // Get the spread from the document. Spread *pSpread = pDocument->FindFirstSpread (); Node *pTextStory = NULL; // Try to find a single instance of the TextStory node. pTextStory = SliceHelper::FindNextOfClass ( pSpread, pSpread, CC_RUNTIME_CLASS ( TextStory ) ); // If something was found... if ( pTextStory != NULL ) { // ... write out the font set-up. pDocument->WriteEPSSetup ( pFilter ); } else { // Otherwise just get the relevant bits. pDocument->AIExportExtras ( pFilter->GetExportDC () ); } // It worked! return TRUE; } /******************************************************************************************** > BOOL AIEPSRenderRegion::EndLayer () Author: Graeme_Sutherland (Xara Group Ltd) Created: 11/4/00 Inputs: - Returns: TRUE - Success. FALSE - An error occured. Purpose: If there is an existing layer, it writes the end of layer tags, before creating a new layer record. SeeAlso: AIEPSRenderRegion::ExportLayer () ********************************************************************************************/ BOOL AIEPSRenderRegion::EndLayer () { // Only act if there's an active layer. if ( m_ActiveLayer ) { // Cast the RenderDC pointer into a useful form. KernelDC *pDC = static_cast ( CCDC::ConvertFromNativeDC(RenderDC) ); // Write out the end of layer tag. pDC->OutputToken ( _T("LB") ); pDC->OutputNewLine (); pDC->OutputToken ( _T("%AI5_EndLayer") ); pDC->OutputNewLine (); // Set m_ActiveLayer to be FALSE. m_ActiveLayer = FALSE; } return TRUE; } /******************************************************************************************** > void AIEPSRenderRegion::WriteGradientFillInstance () Author: Graeme_Sutherland (Xara Group Ltd) Created: 9/03/00 Purpose: This function identifies whether or not the current fill is a gradient fill, and writes in the appropriate values if it is. I've already created the records of the gradients themselves (see above), so this is just a case of matching the gradient with its ID, and Adobe Illustrator can then put the records together. (ChrisG 18/12/00) - all this does now is increment the counters used for determining which gradient fill is currently being used. SeeAlso: EPSRenderRegion::WriteGradientFill, WriteGradientFillUsage ********************************************************************************************/ void AIEPSRenderRegion::WriteGradientFillInstance () { KernelDC *pDC = (KernelDC*)CCDC::ConvertFromNativeDC(RenderDC); FillGeometryAttribute *pFillAttr = ( FillGeometryAttribute* ) CurrentAttrs[ATTR_FILLGEOMETRY].pAttr; DocCoord StartPoint = *( pFillAttr->GetStartPoint () ); DocCoord EndPoint = *( pFillAttr->GetEndPoint () ); DocCoord Delta = EndPoint - StartPoint; double Angle = 90.0f; double Length = FlashExportDC::CalculateLength ( Delta ); TCHAR Name [80]; BOOL IsRadial = pFillAttr->IsARadialFill () || pFillAttr->IsASquareFill (); INT32 id = 0; if (IsGradientFillValidForExport (pFillAttr)) { id = FindGradientInCache (pFillAttr, GetFillEffect ()); // If the fill is, or can be approximated by, a radial fill, set the the definition to // indicate this. if ( IsRadial ) { // Set up the name to show that it's a radial gradient. camSprintf ( Name, _T("(%s %d)"), RadialGradient, id ); } // Otherwise it's represented by a linear fill. else { // Set up the name to show that it's a linear gradient. camSprintf ( Name, _T("(%s %d)"), LinearGradient, id ); // Reset the end point value if we're dealing with a three colour fill. if ( pFillAttr->IsAThreeColFill () ) { DocCoord EndPoint2 = *( pFillAttr->GetEndPoint2 () ); // Complete the parallellogram of vectors. EndPoint += EndPoint2 - StartPoint; } } // (ChrisG 16/01/01) Since all the endpoint calculations have now been done, // we can determine Delta, length and angle. (NOTE: these cannot be done // before here, as we wouldn't be using the correct endpoint values.) Delta = EndPoint - StartPoint; Length = FlashExportDC::CalculateLength ( Delta ); // The tangent of an angle is undefined for PI/2 radians. (i.e. when Delta.x // is zero.) if ( Delta.x != 0 ) { Angle = atan ( ( double ) Delta.y / ( double ) Delta.x ); // AI uses degrees, whereas the standard maths library uses radians, so // effect a conversion. Angle *= ( 180.0 / PI ); // Pi radians = 180 degrees. // Tan repeats itself every Pi radians, so add 180 degrees to the angle // if it's got a negative value of Delta.y. Special case: If Delta.x is // negative, and Delta.y is 0, the angle is 180 degrees. if ( Delta.x < 0 ) { // I want the angle to be in the interval ]-180, 180], and so I've // got special case handling, depending on which quadrant it lies // in initially. Without the if statement here, the interval would // be ]-90, 270]. if ( Angle >= 0.0 ) { Angle -= 180.0; } else { Angle += 180.0; } } } // More special case stuff. If Delta.x is 0, then the angle must be either // +90 or -90 degrees. And we can get the sign of the angle from the sign // of Delta.y else if ( Delta.y < 0 ) { Angle = -90.0; } // AI uses points as a co-ordinate system - we use millipoints. Dividing // the length of the fill by 1,000 will thus return it's value in points. Length /= 1000; // Write out the appropriate fill record. pDC->OutputToken ( _T("1") ); // It is a clipped gradient. pDC->OutputToken ( Name ); // The gradient's name. pDC->OutputCoord ( StartPoint ); // Start point for the gradient fill. pDC->OutputReal ( Angle ); // The angle of the fill from the X-Axis. pDC->OutputReal ( Length ); // The length of the matrix. pDC->OutputToken ( _T("1 0 0 1 0 0 Bg") ); // Matrix manipulating the fill. (Unused.) pDC->OutputNewLine (); } // End of IsGradientFillValidForExport. } /******************************************************************************************** > void AIEPSRenderRegion::OutputFillColour () Author: Graeme_Sutherland (Xara Group Ltd) Created: 29/3/00 Purpose: If the fill is a gradient fill, this method will invoke the WriteGradientFillInstance method, otherwise it will call the base EPSRenderRegion OutputFillColour method. This allows Camelot to support gradient fills in the AI file format. (ChrisG 30/3/2001) - Gradient fill instances should be exported when they are used on an object, not when a fill attribute is found. This way, fills can be applied to multiple objects. SeeAlso: AIEPSRenderRegion::OutputStrokeColour ********************************************************************************************/ void AIEPSRenderRegion::OutputFillColour () { // Output the actual fill colour. EPSRenderRegion::OutputFillColour (); } /******************************************************************************************** > BOOL AIEPSRenderRegion::LoadBitmapMatrix ( NodeBitmap *pNodeBMP ) Author: Graeme_Sutherland (Xara Group Ltd) Created: 4/4/00 Inputs: pNodeBitmap - A pointer to the bitmap node being exported. Returns: TRUE - Successful. FALSE - An error has occured. Purpose: This code generates a matrix to position and manipulate a bitmap within an AI file. Roughly, it calculates the angle of rotation, followed by the skew angle, and creates a matrix using these values. See Also: FlashExportDC::WriteBitmapFill () ********************************************************************************************/ BOOL AIEPSRenderRegion::LoadBitmapMatrix ( NodeBitmap *pNodeBMP ) { // Step 1: Extract the co-ordinates of three corners from the bitmap record, and store // them as named co-ordinates within this function. The names are a historical // reference to the original names within the FlashExportDC, and refer to the // positions of a bitmap fill. double Angle = pNodeBMP->GetRotationAngle (); double SinRotate = sin ( Angle ); double CosRotate = cos ( Angle ); // double Skew = 0; DocCoord LowLeft = pNodeBMP->Parallel [3]; // Low corner of the bitmap. DocCoord TopLeft = pNodeBMP->Parallel [0]; // Max Y corner. DocCoord LowRight = pNodeBMP->Parallel [2]; // Max X corner. // Step 2: Calculate the normalised dot product of the two vectors, and this forms // the cosine of their internal angle. double WidthX = static_cast ( LowLeft.x - LowRight.x ) / 1000; double WidthY = static_cast ( LowLeft.y - LowRight.y ) / 1000; double HeightX = static_cast ( TopLeft.x - LowLeft.x ) / 1000; double HeightY = static_cast ( TopLeft.y - LowLeft.y ) / 1000; // Get the lengths of these vectors to normalise the dot-product. (Necessary for the // trignometric function calculations.) double Width = sqrt ( ( WidthX * WidthX ) + ( WidthY * WidthY ) ); double Height = sqrt ( ( HeightX * HeightX ) + ( HeightY * HeightY ) ); /* // The dot product calculation. By normalising the result, the cosine of the angle is // calculated. double CosSkew = ( ( WidthX * HeightX ) + ( HeightY * WidthY ) ) / ( Height * Width ); double SinSkew = sqrt ( 1 - ( CosSkew * CosSkew ) ); // Multiply the width of the shape by the sine derived from the dot product. This will // give the actual width, and not the one distorted by skewing. Width *= SinSkew; // If the DotSine value is not 0, calculate the tan value that forms the skew angle. // Since tan is undefined for 0, I'm ignoring this state. if ( SinSkew != 0 ) { Skew = - CosSkew / SinSkew; } */ // Grab a reference to the bitmap, and pull it's height and width from it. KernelBitmap *pBitmap = pNodeBMP->GetBitmap (); // A reference to the bitmap. double BMPWidth = static_cast ( pBitmap->GetWidth () ); double BMPHeight = static_cast ( pBitmap->GetHeight () ); // Catch BMPWidth or BMPHeight set to zero. This shouldn't happen... if ( BMPWidth == 0 ) BMPWidth = 1; if ( BMPHeight == 0 ) BMPHeight = 1; // Step 4: Use the above values to calculate the values with which to populate the // transformation matrix. double ScaleX = Width / BMPWidth; double ScaleY = Height / BMPHeight; // Step 5: Use these to build the matrix. The variables a, b, c, d refer to the AI // matrix entries. The form of the matrix is: // // | SinRotate CosRotate | | ScaleX 0 | // | -CosRotate SinRotate | | Skew * ScaleY ScaleY | m_a = ( SinRotate * ScaleX ); // + ( CosRotate * Skew * ScaleY ); m_b = ( - CosRotate * ScaleX ); // + ( SinRotate * Skew * ScaleY ); m_c = CosRotate * ScaleY; m_d = SinRotate * ScaleY; // There's a weird bug with the AI export, in that a small (>0.0001) value is misread // by Adobe Illustrator as a monstrous value. To get around this, I'm going to set all // near zero values to zero. if ( Absol ( m_a ) < 1e-4 ) { m_a = 0; } if ( Absol ( m_b ) < 1e-4 ) { m_b = 0; } if ( Absol ( m_c ) < 1e-4 ) { m_c = 0; } if ( Absol ( m_d ) < 1e-4 ) { m_d = 0; } // Step 6: Calculate the translation part of the matrix. m_T = TopLeft; return TRUE; } /******************************************************************************************** > BOOL AIEPSRenderRegion::LoadTranslationMatrix ( DocCoord &Translation ) Author: Graeme_Sutherland (Xara Group Ltd) Created: 10/4/00 Inputs: - Returns: TRUE - Success. FALSE - An error occured. Purpose: Loads the matrix with: | 1 0 Translation.x | | 0 1 Translation.y | Which can then be written with WriteLoadedMatrix. SeeAlso: AIESPRenderRegion::LoadBitmapMatrix, AIEPSRenderRegion::WriteLoadedMatrix ********************************************************************************************/ BOOL AIEPSRenderRegion::LoadTranslationMatrix ( DocCoord &Translation ) { // Set the transformation part of the matrix to be the identity matrix. m_a = 1; m_b = 0; m_c = 0; m_d = 1; // And load up the translation value. m_T = Translation; return TRUE; } /******************************************************************************************** > BOOL AIEPSRenderRegion::WriteLoadedMatrix ( void ) Author: Graeme_Sutherland (Xara Group Ltd) Created: 10/4/00 Inputs: - Returns: TRUE - Success. FALSE - An error occured. Purpose: Writes a pre-loaded matrix to the file. I've added a couple of functions to create a matrix first, since it'll be more efficient to store the data, and re-write it as required, than to recalculate it twice. SeeAlso: AIESPRenderRegion::LoadBitmapMatrix, AIEPSRenderRegion::LoadTranslationMatrix ********************************************************************************************/ BOOL AIEPSRenderRegion::WriteLoadedMatrix ( void ) { // Cast the DC pointer to be a KernelDC pointer. This allows it to access useful member // functions. KernelDC *pDC = static_cast ( CCDC::ConvertFromNativeDC(RenderDC) ); // Write the matrix. pDC->OutputToken ( _T("[") ); pDC->OutputReal ( m_a ); pDC->OutputReal ( m_b ); pDC->OutputReal ( m_c ); pDC->OutputReal ( m_d ); pDC->OutputCoord ( m_T ); pDC->OutputToken ( _T("]") ); return TRUE; } /******************************************************************************************** > void AIEPSRenderRegion::WriteMask ( Path *MaskPath, BOOL OutputGroupToken = FALSE ) Author: Graeme_Sutherland (Xara Group Ltd) Created: 7/4/00 Inputs: DrawPath - The path to render. OutputGroupToken whether or not we output a 'q' clipping group token. Purpose: Creates a path that defines a masking pattern. All successive paths (until the end of mask operator) will be clipped to this shape. I've repeated a lot of code from EPSRenderRegion::ExportPath because I'm scared of changing what is a fairly fundamental class, and then finding that there are several hundred resultant problems. A lot of the filters are pretty shakey at the moment in any case. SeeAlso: RenderRegion::ExportPath ********************************************************************************************/ BOOL AIEPSRenderRegion::WriteMask ( Path *MaskPath, BOOL OutputGroupToken ) { KernelDC *pDC = static_cast ( CCDC::ConvertFromNativeDC(RenderDC) ); // DC for export file. DocCoord *Coords = MaskPath->GetCoordArray(); PathVerb *Verbs = MaskPath->GetVerbArray(); INT32 NumCoords = MaskPath->GetNumCoords(); INT32 ReadPos = 1; // Reading data from. BOOL IsCompound = FALSE; // (ChrisG 16/01/01) Only write masks/clips if there is a path to clip to. if (NumCoords != 0) { if (OutputGroupToken) { // Start the mask. pDC->OutputToken ( _T("q") ); pDC->OutputNewLine (); } // Check to see if this is a compound path. If it is, there will be more than one // moveto command. while ( ReadPos < NumCoords ) { // Find out the type of element that we are over, after the close flag has been // removed. if ( ( ( Verbs [ReadPos] ) & ( ~PT_CLOSEFIGURE ) ) == PT_MOVETO ) { // This is a compound path - stop searching for moveto's. IsCompound = TRUE; // Write the tag to indicate that it's a compound path. pDC->OutputToken ( _T("*u") ); pDC->OutputNewLine (); break; } else { // Try next point ReadPos++; } } // Reset to start of path ReadPos = 0; // loop through the whole path while(ReadPos < NumCoords) { // Find out the type of element that we are over (after the close flag has been removed) // Coord P[4]; switch ( (Verbs[ReadPos]) & (~PT_CLOSEFIGURE) ) { case PT_MOVETO: // If this is in the middle of the path, specify how this sub-path // should be rendered - this is needed so that we re-import all the flags // correctly on complex paths (e.g. PT_CLOSEFIGURE) if (ReadPos > 0) { // Write out the mask definition. WriteMaskTags (); } // Output the moveto command pDC->OutputCoord(Coords[ReadPos]); pDC->OutputToken(TEXT("m")); pDC->OutputNewLine(); ReadPos++; break; case PT_LINETO: // Output the lineto command pDC->OutputCoord(Coords[ReadPos]); pDC->OutputToken(TEXT("l")); pDC->OutputNewLine(); ReadPos++; break; case PT_BEZIERTO: // If this point is a bezier, then the next 2 points should be beziers to ENSURE((Verbs[ReadPos+1]) & (~PT_CLOSEFIGURE), "Bezier found with 1 point"); ENSURE((Verbs[ReadPos+2]) & (~PT_CLOSEFIGURE), "Bezier found with 2 points"); // Make sure that this is not at the start of the path ENSURE(ReadPos > 0, "Broken path found while exporting EPS" ); // Output the moveto command pDC->OutputCoord(Coords[ReadPos]); pDC->OutputCoord(Coords[ReadPos+1]); pDC->OutputCoord(Coords[ReadPos+2]); pDC->OutputToken(TEXT("c")); pDC->OutputNewLine(); ReadPos += 3; break; default: ENSURE( FALSE, "We found a Path Element that does not exist!" ); break; } } // Do the final (or possibly only) render command WriteMaskTags (); if ( IsCompound ) { // End compound path pDC->OutputToken(_T("*U")); pDC->OutputNewLine(); } // Wrap up the mask. pDC->OutputToken ( _T("0 O") ); pDC->OutputNewLine (); } // End of if there is a clip path. return TRUE; } /******************************************************************************************** > void AIEPSRenderRegion::WriteMaskTags ( void ) Author: Graeme_Sutherland (Xara Group Ltd) Created: 11/4/00 Returns: TRUE - Success. FALSE - Error. Purpose: Writes out the tags at the end of each block of a mask's definition. SeeAlso: RenderRegion::ExportPath ********************************************************************************************/ BOOL AIEPSRenderRegion::WriteMaskTags () { KernelDC *pDC = static_cast ( CCDC::ConvertFromNativeDC(RenderDC) ); // DC for export file. // Write out the mask definition tags. pDC->OutputToken ( _T("h") ); pDC->OutputNewLine (); pDC->OutputToken ( _T("W") ); pDC->OutputNewLine (); pDC->OutputToken ( _T("n") ); pDC->OutputNewLine (); return TRUE; } /******************************************************************************************** > BOOL AIEPSRenderRegion::WriteBitmapRecord ( OILBitmap *pBitmap ) Author: Graeme_Sutherland (Xara Group Ltd) Created: 10/4/00 Inputs: pBitmap - A pointer to the OIL bitmap object being rendered. Returns: TRUE - If successful. FALSE - If an error is encountered. Purpose: Creates a kernel bitmap instance within the AI file. SeeAlso: NodeBitmap::ExportRender () ********************************************************************************************/ BOOL AIEPSRenderRegion::WriteBitmapRecord ( OILBitmap *pBitmap ) { // Set up the local variables. // KernelDC *pDC = static_cast ( CCDC::ConvertFromNativeDC(RenderDC) ); INT32 Width = static_cast ( pBitmap->GetWidth () ); INT32 Height = static_cast ( pBitmap->GetHeight () ); // Write out the bitmap header. WriteBitmapHeader ( Width, Height ); // Write out the body. WriteBitmapBody ( pBitmap, Width, Height ); // Finish off the bitmap record. WriteBitmapTail (); // It worked! return TRUE; } /******************************************************************************************** > BOOL AIEPSRenderRegion::WriteBitmapHeader ( UINT32 Width, UINT32 Height ) Author: Graeme_Sutherland (Xara Group Ltd) Created: 13/4/00 Inputs: Width - The width (in pixels) of the bitmap image. Height - The height (in pixels) of the bitmap image. Returns: TRUE - If successful. FALSE - If an error is encountered. Purpose: Writes the AI header for a bitmap record. SeeAlso: NodeBitmap::ExportRender () ********************************************************************************************/ BOOL AIEPSRenderRegion::WriteBitmapHeader ( INT32 Width, INT32 Height ) { // Set up kernel DC up in an appropriate form. KernelDC *pDC = static_cast ( CCDC::ConvertFromNativeDC(RenderDC) ); // Declare the bitmap record. pDC->OutputToken ( _T("%AI5_File:") ); pDC->OutputNewLine (); pDC->OutputToken ( _T("%AI5_BeginRaster") ); pDC->OutputNewLine (); // (ChrisG 23/11/00) The Xh operator has been removed, as it wasn't correct (it didn't // have the third, unknown, value), it isn't in the Illustrator File Format Manual, // and all the information in it seems to be duplicated in the standard XI operator, // which is written out immediately after the Xh one was. // The matrix. // WriteLoadedMatrix (); // Height and width. (and an extra value - as there's no mention of this token in the // Illustrator file format manual, I have no idea what it signifies, but Photoshop // needs it, or else it gives MPS Parser Errors). // pDC->OutputValue ( Width ); // pDC->OutputValue ( Height ); // pDC->OutputValue ( 0l ); // pDC->OutputToken ( _T("Xh") ); // pDC->OutputNewLine (); // Write out the XI operator. // The matrix. WriteLoadedMatrix (); // The bounds of the bitmap. pDC->OutputToken ( _T("0 0") ); pDC->OutputValue ( Width ); pDC->OutputValue ( Height ); // Height and width. Note: The AI documentation puts their order the other // way around, but the AI export from Fireworks uses this order. pDC->OutputValue ( Width ); pDC->OutputValue ( Height ); // These values are always constant, and so I don't need to worry about // them changing. pDC->OutputToken ( _T("8 3 0 0 0 0") ); pDC->OutputNewLine (); // Write out the file size. pDC->OutputToken ( _T("%%BeginData:") ); pDC->OutputValue ( static_cast ( Height * Width * 3 ) ); pDC->OutputNewLine (); // Wrap out the XI operator. pDC->OutputToken ( _T("XI") ); pDC->OutputNewLine (); return TRUE; } /******************************************************************************************** > BOOL AIEPSRenderRegion::WriteBitmapBody ( OILBitmap *pBitmap, INT32 Width, INT32 Height ) Author: Graeme_Sutherland (Xara Group Ltd) Created: 13/4/00 Inputs: pBitmap - A pointer to the OIL bitmap object being rendered. Width - The width of the bitmap in pixels. Height - The height of the bitmap in pixels. Returns: TRUE - If successful. FALSE - If an error is encountered. Purpose: Writes the bitmap's data to the file. SeeAlso: NodeBitmap::ExportRender () ********************************************************************************************/ BOOL AIEPSRenderRegion::WriteBitmapBody ( OILBitmap *pBitmap, INT32 Width, INT32 Height ) { // Set up the local variables. KernelDC *pDC = static_cast ( CCDC::ConvertFromNativeDC(RenderDC) ); INT32 Count = 0; // Write the initial %. pDC->OutputToken ( _T("%") ); // Write out the bitmap data. It's necessary to go backwards through y // otherwise the bitmap is rendered upside down. for ( INT32 y = Height - 1; y >= 0; y-- ) { for ( INT32 x = 0; x < Width; x++ ) { Pixel32bpp BMPPixel = pBitmap->ReadPixel32bpp ( x, y ); TCHAR Output [7]; // Wrap the line to keep the file tidy. if ( Count >= 10 ) { pDC->OutputNewLine (); pDC->OutputToken ( _T("%") ); Count = 0; } // Translate the RGB values into an ASCII string. camSprintf ( Output, _T("%.2X%.2X%.2X"), BMPPixel.Red, BMPPixel.Green, BMPPixel.Blue ); // And output them. I'm writing directly to the file because the OutputToken () // function introduces spaces between the values, which is not a good thing for // the AI file format. This way, it's much happier. :) if ( !pDC->OutputTCHARAsChar ( Output, 6 ) ) { // There's been a problem. return FALSE; } Count ++; } } // Write a new line after the bitmap record, to keep things tidy. pDC->OutputNewLine (); return TRUE; } /******************************************************************************************** > BOOL AIEPSRenderRegion::WriteContoneBody ( OILBitmap *pBitmap ) Author: Graeme_Sutherland (Xara Group Ltd) Created: 13/4/00 Inputs: pBitmap - A pointer to the OIL bitmap object being rendered. Returns: TRUE - If successful. FALSE - If an error is encountered. Purpose: Writes a contone bitmap to the file as a 32bpp image. SeeAlso: NodeBitmap::ExportRender () ********************************************************************************************/ BOOL AIEPSRenderRegion::WriteContoneBody ( OILBitmap *pBitmap ) { // Set up the local variables. KernelDC *pDC = static_cast ( CCDC::ConvertFromNativeDC(RenderDC) ); INT32 Width = static_cast ( pBitmap->GetWidth () ); INT32 Height = static_cast ( pBitmap->GetHeight () ); INT32 Count = 0; // Write the initial %. pDC->OutputToken ( _T("%") ); // Write out the bitmap data. It's necessary to go backwards through y // otherwise the bitmap is rendered upside down. for ( INT32 y = Height - 1; y >= 0; y-- ) { for ( INT32 x = 0; x < Width; x++ ) { UINT32 Index = pBitmap->ReadPixelGreyscale ( x, y ); DocColour PixelColour = pBitmap->GetContonePaletteEntry ( Index ); TCHAR Output [7]; INT32 lRed; INT32 lGreen; INT32 lBlue; // Extract the values from the DocColour. PixelColour.GetRGBValue ( &lRed, &lGreen, &lBlue ); // Wrap the line to keep the file tidy. if ( Count >= 10 ) { pDC->OutputNewLine (); pDC->OutputToken ( _T("%") ); Count = 0; } // Translate the RGB values into an ASCII string. camSprintf ( Output, _T("%.2X%.2X%.2X"), lRed, lGreen, lBlue ); // And output them. I'm writing directly to the file because the OutputToken () // function introduces spaces between the values, which is not a good thing for // the AI file format. This way, it's much happier. :) if ( !pDC->OutputTCHARAsChar ( Output, 6 ) ) { // There's been a problem. return FALSE; } Count ++; } } // Write a new line after the bitmap record, to keep things tidy. pDC->OutputNewLine (); return TRUE; } /******************************************************************************************** > BOOL AIEPSRenderRegion::WriteBitmapTail ( void ) Author: Graeme_Sutherland (Xara Group Ltd) Created: 13/4/00 Inputs: - Returns: TRUE - If successful. FALSE - If an error is encountered. Purpose: Wraps up the bitmap instance. SeeAlso: NodeBitmap::ExportRender () ********************************************************************************************/ BOOL AIEPSRenderRegion::WriteBitmapTail ( void ) { // Set up kernel DC up in an appropriate form. KernelDC *pDC = static_cast ( CCDC::ConvertFromNativeDC(RenderDC) ); // Wrap up the bitmap record. pDC->OutputToken ( _T("%%EndData") ); pDC->OutputNewLine (); pDC->OutputToken ( _T("XH") ); pDC->OutputNewLine (); pDC->OutputToken ( _T("%AI5_EndRaster") ); pDC->OutputNewLine (); // ChrisG (27/10/00). // This token was changed from 'N' close unfilled path, to 'F' close filled path. The // latter preserves information about the current line width, join type, fill colour, // etc... Whereas the 'N' token clears all this info, so any text objects or paths which // follow the bitmap were picking up colour and line info for an unfilled/unstroked path, // rather than the current attributes. pDC->OutputToken ( _T("F") ); pDC->OutputNewLine (); // The colours are no longer valid, as the bitmap definition has invalidated them. m_bValidPathAttrs = FALSE; m_bValidTextAttrs = FALSE; return TRUE; } /******************************************************************************************** > void AIEPSRenderRegion::OutputWindingRule () Author: Chris_Gallimore (Xara Group Ltd) Created: 19/12/00 Purpose: Output the winding rule used to AI EPS ********************************************************************************************/ void AIEPSRenderRegion::OutputWindingRule () { // Set up kernel DC up in an appropriate form. KernelDC *pDC = static_cast ( CCDC::ConvertFromNativeDC(RenderDC) ); if (RR_WINDINGRULE() == EvenOddWinding) { // Even-Odd winding (Camelot's default) pDC->OutputValue ((INT32)1); } else if (RR_WINDINGRULE() == NonZeroWinding) { // Non-Zero winding (EPS's default) pDC->OutputValue ((INT32)0); } else { // Positive or negative winding (only Even-Odd and Non-Zero are supported in AI7), // so assume that we're using camelot's default rule. pDC->OutputValue ((INT32)1); } pDC->OutputToken (_T("XR")); pDC->OutputNewLine (); } /******************************************************************************************** > void AIEPSRenderRegion::OutputLineWidth() Author: Chris_Gallimore (Xara Group Ltd) Created: 1/11/00 Purpose: Output the standard line width token and measure for AI EPS ********************************************************************************************/ void AIEPSRenderRegion::OutputLineWidth() { TCHAR buf [64]; // Set line width if (ExportingOnPath ()) { OverflowTextStartGap (); camSprintf (buf, _T("%.2f w\n"), ((double) RR_LINEWIDTH())/1000); OverflowTextWrite (buf); } EPSRenderRegion::OutputLineWidth (); } /******************************************************************************************** > void AIEPSRenderRegion::OutputJoinType() Author: Chris_Gallimore (Xara Group Ltd) Created: 1/11/00 Purpose: Output the standard AI EPS join type ********************************************************************************************/ void AIEPSRenderRegion::OutputJoinType() { TCHAR buf [64]; // Set line Join Type if (ExportingOnPath ()) { OverflowTextStartGap (); camSprintf (buf, _T("%d j\n"), (UINT32)RR_JOINTYPE()); OverflowTextWrite (buf); } EPSRenderRegion::OutputJoinType (); } /******************************************************************************************** > void AIEPSRenderRegion::OutputDashPattern() Author: Chris_Gallimore (Xara Group Ltd) Created: 1/11/00 Purpose: Outputs EPS dash pattern values. This function currently only exists so that extra handling can be easily achieved for Illustrator EPS export of dash patterns in the future (if need be). SeeAlso: EPSRenderRegion::OutputDashPattern (); EPSRenderRegion::OutputDashPatternInit(); ********************************************************************************************/ void AIEPSRenderRegion::OutputDashPattern() { EPSRenderRegion::OutputDashPattern (); } /******************************************************************************************** > void AIEPSRenderRegion::OutputStartCap() Author: Chris_Gallimore (Xara Group Ltd) Created: 1/11/00 Purpose: Output the start cap setting using the usual 'J' EPS token ********************************************************************************************/ void AIEPSRenderRegion::OutputStartCap() { TCHAR buf [64]; if (ExportingOnPath ()) { OverflowTextStartGap (); camSprintf (buf, _T("%d J\n"), (UINT32)RR_STARTCAP()); OverflowTextWrite (buf); } EPSRenderRegion::OutputStartCap (); } /******************************************************************************************** > void AIEPSRenderRegion::OutputMitreLimit() Author: Chris_Gallimore (Xara Group Ltd) Created: 1/11/00 Purpose: Output the current mitre limit value using the usual 'M' EPS token. Again, this is only here so that the AI export can be easily expanded or modified. ********************************************************************************************/ void AIEPSRenderRegion::OutputMitreLimit() { EPSRenderRegion::OutputMitreLimit (); } /******************************************************************************************** > void AIEPSRenderRegion::OutputStrokeColour() Author: Chris_Gallimore (Xara Group Ltd) Created: 1/11/00 Purpose: Output the current stroke colour. This function will use one of the following EPS tokens K - if not separating and colour is unnamed X - if not separating and colour is named G - if separating SeeAlso: AIEPSRenderRegion::OutputFillColour ********************************************************************************************/ void AIEPSRenderRegion::OutputStrokeColour() { // Write out the stroke colour EPSRenderRegion::OutputStrokeColour (); } /******************************************************************************************** > void AIEPSRenderRegion::OutputFontName() Author: Chris_Gallimore (Xara Group Ltd) Created: 1/11/00 Purpose: Output an Illustrator format fontname and pointsize token. The format is /_fontname[-Bold|-Italic|-BoldItalic] pointsize Tf. EncodeFontName will generate this format from an internal fontname. DocodeFontName will return an internal format fontname and flags where nec. ********************************************************************************************/ void AIEPSRenderRegion::OutputFontName() { if (ExportingOnPath ()) { TCHAR buffer [64]; OverflowTextStartGap (); String_64 FontName; String_64 EncodedFontName; String_64 Append(_T("/_")); // get information about the current font FONTMANAGER->GetFontName(RR_TXTFONTTYPEFACE(), FontName); // Graeme (31-3-00) - I've lifted this piece of code from the CamelotEPSRenderRegion. It // appears to map an existing, encoded font name onto its Postscript counterpart. FONTMANAGER->EncodeAndMapFontName(FontName, EncodedFontName, GetFontStyle()); EncodedFontName.Insert(Append,0); // Output the fontsize next double PointSize = ((double)RR_TXTFONTSIZE())/1000; // finally do output the font token camSprintf (buffer, _T("%s %.1f Tf\n"), (TCHAR *)EncodedFontName, PointSize); OverflowTextWrite (buffer); } EPSRenderRegion::OutputFontName (); } /******************************************************************************************** > void AIEPSRenderRegion::OutputTextRenderMode() Author: Chris_Gallimore (Xara Group Ltd) Created: 1/11/00 Purpose: Output the change in the text's Render Mode (Tr) - this happens when the text's fill or line colour changes to or from blank, see-through colour ********************************************************************************************/ void AIEPSRenderRegion::OutputTextRenderMode () { INT32 Style=0; if (ExportingOnPath ()) { OverflowTextStartGap (); // Is there a currently active fill colour? if (! (RR_FILLCOLOUR().IsTransparent()) ) Style+=1; // Is there a currently active line colour? if (! (RR_STROKECOLOUR().IsTransparent()) ) Style+=2; switch (Style) { case 0: OverflowTextWrite (_T("3 Tr\n")); // Invisible break; case 1: OverflowTextWrite (_T("0 Tr\n")); // filled only break; case 2: OverflowTextWrite (_T("1 Tr\n")); // stroked only break; case 3: OverflowTextWrite (_T("2 Tr")); // filled and stroked break; } } EPSRenderRegion::OutputTextRenderMode (); } /******************************************************************************************** > void AIEPSRenderRegion::OutputTextAspectRatio() Author: Chris_Gallimore (Xara Group Ltd) Created: 1/11/00 Purpose: Output the change in the text's aspect ratio (Tz) ********************************************************************************************/ void AIEPSRenderRegion::OutputTextAspectRatio () { TCHAR buf [64]; if (ExportingOnPath ()) { OverflowTextStartGap (); camSprintf (buf, _T("%.0f Tz\n"), (RR_TXTASPECTRATIO().MakeDouble()*100.0)); // convert from ratio to % OverflowTextWrite (buf); } EPSRenderRegion::OutputTextAspectRatio (); } /******************************************************************************************** > void AIEPSRenderRegion::OutputTextTracking() Author: Chris_Gallimore (Xara Group Ltd) Created: 1/11/00 Purpose: Output the change in the tracking (Tt) ********************************************************************************************/ void AIEPSRenderRegion::OutputTextTracking () { // Must be output in 1/1000 of an em. // 1 em = point size of font. // Tracking internally =millipoints. TCHAR buf [64]; if (ExportingOnPath ()) { OverflowTextStartGap (); camSprintf (buf, _T("%d Tt\n"), RR_TXTTRACKING()); OverflowTextWrite (buf); } EPSRenderRegion::OutputTextTracking (); } /******************************************************************************************** > void EPSRenderRegion::OutputTextJustification() Author: Chris_Gallimore (Xara Group Ltd) Created: 1/11/00 Purpose: Output the change in the text justification (Ta) ********************************************************************************************/ void AIEPSRenderRegion::OutputTextJustification () { TCHAR buf [64]; if (ExportingOnPath ()) { OverflowTextStartGap (); switch (RR_TXTJUSTIFICATION()) { case JLEFT: camSprintf (buf, _T("%d Ta\n"), ((INT32)0)); break; case JRIGHT: camSprintf (buf, _T("%d Ta\n"), ((INT32)2)); break; case JCENTRE: camSprintf (buf, _T("%d Ta\n"), ((INT32)1)); break; case JFULL: camSprintf (buf, _T("%d Ta\n"), ((INT32)3)); break; } OverflowTextWrite (buf); } EPSRenderRegion::OutputTextJustification (); } /******************************************************************************************** > void AIEPSRenderRegion::OutputTextLineSpacing() Author: Chris_Gallimore (Xara Group Ltd) Created: 1/11/00 Purpose: Output the change in the line spacing (Tl) ********************************************************************************************/ void AIEPSRenderRegion::OutputTextLineSpacing () { if (ExportingOnPath ()) { TCHAR buf [64]; OverflowTextStartGap (); // Output line spacing in points. // format - paraspace linespace Tl double ptLineSpace; double ptParaSpace=0; TxtLineSpaceAttribute* pLineSpace = (TxtLineSpaceAttribute*)(CurrentAttrs[ATTR_TXTLINESPACE].pAttr); // There are some rules for reading the linespacing value which I shall divulge // If IsARation is true then use the proportinal linespacing value. // else use the absolute linespacing // However if the absolute linespacing is zero, then we MUST use the proportional // linespacing. Eeek! // ie it is an error if (absolute==0 && !IsARatio()) which we will check for here double FontSize = (double)RR_TXTFONTSIZE(); // in millipoints 12pt = 12000 mp double absLineSpace = (double)pLineSpace->Value; double proLineSpace = (pLineSpace->Ratio).MakeDouble(); BOOL Proportional = pLineSpace->IsARatio(); if (!Proportional && absLineSpace==0) { ERROR3("Absolute line spacing is zero yet IsARatio() is FALSE, in GetValidTextAttributes()"); // Make sure we use the proportional value no matter what. Proportional=TRUE; } if (Proportional) ptLineSpace = FontSize*proLineSpace/1000; else ptLineSpace = absLineSpace/1000; camSprintf (buf, _T("%.0f %.0f Tl\n"), ptLineSpace, ptParaSpace); OverflowTextWrite (buf); } EPSRenderRegion::OutputTextLineSpacing (); } /******************************************************************************************** > void AIEPSRenderRegion::OutputTextBaselineShift() Author: Chris_Gallimore (Xara Group Ltd) Created: 1/11/00 Purpose: Output the change in Baseline shift (Ts) ********************************************************************************************/ void AIEPSRenderRegion::OutputTextBaselineShift () { TCHAR buf [64]; // Output baseline shift in points // format = rise Ts if (ExportingOnPath ()) { OverflowTextStartGap (); double BaseLine = ((double)RR_TXTBASELINE())/1000; camSprintf (buf, _T("%.1f Ts\n"), BaseLine); OverflowTextWrite (buf); } EPSRenderRegion::OutputTextBaselineShift (); } /******************************************************************************************** > void AIEPSRenderRegion::OutputTextSubSuperScript() Author: Chris_Gallimore (Xara Group Ltd) Created: 1/11/00 Purpose: Output the change in the Sub/Super script attribute (Ts) ********************************************************************************************/ void AIEPSRenderRegion::OutputTextSubSuperScript () { TCHAR buf [64]; if (ExportingOnPath ()) { OverflowTextStartGap (); // Output sub/superscript // in millipoints 12pt = 12000 mp double FontSize = ((double)RR_TXTFONTSIZE())/1000; TxtScriptAttribute* pScript = RR_TXTSCRIPT(); double offset = (pScript->Offset).MakeDouble(); double size = (pScript->Size).MakeDouble(); OverflowTextWrite (_T("%%XSScript\n")); double rise = FontSize*offset; camSprintf (buf, _T("%.1f Ts\n"), rise); OverflowTextWrite (buf); double ptsize = FontSize*size; String_64 MappedFont; String_64 Append(_T("/_")); String_64 FontName; FONTMANAGER->GetFontName(RR_TXTFONTTYPEFACE(), FontName); // Graeme (31-3-00) - Map the encoded name onto the PS font name. FONTMANAGER->EncodeAndMapFontName(FontName,MappedFont,GetFontStyle()); // Graeme (14-6-00) - I should add ascent and descent values, but Camelot doesn't // seem to store them anywhere. MappedFont.Insert(Append,0); camSprintf (buf, _T("%s %.1f Tf\n"),(TCHAR *)MappedFont, ptsize); // Output the ascent. // Output the descent. OverflowTextWrite (buf); } EPSRenderRegion::OutputTextSubSuperScript (); } /*------------*/ //---------------------------------------------------------------- // // The following functions are all overflow text functions, // which write an additional text line, terminated with the // token (TX), to the EPS file, the Output functions declared // earlier should call these functions to add any attribute // changes, so that the two text blocks remain consistent. // //---------------------------------------------------------------- /******************************************************************************************** > void AIEPSRenderRegion::ExportingOnPath() Author: Chris_Gallimore (Xara Group Ltd) Created: 2/11/00 Returns: TRUE - yes, we are exporting on a path FALSE - no, we aren't exporting on a path Purpose: To determine whether this text is being exported on a path. SeeAlso: ********************************************************************************************/ BOOL AIEPSRenderRegion::ExportingOnPath () { if (m_fpOverflowText) return TRUE; else return FALSE; } /******************************************************************************************** > void AIEPSRenderRegion::OverflowTextStart() Author: Chris_Gallimore (Xara Group Ltd) Created: 1/11/00 Purpose: Start the overflow text block. SeeAlso: ********************************************************************************************/ void AIEPSRenderRegion::OverflowTextStart () { // open overflow file m_fpOverflowText = fopen ("OverFlow.txt", "w"); // write out start info - NOTE: we're currently in a gap from writing the initial // attributes, so we're going to have to close it. m_bInTextGap = TRUE; } /******************************************************************************************** > void AIEPSRenderRegion::OverflowTextFinish() Author: Chris_Gallimore (Xara Group Ltd) Created: 1/11/00 Purpose: Finish the overflow text block and write both blocks to the actual EPS file. SeeAlso: ********************************************************************************************/ void AIEPSRenderRegion::OverflowTextFinish () { ASSERT (m_fpOverflowText); // write out final information. OverflowTextStartGap (); // close files. fclose (m_fpOverflowText); m_fpOverflowText = NULL; // copy info into actual EPS file. char cbuffer [1025]; TCHAR buffer [1025]; KernelDC *pDC = (KernelDC*)CCDC::ConvertFromNativeDC(RenderDC); FILE * fp; // Write out the Extra info. fp = fopen ("OverFlow.txt", "r+t"); while (fgets (cbuffer, 1024, fp) != NULL) { char c; INT32 i=0; cbuffer[1024]=0; do { c=cbuffer[i]; buffer[i]=c; // 1:1 CHAR->TCHAR conversion i++; } while (c); // deal with newlines. pDC->OutputToken (buffer); pDC->OutputNewLine (); } fclose (fp); // clean up temp. files remove ("OverFlow.txt"); } /******************************************************************************************** > void AIEPSRenderRegion::OverflowTextStartGap() Author: Chris_Gallimore (Xara Group Ltd) Created: 2/11/00 Purpose: Starts a new gap in the Overflow text, so that attributes can be written into the overflow blocks (the TX ones) SeeAlso: AIEPSRenderRegion::OverflowTextFinishGap () ********************************************************************************************/ void AIEPSRenderRegion::OverflowTextStartGap () { ASSERT (ExportingOnPath ()); // if we're not in a gap, finish writing the old TX block and start a new gap, if (m_bInTextGap == FALSE) { OverflowTextWrite (_T(") TX\n")); m_bInTextGap = TRUE; } } /******************************************************************************************** > void AIEPSRenderRegion::OverflowTextFinishGap() Author: Chris_Gallimore (Xara Group Ltd) Created: 2/11/00 Purpose: Closes a gap for writing attributes in the overflow text, so that a new line of overflow text can be started. SeeAlso: AIEPSRenderRegion::OverflowTextStartGap (); ********************************************************************************************/ void AIEPSRenderRegion::OverflowTextFinishGap () { ASSERT (ExportingOnPath ()); // if we're in a text gap (for changing the attributes), start writing a new TX block. if (m_bInTextGap == TRUE) { OverflowTextWrite (_T("(")); m_bInTextGap = FALSE; } } /******************************************************************************************** > void AIEPSRenderRegion::OverflowTextWrite() Author: Chris_Gallimore (Xara Group Ltd) Created: 1/11/00 Purpose: Write some text to the overflow body file. This will store all the text and modified attributes for the TX text block. SeeAlso: ********************************************************************************************/ void AIEPSRenderRegion::OverflowTextWrite (TCHAR * text) { ASSERT (ExportingOnPath ()); while (*text) { fprintf (m_fpOverflowText, "%c", *text++); } } /******************************************************************************************** > BOOL AIEPSRenderRegion::OverflowTextWriteSingleColour (UINT32 n) Author: Chris_Gallimore (Xara Group Ltd) Created: 3/11/00 Inputs: n - the colour value (range 0-255) to write to the EPS file. Purpose: Output a colour value to the overflow text file. A 'single colour' is a value as used in the Camelot 'Colour' class, i.e. in the range 0 to 255. This range is converted to the range 0.0 to 1.0, and output to the file. SeeAlso: KernelDC::OutputColourValue ********************************************************************************************/ void AIEPSRenderRegion::OverflowTextWriteSingleColour (UINT32 n) { // Output to string TCHAR buf[20]; // Convert to points, getting integer and fractional parts camSprintf (buf, _T("%.2f "), ((double) n) / 255); OverflowTextWrite (buf); } /******************************************************************************************** > BOOL AIEPSRenderRegion::OverflowTextWriteColour(PColourCMYK *pCol) Author: Chris_Gallimore (Xara Group Ltd) Created: 3/11/00 Inputs: Col - the CMYK colour values to be output. Purpose: Output a colour, as expressed by the Camelot 'Colour' class. This function takes the CMYK variation, is this is most appropriate to EPS files. Camelot colour values are converted from the 0-255 range to the 0.0-1.0 range before being output. e.g. MonoOn PColourCMYK Col = { 255, 255, 128, 0 }; pDC->OutputColour(&Col); MonoOff will give the following output: MonoOn 1.0 1.0 0.5 0.0 MonoOff SeeAlso: KernelDC::OutputColourValue Errors: Disk/file error => ERROR1 ********************************************************************************************/ void AIEPSRenderRegion::OverflowTextWriteColour (PColourCMYK *pCol) { // Write each of the colour values. OverflowTextWriteSingleColour (pCol->Cyan); OverflowTextWriteSingleColour (pCol->Magenta); OverflowTextWriteSingleColour (pCol->Yellow); OverflowTextWriteSingleColour (pCol->Key); } /******************************************************************************************** > void AIEPSRenderRegion::OverflowTextWriteNamedColour (DocColour *pCol, ColourContext* pContext = NULL) Author: Tim_Browse (Xara Group Ltd) Created: 09/08/94 Inputs: pCol - the colour to output. pContext - the context to use to convert the colour before output. if this is NULL the default CMYK context will be used. Returns: TRUE if the data was written ok; FALSE if not => ERROR1 Purpose: Similar to OutputColour(), except it outputs the colour name and tint of the colour as well. If pCol does not reference an indexed colour, then the name "NoName" is used. SeeAlso: KernelDC::OutputColour Errors: Disk/file error => ERROR1 ********************************************************************************************/ void AIEPSRenderRegion::OverflowTextWriteNamedColour(DocColour *pCol, ColourContext* pContext) { // Get CMYK version of this colour. PColourCMYK CMYK; pCol->GetCMYKValue(pContext, &CMYK); // Output CMYK version OverflowTextWriteColour (&CMYK); // Get the indexed colour from the DocColour. IndexedColour *pIndCol = pCol->FindParentIndexedColour(); // Cope with the unexpected! // ENSURE(pIndCol != NULL, "Named colour has no index colour!"); if (pIndCol == NULL) { if (pCol->IsTransparent()) { // This is a 'no colour' type colour, so output a zero-length colour name, // as this is the only way we can handle this at the moment. return; } else { // Otherwise make up a colour name (see epsfiltr.h). OverflowTextWrite (_T("(")); OverflowTextWrite (ImmediateColourFudgeyBodgeName); OverflowTextWrite (_T(")")); } } else { // Got an indexed colour - output its name // (Pass in TRUE to get a unique-identifier for local colours rather than "Local colour") String_64 *ColName = pIndCol->GetName(TRUE); OverflowTextWrite (_T("(")); OverflowTextWrite ((TCHAR *) (*ColName)); OverflowTextWrite (_T(")")); } // Always tint 0 OverflowTextWrite (_T(" 0 ")); } /******************************************************************************************** > void AIEPSRenderRegion::OverflowTextWriteColourName (DocColour *pCol) Author: Chris_Gallimore (Xara Group Ltd) Created: 18/12/00 Inputs: pCol - the colour to output. Purpose: Outputs a named colour's name as a string. If pCol does not reference an indexed colour, then the name "NoName" is used. This was seperated from OutputNamedColour so that the DeviceContext will not need to worry about which ColourModel to use (CMYK, RGB, etc...), as this will be determined by the RenderRegion. SeeAlso: KernelDC::OutputNamedColour Errors: Disk/file error => ERROR1 ********************************************************************************************/ void AIEPSRenderRegion::OverflowTextWriteColourName (DocColour *pCol) { // Get the indexed colour from the DocColour. IndexedColour *pIndCol = pCol->FindParentIndexedColour(); if (pIndCol == NULL) { if (pCol->IsTransparent()) { // This is a 'no colour' type colour, so output a zero-length colour name, // as this is the only way we can handle this at the moment. OverflowTextWrite (_T("()")); } else { // Otherwise make up a colour name (see epsfiltr.h). OverflowTextWrite (_T("(")); OverflowTextWrite (ImmediateColourFudgeyBodgeName); OverflowTextWrite (_T(")")); } } else { // Got an indexed colour - output its name // (Pass in TRUE to get a unique-identifier for local colours rather than "Local colour") String_64 *ColName = pIndCol->GetName(TRUE); OverflowTextWrite (_T("(")); OverflowTextWrite ((TCHAR *) (*ColName)); OverflowTextWrite (_T(")")); } } /******************************************************************************************** > virtual void AIEPSRenderRegion::SetClipRegion(ClipRegionAttribute* pClipAttr, BOOL Temp) Author: Chris_Gallimore (Xara Group Ltd) Created: 8th November 2000 Inputs: pClipAttr ptr to the new ClipRegionAttribute to set as current in our RR. Temp whether the attr is temporary or not. Outputs: Postscript's context is saved and the commands are output to intersect its current clipping path with that defined by the ClipAttr. Also, the RR's current attr context is updated to use this new ClipAttr. Purpose: The Adobe Illustrator version of Karim's EPSRenderRegion::SetClipRegion, This was needed as Illustrator uses non-standard EPS commands Shrink the RR's clipping region to the region defined by pClipAttr. We do this by: 1. Recording a save-state command. 2. Exporting a description of the clipping path in pClipAttr. 3. Recording a 'clip-to-last-path' command. 4. Recording a 'start-new-path' command, as we don't want our clip-path to get stroked or rendered in any way. Notes: We mustn't record clipping commands when rendering the default attribute. Therefore this method does *nothing whatsoever* unless pClipAttr's ptr to its clipping-path is non-NULL. This ptr is NULL in the default attr. See also: RenderRegion::SetClipRegion(), EPSRenderRegion::SetClipRegion() ********************************************************************************************/ void AIEPSRenderRegion::SetClipRegion(ClipRegionAttribute* pClipAttr, BOOL Temp) { if (pClipAttr->GetClipPath() != NULL) { // Update the RR's table of current attributes. RenderRegion::SetClipRegion (pClipAttr, Temp); // Write out the clipping path as a mask. WriteMask(pClipAttr->GetClipPath(), pClipAttr->IsResponsibleForGrouping ()); } } /******************************************************************************************** > virtual void AIEPSRenderRegion::RestoreClipRegion(ClipRegionAttribute* pClipAttr, BOOL Temp) Author: Chris_Gallimore (Xara Group Ltd) Created: 8th November 2000 Inputs: pClipAttr ptr to the ClipRegionAttribute to restore over the current one. Temp whether the attr is temporary or not. Outputs: Postscript is told to restore its last saved context. Purpose: Adobe Illustrator version of Karim's EPSRenderRegion::RestoreClipRegion. See SetClipRegion for why this was necessary. Restore the RR's clipping region. We do this by recording a restore-state command. Errors: ERROR3 if the current ClipRegionAttribute holds a NULL clip-path ptr. This state of affairs should not happen. You can't set a ClipRegionAttribute with a NULL clip-path ptr, so the only such attr is the default one, and you should never be restoring over _that_. See also: RenderRegion::RestoreClipRegion(), EPSRenderRegion::RestoreClipRegion() ********************************************************************************************/ void AIEPSRenderRegion::RestoreClipRegion(ClipRegionAttribute* pClipAttr, BOOL Temp) { // we should never be entered when the current clipattr has a NULL path, as what this // means is that either somebody somehow did a SetClipRegion() with such an attr, or // somebody is trying to Restore() over the default ClipRegionAttribute (which holds a // NULL path ptr). Neither of these actions should occur. if (RR_CLIPREGION()->GetClipPath() == NULL) { ERROR3("EPSRenderRegion::RestoreClipRegion; Current ClipRegionAttribute has a NULL path"); } else { // Update the RR's table of current attributes. RenderRegion::RestoreClipRegion(pClipAttr, Temp); } } /******************************************************************************************** > virtual void AIEPSRenderRegion::SetTextAsShapes (BOOL shapes) Author: Chris_Gallimore (Xara Group Ltd) Created: 8th November 2000 Inputs: shapes - whether to export text as shapes (TRUE) or characters (FALSE) Purpose: Sets the export TextAsShapes flag See also: AIEPSRenderRegion::RenderChar(), TextStory::PreExportRender, TextStory::ExportRender ********************************************************************************************/ void AIEPSRenderRegion::SetTextAsShapes (BOOL shapes) { m_bTextAsShapes = shapes; } /******************************************************************************************** > virtual BOOL AIEPSRenderRegion::GetTextAsShapes () Author: Chris_Gallimore (Xara Group Ltd) Created: 8th November 2000 Returns: BOOL - whether text is exported as shapes (TRUE) or characters (FALSE) Purpose: Sets the export TextAsShapes flag See also: AIEPSRenderRegion::GetTextAsShapes ********************************************************************************************/ BOOL AIEPSRenderRegion::GetTextAsShapes () { return m_bTextAsShapes; } /******************************************************************************************** > virtual BOOL AIEPSRenderRegion::WriteNewLine () Author: Chris_Gallimore (Xara Group Ltd) Created: 9th November 2000 Returns: BOOL - whether the a new line was written successfully. Purpose: Writes a new line to the output of text if text is exported as characters, does nothing if it's exported as shapes. See also: EPSRenderRegion::WriteNewLine ********************************************************************************************/ BOOL AIEPSRenderRegion::WriteNewLine ( void ) { BOOL success; // don't write any newlines if we're exporting text as shapes (cos' it could stuff up // the eps). if (GetTextAsShapes () == TRUE) { success = TRUE; } else { success = EPSRenderRegion::WriteNewLine (); } return success; } /******************************************************************************************** > virtual void AIEPSRenderRegion::OutputFillRGBColour () Author: Chris_Gallimore (Xara Group Ltd) Created: 18/12/00 Purpose: Writes additional info to the file for RGB fill colours See also: EPSRenderRegion::OutputFillRGBColour ********************************************************************************************/ void AIEPSRenderRegion::OutputFillRGBColour () { // NOTE: We need to export any changes to the overflow text here, // as it's the last place we can catch the colour change. if (ExportingOnPath ()) { OverflowTextStartGap (); // Colour values. INT32 red; INT32 green; INT32 blue; PColourCMYK CMYK; // Assume no local context at present - used as a check to make sure that we // aren't exporting as seperations. ColourContext* pContext; ColourPlate* pSeparation; GetOutputColourPlate (COLOURMODEL_CMYK, &pContext, &pSeparation); if (pSeparation==NULL) { // As expected, we ain't using seperations, so we can output as RGB. // Get the current line colour in RGB values and print them out. RR_FILLCOLOUR().GetRGBValue (&red, &green, &blue); OverflowTextWriteSingleColour (red); OverflowTextWriteSingleColour (green); OverflowTextWriteSingleColour (blue); if (RR_FILLCOLOUR().FindParentIndexedColour() == NULL) { // Unnamed colour - just add 'Xa' token OverflowTextWrite (_T("Xa\n")); } else { // Named colour - add Name, tint value, RGB flag and 'Xx' token OverflowTextWriteColourName (&(RR_FILLCOLOUR())); OverflowTextWrite (_T(" 0 1 Xx\n")); } } else { // Since we are seperating the colours, then use CMYK mode. // Assume unnamed colour as 'g' grey fill operator does // not allow for custom greys. if (pSeparation->IsMonochrome()) { RR_FILLCOLOUR().GetCMYKValue(pContext, &CMYK); BYTE c = 0xFF - CMYK.Key; OverflowTextWriteSingleColour (c); OverflowTextWrite (_T("g\n")); } else { RR_FILLCOLOUR().GetCMYKValue(pContext, &CMYK); OverflowTextWriteColour (&CMYK); OverflowTextWrite (_T("x\n")); } } } // Write out the usual EPS colour tokens. EPSRenderRegion::OutputFillRGBColour (); } /******************************************************************************************** > virtual void AIEPSRenderRegion::OutputFillCMYKColour () Author: Chris_Gallimore (Xara Group Ltd) Created: 18/12/00 Purpose: Writes additional info to the file for CMYK fill colours See also: EPSRenderRegion::OutputFillCMYKColour ********************************************************************************************/ void AIEPSRenderRegion::OutputFillCMYKColour () { // NOTE: We need to export any changes to the overflow text here, // as it's the last place we can catch the colour change. if (ExportingOnPath ()) { PColourCMYK CMYK; OverflowTextStartGap (); // Assume no local context at present ColourContext* pContext; ColourPlate* pSeparation; GetOutputColourPlate(COLOURMODEL_CMYK, &pContext, &pSeparation); if (pSeparation==NULL) { // Get the current line colour in CMYK values, unless it's transparent // Print out colour component values. if (RR_FILLCOLOUR().FindParentIndexedColour() == NULL) { // Unnamed colour // retrieve the colour value RR_FILLCOLOUR().GetCMYKValue(pContext, &CMYK); OverflowTextWriteColour (&CMYK); OverflowTextWrite (_T("k\n")); } else { // Named colour OverflowTextWriteNamedColour (&(RR_FILLCOLOUR()), pContext); OverflowTextWrite (_T("x\n")); } } else { // Assume unnamed colour as 'g' grey fill operator does // not allow for custom greys. if (pSeparation->IsMonochrome()) { RR_FILLCOLOUR().GetCMYKValue(pContext, &CMYK); BYTE c = 0xFF - CMYK.Key; OverflowTextWriteSingleColour (c); OverflowTextWrite (_T("g\n")); } else { RR_FILLCOLOUR().GetCMYKValue(pContext, &CMYK); OverflowTextWriteColour (&CMYK); OverflowTextWrite (_T("x\n")); } } } // Call the base class to write the ordinary info out EPSRenderRegion::OutputFillCMYKColour (); } /******************************************************************************************** > virtual void AIEPSRenderRegion::OutputStrokeRGBColour () Author: Chris_Gallimore (Xara Group Ltd) Created: 18/12/00 Purpose: Writes additional info to the file for RGB stroke colours See also: EPSRenderRegion::OutputStrokeRGBColour ********************************************************************************************/ void AIEPSRenderRegion::OutputStrokeRGBColour () { // NOTE: We need to export any changes to the overflow text here, // as it's the last place we can catch the colour change. if (ExportingOnPath ()) { OverflowTextStartGap (); // Colour values. INT32 red; INT32 green; INT32 blue; PColourCMYK CMYK; // Assume no local context at present - used as a check to make sure that we // aren't exporting as seperations. ColourContext* pContext; ColourPlate* pSeparation; GetOutputColourPlate (COLOURMODEL_CMYK, &pContext, &pSeparation); if (pSeparation==NULL) { // As expected, we ain't using seperations, so we can output as RGB. // Get the current line colour in RGB values and print them out. RR_STROKECOLOUR().GetRGBValue (&red, &green, &blue); OverflowTextWriteSingleColour (red); OverflowTextWriteSingleColour (green); OverflowTextWriteSingleColour (blue); if (RR_STROKECOLOUR().FindParentIndexedColour() == NULL) { // Unnamed colour - just add 'XA' token OverflowTextWrite (_T("XA\n")); } else { // Named colour - add Name, tint value, RGB flag and 'XX' token OverflowTextWriteColourName (&(RR_STROKECOLOUR())); OverflowTextWrite (_T(" 0 1 XX\n")); } } else { // Since we are seperating the colours, then use CMYK mode. // Assume unnamed colour as 'g' grey fill operator does // not allow for custom greys. if (pSeparation->IsMonochrome()) { RR_STROKECOLOUR().GetCMYKValue(pContext, &CMYK); BYTE c = 0xFF - CMYK.Key; OverflowTextWriteSingleColour (c); OverflowTextWrite (_T("G\n")); } else { RR_STROKECOLOUR().GetCMYKValue(pContext, &CMYK); OverflowTextWriteColour (&CMYK); OverflowTextWrite (_T("X\n")); } } } // Call the base class to process the colour normally (i.e. to the EPS file itself) EPSRenderRegion::OutputStrokeRGBColour (); } /******************************************************************************************** > virtual void AIEPSRenderRegion::OutputStrokeCMYKColour () Author: Chris_Gallimore (Xara Group Ltd) Created: 18/12/00 Purpose: Writes additional info to the file for CMYK fill colours See also: EPSRenderRegion::OutputFillCMYKColour ********************************************************************************************/ void AIEPSRenderRegion::OutputStrokeCMYKColour () { // Write out the colour to the overflow file if we're doing text on a path. if (ExportingOnPath ()) { PColourCMYK CMYK; OverflowTextStartGap (); // Assume no local context at present ColourContext* pContext; ColourPlate* pSeparation; GetOutputColourPlate(COLOURMODEL_CMYK, &pContext, &pSeparation); if (pSeparation==NULL) { // Get the current line colour in CMYK values, unless it's transparent // Print out colour component values. if (RR_STROKECOLOUR().FindParentIndexedColour() == NULL) { // Unnamed colour // retrieve the colour value RR_STROKECOLOUR().GetCMYKValue(pContext, &CMYK); OverflowTextWriteColour (&CMYK); OverflowTextWrite (_T("K\n")); } else { // Named colour OverflowTextWriteNamedColour (&(RR_STROKECOLOUR()), pContext); OverflowTextWrite (_T("X\n")); } } else { // Assume unnamed colour as 'g' grey fill operator does // not allow for custom greys. if (pSeparation->IsMonochrome()) { RR_STROKECOLOUR().GetCMYKValue(pContext, &CMYK); BYTE c = 0xFF - CMYK.Key; OverflowTextWriteSingleColour (c); OverflowTextWrite (_T("G\n")); } else { RR_STROKECOLOUR().GetCMYKValue(pContext, &CMYK); OverflowTextWriteColour (&CMYK); OverflowTextWrite (_T("X\n")); } } } // Call the base class to write out the ordinary information EPSRenderRegion::OutputStrokeCMYKColour (); } /******************************************************************************************** > virtual BOOL AIEPSRenderRegion::IsGradientFillValidForExport () Author: Chris_Gallimore (Xara Group Ltd) Created: 15/01/01 Purpose: Determines whether a particular fill can be used through the AI export See also: AIEPSRenderRegion::WriteGradientFills, AIEPSRenderRegion::WriteGradientUsage ********************************************************************************************/ BOOL AIEPSRenderRegion::IsGradientFillValidForExport (FillGeometryAttribute * pFill) { BOOL acceptable = FALSE; if ( pFill->IsAColourFill () && // Is it a colour fill? pFill->IsAGradFill () && // Is it a graduated fill? ! (pFill->IsAFractalFill ()) && // Is it not a fractal (clouds) fill? ! (pFill->IsANoiseFill ()) && // Is it not a fractal (plasma) fill? ! (pFill->IsABitmapFill ()) ) // Is it not a bitmap fill? { acceptable = TRUE; } else { acceptable = FALSE; } return acceptable; } /******************************************************************************************** > virtual BOOL AIEPSRenderRegion::IncludeGradientFill () Author: Chris_Gallimore (Xara Group Ltd) Created: 5/2/2001 Purpose: Stores information about a fill so that the same fill isn't declared multiple times. See also: AIEPSRenderRegion::WriteGradientFills, AIEPSRenderRegion::WriteGradientUsage, AIEPSRenderRegion::FindGradientInCache ********************************************************************************************/ void AIEPSRenderRegion::IncludeGradientFill (FillGeometryAttribute * pFill, EFFECTTYPE effect) { ERROR3IF ((pFill == NULL), "AIEPSRenderRegion::IncludeGradientFill - Attempted to include NULL fill"); ListFill * pNewList = NULL; FillGeometryAttribute * pNewFill = NULL; // don't try and store the fill if it's invalid. if (pFill != NULL) { // Create a new FillGeometryAttribute, of the same type as the one passed in. // NOTE: Four colour fills are derived from three colour fills, so they return // TRUE for IsAThreeColFill. This means that we need to process four colour // fills before we process three colour fills, otherwise all four colour fills // will become represented by three colour ones and the copy won't match the // original. if (pFill->IsALinearFill ()) pNewFill = new LinearFillAttribute; else if (pFill->IsARadialFill ()) pNewFill = new RadialFillAttribute; else if (pFill->IsAConicalFill ()) pNewFill = new ConicalFillAttribute; else if (pFill->IsASquareFill ()) pNewFill = new SquareFillAttribute; else if (pFill->IsAFourColFill ()) pNewFill = new FourColFillAttribute; else if (pFill->IsAThreeColFill ()) pNewFill = new ThreeColFillAttribute; else if (pFill->IsAFractalFill ()) pNewFill = new FractalFillAttribute; else if (pFill->IsANoiseFill ()) pNewFill = new NoiseFillAttribute; else if (pFill->IsABitmapFill ()) pNewFill = new BitmapFillAttribute; else { pNewFill = NULL; ERROR3 ("AIEPSRenderRegion::IncludeGradientFill - Unrecognised fill included"); } // Copy fill data if the the fill is recognised. There's no point in doing this if // we didn't recognise the fill, as the copy is bound to be different to the original. if (pNewFill != NULL) { // Copy all the relevant info. *(pNewFill) = *pFill; ERROR3IF ( !(*pFill == *pNewFill), "AIEPSRenderRegion::IncludeGradientFill - Copied fill doesn't match original"); // New Fill is built, so add it into either the radial or linear list. if (pFill->IsARadialFill () || pFill->IsASquareFill ()) { pNewList = new ListFill (pNewFill, effect, m_pRadialGradList); m_pRadialGradList = pNewList; } else { pNewList = new ListFill (pNewFill, effect, m_pLinearGradList); m_pLinearGradList = pNewList; } } } } /******************************************************************************************** > virtual void AIEPSRenderRegion::ClearGradientCache () Author: Chris_Gallimore (Xara Group Ltd) Created: 30/3/2001 Purpose: Deletes the list of stored gradient fills, that was built up using IncludeGradientFill See also: AIEPSRenderRegion::IncludeGradientFill, AIEPSRenderRegion::FindGradientInCache ********************************************************************************************/ void AIEPSRenderRegion::ClearGradientCache () { ListFill * pList = NULL; // only clear it if there is a list to clear if (m_pLinearGradList) { // Cycle through the linear list deleting each item in turn. while (m_pLinearGradList != NULL) { pList = m_pLinearGradList->pNext; delete m_pLinearGradList; m_pLinearGradList = pList; } } if (m_pRadialGradList) { // Cycle through the radial list deleting each item in turn. while (m_pRadialGradList != NULL) { pList = m_pRadialGradList->pNext; delete m_pRadialGradList; m_pRadialGradList = pList; } } } /******************************************************************************************** > virtual INT32 AIEPSRenderRegion::FindGradientInCache () Author: Chris_Gallimore (Xara Group Ltd) Created: 30/3/2001 Returns the identifier of the found gradient fill, or 0 if it wasn't found Purpose: Deletes the list of stored gradient fills, that was built up using IncludeGradientFill See also: AIEPSRenderRegion::IncludeGradientFill, ********************************************************************************************/ INT32 AIEPSRenderRegion::FindGradientInCache (FillGeometryAttribute * pFill, EFFECTTYPE effect) { INT32 id = 0; ListFill * pList = NULL; FillGeometryAttribute * pStoredFill = NULL; double fillBias = 0; CProfileBiasGain fillProfile = pFill->GetProfile (); fillBias = (double) fillProfile.GetBias (); // work out which list we want to search in. if (pFill->IsARadialFill () || pFill->IsASquareFill ()) pList = m_pRadialGradList; else pList = m_pLinearGradList; // Cycle through all the fills until we find the one we want, or until we run out of fills while ((pList != NULL) && (id == 0)) { pStoredFill = pList->pFill; // Because the start and end points for an Illustrator fill are not included in the // cache, we don't need to worry about them being the same. // // So, we set all the start and end points to be the same (since they would cause two // identical fills in different places, or of different lengths to be considered as // different otherwise). pStoredFill->SetStartPoint (pFill->GetStartPoint ()); pStoredFill->SetEndPoint (pFill->GetEndPoint ()); pStoredFill->SetEndPoint2 (pFill->GetEndPoint2 ()); pStoredFill->SetEndPoint3 (pFill->GetEndPoint3 ()); // Now that the fill coords are the same, test to see whether they're // the same fill, or not. if (*pStoredFill == *pFill) { double storeBias = 0; CProfileBiasGain storeProfile = pStoredFill->GetProfile (); storeBias = (double) storeProfile.GetBias (); if ((storeBias == fillBias) && (pList->effect == effect)) id = pList->id; } // Check the next fill pList = pList->pNext; } return id; } /******************************************************************************************** > virtual void AIEPSRenderRegion::BuildGradientCache (Node * pStartNode) Author: Chris_Gallimore (Xara Group Ltd) Created: 20/3/2001 Inputs: pStartNode - the node to start looking from. Purpose: Builds up a 'cache' of gradient fills. See also: AIEPSRenderRegion::WriteGradientFills, AIEPSRenderRegion::WriteGradientDefinitions ********************************************************************************************/ void AIEPSRenderRegion::BuildGradientCache (Node * pStartNode) { // Node * pChild = NULL; // Set up the gradient searching system AIEPSGradientScanRenderRegion scanRR (this); scanRR.AttachDevice (DocView::GetSelected (), RenderDC, RenderView->GetDoc()->FindFirstSpread ()); scanRR.InitDevice (); // Although the counting system does return the number of fills found, it can be got from // the scanner just as easily. BuildGradientCacheUsingScanner (pStartNode, &scanRR); } /******************************************************************************************** > virtual void AIEPSRenderRegion::BuildGradientCacheUsingScanner (Node * pNode, AIEPSGradientScanRenderRegion * pScan) Author: Chris_Gallimore (Xara Group Ltd) Created: 20/3/2001 Inputs: pNode - the node to start looking from. pScan - the scanner to use . Purpose: Builds up the gradient cache by scanning through each node under pNode for new fills See also: AIEPSRenderRegion::WriteGradientFills, AIEPSRenderRegion::WriteGradientDefinitions ********************************************************************************************/ void AIEPSRenderRegion::BuildGradientCacheUsingScanner (Node * pNode, AIEPSGradientScanRenderRegion * pScan) { // Children are rendered first, so that attributes will be applied to the right objects. Node * pChild = pNode->FindFirstChild (); while (pChild!=NULL) { BuildGradientCacheUsingScanner (pChild, pScan); pChild = pChild->FindNext (); } // "Render" the node into the scanner. Currently, rendering is the only way to access // the intermediate steps (and their attributes) in blends, contours and some others, // so we need to use a special Render Region to catch the gradient (fill) information. // // NOTE: Since this uses the same system as the main render loop, it shouldn't be possible // for the gradient cache and the main file to become out of sync. Also, bevels and // shadows are excluded, as they are rendered as bitmaps in AI export. if (!pNode->IsABevel () && !pNode->IsAShadow ()) pNode->Render (pScan); } /******************************************************************************************** > virtual void AIEPSRenderRegion::WriteGradientCount () Author: Chris_Gallimore (Xara Group Ltd) Created: 20/3/2001 Purpose: Writes out the count of all the gradient fills in the Cache See also: AIEPSRenderRegion::WriteGradientDefinitions, AIEPSRenderRegion::BuildGradientCache ********************************************************************************************/ void AIEPSRenderRegion::WriteGradientCount () { KernelDC *pDC = (KernelDC*)CCDC::ConvertFromNativeDC(RenderDC); ListFill * pList = m_pLinearGradList; INT32 numFills = 0; // Cycle through the gradient cache writing out fills as we go. // // First count the linear fills while (pList != NULL) { numFills ++; pList = pList->pNext; } // Then count the radial fills. pList = m_pRadialGradList; while (pList != NULL) { numFills ++; pList = pList->pNext; } // If there are any fills, then write the count out. if (numFills) { pDC->OutputValue (numFills); pDC->OutputToken (_T("Bn")); pDC->OutputNewLine (); } } /******************************************************************************************** > virtual void AIEPSRenderRegion::WriteGradientDefinitions () Author: Chris_Gallimore (Xara Group Ltd) Created: 5/2/2001 Purpose: Writes out the definitions of all the gradient fills in the Cache See also: AIEPSRenderRegion::WriteGradientCount, AIEPSRenderRegion::BuildGradientCache ********************************************************************************************/ void AIEPSRenderRegion::WriteGradientDefinitions () { ListFill * pList = NULL; FillGeometryAttribute * pFill = NULL; // Cycle through the gradient cache writing out fills as we go. // // First do the linear list. pList = m_pLinearGradList; while (pList != NULL) { pFill = pList->pFill; WriteLinearFill ( pFill, pList->effect, pList->id ); pList = pList->pNext; } // Then do the radial list. pList = m_pRadialGradList; while (pList != NULL) { pFill = pList->pFill; WriteRadialFill ( pFill, pList->effect, pList->id ); pList = pList->pNext; } } CC_IMPLEMENT_DYNAMIC(AIEPSGradientScanRenderRegion, VectorFileRenderRegion) /******************************************************************************************** > virtual void AIEPSGradientScanRenderRegion::AIEPSGradientScanRenderRegion (RenderRegion * pControllingRegion) Author: Chris_Gallimore (Xara Group Ltd) Created: 5/2/2001 Inputs: pControllingRegion - the AIEPSRenderRegion that created this.. Purpose: Used to scan the tree for gradient fills when exporting to AI EPS format. The objects are rendered into this region, which then stores the fills in a gradient cache. This is necessary, as objects such as blends only allow access to the intermediate steps when they are being rendered, and any gradient fills need to be cached in the AI file before proper rendering starts. ********************************************************************************************/ AIEPSGradientScanRenderRegion::AIEPSGradientScanRenderRegion (RenderRegion * pControllingRegion) : VectorFileRenderRegion () { SetControllingRegion (pControllingRegion); DocRect clipRect = pControllingRegion->GetClipRect (); SetClipRect (clipRect); } /******************************************************************************************** > virtual void AIEPSGradientScanRenderRegion::DrawPathToOutputDevice (Path *pPath, PathProcessor *pCaller = NULL, PathShape ShapePath = PATHSHAPE_PATH) Author: Chris_Gallimore (Xara Group Ltd) Created: 5/2/2001 Inputs: see RenderRegion::DrawPathToOutputDevice Purpose: Used to catch gradient fills on objects (mainly in blends, contours, brushes, and other objects which won't show up in the tree) so that they can be included properly in the caching system. See also: RenderRegion::DrawPath ********************************************************************************************/ void AIEPSGradientScanRenderRegion::DrawPathToOutputDevice (Path *DrawPath, PathShape shapePath /*= PATHSHAPE_PATH*/) { FillGeometryAttribute * pFill = (FillGeometryAttribute *) CurrentAttrs[ATTR_FILLGEOMETRY].pAttr; AIEPSRenderRegion * pControl = (AIEPSRenderRegion *) GetControllingRegion (); EFFECTTYPE effect = GetFillEffect (); // Write and store the fill info it's got a gradient fill. if (pControl->IsGradientFillValidForExport (pFill) && !pControl->FindGradientInCache (pFill, effect)) { // Store this fill - function takes a copy of the whole fill (not just the pointer), // so this is safe. pControl->IncludeGradientFill (pFill, effect); } // Reset the fill to RGB. This is temp, so it will be deleted when it is no longer used. SetFillEffect (new FillEffectFadeAttribute, TRUE); } /******************************************************************************************** > virtual void AIEPSGradientScanRenderRegion::SetControllingRegion (RenderRegion * pControllingRegion) Author: Chris_Gallimore (Xara Group Ltd) Created: 5/2/2001 Inputs: the AIEPSRenderRegion that we need to send all the information to once we've scanned it in. Purpose: sets the controlling AIEPSRenderRegion. I.e. the one that needs to cache the gradient fills. ********************************************************************************************/ void AIEPSGradientScanRenderRegion::SetControllingRegion (RenderRegion * pControllingRegion) { ERROR3IF (!pControllingRegion->IsKindOf (CC_RUNTIME_CLASS(AIEPSRenderRegion)), "AIEPSGradientScanRenderRegion::SetControllingRegion - controlling region is not an AIEPSRegion"); m_pController = pControllingRegion; } CC_IMPLEMENT_DYNAMIC (ListFill, CCObject); /******************************************************************************************** > ListFill::ListFill () Author: Chris_Gallimore (Xara Group Ltd) Created: 30/3/2001 Purpose: Constructor for a gradient fill holder, so it can be stored in a linked list cache. All variables (esp. the id) should be set before use. It is better to use the constructor below, as this will allocate id numbers uniquely. ********************************************************************************************/ ListFill::ListFill () { pFill = NULL; pNext = NULL; id = 1; effect = EFFECT_RGB; } /******************************************************************************************** > ListFill::ListFill (FillGeometry * pNewFill, ListFill * pNewNext) Author: Chris_Gallimore (Xara Group Ltd) Created: 30/3/2001 Inputs: pNewFill - The fill to be stored. pNewNext - The next item in the list. Purpose: Constructor for a gradient fill holder, so it can be stored in a linked list cache. ********************************************************************************************/ ListFill::ListFill (FillGeometryAttribute * pNewFill, EFFECTTYPE eff, ListFill * pNewNext) { pFill = pNewFill; effect = eff; pNext = pNewNext; if(pNewNext) { id = pNewNext->id + 1; } else { id = 1; } } /******************************************************************************************** > ListFill::~ListFill () Author: Chris_Gallimore (Xara Group Ltd) Created: 30/3/2001 Purpose: Destructor for a ListFill item. This also cleans up the fill it contains (if any). ********************************************************************************************/ ListFill::~ListFill () { if (pFill != NULL) delete pFill; }