lunasvg
lunasvg copied to clipboard
accessing the internal objects
Hi @sammycage
This point probably requires a deep discussion, below I try to explain my needs. It would be useful to identify an object inside SVG code in order to change some properties after the SVG file has been loaded in memory.
Perhaps the attribute ID ( https://www.w3.org/TR/SVG2/struct.html#IDAttribute ) is the right one to identify objects.
Mainly the purpose is for being able to change colours, lines width, visibility, etc … It would be very useful to get the position and size occupied by every addressable SVG objects
LunaSVG library should keep a list of SVG objects and expose it in some way The user should inform the library about the object ID and the attribute to change with the new value.
Regads Rossano
We are looking for SVG rendering library for our icons and we also need modify some object properties such as color, opacity, or stroke width. It is possible with NanoSVG.
@jry2 Are you still searching?
Yes. We also need to hide group with specified id.
@jry2 I'll let you know when it's ready.
Maybe classes as well? It will help mass manipulation based on the class
@rossanoparis @kariem2k @jry2 Added with this commit https://github.com/sammycage/lunasvg/commit/760e7e86519ced749e359d472a3a04620aa5cf00
Example:
auto document = Document::loadFromData("<svg viewBox='0 0 200 200'><circle id='a' cx='50' cy='50' r='40' fill='green'/></svg>");
auto element = document->getElementById("a");
element.setAttribute("cx", "100");
element.setAttribute("cy", "100");
element.setAttribute("r", "100");
element.setAttribute("fill", "red");
document->updateLayout();
@rossanoparis Reminder: Don't forget to call Document::updateLayout
after modifying the document.
@sammycage it sounds great! Thank you for starting this kind of implementation
I couldn't wait to test it ... what about gradients
Is there a chance to operate with gradients handled by using urls instead of constant colours
@rossanoparis Hello! You have the flexibility to modify any attribute using the versatile setAttribute
method. Take a look at the code snippet below for a practical example. This snippet demonstrates how to transition the fill of a circle seamlessly, progressing from a solid color to a gradient and finally to a pattern:
std::string content = R"SVG(
<svg width="200" height="200">
<defs>
<!-- Gradient -->
<linearGradient id="myGradient" x1="0%" y1="0%" x2="100%" y2="100%">
<stop offset="0%" style="stop-color: #ff0000;"/>
<stop offset="100%" style="stop-color: #0000ff;"/>
</linearGradient>
<!-- Pattern -->
<pattern id="myPattern" x="0" y="0" width="20" height="20" patternUnits="userSpaceOnUse">
<circle cx="10" cy="10" r="8" fill="#00ff00"/>
</pattern>
</defs>
<circle id="myCircle" cx="100" cy="100" r="50" fill="red"/>
</svg>)SVG";
auto document = Document::loadFromData(content);
writeToPng(*document, "circle-with-solid-color.png");
auto circle = document->getElementById("myCircle");
circle.setAttribute("fill", "url(#myGradient)");
document->updateLayout();
writeToPng(*document, "circle-with-gradient.png");
circle.setAttribute("fill", "url(#myPattern)");
document->updateLayout();
writeToPng(*document, "circle-with-pattern.png");
Yes, It's clear, but my question was different. What I meant was related to gradient itself, how can I modify gradient or pattern attributes
@rossanoparis Yes, I understand now. To modify the attributes of a gradient or a pattern, you can use a similar approach with the setAttribute
method. For example:
// To modify gradient attributes
auto gradient = document->getElementById("yourGradientId");
gradient.setAttribute("x1", "newX1Value");
gradient.setAttribute("y1", "newY1Value");
// Add more attributes as needed
// To modify pattern attributes
auto pattern = document->getElementById("yourPatternId");
pattern.setAttribute("x", "newXValue");
pattern.setAttribute("y", "newYValue");
// Add more attributes as needed
Feel free to adapt this code based on the specific attributes you want to modify. Let me know if you have any further questions or if there's anything else I can assist you with. But it seems like you've got the method now!
Thank you @sammycage, it is clear
Regarding attributes I'd like to introduce two new requests/discussions.
1) Groups or single object "transformations" With "transformation" I mainly mean:
- rotation
- scale
- move
- vertical or horizontal flip
It would be very useful, using the same approach as attributes, to apply transformations to all objects contained in a group or even to a single object.
I imagine something like:
auto group1 = document->getElementById("Group-01");
group1.Rotate(angle, center-x, center-y);
group1.Move(new-x, new-y);
group1.Scale(scale, center-x, center-y);
group1.FlipVertical(reference-x, reference-y);
group1.FlipHorizontal(reference-x, reference-y);
auto triangle = document->getElementById("OBJ2");
triangle.Rotate(angle, center-x, center-y);
triangle.FlipVertical(reference-x, reference-y);
triangle.Move(new-x, new-y);
2) Groups or single object "references" With "reference" I mainly mean methodd based on which a user can get information from objects regarding the area occupied by a group or single objects; even for texts which is not drawn by the library, but that could be drawn in other way by the user.
It would be very useful, using the same approach as attributes, to get such information.
I imagine something like:
auto group1 = document->getElementById("Group-01");
group1.getP1();
group1.getP2();
group1.getP3();
group1.getP4();
auto triangle = document->getElementById("OBJ2");
triangle.getP1();
triangle.getP2();
auto text = document->getElementById("MyText1");
text.getP1();
text.getP2();
text.getP3();
text.getP4();
Transformations and references features would be very powerful to "animate" a SVG document ... what do you think about them.
@rossanoparis I appreciate your detailed suggestions and the illustrative diagrams you provided. I'm confident that incorporating the DomElement::getBBox
and DomElement::getLocalTransform
methods will effectively address the concerns you raised regarding "transformations" and "references. https://github.com/sammycage/lunasvg/commit/6947ee1a3c8a189f9bfb39107e101d0babf8e008
This code snippet illustrates the usage of DomElement::getBBox
to reposition an SVG group to a new reference position.
std::string content = R"SVG(
<svg width="200" height="200">
<g id="myGroup">
<rect width="50" height="50" fill="blue"/>
</g>
</svg>)SVG";
auto document = Document::loadFromData(content);
writeToPng(*document, "original.png");
// Get the SVG element and its bounding box
auto element = document->getElementById("myGroup");
auto bbox = element.getBBox();
// Calculate the new position (e.g., move 150 units to the right and 30 units down)
auto newX = 150;
auto newY = 30;
// Calculate the translation values
auto deltaX = newX - bbox.x;
auto deltaY = newY - bbox.y;
// Apply the transform attribute to move the SVG group
element.setAttribute("transform", "translate(" + std::to_string(deltaX) + " " + std::to_string(deltaY) + ")");
document->updateLayout();
writeToPng(*document, "translated.png");
Kind @sammycage thank you for aving accepted my proposal and ... wow! how fast you are :)
I don't have time in this week to test the latest commits. Just for my information, did you already implemented the two points above described ?
Again thank you, regards
@rossanoparis You can adjust SVG transformations with the following code. Feel free to ask any further questions!
void writeToPng(Document& document, const char* filename)
{
auto bitmap = document.renderToBitmap();
stbi_write_png(filename, bitmap.width(), bitmap.height(), 4, bitmap.data(), 0);
std::cout << "Generated PNG file : " << filename << std::endl;
}
void setTransformAttribute(DomElement& element, const Matrix& matrix)
{
std::string transform("matrix(");
transform += std::to_string(matrix.a);
transform += ' ';
transform += std::to_string(matrix.b);
transform += ' ';
transform += std::to_string(matrix.c);
transform += ' ';
transform += std::to_string(matrix.d);
transform += ' ';
transform += std::to_string(matrix.e);
transform += ' ';
transform += std::to_string(matrix.f);
transform += ')';
element.setAttribute("transform", transform);
}
int main(int argc, char** argv)
{
std::string content = R"SVG(
<svg width="200" height="200">
<g id="myGroup">
<rect width="50" height="50" fill="blue"/>
</g>
</svg>)SVG";
auto document = Document::loadFromData(content);
writeToPng(*document, "original.png");
// Retrieve the SVG element and its transformation matrix.
auto element = document->getElementById("myGroup");
auto transform = element.getLocalTransform();
// Modify the transformation matrix.
transform.translate(50, 50);
transform.rotate(45);
// Apply the modified transformation to the SVG element.
setTransformAttribute(element, transform);
document->updateLayout();
writeToPng(*document, "transformed.png");
return 0;
}
Thank you @sammycage
There are some points I don't understand their behaviour, perhaps because of my lack of knowledge ...
I'm debugging this feature, and I'm going to open different issues for each test I'm doing related to: scale, rotate, translate In this way, treating one by one it results in tidy resources for all, I think
...