// -------------------------------------------------------------------- // Ipe style sheet // -------------------------------------------------------------------- /* This file is part of the extensible drawing editor Ipe. Copyright (C) 1993-2004 Otfried Cheong Ipe is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. As a special exception, you have permission to link Ipe with the CGAL library and distribute executables, as long as you follow the requirements of the Gnu General Public License in regard to all of the software in the executable aside from CGAL. Ipe is 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 Ipe; if not, you can find it at "http://www.gnu.org/copyleft/gpl.html", or write to the Free Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ #include "ipestyle.h" #include "ipeobj.h" #include "ipepainter.h" // -------------------------------------------------------------------- /*! \class IpeStyleSheet \ingroup doc \brief A style sheet maps symbolic names to absolute values. Ipe documents can use symbolic attributes, such as 'normal', 'fat', or 'thin' for line thickness, or 'red', 'navy', 'turquoise' for color. The mapping to an absolute pen thickness or RGB value is performed by an IpeStyleSheet. IpeStyleSheet's cascade in the sense that a document can refer to several style sheets, which are arranged on a stack. The document owns the 'top-level stylesheet', which in turn owns the stack below it. A lookup is done from top to bottom, and returns as soon as a match is found. Ipe always appends the built-in "standard" style sheet at the bottom of the cascade. Style sheets are always included when the document is saved, so that an Ipe document is self-contained. The built-in style sheet is guaranteed not to change in ways that would change the meaning of existing Ipe files. In other words, new symbolic names may be defined, but no existing ones will be removed or mapped differently. Note that the style sheet depends (and includes a pointer to) the IpeRepository of the document. You cannot attach a style sheet to another document---the IpeRepository wouldn't make sense. To transfer a style sheet, it has to be externalized and internalized again with the new repository. For this reason, copying and assigning IpeStyleSheet is illegal. */ #define MASK 0x1fffffff // -------------------------------------------------------------------- //! The default constructor creates an empty style sheet. IpeStyleSheet::IpeStyleSheet(IpeRepository *rep) { iStandard = false; iCascade = 0; iRepository = rep; iTLMargin.iX = -1.0; // no margins set iShading.iType = IpeShading::ENone; } //! Destructor. IpeStyleSheet::~IpeStyleSheet() { delete iCascade; for (TemplateMap::iterator it = iTemplates.begin(); it != iTemplates.end(); ++it) delete it->second; } //! Copy constructor is disabled: it panics. IpeStyleSheet::IpeStyleSheet(const IpeStyleSheet &) { assert(false); } //! Assignment operator is disabled: it panics. IpeStyleSheet &IpeStyleSheet::operator=(const IpeStyleSheet &) { assert(false); return *this; } //! Return total LaTeX preamble (of the whole cascade). IpeString IpeStyleSheet::TotalPreamble() const { if (iCascade) return iCascade->TotalPreamble() + "\n" + iPreamble; else return iPreamble; } //! Set text margins. void IpeStyleSheet::SetMargins(const IpeVector &tl, const IpeVector &br) { iTLMargin = tl; iBRMargin = br; } //! Find text margins in style sheet cascade. void IpeStyleSheet::FindMargins(IpeVector &tl, IpeVector &br) const { if (iTLMargin.iX >= 0) { tl = iTLMargin; br = iBRMargin; } else if (iCascade) { iCascade->FindMargins(tl, br); } else { tl = IpeVector(0.0, 0.0); br = IpeVector(0.0, 0.0); } } //! Set the shading in this style sheet. void IpeStyleSheet::SetShading(const IpeShading &s) { iShading = s; } //! Find shading in style sheet cascade. IpeShading IpeStyleSheet::FindShading() const { if (iShading.iType != IpeShading::ENone || !iCascade) return iShading; else return iCascade->FindShading(); } //! Add font \a s to list of fonts with cmap generation. void IpeStyleSheet::AddCMap(IpeString s) { iCMaps.push_back(s); } void IpeStyleSheet::AllCMaps(std::vector &seq) const { if (iCascade) iCascade->AllCMaps(seq); std::copy(iCMaps.begin(), iCMaps.end(), std::back_inserter(seq)); } // -------------------------------------------------------------------- //! Add a template object. void IpeStyleSheet::AddTemplate(IpeAttribute name, const IpeObject *obj) { if (!name.IsSymbolic()) return; iTemplates[name.Index()] = obj; } //! Find a template object with given name. const IpeObject *IpeStyleSheet::FindTemplate(IpeAttribute attr) const { TemplateMap::const_iterator it = iTemplates.find(attr.Index()); if (it != iTemplates.end()) return it->second; else if (iCascade) return iCascade->FindTemplate(attr); else return 0; } //! Add an attribute. void IpeStyleSheet::Add(IpeAttribute name, IpeAttribute value) { if (!name.IsSymbolic()) return; iMap[name.iName & MASK] = value; } //! Find a symbolic attribute. /*! Returns \c null attribute if \a sym is \c null or cannot be found in the style sheet. In all other cases, returns an absolute attribute (one for which IsAbsolute() returns \c true). */ IpeAttribute IpeStyleSheet::Find(IpeAttribute sym) const { if (sym.IsNull() || sym.IsAbsolute()) return sym; Map::const_iterator it = iMap.find(sym.iName & MASK); if (it != iMap.end()) { return it->second; } else if (iCascade) { return iCascade->Find(sym); } else return IpeAttribute(); // null } // -------------------------------------------------------------------- //! Set the next level of the style sheet cascade. Takes ownership. /*! The previous contents is \e not destroyed. You have to take care of that. */ void IpeStyleSheet::SetCascade(IpeStyleSheet *sheet) { iCascade = sheet; } //! Return all symbolic names in the style sheet cascade. /*! Names are simply appended from top to bottom of the cascade. Names are inserted only once. */ void IpeStyleSheet::AllNames(IpeKind kind, IpeAttributeSeq &seq) const { if (kind == IpeAttribute::ETemplate) { for (TemplateMap::const_iterator it = iTemplates.begin(); it != iTemplates.end(); ++it) { IpeAttribute attr(kind, true, it->first); if (std::find(seq.begin(), seq.end(), attr) == seq.end()) seq.push_back(attr); } } else { uint kindMatch = (kind << IpeAttribute::EKindShift); for (Map::const_iterator it = iMap.begin(); it != iMap.end(); ++it) { if (uint(it->first & IpeAttribute::EKindMask) == kindMatch) { IpeAttribute attr(it->first | IpeAttribute::ESymbolic); if (std::find(seq.begin(), seq.end(), attr) == seq.end()) seq.push_back(attr); } } } if (iCascade) iCascade->AllNames(kind, seq); } void IpeStyleSheet::AllNames(IpeAttributeSeq *attr) const { for (int kind = 0; kind <= IpeAttribute::ETemplate; ++kind) { attr[kind].clear(); if (kind == IpeAttribute::EColor) { attr[kind].push_back(IpeAttribute::Void()); attr[kind].push_back(IpeAttribute::Black()); attr[kind].push_back(IpeAttribute::White()); } else if (kind == IpeAttribute::EDashStyle) attr[kind].push_back(IpeAttribute::Solid()); AllNames(IpeKind(kind), attr[kind]); } } // -------------------------------------------------------------------- static const char *scalarNames[IpeAttribute::ETemplate + 1] = { "linewidth", "marksize", "arrowsize", "grid", "angle", "dashstyle", "textsize", "textstretch", "color", "media", "template", }; //! Save style sheet in XML format. void IpeStyleSheet::SaveAsXml(IpeStream &stream) const { stream << "\n"; IpePainter painter(this); for (TemplateMap::const_iterator it = iTemplates.begin(); it != iTemplates.end(); ++it) { stream << "\n"; } IpeRepository *rep = iRepository; for (Map::const_iterator it = iMap.begin(); it != iMap.end(); ++it) { int kind = (it->first >> IpeAttribute::EKindShift); stream << "<" << scalarNames[kind] << " name=\"" << rep->String(IpeAttribute(it->first | IpeAttribute::ESymbolic)) << "\" value=\"" << rep->String(it->second) << "\"/>\n"; } if (!iPreamble.empty()) { stream << ""; stream.PutXmlString(iPreamble); stream << "\n"; } if (iTLMargin.iX >= 0) stream << "\n"; if (iShading.iType != IpeShading::ENone) { stream << "\n"; } stream << "\n"; } //! Save whole style sheet cascade in XML format. void IpeStyleSheet::SaveCascadeAsXml(IpeStream &stream) const { if (iCascade) iCascade->SaveCascadeAsXml(stream); if (!IsStandard()) SaveAsXml(stream); } // --------------------------------------------------------------------