index 8d60f5fe01fd38b57a2a9c452b7491d835d2b977..fcb9c23d2389dd7fea5b9643d4ee32b013ab291b 100644 (file)
* the inputting and outputting of OpenDocument Format (ODF) files from
* within Inkscape. Although the initial implementations will be very lossy
* do to the differences in the models of SVG and ODF, they will hopefully
- * improve greatly with time.
+ * improve greatly with time. People should consider this to be a framework
+ * that can be continously upgraded for ever improving fidelity. Potential
+ * developers should especially look in preprocess() and writeTree() to see how
+ * the SVG tree is scanned, read, translated, and then written to ODF.
*
* http://www.w3.org/TR/2004/REC-DOM-Level-3-Core-20040407/idl-definitions.html
*
#include "dom/util/ziptool.h"
#include "dom/io/domstream.h"
#include "dom/io/bufferstream.h"
+#include "dom/io/stringstream.h"
//# Shorthand notation
typedef org::w3c::dom::DOMString DOMString;
+typedef org::w3c::dom::XMLCh XMLCh;
typedef org::w3c::dom::io::OutputStreamWriter OutputStreamWriter;
typedef org::w3c::dom::io::BufferOutputStream BufferOutputStream;
+typedef org::w3c::dom::io::StringOutputStream StringOutputStream;
//########################################################################
//# C L A S S SingularValueDecomposition
SVDMatrix()
{
- d = (double *)0;
- rows = cols = size = 0;
+ init();
}
SVDMatrix(unsigned int rowSize, unsigned int colSize)
{
+ init();
rows = rowSize;
cols = colSize;
size = rows * cols;
- d = new double[size];
+ d = new double[size];
for (unsigned int i=0 ; i<size ; i++)
d[i] = 0.0;
}
SVDMatrix(double *vals, unsigned int rowSize, unsigned int colSize)
{
+ init();
rows = rowSize;
cols = colSize;
size = rows * cols;
- d = new double[size];
+ d = new double[size];
for (unsigned int i=0 ; i<size ; i++)
d[i] = vals[i];
}
- virtual ~SVDMatrix()
- {
- delete d;
- }
SVDMatrix(const SVDMatrix &other)
{
+ init();
assign(other);
}
return *this;
}
+ virtual ~SVDMatrix()
+ {
+ delete[] d;
+ }
+
double& operator() (unsigned int row, unsigned int col)
{
if (row >= rows || col >= cols)
private:
+ virtual void init()
+ {
+ badval = 0.0;
+ d = NULL;
+ rows = 0;
+ cols = 0;
+ size = 0;
+ }
+
void assign(const SVDMatrix &other)
{
if (d)
- delete d;
+ {
+ delete[] d;
+ d = 0;
+ }
rows = other.rows;
cols = other.cols;
size = other.size;
unsigned int size;
};
+
+
/**
*
* ====================================================
SingularValueDecomposition (const SVDMatrix &mat)
{
- A = mat;
+ A = mat;
+ s = NULL;
+ s_size = 0;
calculate();
}
virtual ~SingularValueDecomposition()
{
- delete s;
+ delete[] s;
}
/**
/**
* Return the s[index] value
- */
- double getS(unsigned int index);
+ */ double getS(unsigned int index);
/**
* Two norm
#define pi 3.14159
//#define pxToCm 0.0275
-#define pxToCm 0.04
+#define pxToCm 0.03
#define piToRad 0.0174532925
#define docHeightCm 22.86
//# O U T P U T
//########################################################################
-static std::string getAttribute( Inkscape::XML::Node *node, char *attrName)
+/**
+ * Get the value of a node/attribute pair
+ */
+static Glib::ustring getAttribute( Inkscape::XML::Node *node, char *attrName)
{
- std::string val;
+ Glib::ustring val;
char *valstr = (char *)node->attribute(attrName);
if (valstr)
val = (const char *)valstr;
}
-static std::string getExtension(const std::string &fname)
+
+/**
+ * Get the extension suffix from the end of a file name
+ */
+static Glib::ustring getExtension(const Glib::ustring &fname)
{
- std::string ext;
+ Glib::ustring ext;
unsigned int pos = fname.rfind('.');
if (pos == fname.npos)
}
-static std::string formatTransform(NR::Matrix &tf)
+static Glib::ustring formatTransform(NR::Matrix &tf)
{
- std::string str;
+ Glib::ustring str;
if (!tf.test_identity())
{
- char buf[128];
- snprintf(buf, 127, "matrix(%.3f %.3f %.3f %.3f %.3f %.3f)",
+ StringOutputStream outs;
+ OutputStreamWriter out(outs);
+ out.printf("matrix(%.3f %.3f %.3f %.3f %.3f %.3f)",
tf[0], tf[1], tf[2], tf[3], tf[4], tf[5]);
- str = buf;
+ str = outs.getString();
}
return str;
}
+/**
+ * Encode a string, checking for XML entities, to
+ * make an XML string safe for output
+ */
+static Glib::ustring toXml(const Glib::ustring &str)
+{
+ Glib::ustring outbuf;
+ for (unsigned int i=0 ; i<str.size() ; i++)
+ {
+ XMLCh ch = (XMLCh) str[i];
+ if (ch == '&')
+ outbuf.append("&r;");
+ else if (ch == '<')
+ outbuf.append("<");
+ else if (ch == '>')
+ outbuf.append(">");
+ else if (ch == '"')
+ outbuf.append(""");
+ else if (ch == '\'')
+ outbuf.append("'");
+ else
+ outbuf.push_back(ch);
+ }
+ return outbuf;
+}
+
+
+
/**
}
+
+
/**
* Get the bounding box of an item, as mapped onto
* an ODF document, in cm.
+static void gatherText(Inkscape::XML::Node *node, Glib::ustring &buf)
+{
+ if (node->type() == Inkscape::XML::TEXT_NODE)
+ {
+ char *s = (char *)node->content();
+ if (s)
+ buf.append(s);
+ }
+
+ for (Inkscape::XML::Node *child = node->firstChild() ;
+ child != NULL; child = child->next())
+ {
+ gatherText(child, buf);
+ }
+
+}
/**
- * Method descends into the repr tree, converting image and style info
+ * FIRST PASS.
+ * Method descends into the repr tree, converting image, style, and gradient info
* into forms compatible in ODF.
*/
void
OdfOutput::preprocess(ZipFile &zf, Inkscape::XML::Node *node)
{
- std::string nodeName = node->name();
- std::string id = getAttribute(node, "id");
+ Glib::ustring nodeName = node->name();
+ Glib::ustring id = getAttribute(node, "id");
+
+ //### First, check for metadata
+ if (nodeName == "metadata" || nodeName == "svg:metadata")
+ {
+ Inkscape::XML::Node *mchild = node->firstChild() ;
+ if (!mchild || strcmp(mchild->name(), "rdf:RDF"))
+ return;
+ Inkscape::XML::Node *rchild = mchild->firstChild() ;
+ if (!rchild || strcmp(rchild->name(), "cc:Work"))
+ return;
+ for (Inkscape::XML::Node *cchild = rchild->firstChild() ;
+ cchild ; cchild = cchild->next())
+ {
+ Glib::ustring ccName = cchild->name();
+ Glib::ustring ccVal;
+ gatherText(cchild, ccVal);
+ //g_message("ccName: %s ccVal:%s", ccName.c_str(), ccVal.c_str());
+ metadata[ccName] = ccVal;
+ }
+ return;
+ }
+ //Now consider items.
SPObject *reprobj = SP_ACTIVE_DOCUMENT->getObjectByRepr(node);
if (!reprobj)
return;
//### Get SVG-to-ODF transform
NR::Matrix tf = getODFTransform(item);
-
if (nodeName == "image" || nodeName == "svg:image")
{
//g_message("image");
- std::string href = getAttribute(node, "xlink:href");
+ Glib::ustring href = getAttribute(node, "xlink:href");
if (href.size() > 0)
{
- std::string oldName = href;
- std::string ext = getExtension(oldName);
+ Glib::ustring oldName = href;
+ Glib::ustring ext = getExtension(oldName);
if (ext == ".jpeg")
ext = ".jpg";
if (imageTable.find(oldName) == imageTable.end())
char buf[64];
snprintf(buf, 63, "Pictures/image%d%s",
(int)imageTable.size(), ext.c_str());
- std::string newName = buf;
+ Glib::ustring newName = buf;
imageTable[oldName] = newName;
- std::string comment = "old name was: ";
+ Glib::ustring comment = "old name was: ";
comment.append(oldName);
URI oldUri(oldName);
//g_message("oldpath:%s", oldUri.getNativePath().c_str());
bool isGradient = false;
StyleInfo si;
+ //## Style. Look in writeStyle() below to see what info
+ // we need to read into StyleInfo. Note that we need to
+ // determine whether information goes into a style element
+ // or a gradient element.
//## FILL
if (style->fill.type == SP_PAINT_TYPE_COLOR)
{
if (gi.equals(*iter))
{
//map to existing gradientTable entry
- std::string gradientName = iter->name;
+ Glib::ustring gradientName = iter->name;
//g_message("found duplicate style:%s", gradientName.c_str());
gradientLookupTable[id] = gradientName;
gradientMatch = true;
{
char buf[16];
snprintf(buf, 15, "gradient%d", (int)gradientTable.size());
- std::string gradientName = buf;
+ Glib::ustring gradientName = buf;
gi.name = gradientName;
gradientTable.push_back(gi);
gradientLookupTable[id] = gradientName;
if (si.equals(*iter))
{
//map to existing styleTable entry
- std::string styleName = iter->name;
+ Glib::ustring styleName = iter->name;
//g_message("found duplicate style:%s", styleName.c_str());
styleLookupTable[id] = styleName;
styleMatch = true;
{
char buf[16];
snprintf(buf, 15, "style%d", (int)styleTable.size());
- std::string styleName = buf;
+ Glib::ustring styleName = buf;
si.name = styleName;
styleTable.push_back(si);
styleLookupTable[id] = styleName;
+/**
+ * Writes the manifest. Currently it only changes according to the
+ * file names of images packed into the zip file.
+ */
bool OdfOutput::writeManifest(ZipFile &zf)
{
BufferOutputStream bouts;
outs.printf(" <manifest:file-entry manifest:media-type=\"text/xml\" manifest:full-path=\"content.xml\"/>\n");
outs.printf(" <manifest:file-entry manifest:media-type=\"text/xml\" manifest:full-path=\"meta.xml\"/>\n");
outs.printf(" <!--List our images here-->\n");
- std::map<std::string, std::string>::iterator iter;
+ std::map<Glib::ustring, Glib::ustring>::iterator iter;
for (iter = imageTable.begin() ; iter!=imageTable.end() ; iter++)
{
- std::string oldName = iter->first;
- std::string newName = iter->second;
+ Glib::ustring oldName = iter->first;
+ Glib::ustring newName = iter->second;
- std::string ext = getExtension(oldName);
+ Glib::ustring ext = getExtension(oldName);
if (ext == ".jpeg")
ext = ".jpg";
outs.printf(" <manifest:file-entry manifest:media-type=\"");
}
+/**
+ * This writes the document meta information to meta.xml
+ */
bool OdfOutput::writeMeta(ZipFile &zf)
{
BufferOutputStream bouts;
time_t tim;
time(&tim);
+ std::map<Glib::ustring, Glib::ustring>::iterator iter;
+ Glib::ustring creator = "unknown";
+ iter = metadata.find("dc:creator");
+ if (iter != metadata.end())
+ creator = iter->second;
+ Glib::ustring date = "";
+ iter = metadata.find("dc:date");
+ if (iter != metadata.end())
+ date = iter->second;
+
outs.printf("<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n");
outs.printf("\n");
outs.printf("\n");
outs.printf("xmlns:anim=\"urn:oasis:names:tc:opendocument:xmlns:animation:1.0\"\n");
outs.printf("office:version=\"1.0\">\n");
outs.printf("<office:meta>\n");
- outs.printf(" <meta:generator>Inkscape.org - 0.44</meta:generator>\n");
- outs.printf(" <meta:initial-creator>clark kent</meta:initial-creator>\n");
- outs.printf(" <meta:creation-date>2006-04-13T17:12:29</meta:creation-date>\n");
- outs.printf(" <dc:creator>clark kent</dc:creator>\n");
- outs.printf(" <dc:date>2006-04-13T17:13:20</dc:date>\n");
- outs.printf(" <dc:language>en-US</dc:language>\n");
+ outs.printf(" <meta:generator>Inkscape.org - 0.45</meta:generator>\n");
+ outs.printf(" <meta:initial-creator>%s</meta:initial-creator>\n",
+ toXml(creator).c_str());
+ outs.printf(" <meta:creation-date>%s</meta:creation-date>\n", date.c_str());
+ for (iter = metadata.begin() ; iter != metadata.end() ; iter++)
+ {
+ Glib::ustring name = iter->first;
+ Glib::ustring value = iter->second;
+ if (name.size() > 0 && value.size()>0)
+ {
+ outs.printf(" <%s>%s</%s>\n",
+ toXml(name).c_str(), toXml(value).c_str(), toXml(name).c_str());
+ }
+ }
outs.printf(" <meta:editing-cycles>2</meta:editing-cycles>\n");
outs.printf(" <meta:editing-duration>PT56S</meta:editing-duration>\n");
outs.printf(" <meta:user-defined meta:name=\"Info 1\"/>\n");
}
+
+
+/**
+ * This is called just before writeTree(), since it will write style and
+ * gradient information above the <draw> tag in the content.xml file
+ */
bool OdfOutput::writeStyle(Writer &outs)
{
outs.printf("<office:automatic-styles>\n");
outs.printf(" <style:graphic-properties draw:stroke=\"none\" draw:fill=\"none\"\n");
outs.printf(" draw:textarea-horizontal-align=\"center\"\n");
outs.printf(" draw:textarea-vertical-align=\"middle\" draw:color-mode=\"standard\"\n");
- outs.printf(" draw:luminance=\"0%\" draw:contrast=\"0%\" draw:gamma=\"100%\" draw:red=\"0%\"\n");
- outs.printf(" draw:green=\"0%\" draw:blue=\"0%\" fo:clip=\"rect(0cm 0cm 0cm 0cm)\"\n");
- outs.printf(" draw:image-opacity=\"100%\" style:mirror=\"none\"/>\n");
+ outs.printf(" draw:luminance=\"0%%\" draw:contrast=\"0%%\" draw:gamma=\"100%%\" draw:red=\"0%%\"\n");
+ outs.printf(" draw:green=\"0%%\" draw:blue=\"0%%\" fo:clip=\"rect(0cm 0cm 0cm 0cm)\"\n");
+ outs.printf(" draw:image-opacity=\"100%%\" style:mirror=\"none\"/>\n");
outs.printf("</style:style>\n");
outs.printf("<style:style style:name=\"P1\" style:family=\"paragraph\">\n");
outs.printf(" <style:paragraph-properties fo:text-align=\"center\"/>\n");
outs.printf("</style:style>\n");
- //## Dump our style table
+ /*
+ ==========================================================
+ Dump our style table. Styles should have a general layout
+ something like the following. Look in:
+ http://books.evc-cit.info/odbook/ch06.html#draw-style-file-section
+ for style and gradient information.
+ <style:style style:name="gr13"
+ style:family="graphic" style:parent-style-name="standard">
+ <style:graphic-properties draw:stroke="solid"
+ svg:stroke-width="0.1cm"
+ svg:stroke-color="#ff0000"
+ draw:fill="solid" draw:fill-color="#e6e6ff"/>
+ </style:style>
+ ==========================================================
+ */
outs.printf("<!-- ####### Styles from Inkscape document ####### -->\n");
std::vector<StyleInfo>::iterator iter;
for (iter = styleTable.begin() ; iter != styleTable.end() ; iter++)
/**
* Writes an SVG path as an ODF <draw:path>
*/
-static void
+static int
writePath(Writer &outs, NArtBpath const *bpath,
NR::Matrix &tf, double xoff, double yoff)
{
- bool closed = false;
+ bool closed = false;
+ int nrPoints = 0;
NArtBpath *bp = (NArtBpath *)bpath;
+
+ double destx = 0.0;
+ double desty = 0.0;
+ int code = -1;
+
for ( ; bp->code != NR_END; bp++)
{
+ code = bp->code;
+
NR::Point const p1(bp->c(1) * tf);
NR::Point const p2(bp->c(2) * tf);
NR::Point const p3(bp->c(3) * tf);
double x1 = (p1[NR::X] - xoff) * 1000.0;
+ if (fabs(x1)<1.0) x1=0.0;
double y1 = (p1[NR::Y] - yoff) * 1000.0;
+ if (fabs(y1)<1.0) y1=0.0;
double x2 = (p2[NR::X] - xoff) * 1000.0;
+ if (fabs(x2)<1.0) x2=0.0;
double y2 = (p2[NR::Y] - yoff) * 1000.0;
+ if (fabs(y2)<1.0) y2=0.0;
double x3 = (p3[NR::X] - xoff) * 1000.0;
+ if (fabs(x3)<1.0) x3=0.0;
double y3 = (p3[NR::Y] - yoff) * 1000.0;
+ if (fabs(y3)<1.0) y3=0.0;
+ destx = x3;
+ desty = y3;
- switch (bp->code)
+ switch (code)
{
case NR_LINETO:
- outs.printf("L %.3f,%.3f ", x3 , y3);
+ outs.printf("L %.3f %.3f ", destx, desty);
break;
case NR_CURVETO:
- outs.printf("C %.3f,%.3f %.3f,%.3f %.3f,%.3f ",
- x1, y1, x2, y2, x3, y3);
+ outs.printf("C %.3f %.3f %.3f %.3f %.3f %.3f ",
+ x1, y1, x2, y2, destx, desty);
break;
case NR_MOVETO_OPEN:
case NR_MOVETO:
if (closed)
- outs.printf("z ");
- closed = ( bp->code == NR_MOVETO );
- outs.printf("M %.3f,%.3f ", x3 , y3);
+ outs.printf("Z ");
+ closed = ( code == NR_MOVETO );
+ outs.printf("M %.3f %.3f ", destx, desty);
break;
default:
}
+ nrPoints++;
}
if (closed)
- outs.printf("z");;
+ {
+ outs.printf("Z");
+ }
+ return nrPoints;
}
/**
+ * SECOND PASS.
* This is the main SPObject tree output to ODF. preprocess()
- * must be called prior to this
+ * must be called prior to this, as elements will often reference
+ * data parsed and tabled in preprocess().
*/
bool OdfOutput::writeTree(Writer &outs, Inkscape::XML::Node *node)
{
SPItem *item = SP_ITEM(reprobj);
- std::string nodeName = node->name();
- std::string id = getAttribute(node, "id");
+ Glib::ustring nodeName = node->name();
+ Glib::ustring id = getAttribute(node, "id");
//### Get SVG-to-ODF transform
NR::Matrix tf = getODFTransform(item);
NR::Matrix itemTransform = getODFItemTransform(item);
- std::string itemTransformString = formatTransform(itemTransform);
+ Glib::ustring itemTransformString = formatTransform(itemTransform);
- std::string href = getAttribute(node, "xlink:href");
- std::map<std::string, std::string>::iterator iter = imageTable.find(href);
+ Glib::ustring href = getAttribute(node, "xlink:href");
+ std::map<Glib::ustring, Glib::ustring>::iterator iter = imageTable.find(href);
if (iter == imageTable.end())
{
g_warning("image '%s' not in table", href.c_str());
return false;
}
- std::string newName = iter->second;
+ Glib::ustring newName = iter->second;
outs.printf("<draw:frame ");
if (id.size() > 0)
outs.printf("svg:width=\"%.3fcm\" svg:height=\"%.3fcm\" ",
iwidth, iheight);
if (itemTransformString.size() > 0)
+ {
outs.printf("draw:transform=\"%s translate(%.3fcm, %.3fcm)\" ",
- itemTransformString.c_str(), ix, iy);
+ itemTransformString.c_str(), ix, iy);
+ }
+ else
+ {
+ outs.printf("draw:transform=\"translate(%.3fcm, %.3fcm)\" ",
+ ix, iy);
+ }
outs.printf(">\n");
outs.printf(" <draw:image xlink:href=\"%s\" xlink:type=\"simple\"\n",
if (id.size()>0)
outs.printf("id=\"%s\" ", id.c_str());
- std::map<std::string, std::string>::iterator siter;
+ std::map<Glib::ustring, Glib::ustring>::iterator siter;
siter = styleLookupTable.find(id);
if (siter != styleLookupTable.end())
{
- std::string styleName = siter->second;
+ Glib::ustring styleName = siter->second;
outs.printf("draw:style-name=\"%s\" ", styleName.c_str());
}
- std::map<std::string, std::string>::iterator giter;
+ std::map<Glib::ustring, Glib::ustring>::iterator giter;
giter = gradientLookupTable.find(id);
if (giter != gradientLookupTable.end())
{
- std::string gradientName = giter->second;
+ Glib::ustring gradientName = giter->second;
outs.printf("draw:fill-gradient-name=\"%s\" ",
gradientName.c_str());
}
bbox_width * 1000.0, bbox_height * 1000.0);
outs.printf(" svg:d=\"");
- writePath(outs, curve->bpath, tf, bbox_x, bbox_y);
+ int nrPoints = writePath(outs, SP_CURVE_BPATH(curve),
+ tf, bbox_x, bbox_y);
outs.printf("\"");
outs.printf(">\n");
- outs.printf("</draw:path>\n");
+ outs.printf(" <!-- %d nodes -->\n", nrPoints);
+ outs.printf("</draw:path>\n\n");
sp_curve_unref(curve);
-
+/**
+ * Write the content.xml file. Writes the namesspace headers, then
+ * calls writeStyle() and writeTree().
+ */
bool OdfOutput::writeContent(ZipFile &zf, Inkscape::XML::Node *node)
{
BufferOutputStream bouts;
outs.printf("<office:scripts/>\n");
outs.printf("\n");
outs.printf("\n");
- //AffineTransform trans = new AffineTransform();
- //trans.scale(12.0, 12.0);
outs.printf("<!-- ######### CONVERSION FROM SVG STARTS ######## -->\n");
outs.printf("<!--\n");
outs.printf("*************************************************************************\n");
void
OdfOutput::reset()
{
+ metadata.clear();
styleTable.clear();
styleLookupTable.clear();
gradientTable.clear();