X-Git-Url: https://git.tokkee.org/?a=blobdiff_plain;f=src%2Ftext-chemistry.cpp;h=f2ea4367b9acd5ee27de6f2893d27e34d4dd3b56;hb=941ff7c62ac6b94d08e349025428ca1b2e0b6f3a;hp=453ddfc1949f6699101f9590ac32ecfd7a64db02;hpb=4ec52515516e3a6bf2fbc3ed01a7e179bde16d20;p=inkscape.git diff --git a/src/text-chemistry.cpp b/src/text-chemistry.cpp index 453ddfc19..f2ea4367b 100644 --- a/src/text-chemistry.cpp +++ b/src/text-chemistry.cpp @@ -1,10 +1,10 @@ -#define __SP_TEXT_CHEMISTRY_C__ - /* * Text commands * * Authors: * bulia byak + * Jon A. Cruz + * Abhishek Sharma * * Copyright (C) 2004 authors * @@ -14,21 +14,30 @@ #ifdef HAVE_CONFIG_H # include #endif + +#include +#include +#include + #include "libnr/nr-matrix-fns.h" #include "xml/repr.h" -#include #include "sp-rect.h" #include "sp-textpath.h" #include "inkscape.h" +#include "desktop.h" #include "document.h" #include "message-stack.h" #include "selection.h" +#include "style.h" #include "desktop-handles.h" #include "text-editing.h" +#include "text-chemistry.h" #include "sp-flowtext.h" #include "sp-flowregion.h" #include "sp-flowdiv.h" +#include "sp-tspan.h" +using Inkscape::DocumentUndo; SPItem * text_in_selection(Inkscape::Selection *selection) @@ -90,6 +99,8 @@ text_put_on_path() SPItem *text = text_or_flowtext_in_selection(selection); SPItem *shape = shape_in_selection(selection); + Inkscape::XML::Document *xml_doc = desktop->doc()->getReprDoc(); + if (!text || !shape || g_slist_length((GSList *) selection->itemList()) != 2) { sp_desktop_message_stack(desktop)->flash(Inkscape::WARNING_MESSAGE, _("Select a text and a path to put text on path.")); return; @@ -100,17 +111,42 @@ text_put_on_path() return; } - if (SP_IS_FLOWTEXT(text)) { - sp_desktop_message_stack(desktop)->flash(Inkscape::ERROR_MESSAGE, _("You cannot put flowtext on a path. Convert flowtext to text first.")); - return; - } - if (SP_IS_RECT(shape)) { // rect is the only SPShape which is not yet, and thus SVG forbids us from putting text on it sp_desktop_message_stack(desktop)->flash(Inkscape::ERROR_MESSAGE, _("You cannot put text on a rectangle in this version. Convert rectangle to path first.")); return; } + // if a flowed text is selected, convert it to a regular text object + if (SP_IS_FLOWTEXT(text)) { + + if (!SP_FLOWTEXT(text)->layout.outputExists()) { + sp_desktop_message_stack(desktop)-> + flash(Inkscape::WARNING_MESSAGE, + _("The flowed text(s) must be visible in order to be put on a path.")); + } + + Inkscape::XML::Node *repr = SP_FLOWTEXT(text)->getAsText(); + + if (!repr) return; + + Inkscape::XML::Node *parent = SP_OBJECT_REPR(text)->parent(); + parent->appendChild(repr); + + SPItem *new_item = (SPItem *) sp_desktop_document(desktop)->getObjectByRepr(repr); + new_item->doWriteTransform(repr, text->transform); + SP_OBJECT(new_item)->updateRepr(); + + Inkscape::GC::release(repr); + text->deleteObject(); // delete the orignal flowtext + + sp_desktop_document(desktop)->ensureUpToDate(); + + selection->clear(); + + text = new_item; // point to the new text + } + Inkscape::Text::Layout const *layout = te_get_layout(text); Inkscape::Text::Layout::Alignment text_alignment = layout->paragraphAlignment(layout->begin()); @@ -125,7 +161,7 @@ text_put_on_path() } // create textPath and put it into the text - Inkscape::XML::Node *textpath = sp_repr_new("svg:textPath"); + Inkscape::XML::Node *textpath = xml_doc->createElement("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) @@ -135,8 +171,8 @@ text_put_on_path() 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(); + // Make a copy of each text child + Inkscape::XML::Node *copy = ((Inkscape::XML::Node *) i->data)->duplicate(xml_doc); // We cannot have multiline in textpath, so remove line attrs from tspans if (!strcmp(copy->name(), "svg:tspan")) { copy->setAttribute("sodipodi:role", NULL); @@ -153,8 +189,8 @@ text_put_on_path() SP_OBJECT_REPR(text)->setAttribute("x", NULL); SP_OBJECT_REPR(text)->setAttribute("y", NULL); - sp_document_done(sp_desktop_document(desktop), SP_VERB_CONTEXT_TEXT, - /* TODO: annotate */ "text-chemistry.cpp:157"); + DocumentUndo::done(sp_desktop_document(desktop), SP_VERB_CONTEXT_TEXT, + _("Put text on path")); g_slist_free(text_reprs); } @@ -180,7 +216,7 @@ text_remove_from_path() continue; } - SPObject *tp = sp_object_first_child(SP_OBJECT(items->data)); + SPObject *tp = SP_OBJECT(items->data)->firstChild(); did = true; @@ -190,9 +226,9 @@ text_remove_from_path() if (!did) { sp_desktop_message_stack(desktop)->flash(Inkscape::ERROR_MESSAGE, _("No texts-on-paths in the selection.")); } else { + DocumentUndo::done(sp_desktop_document(desktop), SP_VERB_CONTEXT_TEXT, + _("Remove text from path")); selection->setList(g_slist_copy((GSList *) selection->itemList())); // reselect to update statusbar description - sp_document_done(sp_desktop_document(desktop), SP_VERB_CONTEXT_TEXT, - /* TODO: annotate */ "text-chemistry.cpp:195"); } } @@ -203,7 +239,21 @@ text_remove_all_kerns_recursively(SPObject *o) 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)) { + // if x contains a list, leave only the first value + gchar *x = (gchar *) SP_OBJECT_REPR(o)->attribute("x"); + if (x) { + gchar **xa_space = g_strsplit(x, " ", 0); + gchar **xa_comma = g_strsplit(x, ",", 0); + if (xa_space && *xa_space && *(xa_space + 1)) { + SP_OBJECT_REPR(o)->setAttribute("x", g_strdup(*xa_space)); + } else if (xa_comma && *xa_comma && *(xa_comma + 1)) { + SP_OBJECT_REPR(o)->setAttribute("x", g_strdup(*xa_comma)); + } + g_strfreev(xa_space); + g_strfreev(xa_comma); + } + + for (SPObject *i = o->firstChild(); i != NULL; i = i->getNext()) { text_remove_all_kerns_recursively(i); } } @@ -226,21 +276,22 @@ text_remove_all_kerns() for (GSList *items = g_slist_copy((GSList *) selection->itemList()); items != NULL; items = items->next) { + SPObject *obj = SP_OBJECT(items->data); - if (!SP_IS_TEXT(SP_OBJECT(items->data))) { + if (!SP_IS_TEXT(obj) && !SP_IS_TSPAN(obj) && !SP_IS_FLOWTEXT(obj)) { continue; } - text_remove_all_kerns_recursively(SP_OBJECT(items->data)); - SP_OBJECT(items->data)->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG | SP_TEXT_LAYOUT_MODIFIED_FLAG); + text_remove_all_kerns_recursively(obj); + obj->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG | SP_TEXT_LAYOUT_MODIFIED_FLAG); did = true; } if (!did) { sp_desktop_message_stack(desktop)->flash(Inkscape::ERROR_MESSAGE, _("Select text(s) to remove kerns from.")); } else { - sp_document_done(sp_desktop_document(desktop), SP_VERB_CONTEXT_TEXT, - /* TODO: annotate */ "text-chemistry.cpp:243"); + DocumentUndo::done(sp_desktop_document(desktop), SP_VERB_CONTEXT_TEXT, + _("Remove manual kerns")); } } @@ -252,10 +303,11 @@ text_flow_into_shape() return; SPDocument *doc = sp_desktop_document (desktop); + Inkscape::XML::Document *xml_doc = doc->getReprDoc(); Inkscape::Selection *selection = sp_desktop_selection(desktop); - SPItem *text = text_in_selection(selection); + SPItem *text = text_or_flowtext_in_selection(selection); SPItem *shape = shape_in_selection(selection); if (!text || !shape || g_slist_length((GSList *) selection->itemList()) < 2) { @@ -263,18 +315,20 @@ text_flow_into_shape() 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); + if (SP_IS_TEXT(text)) { + // 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"); + Inkscape::XML::Node *root_repr = xml_doc->createElement("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"); + Inkscape::XML::Node *region_repr = xml_doc->createElement("svg:flowRegion"); root_repr->appendChild(region_repr); SPObject *object = doc->getObjectByRepr(region_repr); g_return_if_fail(SP_IS_FLOWREGION(object)); @@ -285,7 +339,7 @@ text_flow_into_shape() items = items->next) { SPItem *item = SP_ITEM(items->data); if (SP_IS_SHAPE(item)){ - Inkscape::XML::Node *clone = sp_repr_new("svg:use"); + Inkscape::XML::Node *clone = xml_doc->createElement("svg:use"); clone->setAttribute("x", "0"); clone->setAttribute("y", "0"); clone->setAttribute("xlink:href", g_strdup_printf("#%s", SP_OBJECT_REPR(item)->attribute("id"))); @@ -295,28 +349,42 @@ text_flow_into_shape() } } - 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)); + if (SP_IS_TEXT(text)) { // flow from text, as string + Inkscape::XML::Node *para_repr = xml_doc->createElement("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::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 = xml_doc->createTextNode(text_ustring.c_str()); // FIXME: transfer all formatting! and convert newlines into flowParas! + para_repr->appendChild(text_repr); - 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); + Inkscape::GC::release(para_repr); + Inkscape::GC::release(text_repr); + + } else { // reflow an already flowed text, preserving paras + for (SPObject *o = SP_OBJECT(text)->children; o != NULL; o = o->next) { + if (SP_IS_FLOWPARA(o)) { + Inkscape::XML::Node *para_repr = SP_OBJECT_REPR(o)->duplicate(xml_doc); + root_repr->appendChild(para_repr); + object = doc->getObjectByRepr(para_repr); + g_return_if_fail(SP_IS_FLOWPARA(object)); + Inkscape::GC::release(para_repr); + } + } + } SP_OBJECT(text)->deleteObject (true); - sp_document_done(doc, SP_VERB_CONTEXT_TEXT, - /* TODO: annotate */ "text-chemistry.cpp:312"); + DocumentUndo::done(doc, SP_VERB_CONTEXT_TEXT, + _("Flow text into shape")); sp_desktop_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 @@ -327,6 +395,7 @@ text_unflow () return; SPDocument *doc = sp_desktop_document (desktop); + Inkscape::XML::Document *xml_doc = doc->getReprDoc(); Inkscape::Selection *selection = sp_desktop_selection(desktop); @@ -349,34 +418,45 @@ text_unflow () SPItem *flowtext = SP_ITEM(items->data); + // we discard transform when unflowing, but we must preserve expansion which is visible as + // font size multiplier + double ex = (flowtext->transform).descrim(); + + if (sp_te_get_string_multiline(flowtext) == NULL) { // flowtext is empty + continue; + } + /* Create */ - Inkscape::XML::Node *rtext = sp_repr_new("svg:text"); + Inkscape::XML::Node *rtext = xml_doc->createElement("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]); + SP_ITEM(flowtext)->invoke_bbox( &bbox, SP_ITEM(flowtext)->i2doc_affine(), TRUE); + Geom::Point xy(bbox.x0, bbox.y0); + if (xy[Geom::X] != 1e18 && xy[Geom::Y] != 1e18) { + sp_repr_set_svg_double(rtext, "x", xy[Geom::X]); + sp_repr_set_svg_double(rtext, "y", xy[Geom::Y]); } /* Create */ - Inkscape::XML::Node *rtspan = sp_repr_new("svg:tspan"); + Inkscape::XML::Node *rtspan = xml_doc->createElement("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!!! + Inkscape::XML::Node *text_repr = xml_doc->createTextNode(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); + // restore the font size multiplier from the flowtext's transform + SP_TEXT(text_object)->_adjustFontsizeRecursive(SP_ITEM(text_object), ex); + new_objs = g_slist_prepend (new_objs, text_object); old_objs = g_slist_prepend (old_objs, flowtext); @@ -394,10 +474,75 @@ text_unflow () g_slist_free (old_objs); g_slist_free (new_objs); - sp_document_done(doc, SP_VERB_CONTEXT_TEXT, - /* TODO: annotate */ "text-chemistry.cpp:398"); + DocumentUndo::done(doc, SP_VERB_CONTEXT_TEXT, + _("Unflow flowed text")); } +void +flowtext_to_text() +{ + SPDesktop *desktop = SP_ACTIVE_DESKTOP; + + Inkscape::Selection *selection = sp_desktop_selection(desktop); + + if (selection->isEmpty()) { + sp_desktop_message_stack(desktop)->flash(Inkscape::WARNING_MESSAGE, + _("Select flowed text(s) to convert.")); + return; + } + + bool did = false; + + GSList *reprs = NULL; + GSList *items = g_slist_copy((GSList *) selection->itemList()); + for (; items != NULL; items = items->next) { + + SPItem *item = (SPItem *) items->data; + + if (!SP_IS_FLOWTEXT(item)) + continue; + + if (!SP_FLOWTEXT(item)->layout.outputExists()) { + sp_desktop_message_stack(desktop)-> + flash(Inkscape::WARNING_MESSAGE, + _("The flowed text(s) must be visible in order to be converted.")); + return; + } + + Inkscape::XML::Node *repr = SP_FLOWTEXT(item)->getAsText(); + + if (!repr) break; + + did = true; + + Inkscape::XML::Node *parent = SP_OBJECT_REPR(item)->parent(); + parent->addChild(repr, SP_OBJECT_REPR(item)); + + SPItem *new_item = (SPItem *) sp_desktop_document(desktop)->getObjectByRepr(repr); + new_item->doWriteTransform(repr, item->transform); + SP_OBJECT(new_item)->updateRepr(); + + Inkscape::GC::release(repr); + item->deleteObject(); + + reprs = g_slist_prepend(reprs, repr); + } + + g_slist_free(items); + + if (did) { + DocumentUndo::done(sp_desktop_document(desktop), + SP_VERB_OBJECT_FLOWTEXT_TO_TEXT, + _("Convert flowed text to text")); + selection->setReprList(reprs); + } else { + sp_desktop_message_stack(desktop)-> + flash(Inkscape::ERROR_MESSAGE, + _("No flowed text(s) to convert in the selection.")); + } + + g_slist_free(reprs); +} /* @@ -409,4 +554,4 @@ text_unflow () fill-column:99 End: */ -// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=99 : +// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:fileencoding=utf-8:textwidth=99 :