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 "GuiPainter.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 USE_PIXMAP_CACHE to 1 for enabling the use of a Pixmap cache when
33 // drawing text. This is especially useful for older PPC/Mac systems.
34 #if (QT_VERSION < 0x040200) || defined(Q_WS_X11)
35 #define USE_PIXMAP_CACHE 0
37 #define USE_PIXMAP_CACHE 1
48 bool const usePixmapCache = USE_PIXMAP_CACHE;
50 QString generateStringSignature(QString const & str, Font const & f)
53 sig.append(QChar(static_cast<short>(f.family())));
54 sig.append(QChar(static_cast<short>(f.series())));
55 sig.append(QChar(static_cast<short>(f.realShape())));
56 sig.append(QChar(static_cast<short>(f.size())));
57 sig.append(QChar(static_cast<short>(f.color())));
63 GuiPainter::GuiPainter(QPaintDevice * device)
64 : QPainter(device), Painter()
66 // new QPainter has default QPen:
67 current_color_ = Color::black;
68 current_ls_ = line_solid;
69 current_lw_ = line_thin;
73 GuiPainter::~GuiPainter()
76 //lyxerr << "GuiPainter::end()" << endl;
80 void GuiPainter::setQPainterPen(Color_color col,
81 Painter::line_style ls, Painter::line_width lw)
83 if (col == current_color_ && ls == current_ls_ && lw == current_lw_)
90 QPen pen = QPainter::pen();
92 pen.setColor(guiApp->colorCache().get(col));
95 case line_solid: pen.setStyle(Qt::SolidLine); break;
96 case line_onoffdash: pen.setStyle(Qt::DotLine); break;
100 case line_thin: pen.setWidth(0); break;
101 case line_thick: pen.setWidth(3); break;
108 void GuiPainter::point(int x, int y, Color_color col)
110 if (!isDrawingEnabled())
118 void GuiPainter::line(int x1, int y1, int x2, int y2,
123 if (!isDrawingEnabled())
126 setQPainterPen(col, ls, lw);
127 bool const do_antialiasing = renderHints() & TextAntialiasing
128 && x1 != x2 && y1 != y2;
129 setRenderHint(Antialiasing, do_antialiasing);
130 drawLine(x1, y1, x2, y2);
131 setRenderHint(Antialiasing, false);
135 void GuiPainter::lines(int const * xp, int const * yp, int np,
140 if (!isDrawingEnabled())
143 // double the size if needed
144 static QVector<QPoint> points(32);
145 if (np > points.size())
146 points.resize(2 * np);
148 bool antialias = false;
149 for (int i = 0; i < np; ++i) {
150 points[i].setX(xp[i]);
151 points[i].setY(yp[i]);
153 antialias |= xp[i-1] != xp[i] && yp[i-1] != yp[i];
155 setQPainterPen(col, ls, lw);
156 bool const text_is_antialiased = renderHints() & TextAntialiasing;
157 setRenderHint(Antialiasing, antialias && text_is_antialiased);
158 drawPolyline(points.data(), np);
159 setRenderHint(Antialiasing, false);
163 void GuiPainter::rectangle(int x, int y, int w, int h,
168 if (!isDrawingEnabled())
171 setQPainterPen(col, ls, lw);
172 drawRect(x, y, w, h);
176 void GuiPainter::fillRectangle(int x, int y, int w, int h, Color_color col)
178 fillRect(x, y, w, h, guiApp->colorCache().get(col));
182 void GuiPainter::arc(int x, int y, unsigned int w, unsigned int h,
183 int a1, int a2, Color_color col)
185 if (!isDrawingEnabled())
188 // LyX usings 1/64ths degree, Qt usings 1/16th
190 bool const do_antialiasing = renderHints() & TextAntialiasing;
191 setRenderHint(Antialiasing, do_antialiasing);
192 drawArc(x, y, w, h, a1 / 4, a2 / 4);
193 setRenderHint(Antialiasing, false);
197 void GuiPainter::image(int x, int y, int w, int h, graphics::Image const & i)
199 graphics::GuiImage const & qlimage =
200 static_cast<graphics::GuiImage const &>(i);
202 fillRectangle(x, y, w, h, Color::graphicsbg);
204 if (!isDrawingEnabled())
207 drawImage(x, y, qlimage.qimage(), 0, 0, w, h);
211 int GuiPainter::text(int x, int y, char_type c, Font const & f)
214 return text(x, y, s, f);
218 int GuiPainter::smallCapsText(int x, int y,
219 QString const & s, Font const & f)
222 smallfont.decSize().decSize().setShape(Font::UP_SHAPE);
224 QFont const & qfont = guiApp->guiFontLoader().get(f);
225 QFont const & qsmallfont = guiApp->guiFontLoader().get(smallfont);
227 setQPainterPen(f.realColor());
229 size_t const ls = s.length();
230 for (unsigned int i = 0; i < ls; ++i) {
231 QChar const c = s[i].toUpper();
237 if (isDrawingEnabled())
238 drawText(x + textwidth, y, c);
239 textwidth += fontMetrics().width(c);
245 int GuiPainter::text(int x, int y, docstring const & s,
251 /* Caution: The following ucs4 to QString conversions work for symbol fonts
252 only because they are no real conversions but simple casts in reality.
253 When we want to draw a symbol or calculate the metrics we pass the position
254 of the symbol in the font (as given in lib/symbols) as a char_type to the
255 frontend. This is just wrong, because the symbol is no UCS4 character at
256 all. You can think of this number as the code point of the symbol in a
257 custom symbol encoding. It works because this char_type is lateron again
258 interpreted as a position in the font again.
259 The correct solution would be to have extra functions for symbols, but that
260 would require to duplicate a lot of frontend and mathed support code.
262 QString str = toqstr(s);
265 // HACK: QT3 refuses to show single compose characters
266 // Still needed with Qt4?
267 if (ls == 1 && str[0].unicode() >= 0x05b0 && str[0].unicode() <= 0x05c2)
271 GuiFontInfo & fi = guiApp->guiFontLoader().fontinfo(f);
275 if (f.realShape() == Font::SMALLCAPS_SHAPE) {
276 textwidth = smallCapsText(x, y, str, f);
277 if (f.underbar() == Font::ON)
278 underline(f, x, y, textwidth);
282 // Here we use the font width cache instead of
283 // textwidth = fontMetrics().width(str);
284 // because the above is awfully expensive on MacOSX
285 textwidth = fi.metrics->width(s);
286 if (f.underbar() == Font::ON)
287 underline(f, x, y, textwidth);
289 if (!isDrawingEnabled())
292 // Qt4 does not display a glyph whose codepoint is the
293 // same as that of a soft-hyphen (0x00ad), unless it
294 // occurs at a line-break. As a kludge, we force Qt to
295 // render this glyph using a one-column line.
296 if (s.size() == 1 && str[0].unicode() == 0x00ad) {
297 setQPainterPen(f.realColor());
298 QTextLayout adsymbol(str);
299 adsymbol.setFont(fi.font);
300 adsymbol.beginLayout();
301 QTextLine line = adsymbol.createLine();
302 line.setNumColumns(1);
303 line.setPosition(QPointF(0, -line.ascent()));
304 adsymbol.endLayout();
305 line.draw(this, QPointF(x, y));
309 if (!usePixmapCache) {
310 // don't use the pixmap cache,
311 // draw directly onto the painting device
312 setQPainterPen(f.realColor());
313 if (font() != fi.font)
315 // We need to draw the text as LTR as we use our own bidi code.
316 setLayoutDirection(Qt::LeftToRight);
317 // We need to draw the text as LTR as we use our own bidi code.
318 setLayoutDirection(Qt::LeftToRight);
320 //LYXERR(Debug::PAINTING) << "draw " << std::string(str.toUtf8())
321 // << " at " << x << "," << y << std::endl;
326 QString key = generateStringSignature(str, f);
327 // Warning: Left bearing is in general negative! Only the case
328 // where left bearing is negative is of interest WRT the the
329 // pixmap width and the text x-position.
330 // Only the left bearing of the first character is important
331 // as we always write from left to right, even for
332 // right-to-left languages.
333 int const lb = std::min(fi.metrics->lbearing(s[0]), 0);
334 int const mA = fi.metrics->maxAscent();
335 if (!QPixmapCache::find(key, pm)) {
336 // Only the right bearing of the last character is
337 // important as we always write from left to right,
338 // even for right-to-left languages.
339 int const rb = fi.metrics->rbearing(s[s.size()-1]);
340 int const w = textwidth + rb - lb;
341 int const mD = fi.metrics->maxDescent();
342 int const h = mA + mD;
344 pm.fill(Qt::transparent);
346 p.setQPainterPen(f.realColor());
347 if (p.font() != fi.font)
349 // We need to draw the text as LTR as we use our own bidi code.
350 p.setLayoutDirection(Qt::LeftToRight);
351 p.drawText(-lb, mA, str);
352 QPixmapCache::insert(key, pm);
353 //LYXERR(Debug::PAINTING) << "h=" << h << " mA=" << mA << " mD=" << mD
354 // << " w=" << w << " lb=" << lb << " tw=" << textwidth
355 // << " rb=" << rb << endl;
357 // Draw the cached pixmap.
358 drawPixmap(x + lb, y - mA, pm);
364 } // namespace frontend