Code

Extensions. Add option to choose dxf output units
[inkscape.git] / src / text-chemistry.cpp
index 4ea47fd278fd4caf19e1d9616d5e28126be4c798..f2ea4367b9acd5ee27de6f2893d27e34d4dd3b56 100644 (file)
@@ -1,10 +1,10 @@
-#define __SP_TEXT_CHEMISTRY_C__
-
 /*
  * Text commands
  *
  * Authors:
  *   bulia byak
+ *   Jon A. Cruz <jon@joncruz.org>
+ *   Abhishek Sharma
  *
  * Copyright (C) 2004 authors
  *
 #ifdef HAVE_CONFIG_H
 # include <config.h>
 #endif
+
+#include <cstring>
+#include <string>
+#include <glibmm/i18n.h>
+
 #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 "desktop.h"
 #include "document.h"
 #include "message-stack.h"
 #include "selection.h"
@@ -30,7 +35,9 @@
 #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)
@@ -92,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 <b>a text and a path</b> to put text on path."));
         return;
@@ -110,18 +119,28 @@ text_put_on_path()
 
     // 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 <b>visible</b> 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);
-        sp_item_write_transform(new_item, repr, text->transform);
+        new_item->doWriteTransform(repr, text->transform);
         SP_OBJECT(new_item)->updateRepr();
 
         Inkscape::GC::release(repr);
         text->deleteObject(); // delete the orignal flowtext
 
-        sp_document_ensure_up_to_date(sp_desktop_document(desktop));
+        sp_desktop_document(desktop)->ensureUpToDate();
 
         selection->clear();
 
@@ -142,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)
@@ -152,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);
@@ -170,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, 
-                     _("Put text on path"));
+    DocumentUndo::done(sp_desktop_document(desktop), SP_VERB_CONTEXT_TEXT, 
+                       _("Put text on path"));
     g_slist_free(text_reprs);
 }
 
@@ -197,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;
 
@@ -207,9 +226,9 @@ text_remove_from_path()
     if (!did) {
         sp_desktop_message_stack(desktop)->flash(Inkscape::ERROR_MESSAGE, _("<b>No texts-on-paths</b> 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, 
-                         _("Remove text from path"));
     }
 }
 
@@ -220,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);
     }
 }
@@ -243,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 <b>text(s)</b> to remove kerns from."));
     } else {
-        sp_document_done(sp_desktop_document(desktop), SP_VERB_CONTEXT_TEXT, 
-                         _("Remove manual kerns"));
+        DocumentUndo::done(sp_desktop_document(desktop), SP_VERB_CONTEXT_TEXT, 
+                           _("Remove manual kerns"));
     }
 }
 
@@ -269,6 +303,7 @@ 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);
 
@@ -286,14 +321,14 @@ text_flow_into_shape()
       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));
@@ -304,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")));
@@ -315,7 +350,7 @@ text_flow_into_shape()
     }
 
     if (SP_IS_TEXT(text)) { // flow from text, as string
-        Inkscape::XML::Node *para_repr = sp_repr_new("svg:flowPara");
+        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));
@@ -323,7 +358,7 @@ text_flow_into_shape()
         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!
+        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::GC::release(para_repr);
@@ -332,7 +367,7 @@ text_flow_into_shape()
     } 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();
+                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));
@@ -343,8 +378,8 @@ text_flow_into_shape()
 
     SP_OBJECT(text)->deleteObject (true);
 
-    sp_document_done(doc, SP_VERB_CONTEXT_TEXT,
-                     _("Flow text into shape"));
+    DocumentUndo::done(doc, SP_VERB_CONTEXT_TEXT,
+                       _("Flow text into shape"));
 
     sp_desktop_selection(desktop)->set(SP_ITEM(root_object));
 
@@ -360,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);
 
@@ -382,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 <text> */
-        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 <tspan> */
-        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);
 
@@ -427,8 +474,8 @@ text_unflow ()
     g_slist_free (old_objs);
     g_slist_free (new_objs);
 
-    sp_document_done(doc, SP_VERB_CONTEXT_TEXT, 
-                     _("Unflow flowed text"));
+    DocumentUndo::done(doc, SP_VERB_CONTEXT_TEXT, 
+                       _("Unflow flowed text"));
 }
 
 void
@@ -455,14 +502,24 @@ flowtext_to_text()
         if (!SP_IS_FLOWTEXT(item))
             continue;
 
-        did = true;
+        if (!SP_FLOWTEXT(item)->layout.outputExists()) {
+            sp_desktop_message_stack(desktop)->
+                flash(Inkscape::WARNING_MESSAGE, 
+                      _("The flowed text(s) must be <b>visible</b> 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->appendChild(repr);
+        parent->addChild(repr, SP_OBJECT_REPR(item));
 
         SPItem *new_item = (SPItem *) sp_desktop_document(desktop)->getObjectByRepr(repr);
-        sp_item_write_transform(new_item, repr, item->transform);
+        new_item->doWriteTransform(repr, item->transform);
         SP_OBJECT(new_item)->updateRepr();
     
         Inkscape::GC::release(repr);
@@ -474,9 +531,9 @@ flowtext_to_text()
     g_slist_free(items);
 
     if (did) {
-        sp_document_done(sp_desktop_document(desktop), 
-                         SP_VERB_OBJECT_FLOWTEXT_TO_TEXT,
-                         _("Convert flowed text to text"));
+        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)->
@@ -497,4 +554,4 @@ flowtext_to_text()
   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 :