// $Id: cdroutl.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============================ */ // *********Outlines for the CDR file filter /* */ #include "camtypes.h" #include "cdrfiltr.h" #include "nodepath.h" //#include "trans2d.h" - in camtypes.h [AUTOMATICALLY REMOVED] //#include "group.h" - in camtypes.h [AUTOMATICALLY REMOVED] #include "impcol.h" DECLARE_SOURCE("$Revision: 1282 $"); #define new CAM_DEBUG_NEW /******************************************************************************************** > BOOL CDRFilter::SetLineAttr(cdrfOffsetHeader *Object) Author: Ben_Summers (Xara Group Ltd) Created: 27 03 95 Inputs: address of line data *within* the main object data (loda chunk). Can be zero Returns: error flag Purpose: sets the line attributes SeeAlso: Filter ********************************************************************************************/ #define MAX_LINE_WIDTH (4*72000) BOOL CDRFilter::SetLineAttr(cdrfOffsetHeader *Object) { if(Version == CDRVERSION_3) { return SetLineAttr3(Object); } // find the reference... DWORD *pReference = (DWORD *)FindDataInObject(Object, cdrfOBJOFFSETTYPE_LINE); // if the pointer to the reference is zero, then the reference is probably // within some random style definition if(pReference == 0) { // OK, try and find a style number within the style reference WORD *pStyleReference = (WORD *)FindDataInObject(Object, cdrfOBJOFFSETTYPE_STYLE); if(pStyleReference != 0) { // OK, see if we can find a the style... cdrfStyle *pStyle; INT32 StyleSize; if((pStyle = (cdrfStyle *)Styles.Find(*pStyleReference, &StyleSize)) != 0) { // OK, got a style... now find a fill reference within it pReference = (DWORD *)FindDataInObject(&pStyle->Header, cdrfSTYLEOFFSETTYPE_OUTLINE); } } } cdrfOutline *Out; INT32 OutSize; if(pReference == 0 || (Out = (cdrfOutline *)Outlines.Find(*pReference, &OutSize)) == 0 || OutSize < sizeof(cdrfOutline)) { // if no outline data, or outline definitions couldn't be found, or outline // definition is too small, set up the default line attributes if(!SetLineWidth(cdrfOUTLINE_DEFAULT_WIDTH)) return FALSE; PColourCMYK cmyk; cmyk.Cyan = cmyk.Magenta = cmyk.Yellow = 0; cmyk.Key = 255; DocColour Col; Col.SetCMYKValue(&cmyk); if(!SetLineColour(Col)) return FALSE; return TRUE; } // check that this thingy should have an outline if((CDRDATA_WORD(Out->Flags) & cdrfOUTLINEFLAGS_NOOUTLINE) != 0) { if (!SetLineColour(DocColour(COLOUR_TRANS))) return FALSE; return TRUE; } // convert the line colour DocColour Col; ConvertColour(&Out->Colour, &Col); // find the line width INT32 LineWidth = CDRDATA_WORD(Out->LineThickness) * CDRCOORDS_TO_MILLIPOINTS; if(LineWidth > MAX_LINE_WIDTH) LineWidth = MAX_LINE_WIDTH; // find the join type JointType JType; switch(CDRDATA_WORD(Out->JoinStyle)) { case cdrfJOINSTYLE_SQUARE: JType = MitreJoin; break; case cdrfJOINSTYLE_ROUNDED: JType = RoundJoin; break; default: case cdrfJOINSTYLE_BEVEL: JType = BevelledJoin; break; } // set the cap style LineCapType CType; switch(CDRDATA_WORD(Out->EndStyle)) { case cdrfENDSTYLE_BUTT: CType = LineCapButt; break; case cdrfENDSTYLE_ROUNDED: CType = LineCapRound; break; default: case cdrfENDSTYLE_SQUARE: CType = LineCapSquare; break; } // set the dash pattern DashRec Dash; DashElement DashArray[cdrfMAX_DASH_ELEMENTS]; if(CDRDATA_WORD(Out->NDashSegments) > 0 && (CDRDATA_WORD(Out->Flags) & cdrfOUTLINEFLAGS_NODASH) == 0) { // set a dash pattern INT32 NSeg = CDRDATA_WORD(Out->NDashSegments); if(NSeg > cdrfMAX_DASH_ELEMENTS) NSeg = cdrfMAX_DASH_ELEMENTS; INT32 l; for(l = 0; l < NSeg; l++) { DashArray[l] = CDRDATA_WORD(Out->DashSegments[l]) * LineWidth; } Dash.Elements = NSeg; Dash.DashStart = 0; Dash.ElementData = DashArray; Dash.LineWidth = LineWidth; Dash.ScaleWithLineWidth = TRUE; Dash.DashID = -1; } else { // no dash pattern Dash = SD_SOLID; } // set the attributes if(!SetLineWidth(LineWidth) || !SetLineColour(Col) || !SetJoinType(JType) || !SetLineCap(CType) || !SetDashPattern(Dash)) return FALSE; // check for arrowheads if(CDRDATA_DWORD(Out->StartArrowReference) != 0 || CDRDATA_DWORD(Out->EndArrowReference) != 0) { // apply the attributes to the object // If not filled, then set the ignore bit on the fill attribute. if (ObjFilled == FALSE) CurrentAttrs[ATTR_FILLGEOMETRY].Ignore = TRUE; // Add attributes to the path, if they are different from the default... AttributeManager::ApplyBasedOnDefaults(pMadeNode, CurrentAttrs); // DeleteCurrentAttrs(); // SetUpCurrentAttrs(); // Enable the fill attribute again CurrentAttrs[ATTR_FILLGEOMETRY].Ignore = FALSE; // ensure that the attributes won't be applied again AttrsAlreadyApplied = TRUE; // apply arrow heads to the path AddArrowheadsToPath(CDRDATA_DWORD(Out->StartArrowReference), CDRDATA_DWORD(Out->EndArrowReference), &Col, LineWidth, CType, JType); } return TRUE; } /******************************************************************************************** > BOOL CDRFilter::SetLineAttr3(cdrfOffsetHeader *Object) Author: Ben_Summers (Xara Group Ltd) Created: 05/04/95 Inputs: address of line data *within* the main object data (loda chunk). Can be zero Returns: error flag Purpose: sets the line attributes for version 3 attributes SeeAlso: Filter ********************************************************************************************/ #define MAX_LINE_WIDTH (4*72000) BOOL CDRFilter::SetLineAttr3(cdrfOffsetHeader *Object) { cdrfOutlineV3 *Out = (cdrfOutlineV3 *)FindDataInObject(Object, cdrfOBJOFFSETTYPE_LINE); if(Out == 0) { // if no outline data, or outline definitions couldn't be found, or outline // definition is too small, set up the default line attributes if(!SetLineWidth(cdrfOUTLINE_DEFAULT_WIDTH)) return FALSE; PColourCMYK cmyk; cmyk.Cyan = cmyk.Magenta = cmyk.Yellow = 0; cmyk.Key = 255; DocColour Col; Col.SetCMYKValue(&cmyk); if(!SetLineColour(Col)) return FALSE; return TRUE; } // check that this thingy should have an outline if((Out->Flags & cdrfOUTLINEFLAGSV3_STROKED) == 0) { if (!SetLineColour(DocColour(COLOUR_TRANS))) return FALSE; return TRUE; } // convert the line colour DocColour Col; ConvertColour((cdrfColour *)&Out->Colour, &Col); // find the line width INT32 LineWidth = CDRDATA_WORD(Out->LineThickness) * CDRCOORDS_TO_MILLIPOINTS; if(LineWidth > MAX_LINE_WIDTH) LineWidth = MAX_LINE_WIDTH; // find the join type JointType JType; switch(CDRDATA_WORD(Out->JoinStyle)) { case cdrfJOINSTYLE_SQUARE: JType = MitreJoin; break; case cdrfJOINSTYLE_ROUNDED: JType = RoundJoin; break; default: case cdrfJOINSTYLE_BEVEL: JType = BevelledJoin; break; } // set the cap style LineCapType CType; switch(Out->EndStyle) // byte { case cdrfENDSTYLE_BUTT: CType = LineCapButt; break; case cdrfENDSTYLE_ROUNDED: CType = LineCapRound; break; default: case cdrfENDSTYLE_SQUARE: CType = LineCapSquare; break; } // set the dash pattern /* DashRec Dash; DashElement DashArray[cdrfMAX_DASH_ELEMENTS]; if((ObjectFlagsV3 & cdrfOBJFLAGSV3_NODASH) == 0 && Out->NDashSegments > 0) // byte { // set a dash pattern INT32 NSeg = CDRDATA_WORD(Out->NDashSegments); if(NSeg > cdrfMAX_DASH_ELEMENTS) NSeg = cdrfMAX_DASH_ELEMENTS; INT32 l; for(l = 0; l < NSeg; l++) { DashArray[l] = Out->DashSegments[l] * LineWidth; // byte } Dash.Elements = NSeg; Dash.DashStart = 0; Dash.ElementData = DashArray; Dash.LineWidth = LineWidth; Dash.ScaleWithLineWidth = TRUE; Dash.DashID = -1; } else { // no dash pattern Dash = SD_SOLID; } */ // set the attributes if(!SetLineWidth(LineWidth) || !SetLineColour(Col) || !SetJoinType(JType) || !SetLineCap(CType)/* || !SetDashPattern(Dash)*/) return FALSE; // check for arrowheads if(CDRDATA_DWORD(Out->StartArrowReference) != 0 || CDRDATA_DWORD(Out->EndArrowReference) != 0) { // apply the attributes to the object // If not filled, then set the ignore bit on the fill attribute. if (ObjFilled == FALSE) CurrentAttrs[ATTR_FILLGEOMETRY].Ignore = TRUE; // Add attributes to the path, if they are different from the default... AttributeManager::ApplyBasedOnDefaults(pMadeNode, CurrentAttrs); // Enable the fill attribute again CurrentAttrs[ATTR_FILLGEOMETRY].Ignore = FALSE; // ensure that the attributes won't be applied again AttrsAlreadyApplied = TRUE; // apply arrow heads to the path AddArrowheadsToPath(CDRDATA_DWORD(Out->StartArrowReference), CDRDATA_DWORD(Out->EndArrowReference), &Col, LineWidth, CType, JType); } return TRUE; } /******************************************************************************************** > BOOL CDRFilter::AddAttributesToArrowheadNode(NodeRenderableBounded *N, DocColour *Col, INT32 LineWidth, LineCapType Cap, JointType Join); Author: Ben_Summers (Xara Group Ltd) Created: 29 03 95 Inputs: node, colour and line width Returns: error flag Purpose: apply attributes to a arrow head node SeeAlso: Filter ********************************************************************************************/ BOOL CDRFilter::AddAttributesToArrowheadNode(NodeRenderableBounded *N, DocColour *Col, INT32 LineWidth, LineCapType Cap, JointType Join) { if(IS_A(N, NodeGroup)) { // go through all the members of the group setting their attributes Node *pNode; pNode = N->FindFirstChild(); while(pNode != 0) { if(IS_A(pNode, NodePath)) AddAttributesToArrowheadPath((NodePath *)pNode, Col, LineWidth, Cap, Join); pNode = pNode->FindNext(); } } else if(IS_A(N, NodePath)) { // simply apply the attributes to this path AddAttributesToArrowheadPath((NodePath *)N, Col, LineWidth, Cap, Join); } else { ERROR3("Something unexpected passed to AddAttributesToArrowheadNode"); } return TRUE; } /******************************************************************************************** > BOOL CDRFilter::AddAttributesToArrowheadPath(NodePath *P, DocColour *Col, INT32 LineWidth, LineCapType Cap, JointType Join); Author: Ben_Summers (Xara Group Ltd) Created: 29 03 95 Inputs: node, colour and line width Returns: error flag Purpose: apply attributes to a arrow head path SeeAlso: Filter ********************************************************************************************/ BOOL CDRFilter::AddAttributesToArrowheadPath(NodePath *P, DocColour *Col, INT32 LineWidth, LineCapType Cap, JointType Join) { if(P->InkPath.IsFilled) { // it's a filled path, so it need to be filled with the colour and have no line // colour SetPathFilled(TRUE); if(!SetLineColour(DocColour(COLOUR_TRANS)) || !SetFillColour(*Col)) return FALSE; } else { // it's not a filled colour, so don't fill it and stroke it with the line width and colour if(!SetLineColour(*Col) || !SetLineWidth(LineWidth) || !SetLineCap(Cap) || !SetJoinType(Join)) return FALSE; } // apply the attributes to the object // If not filled, then set the ignore bit on the fill attribute. // if (P->InkPath.IsFilled == FALSE) // CurrentAttrs[ATTR_FILLGEOMETRY].Ignore = TRUE; // else CurrentAttrs[ATTR_FILLGEOMETRY].Ignore = FALSE; // Add attributes to the path, if they are different from the default... BOOL Result = AttributeManager::ApplyBasedOnDefaults(P, CurrentAttrs); // DeleteCurrentAttrs(); // SetUpCurrentAttrs(); // Enable the fill attribute again CurrentAttrs[ATTR_FILLGEOMETRY].Ignore = FALSE; return Result; } /******************************************************************************************** > BOOL CDRFilter::DoesPathNeedArrowheads(NodePath *P) Author: Ben_Summers (Xara Group Ltd) Created: 29 03 95 Inputs: NodePath to search Returns: whether the path need some arrowheads Purpose: To find out whether a path need attributes SeeAlso: Filter ********************************************************************************************/ BOOL CDRFilter::DoesPathNeedArrowheads(NodePath *P) { // if the number of end points is greater than 0, then it needs some arrowheads if(P->InkPath.GetNumEndPoints() > 0) return TRUE; return FALSE; } /******************************************************************************************** > BOOL CDRFilter::TransformArrowhead(NodePath *N, DocCoord *Point, DocCoord *Other, BOOL Start, INT32 LineWidth, INT32 Distance) Author: Ben_Summers (Xara Group Ltd) Created: 29 03 95 Inputs: The path to transform, the This coordsinate (the coordinate to apply it to) and the previous coordinate (the one before or after it), whether it's a start one or not and the line width and the distance to shift it from the arrow definition. Returns: error flag Purpose: transform an arrowhead path to be the correct size and orientation SeeAlso: Filter ********************************************************************************************/ BOOL CDRFilter::TransformArrowhead(NodeRenderableBounded *N, DocCoord *Point, DocCoord *Other, BOOL Start, INT32 LineWidth, INT32 Distance) { // first of all, work out how much we need to scale this thingy by double Scale = (double)LineWidth / (double)cdrfARROWHEAD_LINEWIDTH; // work out the adj and opp side of the triangle we're going to rotate around double Adj = (double)Point->x - (double)Other->x; double Opp = (double)Point->y - (double)Other->y; // work out the hyp of the triangle double Hyp = sqrt((Adj * Adj) + (Opp * Opp)); // how far we need to move the arrowhead by double Move = Scale * (double)Distance; // calculate a matrix double a, b, c, d, e, f; if(Hyp == 0) { // default thing which shouldn't do too much hard - avoid div by zero a = Scale; b = 0; c = 0; d = Scale; e = Point->x + Move; f = Point->y; } else { double cosTheta = (Adj / Hyp); double sinTheta = (Opp / Hyp); a = Scale * cosTheta; b = Scale * sinTheta; c = 0 - (Scale * sinTheta); d = Scale * cosTheta; e = Point->x + (Move * cosTheta); f = Point->y + (Move * sinTheta); } // knock up a matrix Matrix M = Matrix(a, b, c, d, (INT32)e, (INT32)f); // need to flip it over? if(Start) { Matrix F = Matrix(1, 0, 0, -1, 0, 0); M = F * M; } // transform the node Trans2DMatrix Trans(M); N->Transform(Trans); return TRUE; } /******************************************************************************************** > BOOL CDRFilter::AddArrowheadsToPath(DWORD StartArrow, DWORD EndArrow, DocColour *Col, INT32 LineWidth, LineCapType Cap, JointType Join) Author: Ben_Summers (Xara Group Ltd) Created: 29 03 95 Inputs: Returns: error flag Purpose: adds arrowheads to a path. Changes pMadeNode from a path to a group SeeAlso: Filter ********************************************************************************************/ BOOL CDRFilter::AddArrowheadsToPath(DWORD StartArrow, DWORD EndArrow, DocColour *Col, INT32 LineWidth, LineCapType Cap, JointType Join) { if(!IS_A(pMadeNode, NodePath)) return TRUE; // don't apply arrowheads to non path things NodePath *pPath = (NodePath *)pMadeNode; // check that the path actaully needs some arrow heads if(!DoesPathNeedArrowheads(pPath) || (StartArrow == 0 && EndArrow == 0)) return TRUE; // get pointers to nodes of the arrow heads to copy NodeRenderableBounded *StartA = 0; NodeRenderableBounded *EndA = 0; INT32 StartDistance; INT32 EndDistance; if(StartArrow != 0) { BOOL NotPresent; StartA = Arrowheads.GetConvertedNode(StartArrow, &StartDistance, &NotPresent); if(StartA == 0 && NotPresent == FALSE) return FALSE; // error occurred } if(EndArrow != 0) { BOOL NotPresent; EndA = Arrowheads.GetConvertedNode(EndArrow, &EndDistance, &NotPresent); if(EndA == 0 && NotPresent == FALSE) return FALSE; // error occurred } // check that enough arrowheads were found to do something with if(StartA == 0 && EndA == 0) return TRUE; // get rid of any dash patterns DashRec NoDash; NoDash = SD_SOLID; SetDashPattern(NoDash); // get some info about the path DocCoord *Coords = pPath->InkPath.GetCoordArray(); PathVerb *Verbs = pPath->InkPath.GetVerbArray(); INT32 NCoords = pPath->InkPath.GetNumCoords(); INT32 SubPathStart = -1; // the number of the coord the sub path starts on INT32 SubPathEnd = -1; // the number of the coord the sub path ends on // set up a pointers for the nodes we're about to create - it points to the made // at first, but shuffles on as nodes are copied. You see, we have to copy nodes which // are attached to something NodeRenderableBounded *ThisNode = pPath; // work out the trim distances double StartTrimDistance; double EndTrimDistance; if(StartA != 0) StartTrimDistance = ((double)LineWidth / (double)cdrfARROWHEAD_LINEWIDTH) * (double)StartDistance; if(EndA != 0) EndTrimDistance = ((double)LineWidth / (double)cdrfARROWHEAD_LINEWIDTH) * (double)EndDistance; // run through all the coords INT32 c; for(c = 0; c < NCoords; c++) { UINT32 Verb = Verbs[c] & ~PT_CLOSEFIGURE; BOOL CloseHere = ((Verbs[c] & PT_CLOSEFIGURE) != 0)?TRUE:FALSE; // if the verb is a close figure, this sub path is close, so we can't add arrowheads // to it. Invalidate the path start to avoid getting any arrowheads if(CloseHere) SubPathStart = -1; // if we get a new moveto and the sub path start is valid then we have a applying arrowheads // situation if((Verb == PT_MOVETO || ((c == (NCoords - 1)) && !CloseHere)) && c != 0) { // set the sub path end if(Verb == PT_MOVETO) { // if it's a moveto, then the sub path end was at the previous coord SubPathEnd = c - 1; } else { // if it wasn't, then it's this one (end of path) SubPathEnd = c; } } // have we got a valid start path marker? if(SubPathStart != -1 && SubPathEnd != -1) { // OK, now we need to trim the sub path if(StartA != 0) { // trim the start of the path if the next element is a line if((Verbs[SubPathStart + 1] & ~PT_CLOSEFIGURE) == PT_LINETO) { double dx, dy; dx = Coords[SubPathStart + 1].x - Coords[SubPathStart].x; dy = Coords[SubPathStart + 1].y - Coords[SubPathStart].y; double len = sqrt((dx * dx) + (dy * dy)); // if the resulting path line leaves enough room, trim it if((len - StartTrimDistance) >= 32.0) { double factor = StartTrimDistance / len; dx *= factor; Coords[SubPathStart].x += (INT32)dx; dy *= factor; Coords[SubPathStart].y += (INT32)dy; } } // now pop an arrowhead on the resulting point // first, make a copy of our node if(!StartA->CopyNode(ThisNode, NEXT)) return FALSE; // error! ThisNode = (NodeRenderableBounded *)ThisNode->FindNext(); ERROR3IF(ThisNode == 0, "CopyNode returned a zero path but didn't complain"); ERROR3IF(ThisNode->IsKindOf(CC_RUNTIME_CLASS(NodeRenderableBounded)) == FALSE, "copied thing isn't a renderable bounded node"); // transform it if(!TransformArrowhead(ThisNode, &Coords[SubPathStart], &Coords[SubPathStart + 1], TRUE, LineWidth, StartDistance)) return FALSE; // and apply attributes to it if(!AddAttributesToArrowheadNode(ThisNode, Col, LineWidth, Cap, Join)) return FALSE; } // and the same for the end path if(EndA != 0) { if((Verbs[SubPathEnd] & ~PT_CLOSEFIGURE) == PT_LINETO) { double dx, dy; dx = Coords[SubPathEnd].x - Coords[SubPathEnd - 1].x; dy = Coords[SubPathEnd].y - Coords[SubPathEnd - 1].y; double len = sqrt((dx * dx) + (dy * dy)); // if the resulting path line leaves enough room, trim it if((len - EndTrimDistance) >= 32.0) { double factor = EndTrimDistance / len; dx *= factor; Coords[SubPathEnd].x += (INT32)dx; dy *= factor; Coords[SubPathEnd].y += (INT32)dy; } } // now pop an arrowhead on the resulting point // first, make a copy of our node if(!EndA->CopyNode(ThisNode, NEXT)) return FALSE; // error! ThisNode = (NodeRenderableBounded *)ThisNode->FindNext(); ERROR3IF(ThisNode == 0, "CopyNode returned a zero path but didn't complain"); ERROR3IF(ThisNode->IsKindOf(CC_RUNTIME_CLASS(NodeRenderableBounded)) == FALSE, "copied thing isn't a renderable bounded node"); // transform it if(!TransformArrowhead(ThisNode, &Coords[SubPathEnd], &Coords[SubPathEnd - 1], FALSE, LineWidth, EndDistance)) return FALSE; // and apply attributes to it if(!AddAttributesToArrowheadNode(ThisNode, Col, LineWidth, Cap, Join)) return FALSE; } } // set the new sub path start marker and invalidate the end point marker if(Verb == PT_MOVETO) { SubPathStart = c; SubPathEnd = -1; } } // ensure that the bounding rectangle is updated pPath->InvalidateBoundingRect(); // get a new group node NodeGroup *pGroup = new NodeGroup; if(pGroup == 0) return FALSE; // attach everything to it pMadeNode->InsertChainSimple(pGroup, FIRSTCHILD); // and make the made node the group pMadeNode = pGroup; return TRUE; } /******************************************************************************************** > BOOL CDRArrowheadStore::AddChunkToStore(RIFFFile *RIFF) Author: Ben_Summers (Xara Group Ltd) Created: 22 03 95 Inputs: A RIFFFile object Returns: error flag Purpose: Stores arrowhead chunks for the CDRFilter class SeeAlso: CDRFilter, CDRAttributeStore ********************************************************************************************/ BOOL CDRArrowheadStore::AddChunkToStore(RIFFFile *RIFF) { if(RIFF->GetObjType() != RIFFOBJECTTYPE_CHUNK) { ERROR2(FALSE, "CDRAttributeStore::AddChunkToStore called without a chunk in the RIFFFile"); } // get a new item obect CDRArrowheadStoredItem *Item = new CDRArrowheadStoredItem; if(Item == 0) return FALSE; // get the data of the RIFF chunk if(!Item->Aquire(RIFF)) { delete Item; return FALSE; } Item->Size = RIFF->GetObjSize(); // and add the new item to the list AddTail(Item); return TRUE; } /******************************************************************************************** > NodeRenderableBounded *CDRArrowheadStore::GetConvertedNode(DWORD Reference, INT32 *Distance, BOOL *NotPresent) Author: Ben_Summers (Xara Group Ltd) Created: 29 03 95 Inputs: a DWORD reference Returns: node pointer or 0 for an error Purpose: gets a converted node for the arrow head which should be copied and transformed. Converted nodes are cached. If *NotPresent is TRUE when 0 is returned then the arrowhead couldn't be found rather than an error occuring SeeAlso: CDRFilter, CDRAttributeStore ********************************************************************************************/ #define GCN_LASTSUB_NONE 0 #define GCN_LASTSUB_CLOSED 1 #define GCN_LASTSUB_OPEN 2 NodeRenderableBounded *CDRArrowheadStore::GetConvertedNode(DWORD Reference, INT32 *Distance, BOOL *NotPresent) { // set up the not present thingy *NotPresent = FALSE; // find the correct chunk CDRArrowheadStoredItem *Item; INT32 Size; if(IsEmpty()) return 0; // no items in the list Item = (CDRArrowheadStoredItem *)GetHead(); // scan though the list looking for the reference while(Item != 0) { if(CDRDATA_DWORD(*((DWORD *)(Item->Block))) == Reference) { Size = Item->Size; break; } Item = (CDRArrowheadStoredItem *)GetNext(Item); } // did we find a chunk? if(Item == 0) { *NotPresent = TRUE; return 0; } // locate the coordinates cdrfArrowhead *Arrow = (cdrfArrowhead *)Item->Block; cdrfCoord *Coords = (cdrfCoord *)(Item->Block + CDRDATA_WORD(Arrow->CoordsOffset) + cdrfARROWHEAD_COORDOFF_CORRECT); // store the distance from the definitons *Distance = CDRDATA_SWORD(Arrow->Distance); // check to see if a cached pointer is available if(Item->pNode != 0) return Item->pNode; // OK, convert that arrowhead // this is not particularly pleasant. We need to scan though the coords creating a path, // each sub path must be a seperate path unless the next subpath has the same closedness // as the previous one, in which case it's a sub path. // this is because closed sub paths have different attributes to open ones. It's a nasty // system, and rather overcomplicated, but never mind. INT32 ThisType = GCN_LASTSUB_NONE; // the type of this subpath INT32 LastType = GCN_LASTSUB_NONE; // the type of the last subpath NodePath *FirstPath = 0; // the first path in my set of paths NodePath *LastPath = 0; // the last path in my set of paths NodePath *ThisPath = 0; // the path I'm currently working on // check that the first node type is a move to avoid stuffing up my next bit if((Arrow->NodeTypes[0] & cdrfPATHCOORDTYPE_MASK) != cdrfPATHCOORDTYPE_MOVE) { // for now, if the first element isn't a move, pretend that it doesn't exist *NotPresent = TRUE; return 0; } INT32 CoordType; INT32 l; UINT32 Control1 = 0; // number of first control point UINT32 Control2 = 0; // of second DocCoord co, cn1, cn2; // coordinates PathFlags Flags; INT32 NNodes = CDRDATA_WORD(Arrow->NNodes); BOOL NeedMoveTo = TRUE; // convert all the coordinates for(l = 0; l < NNodes; l++) { CoordType = Arrow->NodeTypes[l] & cdrfPATHCOORDTYPE_MASK; if(CoordType == cdrfPATHCOORDTYPE_MOVE || l == 0) { // start a new path! LastType = ThisType; // first of all, what type of path is this? if((Arrow->NodeTypes[l] & cdrfPATHCOORDATTR_CLOSE) != 0) ThisType = GCN_LASTSUB_CLOSED; else ThisType = GCN_LASTSUB_OPEN; // OK, do we need to start a new path? if(ThisType != LastType) { // yep, attach the last one we did if(ThisPath != 0) { // check that the path is OK if(!ThisPath->InkPath.EnsureValid()) { // no, it's completely knackered delete ThisPath; ThisPath = 0; } else { // finish off the path ThisPath->InvalidateBoundingRect(); if(FirstPath == 0) FirstPath = ThisPath; if(LastPath != 0) ThisPath->AttachNode(LastPath, NEXT); LastPath = ThisPath; ThisPath = 0; } } // get a new path ThisPath = new NodePath; if(ThisPath == 0) return 0; // no path created. A bit of a pity // set it up ready for conversion if(!ThisPath->SetUpPath()) return 0; ThisPath->InkPath.FindStartOfPath(); // set whether it's filled or not if(ThisType == GCN_LASTSUB_CLOSED) { ThisPath->InkPath.IsFilled = TRUE; ThisPath->InkPath.IsStroked = FALSE; } else { ThisPath->InkPath.IsFilled = FALSE; ThisPath->InkPath.IsStroked = TRUE; } } } co.x = CDRDATA_SWORD(Coords[l].X); co.y = CDRDATA_SWORD(Coords[l].Y); // ensure we get all the move tos we need. if(NeedMoveTo && CoordType != cdrfPATHCOORDTYPE_MOVE) { if(!ThisPath->InkPath.InsertMoveTo(co)) return 0; } NeedMoveTo = FALSE; // convert the coordinates switch(CoordType) { case cdrfPATHCOORDTYPE_MOVE: // add a move to this path if(!ThisPath->InkPath.InsertMoveTo(co)) return 0; break; case cdrfPATHCOORDTYPE_LINETO: // add a line to this coord to the path if(!ThisPath->InkPath.InsertLineTo(co)) return 0; break; case cdrfPATHCOORDTYPE_CURVE: // check we have some control points for this curve // a control point cannot be the first coord, so it's OK to check against 0 if(Control1 == 0 || Control2 == 0) { TRACEUSER( "Ben", _T("No control points for curve element\n")); return 0; } // convert the control points cn1.x = CDRDATA_SWORD(Coords[Control1].X); cn1.y = CDRDATA_SWORD(Coords[Control1].Y); cn2.x = CDRDATA_SWORD(Coords[Control2].X); cn2.y = CDRDATA_SWORD(Coords[Control2].Y); // create the curve Flags.IsSelected = FALSE; Flags.IsSmooth = Flags.IsRotate = ((Arrow->NodeTypes[l] & cdrfPATHCOORDATTR_SMOOTH) != 0)?TRUE:FALSE; Flags.IsEndPoint; // insert it into the path if(!ThisPath->InkPath.InsertCurveTo(cn1, cn2, co, &Flags)) return 0; break; case cdrfPATHCOORDTYPE_CONTROL: // shuffle the control points we've already got and add the new one Control1 = Control2; Control2 = l; break; default: break; } // is this a close subpath situtation? if((Arrow->NodeTypes[l] & cdrfPATHCOORDATTR_CLOSE) != 0) { // close the sub path if(CoordType != cdrfPATHCOORDTYPE_MOVE) { if(!ThisPath->InkPath.CloseSubPath()) return 0; // check the next coord type NeedMoveTo = TRUE; } } } // finish off the last path ThisPath->InvalidateBoundingRect(); // finish off this path we're doing if(ThisPath != 0) { if(FirstPath == 0) FirstPath = ThisPath; if(LastPath != 0) ThisPath->AttachNode(LastPath, NEXT); LastPath = ThisPath; ThisPath = 0; } // make up a nice group if there's more than one path NodeRenderableBounded *ToInsert = 0; if(FirstPath == LastPath) { ToInsert = FirstPath; } else { ToInsert = new NodeGroup; if(ToInsert == 0) return 0; FirstPath->InsertChainSimple(ToInsert, FIRSTCHILD); } Item->pNode = ToInsert; return ToInsert; } /******************************************************************************************** > CDRArrowheadStoredItem::CDRArrowheadStoredItem(void) Author: Ben_Summers (Xara Group Ltd) Created: 29 03 95 Inputs: Returns: Purpose: Constructor SeeAlso: CDRAttributeStore, CDRAttributeStoredItem ********************************************************************************************/ CDRArrowheadStoredItem::CDRArrowheadStoredItem(void) { pNode = 0; } /******************************************************************************************** > CDRArrowheadStoredItem::~CDRArrowheadStoredItem(void) Author: Ben_Summers (Xara Group Ltd) Created: 29 03 95 Inputs: Returns: Purpose: Destructor SeeAlso: CDRAttributeStore, CDRAttributeStoredItem ********************************************************************************************/ CDRArrowheadStoredItem::~CDRArrowheadStoredItem(void) { if(pNode != 0) pNode->CascadeDelete(); delete pNode; } /*******************************************************************************************/ typedef struct { BYTE C, M, Y, K; TCHAR *Name; } StandardCDRColour; static StandardCDRColour StandardCDRColours[] = { {0, 0, 0, 100, "Black"}, {0, 0, 0, 90, "90% Black"}, {0, 0, 0, 80, "80% Black"}, {0, 0, 0, 70, "70% Black"}, {0, 0, 0, 60, "60% Black"}, {0, 0, 0, 50, "50% Black"}, {0, 0, 0, 40, "40% Black"}, {0, 0, 0, 30, "30% Black"}, {0, 0, 0, 20, "20% Black"}, {0, 0, 0, 10, "10% Black"}, {0, 0, 0, 0, "White"}, {100, 100, 0, 0, "Blue"}, {100, 0, 0, 0, "Cyan"}, {100, 0, 100, 0, "Green"}, {0, 0, 100, 0, "Yellow"}, {0, 100, 100, 0, "Red"}, {0, 100, 0, 0, "Magenta"}, {20, 80, 0, 20, "Purple"}, {0, 60, 100, 0, "Orange"}, {0, 40, 20, 0, "Pink"}, {0, 20, 20, 60, "Dark Brown"}, {20, 20, 0, 0, "Powder Blue"}, {40, 40, 0, 0, "Pastel Blue"}, {60, 40, 0, 0, "Baby Blue"}, {60, 60, 0, 0, "Electric Blue"}, {40, 40, 0, 20, "Twilight Blue"}, {60, 40, 0, 40, "Navy Blue"}, {40, 40, 0, 60, "Deep Navy Blue"}, {40, 20, 0, 40, "Desert Blue"}, {100, 20, 0, 0, "Sky Blue"}, {40, 0, 0, 0, "Ice Blue"}, {20, 0, 0, 20, "Light BlueGreen"}, {20, 0, 0, 40, "Ocean Green"}, {20, 0, 0, 60, "Moss Green"}, {20, 0, 0, 80, "Dark Green"}, {40, 0, 20, 60, "Forest Green"}, {60, 0, 40, 40, "Grass Green"}, {40, 0, 20, 40, "Kentucky Green"}, {60, 0, 40, 20, "Light Green"}, {60, 0, 60, 20, "Spring Green"}, {60, 0, 20, 0, "Turquoise"}, {60, 0, 20, 20, "Sea Green"}, {20, 0, 20, 20, "Faded Green"}, {20, 0, 20, 0, "Ghost Green"}, {40, 0, 40, 0, "Mint Green"}, {20, 0, 20, 40, "Army Green"}, {20, 0, 40, 40, "Avocado Green"}, {20, 0, 60, 20, "Martian Green"}, {20, 0, 40, 20, "Dull Green"}, {40, 0, 100, 0, "Chartreuse"}, {20, 0, 60, 0, "Moon Green"}, {0, 0, 20, 80, "Murky Green"}, {0, 0, 20, 60, "Olive Drab"}, {0, 0, 20, 40, "Khaki"}, {0, 0, 40, 40, "Olive"}, {0, 0, 60, 20, "Banana Yellow"}, {0, 0, 60, 0, "Light Yellow"}, {0, 0, 40, 0, "Chalk"}, {0, 0, 20, 0, "Pale Yellow"}, {0, 20, 40, 40, "Brown"}, {0, 40, 60, 20, "Red Brown"}, {0, 20, 60, 20, "Gold"}, {0, 60, 80, 0, "Autumn Orange"}, {0, 40, 80, 0, "Light Orange"}, {0, 40, 60, 0, "Peach"}, {0, 20, 100, 0, "Deep Yellow"}, {0, 20, 40, 0, "Sand"}, {0, 20, 40, 60, "Walnut"}, {0, 60, 60, 40, "Ruby Red"}, {0, 60, 80, 20, "Brick Red"}, {0, 60, 60, 0, "Tropical Pink"}, {0, 40, 40, 0, "Soft Pink"}, {0, 20, 20, 0, "Faded Pink"}, {0, 40, 20, 40, "Crimson"}, {0, 60, 40, 20, "Regal Red"}, {0, 60, 20, 20, "Deep Rose"}, {0, 100, 60, 0, "Neon Red"}, {0, 60, 40, 0, "Deep Pink"}, {0, 80, 40, 0, "Hot Pink"}, {0, 40, 20, 20, "Dusty Rose"}, {0, 40, 0, 60, "Plum"}, {0, 60, 0, 40, "Deep Violet"}, {0, 40, 0, 0, "Light Violet"}, {0, 40, 0, 20, "Violet"}, {0, 20, 0, 40, "Dusty Plum"}, {0, 20, 0, 20, "Pale Purple"}, {20, 60, 0, 20, "Majestic Purple"}, {20, 80, 0, 0, "Neon Purple"}, {20, 60, 0, 0, "Light Purple"}, {20, 40, 0, 20, "Twilight Violet"}, {20, 40, 0, 0, "Easter Purple"}, {20, 40, 0, 60, "Deep Purple"}, {20, 40, 0, 40, "Grape"}, {40, 60, 0, 0, "Blue Violet"}, {40, 100, 0, 0, "Blue Purple"}, {40, 80, 0, 20, "Deep River"}, {60, 80, 0, 0, "Deep Azure"}, {40, 60, 0, 40, "Storm Blue"}, {60, 80, 0, 20, "Deep Blue"}, {100, 100, 100, 100, "100C100M100Y100K"} }; /******************************************************************************************** > BOOL CDRFilter::AddStandardColourSet(Document *pDocument) Author: Ben_Summers (Xara Group Ltd) Created: 05/06/95 Inputs: Returns: Purpose: Adds the standard set of colours to a Corel file ********************************************************************************************/ BOOL CDRFilter::AddStandardColourSet(Document *pDocument) { ColourListComponent *pColours = 0; DocComponent *pComponent = pDocument->EnumerateDocComponents(NULL); while (pComponent != NULL) { // If this is the colour component, remember it if (pComponent->GetRuntimeClass() == CC_RUNTIME_CLASS(ColourListComponent)) { pColours = (ColourListComponent *) pComponent; break; } // Look for next doc component pComponent = pDocument->EnumerateDocComponents(pComponent); } if(pColours == 0) return TRUE; // can't be bothered to complain nicely about this very unlikely error // get an importedcolours object ImportedColours *pNewColours = new ImportedColours(pColours, FALSE); if(pNewColours == 0 || !pNewColours->Init()) return FALSE; BOOL ok = TRUE; // add colours to it... ColourCMYK NewColour; for(INT32 l = 0; l < (sizeof(StandardCDRColours) / sizeof(StandardCDRColour)); l++) { NewColour.Cyan = ((double)StandardCDRColours[l].C) / 100; NewColour.Magenta = ((double)StandardCDRColours[l].M) / 100; NewColour.Yellow = ((double)StandardCDRColours[l].Y) / 100; NewColour.Key = ((double)StandardCDRColours[l].K) / 100; String_64 ColNameS(StandardCDRColours[l].Name); if(!pNewColours->AddColour(&ColNameS, &NewColour)) { ok = FALSE; break; } } // add the colours to the document if(ok) { pNewColours->AddColoursToDocument(); } else { pNewColours->DestroyColours(); } // delete the imported colours object delete pNewColours; return ok; }