1
/****************************************************************************
3
** This file is part of the LibreCAD project, a 2D CAD program
5
** Copyright (C) 2010 R. van Twisk (librecad@rvt.dds.nl)
6
** Copyright (C) 2001-2003 RibbonSoft. All rights reserved.
9
** This file may be distributed and/or modified under the terms of the
10
** GNU General Public License version 2 as published by the Free Software
11
** Foundation and appearing in the file gpl-2.0.txt included in the
12
** packaging of this file.
14
** This program is distributed in the hope that it will be useful,
15
** but WITHOUT ANY WARRANTY; without even the implied warranty of
16
** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17
** GNU General Public License for more details.
19
** You should have received a copy of the GNU General Public License
20
** along with this program; if not, write to the Free Software
21
** Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
23
** This copyright notice MUST APPEAR in all copies of the script!
25
**********************************************************************/
31
#include "rs_fontlist.h"
32
#include "rs_insert.h"
37
RS_MText::RS_MText(RS_EntityContainer* parent,
38
const RS_MTextData& d)
39
: RS_EntityContainer(parent), data(d) {
49
* Sets a new text. The entities representing the
52
void RS_MText::setText(const QString& t) {
55
// handle some special flags embedded in the text:
56
if (data.text.left(4)=="\\A0;") {
57
data.text = data.text.mid(4);
58
data.valign = RS_MTextData::VABottom;
59
} else if (data.text.left(4)=="\\A1;") {
60
data.text = data.text.mid(4);
61
data.valign = RS_MTextData::VAMiddle;
62
} else if (data.text.left(4)=="\\A2;") {
63
data.text = data.text.mid(4);
64
data.valign = RS_MTextData::VATop;
67
if (data.updateMode==RS2::Update) {
76
* Gets the alignment as an int.
78
* @return 1: top left ... 9: bottom right
80
int RS_MText::getAlignment() {
81
if (data.valign==RS_MTextData::VATop) {
82
if (data.halign==RS_MTextData::HALeft) {
84
} else if (data.halign==RS_MTextData::HACenter) {
86
} else if (data.halign==RS_MTextData::HARight) {
89
} else if (data.valign==RS_MTextData::VAMiddle) {
90
if (data.halign==RS_MTextData::HALeft) {
92
} else if (data.halign==RS_MTextData::HACenter) {
94
} else if (data.halign==RS_MTextData::HARight) {
97
} else if (data.valign==RS_MTextData::VABottom) {
98
if (data.halign==RS_MTextData::HALeft) {
100
} else if (data.halign==RS_MTextData::HACenter) {
102
} else if (data.halign==RS_MTextData::HARight) {
113
* Sets the alignment from an int.
115
* @param a 1: top left ... 9: bottom right
117
void RS_MText::setAlignment(int a) {
121
data.halign = RS_MTextData::HALeft;
124
data.halign = RS_MTextData::HACenter;
127
data.halign = RS_MTextData::HARight;
131
switch ((int)ceil(a/3.0)) {
134
data.valign = RS_MTextData::VATop;
137
data.valign = RS_MTextData::VAMiddle;
140
data.valign = RS_MTextData::VABottom;
149
* @return Number of lines in this text entity.
151
int RS_MText::getNumberOfLines() {
154
for (int i=0; i<(int)data.text.length(); ++i) {
155
if (data.text.at(i).unicode()==0x0A) {
167
* Updates the Inserts (letters) of this text. Called when the
168
* text or it's data, position, alignment, .. changes.
169
* This method also updates the usedTextWidth / usedTextHeight property.
171
void RS_MText::update() {
173
RS_DEBUG->print("RS_Text::update");
182
usedTextHeight = 0.0;
184
RS_Font* font = RS_FONTLIST->requestFont(data.style);
190
RS_Vector letterPos = RS_Vector(0.0, -9.0);
191
RS_Vector letterSpace = RS_Vector(font->getLetterSpacing(), 0.0);
192
RS_Vector space = RS_Vector(font->getWordSpacing(), 0.0);
195
// Every single text line gets stored in this entity container
196
// so we can move the whole line around easely:
197
RS_EntityContainer* oneLine = new RS_EntityContainer(this);
199
// First every text line is created with
200
// alignement: top left
203
// Rotation, scaling and centering is done later
206
for (int i=0; i<(int)data.text.length(); ++i) {
207
bool handled = false;
208
switch (data.text.at(i).unicode()) {
211
updateAddLine(oneLine, lineCounter++);
212
oneLine = new RS_EntityContainer(this);
213
letterPos = RS_Vector(0.0, -9.0);
224
// code (e.g. \S, \P, ..)
226
int ch = data.text.at(i).unicode();
229
updateAddLine(oneLine, lineCounter++);
230
oneLine = new RS_EntityContainer(this);
231
letterPos = RS_Vector(0.0, -9.0);
237
// \f{symbol} changes font to symbol
238
// \f{} sets font to standard
241
if(data.text.at(i).unicode()!='{') {
245
int j=data.text.indexOf('}',i);
252
fontName=data.text.mid(i+1,j-i-1);
253
RS_Font* fontNew = RS_FONTLIST->requestFont(
256
if(fontNew != NULL) {
259
if(font==NULL) font = RS_FONTLIST->requestFont("standard");
268
//letterPos += letterSpace;
272
while (data.text.at(i).unicode()!='^' &&
273
//data.text.at(i).unicode()!='/' &&
274
data.text.at(i).unicode()!='\\' &&
275
//data.text.at(i).unicode()!='#' &&
276
i<(int)data.text.length()) {
277
up += data.text.at(i);
283
if (data.text.at(i-1).unicode()=='^' &&
284
data.text.at(i).unicode()==' ') {
289
while (data.text.at(i).unicode()!=';' &&
290
i<(int)data.text.length()) {
291
dw += data.text.at(i);
299
RS_MTextData(letterPos + RS_Vector(0.0,9.0),
300
4.0, 100.0, RS_MTextData::VATop, RS_MTextData::HALeft,
301
RS_MTextData::LeftToRight, RS_MTextData::Exact,
304
upper->setLayer(NULL);
305
upper->setPen(RS_Pen(RS2::FlagInvalid));
306
oneLine->addEntity(upper);
311
RS_MTextData(letterPos+RS_Vector(0.0,4.0),
312
4.0, 100.0, RS_MTextData::VATop, RS_MTextData::HALeft,
313
RS_MTextData::LeftToRight, RS_MTextData::Exact,
316
lower->setLayer(NULL);
317
lower->setPen(RS_Pen(RS2::FlagInvalid));
318
oneLine->addEntity(lower);
321
upper->calculateBorders();
322
lower->calculateBorders();
324
double w1 = upper->getSize().x;
325
double w2 = lower->getSize().x;
328
letterPos += RS_Vector(w1, 0.0);
330
letterPos += RS_Vector(w2, 0.0);
332
letterPos += letterSpace;
342
//if char is not handled continue in default: statement
348
QString letterText = QString(data.text.at(i));
349
if (font->findLetter(letterText) == NULL) {
350
RS_DEBUG->print("RS_Text::update: missing font for letter( %s ), replaced it with QChar(0xfffd)",qPrintable(letterText));
351
letterText = QChar(0xfffd);
353
// if (font->findLetter(QString(data.text.at(i))) != NULL) {
355
RS_DEBUG->print("RS_Text::update: insert a "
356
"letter at pos: %f/%f", letterPos.x, letterPos.y);
358
RS_InsertData d(letterText,
362
1,1, RS_Vector(0.0,0.0),
363
font->getLetterList(), RS2::NoUpdate);
365
RS_Insert* letter = new RS_Insert(this, d);
366
RS_Vector letterWidth;
367
letter->setPen(RS_Pen(RS2::FlagInvalid));
368
letter->setLayer(NULL);
370
letter->forcedCalculateBorders();
373
//letterWidth = RS_Vector(letter->getSize().x, 0.0);
375
letterWidth = RS_Vector(letter->getMax().x-letterPos.x, 0.0);
376
if (letterWidth.x < 0)
377
letterWidth.x = -letterSpace.x;
379
oneLine->addEntity(letter);
381
// next letter position:
382
letterPos += letterWidth;
383
letterPos += letterSpace;
390
double tt = updateAddLine(oneLine, lineCounter);
391
if (data.valign == RS_MTextData::VABottom) {
392
RS_Vector ot = RS_Vector(0.0,-tt).rotate(data.angle);
393
RS_EntityContainer::move(ot);
396
usedTextHeight -= data.height*data.lineSpacingFactor*5.0/3.0
398
forcedCalculateBorders();
400
RS_DEBUG->print("RS_Text::update: OK");
406
* Used internally by update() to add a text line created with
407
* default values and alignment to this text container.
409
* @param textLine The text line.
410
* @param lineCounter Line number.
412
* @return distance over the text base-line
414
double RS_MText::updateAddLine(RS_EntityContainer* textLine, int lineCounter) {
417
RS_DEBUG->print("RS_Text::updateAddLine: width: %f", textLine->getSize().x);
419
//textLine->forcedCalculateBorders();
420
//RS_DEBUG->print("RS_Text::updateAddLine: width 2: %f", textLine->getSize().x);
422
// Move to correct line position:
423
textLine->move(RS_Vector(0.0, -9.0 * lineCounter
424
* data.lineSpacingFactor * ls));
426
if( ! RS_EntityContainer::autoUpdateBorders) {
427
//only update borders when needed
428
textLine->forcedCalculateBorders();
430
RS_Vector textSize = textLine->getSize();
432
RS_DEBUG->print("RS_Text::updateAddLine: width 2: %f", textSize.x);
435
switch (data.halign) {
436
case RS_MTextData::HACenter:
437
RS_DEBUG->print("RS_Text::updateAddLine: move by: %f", -textSize.x/2.0);
438
textLine->move(RS_Vector(-textSize.x/2.0, 0.0));
441
case RS_MTextData::HARight:
442
textLine->move(RS_Vector(-textSize.x, 0.0));
450
double vSize = getNumberOfLines()*9.0*data.lineSpacingFactor*ls
451
- (9.0*data.lineSpacingFactor*ls - 9.0);
453
switch (data.valign) {
454
case RS_MTextData::VAMiddle:
455
textLine->move(RS_Vector(0.0, vSize/2.0));
458
case RS_MTextData::VABottom:
459
textLine->move(RS_Vector(0.0, vSize));
467
textLine->scale(RS_Vector(0.0,0.0),
468
RS_Vector(data.height/9.0, data.height/9.0));
470
textLine->forcedCalculateBorders();
472
// Update actual text size (before rotating, after scaling!):
473
if (textLine->getSize().x>usedTextWidth) {
474
usedTextWidth = textLine->getSize().x;
477
usedTextHeight += data.height*data.lineSpacingFactor*ls;
479
// Gets the distance over text base-line (before rotating, after scaling!):
480
double textTail = textLine->getMin().y;
483
textLine->rotate(RS_Vector(0.0,0.0), data.angle);
486
textLine->move(data.insertionPoint);
487
textLine->setPen(RS_Pen(RS2::FlagInvalid));
488
textLine->setLayer(NULL);
489
textLine->forcedCalculateBorders();
496
RS_Vector RS_MText::getNearestEndpoint(const RS_Vector& coord, double* dist)const {
498
*dist = data.insertionPoint.distanceTo(coord);
500
return data.insertionPoint;
504
RS_VectorSolutions RS_MText::getRefPoints() {
505
RS_VectorSolutions ret(data.insertionPoint);
510
RS_Vector RS_MText::getNearestRef(const RS_Vector& coord,
513
//return getRefPoints().getClosest(coord, dist);
514
return RS_Entity::getNearestRef(coord, dist);
518
void RS_MText::move(const RS_Vector& offset) {
519
RS_EntityContainer::move(offset);
520
data.insertionPoint.move(offset);
526
void RS_MText::rotate(const RS_Vector& center, const double& angle) {
527
RS_Vector angleVector(angle);
528
RS_EntityContainer::rotate(center, angleVector);
529
data.insertionPoint.rotate(center, angleVector);
530
data.angle = RS_Math::correctAngle(data.angle+angle);
533
void RS_MText::rotate(const RS_Vector& center, const RS_Vector& angleVector) {
534
RS_EntityContainer::rotate(center, angleVector);
535
data.insertionPoint.rotate(center, angleVector);
536
data.angle = RS_Math::correctAngle(data.angle+angleVector.angle());
542
void RS_MText::scale(const RS_Vector& center, const RS_Vector& factor) {
543
data.insertionPoint.scale(center, factor);
544
data.width*=factor.x;
545
data.height*=factor.x;
551
void RS_MText::mirror(const RS_Vector& axisPoint1, const RS_Vector& axisPoint2) {
552
data.insertionPoint.mirror(axisPoint1, axisPoint2);
553
//double ang = axisPoint1.angleTo(axisPoint2);
554
bool readable = RS_Math::isAngleReadable(data.angle);
557
vec.setPolar(1.0, data.angle);
558
vec.mirror(RS_Vector(0.0,0.0), axisPoint2-axisPoint1);
559
data.angle = vec.angle();
562
data.angle = RS_Math::makeAngleReadable(data.angle, readable, &corr);
565
if (data.halign==RS_MTextData::HALeft) {
566
data.halign=RS_MTextData::HARight;
567
} else if (data.halign==RS_MTextData::HARight) {
568
data.halign=RS_MTextData::HALeft;
571
if (data.valign==RS_MTextData::VATop) {
572
data.valign=RS_MTextData::VABottom;
573
} else if (data.valign==RS_MTextData::VABottom) {
574
data.valign=RS_MTextData::VATop;
582
bool RS_MText::hasEndpointsWithinWindow(const RS_Vector& /*v1*/, const RS_Vector& /*v2*/) {
589
* Implementations must stretch the given range of the entity
590
* by the given offset.
592
void RS_MText::stretch(const RS_Vector& firstCorner, const RS_Vector& secondCorner, const RS_Vector& offset) {
594
if (getMin().isInWindow(firstCorner, secondCorner) &&
595
getMax().isInWindow(firstCorner, secondCorner)) {
604
* Dumps the point's data to stdout.
606
std::ostream& operator << (std::ostream& os, const RS_MText& p) {
607
os << " Text: " << p.getData() << "\n";