~librecad-dev/librecad/librecad

« back to all changes in this revision

Viewing changes to librecad/src/lib/engine/rs_mtext.cpp

  • Committer: Scott Howard
  • Date: 2014-02-21 19:07:55 UTC
  • Revision ID: showard@debian.org-20140221190755-csjax9wb146hgdq4
first commit

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/****************************************************************************
 
2
**
 
3
** This file is part of the LibreCAD project, a 2D CAD program
 
4
**
 
5
** Copyright (C) 2010 R. van Twisk (librecad@rvt.dds.nl)
 
6
** Copyright (C) 2001-2003 RibbonSoft. All rights reserved.
 
7
**
 
8
**
 
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.
 
13
**
 
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.
 
18
**
 
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
 
22
**
 
23
** This copyright notice MUST APPEAR in all copies of the script!
 
24
**
 
25
**********************************************************************/
 
26
 
 
27
 
 
28
#include "rs_font.h"
 
29
#include "rs_mtext.h"
 
30
 
 
31
#include "rs_fontlist.h"
 
32
#include "rs_insert.h"
 
33
 
 
34
/**
 
35
 * Constructor.
 
36
 */
 
37
RS_MText::RS_MText(RS_EntityContainer* parent,
 
38
                 const RS_MTextData& d)
 
39
        : RS_EntityContainer(parent), data(d) {
 
40
 
 
41
    usedTextHeight = 0.0;
 
42
    usedTextWidth = 0.0;
 
43
    setText(data.text);
 
44
}
 
45
 
 
46
 
 
47
 
 
48
/**
 
49
 * Sets a new text. The entities representing the
 
50
 * text are updated.
 
51
 */
 
52
void RS_MText::setText(const QString& t) {
 
53
    data.text = t;
 
54
 
 
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;
 
65
    }
 
66
 
 
67
    if (data.updateMode==RS2::Update) {
 
68
        update();
 
69
        //calculateBorders();
 
70
    }
 
71
}
 
72
 
 
73
 
 
74
 
 
75
/**
 
76
 * Gets the alignment as an int.
 
77
 *
 
78
 * @return  1: top left ... 9: bottom right
 
79
 */
 
80
int RS_MText::getAlignment() {
 
81
    if (data.valign==RS_MTextData::VATop) {
 
82
        if (data.halign==RS_MTextData::HALeft) {
 
83
            return 1;
 
84
        } else if (data.halign==RS_MTextData::HACenter) {
 
85
            return 2;
 
86
        } else if (data.halign==RS_MTextData::HARight) {
 
87
            return 3;
 
88
        }
 
89
    } else if (data.valign==RS_MTextData::VAMiddle) {
 
90
        if (data.halign==RS_MTextData::HALeft) {
 
91
            return 4;
 
92
        } else if (data.halign==RS_MTextData::HACenter) {
 
93
            return 5;
 
94
        } else if (data.halign==RS_MTextData::HARight) {
 
95
            return 6;
 
96
        }
 
97
    } else if (data.valign==RS_MTextData::VABottom) {
 
98
        if (data.halign==RS_MTextData::HALeft) {
 
99
            return 7;
 
100
        } else if (data.halign==RS_MTextData::HACenter) {
 
101
            return 8;
 
102
        } else if (data.halign==RS_MTextData::HARight) {
 
103
            return 9;
 
104
        }
 
105
    }
 
106
 
 
107
    return 1;
 
108
}
 
109
 
 
110
 
 
111
 
 
112
/**
 
113
 * Sets the alignment from an int.
 
114
 *
 
115
 * @param a 1: top left ... 9: bottom right
 
116
 */
 
