Inkscape
Vector Graphics Editor
Loading...
Searching...
No Matches
drawing-text.cpp
Go to the documentation of this file.
1// SPDX-License-Identifier: GPL-2.0-or-later
/*
6 * Authors:
7 * Krzysztof KosiƄski <tweenk.pl@gmail.com>
8 *
9 * Copyright (C) 2011 Authors
10 * Released under GNU GPL v2+, read the file 'COPYING' for more information.
11 */
12
13#include <2geom/pathvector.h>
14
15#include <iostream>
16#include <iomanip>
17
18#include "style.h"
19
20#include "cairo-utils.h"
21#include "drawing-context.h"
22#include "drawing-surface.h"
23#include "drawing-text.h"
24#include "drawing.h"
25
26#include "helper/geom.h"
27
29
30namespace Inkscape {
31
33 : DrawingItem(drawing)
34 , _glyph(0)
35{
36}
37
38void DrawingGlyphs::setGlyph(std::shared_ptr<FontInstance> font, unsigned int glyph, Geom::Affine const &trans)
39{
40 defer([=, this, font = std::move(font)] {
42
43 assert(!_drawing.snapshotted());
44 setTransform(trans);
45
46 _font_data = font->share_data();
47 _glyph = glyph;
48
49 design_units = 1.0;
50 pathvec = nullptr;
51 pixbuf = nullptr;
52
53 // Load pathvectors and pixbufs in advance, as must be done on main thread.
54 if (font) {
55 design_units = font->GetDesignUnits();
56 pathvec = font->PathVector(_glyph);
57 bbox_exact = font->BBoxExact(_glyph);
58 bbox_pick = font->BBoxPick( _glyph);
59 bbox_draw = font->BBoxDraw( _glyph);
60 if (font->FontHasSVG()) {
61 pixbuf = font->PixBuf(_glyph);
62 }
63 font_descr = pango_font_description_to_string(font->get_descr());
64 // std::cout << "DrawingGlyphs::setGlyph: " << std::setw(6) << glyph
65 // << " design_units: " << design_units
66 // << " bbox_exact: " << bbox_exact
67 // << " bbox_pick: " << bbox_pick
68 // << " bbox_draw: " << bbox_draw
69 // << std::endl;
70 }
71
73 });
74}
75
77{
78 std::cerr << "DrawingGlyphs: Use parent style" << std::endl;
79}
80
81unsigned DrawingGlyphs::_updateItem(Geom::IntRect const &/*area*/, UpdateContext const &ctx, unsigned /*flags*/, unsigned /*reset*/)
82{
83 auto ggroup = cast<DrawingText>(&std::as_const(*_parent));
84 if (!ggroup) {
86 }
87
88 if (!pathvec) {
89 // Bitmap font
90 return STATE_ALL;
91 }
92
93 Geom::Rect bbox_pick_scaled_d = bbox_pick * ctx.ctm;
94 Geom::Rect bbox_draw_scaled_d = bbox_draw * ctx.ctm;
95
96 if (ggroup->_nrstyle.data.stroke.type != NRStyleData::PaintType::NONE) {
97 // this expands the selection box for cases where the stroke is "thick"
98 float scale = ctx.ctm.descrim();
99 if (_transform) {
100 scale /= _transform->descrim(); // FIXME temporary hack
101 }
102 float width = std::max<double>(0.125, ggroup->_nrstyle.data.stroke_width * scale);
103 if (std::fabs(ggroup->_nrstyle.data.stroke_width * scale) > 0.01) { // FIXME: this is always true
104 bbox_pick_scaled_d.expandBy(0.5 * width);
105 bbox_draw_scaled_d.expandBy(0.5 * width);
106 }
107
108 float miterMax = width * ggroup->_nrstyle.data.miter_limit;
109 if (miterMax > 0.01) {
110 // grunt mode. we should compute the various miters instead
111 // (one for each point on the curve)
112 bbox_draw_scaled_d.expandBy(miterMax);
113 }
114 }
115
116 bbox_pick_scaled = bbox_pick_scaled_d.roundOutwards(); // Used for picking
117 bbox_draw_scaled = bbox_draw_scaled_d.roundOutwards(); // Used for drawing
118
119 // drawing-item variable
121
122 // std::cout << "DrawingGlyphs::_updateItem: "
123 // << " glyph: " << std::setw(6) << _glyph
124 // << " bbox_pick_scaled: " << bbox_pick_scaled
125 // << " bbox_draw_scaled: " << bbox_draw_scaled
126 // << std::endl;
127
128 return STATE_ALL;
129}
130
131DrawingItem *DrawingGlyphs::_pickItem(Geom::Point const &p, double /*delta*/, unsigned flags)
132{
133 auto ggroup = cast<DrawingText>(_parent);
134 if (!ggroup) {
135 throw InvalidItemException();
136 }
137 DrawingItem *result = nullptr;
138 bool invisible = ggroup->_nrstyle.data.fill.type == NRStyleData::PaintType::NONE &&
139 ggroup->_nrstyle.data.stroke.type == NRStyleData::PaintType::NONE;
140 bool outline = flags & PICK_OUTLINE;
141
142 if (outline || !invisible) {
143 // With text we take a simple approach: pick if the point is in a character pick bbox
144 Geom::Rect temp(bbox_pick_scaled); // Convert from Geom::RectInt
145 if (temp.contains(p)) {
146 result = this;
147 }
148 }
149 return result;
150}
151
153 : DrawingGroup(drawing)
154 , style_vector_effect_stroke(false)
155 , style_stroke_extensions_hairline(false)
156 , style_clip_rule(SP_WIND_RULE_EVENODD)
157{
158}
159
160bool DrawingText::addComponent(std::shared_ptr<FontInstance> const &font, unsigned int glyph, Geom::Affine const &trans, float width, float ascent, float descent, float phase_length)
161{
162 if (glyph == 0x0fffffff) {
163 // 0x0fffffff is returned by Pango for a zero-width empty glyph which we can ignore (e.g. 0xFE0F, Emoji variant selector).
164 return false;
165 }
166
167 if (!font) {
168 std::cerr << "DrawingTExt::addComponent: no font!" << std::endl;
169 return false;
170 }
171
172 defer([=, this, font = std::move(font)] () mutable {
174 auto ng = new DrawingGlyphs(_drawing);
175 assert(!_drawing.snapshotted());
176 ng->setGlyph(font, glyph, trans);
177 ng->_width = width; // used especially when _drawable = false, otherwise, it is the advance of the font
178 ng->_asc = ascent; // Of line, not of this one character. In pixels.
179 ng->_dsc = descent; // Of line, not of this one character. In pixels.
180 ng->_pl = phase_length; // used for phase of dots, dashes, and wavy
181 appendChild(ng);
182 });
183
184 return true;
185}
186
187void DrawingText::setStyle(SPStyle const *style, SPStyle const *context_style)
188{
189 DrawingGroup::setStyle(style, context_style);
190
191 auto vector_effect_stroke = false;
192 auto stroke_extensions_hairline = false;
193 auto clip_rule = SP_WIND_RULE_EVENODD;
194 if (_style) {
195 vector_effect_stroke = _style->vector_effect.stroke;
196 stroke_extensions_hairline = _style->stroke_extensions.hairline;
197 clip_rule = _style->clip_rule.computed;
198 }
199
200 defer([=, this, nrstyle = NRStyleData(_style, _context_style)] () mutable {
201 _nrstyle.set(std::move(nrstyle));
202 style_vector_effect_stroke = vector_effect_stroke;
203 style_stroke_extensions_hairline = stroke_extensions_hairline;
204 style_clip_rule = clip_rule;
205 });
206}
207
208void DrawingText::setChildrenStyle(SPStyle const *context_style)
209{
210 DrawingGroup::setChildrenStyle(context_style);
211
212 defer([this, nrstyle = NRStyleData(_style, _context_style)] () mutable {
213 _nrstyle.set(std::move(nrstyle));
214 });
215}
216
217unsigned DrawingText::_updateItem(Geom::IntRect const &area, UpdateContext const &ctx, unsigned flags, unsigned reset)
218{
220 return DrawingGroup::_updateItem(area, ctx, flags, reset);
221}
222
223void DrawingText::decorateStyle(DrawingContext &dc, double vextent, double xphase, Geom::Point const &p1, Geom::Point const &p2, double thickness) const
224{
225 double wave[16]={
226 // clang-format off
227 0.000000, 0.382499, 0.706825, 0.923651, 1.000000, 0.923651, 0.706825, 0.382499,
228 0.000000, -0.382499, -0.706825, -0.923651, -1.000000, -0.923651, -0.706825, -0.382499,
229 // clang-format on
230 };
231 int dashes[16]={
232 // clang-format off
233 8, 7, 6, 5,
234 4, 3, 2, 1,
235 -8, -7, -6, -5,
236 -4, -3, -2, -1
237 // clang-format on
238 };
239 int dots[16]={
240 // clang-format off
241 4, 3, 2, 1,
242 -4, -3, -2, -1,
243 4, 3, 2, 1,
244 -4, -3, -2, -1
245 // clang-format on
246 };
247 double step = vextent/32.0;
248 unsigned i = 15 & (unsigned) round(xphase/step); // xphase is >= 0.0
249
250 /* For most spans draw the last little bit right to p2 or even a little beyond.
251 This allows decoration continuity within the line, and does not step outside the clip box off the end
252 For the first/last section on the line though, stay well clear of the edge, or when the
253 text is dragged it may "spray" pixels.
254 */
255 /* snap to nearest step in X */
256 Geom::Point ps = Geom::Point(step * round(p1[Geom::X]/step),p1[Geom::Y]);
257 Geom::Point pf = Geom::Point(step * round(p2[Geom::X]/step),p2[Geom::Y]);
258 Geom::Point poff = Geom::Point(0,thickness/2.0);
259
261 ps -= Geom::Point(0, vextent/12.0);
262 pf -= Geom::Point(0, vextent/12.0);
263 dc.rectangle( Geom::Rect(ps + poff, pf - poff));
264 ps += Geom::Point(0, vextent/6.0);
265 pf += Geom::Point(0, vextent/6.0);
266 dc.rectangle( Geom::Rect(ps + poff, pf - poff));
267 }
268 /* The next three have a problem in that they are phase dependent. The bits of a line are not
269 necessarily passing through this routine in order, so we have to use the xphase information
270 to figure where in each of their cycles to start. Only accurate to 1 part in 16.
271 Huge positive offset should keep the phase calculation from ever being negative.
272 */
274 // FIXME: Per spec, this should produce round dots.
275 Geom::Point pv = ps;
276 while(true){
277 Geom::Point pvlast = pv;
278 if(dots[i]>0){
279 if(pv[Geom::X] > pf[Geom::X]) break;
280
281 pv += Geom::Point(step * (double)dots[i], 0.0);
282
283 if(pv[Geom::X]>= pf[Geom::X]){
284 // Last dot
285 dc.rectangle( Geom::Rect(pvlast + poff, pf - poff));
286 break;
287 } else {
288 dc.rectangle( Geom::Rect(pvlast + poff, pv - poff));
289 }
290
291 pv += Geom::Point(step * 4.0, 0.0);
292
293 } else {
294 pv += Geom::Point(step * -(double)dots[i], 0.0);
295 }
296 i = 0; // once in phase, it stays in phase
297 }
298 }
300 Geom::Point pv = ps;
301 while(true){
302 Geom::Point pvlast = pv;
303 if(dashes[i]>0){
304 if(pv[Geom::X]> pf[Geom::X]) break;
305
306 pv += Geom::Point(step * (double)dashes[i], 0.0);
307
308 if(pv[Geom::X]>= pf[Geom::X]){
309 // Last dash
310 dc.rectangle( Geom::Rect(pvlast + poff, pf - poff));
311 break;
312 } else {
313 dc.rectangle( Geom::Rect(pvlast + poff, pv - poff));
314 }
315
316 pv += Geom::Point(step * 8.0, 0.0);
317
318 } else {
319 pv += Geom::Point(step * -(double)dashes[i], 0.0);
320 }
321 i = 0; // once in phase, it stays in phase
322 }
323 }
325 double amp = vextent/10.0;
326 double x = ps[Geom::X];
327 double y = ps[Geom::Y] + poff[Geom::Y];
328 dc.moveTo(Geom::Point(x, y + amp * wave[i]));
329 while(true){
330 i = ((i + 1) & 15);
331 x += step;
332 dc.lineTo(Geom::Point(x, y + amp * wave[i]));
333 if(x >= pf[Geom::X])break;
334 }
335 y = ps[Geom::Y] - poff[Geom::Y];
336 dc.lineTo(Geom::Point(x, y + amp * wave[i]));
337 while(true){
338 i = ((i - 1) & 15);
339 x -= step;
340 dc.lineTo(Geom::Point(x, y + amp * wave[i]));
341 if(x <= ps[Geom::X])break;
342 }
343 dc.closePath();
344 }
345 else { // TEXT_DECORATION_STYLE_SOLID, also default in case it was not set for some reason
346 dc.rectangle( Geom::Rect(ps + poff, pf - poff));
347 }
348}
349
350/* returns scaled line thickness */
351void DrawingText::decorateItem(DrawingContext &dc, double phase_length, bool under) const
352{
353 if ( _nrstyle.data.font_size <= 1.0e-32 )return; // might cause a divide by zero or overflow and nothing would be visible anyway
354 double tsp_width_adj = _nrstyle.data.tspan_width / _nrstyle.data.font_size;
355 double tsp_asc_adj = _nrstyle.data.ascender / _nrstyle.data.font_size;
356 double tsp_size_adj = (_nrstyle.data.ascender + _nrstyle.data.descender) / _nrstyle.data.font_size;
357
358 double final_underline_thickness = CLAMP(_nrstyle.data.underline_thickness, tsp_size_adj/30.0, tsp_size_adj/10.0);
359 double final_line_through_thickness = CLAMP(_nrstyle.data.line_through_thickness, tsp_size_adj/30.0, tsp_size_adj/10.0);
360
361 double xphase = phase_length/ _nrstyle.data.font_size; // used to figure out phase of patterns
362
363 Geom::Point p1;
364 Geom::Point p2;
365 // All lines must be the same thickness, in combinations, line_through trumps underline
366 double thickness = final_underline_thickness;
367 if ( thickness <= 1.0e-32 )return; // might cause a divide by zero or overflow and nothing would be visible anyway
368 dc.setTolerance(0.5); // Is this really necessary... could effect dots.
369
370 if( under ) {
371
374 p2 = Geom::Point(tsp_width_adj,-_nrstyle.data.underline_position);
375 decorateStyle(dc, tsp_size_adj, xphase, p1, p2, thickness);
376 }
377
379 p1 = Geom::Point(0.0, tsp_asc_adj -_nrstyle.data.underline_position + 1 * final_underline_thickness);
380 p2 = Geom::Point(tsp_width_adj,tsp_asc_adj -_nrstyle.data.underline_position + 1 * final_underline_thickness);
381 decorateStyle(dc, tsp_size_adj, xphase, p1, p2, thickness);
382 }
383
384 } else {
385 // Over
386
388 thickness = final_line_through_thickness;
391 decorateStyle(dc, tsp_size_adj, xphase, p1, p2, thickness);
392 }
393
394 // Obviously this does not blink, but it does indicate which text has been set with that attribute
396 thickness = final_line_through_thickness;
397 p1 = Geom::Point(0.0, _nrstyle.data.line_through_position - 2*final_line_through_thickness);
398 p2 = Geom::Point(tsp_width_adj,_nrstyle.data.line_through_position - 2*final_line_through_thickness);
399 decorateStyle(dc, tsp_size_adj, xphase, p1, p2, thickness);
400 p1 = Geom::Point(0.0, _nrstyle.data.line_through_position + 2*final_line_through_thickness);
401 p2 = Geom::Point(tsp_width_adj,_nrstyle.data.line_through_position + 2*final_line_through_thickness);
402 decorateStyle(dc, tsp_size_adj, xphase, p1, p2, thickness);
403 }
404 }
405}
406
407unsigned DrawingText::_renderItem(DrawingContext &dc, RenderContext &rc, Geom::IntRect const &area, unsigned flags, DrawingItem const *stop_at) const
408{
409 auto visible = area & _bbox;
410 if (!visible) {
411 return RENDER_OK;
412 }
413
414 bool outline = flags & RENDER_OUTLINE;
415
416 if (outline) {
417 auto rgba = rc.outline_color;
419 dc.setSource(rgba);
420 dc.setTolerance(0.5); // low quality, but good enough for outline mode
421
422 for (auto & i : _children) {
423 auto g = cast<DrawingGlyphs>(&i);
424 if (!g) throw InvalidItemException();
425
427 // skip glyphs with singular transforms
428 if (g->_ctm.isSingular()) continue;
429 dc.transform(g->_ctm);
430 if (g->pathvec){
431 dc.path(*g->pathvec);
432 dc.fill();
433 }
434 // TODO If pathvec empty, draw box.
435 }
436 return RENDER_OK;
437 }
438
439 // NOTE: This is very similar to drawing-shape.cpp; the only differences are in path feeding
440 // and in applying text decorations.
441
442 // Do we have text decorations?
444
445 // prepareFill / prepareStroke need to be called with _ctm in effect.
446 // However, we might need to apply a different ctm for glyphs.
447 // Therefore, only apply this ctm temporarily.
450 CairoPatternUniqPtr has_td_fill;
451 CairoPatternUniqPtr has_td_stroke;
452
453 {
455 dc.transform(_ctm);
456
459
460 // Avoid creating patterns if not needed
461 if (decorate) {
464 }
465 }
466
467 if (has_fill || has_stroke || has_td_fill || has_td_stroke) {
468
469 // Determine order for fill and stroke.
470 // Text doesn't have markers, we can do paint-order quick and dirty.
471 bool fill_first = false;
475 fill_first = true;
476 } // Won't get "stroke fill stroke" but that isn't 'valid'
477
478
479 // Determine geometry of text decoration
480 double phase_length = 0.0;
481 Geom::Affine aff;
482 if (decorate) {
483
484 Geom::Affine rotinv;
485 bool invset = false;
486 double leftmost = DBL_MAX;
487 bool first_y = true;
488 double start_y = 0.0;
489 for (auto & i : _children) {
490
491 auto g = cast<DrawingGlyphs>(&i);
492 if (!g) throw InvalidItemException();
493
494 if (!invset) {
495 rotinv = g->_ctm.withoutTranslation().inverse();
496 invset = true;
497 }
498
499 Geom::Point pt = g->_ctm.translation() * rotinv;
500 if (pt[Geom::X] < leftmost) {
501 leftmost = pt[Geom::X];
502 aff = g->_ctm;
503 phase_length = g->_pl;
504 }
505
506 // Check for text on a path. FIXME: This needs better test (and probably not here).
507 if (first_y) {
508 first_y = false;
509 start_y = pt[Geom::Y];
510 }
511 else if (std::fabs(pt[Geom::Y] - start_y) > 1.0e-6) {
512 // If the text has been mapped onto a path, which causes y to vary, drop the
513 // text decorations. To handle that properly would need a conformal map.
514 decorate = false;
515 }
516 }
517 }
518
519 // Draw text decorations that go UNDER the text (underline, over-line)
520 if (decorate) {
521
522 {
524 dc.transform(aff); // must be leftmost affine in span
525 decorateItem(dc, phase_length, true);
526 }
527
528 {
530 dc.transform(_ctm); // Needed so that fill pattern rotates with text
531
532 if (has_td_fill && fill_first) {
533 _nrstyle.applyTextDecorationFill(dc, has_td_fill);
534 dc.fillPreserve();
535 }
536
537 if (has_td_stroke) {
538 _nrstyle.applyTextDecorationStroke(dc, has_td_stroke);
539 dc.strokePreserve();
540 }
541
542 if (has_td_fill && !fill_first) {
543 _nrstyle.applyTextDecorationFill(dc, has_td_fill);
544 dc.fillPreserve();
545 }
546
547 }
548
549 dc.newPath(); // Clear text-decoration path
550 }
551
552 // Accumulate the path that represents the glyphs and/or draw SVG glyphs.
553 for (auto &i : _children) {
554 auto g = cast<DrawingGlyphs>(&i);
555 if (!g) throw InvalidItemException();
556
558 if (g->_ctm.isSingular()) {
559 std::cerr << "DrawingText::_renderItem: glyph matrix is singular!" << std::endl;
560 continue;
561 }
562 dc.transform(g->_ctm);
563
564#if 0
565 // Draw various boxes for debugging
566 auto path_copy = cairo_copy_path(dc.raw()); // Cairo save/restore doesn't apply to path!
567 {
569 dc.newPath();
570 dc.rectangle(g->bbox_exact);
571 dc.setLineWidth(0.02);
572 dc.setSource(0x80ffff80); // Bluegreen
573 dc.stroke();
574 }
575 {
577 dc.newPath();
578 dc.rectangle(g->bbox_pick);
579 dc.setLineWidth(0.02);
580 dc.setSource(0xff80ff80); // Purple
581 dc.stroke();
582 }
583 {
585 dc.newPath();
586 dc.rectangle(g->bbox_draw);
587 dc.setLineWidth(0.02);
588 dc.setSource(0xffff8080); // Yellow
589 dc.stroke();
590 }
591 cairo_append_path(dc.raw(), path_copy);
592 cairo_path_destroy(path_copy);
593 // End debug boxes.
594#endif
595
596 if (g->pathvec) {
597
598 // Draw various boxes for debugging
599 // auto path_copy = cairo_copy_path(dc.raw()); // Cairo save/restore doesn't apply to path!
600 // {
601 // Geom::OptRect box = bounds_exact(*g->pathvec);
602 // if (box) {
603 // Inkscape::DrawingContext::Save save(dc);
604 // dc.newPath();
605 // dc.rectangle(*box);
606 // dc.setLineWidth(0.02);
607 // dc.setSource(0xff000080);
608 // dc.stroke();
609 // }
610 // }
611 // cairo_append_path(dc.raw(), path_copy); // Restore path.
612 // cairo_path_destroy(path_copy);
613 // End debug boxes.
614
615 if (g->pixbuf) {
616 {
617 // pixbuf is in font design units, scale to embox.
618 double scale = g->design_units;
619 if (scale <= 0) scale = 1000;
621 dc.translate(0, 1);
622 dc.scale(1.0 / scale, -1.0 / scale);
623 dc.setSource(g->pixbuf->getSurfaceRaw(), 0, 0);
624 dc.paint(1);
625 }
626 } else {
627 dc.path(*g->pathvec);
628 }
629 }
630 }
631
632 // Draw the glyphs (non-SVG glyphs).
633 {
635 dc.transform(_ctm);
636 if (has_fill && fill_first) {
638 dc.fillPreserve();
639 }
640 }
641 {
644 dc.transform(_ctm);
645 }
646 if (has_stroke) {
648
649 // If the stroke is a hairline, set it to exactly 1px on screen.
650 // If visible hairline mode is on, make sure the line is at least 1px.
652 double dx = 1.0, dy = 0.0;
653 dc.device_to_user_distance(dx, dy);
654 auto pixel_size = std::hypot(dx, dy);
656 dc.setHairline();
657 }
658 }
659
660 dc.strokePreserve();
661 }
662 }
663 {
665 dc.transform(_ctm);
666 if (has_fill && !fill_first) {
668 dc.fillPreserve();
669 }
670 }
671 dc.newPath(); // Clear glyphs path
672
673 // Draw text decorations that go OVER the text (line through, blink)
674 if (decorate) {
675
676 {
678 dc.transform(aff); // must be leftmost affine in span
679 decorateItem(dc, phase_length, false);
680 }
681
682 {
684 dc.transform(_ctm); // Needed so that fill pattern rotates with text
685
686 if (has_td_fill && fill_first) {
687 _nrstyle.applyTextDecorationFill(dc, has_td_fill);
688 dc.fillPreserve();
689 }
690
691 if (has_td_stroke) {
692 _nrstyle.applyTextDecorationStroke(dc, has_td_stroke);
693 dc.strokePreserve();
694 }
695
696 if (has_td_fill && !fill_first) {
697 _nrstyle.applyTextDecorationFill(dc, has_td_fill);
698 dc.fillPreserve();
699 }
700
701 }
702
703 dc.newPath(); // Clear text-decoration path
704 }
705
706 }
707 return RENDER_OK;
708}
709
711{
713
715 dc.setFillRule(CAIRO_FILL_RULE_EVEN_ODD);
716 } else {
717 dc.setFillRule(CAIRO_FILL_RULE_WINDING);
718 }
719
720 for (auto & i : _children) {
721 auto g = cast<DrawingGlyphs>(&i);
722 if (!g) {
723 throw InvalidItemException();
724 }
725
727 dc.transform(g->_ctm);
728 if (g->pathvec){
729 dc.path(*g->pathvec);
730 }
731 }
732 dc.fill();
733}
734
735DrawingItem *DrawingText::_pickItem(Geom::Point const &p, double delta, unsigned flags)
736{
737 return DrawingGroup::_pickItem(p, delta, flags) ? this : nullptr;
738}
739
740} // end namespace Inkscape
741
742/*
743 Local Variables:
744 mode:c++
745 c-file-style:"stroustrup"
746 c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
747 indent-tabs-mode:nil
748 fill-column:99
749 End:
750*/
751// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:fileencoding=utf-8:textwidth=99 :
double scale
Definition aa.cpp:228
Cairo integration helpers.
3x3 matrix representing an affine transformation.
Definition affine.h:70
Coord descrim() const
Calculate the descriminant.
Definition affine.cpp:434
Affine inverse() const
Compute the inverse matrix.
Definition affine.cpp:388
Affine withoutTranslation() const
Definition affine.h:169
Axis aligned, non-empty, generic rectangle.
bool contains(GenericRect< C > const &r) const
Check whether the rectangle includes all points in the given rectangle.
void expandBy(C amount)
Expand the rectangle in both directions by the specified amount.
Two-dimensional point that doubles as a vector.
Definition point.h:66
Axis aligned, non-empty rectangle.
Definition rect.h:92
IntRect roundOutwards() const
Return the smallest integer rectangle which contains this one.
Definition rect.h:141
RAII idiom for saving the state of DrawingContext.
Minimal wrapper over Cairo.
void path(Geom::PathVector const &pv)
void setSource(cairo_pattern_t *source)
void scale(Geom::Scale const &s)
void transform(Geom::Affine const &trans)
void setTolerance(double tol)
void device_to_user_distance(double &dx, double &dy)
void paint(double alpha=1.0)
void rectangle(Geom::Rect const &r)
void setFillRule(cairo_fill_rule_t rule)
void lineTo(Geom::Point const &p)
void moveTo(Geom::Point const &p)
void translate(Geom::Point const &t)
Geom::IntRect bbox_draw_scaled
DrawingGlyphs(Drawing &drawing)
void setGlyph(std::shared_ptr< FontInstance > font, unsigned int glyph, Geom::Affine const &trans)
Geom::IntRect bbox_pick_scaled
void setStyle(SPStyle const *style, SPStyle const *context_style=nullptr) override
Process information related to the new style.
unsigned _updateItem(Geom::IntRect const &area, UpdateContext const &ctx, unsigned flags, unsigned reset) override
std::shared_ptr< void const > _font_data
Inkscape::Pixbuf const * pixbuf
Geom::PathVector const * pathvec
DrawingItem * _pickItem(Geom::Point const &p, double delta, unsigned flags) override
unsigned _updateItem(Geom::IntRect const &area, UpdateContext const &ctx, unsigned flags, unsigned reset) override
DrawingItem * _pickItem(Geom::Point const &p, double delta, unsigned flags) override
SVG drawing item for display.
DrawingPattern * _fill_pattern
void appendChild(DrawingItem *item)
Geom::OptIntRect _bbox
Bounding box in display (pixel) coords including stroke.
virtual void setStyle(SPStyle const *style, SPStyle const *context_style=nullptr)
Process information related to the new style.
Geom::OptRect _item_bbox
Geometric bounding box in item's user space.
DrawingItem * _parent
virtual void setChildrenStyle(SPStyle const *context_style)
Recursively update children style.
SPStyle const * _style
std::unique_ptr< Geom::Affine > _transform
Incremental transform from parent to this item's coords.
void _markForUpdate(unsigned state, bool propagate)
Marks the item as needing a recomputation of internal data.
DrawingPattern * _stroke_pattern
void setTransform(Geom::Affine const &trans)
SPStyle const * _context_style
Geom::Affine _ctm
Total transform from item coords to display coords.
void _markForRendering()
Marks the current visual bounding box of the item for redrawing.
void decorateItem(DrawingContext &dc, double phase_length, bool under) const
DrawingText(Drawing &drawing)
void setStyle(SPStyle const *style, SPStyle const *context_style=nullptr) override
Process information related to the new style.
void setChildrenStyle(SPStyle const *context_style) override
Recursively update children style.
unsigned _renderItem(DrawingContext &dc, RenderContext &rc, Geom::IntRect const &area, unsigned flags, DrawingItem const *stop_at) const override
unsigned _updateItem(Geom::IntRect const &area, UpdateContext const &ctx, unsigned flags, unsigned reset) override
SPWindRule style_clip_rule
void decorateStyle(DrawingContext &dc, double vextent, double xphase, Geom::Point const &p1, Geom::Point const &p2, double thickness) const
void _clipItem(DrawingContext &dc, RenderContext &rc, Geom::IntRect const &area) const override
bool style_stroke_extensions_hairline
friend class DrawingGlyphs
bool addComponent(std::shared_ptr< FontInstance > const &font, int unsigned glyph, Geom::Affine const &trans, float width, float ascent, float descent, float phase_length)
DrawingItem * _pickItem(Geom::Point const &p, double delta, unsigned flags) override
bool snapshotted() const
Definition drawing.h:89
NRStyleData data
Definition nr-style.h:149
void applyTextDecorationFill(DrawingContext &dc, CairoPatternUniqPtr const &cp) const
Definition nr-style.cpp:371
void applyStroke(DrawingContext &dc, CairoPatternUniqPtr const &cp) const
Definition nr-style.cpp:377
CairoPatternUniqPtr prepareFill(DrawingContext &dc, RenderContext &rc, Geom::IntRect const &area, Geom::OptRect const &paintbox, DrawingPattern const *pattern) const
Definition nr-style.cpp:345
CairoPatternUniqPtr prepareStroke(DrawingContext &dc, RenderContext &rc, Geom::IntRect const &area, Geom::OptRect const &paintbox, DrawingPattern const *pattern) const
Definition nr-style.cpp:350
void applyTextDecorationStroke(DrawingContext &dc, CairoPatternUniqPtr const &cp) const
Definition nr-style.cpp:391
CairoPatternUniqPtr prepareTextDecorationFill(DrawingContext &dc, RenderContext &rc, Geom::IntRect const &area, Geom::OptRect const &paintbox, DrawingPattern const *pattern) const
Definition nr-style.cpp:355
CairoPatternUniqPtr prepareTextDecorationStroke(DrawingContext &dc, RenderContext &rc, Geom::IntRect const &area, Geom::OptRect const &paintbox, DrawingPattern const *pattern) const
Definition nr-style.cpp:360
void applyFill(DrawingContext &dc, CairoPatternUniqPtr const &cp) const
Definition nr-style.cpp:365
void set(NRStyleData &&data)
Definition nr-style.cpp:339
An SVG style object.
Definition style.h:45
T< SPAttr::CLIP_RULE, SPIEnum< SPWindRule > > clip_rule
clip-rule: 0 nonzero, 1 evenodd
Definition style.h:204
T< SPAttr::STROKE_EXTENSIONS, SPIStrokeExtensions > stroke_extensions
-inkscape-stroke
Definition style.h:263
T< SPAttr::VECTOR_EFFECT, SPIVectorEffect > vector_effect
vector effect
Definition style.h:237
RectangularCluster rc
Css & result
static char const dots[]
Definition dir-util.cpp:69
Cairo drawing context with Inkscape extensions.
Cairo surface that remembers its origin.
Group belonging to an SVG drawing element.
SVG drawing for display.
static bool has_fill(SPObject *source)
static bool has_stroke(SPObject *source)
The data describing a single loaded font.
@ Y
Definition coord.h:48
@ X
Definition coord.h:48
Specific geometry functions for Inkscape, not provided my lib2geom.
Helper class to stream background task notifications as a series of messages.
Geom::PathVector outline(Geom::Path const &input, double width, double miter, LineJoinType join, LineCapType butt, double tolerance)
Strokes the path given by input.
std::unique_ptr< cairo_pattern_t, CairoPatternFreer > CairoPatternUniqPtr
Definition nr-style.h:34
PathVector - a sequence of subpaths.
std::array< PaintOrderType, 3 > paint_order_layer
Definition nr-style.h:88
@ SP_WIND_RULE_EVENODD
Definition style-enums.h:26
SPStyle - a style object for SPItem objects.
int delta
double width