8

I'm working with an SVG element that should look something like this: (sorry that I have to post this as a link instead of as a picture, but as a new user I didn't have permissions to post images)

A border with rounded corners in the middle of a page, but where the the border is removed where header/footer is to be inserted. User-specified text is to be inserted into header, footer and into the frame itself. The rectangle is painted on top of another background (a picture, another rectangle with a color, etc.). I need to find a way to keep the original background, paint only parts of the border and place the text on top of the original background.

I have come up with this solution:

<svg version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="100%" height="100%" viewBox="0 0 620 1100" preserveAspectRatio="xMidYMid meet">
    <defs>
        <!--Define some texts-->
        <text id="text1" x="90" y="100" style="text-anchor:start;font-size:30px;">THIS IS MY HEADER</text>
        <text id="text2" x="525" y="1010" style="text-anchor:end;font-size:30px;">MY TEXT HERE</text>

        <mask id="Mask1">
            <!--draw the entire screen-->
            <rect x="0" y="0" width="620" height="1100" style="fill:white;" />
            <!--Mask out the two rectangles where text is to be placed-->
            <rect x="300" y="980" width="350" height="50" style="fill:black;" />
            <rect x="90" y="90" width="350" height="40" style="fill:black;" />
        </mask>
    </defs>

    <!--The original background (here a rect with a fill color, but could also be an image)-->      
    <rect x="0" y="0" width="620" height="1100" style="fill:#f0ffb6"/>
    <!--Draw the rectangle but applying the mask-->
    <rect x="100" y="100" rx="20" ry="20" width="420" height="900" mask="url(#Mask1)" style="fill:none;stroke:green;stroke-width:5"/>

    <!--Draw the text-->                
    <use xlink:href="#text1" fill="black" stroke="black" stroke-width="1" />
    <use xlink:href="#text2" fill="black" stroke="black" stroke-width="1" />

    <text x="200" y="200">This text goes into the border</text>
</svg>

My problem now is that the last two rects in the mask (rectangles where not to draw the border) must have a static width. If the user changes the text width, the user also needs to calculate a new with of the text and update these two rectangles.

Is there a way to mask out a block the exact same width as the text itself and skip the rectangles in the mask. Or is this the only way of creating such a mask? If anybody "out there" has a better and more intuitive way of achieving this layout, I'd be more than interested in hearing from you.

Thanks for your help.

Iznogood
  • 103
  • 1
  • 1
  • 5
  • Without javascript this is more complicated than it would seem at first sight. Here is a possible workaround http://stackoverflow.com/a/8680391/524555 – methodofaction Sep 04 '12 at 10:40
  • I was afraid that this would be difficult without scripting it. Is there any other way of achieving a similar effect without using masks? I have been playing with the idea of using a textPath with stroke="green" and try to adjust the start of the stroke so that the green line starts after the text. – Iznogood Sep 04 '12 at 11:55

2 Answers2

22

"Removing the background" of svg text without scripting can be done using svg filters.

For example:

<filter x="0" y="0" width="1" height="1" id="removebackground">
   <feFlood flood-color="blue"/>
   <feComposite in="SourceGraphic"/>
</filter>

Which can be used by the text like this:

<use xlink:href="#text1" fill="black" filter="url(#removebackground)"/>

This will adapt to the string width automatically. If you need some padding you can tweak the x,y,width,height attributes on the filter element.

Hmm, thinking of this again, this might not be what you wanted exactly. But it's possible to adapt the above. Here's your file with this solution:

<svg version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="100%" height="100%" viewBox="0 0 620 1100" preserveAspectRatio="xMidYMid meet">
    <defs>
        <!--Define some texts-->
        <text id="text1" x="90" y="100" style="text-anchor:start;font-size:30px;">THIS IS MY HEADER</text>
        <text id="text2" x="525" y="1010" style="text-anchor:end;font-size:30px;">MY TEXT HERE</text>
        <filter x="0" y="0" width="1" height="1" id="removebackground">
            <feFlood flood-color="black"/>
        </filter>

        <mask id="Mask1">
            <!--draw the entire screen-->
            <rect x="0" y="0" width="620" height="1100" style="fill:white;" />
            <!--Mask out the two rectangles where text is to be placed-->
            <use xlink:href="#text1" filter="url(#removebackground)"/>
            <use xlink:href="#text2" filter="url(#removebackground)"/>
        </mask>
    </defs>

    <!--The original background (here a rect with a fill color, but could also be an image)-->      
    <rect x="0" y="0" width="620" height="1100" style="fill:#f0ffb6"/>
    <!--Draw the rectangle but applying the mask-->
    <rect x="100" y="100" rx="20" ry="20" width="420" height="900" mask="url(#Mask1)" style="fill:none;stroke:green;stroke-width:5"/>

    <!--Draw the text-->                
    <use xlink:href="#text1" fill="black" stroke="black" stroke-width="1" />
    <use xlink:href="#text2" fill="black" stroke="black" stroke-width="1" />

    <text x="200" y="200">This text goes into the border</text>
</svg>
Erik Dahlström
  • 59,452
  • 12
  • 120
  • 139
5

I believe a simple way to affect this is to use the user's text as a luminance mask with a big stroke-width set & a black stroke. Next, place the text normally over the masked border.

Jack
  • 2,229
  • 2
  • 23
  • 37
  • I might add that the border mask should be composed of a page-size white rectangle with the super-bold stroke text in black, leaving text-sized gaps in the border without scripting. – Jack Sep 04 '12 at 19:38
  • That's a brilliantly easy way of achieving the effect I wanted. I never thought of that. Although I regard Erik Dahlströms solution as the "proper" way of doing it, yours is most likely the solution I will use since, as it turned out, e-pub renderers like Adobe and iBooks does not function correctly using filters. As always, one will have to let the correct forgo and do some hack that works instead. EDIT: since I don't have enough reputation to vote "This answer is useful", can somebody do it for me? – Iznogood Sep 05 '12 at 02:00
  • 8
    Sounds clever; do you have a concrete example for us? – Alastair Maw Nov 26 '14 at 17:34