3 * This file is part of LyX, the document processor.
4 * Licence details can be found in the file COPYING.
7 * \author Abdelrazak Younes
9 * Full author contact details are available in file CREDITS.
14 #include "QLPainter.h"
16 #include "GuiApplication.h"
17 #include "GuiFontMetrics.h"
20 #include "GuiApplication.h"
21 #include "qt_helpers.h"
27 #include "support/unicode.h"
29 #include <QPixmapCache>
30 #include <QTextLayout>
32 // Set this to one for enabling the use of a Pixmap cache when drawing
33 // text. This is especially useful for older PPC/Mac systems.
34 #define USE_PIXMAP_CACHE 1
44 bool const usePixmapCache = USE_PIXMAP_CACHE;
46 QString generateStringSignature(QString const & str, Font const & f)
49 sig.append(QChar(static_cast<short>(f.family())));
50 sig.append(QChar(static_cast<short>(f.series())));
51 sig.append(QChar(static_cast<short>(f.realShape())));
52 sig.append(QChar(static_cast<short>(f.size())));
53 sig.append(QChar(static_cast<short>(f.color())));
59 QLPainter::QLPainter(QPaintDevice * device)
60 : QPainter(device), Painter()
62 // new QPainter has default QPen:
63 current_color_ = Color::black;
64 current_ls_ = line_solid;
65 current_lw_ = line_thin;
69 QLPainter::~QLPainter()
72 //lyxerr << "QLPainter::end()" << endl;
76 void QLPainter::setQPainterPen(Color_color col,
77 Painter::line_style ls, Painter::line_width lw)
79 if (col == current_color_ && ls == current_ls_ && lw == current_lw_)
86 QPen pen = QPainter::pen();
88 pen.setColor(guiApp->colorCache().get(col));
91 case line_solid: pen.setStyle(Qt::SolidLine); break;
92 case line_onoffdash: pen.setStyle(Qt::DotLine); break;
96 case line_thin: pen.setWidth(0); break;
97 case line_thick: pen.setWidth(3); break;
104 void QLPainter::point(int x, int y, Color_color col)
106 if (!isDrawingEnabled())
114 void QLPainter::line(int x1, int y1, int x2, int y2,
119 if (!isDrawingEnabled())
122 setQPainterPen(col, ls, lw);
123 bool const do_antialiasing = renderHints() & TextAntialiasing
124 && x1 != x2 && y1 != y2;
125 setRenderHint(Antialiasing, do_antialiasing);
126 drawLine(x1, y1, x2, y2);
127 setRenderHint(Antialiasing, false);
131 void QLPainter::lines(int const * xp, int const * yp, int np,
136 if (!isDrawingEnabled())
139 // double the size if needed
140 static QVector<QPoint> points(32);
141 if (np > points.size())
142 points.resize(2 * np);
144 bool antialias = false;
145 for (int i = 0; i < np; ++i) {
146 points[i].setX(xp[i]);
147 points[i].setY(yp[i]);
149 antialias |= xp[i-1] != xp[i] && yp[i-1] != yp[i];
151 setQPainterPen(col, ls, lw);
152 bool const text_is_antialiased = renderHints() & TextAntialiasing;
153 setRenderHint(Antialiasing, antialias && text_is_antialiased);
154 drawPolyline(points.data(), np);
155 setRenderHint(Antialiasing, false);
159 void QLPainter::rectangle(int x, int y, int w, int h,
164 if (!isDrawingEnabled())
167 setQPainterPen(col, ls, lw);
168 drawRect(x, y, w, h);
172 void QLPainter::fillRectangle(int x, int y, int w, int h, Color_color col)
174 fillRect(x, y, w, h, guiApp->colorCache().get(col));
178 void QLPainter::arc(int x, int y, unsigned int w, unsigned int h,
179 int a1, int a2, Color_color col)
181 if (!isDrawingEnabled())
184 // LyX usings 1/64ths degree, Qt usings 1/16th
186 bool const do_antialiasing = renderHints() & TextAntialiasing;
187 setRenderHint(Antialiasing, do_antialiasing);
188 drawArc(x, y, w, h, a1 / 4, a2 / 4);
189 setRenderHint(Antialiasing, false);
193 void QLPainter::image(int x, int y, int w, int h, graphics::Image const & i)
195 graphics::QLImage const & qlimage =
196 static_cast<graphics::QLImage const &>(i);
198 fillRectangle(x, y, w, h, Color::graphicsbg);
200 if (!isDrawingEnabled())
203 drawImage(x, y, qlimage.qimage(), 0, 0, w, h);
207 int QLPainter::text(int x, int y, char_type c, Font const & f)
210 return text(x, y, s, f);
214 int QLPainter::smallCapsText(int x, int y,
215 QString const & s, Font const & f)
218 smallfont.decSize().decSize().setShape(Font::UP_SHAPE);
220 QFont const & qfont = guiApp->guiFontLoader().get(f);
221 QFont const & qsmallfont = guiApp->guiFontLoader().get(smallfont);
223 setQPainterPen(f.realColor());
225 size_t const ls = s.length();
226 for (unsigned int i = 0; i < ls; ++i) {
227 QChar const c = s[i].toUpper();
233 if (isDrawingEnabled())
234 drawText(x + textwidth, y, c);
235 textwidth += fontMetrics().width(c);
241 int QLPainter::text(int x, int y, docstring const & s,
244 /* Caution: The following ucs4 to QString conversions work for symbol fonts
245 only because they are no real conversions but simple casts in reality.
246 When we want to draw a symbol or calculate the metrics we pass the position
247 of the symbol in the font (as given in lib/symbols) as a char_type to the
248 frontend. This is just wrong, because the symbol is no UCS4 character at
249 all. You can think of this number as the code point of the symbol in a
250 custom symbol encoding. It works because this char_type is lateron again
251 interpreted as a position in the font again.
252 The correct solution would be to have extra functions for symbols, but that
253 would require to duplicate a lot of frontend and mathed support code.
255 QString str = toqstr(s);
258 // HACK: QT3 refuses to show single compose characters
259 // Still needed with Qt4?
260 if (ls == 1 && str[0].unicode() >= 0x05b0 && str[0].unicode() <= 0x05c2)
264 QLFontInfo & fi = guiApp->guiFontLoader().fontinfo(f);
268 if (f.realShape() == Font::SMALLCAPS_SHAPE) {
269 textwidth = smallCapsText(x, y, str, f);
270 if (f.underbar() == Font::ON)
271 underline(f, x, y, textwidth);
275 // Here we use the font width cache instead of
276 // textwidth = fontMetrics().width(str);
277 // because the above is awfully expensive on MacOSX
278 textwidth = fi.metrics->width(s);
279 if (f.underbar() == Font::ON)
280 underline(f, x, y, textwidth);
282 if (!isDrawingEnabled())
285 // Qt4 does not display a glyph whose codepoint is the
286 // same as that of a soft-hyphen (0x00ad), unless it
287 // occurs at a line-break. As a kludge, we force Qt to
288 // render this glyph using a one-column line.
289 if (s.size() == 1 && str[0].unicode() == 0x00ad) {
290 setQPainterPen(f.realColor());
291 QTextLayout adsymbol(str);
292 adsymbol.setFont(fi.font);
293 adsymbol.beginLayout();
294 QTextLine line = adsymbol.createLine();
295 line.setNumColumns(1);
296 line.setPosition(QPointF(0, -line.ascent()));
297 adsymbol.endLayout();
298 line.draw(this, QPointF(x, y));
302 if (!usePixmapCache) {
303 // don't use the pixmap cache,
304 // draw directly onto the painting device
305 setQPainterPen(f.realColor());
306 if (font() != fi.font)
308 // We need to draw the text as LTR as we use our own bidi code.
309 setLayoutDirection(Qt::LeftToRight);
310 // We need to draw the text as LTR as we use our own bidi code.
311 setLayoutDirection(Qt::LeftToRight);
313 LYXERR(Debug::PAINTING) << "draw " << std::string(str.toUtf8())
314 << " at " << x << "," << y << std::endl;
319 QString key = generateStringSignature(str, f);
320 // Warning: Left bearing is in general negative! Only the case
321 // where left bearing is negative is of interest WRT the the
322 // pixmap width and the text x-position.
323 // Only the left bearing of the first character is important
324 // as we always write from left to right, even for
325 // right-to-left languages.
326 int const lb = std::min(fi.metrics->lbearing(s[0]), 0);
327 int const mA = fi.metrics->maxAscent();
328 if (!QPixmapCache::find(key, pm)) {
329 // Only the right bearing of the last character is
330 // important as we always write from left to right,
331 // even for right-to-left languages.
332 int const rb = fi.metrics->rbearing(s[s.size()-1]);
333 int const w = textwidth + rb - lb;
334 int const mD = fi.metrics->maxDescent();
335 int const h = mA + mD;
337 pm.fill(Qt::transparent);
339 p.setQPainterPen(f.realColor());
340 if (p.font() != fi.font)
342 // We need to draw the text as LTR as we use our own bidi code.
343 p.setLayoutDirection(Qt::LeftToRight);
344 p.drawText(-lb, mA, str);
345 QPixmapCache::insert(key, pm);
346 LYXERR(Debug::PAINTING) << "h=" << h << " mA=" << mA << " mD=" << mD
347 << " w=" << w << " lb=" << lb << " tw=" << textwidth
348 << " rb=" << rb << endl;
350 // Draw the cached pixmap.
351 drawPixmap(x + lb, y - mA, pm);
357 } // namespace frontend