Drawable::createFromSVG with text?


#1

Hi,

I am able to create and display a Drawable from my svg via Drawable::createFromSVG.

The Drawable seems to render correctly but without any text the svg contains.

The svg is created with Adobe Illustrator. I also checked to add a text with InkScape with the same result.

The text itself is nothing fancy:

InkScape:

<text

   xml:space="preserve"

   style="font-size:24px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;line-height:125%;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;font-family:Arial;-inkscape-font-specification:Arial"

   x="455.88177"

   y="527.22296"

   id="text3126"

   sodipodi:linespacing="125%"><tspan

     sodipodi:role="line"

     id="tspan3128"

     x="455.88177"

     y="527.22296">Test</tspan></text>

or created dy Illustrator:

        <text

   transform="matrix(1 0 0 1 965.827 94.5257)"

   font-family="'Arial-BoldMT'"

   font-size="24"

   id="text143">Test</text>

I tried leaving out or changing the font-family. But no change.

Any idea whats going wrong here? Is svg text rendering supported at all?

Thanks,

raketa


#2

No, SVG text rendering isn't supported - the spec for text rendering is huge, and I just didn't have time to implement it!


#3

thanks, Jules,

yes, I figured by looking at the code. Maybe I implement a rudimentary text parser. I just need to place labels on my controls.

Speaking of controls: Currently parseSVGElement prevents the creation of a Drawable from a svg subtree by checking with hasTagNameIgnoringNamespace for a "svg" tag.

By using svg subtrees we can streamline the GUI creation by having the Graphic Designer placing all controls in a single svg, which then gets generically parsed by our software so that corresponding controls are created and attached.

Cheers,

raketa


#4

Yes, but technically I don't think that a sub-tree of an SVG is valid SVG, so I think the parser is correct to reject it.. (My memory of the details of SVG is a bit vague though, so correct me if I'm wrong about that!)


#5

I just start to dig into svg myself. You are probably right that not all subtree's are valid svgs itself. (i.e. if referencing "url"s=others parts of the svg), but when taking care it can work. Probably not a general feature though, so rejecting it is probably right.


#6

I see that you already started a

Drawable* parseText (const XmlPath& xml)

and that the use of the Path class is prepared. Did you plan (or would you suggest) to render text as a path? Or the other way around can a path be used on DrawableText objects?

For rudimentary text support I thought of adding text as DrawableText. But I wouldnt see the use of the Path?

 

I guess that

Array <float> xCoords, yCoords, dxCoords, dyCoords;

getCoordList (xCoords, getInheritedAttribute (xml, "x"), true, true);
getCoordList (yCoords, getInheritedAttribute (xml, "y"), true, false);
getCoordList (dxCoords, getInheritedAttribute (xml, "dx"), true, true);
getCoordList (dyCoords, getInheritedAttribute (xml, "dy"), true, false);

retrieves the origins of the relative coords (if there are any) from parent objects?

Thanks,
raketa


#7

OK. I have an implementation for my purpose.

Would you be interested in my implementation and how shoud/can I provide it to you?

 

One thing though: Currently I had to hardcode a font size factor:

It looks like the font size is not correctly translated. Its about 20% smaller than the InkScape fonts. It looks svg gives the size in Point. Maybe there is a translation to pixel needed?

 


#8

Sounds interesting, I'd like to take a look at what you've done if you like.

Perhaps for fonts the method you were looking for is use Font::withPointHeight() ?


#9

OK, after squinting on the svg specs I have to admit following really is only a starting point to display the specific Illustrator output my Graphic Designer is delivering to me (./modules/juce_gui_basics/drawables/juce_SVGParser.cpp):

    Drawable* parseText (const XmlPath& xml)
    {
        Array <float> xCoords, yCoords, dxCoords, dyCoords;
        getCoordList (xCoords, getInheritedAttribute (xml, "x"), true, true);
        getCoordList (yCoords, getInheritedAttribute (xml, "y"), true, false);
        getCoordList (dxCoords, getInheritedAttribute (xml, "dx"), true, true);
        getCoordList (dyCoords, getInheritedAttribute (xml, "dy"), true, false);

        DrawableComposite* const drawable = new DrawableComposite();

        drawable->setComponentID (xml->getStringAttribute ("id"));

        //xxx not done text yet!
        forEachXmlChildElement (*xml, e)
        {
            if (e->isTextElement())
            {
                DrawableText* text = new DrawableText();
                String string = e->getText();
                Font font(
                    xml->getStringAttribute ("font-family", Font::getDefaultSansSerifFontName()).
                        trimCharactersAtStart("'").trimCharactersAtEnd("'"),
                    xml->getStringAttribute ("font-style", Font::getDefaultStyle()).
                        trimCharactersAtStart("'").trimCharactersAtEnd("'"),
                    xml->getDoubleAttribute ("font-size", 12)*1.2);
                text->setFont (font,true);
                float x = xml->getDoubleAttribute ("x",0);
                float y = xml->getDoubleAttribute ("y",0)-font.getAscent();
                if (xml->hasAttribute ("transform"))
                {
                    AffineTransform transform = parseTransform (xml->getStringAttribute ("transform"));
                    text->setTransform (transform);
                    transform.transformPoint (x,y);
                }
                if (xml->hasAttribute ("fill"))
                {
                    text->setColour (Colour (0xff000000|
                        xml->getStringAttribute ("fill").trimCharactersAtStart("#").getHexValue32()));
                }
                text->setBoundingBox (Rectangle<float> (x,y,font.getStringWidth (string),font.getHeight()));
                text->setText (string);
/*
std::clog<<
    e->getText()<<" ("<<x<<", "<<y<<", "<<
    font.getTypefaceName()<<", "<<
    font.getTypefaceStyle()<<", "<<
    font.getHeight()<<", "<<
    std::hex<<text->getColour().toDisplayString(true)<<std::dec<<
    std::endl;
//std::clog<<"x:";for(auto&coord:xCoords)std::clog<<coord<<", ";std::clog<<std::endl;
//std::clog<<"y:";for(auto&coord:yCoords)std::clog<<coord<<", ";std::clog<<std::endl;
//std::clog<<"dx:";for(auto&coord:dxCoords)std::clog<<coord<<", ";std::clog<<std::endl;
//std::clog<<"dy:";for(auto&coord:dyCoords)std::clog<<coord<<", ";std::clog<<std::endl;
*/
                drawable->addAndMakeVisible (text);
            }
            else if (e->hasTagNameIgnoringNamespace ("tspan"))
            {
                drawable->addAndMakeVisible (parseText (xml.getChild (e)));
            }
        }
        return drawable;
    }

One probably should take advantage of the existing parse path functions, but currently this only serves as a proof of concept to create my GUI. With advancing the GUI requirements this could advance as well. Also the parent coords are not used yet. Before putting in more effort I would like to hear if this is principal the right approach.

Cheers,

raketa


#10

Well, it's a start, though I can immediately see lots of issues that would need to be fixed.. I don't have time to commit to it at the moment, but if you keep working on it, keep me posted!