// this function does nothing more than store all its parameters for future reference
void Layout::appendText(Glib::ustring const &text, SPStyle *style, void *source_cookie, OptionalTextTagAttrs const *optional_attributes, unsigned optional_attributes_offset, Glib::ustring::const_iterator text_begin, Glib::ustring::const_iterator text_end)
{
    if (style == NULL) return;

    InputStreamTextSource *new_source = new InputStreamTextSource;

    new_source->source_cookie = source_cookie;
    new_source->text = &text;
    new_source->text_begin = text_begin;
    new_source->text_end = text_end;
    new_source->style = style;
    sp_style_ref(style);

    new_source->text_length = 0;
    for ( ; text_begin != text_end && text_begin != text.end() ; text_begin++)
        new_source->text_length++;        // save this because calculating the length of a UTF-8 string is expensive

    if (optional_attributes) {
        // we need to fill in x and y even if the text is empty so that empty paragraphs can be positioned correctly
        _copyInputVector(optional_attributes->x, optional_attributes_offset, &new_source->x, std::max(1, new_source->text_length));
        _copyInputVector(optional_attributes->y, optional_attributes_offset, &new_source->y, std::max(1, new_source->text_length));
        _copyInputVector(optional_attributes->dx, optional_attributes_offset, &new_source->dx, new_source->text_length);
        _copyInputVector(optional_attributes->dy, optional_attributes_offset, &new_source->dy, new_source->text_length);
        _copyInputVector(optional_attributes->rotate, optional_attributes_offset, &new_source->rotate, new_source->text_length);
        if (!optional_attributes->rotate.empty() && optional_attributes_offset >= optional_attributes->rotate.size()) {
            SVGLength last_rotate;
            last_rotate = 0.f;
            for (std::vector<SVGLength>::const_iterator it = optional_attributes->rotate.begin() ; it != optional_attributes->rotate.end() ; ++it)
                if (it->_set)
                    last_rotate = *it;
            new_source->rotate.resize(1, last_rotate);
        }
    }
    
    _input_stream.push_back(new_source);
}
Inkscape::XML::Node *SPFlowtext::getAsText()
{
    if (!this->layout.outputExists()) {
        return NULL;
    }

    Inkscape::XML::Document *xml_doc = this->document->getReprDoc();
    Inkscape::XML::Node *repr = xml_doc->createElement("svg:text");
    repr->setAttribute("xml:space", "preserve");
    repr->setAttribute("style", this->getRepr()->attribute("style"));
    Geom::Point anchor_point = this->layout.characterAnchorPoint(this->layout.begin());
    sp_repr_set_svg_double(repr, "x", anchor_point[Geom::X]);
    sp_repr_set_svg_double(repr, "y", anchor_point[Geom::Y]);

    for (Inkscape::Text::Layout::iterator it = this->layout.begin() ; it != this->layout.end() ; ) {
        Inkscape::XML::Node *line_tspan = xml_doc->createElement("svg:tspan");
        line_tspan->setAttribute("sodipodi:role", "line");

        Inkscape::Text::Layout::iterator it_line_end = it;
        it_line_end.nextStartOfLine();

        while (it != it_line_end) {

            Inkscape::XML::Node *span_tspan = xml_doc->createElement("svg:tspan");
            Geom::Point anchor_point = this->layout.characterAnchorPoint(it);
            // use kerning to simulate justification and whatnot
            Inkscape::Text::Layout::iterator it_span_end = it;
            it_span_end.nextStartOfSpan();
            Inkscape::Text::Layout::OptionalTextTagAttrs attrs;
            this->layout.simulateLayoutUsingKerning(it, it_span_end, &attrs);
            // set x,y attributes only when we need to
            bool set_x = false;
            bool set_y = false;
            if (!this->transform.isIdentity()) {
                set_x = set_y = true;
            } else {
                Inkscape::Text::Layout::iterator it_chunk_start = it;
                it_chunk_start.thisStartOfChunk();
                if (it == it_chunk_start) {
                    set_x = true;
                    // don't set y so linespacing adjustments and things will still work
                }
                Inkscape::Text::Layout::iterator it_shape_start = it;
                it_shape_start.thisStartOfShape();
                if (it == it_shape_start)
                    set_y = true;
            }
            if (set_x && !attrs.dx.empty())
                attrs.dx[0] = 0.0;
            TextTagAttributes(attrs).writeTo(span_tspan);
            if (set_x)
                sp_repr_set_svg_double(span_tspan, "x", anchor_point[Geom::X]);  // FIXME: this will pick up the wrong end of counter-directional runs
            if (set_y)
                sp_repr_set_svg_double(span_tspan, "y", anchor_point[Geom::Y]);
            if (line_tspan->childCount() == 0) {
                sp_repr_set_svg_double(line_tspan, "x", anchor_point[Geom::X]);  // FIXME: this will pick up the wrong end of counter-directional runs
                sp_repr_set_svg_double(line_tspan, "y", anchor_point[Geom::Y]);
            }

            SPObject *source_obj = 0;
            void *rawptr = 0;
            Glib::ustring::iterator span_text_start_iter;
            this->layout.getSourceOfCharacter(it, &rawptr, &span_text_start_iter);
            source_obj = SP_OBJECT (rawptr);
            gchar *style_text = sp_style_write_difference((SP_IS_STRING(source_obj) ? source_obj->parent : source_obj)->style, this->style);
            if (style_text && *style_text) {
                span_tspan->setAttribute("style", style_text);
                g_free(style_text);
            }

            if (SP_IS_STRING(source_obj)) {
                Glib::ustring *string = &SP_STRING(source_obj)->string;
                SPObject *span_end_obj = 0;
                void *rawptr = 0;
                Glib::ustring::iterator span_text_end_iter;
                this->layout.getSourceOfCharacter(it_span_end, &rawptr, &span_text_end_iter);
                span_end_obj = SP_OBJECT(rawptr);
                if (span_end_obj != source_obj) {
                    if (it_span_end == this->layout.end()) {
                        span_text_end_iter = span_text_start_iter;
                        for (int i = this->layout.iteratorToCharIndex(it_span_end) - this->layout.iteratorToCharIndex(it) ; i ; --i)
                            ++span_text_end_iter;
                    } else
                        span_text_end_iter = string->end();    // spans will never straddle a source boundary
                }

                if (span_text_start_iter != span_text_end_iter) {
                    Glib::ustring new_string;
                    while (span_text_start_iter != span_text_end_iter)
                        new_string += *span_text_start_iter++;    // grr. no substr() with iterators
                    Inkscape::XML::Node *new_text = xml_doc->createTextNode(new_string.c_str());
                    span_tspan->appendChild(new_text);
                    Inkscape::GC::release(new_text);
                }
            }
            it = it_span_end;

            line_tspan->appendChild(span_tspan);
            Inkscape::GC::release(span_tspan);
        }
        repr->appendChild(line_tspan);
        Inkscape::GC::release(line_tspan);
    }

    return repr;
}