3 // Part of the Architektonas Project
4 // Originally part of QCad Community Edition by Andrew Mustun
5 // Extensively rewritten and refactored by James L. Hammons
6 // (C) 2010 Underground Software
8 // JLH = James L. Hammons <jlhamm@acm.org>
11 // --- ---------- -----------------------------------------------------------
12 // JLH 05/27/2010 Added this text. :-)
18 #include "rs_fontlist.h"
19 #include "rs_insert.h"
24 RS_Text::RS_Text(RS_EntityContainer * parent, const RS_TextData & d): RS_EntityContainer(parent), data(d)
31 /*virtual*/ RS_Text::~RS_Text()
35 /*virtual*/ RS_Entity * RS_Text::clone()
37 RS_Text * t = new RS_Text(*this);
38 #warning "!!! Need to deal with setAutoDelete() Qt3->Qt4 !!!"
39 // t->entities.setAutoDelete(entities.autoDelete());
45 /** @return RS2::EntityText */
46 /*virtual*/ RS2::EntityType RS_Text::rtti() const
48 return RS2::EntityText;
51 /** @return Copy of data that defines the text. */
52 RS_TextData RS_Text::getData() const
58 * @return Number of lines in this text entity.
60 int RS_Text::getNumberOfLines()
64 for(int i=0; i<(int)data.text.length(); i++)
66 if (data.text.at(i).unicode() == 0x0A)
74 * Updates the Inserts (letters) of this text. Called when the
75 * text or it's data, position, alignment, .. changes.
76 * This method also updates the usedTextWidth / usedTextHeight property.
78 void RS_Text::update()
81 printf("RS_Text::update(): ");
83 RS_DEBUG->print("RS_Text::update");
87 printf("isUndone=%s, ", (isUndone() ? "true" : "false"));
96 printf("data.style=\"%s\", ", data.style.toAscii().data());
98 RS_Font * font = RS_FONTLIST->requestFont(data.style);
101 printf("font=%08X\n", (unsigned int)font);
106 Vector letterPos = Vector(0.0, -9.0);
107 Vector letterSpace = Vector(font->getLetterSpacing(), 0.0);
108 Vector space = Vector(font->getWordSpacing(), 0.0);
111 // Every single text line gets stored in this entity container
112 // so we can move the whole line around easely:
113 RS_EntityContainer * oneLine = new RS_EntityContainer(this);
115 // First every text line is created with
116 // alignement: top left
119 // Rotation, scaling and centering is done later
122 for(int i=0; i<(int)data.text.length(); ++i)
124 switch (data.text.at(i).unicode())
128 updateAddLine(oneLine, lineCounter++);
129 oneLine = new RS_EntityContainer(this);
130 letterPos = Vector(0.0, -9.0);
140 // code (e.g. \S, \P, ..)
142 int ch = data.text.at(i).unicode();
147 updateAddLine(oneLine, lineCounter++);
148 oneLine = new RS_EntityContainer(this);
149 letterPos = Vector(0.0, -9.0);
156 //letterPos += letterSpace;
160 while (data.text.at(i).unicode() != '^'
161 && data.text.at(i).unicode() != '\\'
162 && i < (int)data.text.length())
164 up += data.text.at(i);
170 if (data.text.at(i - 1).unicode() == '^'
171 && data.text.at(i).unicode() == ' ')
177 while (data.text.at(i).unicode() != ';' && i < (int)data.text.length())
179 dw += data.text.at(i);
184 RS_Text * upper = new RS_Text(oneLine,
185 RS_TextData(letterPos + Vector(0.0, 9.0), 4.0, 100.0,
186 RS2::VAlignTop, RS2::HAlignLeft, RS2::LeftToRight,
187 RS2::Exact, 1.0, up, data.style, 0.0, RS2::Update));
188 upper->setLayer(NULL);
189 upper->setPen(RS_Pen(RS2::FlagInvalid));
190 oneLine->addEntity(upper);
192 RS_Text * lower = new RS_Text(oneLine,
193 RS_TextData(letterPos+Vector(0.0, 4.0), 4.0, 100.0,
194 RS2::VAlignTop, RS2::HAlignLeft, RS2::LeftToRight,
195 RS2::Exact, 1.0, dw, data.style, 0.0, RS2::Update));
196 lower->setLayer(NULL);
197 lower->setPen(RS_Pen(RS2::FlagInvalid));
198 oneLine->addEntity(lower);
201 upper->calculateBorders();
202 lower->calculateBorders();
204 double w1 = upper->getSize().x;
205 double w2 = lower->getSize().x;
208 letterPos += Vector(w1, 0.0);
210 letterPos += Vector(w2, 0.0);
212 letterPos += letterSpace;
224 // Regular text works because the .style is "normallatin1" while the dimensions are done
225 // using "standard"... So that's why it doesn't work.
226 // "standard" exists, but it seems that no characters are loaded from it...
228 //printf("--> default: pos=%d, char=%04X, font size=%d\n", i, data.text.at(i).unicode(), font->getLetterList()->count());
230 //#warning "!!! font->findLetter() not working correctly !!!"
231 if (font->findLetter(QString(data.text.at(i))))
233 //We ain't getting here:
235 About to draw TEXT entity... TEXT="107.25"
236 RS_Dimension::updateCreateDimensionLine()...
238 RS_Text::update(): isUndone=false, font=094C1020
239 --> default: pos=0, char=0031
240 --> default: pos=1, char=0030
241 --> default: pos=2, char=0037
242 --> default: pos=3, char=002E
243 --> default: pos=4, char=0032
244 --> default: pos=5, char=0035
246 //printf(" char=%s\n", QString(data.text.at(i)).toAscii().data());
247 RS_DEBUG->print("RS_Text::update: insert a letter at pos: %f/%f", letterPos.x, letterPos.y);
248 RS_InsertData d(QString(data.text.at(i)), letterPos, Vector(1.0, 1.0),
249 0.0, 1, 1, Vector(0.0, 0.0), font->getLetterList(), RS2::NoUpdate);
251 RS_Insert * letter = new RS_Insert(this, d);
252 letter->setPen(RS_Pen(RS2::FlagInvalid));
253 letter->setLayer(NULL);
255 letter->forcedCalculateBorders();
258 //letterWidth = Vector(letter->getSize().x, 0.0);
260 Vector letterWidth = Vector(letter->getMax().x - letterPos.x, 0.0);
261 oneLine->addEntity(letter);
263 // next letter position:
264 letterPos += letterWidth;
265 letterPos += letterSpace;
272 updateAddLine(oneLine, lineCounter);
273 usedTextHeight -= data.height * data.lineSpacingFactor * 1.6 - data.height;
274 forcedCalculateBorders();
276 RS_DEBUG->print("RS_Text::update: OK");
280 * Used internally by update() to add a text line created with
281 * default values and alignment to this text container.
283 * @param textLine The text line.
284 * @param lineCounter Line number.
286 void RS_Text::updateAddLine(RS_EntityContainer* textLine, int lineCounter)
288 RS_DEBUG->print("RS_Text::updateAddLine: width: %f", textLine->getSize().x);
290 //textLine->forcedCalculateBorders();
291 //RS_DEBUG->print("RS_Text::updateAddLine: width 2: %f", textLine->getSize().x);
293 // Move to correct line position:
294 textLine->move(Vector(0.0, -9.0 * lineCounter * data.lineSpacingFactor * 1.6));
296 textLine->forcedCalculateBorders();
297 Vector textSize = textLine->getSize();
299 RS_DEBUG->print("RS_Text::updateAddLine: width 2: %f", textSize.x);
304 case RS2::HAlignCenter:
305 RS_DEBUG->print("RS_Text::updateAddLine: move by: %f", -textSize.x / 2.0);
306 textLine->move(Vector(-textSize.x / 2.0, 0.0));
309 case RS2::HAlignRight:
310 textLine->move(Vector(-textSize.x, 0.0));
318 double vSize = getNumberOfLines() * 9.0 * data.lineSpacingFactor * 1.6 - (9.0 * data.lineSpacingFactor * 1.6 - 9.0);
322 case RS2::VAlignMiddle:
323 textLine->move(Vector(0.0, vSize / 2.0));
326 case RS2::VAlignBottom:
327 textLine->move(Vector(0.0, vSize));
335 textLine->scale(Vector(0.0, 0.0), Vector(data.height / 9.0, data.height / 9.0));
337 textLine->forcedCalculateBorders();
339 // Update actual text size (before rotating, after scaling!):
340 if (textLine->getSize().x > usedTextWidth)
341 usedTextWidth = textLine->getSize().x;
343 usedTextHeight += data.height * data.lineSpacingFactor * 1.6;
346 textLine->rotate(Vector(0.0, 0.0), data.angle);
349 textLine->move(data.insertionPoint);
350 textLine->setPen(RS_Pen(RS2::FlagInvalid));
351 textLine->setLayer(NULL);
352 textLine->forcedCalculateBorders();
357 Vector RS_Text::getInsertionPoint()
359 return data.insertionPoint;
362 double RS_Text::getHeight()
367 void RS_Text::setHeight(double h)
372 double RS_Text::getWidth()
378 * Sets the alignment from an int.
380 * @param a 1: top left ... 9: bottom right
382 void RS_Text::setAlignment(int a)
388 data.halign = RS2::HAlignLeft;
391 data.halign = RS2::HAlignCenter;
394 data.halign = RS2::HAlignRight;
398 switch ((int)ceil(a / 3.0))
402 data.valign = RS2::VAlignTop;
405 data.valign = RS2::VAlignMiddle;
408 data.valign = RS2::VAlignBottom;
414 * Gets the alignment as an int.
416 * @return 1: top left ... 9: bottom right
418 int RS_Text::getAlignment()
420 if (data.valign == RS2::VAlignTop)
422 if (data.halign == RS2::HAlignLeft)
426 else if (data.halign == RS2::HAlignCenter)
430 else if (data.halign == RS2::HAlignRight)
435 else if (data.valign == RS2::VAlignMiddle)
437 if (data.halign == RS2::HAlignLeft)
441 else if (data.halign == RS2::HAlignCenter)
445 else if (data.halign == RS2::HAlignRight)
450 else if (data.valign == RS2::VAlignBottom)
452 if (data.halign == RS2::HAlignLeft)
456 else if (data.halign == RS2::HAlignCenter)
460 else if (data.halign == RS2::HAlignRight)
469 RS2::VAlign RS_Text::getVAlign()
474 void RS_Text::setVAlign(RS2::VAlign va)
479 RS2::HAlign RS_Text::getHAlign()
484 void RS_Text::setHAlign(RS2::HAlign ha)
489 RS2::TextDrawingDirection RS_Text::getDrawingDirection()
491 return data.drawingDirection;
494 RS2::TextLineSpacingStyle RS_Text::getLineSpacingStyle()
496 return data.lineSpacingStyle;
499 void RS_Text::setLineSpacingFactor(double f)
501 data.lineSpacingFactor = f;
504 double RS_Text::getLineSpacingFactor()
506 return data.lineSpacingFactor;
510 * Sets a new text. The entities representing the
513 void RS_Text::setText(const QString & t)
517 // handle some special flags embedded in the text:
518 if (data.text.left(4) == "\\A0;")
520 data.text = data.text.mid(4);
521 data.valign = RS2::VAlignBottom;
523 else if (data.text.left(4) == "\\A1;")
525 data.text = data.text.mid(4);
526 data.valign = RS2::VAlignMiddle;
528 else if (data.text.left(4) == "\\A2;")
530 data.text = data.text.mid(4);
531 data.valign = RS2::VAlignTop;
534 if (data.updateMode == RS2::Update)
537 //calculateBorders();
541 QString RS_Text::getText()
546 void RS_Text::setStyle(const QString & s)
551 QString RS_Text::getStyle()
556 void RS_Text::setAngle(double a)
561 double RS_Text::getAngle()
566 double RS_Text::getUsedTextWidth()
568 return usedTextWidth;
571 double RS_Text::getUsedTextHeight()
573 return usedTextHeight;
576 /*virtual*/ double RS_Text::getLength()
581 VectorSolutions RS_Text::getRefPoints()
583 VectorSolutions ret(data.insertionPoint);
587 Vector RS_Text::getNearestRef(const Vector & coord, double * dist)
589 return RS_Entity::getNearestRef(coord, dist);
592 void RS_Text::move(Vector offset)
594 data.insertionPoint.move(offset);
598 void RS_Text::rotate(Vector center, double angle)
600 data.insertionPoint.rotate(center, angle);
601 data.angle = RS_Math::correctAngle(data.angle + angle);
605 void RS_Text::scale(Vector center, Vector factor)
607 data.insertionPoint.scale(center, factor);
608 data.width *= factor.x;
609 data.height *= factor.x;
613 void RS_Text::mirror(Vector axisPoint1, Vector axisPoint2)
615 data.insertionPoint.mirror(axisPoint1, axisPoint2);
616 //double ang = axisPoint1.angleTo(axisPoint2);
617 bool readable = RS_Math::isAngleReadable(data.angle);
620 vec.setPolar(1.0, data.angle);
621 vec.mirror(Vector(0.0, 0.0), axisPoint2 - axisPoint1);
622 data.angle = vec.angle();
625 data.angle = RS_Math::makeAngleReadable(data.angle, readable, &corr);
629 if (data.halign == RS2::HAlignLeft)
631 data.halign = RS2::HAlignRight;
633 else if (data.halign == RS2::HAlignRight)
635 data.halign = RS2::HAlignLeft;
640 if (data.valign == RS2::VAlignTop)
642 data.valign = RS2::VAlignBottom;
644 else if (data.valign == RS2::VAlignBottom)
646 data.valign = RS2::VAlignTop;
653 bool RS_Text::hasEndpointsWithinWindow(Vector /*v1*/, Vector /*v2*/)
659 * Implementations must stretch the given range of the entity
660 * by the given offset.
662 void RS_Text::stretch(Vector firstCorner, Vector secondCorner, Vector offset)
664 if (getMin().isInWindow(firstCorner, secondCorner)
665 && getMax().isInWindow(firstCorner, secondCorner))
670 * Dumps the point's data to stdout.
672 std::ostream & operator<<(std::ostream & os, const RS_Text & p)
674 os << " Text: " << p.getData() << "\n";