117
void RS_MText::setAlignment(int a) {
 
118
    switch (a%3) {
 
119
    default:
 
120
    case 1:
 
121
        data.halign = RS_MTextData::HALeft;
 
122
        break;
 
123
    case 2:
 
124
        data.halign = RS_MTextData::HACenter;
 
125
        break;
 
126
    case 0:
 
127
        data.halign = RS_MTextData::HARight;
 
128
        break;
 
129
    }
 
130
 
 
131
    switch ((int)ceil(a/3.0)) {
 
132
    default:
 
133
    case 1:
 
134
        data.valign = RS_MTextData::VATop;
 
135
        break;
 
136
    case 2:
 
137
        data.valign = RS_MTextData::VAMiddle;
 
138
        break;
 
139
    case 3:
 
140
        data.valign = RS_MTextData::VABottom;
 
141
        break;
 
142
    }
 
143
 
 
144
}
 
145
 
 
146
 
 
147
 
 
148
/**
 
149
 * @return Number of lines in this text entity.
 
150
 */
 
151
int RS_MText::getNumberOfLines() {
 
152
    int c=1;
 
153
 
 
154
    for (int i=0; i<(int)data.text.length(); ++i) {
 
155
        if (data.text.at(i).unicode()==0x0A) {
 
156
            c++;
 
157
        }
 
158
    }
 
159
 
 
160
    return c;
 
161
}
 
162
 
 
163
 
 
164
 
 
165
 
 
166
/**
 
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.
 
170
 */
 
171
void RS_MText::update() {
 
172
 
 
173
    RS_DEBUG->print("RS_Text::update");
 
174
 
 
175
    clear();
 
176
 
 
177
    if (isUndone()) {
 
178
        return;
 
179
    }
 
180
 
 
181
    usedTextWidth = 0.0;
 
182
    usedTextHeight = 0.0;
 
183
 
 
184
    RS_Font* font = RS_FONTLIST->requestFont(data.style);
 
185
 
 
186
    if (font==NULL) {
 
187
        return;
 
188
    }
 
189
 
 
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);
 
193
    int lineCounter = 0;
 
194
 
 
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);
 
198
 
 
199
    // First every text line is created with
 
200
    //   alignement: top left
 
201
    //   angle: 0
 
202
    //   height: 9.0
 
203
    // Rotation, scaling and centering is done later
 
204
 
 
205
    // For every letter:
 
