void WMeasurePaintDevice::drawText(const WRectF& rect, WFlags<AlignmentFlag> flags, TextFlag textFlag, const WString& text, const WPointF *clipPoint) { if (clipPoint && painter()) { if (!painter()->clipPathTransform().map(painter()->clipPath()) .isPointInPath(painter()->worldTransform().map(*clipPoint))) return; } double w = 0, h = 0; WString line = text; WFontMetrics fm = fontMetrics(); for (;;) { WTextItem t = measureText(line, rect.width(), textFlag == TextWordWrap ? true : false); h += fm.height(); w = std::max(w, t.width()); if (t.text() == line) break; else line = WString ::fromUTF8(line.toUTF8().substr(t.text().toUTF8().length())); } AlignmentFlag horizontalAlign = flags & AlignHorizontalMask; AlignmentFlag verticalAlign = flags & AlignVerticalMask; double x, y; switch (horizontalAlign) { case AlignLeft: x = rect.left(); break; case AlignCenter: x = rect.left() + (rect.width() - w) / 2; break; case AlignRight: default: x = rect.right() - w; break; } switch (verticalAlign) { case AlignTop: y = rect.top(); break; case AlignMiddle: y = rect.top() + (rect.height() - h) / 2; break; case AlignBottom: default: y = rect.bottom() - h; break; } expandBounds(WRectF(x, y, w, h)); }
GList *FontSupport::layoutText(const WFont& font, const std::string& utf8, std::vector<PangoGlyphString *>& glyphs, int& width) { PANGO_LOCK; enabledFontFormats = enabledFontFormats_; FontMatch match = matchFont(font); PangoAttrList *attrs = pango_attr_list_new(); pango_context_set_font_description(context_, match.pangoFontDescription()); GList *items = pango_itemize(context_, utf8.c_str(), 0, utf8.length(), attrs, nullptr); width = 0; for (GList *elem = items; elem; elem = elem->next) { PangoItem *item = (PangoItem *)elem->data; PangoAnalysis *analysis = &item->analysis; PangoGlyphString *gl = pango_glyph_string_new(); pango_shape(utf8.c_str() + item->offset, item->length, analysis, gl); glyphs.push_back(gl); if (device_) { currentFont_ = analysis->font; WTextItem textItem = device_->measureText(WString::fromUTF8(utf8.substr(item->offset, item->length)), -1, false); width += pangoUnitsFromDouble(textItem.width()); currentFont_ = nullptr; } else width += pango_glyph_string_get_width(gl); } pango_attr_list_unref(attrs); return items; }
WTextItem FontSupport::measureText(const WFont& font, const WString& text, double maxWidth, bool wordWrap) { PANGO_LOCK; enabledFontFormats = enabledFontFormats_; /* * Note: accurate measuring on a bitmap requires that the transformation * is applied, because hinting may push chars to boundaries e.g. when * rotated (or scaled too?) */ std::string utf8 = text.toUTF8(); const char *s = utf8.c_str(); if (wordWrap) { int utflen = g_utf8_strlen(s, -1); PangoLogAttr *attrs = new PangoLogAttr[utflen + 1]; PangoLanguage *language = pango_language_from_string("en-US"); pango_get_log_attrs(s, utf8.length(), -1, language, attrs, utflen + 1); double w = 0, nextW = -1; int current = 0; int measured = 0; int end = 0; bool maxWidthReached = false; for (int i = 0; i < utflen + 1; ++i) { if (i == utflen || attrs[i].is_line_break) { int cend = g_utf8_offset_to_pointer(s, end) - s; WTextItem ti = measureText(font, WString::fromUTF8(utf8.substr(measured, cend - measured)), -1, false); if (isEpsilonMore(w + ti.width(), maxWidth)) { nextW = ti.width(); maxWidthReached = true; break; } else { measured = cend; current = g_utf8_offset_to_pointer(s, i) - s; w += ti.width(); if (i == utflen) { w += measureText(font, WString::fromUTF8(utf8.substr(measured)), -1, false).width(); measured = utf8.length(); } } } if (!attrs[i].is_white) end = i + 1; } delete[] attrs; if (maxWidthReached) { return WTextItem(WString::fromUTF8(utf8.substr(0, current)), w, nextW); } else { /* * For some reason, the sum of the individual widths is a bit less * (for longer stretches of text), so we re-measure it ! */ w = measureText(font, WString::fromUTF8(utf8.substr(0, measured)), -1, false).width(); return WTextItem(text, w); } } else { std::vector<PangoGlyphString *> glyphs; int width; GList *items = layoutText(font, utf8, glyphs, width); double w = pangoUnitsToDouble(width); for (unsigned i = 0; i < glyphs.size(); ++i) pango_glyph_string_free(glyphs[i]); g_list_foreach(items, (GFunc) pango_item_free, nullptr); g_list_free(items); return WTextItem(text, w); } }
void FontSupport::drawText(const WFont& font, const WRectF& rect, WFlags<AlignmentFlag> flags, const WString& text) { PANGO_LOCK; enabledFontFormats = enabledFontFormats_; std::string utf8 = text.toUTF8(); std::vector<PangoGlyphString *> glyphs; int width; GList *items = layoutText(font, utf8, glyphs, width); AlignmentFlag hAlign = flags & AlignHorizontalMask; AlignmentFlag vAlign = flags & AlignVerticalMask; /* FIXME handle bidi ! */ double x; switch (hAlign) { case AlignmentFlag::Left: x = rect.left(); break; case AlignmentFlag::Right: x = rect.right() - pangoUnitsToDouble(width); break; case AlignmentFlag::Center: x = rect.center().x() - pangoUnitsToDouble(width/2); break; default: x = 0; } unsigned i = 0; for (GList *elem = items; elem; elem = elem->next) { PangoItem *item = (PangoItem *)elem->data; PangoAnalysis *analysis = &item->analysis; PangoGlyphString *gl = glyphs[i++]; currentFont_ = analysis->font; /* * Note, we are actually ignoring the selected glyphs here, which * is a pitty and possibly wrong if the device does not make the * same selection ! */ WString s = WString::fromUTF8(utf8.substr(item->offset, item->length)); device_->drawText(WRectF(x, rect.y(), 1000, rect.height()), AlignmentFlag::Left | vAlign, TextFlag::SingleLine, s, nullptr); WTextItem textItem = device_->measureText(s, -1, false); x += textItem.width(); pango_item_free(item); pango_glyph_string_free(gl); } g_list_free(items); currentFont_ = nullptr; }