void sp_document_set_height (SPDocument * document, gdouble height, const SPUnit *unit) { SPRoot *root = SP_ROOT(document->root); if (root->height.unit == SVGLength::PERCENT && root->viewBox_set) { // set to viewBox= root->viewBox.y1 = root->viewBox.y0 + sp_units_get_pixels (height, *unit); } else { // set to height= gdouble old_computed = root->height.computed; root->height.computed = sp_units_get_pixels (height, *unit); /* SVG does not support meters as a unit, so we must translate meters to * cm when writing */ if (!strcmp(unit->abbr, "m")) { root->height.value = 100*height; root->height.unit = SVGLength::CM; } else { root->height.value = height; root->height.unit = (SVGLength::Unit) sp_unit_get_svg_unit(unit); } if (root->viewBox_set) root->viewBox.y1 = root->viewBox.y0 + (root->height.computed / old_computed) * (root->viewBox.y1 - root->viewBox.y0); } SP_OBJECT (root)->updateRepr(); }
void sp_document_setup_viewport (SPDocument *doc, SPItemCtx *ctx) { ctx->ctx.flags = 0; ctx->i2doc = Geom::identity(); /* Set up viewport in case svg has it defined as percentages */ if (SP_ROOT(doc->root)->viewBox_set) { // if set, take from viewBox ctx->vp.x0 = SP_ROOT(doc->root)->viewBox.x0; ctx->vp.y0 = SP_ROOT(doc->root)->viewBox.y0; ctx->vp.x1 = SP_ROOT(doc->root)->viewBox.x1; ctx->vp.y1 = SP_ROOT(doc->root)->viewBox.y1; } else { // as a last resort, set size to A4 ctx->vp.x0 = 0.0; ctx->vp.y0 = 0.0; ctx->vp.x1 = 210 * PX_PER_MM; ctx->vp.y1 = 297 * PX_PER_MM; } ctx->i2vp = Geom::identity(); }
gdouble sp_document_height(SPDocument *document) { g_return_val_if_fail(document != NULL, 0.0); g_return_val_if_fail(document->priv != NULL, 0.0); g_return_val_if_fail(document->root != NULL, 0.0); SPRoot *root = SP_ROOT(document->root); if (root->height.unit == SVGLength::PERCENT && root->viewBox_set) return root->viewBox.y1 - root->viewBox.y0; return root->height.computed; }
void SPDocument::add_persp3d (Persp3D * const /*persp*/) { SPDefs *defs = SP_ROOT(this->root)->defs; for (SPObject *i = sp_object_first_child(SP_OBJECT(defs)); i != NULL; i = SP_OBJECT_NEXT(i) ) { if (SP_IS_PERSP3D(i)) { g_print ("Encountered a Persp3D in defs\n"); } } g_print ("Adding Persp3D to defs\n"); persp3d_create_xml_element (this); }
/** * Virtual print callback. */ static void sp_root_print(SPItem *item, SPPrintContext *ctx) { SPRoot *root = SP_ROOT(item); sp_print_bind(ctx, root->c2p, 1.0); if (((SPItemClass *) (parent_class))->print) { ((SPItemClass *) (parent_class))->print(item, ctx); } sp_print_release(ctx); }
/** * Calls the <tt>modified</tt> routine of the SPRoot object's parent class. * Also, if the viewport has been modified, it sets the document size to the new * height and width. */ static void sp_root_modified(SPObject *object, guint flags) { SPRoot *root = SP_ROOT(object); if (((SPObjectClass *) (parent_class))->modified) (* ((SPObjectClass *) (parent_class))->modified)(object, flags); /* fixme: (Lauris) */ if (!object->parent && (flags & SP_OBJECT_VIEWPORT_MODIFIED_FLAG)) { root->document->emitResizedSignal(root->width.computed, root->height.computed); } }
/** * Displays the SPRoot item on the drawing. */ static Inkscape::DrawingItem * sp_root_show(SPItem *item, Inkscape::Drawing &drawing, unsigned int key, unsigned int flags) { SPRoot *root = SP_ROOT(item); Inkscape::DrawingItem *ai; if (((SPItemClass *) (parent_class))->show) { ai = ((SPItemClass *) (parent_class))->show(item, drawing, key, flags); if (ai) { Inkscape::DrawingGroup *g = dynamic_cast<Inkscape::DrawingGroup *>(ai); g->setChildTransform(root->c2p); } } else { ai = NULL; } return ai; }
/** * Writes the object into the repr object, then calls the parent's write routine. */ static Inkscape::XML::Node * sp_root_write(SPObject *object, Inkscape::XML::Document *xml_doc, Inkscape::XML::Node *repr, guint flags) { SPRoot *root = SP_ROOT(object); if ((flags & SP_OBJECT_WRITE_BUILD) && !repr) { repr = xml_doc->createElement("svg:svg"); } if (flags & SP_OBJECT_WRITE_EXT) { repr->setAttribute("inkscape:version", Inkscape::version_string); } if ( !repr->attribute("version") ) { gchar* myversion = sp_version_to_string(root->version.svg); repr->setAttribute("version", myversion); g_free(myversion); } if (fabs(root->x.computed) > 1e-9) sp_repr_set_svg_double(repr, "x", root->x.computed); if (fabs(root->y.computed) > 1e-9) sp_repr_set_svg_double(repr, "y", root->y.computed); /* Unlike all other SPObject, here we want to preserve absolute units too (and only here, * according to the recommendation in http://www.w3.org/TR/SVG11/coords.html#Units). */ repr->setAttribute("width", sp_svg_length_write_with_units(root->width).c_str()); repr->setAttribute("height", sp_svg_length_write_with_units(root->height).c_str()); if (root->viewBox_set) { Inkscape::SVGOStringStream os; os << root->viewBox.left() << " " << root->viewBox.top() << " " << root->viewBox.width() << " " << root->viewBox.height(); repr->setAttribute("viewBox", os.str().c_str()); } if (((SPObjectClass *) (parent_class))->write) ((SPObjectClass *) (parent_class))->write(object, xml_doc, repr, flags); return repr; }
SPDocument * sp_document_create(Inkscape::XML::Document *rdoc, gchar const *uri, gchar const *base, gchar const *name, unsigned int keepalive) { SPDocument *document; Inkscape::XML::Node *rroot; Inkscape::Version sodipodi_version; Inkscape::Preferences *prefs = Inkscape::Preferences::get(); rroot = rdoc->root(); document = new SPDocument(); document->keepalive = keepalive; document->rdoc = rdoc; document->rroot = rroot; #ifndef WIN32 prepend_current_dir_if_relative(&(document->uri), uri); #else // FIXME: it may be that prepend_current_dir_if_relative works OK on windows too, test! document->uri = uri? g_strdup(uri) : NULL; #endif // base is simply the part of the path before filename; e.g. when running "inkscape ../file.svg" the base is "../" // which is why we use g_get_current_dir() in calculating the abs path above //This is NULL for a new document if (base) document->base = g_strdup(base); else document->base = NULL; document->name = g_strdup(name); document->root = sp_object_repr_build_tree(document, rroot); sodipodi_version = SP_ROOT(document->root)->version.sodipodi; /* fixme: Not sure about this, but lets assume ::build updates */ rroot->setAttribute("sodipodi:version", SODIPODI_VERSION); rroot->setAttribute("inkscape:version", Inkscape::version_string); /* fixme: Again, I moved these here to allow version determining in ::build (Lauris) */ /* Quick hack 2 - get default image size into document */ if (!rroot->attribute("width")) rroot->setAttribute("width", "100%"); if (!rroot->attribute("height")) rroot->setAttribute("height", "100%"); /* End of quick hack 2 */ /* Quick hack 3 - Set uri attributes */ if (uri) { rroot->setAttribute("sodipodi:docname", uri); } /* End of quick hack 3 */ /* Eliminate obsolete sodipodi:docbase, for privacy reasons */ rroot->setAttribute("sodipodi:docbase", NULL); /* Eliminate any claim to adhere to a profile, as we don't try to */ rroot->setAttribute("baseProfile", NULL); // creating namedview if (!sp_item_group_get_child_by_name((SPGroup *) document->root, NULL, "sodipodi:namedview")) { // if there's none in the document already, Inkscape::XML::Node *rnew = NULL; rnew = rdoc->createElement("sodipodi:namedview"); //rnew->setAttribute("id", "base"); // Add namedview data from the preferences // we can't use getAllEntries because this could produce non-SVG doubles Glib::ustring pagecolor = prefs->getString("/template/base/pagecolor"); if (!pagecolor.empty()) { rnew->setAttribute("pagecolor", pagecolor.data()); } Glib::ustring bordercolor = prefs->getString("/template/base/bordercolor"); if (!bordercolor.empty()) { rnew->setAttribute("bordercolor", bordercolor.data()); } sp_repr_set_svg_double(rnew, "borderopacity", prefs->getDouble("/template/base/borderopacity", 1.0)); sp_repr_set_svg_double(rnew, "objecttolerance", prefs->getDouble("/template/base/objecttolerance", 10.0)); sp_repr_set_svg_double(rnew, "gridtolerance", prefs->getDouble("/template/base/gridtolerance", 10.0)); sp_repr_set_svg_double(rnew, "guidetolerance", prefs->getDouble("/template/base/guidetolerance", 10.0)); sp_repr_set_svg_double(rnew, "inkscape:pageopacity", prefs->getDouble("/template/base/inkscape:pageopacity", 0.0)); sp_repr_set_int(rnew, "inkscape:pageshadow", prefs->getInt("/template/base/inkscape:pageshadow", 2)); sp_repr_set_int(rnew, "inkscape:window-width", prefs->getInt("/template/base/inkscape:window-width", 640)); sp_repr_set_int(rnew, "inkscape:window-height", prefs->getInt("/template/base/inkscape:window-height", 480)); // insert into the document rroot->addChild(rnew, NULL); // clean up Inkscape::GC::release(rnew); } /* Defs */ if (!SP_ROOT(document->root)->defs) { Inkscape::XML::Node *r; r = rdoc->createElement("svg:defs"); rroot->addChild(r, NULL); Inkscape::GC::release(r); g_assert(SP_ROOT(document->root)->defs); } /* Default RDF */ rdf_set_defaults( document ); if (keepalive) { inkscape_ref(); } // Remark: Here, we used to create a "currentpersp3d" element in the document defs. // But this is probably a bad idea since we need to adapt it for every change of selection, which will // completely clutter the undo history. Maybe rather save it to prefs on exit and re-read it on startup? document->current_persp3d = persp3d_document_first_persp(document); if (!document->current_persp3d) { document->current_persp3d = persp3d_create_xml_element (document); } sp_document_set_undo_sensitive(document, true); // reset undo key when selection changes, so that same-key actions on different objects are not coalesced if (!Inkscape::NSApplication::Application::getNewGui()) { g_signal_connect(G_OBJECT(INKSCAPE), "change_selection", G_CALLBACK(sp_document_reset_key), document); g_signal_connect(G_OBJECT(INKSCAPE), "activate_desktop", G_CALLBACK(sp_document_reset_key), document); } else { document->_selection_changed_connection = Inkscape::NSApplication::Editor::connectSelectionChanged (sigc::mem_fun (*document, &SPDocument::reset_key)); document->_desktop_activated_connection = Inkscape::NSApplication::Editor::connectDesktopActivated (sigc::mem_fun (*document, &SPDocument::reset_key)); } return document; }
Geom::Matrix const sp_desktop_root2dt_affine (SPDesktop const *dt) { SPRoot const *root = SP_ROOT(SP_DOCUMENT_ROOT(dt->doc())); return root->c2p * dt->doc2dt(); }
/** * This callback routine updates the SPRoot object when its attributes have been changed. */ static void sp_root_update(SPObject *object, SPCtx *ctx, guint flags) { SPRoot *root = SP_ROOT(object); SPItemCtx *ictx = (SPItemCtx *) ctx; /* fixme: This will be invoked too often (Lauris) */ /* fixme: We should calculate only if parent viewport has changed (Lauris) */ /* If position is specified as percentage, calculate actual values */ if (root->x.unit == SVGLength::PERCENT) { root->x.computed = root->x.value * ictx->viewport.width(); } if (root->y.unit == SVGLength::PERCENT) { root->y.computed = root->y.value * ictx->viewport.height(); } if (root->width.unit == SVGLength::PERCENT) { root->width.computed = root->width.value * ictx->viewport.width(); } if (root->height.unit == SVGLength::PERCENT) { root->height.computed = root->height.value * ictx->viewport.height(); } /* Create copy of item context */ SPItemCtx rctx = *ictx; /* Calculate child to parent transformation */ root->c2p.setIdentity(); if (object->parent) { /* * fixme: I am not sure whether setting x and y does or does not * fixme: translate the content of inner SVG. * fixme: Still applying translation and setting viewport to width and * fixme: height seems natural, as this makes the inner svg element * fixme: self-contained. The spec is vague here. */ root->c2p = Geom::Affine(Geom::Translate(root->x.computed, root->y.computed)); } if (root->viewBox_set) { double x, y, width, height; /* Determine actual viewbox in viewport coordinates */ if (root->aspect_align == SP_ASPECT_NONE) { x = 0.0; y = 0.0; width = root->width.computed; height = root->height.computed; } else { double scalex, scaley, scale; /* Things are getting interesting */ scalex = root->width.computed / root->viewBox.width(); scaley = root->height.computed / root->viewBox.height(); scale = (root->aspect_clip == SP_ASPECT_MEET) ? MIN(scalex, scaley) : MAX(scalex, scaley); width = root->viewBox.width() * scale; height = root->viewBox.height() * scale; /* Now place viewbox to requested position */ /* todo: Use an array lookup to find the 0.0/0.5/1.0 coefficients, as is done for dialogs/align.cpp. */ switch (root->aspect_align) { case SP_ASPECT_XMIN_YMIN: x = 0.0; y = 0.0; break; case SP_ASPECT_XMID_YMIN: x = 0.5 * (root->width.computed - width); y = 0.0; break; case SP_ASPECT_XMAX_YMIN: x = 1.0 * (root->width.computed - width); y = 0.0; break; case SP_ASPECT_XMIN_YMID: x = 0.0; y = 0.5 * (root->height.computed - height); break; case SP_ASPECT_XMID_YMID: x = 0.5 * (root->width.computed - width); y = 0.5 * (root->height.computed - height); break; case SP_ASPECT_XMAX_YMID: x = 1.0 * (root->width.computed - width); y = 0.5 * (root->height.computed - height); break; case SP_ASPECT_XMIN_YMAX: x = 0.0; y = 1.0 * (root->height.computed - height); break; case SP_ASPECT_XMID_YMAX: x = 0.5 * (root->width.computed - width); y = 1.0 * (root->height.computed - height); break; case SP_ASPECT_XMAX_YMAX: x = 1.0 * (root->width.computed - width); y = 1.0 * (root->height.computed - height); break; default: x = 0.0; y = 0.0; break; } } /* Compose additional transformation from scale and position */ Geom::Scale const viewBox_length( root->viewBox.dimensions() ); Geom::Scale const new_length(width, height); /* Append viewbox transformation */ /* TODO: The below looks suspicious to me (pjrm): I wonder whether the RHS expression should have c2p at the beginning rather than at the end. Test it. */ root->c2p = Geom::Translate(-root->viewBox.min()) * ( new_length * viewBox_length.inverse() ) * Geom::Translate(x, y) * root->c2p; } rctx.i2doc = root->c2p * rctx.i2doc; /* Initialize child viewport */ if (root->viewBox_set) { rctx.viewport = root->viewBox; } else { /* fixme: I wonder whether this logic is correct (Lauris) */ Geom::Point minp(0,0); if (object->parent) { minp = Geom::Point(root->x.computed, root->y.computed); } rctx.viewport = Geom::Rect::from_xywh(minp[Geom::X], minp[Geom::Y], root->width.computed, root->height.computed); } rctx.i2vp = Geom::identity(); /* And invoke parent method */ if (((SPObjectClass *) (parent_class))->update) ((SPObjectClass *) (parent_class))->update(object, (SPCtx *) &rctx, flags); /* As last step set additional transform of drawing group */ for (SPItemView *v = root->display; v != NULL; v = v->next) { Inkscape::DrawingGroup *g = dynamic_cast<Inkscape::DrawingGroup *>(v->arenaitem); g->setChildTransform(root->c2p); } }
/** * Sets the attribute given by key for SPRoot objects to the value specified by value. */ static void sp_root_set(SPObject *object, unsigned int key, gchar const *value) { SPRoot *root = SP_ROOT(object); switch (key) { case SP_ATTR_VERSION: if (!sp_version_from_string(value, &root->version.svg)) { root->version.svg = root->original.svg; } break; case SP_ATTR_INKSCAPE_VERSION: if (!sp_version_from_string(value, &root->version.inkscape)) { root->version.inkscape = root->original.inkscape; } break; case SP_ATTR_X: if (!root->x.readAbsolute(value)) { /* fixme: em, ex, % are probably valid, but require special treatment (Lauris) */ root->x.unset(); } /* fixme: I am almost sure these do not require viewport flag (Lauris) */ object->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG | SP_OBJECT_VIEWPORT_MODIFIED_FLAG); break; case SP_ATTR_Y: if (!root->y.readAbsolute(value)) { /* fixme: em, ex, % are probably valid, but require special treatment (Lauris) */ root->y.unset(); } /* fixme: I am almost sure these do not require viewport flag (Lauris) */ object->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG | SP_OBJECT_VIEWPORT_MODIFIED_FLAG); break; case SP_ATTR_WIDTH: if (!root->width.readAbsolute(value) || !(root->width.computed > 0.0)) { /* fixme: em, ex, % are probably valid, but require special treatment (Lauris) */ root->width.unset(SVGLength::PERCENT, 1.0, 1.0); } object->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG | SP_OBJECT_VIEWPORT_MODIFIED_FLAG); break; case SP_ATTR_HEIGHT: if (!root->height.readAbsolute(value) || !(root->height.computed > 0.0)) { /* fixme: em, ex, % are probably valid, but require special treatment (Lauris) */ root->height.unset(SVGLength::PERCENT, 1.0, 1.0); } object->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG | SP_OBJECT_VIEWPORT_MODIFIED_FLAG); break; case SP_ATTR_VIEWBOX: if (value) { double x, y, width, height; char *eptr; /* fixme: We have to take original item affine into account */ /* fixme: Think (Lauris) */ eptr = (gchar *) value; x = g_ascii_strtod(eptr, &eptr); while (*eptr && ((*eptr == ',') || (*eptr == ' '))) eptr++; y = g_ascii_strtod(eptr, &eptr); while (*eptr && ((*eptr == ',') || (*eptr == ' '))) eptr++; width = g_ascii_strtod(eptr, &eptr); while (*eptr && ((*eptr == ',') || (*eptr == ' '))) eptr++; height = g_ascii_strtod(eptr, &eptr); while (*eptr && ((*eptr == ',') || (*eptr == ' '))) eptr++; if ((width > 0) && (height > 0)) { /* Set viewbox */ root->viewBox = Geom::Rect::from_xywh(x, y, width, height); root->viewBox_set = TRUE; } else { root->viewBox_set = FALSE; } } else { root->viewBox_set = FALSE; } object->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG | SP_OBJECT_VIEWPORT_MODIFIED_FLAG); break; case SP_ATTR_PRESERVEASPECTRATIO: /* Do setup before, so we can use break to escape */ root->aspect_set = FALSE; root->aspect_align = SP_ASPECT_XMID_YMID; root->aspect_clip = SP_ASPECT_MEET; object->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG | SP_OBJECT_VIEWPORT_MODIFIED_FLAG); if (value) { int len; gchar c[256]; gchar const *p, *e; unsigned int align, clip; p = value; while (*p && *p == 32) p += 1; if (!*p) break; e = p; while (*e && *e != 32) e += 1; len = e - p; if (len > 8) break; memcpy(c, value, len); c[len] = 0; /* Now the actual part */ if (!strcmp(c, "none")) { align = SP_ASPECT_NONE; } else if (!strcmp(c, "xMinYMin")) { align = SP_ASPECT_XMIN_YMIN; } else if (!strcmp(c, "xMidYMin")) { align = SP_ASPECT_XMID_YMIN; } else if (!strcmp(c, "xMaxYMin")) { align = SP_ASPECT_XMAX_YMIN; } else if (!strcmp(c, "xMinYMid")) { align = SP_ASPECT_XMIN_YMID; } else if (!strcmp(c, "xMidYMid")) { align = SP_ASPECT_XMID_YMID; } else if (!strcmp(c, "xMaxYMid")) { align = SP_ASPECT_XMAX_YMID; } else if (!strcmp(c, "xMinYMax")) { align = SP_ASPECT_XMIN_YMAX; } else if (!strcmp(c, "xMidYMax")) { align = SP_ASPECT_XMID_YMAX; } else if (!strcmp(c, "xMaxYMax")) { align = SP_ASPECT_XMAX_YMAX; } else { break; } clip = SP_ASPECT_MEET; while (*e && *e == 32) e += 1; if (*e) { if (!strcmp(e, "meet")) { clip = SP_ASPECT_MEET; } else if (!strcmp(e, "slice")) { clip = SP_ASPECT_SLICE; } else { break; } } root->aspect_set = TRUE; root->aspect_align = align; root->aspect_clip = clip; } break; case SP_ATTR_ONLOAD: root->onload = (char *) value; break; default: /* Pass the set event to the parent */ if (((SPObjectClass *) parent_class)->set) { ((SPObjectClass *) parent_class)->set(object, key, value); } break; } }