// -------------------------------------------------------------------- // The Text object. // -------------------------------------------------------------------- /* 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 "ipetext.h" #include "ipevisitor.h" #include "ipepainter.h" /*! \class IpeText \ingroup obj \brief The text object. The text object is a tricky Ipe object. It has a Latex-source representation, which needs to be translated into PDF by Pdflatex before it can be saved as PDF. There are two main types of text objects, labels and minipages. The IsMiniPage() method tells you which. The title and textbox types are variations on these that indicate the function of the text object in a document---that is useful to reposition text objects after the style sheet has been changed. Use Type() to determine the precise type of a text object. The dimensions of a text object are given by Width(), Height(), and Depth(). They are recomputed by Ipe when running LaTeX, with the exception of the width for minipage objects (whose width is fixed). Before Latex has been run, the dimensions are not reliable. The position of the reference point relative to the text box is given by VerticalAlignment() and HorizontalAlignment(). Text can be either transformable, or fixed. In the first case, the Matrix() is applied to the complete text box, in the second case, the Matrix() is only applied to the reference point Position(), and the text box remains fixed relative to the reference point. The Text() must be a legal LaTeX fragment that can be interpreted by LaTeX inside \c \\hbox, possibly using the macros or packages defined in the preamble. */ //! Create from XML stream. IpeText::IpeText(IpeRepository *rep, const IpeXmlAttributes &attr, IpeString data) : IpeObject(rep, attr) { iXForm = 0; iText = data; // parse position IpeLex st(attr["pos"]); st >> iPos.iX >> iPos.iY; iTransformable = false; IpeString str; if (attr.Has("size", str)) iSize = rep->MakeScalar(IpeAttribute::ETextSize, str); if (attr.Has("transformable", str) && str == "yes") iTransformable = true; iType = ELabel; bool hasType = attr.Has("type", str); if (hasType) { if (str == "minipage") iType = EMinipage; else if (str == "title") iType = ETitle; else if (str == "textbox") iType = ETextbox; } iWidth = 10.0; if (attr.Has("width", str)) { iWidth = IpeLex(str).GetDouble(); if (!hasType) iType = EMinipage; } #if 1 // obsolete - remove later if (attr.Has("textstretch", str)) { iWidth *= IpeLex(str).GetDouble(); } #endif iHeight = 10.0; if (attr.Has("height", str)) iHeight = IpeLex(str).GetDouble(); iDepth = 0.0; if (attr.Has("depth", str)) iDepth = IpeLex(str).GetDouble(); iVerticalAlignment = IsMiniPage() ? ETop : EBottom; if (attr.Has("valign", str)) { if (str == "top") iVerticalAlignment = ETop; else if (str == "bottom") iVerticalAlignment = EBottom; else if (str == "baseline") iVerticalAlignment = EBaseline; else if (str == "center") iVerticalAlignment = EVCenter; } iHorizontalAlignment = ELeft; if (attr.Has("halign", str)) { if (str == "left") iHorizontalAlignment = ELeft; else if (str == "right") iHorizontalAlignment = ERight; else if (str == "center") iHorizontalAlignment = EHCenter; } } //! Create text object. IpeText::IpeText(const IpeAllAttributes &attr, IpeString data, const IpeVector &pos, TType type, IpeScalar width) : IpeObject(attr) { iXForm = 0; iText = data; iSize = attr.iTextSize; iPos = pos; iType = type; iWidth = width; iHeight = 10.0; iDepth = 0.0; if (iType == ELabel || iType == ETitle) iVerticalAlignment = EBottom; else iVerticalAlignment = ETop; iHorizontalAlignment = ELeft; iTransformable = attr.iTransformable; } //! Clone object IpeObject *IpeText::Clone() const { return new IpeText(*this); } //! Copy constructor. IpeText::IpeText(const IpeText &rhs) : IpeObject(rhs) { iPos = rhs.iPos; iText = rhs.iText; iSize = rhs.iSize; iWidth = rhs.iWidth; iHeight = rhs.iHeight; iDepth = rhs.iDepth; iType = rhs.iType; iVerticalAlignment = rhs.iVerticalAlignment; iHorizontalAlignment = rhs.iHorizontalAlignment; iXForm = rhs.iXForm; iTransformable = rhs.iTransformable; if (iXForm) iXForm->iRefCount++; } //! Destructor. IpeText::~IpeText() { if (iXForm && --iXForm->iRefCount == 0) delete iXForm; } //! Return pointer to this object. IpeText *IpeText::AsText() { return this; } //! Call VisitText of visitor. void IpeText::Accept(IpeVisitor &visitor) const { visitor.VisitText(this); } //! Save object to XML stream. void IpeText::SaveAsXml(IpePainter &painter, IpeStream &stream, IpeString layer) const { stream << "String(iSize) << "\">"; stream.PutXmlString(iText); stream << "\n"; } //! Save text as PDF. void IpeText::Draw(IpePainter &painter) const { IpeVector pos(iPos); painter.Push(); painter.Transform(Matrix()); painter.Translate(pos); if (!iTransformable) painter.Untransform(false); painter.SetStroke(Stroke()); painter.SetTextSize(Size()); // this size isn't actually used // Adjust alignment: make lower left corner of text box the origin painter.Translate(-Align()); painter.DrawText(this); painter.Pop(); } double IpeText::Distance(const IpeVector &v, const IpeMatrix &m, double bound) const { IpeVector u[5]; Quadrilateral(m, u); u[4] = u[0]; double d = bound; double d1; for (int i = 0; i < 4; ++i) { if ((d1 = IpeSegment(u[i], u[i+1]).Distance(v, d)) < d) d = d1; } return d1; } void IpeText::AddToBBox(IpeRect &box, const IpeMatrix &m) const { IpeVector v[4]; Quadrilateral(m, v); for (int i = 0; i < 4; ++i) box.AddPoint(v[i]); } // void IpeText::SnapVtx(const IpeVector &mouse, const IpeMatrix &m, // IpeVector &pos, double &bound) const void IpeText::SnapVtx(const IpeVector &, const IpeMatrix &, IpeVector &, double &) const { // nothing } // -------------------------------------------------------------------- //! Set width of paragraph. /*! This invalidates (and destroys) the XForm. The function panics if object is not a minipage. */ void IpeText::SetWidth(IpeScalar width) { assert(IsMiniPage()); iWidth = width; SetXForm(0); } //! Set font size of text. /*! This invalidates (and destroys) the XForm. */ void IpeText::SetSize(IpeAttribute size) { iSize = size; SetXForm(0); } //! Set whether the text object can be transformed. /*! This invalidates (and destroys) the XForm. */ void IpeText::SetTransformable(bool transf) { iTransformable = transf; SetXForm(0); } //! Sets the text of the text object. /*! This invalidates (and destroys) the XForm. */ void IpeText::SetText(IpeString text) { iText = text; SetXForm(0); } //! Change type. void IpeText::SetType(TType type) { iType = type; } //! Change horizontal alignment (text moves with respect to reference point). void IpeText::SetHorizontalAlignment(THorizontalAlignment align) { iHorizontalAlignment = align; } //! Change vertical alignment (text moves with respect to reference point). void IpeText::SetVerticalAlignment(TVerticalAlignment align) { iVerticalAlignment = align; } // -------------------------------------------------------------------- //! Check symbolic size attribute. void IpeText::CheckStyle(const IpeStyleSheet *sheet, IpeAttributeSeq &seq) const { CheckSymbol(iSize, sheet, seq); IpeObject::CheckStyle(sheet, seq); } //! Return quadrilateral including the text. /*! This is the bounding box, correctly transformed by Matrix(), taking into consideration whether the object is transformable. */ void IpeText::Quadrilateral(const IpeMatrix &m, IpeVector v[4]) const { IpeScalar wid = iWidth; IpeScalar ht = TotalHeight(); IpeVector offset = -Align(); v[0] = offset + IpeVector(0, 0); v[1] = offset + IpeVector(wid, 0); v[2] = offset + IpeVector(wid, ht); v[3] = offset + IpeVector(0, ht); IpeMatrix m1 = m * Matrix(); if (iTransformable) { IpeLinear m2 = m1.Linear(); for (int i = 0; i < 4; ++i) v[i] = m2 * v[i]; } IpeVector pos = m1 * iPos; for (int i = 0; i < 4; ++i) v[i] = v[i] + pos; } //! Update the PDF code for this object. void IpeText::SetXForm(XForm *xform) const { if (iXForm && --iXForm->iRefCount == 0) delete iXForm; iXForm = xform; if (iXForm) { iXForm->iRefCount = 1; iDepth = iXForm->iStretch.iY * iXForm->iDepth / 100.0; iHeight = iXForm->iStretch.iY * iXForm->iBBox.Height() - iDepth; if (!IsMiniPage()) iWidth = iXForm->iStretch.iX * iXForm->iBBox.Width(); } } //! Return position of reference point in text box coordinate system. /*! Assume a coordinate system where the text box has corners (0,0) and (Width(), TotalHeight()). This function returns the coordinates of the reference point in this coordinate system. */ IpeVector IpeText::Align() const { IpeVector align(0.0, 0.0); switch (iVerticalAlignment) { case ETop: align.iY = TotalHeight(); break; case EBottom: break; case EVCenter: align.iY = 0.5 * TotalHeight(); break; case EBaseline: align.iY = Depth(); break; } switch (iHorizontalAlignment) { case ELeft: break; case ERight: align.iX = Width(); break; case EHCenter: align.iX = 0.5 * Width(); break; } return align; } // --------------------------------------------------------------------