206
    for (int i=0; i<(int)data.text.length(); ++i) {
 
207
        bool handled = false;
 
208
        switch (data.text.at(i).unicode()) {
 
209
        case 0x0A:
 
210
            // line feed:
 
211
            updateAddLine(oneLine, lineCounter++);
 
212
            oneLine = new RS_EntityContainer(this);
 
213
            letterPos = RS_Vector(0.0, -9.0);
 
214
            handled = true;
 
215
            break;
 
216
 
 
217
        case 0x20:
 
218
            // Space:
 
219
            letterPos+=space;
 
220
            handled = true;
 
221
            break;
 
222
 
 
223
        case 0x5C: {
 
224
                // code (e.g. \S, \P, ..)
 
225
                i++;
 
226
                int ch = data.text.at(i).unicode();
 
227
                switch (ch) {
 
228
                case 'P':
 
229
                    updateAddLine(oneLine, lineCounter++);
 
230
                    oneLine = new RS_EntityContainer(this);
 
231
                    letterPos = RS_Vector(0.0, -9.0);
 
232
                    handled = true;
 
233
                    break;
 
234
                    case 'f':
 
235
                    case 'F':
 
236
                    //font change
 
237
                    // \f{symbol} changes font to symbol
 
238
                    // \f{} sets font to standard
 
239
                {
 
240
                    i++;
 
241
                    if(data.text.at(i).unicode()!='{') {
 
242
                        i--;
 
243
                        continue;
 
244
                    }
 
245
                    int j=data.text.indexOf('}',i);
 
246
                    if(j>i){
 
247
                        //
 
248
                        QString fontName;
 
249
                        if(j==i+1)
 
250
                            fontName="standard";
 
251
                        else
 
252
                            fontName=data.text.mid(i+1,j-i-1);
 
253
                        RS_Font* fontNew = RS_FONTLIST->requestFont(
 
254
                                    fontName
 
255
                                    );
 
256
                        if(fontNew != NULL) {
 
257
                            font=fontNew;
 
258
                        }
 
259
                        if(font==NULL) font = RS_FONTLIST->requestFont("standard");
 
260
                        i=j;
 
261
                    }
 
262
                }
 
263
                        continue;
 
264
 
 
265
                case 'S': {
 
266
                        QString up;
 
267
                        QString dw;
 
268
                        //letterPos += letterSpace;
 
269
 
 
270
                        // get upper string:
 
271
                        i++;
 
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);
 
278
                            i++;
 
279
                        }
 
280
 
 
281
                        i++;
 
282
 
 
283
                                                if (data.text.at(i-1).unicode()=='^' &&
 
284
                                                     data.text.at(i).unicode()==' ') {
 
285
                                                        i++;
 
286
                                                }
 
287
 
 
288
                        // get lower string:
 
289
                        while (data.text.at(i).unicode()!=';' &&
 
290
                                i<(int)data.text.length()) {
 
291
                            dw += data.text.at(i);
 
292
                            i++;
 
293
                        }
 
294
 
 
295
                        // add texts:
 
296
                        RS_MText* upper =
 
297
                            new RS_MText(
 
298
                                oneLine,
 
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,
 
302
                                            1.0, up, data.style,
 
303
                                            0.0, RS2::Update));
 
304
                                            upper->setLayer(NULL);
 
305
                        upper->setPen(RS_Pen(RS2::FlagInvalid));
 
306
                        oneLine->addEntity(upper);
 
307
 
 
308
                        RS_MText* lower =
 
309
                            new RS_MText(
 
310
                                oneLine,
 
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,
 
314
                                            1.0, dw, data.style,
 
315
                                            0.0, RS2::Update));
 
316
                                            lower->setLayer(NULL);
 
317
                        lower->setPen(RS_Pen(RS2::FlagInvalid));
 
318
                        oneLine->addEntity(lower);
 
319
 
 
320
                        // move cursor:
 
321
                        upper->calculateBorders();
 
322
                        lower->calculateBorders();
 
323
 
 
324
                        double w1 = upper->getSize().x;
 
325
                        double w2 = lower->getSize().x;
 
326
 
 
327
                        if (w1>w2) {
 
328
                            letterPos += RS_Vector(w1, 0.0);
 
329
                        } else {
 
330
                            letterPos += RS_Vector(w2, 0.0);
 
331
                        }
 
332
                        letterPos += letterSpace;
 
333
                    }
 
334
                    handled = true;
 
335
                    break;
 
336
 
 
337
                default:
 
338
                    i--;
 
339
                    break;
 
340
                }
 
341
            }
 
342
            //if char is not handled continue in default: statement
 
343
            if (handled)
 
344
                break;
 
345
 
 
346
        default: {
 
347
                // One Letter:
 
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);
 
352
                }
 
353
//                if (font->findLetter(QString(data.text.at(i))) != NULL) {
 
354
 
 
355
                                        RS_DEBUG->print("RS_Text::update: insert a "
 
356
                                          "letter at pos: %f/%f", letterPos.x, letterPos.y);
 
357
 
 
358
                    RS_InsertData d(letterText,
 
359
                                    letterPos,
 
360
                                    RS_Vector(1.0, 1.0),
 
361
                                    0.0,
 
362
                                    1,1, RS_Vector(0.0,0.0),
 
363
                                    font->getLetterList(), RS2::NoUpdate);
 
364
 
 
365
                    RS_Insert* letter = new RS_Insert(this, d);
 
366
                    RS_Vector letterWidth;
 
367
                    letter->setPen(RS_Pen(RS2::FlagInvalid));
 
368
                    letter->setLayer(NULL);
 
