// $Id: cmxifltr.cpp 1282 2006-06-09 09:46:49Z alex $ /* @@tag:xara-cn@@ DO NOT MODIFY THIS LINE ================================XARAHEADERSTART=========================== Xara LX, a vector drawing and manipulation program. Copyright (C) 1993-2006 Xara Group Ltd. Copyright on certain contributions may be held in joint with their respective authors. See AUTHORS file for details. LICENSE TO USE AND MODIFY SOFTWARE ---------------------------------- This file is part of Xara LX. Xara LX is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License version 2 as published by the Free Software Foundation. Xara LX and its component source files are distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with Xara LX (see the file GPL in the root directory of the distribution); if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA ADDITIONAL RIGHTS ----------------- Conditional upon your continuing compliance with the GNU General Public License described above, Xara Group Ltd grants to you certain additional rights. The additional rights are to use, modify, and distribute the software together with the wxWidgets library, the wxXtra library, and the "CDraw" library and any other such library that any version of Xara LX relased by Xara Group Ltd requires in order to compile and execute, including the static linking of that library to XaraLX. In the case of the "CDraw" library, you may satisfy obligation under the GNU General Public License to provide source code by providing a binary copy of the library concerned and a copy of the license accompanying it. Nothing in this section restricts any of the rights you have under the GNU General Public License. SCOPE OF LICENSE ---------------- This license applies to this program (XaraLX) and its constituent source files only, and does not necessarily apply to other Xara products which may in part share the same code base, and are subject to their own licensing terms. This license does not apply to files in the wxXtra directory, which are built into a separate library, and are subject to the wxWindows license contained within that directory in the file "WXXTRA-LICENSE". This license does not apply to the binary libraries (if any) within the "libs" directory, which are subject to a separate license contained within that directory in the file "LIBS-LICENSE". ARRANGEMENTS FOR CONTRIBUTION OF MODIFICATIONS ---------------------------------------------- Subject to the terms of the GNU Public License (see above), you are free to do whatever you like with your modifications. However, you may (at your option) wish contribute them to Xara's source tree. You can find details of how to do this at: http://www.xaraxtreme.org/developers/ Prior to contributing your modifications, you will need to complete our contributor agreement. This can be found at: http://www.xaraxtreme.org/developers/contribute/ Please note that Xara will not accept modifications which modify any of the text between the start and end of this header (marked XARAHEADERSTART and XARAHEADEREND). MARKS ----- Xara, Xara LX, Xara X, Xara X/Xtreme, Xara Xtreme, the Xtreme and Xara designs are registered or unregistered trademarks, design-marks, and/or service marks of Xara Group Ltd. All rights in these marks are reserved. Xara Group Ltd, Gaddesden Place, Hemel Hempstead, HP2 6EX, UK. http://www.xara.com/ =================================XARAHEADEREND============================ */ // CMX import Filter #include "camtypes.h" //#include "oilfltrs.h" - in camtypes.h [AUTOMATICALLY REMOVED] //#include "filtrres.h" //#include "ccfile.h" - in camtypes.h [AUTOMATICALLY REMOVED] #include "page.h" //#include "spread.h" - in camtypes.h [AUTOMATICALLY REMOVED] //#include "tim.h" //#include "docview.h" - in camtypes.h [AUTOMATICALLY REMOVED] #include "nodepath.h" #include "riffform.h" #include "progress.h" //#include "document.h" - in camtypes.h [AUTOMATICALLY REMOVED] #include "layer.h" //#include "group.h" - in camtypes.h [AUTOMATICALLY REMOVED] #include "cdrfiltr.h" //#include "fillval.h" - in camtypes.h [AUTOMATICALLY REMOVED] #include "combshps.h" #include "nodetxts.h" #include "attrmap.h" #include "cmxform.h" #include "cmxifltr.h" #include "cmxistut.h" #include "cmxibits.h" //#include "cmxres.h" #include "cmxidata.h" #include "cmxibitm.h" CC_IMPLEMENT_DYNAMIC(CMXImportFilter, VectorFilter) CC_IMPLEMENT_DYNAMIC(CMXImportFilterDataSet, CCObject) #define new CAM_DEBUG_NEW #ifdef _DEBUG void CMXImportFilter::FormatErrorBreakingFunction(void) { TRACEUSER( "Ben", _T(">>> format error in cmx file\n")); } #endif /******************************************************************************************** > CMXImportFilter::CMXImportFilter() Author: Ben_Summers (Xara Group Ltd) Created: 05/08/96 Inputs: None Returns: None Purpose: Constructor for class CMXImportFilter SeeAlso: class VectorFilter ********************************************************************************************/ CMXImportFilter::CMXImportFilter() { // Set up filter descriptions. FilterName.Load(_R(IDT_CMXIMPORTFILTER_FILTERNAME)); FilterInfo.Load(_R(IDT_CMXIMPORTFILTER_FILTERINFO)); FilterID = FILTERID_CMXIMPORT; Flags.CanImport = TRUE; Flags.CanExport = FALSE; ImportMsgID = _R(IDT_IMPORTMSG_CMX); ExportMsgID = _R(IDT_EXPORTMSG_CMX); // set up the info CMXIinfo::SetUpInfo(); // mark the data set as not being used Data = NULL; // By default we're not dealing with a file from CorelDraw 7 CorelDraw7 = FALSE; } /******************************************************************************************** > BOOL CMXImportFilter::Init() Author: Ben_Summers (Xara Group Ltd) Created: 05/08/96 Inputs: None Returns: BOOL, propably saying whether we succeeded or not, buts it not documented Purpose: Initialiser for class CMXImportFilter SeeAlso: Filter ********************************************************************************************/ BOOL CMXImportFilter::Init() { // Get the OILFilter object pOILFilter = new CMXImportOILFilter(this); if (pOILFilter == NULL) return FALSE; return TRUE; } /******************************************************************************************** > INT32 CMXImportFilter::HowCOmpatible(PathName& Filename, ADDR HeaderStart, UINT32 HeaderSize, UINT32 FileSize); Author: Ben_Summers (Xara Group Ltd) Created: 05/08/96 Inputs: Filename - filename of file to check HeaderStart, HeaderSize, - infomation on header of file loaded into memory FileSize - length of file (bytes) Returns: 0 to 10. 0 means I know nothing about this file, 10 means its definitly mine Purpose: Looks at the start of the file to see if we recognise it as one of ours SeeAlso: class Filters, class CDRFilter ********************************************************************************************/ INT32 CMXImportFilter::HowCompatible(PathName & FileName, ADDR HeaderStart, UINT32 HeaderSize, UINT32 FileSize) { // see if we're a nice CMX file... PORTNOTE("byteorder", "TODO: Check byte ordering") RIFFFile_Header *pHdr = (RIFFFile_Header *)HeaderStart; if(pHdr->CK.ckID == RIFFTYPE_RIFF) { switch(pHdr->FormType) { case cmxRIFFFORMTYPE_CMX: // yep, it's one of ours return 10; break; case cmxRIFFFORMTYPE_CDR5: case cmxRIFFFORMTYPE_CDT5: case cmxRIFFFORMTYPE_CDR6: case cmxRIFFFORMTYPE_CDT6: // Tootle through the file if it's a CDR file to see if we // can't load that nice CMX file it might have inside it. if(CDRFilter::HasCDRFileGotCMXFile(&FileName)) { return 10; // we'll have that too, then } break; default: break; } } return 0; } /******************************************************************************************** > BOOL CMXImportFilter::DoImport(SelOperation * Op, CCLexFile * pDiskFile, Document * DestDoc, BOOL AutoChoosen , ImportPosition * Pos, KernelBitmap** ppImportedBitmap, DocCoord* pPosTranslate, String_256* URL) Author: Ben_Summers (Xara Group Ltd) Created: 05/08/96 Inputs: Op - pointer to the operatioj that this input process is associated with, pDiskFile - the CMXImport file to be loaded DestDoc - the document object whcih should hold the data read in from this file URL - original URL of the imported file Returns: TRUE is the input operation worked, FALSE if not Purpose: Read an CMXImport format file and convert it into Camelot tree data structures. When the file is loaded we force a redraw for the area affected by the new data At the moment the data is just put on the first layer of the first spread of the first chapter in the document, and this is a bodge. The operation is terminated (i.e. its End() function is called) if the operation completed successfully. Errors: Fails (returns FALSE) if the document structure is incorrect, if the CMXImport file contains a syntax error, of the CMXImport file cannot be found/opened or if its not actually an CMXImport file. SeeAlso: CMXImportFilter::HowCompatible ********************************************************************************************/ BOOL CMXImportFilter::DoImport(SelOperation * Op, CCLexFile * pDiskFile, Document * DestDoc, BOOL AutoChoosen , ImportPosition * Pos, KernelBitmap** ppImportedBitmap, DocCoord* pPosTranslate, String_256* URL) { // set variables pFile = pDiskFile; Is32Bit = FALSE; // for now... Stage1_ReadHeader sets it properly StartPosition = 0; TheDocument = DestDoc; // create a dataset ERROR3IF(Data != NULL, "CMX data set already created"); Data = new CMXImportFilterDataSet(this); if(Data == NULL) return FALSE; // set up various filter type things Data->pProgress = new Progress(_R(IDT_CMX_IMPORTMSG), 1, FALSE); if(Data->pProgress == NULL) return FALSE; if(!SetUpCurrentAttrs()) return FALSE; // We haven't read the text frame yet Data->TextFrameRead = FALSE; SetTextAttributeIgnore(); // get the spread we're using Document *pTheDocument = GetDocument(); ERROR2IF(pTheDocument == NULL, FALSE, "no document for filter"); Spread *pSpread = NULL; //pTheDocument->GetSelectedSpread(); if (Pos == NULL) { // For now, position objects on 1st page of spread 1 pSpread = GetFirstSpread(DestDoc); Page *pPage = (Page *) pSpread->FindFirstPageInSpread(); ENSURE(pPage->IsKindOf(CC_RUNTIME_CLASS(Page)), "MetaFileFilter::DoImport(): Could not find first Page"); } else { pSpread = Pos->pSpread; } // do various bits and pieces to convert that silly file BOOL EverythingWentSwimmingly = TRUE; TRY { if( !Stage1_ReadHeader(pSpread) || !Stage2_ReadMasterIndex() || !Stage3_ConvertDescriptionSections() || !Stage4_ConvertPage() || !Stage5_PlaceObjectsInTree(pSpread, Op, Pos) ) EverythingWentSwimmingly = FALSE; } CATCH(CFileException, e) { // something went wrong -- clean up CleanUpAfterImport(); return FALSE; } END_CATCH CleanUpAfterImport(); return EverythingWentSwimmingly; } /******************************************************************************************** > void CMXImportFilter::CleanUpAfterImport(void) Author: Ben_Summers (Xara Group Ltd) Created: 07/08/96 Inputs: None Returns: nothin' mate Purpose: Cleans everything up after a sucessful or non-sucessful import ********************************************************************************************/ void CMXImportFilter::CleanUpAfterImport(void) { // delete the data set -- that class will take care of itself and delete everything // necessary to ensure we don't leak anything if(Data != NULL) delete Data; Data = NULL; // vape attributes DeleteCurrentAttrs(); } /******************************************************************************************** > BOOL CMXImportFilter::LocateRootLevelRIFFSection(DWORD Section) Author: Ben_Summers (Xara Group Ltd) Created: 07/08/96 Inputs: None Returns: TRUE if the section was fount Purpose: Sets the file pointer to the start of the data in the specified RIFF section. Doesn't go into lists. ********************************************************************************************/ BOOL CMXImportFilter::LocateRootLevelRIFFSection(DWORD Section) { // go to the beginning of the file Seek(0); // read in the file header, and check it's a riff file RIFFFile_Header Hdr; pFile->read(&Hdr, sizeof(Hdr)); if(Hdr.CK.ckID != RIFFTYPE_RIFF) return FALSE; // file is not a riff form UINT32 loc = sizeof(Hdr); // run through the thingys while(loc < Hdr.CK.ckSize) { RIFFck ck; pFile->read(&ck, sizeof(ck)); loc += sizeof(ck); if(ck.ckID == Section) return TRUE; // found it! // skip to start of next chunk loc += ck.ckSize; Seek(loc); } return FALSE; } /******************************************************************************************** > BOOL CMXImportFilter::Stage1_ReadHeader(Spread *pSpread) Author: Ben_Summers (Xara Group Ltd) Created: 07/08/96 Inputs: None Returns: error flag Purpose: Reads the header from the file, and sets various internal variables to reflect what it found there. It creates the base matrix for transforming stuff. ********************************************************************************************/ BOOL CMXImportFilter::Stage1_ReadHeader(Spread *pSpread) { // find the header of the file if(!LocateRootLevelRIFFSection(cmxRIFFCI_Header)) { // OK, didn't find one of those... see if we can get a CMX one instead if(LocateRootLevelRIFFSection(cmxRIFFFORMTYPE_cmx1)) { // fine... alter the thingy to point to the beginning of this section StartPosition = pFile->tellIn(); // and locate the header section if(!LocateRootLevelRIFFSection(cmxRIFFCI_Header)) { CMXFORMATERROR(FALSE) } } else { CMXFORMATERROR(FALSE) } } // read it in CI_READDATA(FileHeader, Hdr) // check that the id is correct if(strcmp(Hdr.ID, cmxID) != 0) CMXFORMATERROR(FALSE) // wasn't a CMX file // read the handy bits from it switch(Hdr.CoordSize & 0xf) { case cmxCOORDSIZE_32: TRACEUSER( "Ben", _T("\n\n********************** 32 bit CMX file\n")); Is32Bit = TRUE; break; case cmxCOORDSIZE_16: TRACEUSER( "Ben", _T("\n\n********************** 16 bit CMX file\n")); Is32Bit = FALSE; break; default: // don't understand this data size CMXFORMATERROR(FALSE); break; } // set the location of the index Data->IndexFilePosition = Hdr.IndexSection; // set up the base matrix for transformation of the objects on the page Page *pPage = (Page *)pSpread->FindFirstPageInSpread(); // Use bottom left of page as origin as in CMX in (0,0) is the centre of the page DocRect PageRect = pPage->GetPageRect(); DocCoord Origin = DocCoord(PageRect.lo.x + (PageRect.Width() / 2), PageRect.lo.y + (PageRect.Height() / 2)); // set a matrix which transforms things appropriately double Scale; if(Is32Bit) { Scale = 1 / CAMCOORD_SCALEFACTOR32; } else { Scale = 72; } Matrix TheMat = Matrix((FIXED16)Scale, (FIXED16)Scale); // transform the coordinates TheMat *= Matrix(Origin); // set the matrix Data->BaseMatrix = TheMat; Data->BaseScaleFactor = Scale; // and set the current matrix pointer to it Data->pCurrentMatrix = &Data->BaseMatrix; return TRUE; } /******************************************************************************************** > BOOL CMXImportFilter::Stage5_PlaceObjectsInTree(Spread *pSpread, SelOperation *pOp, ImportPosition *pPos) Author: Ben_Summers (Xara Group Ltd) Created: 07/08/96 Inputs: None Returns: error flag Purpose: sticks the objects in the document tree ********************************************************************************************/ BOOL CMXImportFilter::Stage5_PlaceObjectsInTree(Spread *pSpread, SelOperation *pOp, ImportPosition *pPos) { Trans2DMatrix Xlate; BOOL Translate = FALSE; Coord Offset; // do we need to do a drag and drop translation? if(GetDragAndDropTranslation(pPos, Data->AllObjectsBoundsRect, &Offset)) { Translate = TRUE; Xlate = Trans2DMatrix(Offset.x, Offset.y); } BOOL ImportingWithLayers = FALSE; // do we want to import with layers? #ifdef WEBSTER // In Webster, just use the default preference settings as these should be set to // ImportWithLayers to False. if ( (!TheDocument->IsImporting() && Filter::OpenWithLayers) || (TheDocument->IsImporting() && Filter::ImportWithLayers) ) ImportingWithLayers = TRUE; #else // In Camelot, we must check to see if the document is being imported and if so if // there are any frames present. If there are then don't use layers, if there are // then use the preference setting. if (TheDocument->IsImporting()) { Spread * pTheSpread = TheDocument->GetSelectedSpread(); Layer * pFrame = NULL; if (pTheSpread != NULL) pFrame = pTheSpread->FindFirstFrameLayer(); if (pFrame != NULL) ImportingWithLayers = FALSE; // Frames present so do not import layers else ImportingWithLayers = Filter::ImportWithLayers; // No frames so use the preference setting } else { ImportingWithLayers = Filter::OpenWithLayers; } #endif NodeGroup *pGroup = NULL; if(!ImportingWithLayers) { if((pGroup = new NodeGroup) == NULL) return FALSE; } // go through the layer list adding stuff CMXImportLayer *pSarah = (CMXImportLayer *)Data->Layers.GetHead(); while(pSarah != NULL) { ERROR2IF(!IS_A(pSarah, CMXImportLayer), FALSE, "thingy not a CMX import layer"); if(ImportingWithLayers) { // get the layer, and mark the import layer as used Layer *pNewLayer = pSarah->UseSubTreeAsLayer(); if(pNewLayer == NULL) return FALSE; // and maybe translate it a bit if(Translate) pNewLayer->Transform(Xlate); // insert the layer into the document if(!pOp->DoInsertNewNode(pNewLayer, (Node *)pSpread, LASTCHILD, TRUE)) { // It didn't work - delete the sub-tree we just created pNewLayer->CascadeDelete(); delete pNewLayer; return FALSE; } // make sure it's name is unique pNewLayer->EnsureUniqueLayerID(); // ... and finally optimise it's attributes if(!pNewLayer->OptimiseAttributes()) return FALSE; } else { // do stuff without layers ERROR3IF(pGroup == NULL, "no group node created"); // get the subtree of the layer, and add it if(pSarah->GetSubTree() != NULL) // some may be empty { Node *pSubTree = pSarah->GetSubTree(); // add it to the group node pSubTree->InsertChainSimple(pGroup, LASTCHILD); } // mark the layer as being used pSarah->UseSubTree(); } pSarah = (CMXImportLayer *)Data->Layers.GetNext(pSarah); } if(ImportingWithLayers) { // get the insertation node set up TheDocument->ResetInsertionPosition(); } else { // Make sure that there is a layer to put the bitmap onto if (!MakeSureLayerExists(TheDocument)) // There is no layer and one could not be made, so we will have to fail return FALSE; // TheDocument->ResetInsertionPosition(); // check to see if the group node has more than one child // if it doesn't just insert the child and not the group, and then // delete the group node afterwards NodeRenderableBounded *InsertNode = pGroup; NodeRenderableBounded *FirstChild = (NodeRenderableBounded *)pGroup->FindFirstChild(); if(FirstChild != 0) { // if the first child hasn't got a sibling, then there's only one // child to the group, so just insert the child if(FirstChild->FindNext() == 0) InsertNode = FirstChild; } // bit of translation for sir? if(Translate) InsertNode->Transform(Xlate); // if there isn't a group, detach it if(InsertNode != pGroup) InsertNode->UnlinkNodeFromTree(NULL); // invalidate the insert node's bounds InsertNode->InvalidateBoundingRect(); // put the group into the document if (!pOp->DoInsertNewNode(InsertNode, pSpread, TRUE)) { // It didn't work - delete the sub-tree we just created pGroup->CascadeDelete(); delete pGroup; return FALSE; } // optimise the groups attributes if(!InsertNode->OptimiseAttributes()) return FALSE; // delete the group if it wasn't used if(InsertNode != pGroup) delete pGroup; } // add colours if(!AddColoursToDocument()) return FALSE; // do a quick post import on it TheDocument->PostImport(); return TRUE; } /******************************************************************************************** > CMXImportFilterDataSet::CMXImportFilterDataSet(CMXImportFilter *pFilter) Author: Ben_Summers (Xara Group Ltd) Created: 07/08/96 Inputs: none Returns: none Purpose: constructor for the CMX import filter's data set ********************************************************************************************/ CMXImportFilterDataSet::CMXImportFilterDataSet(CMXImportFilter *pFilter) : TransformStack(cmxiSTACKTYPE_TRANSFORM, pFilter) , ClipperStack(cmxiSTACKTYPE_CLIPPER, pFilter) { // mark all index entry positions as not present for(INT32 l = 0; l < cmxMASTERIN_MAXENTRY; l++) MasterIndexEntries[l] = 0; // set things to null pProgress = NULL; CommandsInPage = -1; pCurrentLayer = NULL; pCurrentMatrix = NULL; pTextStory = NULL; pTextLine = NULL; NumColours = 0; Colours = NULL; NumOutlines = 0; Outlines = NULL; NumPens = 0; Pens = NULL; BaseScaleFactor = 0; NumLineStyles = 0; LineStyles = NULL; NumRImages = 0; RImages = NULL; NumProcedures = 0; Procedures = NULL; NumDotDashes = 0; DotDashes = NULL; pClippingPath = NULL; NumArrowShapes = 0; ArrowShapes = NULL; NumArrowheads = 0; Arrowheads = NULL; NumBitmaps = 0; Bitmaps = NULL; NumFonts = 0; Fonts = NULL; // No way to know how many CharInfo are going to be there before actually read them // Use a number big enough. NumCharInfo will be the actual number. NumCharInfo = 0; CharInfos = new cmxiCharInfo[100]; for(INT32 i = 0 ; i < 100 ; i++) CharInfos[i].UsageMask = 0; // flags InPage = FALSE; RemoveLens = FALSE; EndSectionFound = FALSE; HaveFirstObjInBoundsRect = FALSE; // counts of approxed objects AttributesApproximated = 0; ObjectsApproximated = 0; ClippingsApproximated = 0; } /******************************************************************************************** > CMXImportFilterDataSet::~CMXImportFilterDataSet() Author: Ben_Summers (Xara Group Ltd) Created: 07/08/96 Inputs: none Returns: none Purpose: destructor for the CMX import filter's data set -- it will delete all objects which aren't needed after import. ********************************************************************************************/ CMXImportFilterDataSet::~CMXImportFilterDataSet() { // delete list entries -- they will delete their contents if they haven't been used Layers.DeleteAll(); // close down the progress system if(pProgress != NULL) { delete pProgress; pProgress = NULL; } // delete lists of stuff if(Colours != 0) delete [] Colours; if(Outlines != 0) delete [] Outlines; if(Pens != 0) delete [] Pens; if(LineStyles != 0) delete [] LineStyles; if(RImages != 0) delete [] RImages; if(Procedures != 0) delete [] Procedures; if(DotDashes != 0) delete [] DotDashes; if(ArrowShapes != 0) delete [] ArrowShapes; if(Arrowheads != 0) delete [] Arrowheads; if(Bitmaps != 0) delete [] Bitmaps; if(Fonts != 0) delete [] Fonts; if(CharInfos != 0) delete [] CharInfos; } // access functions to avoid adding cmxidata.h to cmxifltr.h void CMXImportFilter::AttributeApproximated(void) {Data->AttributesApproximated++;} void CMXImportFilter::ClippingApproximated(void) {Data->ClippingsApproximated++;} void CMXImportFilter::ObjectApproximated(void) {Data->ObjectsApproximated++;} Matrix *CMXImportFilter::GetBaseMatrix(void) {return &Data->BaseMatrix;}; Matrix *CMXImportFilter::GetCurrentMatrix(void) {return Data->pCurrentMatrix;}; void CMXImportFilter::SetMatrix(Matrix *pNewMatrix) {Data->pCurrentMatrix = pNewMatrix;}; void CMXImportFilter::SetClippingPath(Path *pClipper) {Data->pClippingPath = pClipper;}; /******************************************************************************************** > BOOL CMXImportFiler::TransformNode(Node *pNode) Author: Ben_Summers (Xara Group Ltd) Created: 09/08/96 Inputs: pointer to node to fiddle with Returns: error flag Purpose: transforms the node to the correct place on the page. Only give it nodes which are kinds of NodeRenderableBounded. ********************************************************************************************/ BOOL CMXImportFilter::TransformNode(Node *pNode) { ERROR2IF(Data->pCurrentMatrix == NULL, FALSE, "No matrix to transform by"); // transform the node Trans2DMatrix Transform(*Data->pCurrentMatrix); NodeRenderableBounded *pBN = (NodeRenderableBounded *)pNode; ERROR3IF(!pBN->IsKindOf(CC_RUNTIME_CLASS(NodeRenderableBounded)), "not a transformable node passed to TransformNode"); pBN->Transform(Transform); // do some funky stuff on it DocRect BBox = pBN->GetBoundingRect(TRUE, FALSE); if(Data->HaveFirstObjInBoundsRect == FALSE) { // set the bbox Data->HaveFirstObjInBoundsRect = TRUE; Data->AllObjectsBoundsRect = BBox; } else { Data->AllObjectsBoundsRect = Data->AllObjectsBoundsRect.Union(BBox); } return TRUE; } /******************************************************************************************** > BOOL CMXImportFiler::SetAttributesForNode(Node *pNode, cmxiRenderAttr *pAttr) Author: Ben_Summers (Xara Group Ltd) Created: 09/08/96 Inputs: pointer to node to fiddle with, pointer to attributes from CMX file Returns: error flag Purpose: sets the attributes for the node. The bits and pieces from the render attributes thingy are deleted, so you can't use them twice ********************************************************************************************/ #define MAXLINEWIDTH (720000 * 2) // sounds reasonable BOOL CMXImportFilter::SetAttributesForNode(Node *pNode, void *pvAttr, BOOL DeleteStuff) { cmxiRenderAttr *pAttr = (cmxiRenderAttr *)pvAttr; // remove lens attributes? if(Data->RemoveLens) { Data->RemoveLens = FALSE; if(!SetNoTranspFill()) return FALSE; } BOOL DoFillStuff = TRUE; // ----------------------------------------- fills if((pAttr->Mask & cmxRENDATTRMASK_LENS) != 0) { // can we handle the lens? if(pAttr->LensSpec.Type == cmxLENSTYPE_GLASS) { UINT32 Type; // yep, it's one of those glass ones switch(pAttr->LensSpec.Glass.TintMethod) { case cmxLENSTINTMETH_SUBTRACT: Type = TT_StainGlass; break; case cmxLENSTINTMETH_ADD: Type = TT_Bleach; break; case cmxLENSTINTMETH_AVERAGE: Type = TT_Mix; break; default: Type = TT_Mix; AttributeApproximated(); break; } // munge to our range UINT32 Percent = 255 - ((pAttr->LensSpec.Glass.UniformRate * 255) / 1000); // set the attribute if(!SetFlatTranspFill(Type, Percent)) return FALSE; // set the colour of the object DocColour *pColour = GetColourFromReference(pAttr->LensSpec.Glass.ColourRef); if(pColour == NULL) return FALSE; // set the attribute if(!SetFillColour(*pColour)) return FALSE; Data->RemoveLens = TRUE; // something to remove next time around DoFillStuff = FALSE; // we've handled all fill type stuff } else { AttributeApproximated(); // mark the fact we've ignored something } } // ----------------------------------------- fills if(DoFillStuff) { if((pAttr->Mask & cmxRENDATTRMASK_FILL) != 0) { // got a fill -- set filledness Data->ObjFilled = TRUE; SetPathFilled(TRUE); // set attributes for it BOOL UnknownFill = FALSE; TRACEUSER( "Ben", _T(" fill type is %d\n"), pAttr->FillSpec.FillID); // here we convert the stuff, and delete anything which is created // for us switch(pAttr->FillSpec.FillID) { case cmxFILLID_UNIFORM: { // get a colour for the fill DocColour *pColour = GetColourFromReference(pAttr->FillSpec.Spec.Uniform.ColourReference); if(pColour == NULL) return FALSE; // set the attribute if(!SetFillColour(*pColour)) return FALSE; } break; case cmxFILLID_FOUNTAIN: if(!SetAttributesFountain(pNode, &pAttr->FillSpec)) { if(DeleteStuff) delete pAttr->FillSpec.Spec.Fountain.pColours; return FALSE; } if(DeleteStuff) delete pAttr->FillSpec.Spec.Fountain.pColours; break; case cmxFILLID_PS: if(DeleteStuff) delete pAttr->FillSpec.Spec.Postscript.UserFunction; break; case cmxFILLID_COLPATTERN: if(!SetAttributesTwoColourBitmapFill(pNode, &pAttr->FillSpec)) return FALSE; break; case cmxFILLID_BITMAPFILL: case cmxFILLID_BITMAPFILL16: // also vector fills in v6 // we've got one of those possible bitmap fill type things if(!SetAttributesBitmapFill(pNode, pAttr->FillSpec.Spec.Bitmap.VectorFillReference, &pAttr->FillSpec.Spec.Bitmap.Tile)) return FALSE; break; case cmxFILLID_TEXTURE: if(!SetAttributesBitmapFill(pNode, pAttr->FillSpec.Spec.Texture.VectorFillReference, &pAttr->FillSpec.Spec.Texture.Tile)) return FALSE; break; default: // dunno nothing about this kind of fill ID -- set to no colour UnknownFill = TRUE; break; } if(UnknownFill) { // give a fill no colour if we don't know how to do it AttributeApproximated(); if(!SetNoFill(pNode)) return FALSE; } } else { Data->ObjFilled = FALSE; // no fill here if(!SetNoFill(pNode)) return FALSE; } } // ----------------------------------------- outline if((pAttr->Mask & cmxRENDATTRMASK_OUTLINE) != 0) { // right then, get the outline defn CMXImportOutline *pOut; if(pAttr->OutlineReference <= 0 || pAttr->OutlineReference > Data->NumOutlines) CMXFORMATERROR(FALSE) // outline ref out of range pOut = &Data->Outlines[pAttr->OutlineReference - 1]; if(pOut->NoStroke) { if(!SetNoStroke(pNode)) return FALSE; } else { // have an outline to fiddle with... // get the colour DocColour *pCol = GetColourFromReference(pOut->ColourReference); if(pCol == NULL) return FALSE; // set it... if(!SetLineColour(*pCol)) return FALSE; // do the line width double LineWidth = pOut->Width * Data->BaseScaleFactor; if(LineWidth > MAXLINEWIDTH) LineWidth = MAXLINEWIDTH; if(!SetLineWidth((MILLIPOINT)LineWidth)) return FALSE; // do that funky dot dash pattern thingy DashRec Dash; if(pOut->pDots != NULL && pOut->pDots->NDots != 0) { // set a dash pattern Dash.Elements = pOut->pDots->NDots; Dash.DashStart = 0; Dash.ElementData = pOut->pDots->pElements; Dash.LineWidth = 1; //(MILLIPOINT)LineWidth; Dash.ScaleWithLineWidth = TRUE; Dash.DashID = -1; } else { // no dash pattern Dash = SD_SOLID; } // do the cap and join bits if(!SetLineCap(pOut->Cap) || !SetJoinType(pOut->Join) || !SetDashPattern(Dash)) return FALSE; // do arrowheads if(pOut->pArrowheads != NULL) { // got some arrowheads... // -- <> -- START if(pOut->pArrowheads->pStart != NULL) { if(!SetStartArrow(pOut->pArrowheads->pStart->Arrow)) return FALSE; } else { ArrowRec NullArrow; if(!SetStartArrow(NullArrow)) return FALSE; } // -- <> -- END if(pOut->pArrowheads->pEnd != NULL) { if(!SetEndArrow(pOut->pArrowheads->pEnd->Arrow)) return FALSE; } else { ArrowRec NullArrow; if(!SetEndArrow(NullArrow)) return FALSE; } } else { // no arrowheads -- set a null arrow rec ArrowRec NullArrow; if(!SetStartArrow(NullArrow) || !SetEndArrow(NullArrow)) return FALSE; } } } else { if(!SetNoStroke(pNode)) return FALSE; } return TRUE; } /******************************************************************************************** > BOOL CMXImportFilter::SetNoFill(Node *pNode) Author: Ben_Summers (Xara Group Ltd) Created: 13/08/96 Inputs: node Returns: error flag Purpose: sets attributes for this node to have no fill -- also fiddles it if it's a path ********************************************************************************************/ BOOL CMXImportFilter::SetNoFill(Node *pNode) { if(pNode->IsKindOf(CC_RUNTIME_CLASS(NodePath))) { NodePath *pPath = (NodePath *)pNode; // pPath->InkPath.IsFilled = FALSE; } Data->ObjFilled = FALSE; SetFillColour(DocColour(COLOUR_TRANS)); return SetPathFilled(FALSE); } /******************************************************************************************** > BOOL CMXImportFilter::SetNoStroke(Node *pNode) Author: Ben_Summers (Xara Group Ltd) Created: 13/08/96 Inputs: node Returns: error flag Purpose: sets attributes for this node to have no stroke colour -- also fiddles it if it's a path ********************************************************************************************/ BOOL CMXImportFilter::SetNoStroke(Node *pNode) { // Jim 13/9/96 - Setting Transparent stroke colour is enough - don't set IsStroked to FALSE // otherwise it stops them rendering at zero quality. // if(pNode->IsKindOf(CC_RUNTIME_CLASS(NodePath))) // { // NodePath *pPath = (NodePath *)pNode; // // pPath->InkPath.IsStroked = FALSE; // } SetLineColour(DocColour(COLOUR_TRANS)); return TRUE; } class CMXBecomeA : public BecomeA { public: CMXBecomeA(BecomeAReason ThisReason, CCRuntimeClass* pClass, UndoableOperation* pOp, BOOL sel=TRUE) : BecomeA(ThisReason, pClass, pOp, sel) {}; BOOL PassBack(NodeRenderableInk* pNewNode,NodeRenderableInk* pCreatedByNode,CCAttrMap* pAttrMap=NULL); NodePath *pPath; }; /******************************************************************************************** > BOOL CMXBecomeA::PassBack(NodeRenderableInk* pNewNode,NodeRenderableInk* pCreatedByNode,CCAttrMap* pAttrMap=NULL) Author: Ben_Summers (Xara Group Ltd) Created: 25/08/96 Inputs: random stuff Returns: error flag Purpose: stores the pointer to the copy of my thingy, and bungs the attributes on it. ********************************************************************************************/ BOOL CMXBecomeA::PassBack(NodeRenderableInk* pNewNode,NodeRenderableInk* pCreatedByNode,CCAttrMap* pAttrMap) { pPath = (NodePath *)pNewNode; if(pAttrMap != NULL) { // iterating all (key, value) pairs for (POSITION Pos = pAttrMap->GetStartPosition(); Pos != NULL;) { void *pType,*pVal; pAttrMap->GetNextAssoc(Pos,pType,pVal); // Get the attribute value of this attribute NodeAttribute* pAttr = (NodeAttribute*)pVal; // bung the attribute onto the node thingy pAttr->AttachNode(pNewNode, LASTCHILD); } } return TRUE; } /******************************************************************************************** > BOOL CMXImportFiler::ClipNode(Node **pNode) Author: Ben_Summers (Xara Group Ltd) Created: 09/08/96 Inputs: pointer to pointer to node to clip Returns: error flag Purpose: clips the node to the current clipping path. Note that it may delete this one and change your pointer to a completely new node, just for the fun of it. ********************************************************************************************/ BOOL CMXImportFilter::ClipNode(Node **pNode) { if(Data->pClippingPath == NULL) return TRUE; // nothin' to do NodePath *pPath = NULL; // check to see what kind of node we've got if(!IS_A(*pNode, NodePath)) { CMXBecomeA ParamBecomeA(BECOMEA_PASSBACK, CC_RUNTIME_CLASS(NodePath), NULL, FALSE); if((*pNode)->IsKindOf(CC_RUNTIME_CLASS(NodeRenderableInk))) { if ((*pNode)->CanBecomeA(&ParamBecomeA)) { // is it a text story? if((*pNode)->IsKindOf(CC_RUNTIME_CLASS(TextStory))) { // give formatting it a go ((TextStory *)*pNode)->FormatAndChildren(); } if (!(*pNode)->DoBecomeA(&ParamBecomeA)) { return FALSE; } // quick check... if(!IS_A(ParamBecomeA.pPath, NodePath)) { // didn't get a path! delete ParamBecomeA.pPath; return TRUE; // don't complain, I suppose } // right then, replace the node with this one (*pNode)->CascadeDelete(); delete *pNode; *pNode = ParamBecomeA.pPath; pPath = ParamBecomeA.pPath; } } } else { pPath = (NodePath *)*pNode; } ERROR2IF(pPath == NULL, FALSE, "no path, even though we tried really really hard"); // right then, we've got to do something fabby. Get a new path to stuff stuff in Path *pOutputPath = new Path; if(pOutputPath == NULL || !pOutputPath->Initialise()) return FALSE; // something went wrong // clip the thingy to the thingy if(Data->pClippingPath->ClipPathToPath(pPath->InkPath, pOutputPath, 2 /*CLIP_STYLE_INTERSECT*/) > 0) { // clipping produced something... // now replace the path with the output path if(!pPath->InkPath.CloneFrom(*pOutputPath)) return FALSE; } else { // clipping didn't produce anything -- delete the thingy pPath->CascadeDelete(); delete pPath; // nothing doing, chaps. (*pNode) = NULL; } // delete the output path, don't need it any more delete pOutputPath; return TRUE; } /******************************************************************************************** > BOOL CMXImportFilter::GetCorelBBox(NodeRenderable *pNode, DocRect *BBox) Author: Ben_Summers (Xara Group Ltd) Created: 15/08/96 Inputs: node and a pointer to bbox to fill in Returns: error flag Purpose: gets the Corel simple union bbox of an object ********************************************************************************************/ BOOL CMXImportFilter::GetCorelBBox(NodeRenderableBounded *pNode, DocRect *BBox) { if(pNode->IsKindOf(CC_RUNTIME_CLASS(NodePath))) { // ok, so it's a path. That means we can run through it to find out the simple // union of all points. NodePath *pNodePath = (NodePath *)pNode; // get a pointer to the array of coords in the nice path INT32 NCoords = pNodePath->InkPath.GetNumCoords(); DocCoord *Coords = pNodePath->InkPath.GetCoordArray(); ERROR3IF(NCoords < 1, "Awooga! Trying to find a corel bbox of a path with less than one coord."); INT32 c; INT32 x0, y0, x1, y1; // initialise my bbox variables x0 = x1 = Coords[0].x; y0 = y1 = Coords[0].y; for(c = 1; c < NCoords; c++) { // update my bbox if(Coords[c].x < x0) x0 = Coords[c].x; if(Coords[c].y < y0) y0 = Coords[c].y; if(Coords[c].x > x1) x1 = Coords[c].x; if(Coords[c].y > y1) y1 = Coords[c].y; } // fill in *BBox BBox->lo.x = x0; BBox->lo.y = y0; BBox->hi.x = x1; BBox->hi.y = y1; } else { // if it's not a path object, ask the node what it's bounding box is - we'll just // have to make do with a *correct* bounding box instead of simply corel union. *BBox = pNode->GetBoundingRect(); } return TRUE; } /******************************************************************************************** > BOOL CMXImportFiler::ApplyAttributesToNode(Node *pNode) Author: Ben_Summers (Xara Group Ltd) Created: 09/08/96 Inputs: pointer to node to fiddle with, pointer to attributes from CMX file Returns: error flag Purpose: adds the attributes to the node. ********************************************************************************************/ BOOL CMXImportFilter::ApplyAttributesToNode(Node *pNode) { // not having a node is a valid option here, and shouldn't be complained at // as the previous clipping operation may have decided that there wasn't anything // there after the intersection if(pNode == NULL) return TRUE; // apply the attributes to the object // If not filled, then set the ignore bit on the fill attribute. if(Data->ObjFilled == FALSE) CurrentAttrs[ATTR_FILLGEOMETRY].Ignore = TRUE; // Add attributes to the path, if they are different from the default... BOOL Result = AttributeManager::ApplyBasedOnDefaults(pNode, CurrentAttrs); // Enable the fill attribute again CurrentAttrs[ATTR_FILLGEOMETRY].Ignore = FALSE; return Result; } /******************************************************************************************** > BOOL CMXImportFilter::GotoSectionFromIndex(INT32 MasterIndexEntry, DWORD PredictedRIFFType, BOOL NonExistanceIsFormatError) Author: Ben_Summers (Xara Group Ltd) Created: 13/08/96 Inputs: MasterIndexEntry - entry to read position from PredictedRIFFType - the type the RIFF section should have NonExistanceIsFormatError - whether it's a format error for the section not to exist Found - pointer to flag to say whether we got it or not (can be null) Returns: error flag Purpose: positions the file pointer after the riff header for a section defined in the master index ********************************************************************************************/ BOOL CMXImportFilter::GotoSectionFromIndex(INT32 MasterIndexEntry, DWORD PredictedRIFFType, BOOL NonExistanceIsFormatError, BOOL *Found, INT32 *Size) { if(Found != 0) (*Found) = FALSE; if(Data->MasterIndexEntries[MasterIndexEntry] == 0) { // if non-existance is a problem, do an error if(NonExistanceIsFormatError) CMXFORMATERROR(FALSE) // otherwise return true, but the found flag marks the problem return TRUE; } if(Found != 0) (*Found) = TRUE; TRACEUSER( "Ben", _T("index entry %d at %d\n"), MasterIndexEntry, Data->MasterIndexEntries[MasterIndexEntry]); Seek(Data->MasterIndexEntries[MasterIndexEntry]); RIFFck chdr; pFile->read(&chdr, sizeof(chdr)); if(chdr.ckID != PredictedRIFFType) CMXFORMATERROR(FALSE) if(Size != 0) (*Size) = chdr.ckSize; return TRUE; } /******************************************************************************************** > BOOL CMXImportFilter::SetAttributesFountain(Node *pNode, cmxiFillSpec *Fill) Author: Ben_Summers (Xara Group Ltd) Created: 15/08/96 Inputs: pointer to fill header Returns: error flag Purpose: sets graduated fill attributes ********************************************************************************************/ BOOL CMXImportFilter::SetAttributesFountain(Node *pNode, cmxiFillSpec *Fill) { // find the bounding box of this lovely node DocRect BBox; if(!GetCorelBBox((NodeRenderableBounded *)pNode, &BBox)) return FALSE; // find the minimum and maximim percentages along the grad fill line. DocColour *pStartColour = GetColourFromReference(Fill->Spec.Fountain.pColours->GetColourRefClosestToPosition(0)); DocColour *pEndColour = GetColourFromReference(Fill->Spec.Fountain.pColours->GetColourRefClosestToPosition(100)); // if it's a rainbow effect, work out whether an anti-clockwise thingy will be a // a normal or alt rainbow fill BOOL NormalRainbow = TRUE; if(Fill->Spec.Fountain.FillMode == cmxFILLMODE_HSB_CW || Fill->Spec.Fountain.FillMode == cmxFILLMODE_HSB_CCW) { // convert both colours to HSV to find their hue ColourContext *Conv = ColourContext::GetGlobalDefault(COLOURMODEL_HSVT); ColourHSVT StartC; ColourHSVT EndC; Conv->ConvertColour(pStartColour, (ColourGeneric *)&StartC); Conv->ConvertColour(pEndColour, (ColourGeneric *)&EndC); // compare the hues and work out a difference ColourValue Difference; if(EndC.Hue > StartC.Hue) { Difference = EndC.Hue - StartC.Hue; } else { Difference = 1.0 - (StartC.Hue - EndC.Hue); } // if the difference is more than 0.5 then it's a alt rainbow thingy if(Difference > fixed24(0.5)) NormalRainbow = FALSE; } // set the fade effect switch(Fill->Spec.Fountain.FillMode) { case cmxFILLMODE_RGB: case cmxFILLMODE_CUSTOM: default: SetFadeFillEffect(); break; case cmxFILLMODE_HSB_CCW: if(NormalRainbow) SetRainbowFillEffect(); else SetAltRainbowFillEffect(); break; case cmxFILLMODE_HSB_CW: if(NormalRainbow) SetAltRainbowFillEffect(); else SetRainbowFillEffect(); break; } // and finally, sort out the start and end points of the thingy switch(Fill->Spec.Fountain.Type) { case cmxFOUNTAINTYPE_LINEAR: if(!SetAttributesLinearGrad(pNode, Fill, BBox, pStartColour, pEndColour)) return FALSE; break; case cmxFOUNTAINTYPE_RADIAL: if(!SetAttributesRadialGrad(pNode, Fill, BBox, pStartColour, pEndColour)) return FALSE; break; case cmxFOUNTAINTYPE_SQUARE: if(!SetAttributesRadialGrad(pNode, Fill, BBox, pStartColour, pEndColour, TRUE)) return FALSE; break; case cmxFOUNTAINTYPE_CONICAL: if(!SetAttributesConicalGrad(pNode, Fill, BBox, pStartColour, pEndColour)) return FALSE; break; default: return SetNoFill(pNode); break; } return TRUE; } /******************************************************************************************** > BOOL CMXImportFilter::SetAttributesLinearGrad(Node *pNode, cmxiFillSpec *GFill, DocRect &BBox, DocColour *StartColour, DocColour *EndColour) Author: Ben_Summers (Xara Group Ltd) Created: 15/08/96 Inputs: grad fill defn and converted colours for the fill Returns: error flag Purpose: sets a linear graduated fill from fill definition in the file Copied from coreleps.cpp and modified SeeAlso: Filter ********************************************************************************************/ BOOL CMXImportFilter::SetAttributesLinearGrad(Node *pNode, cmxiFillSpec *GFill, DocRect &BBox, DocColour *StartColour, DocColour *EndColour) { // NB. this function is (hopefully!) over-complex and will be simplified. // However, it's like this atm so I can get my head around the weird maths Corel // forces us to use. // The desired start and end points of the grad fill 'arrow'. DocCoord Start, End; // Calculate width and height MILLIPOINT Width = BBox.Width(); MILLIPOINT Height = BBox.Height(); // bodge just in case of zero height or widths if(Width < 16) Width = 16; if(Height < 16) Height = 16; // Find centre of box DocCoord Centre; Centre.x = BBox.lo.x + (Width / 2); Centre.y = BBox.lo.y + (Height / 2); // Find total area of BBox double TotalArea = (double) Width * (double) Height; // Cope with angles > 180 BOOL Mirror = FALSE; double Angle = GFill->Spec.Fountain.Angle; if (Angle >= PI) { Angle -= PI; Mirror = TRUE; } else if (Angle < 0) { Angle += PI; Mirror = TRUE; } Angle += (PI/2); if (Angle >= PI) { Angle -= PI; } // get edge pad value INT32 EdgePad = GFill->Spec.Fountain.Padding; // Calculate tan of the angle - convert angle to radians first. double TanTheta; if (Angle == (PI/2)) { // Special case for horizontal grad fill arrow. // Make 0% padding first Start.x = BBox.lo.x; Start.y = Centre.y; End.x = BBox.hi.x; End.y = Centre.y; // Find out width of padding INT32 Padding = (Width * EdgePad) / 100; Start.x += Padding; End.x -= Padding; } else if (Angle == 0) { // Special case for vertical grad fill arrow. // Make 0% padding first Start.x = Centre.x; Start.y = BBox.lo.y; End.x = Centre.x; End.y = BBox.hi.y; // Find out width of padding INT32 Padding = (Height * EdgePad) / 100; Start.y += Padding; End.y -= Padding; } else { TanTheta = tan(Angle); // Find out what the maximum padding is that we can achieve using just triangles: // Find the maximum triangle width MILLIPOINT TriWidth = (MILLIPOINT) ((double) Height / TanTheta); // Limit it to sensible value if (TriWidth < 0) TriWidth = -TriWidth; if (TriWidth > Width) TriWidth = Width; // Find the maximum triangle width MILLIPOINT TriHeight = (MILLIPOINT) ((double) Width * TanTheta); // Limit it to sensible value if (TriHeight < 0) TriHeight = -TriHeight; if (TriHeight > Height) TriHeight = Height; // The 'c' values of the y = mx+c equation. MILLIPOINT StartC, EndC; // Work out the maximum percentage/edge padding this gives us // (50 because it's 100 / 2 because we want area of triangle, not rectangle). double Percentage = (50.0 * (double) TriWidth * (double) TriHeight) / TotalArea; INT32 Diff = 0; // Is this enough? if (((INT32) Percentage) >= EdgePad) { // Yes - calculate exactly how big the triangle needs to be. TriHeight = (MILLIPOINT) sqrt(ABS(((double) EdgePad * TotalArea * TanTheta) / 100.0)); TriWidth = (MILLIPOINT) ((double) TriHeight / TanTheta); if (TriWidth < 0) TriWidth = -TriWidth; ENSURE(TriWidth < Width, "Error in Corel Grad fill decoding logic"); } else { // How much percentage do we need to add with each rectangle? Percentage = (EdgePad - Percentage) / 2; // Handle the rectangle stuff. if (TriWidth == Width) { // Need to add rectangles to the top and bottom. Diff = (MILLIPOINT) ((Percentage * Height) / 100.0); } else { // Need to add rectangles to left and right Diff = (MILLIPOINT) ((Percentage * Width) / 100.0); Diff = (MILLIPOINT) (Diff / tan(PI - Angle)); Diff = ABS(Diff); } } // Work out the C value for the start line (c = y - mx) // (m = tan(angle) ) if (Angle == (PI/2)) { //ENSURE(FALSE, "90 degree angle found!"); } else if (Angle < (PI/2)) { StartC = (MILLIPOINT) (BBox.lo.y - ((BBox.hi.x - TriWidth) * TanTheta)); EndC = (MILLIPOINT) (BBox.hi.y - ((BBox.lo.x + TriWidth) * TanTheta)); } else { StartC = (MILLIPOINT) (BBox.lo.y - ((BBox.lo.x + TriWidth) * TanTheta)); EndC = (MILLIPOINT) (BBox.hi.y - ((BBox.hi.x - TriWidth) * TanTheta)); } // Add on difference for rectangles, if any. StartC += Diff; EndC -= Diff; // Work out m and c for the grad fill line. // We know m is -1/m of the triangle's hypotenuse. // c = roy - (rox/m) double FillM = -1.00 / TanTheta; MILLIPOINT FillC = (MILLIPOINT) (Centre.y - (Centre.x * FillM)); // Work out intersections: x = (c2 - c1) / (2m) Start.x = (MILLIPOINT) ( (FillC - StartC) / (TanTheta + (1.00 / TanTheta)) ); Start.y = (MILLIPOINT) ((FillM * Start.x) + FillC); End.x = (MILLIPOINT) ( (FillC - EndC) / (TanTheta + (1.00 / TanTheta)) ); End.y = (MILLIPOINT) ((FillM * End.x) + FillC); } if (Mirror) { // Swap the grid fill end-points over. DocCoord Tmp = Start; Start = End; End = Tmp; } // Set the fill type according to these calculations. return SetLinearFill(*StartColour, *EndColour, Start, End); } /******************************************************************************************** > BOOL CMXImportFilter::SetAttributesConicalGrad(Node *pNode, cmxiFillSpec *GFill, DocRect &BBox, DocColour *StartColour, DocColour *EndColour) Author: Ben_Summers (Xara Group Ltd) Created: 15/08/96 Inputs: grad fill defn and converted colours for the fill Returns: error flag Purpose: sets a conical graduated fill from fill definition in the file ********************************************************************************************/ BOOL CMXImportFilter::SetAttributesConicalGrad(Node *pNode, cmxiFillSpec *GFill, DocRect &BBox, DocColour *StartColour, DocColour *EndColour) { // The desired start and end points of the grad fill 'arrow'. DocCoord Start, End; // Calculate width and height MILLIPOINT Width = BBox.Width(); MILLIPOINT Height = BBox.Height(); // Start point is the centre given by Corel. // This centre is percentage offsets from the centre of the object, i.e. (0,0) is // the centre of the bounding box. Start.x = BBox.lo.x + (Width / 2); Start.y = BBox.lo.y + (Height / 2); Start.x += ((GFill->Spec.Fountain.Offset.x * Width) / 100); Start.y += ((GFill->Spec.Fountain.Offset.y * Height) / 100); // End point is start point + radius but takes into account the angle double Radius = Width / 2; double Theta = GFill->Spec.Fountain.Angle; // make the angle go anti-clockwise Theta = 0 - Theta; // rotate by PI / 2 Theta -= PI / 2; // angle can be negative, ensure it's positive while(Theta < 0) Theta += (2 * PI); // calculate the triangle double dx, dy; dx = Radius * sin(Theta); dy = Radius * cos(Theta); End.x = Start.x + (INT32)dx; End.y = Start.y + (INT32)dy; // Seems that we need to swap start and end colours... // Set the fill type according to these calculations. return SetConicalFill(*EndColour, *StartColour, Start, End); } /******************************************************************************************** > BOOL CMXImportFilter::SetAttributesRadialGrad(Node *pNode, cmxiFillSpec *GFill, DocRect &BBox, DocColour *StartColour, DocColour *EndColour) Author: Ben_Summers (Xara Group Ltd) Created: 15/08/96 Inputs: grad fill defn and converted colours for the fill Returns: error flag Purpose: sets a radial graduated fill from fill definition in the file ********************************************************************************************/ BOOL CMXImportFilter::SetAttributesRadialGrad(Node *pNode, cmxiFillSpec *GFill, DocRect &BBox, DocColour *StartColour, DocColour *EndColour, BOOL IsSquare) { // The desired start and end points of the grad fill 'arrow'. DocCoord Start, End, End2; // Calculate width and height MILLIPOINT Width = BBox.Width(); MILLIPOINT Height = BBox.Height(); // caluculate the source area // first, what's the diagonal length double dWidth = Width; double dHeight = Height; INT32 Diagonal = (INT32)sqrt(dWidth*dWidth + dHeight*dHeight); // and from that calculate area of the box containing the bit of the // bit of the circle in the bbox INT32 Edge = (Diagonal * (100 - (GFill->Spec.Fountain.Padding * 2))) / 100; // Start point is the centre given by Corel. // This centre is percentage offsets from the centre of the object, i.e. (0,0) is // the centre of the bounding box. DocCoord Centre = DocCoord(BBox.lo.x + (Width / 2), BBox.lo.y + (Height / 2)); INT32 OffX = (GFill->Spec.Fountain.Offset.x * Width) / 100; INT32 OffY = (GFill->Spec.Fountain.Offset.y * Height) / 100; Start.x = Centre.x + OffX; Start.y = Centre.y + OffY; // Find required radius of circle. double Radius = Edge / 2; // how far away is the centre of the fill from the centre of the bbox? double dOffX = OffX; double dOffY = OffY; double Dist = (INT32)sqrt(dOffX*dOffX + dOffY*dOffY); // and increase the radius by a bodge factor double BodgeFactor = 1 + (Dist / (double)(Diagonal / 2)); Radius *= BodgeFactor; // End point is start point + radius if(IsSquare) { // angled thingy for square fills double Theta = GFill->Spec.Fountain.Angle; End.x = Start.x + ((MILLIPOINT)(Radius * cos(Theta))); End.y = Start.y + ((MILLIPOINT)(Radius * sin(Theta))); End2.x = Start.x + ((MILLIPOINT)(Radius * cos(Theta + PI/2))); End2.y = Start.y + ((MILLIPOINT)(Radius * sin(Theta + PI/2))); } else { // simple thingy for radial fills End.x = Start.x + ((MILLIPOINT) Radius); End.y = Start.y; } // Seems that we need to swap start and end colours... // Set the fill type according to these calculations. BOOL ok; if(IsSquare) ok = SetSquareFill(*EndColour, *StartColour, Start, End, End2); else ok = SetRadialFill(*EndColour, *StartColour, Start, End); return ok; } /******************************************************************************************** > BOOL CMXImportFilter::SetAttributesBitmapFill(INT32 VectorReference, cmxiTiling *Tile) Author: Ben_Summers (Xara Group Ltd) Created: 19/08/96 Inputs: vector reference, tiling specification Returns: error flag Purpose: sets a bitmap fill from possible fillness in the CMX file... ********************************************************************************************/ BOOL CMXImportFilter::SetAttributesBitmapFill(Node *pNode, INT32 VectorReference, cmxiTiling *Tile) { // check the vector fill reference if(VectorReference <= 0 || VectorReference > Data->NumProcedures) return SetNoFill(pNode); // see if we can get a position for it INT32 Position = Data->Procedures[VectorReference-1].Position; if(Position <= 0) return SetNoFill(pNode); // store the position of the file for later INT32 StartFilePos = Tell(); // jump to the position of the vector fill procedure Seek(Position); // variable for getting the bitmap reference INT32 Jenny = -1; // and see if we can find a draw image command in there somewhere -- skip the header RIFFck hdr; pFile->read(&hdr, sizeof(hdr)); // I'm not going to check the type of this chunk as corel specify one type, and put // another in the file. Stupid documentionness BOOL EndProcLoc = Tell() + hdr.ckSize; // run through the loop... what follows is a load of instructions TRACEUSER( "Ben", _T("searching commands in vector fill procedure:\n")); BOOL Done = FALSE; while(Done == FALSE) { // look at a command // get the current file position INT32 CommandEndFilePos = Tell(); // read the command header CI_READDATA(CommandHeader, ch) // if the command thing is negative, we may need to fudge it if we're going 16 bitness if(ch.Code < 0 && !Is32Bit) ch.Code = 0 - ch.Code; CommandEndFilePos += ch.Size; // get the file pos of the end of the instruction // is this a bit of a DrawImage command? if(ch.Code == cmxINSTR_DrawImage) { // yep -- read in it's data CI_READDATA(DrawImage, di) if(di.ImageFileReference1 > 0 && di.ImageFileReference1 <= Data->NumRImages) { // found our bitmap image -- all done here Jenny = di.ImageFileReference1; break; // don't bother looking at anything else } } else if(ch.Code == cmxINSTR_EndSection) { // the section finished -- stop looking now... break; } // finally, check that we're in the right position in the file for the next command if(Tell() != CommandEndFilePos) Seek(CommandEndFilePos); if(CommandEndFilePos >= EndProcLoc) break; } TRACEUSER( "Ben", _T("finished searching commands, ref = %d\n"), Jenny); // go back to where we were in the file Seek(StartFilePos); // now, did we get a bitmap reference? if not, give up now if(Jenny <= 0) return SetNoFill(pNode); // right then, get the bounding box for the object as we'll need this later DocRect BBox; if(!GetCorelBBox((NodeRenderableBounded *)pNode, &BBox)) return FALSE; // get the coord things from the tiling info DocCoord StartPoint, EndPoint, EndPoint2; if(!GetTilingPoints(&BBox, Tile, &StartPoint, &EndPoint, &EndPoint2)) return FALSE; // locate the bitmap thingy -- we know that the reference we have is OK if(!Data->RImages[Jenny-1].IsRImage()) return SetNoFill(pNode); // can't do that either without one of the RImages KernelBitmap *pBitmap = Data->RImages[Jenny-1].GetBitmap(this); if(pBitmap == 0) return SetNoFill(pNode); // wasn't converted, oh dear // and set the fill (wow) return SetBitmapFill(pBitmap, StartPoint, EndPoint, EndPoint2); } /******************************************************************************************** > BOOL CMXImportFilter::SetAttributesTwoColourBitmapFill(Node *pNode, cmxiFillSpec *Fill) Author: Ben_Summers (Xara Group Ltd) Created: 26/08/96 Inputs: node, fill spec Returns: error flag Purpose: sets a two colour contone fill ********************************************************************************************/ BOOL CMXImportFilter::SetAttributesTwoColourBitmapFill(Node *pNode, cmxiFillSpec *pFill) { // right then, get the bounding box for the object as we'll need this later DocRect BBox; if(!GetCorelBBox((NodeRenderableBounded *)pNode, &BBox)) return FALSE; // get the coord things from the tiling info DocCoord StartPoint, EndPoint, EndPoint2; if(!GetTilingPoints(&BBox, &pFill->Spec.TwoColourBitmap.Tile, &StartPoint, &EndPoint, &EndPoint2)) return FALSE; // reverse the tiling thingys INT32 y0 = StartPoint.y; INT32 y1 = EndPoint2.y; StartPoint.y = y1; EndPoint2.y = y0; EndPoint.y = y1; // check the reference if(pFill->Spec.TwoColourBitmap.BitmapReference <= 0 || pFill->Spec.TwoColourBitmap.BitmapReference > Data->NumBitmaps) return SetNoFill(pNode); // couldn't find the bitmap, go away, your mother was a hamster and your father smelled of elderberries // get the colours DocColour *pStartColour = GetColourFromReference(pFill->Spec.TwoColourBitmap.ForeColourReference); DocColour *pEndColour = GetColourFromReference(pFill->Spec.TwoColourBitmap.BackColourReference); // locate the bitmap thingy -- we know that the reference we have is OK KernelBitmap *pBitmap = Data->Bitmaps[pFill->Spec.TwoColourBitmap.BitmapReference - 1].CreateColouredCopy(this, pStartColour, pEndColour); if(pBitmap == 0) return SetNoFill(pNode); // wasn't converted, oh dear // and set the fill and sod off back to the function we came from return SetBitmapFill(pBitmap, StartPoint, EndPoint, EndPoint2); } /******************************************************************************************** > BOOL CMXImportFilter::GetTilingPoints(DocRect *BBox, cmxiTiling *Tile, DocCoord *StartPoint, DocCoord *EndPoint, DocCoord *EndPoint2) Author: Ben_Summers (Xara Group Ltd) Created: 19/08/96 Inputs: bbox of object, tiling struct, points to fill in Returns: error flag Purpose: works out points for the fill of a tiled thingy ********************************************************************************************/ BOOL CMXImportFilter::GetTilingPoints(DocRect *BBox, cmxiTiling *Tile, DocCoord *StartPoint, DocCoord *EndPoint, DocCoord *EndPoint2) { if(Tile != 0) { // find the size of the tile INT32 SizeX = (INT32)(Tile->Width * Data->BaseScaleFactor); INT32 SizeY = (INT32)(Tile->Height * Data->BaseScaleFactor); // set to the top left + the first tile offset INT32 XPos = BBox->lo.x + ((SizeX * Tile->XOffset) / 100); INT32 YPos = BBox->hi.y - SizeY - ((SizeY * Tile->YOffset) / 100); // set the points *StartPoint = DocCoord(XPos, YPos); *EndPoint = DocCoord(XPos + SizeX, YPos); *EndPoint2 = DocCoord(XPos, YPos + SizeY); } else { // don't have a tile -- set things to fill the entire object *StartPoint = DocCoord(BBox->lo.x, BBox->lo.y); *EndPoint = DocCoord(BBox->hi.x, BBox->lo.y); *EndPoint2 = DocCoord(BBox->lo.x, BBox->hi.y); } return TRUE; } /******************************************************************************************** > BOOL CMXImportFilter::SkipToPreviewBitmap(CCLexFile * pFile) Author: Ben_Summers (Xara Group Ltd) Created: 28/08/96 Inputs: file Returns: True if it is a RIFF format file and found the preview bitmap. False if any problems are found or the file format is wrong. Purpose: sees if there is a preview bitmap to fiddle with -- this will actually work for any RIFF file thingy ********************************************************************************************/ BOOL CMXImportFilter::SkipToPreviewBitmap(CCLexFile * pFile) { // beginning pFile->seekIn(0); // get header RIFFFile_Header hdr; pFile->read(&hdr, sizeof(hdr)); // is it a form? if(hdr.CK.ckID != RIFFTYPE_RIFF) return FALSE; // no thingy INT32 FileEnd = hdr.CK.ckSize; INT32 where = sizeof(hdr); // OK, look at the first few chunks for(INT32 togo = 4; togo > 0 && where < FileEnd; togo--) { // read the chunk header RIFFck ck; pFile->read(&ck, sizeof(ck)); where += sizeof(ck) + ck.ckSize; // does this have the required type? if(ck.ckID == cmxRIFFCI_Thumbnail) { // yep, found it -- skip four bytes DWORD clipboardformat; pFile->read(&clipboardformat, sizeof(clipboardformat)); return TRUE; // found it } // next thingy pFile->seekIn(where); } // nothing found, mate return FALSE; } /******************************************************************************************** > void CMXImportFilter::SetNonTextAttributeIgnore() Author: Claude_Quemerais (Xara Group Ltd) Created: 21/08/96 Inputs: none Purpose: Set the non-text attribute to be ignored ********************************************************************************************/ void CMXImportFilter::SetNonTextAttributeIgnore() { CurrentAttrs[ATTR_BAD_ID].Ignore = TRUE; CurrentAttrs[ATTR_STROKECOLOUR].Ignore = TRUE; CurrentAttrs[ATTR_STROKETRANSP].Ignore = TRUE; CurrentAttrs[ATTR_FILLGEOMETRY].Ignore = TRUE; CurrentAttrs[ATTR_TRANSPFILLGEOMETRY].Ignore = TRUE; CurrentAttrs[ATTR_FILLMAPPING].Ignore = TRUE; CurrentAttrs[ATTR_TRANSPFILLMAPPING].Ignore = TRUE; CurrentAttrs[ATTR_FILLEFFECT].Ignore = TRUE; CurrentAttrs[ATTR_LINEWIDTH].Ignore = TRUE; CurrentAttrs[ATTR_WINDINGRULE].Ignore = TRUE; CurrentAttrs[ATTR_JOINTYPE].Ignore = TRUE; CurrentAttrs[ATTR_QUALITY].Ignore = TRUE; CurrentAttrs[ATTR_DASHPATTERN].Ignore = TRUE; CurrentAttrs[ATTR_STARTCAP].Ignore = TRUE; CurrentAttrs[ATTR_STARTARROW].Ignore = TRUE; CurrentAttrs[ATTR_ENDARROW].Ignore = TRUE; CurrentAttrs[ATTR_MITRELIMIT].Ignore = TRUE; CurrentAttrs[ATTR_TXTFONTTYPEFACE].Ignore = TRUE; CurrentAttrs[ATTR_OVERPRINTLINE].Ignore = TRUE; CurrentAttrs[ATTR_OVERPRINTFILL].Ignore = TRUE; CurrentAttrs[ATTR_PRINTONALLPLATES].Ignore = TRUE; CurrentAttrs[ATTR_FIRST_FREE_ID].Ignore = TRUE; CurrentAttrs[ATTR_MOULD].Ignore = TRUE; CurrentAttrs[ATTR_ENDCAP].Ignore = TRUE; } /******************************************************************************************** > void CMXImportFilter::SetNonTextAttributeNotIgnore() Author: Claude_Quemerais (Xara Group Ltd) Created: 21/08/96 Inputs: none Purpose: Set the non-text attribute not to be ignored ********************************************************************************************/ void CMXImportFilter::SetNonTextAttributeNotIgnore() { CurrentAttrs[ATTR_BAD_ID].Ignore = FALSE; CurrentAttrs[ATTR_STROKECOLOUR].Ignore = FALSE; CurrentAttrs[ATTR_STROKETRANSP].Ignore = FALSE; CurrentAttrs[ATTR_FILLGEOMETRY].Ignore = FALSE; CurrentAttrs[ATTR_TRANSPFILLGEOMETRY].Ignore = FALSE; CurrentAttrs[ATTR_FILLMAPPING].Ignore = FALSE; CurrentAttrs[ATTR_TRANSPFILLMAPPING].Ignore = FALSE; CurrentAttrs[ATTR_FILLEFFECT].Ignore = FALSE; CurrentAttrs[ATTR_LINEWIDTH].Ignore = FALSE; CurrentAttrs[ATTR_WINDINGRULE].Ignore = FALSE; CurrentAttrs[ATTR_JOINTYPE].Ignore = FALSE; CurrentAttrs[ATTR_QUALITY].Ignore = FALSE; CurrentAttrs[ATTR_DASHPATTERN].Ignore = FALSE; CurrentAttrs[ATTR_STARTCAP].Ignore = FALSE; CurrentAttrs[ATTR_STARTARROW].Ignore = FALSE; CurrentAttrs[ATTR_ENDARROW].Ignore = FALSE; CurrentAttrs[ATTR_MITRELIMIT].Ignore = FALSE; CurrentAttrs[ATTR_TXTFONTTYPEFACE].Ignore = FALSE; CurrentAttrs[ATTR_OVERPRINTLINE].Ignore = FALSE; CurrentAttrs[ATTR_OVERPRINTFILL].Ignore = FALSE; CurrentAttrs[ATTR_PRINTONALLPLATES].Ignore = FALSE; CurrentAttrs[ATTR_FIRST_FREE_ID].Ignore = FALSE; CurrentAttrs[ATTR_MOULD].Ignore = FALSE; CurrentAttrs[ATTR_ENDCAP].Ignore = FALSE; } /******************************************************************************************** > void CMXImportFilter::SetTextAttributeIgnore() Author: Claude_Quemerais (Xara Group Ltd) Created: 21/08/96 Inputs: none Purpose: Set the text attributes to be ignored ********************************************************************************************/ void CMXImportFilter::SetTextAttributeIgnore() { CurrentAttrs[ATTR_TXTFONTTYPEFACE].Ignore = TRUE; CurrentAttrs[ATTR_TXTBOLD].Ignore = TRUE; CurrentAttrs[ATTR_TXTITALIC].Ignore = TRUE; CurrentAttrs[ATTR_TXTASPECTRATIO].Ignore = TRUE; CurrentAttrs[ATTR_TXTJUSTIFICATION].Ignore = TRUE; CurrentAttrs[ATTR_TXTTRACKING].Ignore = TRUE; CurrentAttrs[ATTR_TXTUNDERLINE].Ignore = TRUE; CurrentAttrs[ATTR_TXTFONTSIZE].Ignore = TRUE; CurrentAttrs[ATTR_TXTSCRIPT].Ignore = TRUE; CurrentAttrs[ATTR_TXTBASELINE].Ignore = TRUE; CurrentAttrs[ATTR_TXTLINESPACE].Ignore = TRUE; } /******************************************************************************************** > void CMXImportFilter::SetTextAttributeNotIgnore() Author: Claude_Quemerais (Xara Group Ltd) Created: 21/08/96 Inputs: none Purpose: Set the text attribute not to be ignored ********************************************************************************************/ void CMXImportFilter::SetTextAttributeNotIgnore() { CurrentAttrs[ATTR_TXTFONTTYPEFACE].Ignore = FALSE; CurrentAttrs[ATTR_TXTBOLD].Ignore = FALSE; CurrentAttrs[ATTR_TXTITALIC].Ignore = FALSE; CurrentAttrs[ATTR_TXTASPECTRATIO].Ignore = FALSE; CurrentAttrs[ATTR_TXTJUSTIFICATION].Ignore = FALSE; CurrentAttrs[ATTR_TXTTRACKING].Ignore = FALSE; CurrentAttrs[ATTR_TXTUNDERLINE].Ignore = FALSE; CurrentAttrs[ATTR_TXTFONTSIZE].Ignore = FALSE; CurrentAttrs[ATTR_TXTSCRIPT].Ignore = FALSE; CurrentAttrs[ATTR_TXTBASELINE].Ignore = FALSE; CurrentAttrs[ATTR_TXTLINESPACE].Ignore = FALSE; }