1
// --------------------------------------------------------------------
3
// --------------------------------------------------------------------
6
This file is part of the extensible drawing editor Ipe.
7
Copyright (C) 1993-2007 Otfried Cheong
9
Ipe is free software; you can redistribute it and/or modify it
10
under the terms of the GNU General Public License as published by
11
the Free Software Foundation; either version 2 of the License, or
12
(at your option) any later version.
14
As a special exception, you have permission to link Ipe with the
15
CGAL library and distribute executables, as long as you follow the
16
requirements of the Gnu General Public License in regard to all of
17
the software in the executable aside from CGAL.
19
Ipe is distributed in the hope that it will be useful, but WITHOUT
20
ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
21
or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public
22
License for more details.
24
You should have received a copy of the GNU General Public License
25
along with Ipe; if not, you can find it at
26
"http://www.gnu.org/copyleft/gpl.html", or write to the Free
27
Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
32
#include "ipepdfparser.h"
33
#include "ipecanvas.h"
34
#include "ipecanvaspainter.h"
39
#include <QPaintDevice>
40
#include <QApplication>
42
#include <QPainterPath>
44
// What a mess is Windows!
49
inline QPointF QPt(const IpeVector &v)
51
return QPointF(v.iX, v.iY);
54
inline IpeString IpeQ(const QString &str)
56
return IpeString(str.toUtf8());
59
inline QString QIpe(const IpeString &str)
61
return QString::fromUtf8(str.CString());
64
// --------------------------------------------------------------------
66
class IpeRenderData : public IpeBitmap::MRenderData {
68
virtual ~IpeRenderData() { /* Nothing */ }
72
// --------------------------------------------------------------------
74
IpeCanvasPainter::IpeCanvasPainter(const IpeStyleSheet *sheet,
75
IpeCanvasFonts *fonts, QPainter *painter,
76
double zoom, bool pretty,
78
: IpePainter(sheet), iFonts(fonts),
79
iPainter(painter), iZoom(zoom), iPretty(pretty),
80
iMaxBitmapSize(maxBitmapSize)
83
iPainter->setFont(QApplication::font());
85
// these symbolic dash styles are on fixed indices
86
for (int i = 0; i < 4; ++i)
88
StyleSheet()->Find(IpeAttribute(IpeAttribute::EDashStyle, true, i+1));
91
void IpeCanvasPainter::DoMoveTo(const IpeVector &v)
93
if (iType.size() > 0 && iType.back() != EEndClosedPath)
94
iType.push_back(EEndPath);
95
iV.push_back(QPt(Matrix() * v));
96
iType.push_back(EMoveTo);
99
void IpeCanvasPainter::DoLineTo(const IpeVector &v)
101
iV.push_back(QPt(Matrix() * v));
102
iType.push_back(ELineTo);
105
void IpeCanvasPainter::DoCurveTo(const IpeVector &v1, const IpeVector &v2,
108
iV.push_back(QPt(Matrix() * v1));
109
iV.push_back(QPt(Matrix() * v2));
110
iV.push_back(QPt(Matrix() * v3));
111
iType.push_back(ECurveTo);
114
void IpeCanvasPainter::DoClosePath()
116
iType.push_back(EEndClosedPath);
119
void IpeCanvasPainter::DoDrawPath()
121
if (iType.size() > 0 && iType.back() != EEndClosedPath)
122
iType.push_back(EEndPath);
124
const IpeRepository *rep = StyleSheet()->Repository();
129
for (uint i = 0; i < iType.size(); ++i) {
132
path.moveTo(iV[k++]);
135
path.lineTo(iV[k++]);
138
path.cubicTo(iV[k], iV[k+1], iV[k+2]);
148
IpeAttribute windRule = WindRule();
150
windRule = StyleSheet()->WindRule();
151
Qt::FillRule wind = (windRule.Index() != 0) ?
152
Qt::WindingFill : Qt::OddEvenFill;
153
path.setFillRule(wind);
159
IpeAttribute fill = Fill();
160
IpeAttribute stroke = Stroke();
161
IpeAttribute dash = DashStyle();
164
if (fill.IsNullOrVoid() && dash.IsVoid())
167
if (!fill.IsNullOrVoid()) {
168
assert(fill.IsAbsolute());
169
IpeColor fillColor = rep->ToColor(fill);
170
QColor qfill(int(fillColor.iRed * 255),
171
int(fillColor.iGreen * 255),
172
int(fillColor.iBlue * 255));
175
brush.setColor(qfill);
176
brush.setStyle(Qt::SolidPattern);
177
iPainter->setBrush(brush);
179
iPainter->setBrush(Qt::NoBrush);
181
if (!dash.IsVoid() && !stroke.IsNull()) {
182
assert(stroke.IsAbsolute());
183
IpeColor strokeColor = rep->ToColor(stroke);
185
QColor qstroke(int(strokeColor.iRed * 255),
186
int(strokeColor.iGreen * 255),
187
int(strokeColor.iBlue * 255));
189
pen.setColor(qstroke);
192
assert(LineWidth().IsAbsolute());
193
double wid = iZoom * rep->ToScalar(LineWidth()).ToDouble();
194
pen.setWidth(uint(wid + 0.5));
196
IpeAttribute join = LineJoin();
198
join = StyleSheet()->LineJoin();
199
switch (join.Index()) {
200
case IpeStrokeStyle::EMiterJoin:
201
pen.setJoinStyle(Qt::MiterJoin);
203
case IpeStrokeStyle::ERoundJoin:
204
pen.setJoinStyle(Qt::RoundJoin);
206
case IpeStrokeStyle::EBevelJoin:
207
pen.setJoinStyle(Qt::BevelJoin);
210
IpeAttribute cap = LineCap();
212
cap = StyleSheet()->LineCap();
213
switch (cap.Index()) {
214
case IpeStrokeStyle::EButtCap:
215
pen.setCapStyle(Qt::FlatCap);
217
case IpeStrokeStyle::ERoundCap:
218
pen.setCapStyle(Qt::RoundCap);
220
case IpeStrokeStyle::ESquareCap:
221
pen.setCapStyle(Qt::SquareCap);
224
if (!dash.IsNullOrSolid()) {
225
// now comes the tricky part
226
if (dash == iDash[0])
227
pen.setStyle(Qt::DashLine);
228
else if (dash == iDash[1])
229
pen.setStyle(Qt::DotLine);
230
else if (dash == iDash[2])
231
pen.setStyle(Qt::DashDotLine);
232
else if (dash == iDash[3])
233
pen.setStyle(Qt::DashDotDotLine);
235
pen.setStyle(Qt::DotLine);
237
iPainter->setPen(pen);
239
iPainter->setPen(Qt::NoPen);
241
iPainter->drawPath(path);
244
void IpeCanvasPainter::DoDrawBitmap(IpeBitmap bitmap)
246
if (!bitmap.RenderData()) {
247
// todo: should we remember if this failed?
248
IpeBuffer data = bitmap.PixelData();
249
if (data.size() > 0) {
250
IpeRenderData *render = new IpeRenderData;
251
// warning: data buffer must remain alive as long as img is
252
QImage img((uchar *) data.data(), bitmap.Width(), bitmap.Height(),
253
QImage::Format_RGB32);
254
if (img.width() > iMaxBitmapSize || img.height() > iMaxBitmapSize) {
255
int wid = img.width();
256
int ht = img.height();
259
factor = wid / double(iMaxBitmapSize);
261
factor = ht / double(iMaxBitmapSize);
262
wid = int(wid / factor + 0.5);
263
ht = int(ht / factor + 0.5);
264
render->iImage = img.scaled(wid, ht, Qt::KeepAspectRatio,
265
Qt::FastTransformation);
267
render->iImage = img.copy();
268
// img no longer needed
269
bitmap.SetRenderData(render);
273
if (bitmap.RenderData()) {
274
IpeRenderData *render = static_cast<IpeRenderData *>(bitmap.RenderData());
275
IpeMatrix tf = Matrix() * IpeMatrix(1.0 / render->iImage.width(), 0.0,
276
0.0, -1.0 / render->iImage.height(),
279
m.setMatrix(tf.iA[0], tf.iA[1], tf.iA[2], tf.iA[3], tf.iA[4], tf.iA[5]);
280
iPainter->setMatrix(m);
281
iPainter->drawImage(0, 0, render->iImage);
282
iPainter->resetMatrix();
286
void IpeCanvasPainter::DoDrawText(const IpeText *text)
288
// Current origin is lower left corner of text box
290
// Draw bounding box rectangle
291
if (!iPretty && !iDimmed && !text->isInternal()) {
293
pen.setColor(Qt::green);
294
pen.setStyle(Qt::DotLine);
295
iPainter->setPen(pen);
296
iPainter->setBrush(Qt::NoBrush);
298
poly.append(QPt(Matrix() * IpeVector(0,0)));
299
poly.append(QPt(Matrix() * IpeVector(0, text->TotalHeight())));
300
poly.append(QPt(Matrix() * IpeVector(text->Width(), text->TotalHeight())));
301
poly.append(QPt(Matrix() * IpeVector(text->Width(), 0)));
302
iPainter->drawConvexPolygon(poly);
303
iPainter->setPen(Qt::NoPen);
304
iPainter->setBrush(Qt::green);
305
QPointF ref = QPt(Matrix() * text->Align());
306
QRectF r(ref.x() - 3, ref.y() - 3, 6, 6);
307
iPainter->drawRect(r);
310
IpeAttribute str = Stroke();
311
if (str.IsNull()) str = IpeAttribute::Black();
312
assert(str.IsAbsolute());
313
IpeColor stroke = StyleSheet()->Repository()->ToColor(str);
314
QColor col(int(stroke.iRed * 255), int(stroke.iGreen * 255),
315
int(stroke.iBlue * 255));
318
const IpeText::XForm *xf = text->getXForm();
319
if (!xf || !iFonts) {
320
if (!text->isInternal()) {
321
QString s = QIpe(text->Text());
322
int olen = s.length();
323
int i = s.indexOf(QLatin1Char('\n'));
327
if (s.length() < olen)
328
s += QLatin1String("...");
330
QPointF pt = QPt(Matrix().Translation());
331
pt.setY(pt.y() - iPainter->fontMetrics().descent());
332
iPainter->setPen(col);
333
iPainter->drawText(pt, s);
336
iTextRgb = col.rgb();
337
Transform(IpeMatrix(xf->iStretch, 0, 0, xf->iStretch, 0, 0));
338
Execute(xf->iStream);
342
// --------------------------------------------------------------------
344
//! If dimming, replace color by dimmed version.
345
void IpeCanvasPainter::DimColor(QColor &col)
349
col.getHsv(&h, &s, &v);
352
col = col.light(175);
358
// --------------------------------------------------------------------
360
//! Clear PDF argument stack
361
void IpeCanvasPainter::ClearArgs()
363
while (!iArgs.empty()) {
369
void IpeCanvasPainter::Execute(const IpeBuffer &buffer)
371
IpeBufferSource source(buffer);
372
IpePdfParser parser(source);
375
iStrokeRgb = 0x000000;
376
while (!parser.Eos()) {
377
IpePdfToken tok = parser.Token();
378
if (tok.iType != IpePdfToken::EOp) {
379
const IpePdfObj *obj = parser.GetObject();
381
break; // no further parsing attempted
382
iArgs.push_back(obj);
384
// its an operator, execute it
385
IpeString op = tok.iString;
421
else if (op != "ET") {
423
for (uint i = 0; i < iArgs.size(); ++i)
424
a += iArgs[i]->Repr() + " ";
425
ipeDebug("Op %s (%s)", op.CString(), a.CString());
433
void IpeCanvasPainter::Opg(bool stroke)
435
if (iArgs.size() != 1 || !iArgs[0]->Number())
437
int gr = int(iArgs[0]->Number()->Value() * 255);
439
iStrokeRgb = qRgb(gr, gr, gr);
441
iFillRgb = qRgb(gr, gr, gr);
444
void IpeCanvasPainter::Opre()
446
if (iArgs.size() != 4 || !iArgs[0]->Number() || !iArgs[1]->Number()
447
|| !iArgs[2]->Number() || !iArgs[3]->Number())
450
// The 3rd/4th argument is the size of the rectangle
451
IpeVector ll(iArgs[0]->Number()->Value(), iArgs[1]->Number()->Value());
452
IpeVector wh(iArgs[2]->Number()->Value(), iArgs[3]->Number()->Value());
455
brush.setStyle(Qt::SolidPattern);
456
brush.setColor(QRgb(iFillRgb));
457
iPainter->setBrush(brush);
458
QPen pen(Qt::NoPen); // no stroke
459
iPainter->setPen(pen);
460
QPointF p = QPt(Matrix() * ll);
461
QPointF q = QPt(Matrix().Linear() * wh);
462
iPainter->drawRect(QRectF(p.x(), p.y(), q.x(), q.y()));
465
void IpeCanvasPainter::Oprg(bool stroke)
467
if (iArgs.size() != 3 || !iArgs[0]->Number() || !iArgs[1]->Number()
468
|| !iArgs[2]->Number())
470
int r = int(iArgs[0]->Number()->Value() * 255);
471
int g = int(iArgs[1]->Number()->Value() * 255);
472
int b = int(iArgs[2]->Number()->Value() * 255);
476
iStrokeRgb = col.rgb();
478
iFillRgb = col.rgb();
481
void IpeCanvasPainter::Opk(bool stroke)
483
if (iArgs.size() != 4 || !iArgs[0]->Number() || !iArgs[1]->Number()
484
|| !iArgs[2]->Number() || !iArgs[3]->Number())
486
// ignore values, but use text object stroke color
488
iStrokeRgb = iTextRgb;
493
void IpeCanvasPainter::Opcm()
495
if (iArgs.size() != 6)
498
for (int i = 0; i < 6; ++i) {
499
if (!iArgs[i]->Number())
501
m.iA[i] = iArgs[i]->Number()->Value();
506
void IpeCanvasPainter::Opw()
508
if (iArgs.size() != 1 || !iArgs[0]->Number())
510
iLineWid = iArgs[0]->Number()->Value();
514
void IpeCanvasPainter::Opq()
516
if (iArgs.size() != 0)
521
void IpeCanvasPainter::OpQ()
523
if (iArgs.size() != 0)
528
void IpeCanvasPainter::Opm()
530
if (iArgs.size() != 2 || !iArgs[0]->Number() || !iArgs[1]->Number())
532
IpeVector t(iArgs[0]->Number()->Value(), iArgs[1]->Number()->Value());
536
void IpeCanvasPainter::Opl()
538
if (iArgs.size() != 2 || !iArgs[0]->Number() || !iArgs[1]->Number())
540
IpeVector t(iArgs[0]->Number()->Value(), iArgs[1]->Number()->Value());
543
pen.setColor(QRgb(iStrokeRgb));
544
pen.setWidth(uint(iZoom * iLineWid + 0.5));
545
iPainter->setPen(pen);
546
iPainter->drawLine(QPt(Matrix() * iMoveTo), QPt(Matrix() * t));
549
void IpeCanvasPainter::OpBT()
552
iTextPos = iTextLinePos = IpeVector::Zero;
555
void IpeCanvasPainter::OpTf()
557
if (iArgs.size() != 2 || !iArgs[0]->Name() || !iArgs[1]->Number())
559
IpeString name = iArgs[0]->Name()->Value();
560
iFontSize = iArgs[1]->Number()->Value();
563
int font = IpeLex(name.substr(1)).GetInt();
564
// ipeDebug("Setting font %d at %g", font, iFontSize);
565
IpeLinear m = Matrix().Linear() * IpeLinear(iFontSize, 0, 0, iFontSize);
566
iFont = iFonts->GetSize(font, m);
569
void IpeCanvasPainter::OpTd()
571
if (iArgs.size() != 2 || !iArgs[0]->Number() || !iArgs[1]->Number())
573
IpeVector t(iArgs[0]->Number()->Value(), iArgs[1]->Number()->Value());
574
iTextPos = iTextLinePos = iTextLinePos + t;
577
void IpeCanvasPainter::OpTJ()
579
if (!iFont || iArgs.size() != 1 || !iArgs[0]->Array())
581
for (int i = 0; i < iArgs[0]->Array()->Count(); ++i) {
582
const IpePdfObj *obj = iArgs[0]->Array()->Obj(i, 0);
584
iTextPos.iX -= 0.001 * iFontSize * obj->Number()->Value();
585
} else if (obj->String()) {
586
IpeString s = obj->String()->Value();
587
for (int j = 0; j < s.size(); ++j) {
589
IpeVector pt = Matrix() * iTextPos;
590
DrawChar(ch, iFillRgb, int(pt.iX + 0.5), int(pt.iY + 0.5));
591
iTextPos.iX += 0.001 * iFontSize * iFont->Face()->Width(ch);
597
// --------------------------------------------------------------------
600
/*! Glyph is drawn with hotspot at position (x,y) (on the pixmap) */
601
bool IpeCanvasPainter::DrawChar(int ch, QRgb rgb, int x, int y)
606
// generate the glyph pixmap
608
int xOffset, yOffset, gw, gh;
609
uchar *p = iFont->GetGlyph(ch, xOffset, yOffset, gw, gh);
613
QImage image(gw, gh, QImage::Format_ARGB32);
615
if (iFonts->AntiAlias()) {
616
rgb &= 0x00ffffff; // take out alpha
617
for (int yy = 0; yy < gh; ++yy) {
618
uint *dst = (uint *) image.scanLine(yy);
619
for (int xx = 0; xx < gw; ++xx) {
620
uint pix = (*p++ & 0xff) << 24;
625
image.fill(0x00ffffff);
626
rgb = 0xff000000 | rgb;
628
for (int yy = 0; yy < gh; ++yy) {
629
uint *dst = (uint *) image.scanLine(yy);
630
for (int xx = 0; xx < gw; xx += 8) {
632
for (int xx1 = xx; xx1 < xx + 8 && xx1 < gw; ++xx1) {
641
iPainter->drawImage(x - xOffset, y - yOffset, image);
645
// --------------------------------------------------------------------