369
                    letter->update();
 
370
                    letter->forcedCalculateBorders();
 
371
 
 
372
                                        // until 2.0.4.5:
 
373
                    //letterWidth = RS_Vector(letter->getSize().x, 0.0);
 
374
                                        // from 2.0.4.6:
 
375
                    letterWidth = RS_Vector(letter->getMax().x-letterPos.x, 0.0);
 
376
                    if (letterWidth.x < 0)
 
377
                        letterWidth.x = -letterSpace.x;
 
378
 
 
379
                    oneLine->addEntity(letter);
 
380
 
 
381
                    // next letter position:
 
382
                    letterPos += letterWidth;
 
383
                    letterPos += letterSpace;
 
384
//                }
 
385
            }
 
386
            break;
 
387
        }
 
388
    }
 
389
 
 
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);
 
394
    }
 
395
 
 
396
    usedTextHeight -= data.height*data.lineSpacingFactor*5.0/3.0
 
397
                      - data.height;
 
398
    forcedCalculateBorders();
 
399
 
 
400
    RS_DEBUG->print("RS_Text::update: OK");
 
401
}
 
402
 
 
403
 
 
404
 
 
405
/**
 
406
 * Used internally by update() to add a text line created with
 
407
 * default values and alignment to this text container.
 
408
 *
 
409
 * @param textLine The text line.
 
410
 * @param lineCounter Line number.
 
411
 *
 
412
 * @return  distance over the text base-line
 
413
 */
 
414
double RS_MText::updateAddLine(RS_EntityContainer* textLine, int lineCounter) {
 
415
    double ls =5.0/3.0;
 
416
 
 
417
    RS_DEBUG->print("RS_Text::updateAddLine: width: %f", textLine->getSize().x);
 
418
 
 
419
        //textLine->forcedCalculateBorders();
 
420
    //RS_DEBUG->print("RS_Text::updateAddLine: width 2: %f", textLine->getSize().x);
 
421
 
 
422
    // Move to correct line position:
 
423
    textLine->move(RS_Vector(0.0, -9.0 * lineCounter
 
424
                             * data.lineSpacingFactor * ls));
 
425
 
 
426
    if( ! RS_EntityContainer::autoUpdateBorders) {
 
427
        //only update borders when needed
 
428
        textLine->forcedCalculateBorders();
 
429
    }
 
430
    RS_Vector textSize = textLine->getSize();
 
431
 
 
432
        RS_DEBUG->print("RS_Text::updateAddLine: width 2: %f", textSize.x);
 
433
 
 
434
    // Horizontal Align:
 
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));
 
439
        break;
 
440
 
 
441
    case RS_MTextData::HARight:
 
442
        textLine->move(RS_Vector(-textSize.x, 0.0));
 
443
        break;
 
444
 
 
445
    default:
 
446
        break;
 
447
    }
 
448
 
 
449
    // Vertical Align:
 
450
    double vSize = getNumberOfLines()*9.0*data.lineSpacingFactor*ls
 
451
                   - (9.0*data.lineSpacingFactor*ls - 9.0);
 
452
 
 
453
    switch (data.valign) {
 
454
    case RS_MTextData::VAMiddle:
 
455
        textLine->move(RS_Vector(0.0, vSize/2.0));
 
456
        break;
 
457
 
 
458
    case RS_MTextData::VABottom:
 
459
        textLine->move(RS_Vector(0.0, vSize));
 
460
        break;
 
461
 
 
462
    default:
 
463
        break;
 
464
    }
 
465
 
 
466
    // Scale:
 
467
    textLine->scale(RS_Vector(0.0,0.0),
 
468
                    RS_Vector(data.height/9.0, data.height/9.0));
 
469
 
 
470
    textLine->forcedCalculateBorders();
 
471
 
 
472
    // Update actual text size (before rotating, after scaling!):
 
