WTextItem WRasterImage::measureText(const WString& text, double maxWidth, bool wordWrap) { SkScalar w = impl_->textPaint_.measureText(text.toUTF8().c_str(), text.toUTF8().size()); return WTextItem(text, SkScalarToFloat(w), -1); // TODO: what about wordwrap? }
void StyleLayout::loadingIndicatorSelected(WString indicator) { if (indicator.toUTF8() == "WDefaultLoadingIndicator") { WApplication::instance() ->setLoadingIndicator(new WDefaultLoadingIndicator()); } else if (indicator.toUTF8() == "WOverlayLoadingIndicator") { WApplication::instance() ->setLoadingIndicator(new WOverlayLoadingIndicator()); } else if (indicator.toUTF8() == "EmwebLoadingIndicator") { WApplication::instance() ->setLoadingIndicator(new EmwebLoadingIndicator()); } }
WTextItem WPdfImage::measureText(const WString& text, double maxWidth, bool wordWrap) { if (trueTypeFont_ && !trueTypeFonts_->busy()) return trueTypeFonts_->measureText(painter()->font(), text, maxWidth, wordWrap); else { HPDF_REAL width = 0; if (!wordWrap) maxWidth = 1E9; else maxWidth += EPSILON; if (trueTypeFonts_->busy()) setChanged(PainterChangeFlag::Font); std::string s = trueTypeFont_ ? text.toUTF8() : text.narrow(); int bytes = HPDF_Page_MeasureText(page_, s.c_str(), maxWidth, wordWrap, &width); if (trueTypeFont_) return WTextItem(WString::fromUTF8(s.substr(0, bytes)), width); else return WTextItem(text.value().substr(0, bytes), width); } }
bool XSSFilterRemoveScript(WString& text) { if (text.empty()) return true; std::string result = "<span>" + text.toUTF8() + "</span>"; char *ctext = const_cast<char *>(result.c_str()); // Shhht it's okay ! try { xml_document<> doc; doc.parse<parse_comment_nodes | parse_validate_closing_tags | parse_validate_utf8 | parse_xhtml_entity_translation>(ctext); XSSSanitize(doc.first_node()); WStringStream out; print(out.back_inserter(), *doc.first_node(), print_no_indenting); result = out.str(); } catch (parse_error& e) { LOG_ERROR("Error reading XHTML string: " << e.what()); return false; } if (result.length() < 13) result.clear(); else result = result.substr(6, result.length() - 13); text = WString::fromUTF8(result); return true; }
void EncodeRefs(WString& text, WFlags<RefEncoderOption> options) { if (text.empty()) return; std::string result = "<span>" + text.toUTF8() + "</span>"; char *ctext = const_cast<char *>(result.c_str()); // Shhht it's okay ! WApplication *app = WApplication::instance(); try { xml_document<> doc; doc.parse<parse_comment_nodes | parse_validate_closing_tags | parse_validate_utf8 | parse_xhtml_entity_translation>(ctext); EncodeRefs(doc.first_node(), app, options); WStringStream out; print(out.back_inserter(), *doc.first_node(), print_no_indenting); result = out.str(); } catch (parse_error& e) { LOG_ERROR("Error reading XHTML string: " << e.what()); return; } if (result.length() < 13) result.clear(); else result = result.substr(6, result.length() - 13); text = WString::fromUTF8(result); }
void AutoCompleteFileWidget::onFilterModel(const WString& data_) { boost::filesystem::directory_iterator end_itr; std::string data = data_.toUTF8(); for (boost::filesystem::directory_iterator itr(_current_path); itr != end_itr; ++itr) { if(data == itr->path().string()) { if(boost::filesystem::is_directory(itr->path())) { _sp->clearSuggestions(); for (boost::filesystem::directory_iterator itr2(itr->path()); itr2 != end_itr; ++itr2) { _sp->addSuggestion(itr2->path().string()); } return; } } } #ifndef _MSC_VER for(boost::filesystem::directory_iterator itr("/"); itr != end_itr; ++itr) { if(data == itr->path().string()) { for (boost::filesystem::directory_iterator itr2(itr->path()); itr2 != end_itr; ++itr2) { _sp->addSuggestion(itr2->path().string()); } _current_path = data + "/"; return; } } #endif }
WWidget *ProductMenuItem::createItemWidget() { //<div class="collection" style="background: url(productimages/QEES_Dimmer_billed_tekst_01_thumb.jpg) no-repeat;"> WAnchor *result = new WAnchor(); WContainerWidget *collection = new WContainerWidget(result); result->mouseWentOver().connect(SLOT(this, ProductMenuItem::_mouseWentOver)); result->mouseWentOut().connect(SLOT(this, ProductMenuItem::_mouseWentOut)); collection->setStyleClass("collection"); WString imageName = _product->imageThumbName("var/www/", "resources/images/productimages/"); collection->decorationStyle().setBackgroundImage(imageName.toUTF8()); _faderInvisible = new WContainerWidget(collection); _faderInvisible->setStyleClass("fader invisible"); WContainerWidget *faderInvisible2 = new WContainerWidget(_faderInvisible); new WText(_description(), faderInvisible2); // @todo: Add this? Change picutre // <!--<div class="sale_label"></div>--> //WContainerWidget *salesLabel = new WContainerWidget(collection); //salesLabel->setStyleClass("sale_label"); // @todo: Add this? Change picutre // <!--<div class="sale_label"></div>--> //WContainerWidget *offerLabel = new WContainerWidget(collection); //offerLabel->setStyleClass("offer_label"); return result; }
void WsContent::doSearch(WString sSearch) { clear(); WsSearchView* srchView = new WsSearchView(sSearch.toUTF8(), this); resize(WLength(100, WLength::Percentage), WLength(100, WLength::Percentage)); addWidget(srchView); }
std::string WDate::extFormat(const WString& format) { std::string result; std::string f = format.toUTF8(); bool inQuote = false; bool gotQuoteInQuote = false; int d = 0, M = 0, y = 0; for (unsigned i = 0; i < f.length(); ++i) { if (inQuote) { if (f[i] != '\'') { if (gotQuoteInQuote) { gotQuoteInQuote = false; inQuote = false; } else result += extLiteral(f[i]); } else { if (gotQuoteInQuote) { gotQuoteInQuote = false; result += extLiteral(f[i]); } else gotQuoteInQuote = true; } } if (!inQuote) { switch (f[i]) { case 'd': if (d == 0) writeExtLast(result, d, M, y, format); ++d; break; case 'M': if (M == 0) writeExtLast(result, d, M, y, format); ++M; break; case 'y': if (y == 0) writeExtLast(result, d, M, y, format); ++y; break; default: writeExtLast(result, d, M, y, format); if (f[i] == '\'') { inQuote = true; gotQuoteInQuote = false; } else result += extLiteral(f[i]); } } } writeExtLast(result, d, M, y, format); return result; }
static void fatalFormatError(const WString& format, int c, const char* cs) { std::stringstream s; s << "WTime format syntax error (for \"" << format.toUTF8() << "\"): Cannot handle " << c << " consecutive " << cs; throw WException(s.str()); }
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)); }
void WTemplate::format(std::ostream& result, const WString& s, TextFormat textFormat) { if (textFormat == XHTMLText) { WString v = s; if (removeScript(v)) { result << v.toUTF8(); return; } else { EscapeOStream sout(result); sout.append(v.toUTF8(), *plainTextNewLineEscStream_); return; } } else if (textFormat == PlainText) { EscapeOStream sout(result); sout.append(s.toUTF8(), *plainTextNewLineEscStream_); return; } result << s.toUTF8(); }
WColor::WColor(const WString& name) : default_(false), red_(-1), green_(-1), blue_(-1), alpha_(255), name_(name) { WColor c = Wt::Color::parseCssColor(name.toUTF8()); this->setRgb(c.red(), c.green(), c.blue(), c.alpha()); // setRgb() erases name_ name_ = name; }
void WTemplate::format(std::ostream& result, const WString& s, TextFormat textFormat) { WString v = s; if (textFormat == XHTMLText) { if (!removeScript(v)) v = escapeText(v, true); } else if (textFormat == PlainText) v = escapeText(v, true); result << v.toUTF8(); }
bool WTemplate::_tr(const std::vector<WString>& args, std::ostream& result) { if (args.size() >= 1) { WString s = WString::tr(args[0].toUTF8()); for (unsigned j = 1; j < args.size(); ++j) s.arg(args[j]); result << s.toUTF8(); // FIXME formatting / escaping ? return true; } else { LOG_ERROR("Functions::tr(): expects at least one argument"); return false; } }
void WTemplate::bindString(const std::string& varName, const WString& value, TextFormat textFormat) { WWidget *w = resolveWidget(varName); if (w) bindWidget(varName, 0); WString v = value; if (textFormat == XHTMLText && v.literal()) { if (!removeScript(v)) v = escapeText(v, true); } else if (textFormat == PlainText) v = escapeText(v, true); StringMap::const_iterator i = strings_.find(varName); if (i == strings_.end() || i->second != v.toUTF8()) { strings_[varName] = v.toUTF8(); changed_ = true; repaint(RepaintInnerHtml); } }
void WCalendar::yearChanged(WString yearStr) { try { int year = Utils::stoi(yearStr.toUTF8()); if (currentYear_ != year && (year >= 1900 && year <= 2200)) { // ?? currentYear_ = year; emitCurrentPageChanged(); renderMonth(); } } catch (std::exception& e) { } }
ptr<UserAccount> UserAccount::login(Session& session, const WString& userName) { Transaction transaction(session); ptr<UserAccount> ua = session.find<UserAccount>("where name = ?").bind(userName); if(!ua) { WApplication::instance()->log("notice") << "Creating user: " << userName.toUTF8(); ua = session.add(new UserAccount(userName)); } transaction.commit(); return ua; }
WString formatFloat(const WString &format, double value) { std::string f = format.toUTF8(); int buflen = f.length() + 15; char *buf = new char[buflen]; snprintf(buf, buflen, f.c_str(), value); buf[buflen - 1] = 0; WString result = WT_USTRING::fromUTF8(buf); delete[] buf; return result; }
void WProgressBar::updateDom(DomElement& element, bool all) { DomElement *bar = 0, *label = 0; if (all) { WApplication *app = WApplication::instance(); bar = DomElement::createNew(DomElement_DIV); bar->setId("bar" + id()); bar->setProperty(PropertyClass, valueStyleClass_); app->theme()->apply(this, *bar, ProgressBarBarRole); label = DomElement::createNew(DomElement_DIV); label->setId("lbl" + id()); app->theme()->apply(this, *label, ProgressBarLabelRole); } if (changed_ || all) { if (!bar) bar = DomElement::getForUpdate("bar" + id(), DomElement_DIV); if (!label) label = DomElement::getForUpdate("lbl" + id(), DomElement_DIV); bar->setProperty(PropertyStyleWidth, boost::lexical_cast<std::string>(percentage()) + "%"); WString s = text(); removeScript(s); label->setProperty(PropertyInnerHTML, s.toUTF8()); changed_ = false; } if (bar) element.addChild(bar); if (label) element.addChild(label); WInteractWidget::updateDom(element, all); }
std::string WText::formattedText() const { if (text_.format == PlainText) return escapeText(text_.text, true).toUTF8(); else { WApplication *app = WApplication::instance(); if (flags_.test(BIT_ENCODE_INTERNAL_PATHS) || app->session()->hasSessionIdInUrl()) { WFlags<RefEncoderOption> options; if (flags_.test(BIT_ENCODE_INTERNAL_PATHS)) options |= EncodeInternalPaths; if (app->session()->hasSessionIdInUrl()) options |= EncodeRedirectTrampoline; WString result = text_.text; EncodeRefs(result, options); return result.toUTF8(); } else return text_.text.toUTF8(); } }
void WComboBox::updateDom(DomElement& element, bool all) { if (itemsChanged_ || all) { if (!all) element.removeAllChildren(); for (int i = 0; i < count(); ++i) { DomElement *item = DomElement::createNew(DomElement_OPTION); item->setProperty(PropertyValue, boost::lexical_cast<std::string>(i)); item->setProperty(PropertyInnerHTML, escapeText(asString(model_->data(i, modelColumn_))) .toUTF8()); if (isSelected(i)) item->setProperty(PropertySelected, "true"); WString sc = asString(model_->data(i, modelColumn_, StyleClassRole)); if (!sc.empty()) item->setProperty(PropertyClass, sc.toUTF8()); element.addChild(item); } itemsChanged_ = false; selectionChanged_ = false; } if (selectionChanged_) { element.setProperty(PropertySelectedIndex, boost::lexical_cast<std::string>(currentIndex_)); selectionChanged_ = false; } if (!currentlyConnected_ && (activated_.isConnected() || sactivated_.isConnected())) { currentlyConnected_ = true; changed().connect(this, &WComboBox::propagateChange); } WFormWidget::updateDom(element, all); }
void TreesTables::changeRegexp() { WString regexp = regexpFilter->text(); bool valid; #ifndef WT_TARGET_JAVA valid = WRegExp(regexp).isValid(); #else try { WRegExp r(regexp.toUTF8()); valid = true; } catch (std::exception& e) { valid = false; } #endif if (valid) { filteredCocktails->setFilterRegExp(regexp); filteredSortedCocktails->setFilterRegExp(regexp); regexpFilter->removeStyleClass("Wt-invalid"); } else { regexpFilter->addStyleClass("Wt-invalid"); } }
void WTemplate::renderTemplateText(std::ostream& result, const WString& templateText) { std::string text; WApplication *app = WApplication::instance(); if (app && (encodeInternalPaths_ || app->session()->hasSessionIdInUrl())) { WFlags<RefEncoderOption> options; if (encodeInternalPaths_) options |= EncodeInternalPaths; if (app->session()->hasSessionIdInUrl()) options |= EncodeRedirectTrampoline; WString t = templateText; EncodeRefs(t, options); text = t.toUTF8(); } else text = templateText.toUTF8(); std::size_t lastPos = 0; std::vector<WString> args; std::vector<std::string> conditions; int suppressing = 0; for (std::size_t pos = text.find('$'); pos != std::string::npos; pos = text.find('$', pos)) { if (!suppressing) result << text.substr(lastPos, pos - lastPos); lastPos = pos; if (pos + 1 < text.length()) { if (text[pos + 1] == '$') { // $$ -> $ if (!suppressing) result << '$'; lastPos += 2; } else if (text[pos + 1] == '{') { std::size_t startName = pos + 2; std::size_t endName = text.find_first_of(" \r\n\t}", startName); args.clear(); std::size_t endVar = parseArgs(text, endName, args); if (endVar == std::string::npos) { LOG_ERROR("variable syntax error near \"" << text.substr(pos) << "\""); return; } std::string name = text.substr(startName, endName - startName); std::size_t nl = name.length(); if (nl > 2 && name[0] == '<' && name[nl - 1] == '>') { if (name[1] != '/') { std::string cond = name.substr(1, nl - 2); conditions.push_back(cond); if (suppressing || !conditionValue(cond)) ++suppressing; } else { std::string cond = name.substr(2, nl - 3); if (conditions.back() != cond) { LOG_ERROR("mismatching condition block end: " << cond); return; } conditions.pop_back(); if (suppressing) --suppressing; } } else { if (!suppressing) { std::size_t colonPos = name.find(':'); bool handled = false; if (colonPos != std::string::npos) { std::string fname = name.substr(0, colonPos); std::string arg0 = name.substr(colonPos + 1); args.insert(args.begin(), WString::fromUTF8(arg0)); if (resolveFunction(fname, args, result)) handled = true; else args.erase(args.begin()); } if (!handled) resolveString(name, args, result); } } lastPos = endVar + 1; } else { if (!suppressing) result << '$'; // $. -> $. lastPos += 1; } } else { if (!suppressing) result << '$'; // $ at end of template -> $ lastPos += 1; } pos = lastPos; } result << text.substr(lastPos); }
void WDialog::setWindowTitle(const WString& windowTitle) { caption_->setText(WString::fromUTF8("<h3>" + windowTitle.toUTF8() + "</h3>")); }
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); } }
bool WTemplate::renderTemplateText(std::ostream& result, const WString& templateText) { errorText_ = ""; std::string text; if (encodeTemplateText_) text = encode(templateText.toUTF8()); else text = templateText.toUTF8(); std::size_t lastPos = 0; std::vector<WString> args; std::vector<std::string> conditions; int suppressing = 0; for (std::size_t pos = text.find('$'); pos != std::string::npos; pos = text.find('$', pos)) { if (!suppressing) result << text.substr(lastPos, pos - lastPos); lastPos = pos; if (pos + 1 < text.length()) { if (text[pos + 1] == '$') { // $$ -> $ if (!suppressing) result << '$'; lastPos += 2; } else if (text[pos + 1] == '{') { std::size_t startName = pos + 2; std::size_t endName = text.find_first_of(" \r\n\t}", startName); args.clear(); std::size_t endVar = parseArgs(text, endName, args); if (endVar == std::string::npos) { std::stringstream errorStream; errorStream << "variable syntax error near \"" << text.substr(pos) << "\""; errorText_ = errorStream.str(); LOG_ERROR(errorText_); return false; } std::string name = text.substr(startName, endName - startName); std::size_t nl = name.length(); if (nl > 2 && name[0] == '<' && name[nl - 1] == '>') { if (name[1] != '/') { std::string cond = name.substr(1, nl - 2); conditions.push_back(cond); if (suppressing || !conditionValue(cond)) ++suppressing; } else { std::string cond = name.substr(2, nl - 3); if (conditions.empty() || conditions.back() != cond) { std::stringstream errorStream; errorStream << "mismatching condition block end: " << cond; errorText_ = errorStream.str(); LOG_ERROR(errorText_); return false; } conditions.pop_back(); if (suppressing) --suppressing; } } else { if (!suppressing) { std::size_t colonPos = name.find(':'); bool handled = false; if (colonPos != std::string::npos) { std::string fname = name.substr(0, colonPos); std::string arg0 = name.substr(colonPos + 1); args.insert(args.begin(), WString::fromUTF8(arg0)); if (resolveFunction(fname, args, result)) handled = true; else args.erase(args.begin()); } if (!handled) resolveString(name, args, result); } } lastPos = endVar + 1; } else { if (!suppressing) result << '$'; // $. -> $. lastPos += 1; } } else { if (!suppressing) result << '$'; // $ at end of template -> $ lastPos += 1; } pos = lastPos; } result << text.substr(lastPos); return true; }
std::string EncodeHttpHeaderField(const std::string &fieldname, const WString &fieldValue) { // This implements RFC 5987 return fieldname + "*=UTF-8''" + urlEncode(fieldValue.toUTF8()); }
WString htmlEncode(const WString& text, WFlags<HtmlEncodingFlag> flags) { return WString::fromUTF8(htmlEncode(text.toUTF8(), flags)); }
void FontSupport::drawText(const WFont& font, const WRectF& rect, const WTransform& transform, Bitmap& bitmap, WFlags<AlignmentFlag> flags, const WString& text) { PANGO_LOCK; enabledFontFormats = enabledFontFormats_; PangoMatrix matrix; matrix.xx = transform.m11(); matrix.xy = transform.m21(); matrix.yx = transform.m12(); matrix.yy = transform.m22(); matrix.x0 = transform.dx(); matrix.y0 = transform.dy(); std::string utf8 = text.toUTF8(); std::vector<PangoGlyphString *> glyphs; int width; pango_context_set_matrix(context_, &matrix); /* * Oh my god, somebody explain me why we need to do this... */ WFont f = font; f.setSize(font.sizeLength().toPixels() / pango_matrix_get_font_scale_factor(&matrix)); GList *items = layoutText(f, utf8, glyphs, width); pango_context_set_matrix(context_, nullptr); AlignmentFlag hAlign = flags & AlignHorizontalMask; /* 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; } AlignmentFlag vAlign = flags & AlignVerticalMask; PangoFont *pangoFont = matchFont(font).pangoFont(); PangoFontMetrics *metrics = pango_font_get_metrics(pangoFont, nullptr); double ascent = pangoUnitsToDouble(pango_font_metrics_get_ascent(metrics)); double descent = pangoUnitsToDouble(pango_font_metrics_get_descent(metrics)); pango_font_metrics_unref(metrics); double baseline = ascent; double height = ascent + descent; double y; switch (vAlign) { case AlignmentFlag::Top: y = rect.top() + baseline; break; case AlignmentFlag::Middle: y = rect.center().y() - height / 2 + baseline; break; case AlignmentFlag::Bottom: y = rect.bottom() - height + baseline; break; default: y = 0; } FT_Bitmap bmp; bmp.buffer = bitmap.buffer(); bmp.width = bitmap.width(); bmp.rows = bitmap.height(); bmp.pitch = bitmap.pitch(); bmp.pixel_mode = FT_PIXEL_MODE_GRAY; bmp.num_grays = 16; // ??? GList *elem; unsigned i = 0; for (elem = items; elem; elem = elem->next) { PangoItem *item = (PangoItem *)elem->data; PangoAnalysis *analysis = &item->analysis; PangoGlyphString *gl = glyphs[i++]; pango_ft2_render_transformed(&bmp, &matrix, analysis->font, gl, pangoUnitsFromDouble(x), pangoUnitsFromDouble(y)); x += pangoUnitsToDouble(pango_glyph_string_get_width(gl)); pango_glyph_string_free(gl); pango_item_free(item); } g_list_free(items); }