diff options
Diffstat (limited to 'src/text-chemistry.cpp')
| -rw-r--r-- | src/text-chemistry.cpp | 390 |
1 files changed, 390 insertions, 0 deletions
diff --git a/src/text-chemistry.cpp b/src/text-chemistry.cpp new file mode 100644 index 000000000..a61bbdf73 --- /dev/null +++ b/src/text-chemistry.cpp @@ -0,0 +1,390 @@ +#define __SP_TEXT_CHEMISTRY_C__ + +/* + * Text commands + * + * Authors: + * bulia byak + * + * Copyright (C) 2004 authors + * + * Released under GNU GPL, read the file 'COPYING' for more information + */ + +#ifdef HAVE_CONFIG_H +# include <config.h> +#endif +#include "libnr/nr-matrix-fns.h" +#include "xml/repr.h" +#include <glibmm/i18n.h> +#include "sp-rect.h" +#include "sp-textpath.h" +#include "inkscape.h" +#include "document.h" +#include "message-stack.h" +#include "selection.h" +#include "desktop-handles.h" +#include "text-editing.h" +#include "sp-flowtext.h" +#include "sp-flowregion.h" +#include "sp-flowdiv.h" + + +SPItem * +text_in_selection(Inkscape::Selection *selection) +{ + for (GSList *items = (GSList *) selection->itemList(); + items != NULL; + items = items->next) { + if (SP_IS_TEXT(items->data)) + return ((SPItem *) items->data); + } + return NULL; +} + +SPItem * +flowtext_in_selection(Inkscape::Selection *selection) +{ + for (GSList *items = (GSList *) selection->itemList(); + items != NULL; + items = items->next) { + if (SP_IS_FLOWTEXT(items->data)) + return ((SPItem *) items->data); + } + return NULL; +} + +SPItem * +shape_in_selection(Inkscape::Selection *selection) +{ + for (GSList *items = (GSList *) selection->itemList(); + items != NULL; + items = items->next) { + if (SP_IS_SHAPE(items->data)) + return ((SPItem *) items->data); + } + return NULL; +} + +void +text_put_on_path() +{ + SPDesktop *desktop = SP_ACTIVE_DESKTOP; + if (!desktop) + return; + + Inkscape::Selection *selection = SP_DT_SELECTION(desktop); + + SPItem *text = text_in_selection(selection); + SPItem *shape = shape_in_selection(selection); + + if (!text || !shape || g_slist_length((GSList *) selection->itemList()) != 2) { + SP_DT_MSGSTACK(desktop)->flash(Inkscape::WARNING_MESSAGE, _("Select <b>a text and a path</b> to put text on path.")); + return; + } + + if (SP_IS_TEXT_TEXTPATH(text)) { + SP_DT_MSGSTACK(desktop)->flash(Inkscape::ERROR_MESSAGE, _("This text object is <b>already put to a path</b>. Remove it from the path first. Use <b>Shift+D</b> to look up its path.")); + return; + } + + if (SP_IS_RECT(shape)) { + // rect is the only SPShape which is not <path> yet, and thus SVG forbids us from putting text on it + SP_DT_MSGSTACK(desktop)->flash(Inkscape::ERROR_MESSAGE, _("You cannot put text on a rectangle in this version. Convert rectangle to path first.")); + return; + } + + Inkscape::Text::Layout const *layout = te_get_layout(text); + Inkscape::Text::Layout::Alignment text_alignment = layout->paragraphAlignment(layout->begin()); + + // remove transform from text, but recursively scale text's fontsize by the expansion + SP_TEXT(text)->_adjustFontsizeRecursive (text, NR::expansion(SP_ITEM(text)->transform)); + SP_OBJECT_REPR(text)->setAttribute("transform", NULL); + + // make a list of text children + GSList *text_reprs = NULL; + for (SPObject *o = SP_OBJECT(text)->children; o != NULL; o = o->next) { + text_reprs = g_slist_prepend(text_reprs, SP_OBJECT_REPR(o)); + } + + // create textPath and put it into the text + Inkscape::XML::Node *textpath = sp_repr_new("svg:textPath"); + // reference the shape + textpath->setAttribute("xlink:href", g_strdup_printf("#%s", SP_OBJECT_REPR(shape)->attribute("id"))); + if (text_alignment == Inkscape::Text::Layout::RIGHT) + textpath->setAttribute("startOffset", "100%"); + else if (text_alignment == Inkscape::Text::Layout::CENTER) + textpath->setAttribute("startOffset", "50%"); + SP_OBJECT_REPR(text)->addChild(textpath, NULL); + + for ( GSList *i = text_reprs ; i ; i = i->next ) { + // make a copy of each text child + Inkscape::XML::Node *copy = ((Inkscape::XML::Node *) i->data)->duplicate(); + // We cannot have multiline in textpath, so remove line attrs from tspans + if (!strcmp(copy->name(), "svg:tspan")) { + copy->setAttribute("sodipodi:role", NULL); + copy->setAttribute("x", NULL); + copy->setAttribute("y", NULL); + } + // remove the old repr from under text + SP_OBJECT_REPR(text)->removeChild((Inkscape::XML::Node *) i->data); + // put its copy into under textPath + textpath->addChild(copy, NULL); // fixme: copy id + } + + // x/y are useless with textpath, and confuse Batik 1.5 + SP_OBJECT_REPR(text)->setAttribute("x", NULL); + SP_OBJECT_REPR(text)->setAttribute("y", NULL); + + sp_document_done(SP_DT_DOCUMENT(desktop)); + g_slist_free(text_reprs); +} + +void +text_remove_from_path() +{ + SPDesktop *desktop = SP_ACTIVE_DESKTOP; + + Inkscape::Selection *selection = SP_DT_SELECTION(desktop); + + if (selection->isEmpty()) { + SP_DT_MSGSTACK(desktop)->flash(Inkscape::WARNING_MESSAGE, _("Select <b>a text on path</b> to remove it from path.")); + return; + } + + bool did = false; + + for (GSList *items = g_slist_copy((GSList *) selection->itemList()); + items != NULL; + items = items->next) { + + if (!SP_IS_TEXT_TEXTPATH(SP_OBJECT(items->data))) { + continue; + } + + SPObject *tp = sp_object_first_child(SP_OBJECT(items->data)); + + did = true; + + sp_textpath_to_text(tp); + } + + if (!did) { + SP_DT_MSGSTACK(desktop)->flash(Inkscape::ERROR_MESSAGE, _("<b>No texts-on-paths</b> in the selection.")); + } else { + selection->setList(g_slist_copy((GSList *) selection->itemList())); // reselect to update statusbar description + sp_document_done(SP_DT_DOCUMENT(desktop)); + } +} + +void +text_remove_all_kerns_recursively(SPObject *o) +{ + SP_OBJECT_REPR(o)->setAttribute("dx", NULL); + SP_OBJECT_REPR(o)->setAttribute("dy", NULL); + SP_OBJECT_REPR(o)->setAttribute("rotate", NULL); + + for (SPObject *i = sp_object_first_child(o); i != NULL; i = SP_OBJECT_NEXT(i)) { + text_remove_all_kerns_recursively(i); + } +} + +//FIXME: must work with text selection +void +text_remove_all_kerns() +{ + SPDesktop *desktop = SP_ACTIVE_DESKTOP; + + Inkscape::Selection *selection = SP_DT_SELECTION(desktop); + + if (selection->isEmpty()) { + SP_DT_MSGSTACK(desktop)->flash(Inkscape::WARNING_MESSAGE, _("Select <b>text(s)</b> to remove kerns from.")); + return; + } + + bool did = false; + + for (GSList *items = g_slist_copy((GSList *) selection->itemList()); + items != NULL; + items = items->next) { + + if (!SP_IS_TEXT(SP_OBJECT(items->data))) { + continue; + } + + text_remove_all_kerns_recursively(SP_OBJECT(items->data)); + SP_OBJECT(items->data)->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG | SP_TEXT_LAYOUT_MODIFIED_FLAG); + did = true; + } + + if (!did) { + SP_DT_MSGSTACK(desktop)->flash(Inkscape::ERROR_MESSAGE, _("Select <b>text(s)</b> to remove kerns from.")); + } else { + sp_document_done(SP_DT_DOCUMENT(desktop)); + } +} + +void +text_flow_into_shape() +{ + SPDesktop *desktop = SP_ACTIVE_DESKTOP; + if (!desktop) + return; + + SPDocument *doc = SP_DT_DOCUMENT (desktop); + + Inkscape::Selection *selection = SP_DT_SELECTION(desktop); + + SPItem *text = text_in_selection(selection); + SPItem *shape = shape_in_selection(selection); + + if (!text || !shape || g_slist_length((GSList *) selection->itemList()) < 2) { + SP_DT_MSGSTACK(desktop)->flash(Inkscape::WARNING_MESSAGE, _("Select <b>a text</b> and one or more <b>paths or shapes</b> to flow text into frame.")); + return; + } + + // remove transform from text, but recursively scale text's fontsize by the expansion + SP_TEXT(text)->_adjustFontsizeRecursive(text, NR::expansion(SP_ITEM(text)->transform)); + SP_OBJECT_REPR(text)->setAttribute("transform", NULL); + + Inkscape::XML::Node *root_repr = sp_repr_new("svg:flowRoot"); + root_repr->setAttribute("xml:space", "preserve"); // we preserve spaces in the text objects we create + root_repr->setAttribute("style", SP_OBJECT_REPR(text)->attribute("style")); // fixme: transfer style attrs too + SP_OBJECT_REPR(SP_OBJECT_PARENT(shape))->appendChild(root_repr); + SPObject *root_object = doc->getObjectByRepr(root_repr); + g_return_if_fail(SP_IS_FLOWTEXT(root_object)); + + Inkscape::XML::Node *region_repr = sp_repr_new("svg:flowRegion"); + root_repr->appendChild(region_repr); + SPObject *object = doc->getObjectByRepr(region_repr); + g_return_if_fail(SP_IS_FLOWREGION(object)); + + /* Add clones */ + for (GSList *items = (GSList *) selection->itemList(); + items != NULL; + items = items->next) { + SPItem *item = SP_ITEM(items->data); + if (SP_IS_SHAPE(item)){ + Inkscape::XML::Node *clone = sp_repr_new("svg:use"); + clone->setAttribute("x", "0"); + clone->setAttribute("y", "0"); + clone->setAttribute("xlink:href", g_strdup_printf("#%s", SP_OBJECT_REPR(item)->attribute("id"))); + + // add the new clone to the region + region_repr->appendChild(clone); + } + } + + Inkscape::XML::Node *para_repr = sp_repr_new("svg:flowPara"); + root_repr->appendChild(para_repr); + object = doc->getObjectByRepr(para_repr); + g_return_if_fail(SP_IS_FLOWPARA(object)); + + Inkscape::Text::Layout const *layout = te_get_layout(text); + Glib::ustring text_ustring = sp_te_get_string_multiline(text, layout->begin(), layout->end()); + + Inkscape::XML::Node *text_repr = sp_repr_new_text(text_ustring.c_str()); // FIXME: transfer all formatting! and convert newlines into flowParas! + para_repr->appendChild(text_repr); + + SP_OBJECT(text)->deleteObject (true); + + sp_document_done(doc); + + SP_DT_SELECTION(desktop)->set(SP_ITEM(root_object)); + + Inkscape::GC::release(root_repr); + Inkscape::GC::release(region_repr); + Inkscape::GC::release(para_repr); + Inkscape::GC::release(text_repr); +} + +void +text_unflow () +{ + SPDesktop *desktop = SP_ACTIVE_DESKTOP; + if (!desktop) + return; + + SPDocument *doc = SP_DT_DOCUMENT (desktop); + + Inkscape::Selection *selection = SP_DT_SELECTION(desktop); + + + if (!flowtext_in_selection(selection) || g_slist_length((GSList *) selection->itemList()) < 1) { + SP_DT_MSGSTACK(desktop)->flash(Inkscape::WARNING_MESSAGE, _("Select <b>a flowed text</b> to unflow it.")); + return; + } + + GSList *new_objs = NULL; + GSList *old_objs = NULL; + + for (GSList *items = g_slist_copy((GSList *) selection->itemList()); + items != NULL; + items = items->next) { + + if (!SP_IS_FLOWTEXT(SP_OBJECT(items->data))) { + continue; + } + + SPItem *flowtext = SP_ITEM(items->data); + + /* Create <text> */ + Inkscape::XML::Node *rtext = sp_repr_new("svg:text"); + rtext->setAttribute("xml:space", "preserve"); // we preserve spaces in the text objects we create + + /* Set style */ + rtext->setAttribute("style", SP_OBJECT_REPR(flowtext)->attribute("style")); // fixme: transfer style attrs too; and from descendants + + NRRect bbox; + sp_item_invoke_bbox(SP_ITEM(flowtext), &bbox, sp_item_i2doc_affine(SP_ITEM(flowtext)), TRUE); + NR::Point xy(bbox.x0, bbox.y0); + if (xy[NR::X] != 1e18 && xy[NR::Y] != 1e18) { + sp_repr_set_svg_double(rtext, "x", xy[NR::X]); + sp_repr_set_svg_double(rtext, "y", xy[NR::Y]); + } + + /* Create <tspan> */ + Inkscape::XML::Node *rtspan = sp_repr_new("svg:tspan"); + rtspan->setAttribute("sodipodi:role", "line"); // otherwise, why bother creating the tspan? + rtext->addChild(rtspan, NULL); + + gchar *text_string = sp_te_get_string_multiline(flowtext); + Inkscape::XML::Node *text_repr = sp_repr_new_text(text_string); // FIXME: transfer all formatting!!! + free(text_string); + rtspan->appendChild(text_repr); + + SP_OBJECT_REPR(SP_OBJECT_PARENT(flowtext))->appendChild(rtext); + SPObject *text_object = doc->getObjectByRepr(rtext); + + new_objs = g_slist_prepend (new_objs, text_object); + old_objs = g_slist_prepend (old_objs, flowtext); + + Inkscape::GC::release(rtext); + Inkscape::GC::release(rtspan); + Inkscape::GC::release(text_repr); + } + + selection->clear(); + selection->setList(new_objs); + for (GSList *i = old_objs; i; i = i->next) { + SP_OBJECT(i->data)->deleteObject (true); + } + + g_slist_free (old_objs); + g_slist_free (new_objs); + + sp_document_done(doc); +} + + + +/* + Local Variables: + mode:c++ + c-file-style:"stroustrup" + c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +)) + indent-tabs-mode:nil + fill-column:99 + End: +*/ +// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=99 : |