473
    if (textLine->getSize().x>usedTextWidth) {
 
474
        usedTextWidth = textLine->getSize().x;
 
475
    }
 
476
 
 
477
    usedTextHeight += data.height*data.lineSpacingFactor*ls;
 
478
 
 
479
    // Gets the distance over text base-line (before rotating, after scaling!):
 
480
    double textTail = textLine->getMin().y;
 
481
 
 
482
    // Rotate:
 
483
    textLine->rotate(RS_Vector(0.0,0.0), data.angle);
 
484
 
 
485
    // Move:
 
486
    textLine->move(data.insertionPoint);
 
487
    textLine->setPen(RS_Pen(RS2::FlagInvalid));
 
488
    textLine->setLayer(NULL);
 
489
    textLine->forcedCalculateBorders();
 
490
 
 
491
    addEntity(textLine);
 
492
    return textTail;
 
493
}
 
494
 
 
495
 
 
496
RS_Vector RS_MText::getNearestEndpoint(const RS_Vector& coord, double* dist)const {
 
497
    if (dist!=NULL) {
 
498
        *dist = data.insertionPoint.distanceTo(coord);
 
499
    }
 
500
    return data.insertionPoint;
 
501
}
 
502
 
 
503
 
 
504
RS_VectorSolutions RS_MText::getRefPoints() {
 
505
        RS_VectorSolutions ret(data.insertionPoint);
 
506
        return ret;
 
507
}
 
508
 
 
509
 
 
510
RS_Vector RS_MText::getNearestRef(const RS_Vector& coord,
 
511
                                     double* dist) {
 
512
 
 
513
        //return getRefPoints().getClosest(coord, dist);
 
514
        return RS_Entity::getNearestRef(coord, dist);
 
515
}
 
516
 
 
517
 
 
518
void RS_MText::move(const RS_Vector& offset) {
 
519
    RS_EntityContainer::move(offset);
 
520
    data.insertionPoint.move(offset);
 
521
//    update();
 
522
}
 
523
 
 
524
 
 
525
 
 
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);
 
531
//    update();
 
532
}
 
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());
 
537
//    update();
 
538
}
 
539
 
 
540
 
 
541
 
 
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;
 
546
    update();
 
547
}
 
548
 
 
549
 
 
550
 
 
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);
 
555
 
 
556
    RS_Vector vec;
 
557
    vec.setPolar(1.0, data.angle);
 
558
    vec.mirror(RS_Vector(0.0,0.0), axisPoint2-axisPoint1);
 
559
    data.angle = vec.angle();
 
560
 
 
561
    bool corr;
 
562
    data.angle = RS_Math::makeAngleReadable(data.angle, readable, &corr);
 
563
 
 
564
    if (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;
 
569
        }
 
570
    } else {
 
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;
 
575
        }
 
576
    }
 
577
    update();
 
578
}
 
579
 
 
580
 
 
581
 
 
582
bool RS_MText::hasEndpointsWithinWindow(const RS_Vector& /*v1*/, const RS_Vector& /*v2*/) {
 
583
    return false;
 
584
}
 
585
 
 
586
 
 
587
 
 
588
/**
 
589
 * Implementations must stretch the given range of the entity
 
590
 * by the given offset.
 
591
 */
 
592
void RS_MText::stretch(const RS_Vector& firstCorner, const RS_Vector& secondCorner, const RS_Vector& offset) {
 
593
 
 
594
    if (getMin().isInWindow(firstCorner, secondCorner) &&
 
595
            getMax().isInWindow(firstCorner, secondCorner)) {
 
596
 
 
597
        move(offset);
 
598
    }
 
599
}
 
600
 
 
601
 
 
602
 
 
603
/**
 
604
 * Dumps the point's data to stdout.
 
605
 */
 
606
std::ostream& operator << (std::ostream& os, const RS_MText& p) {
 
607
    os << " Text: " << p.getData() << "\n";
 
608
    return os;
 
609
}