SVG -> TCPDF gradient mismatch - pdf

I have the following SVG:
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" version="1.1" width="800" height="150" viewBox="0 0 800 150" xml:space="preserve">
<linearGradient id="SVGID_0" gradientUnits="userSpaceOnUse" x1="-400" y1="-150" x2="-400" y2="0">
<stop offset="0%" style="stop-color:rgb(255,64,64);stop-opacity: 1"/>
<stop offset="100%" style="stop-color:rgb(230,57,155);stop-opacity: 1"/>
</linearGradient>
<rect x="-400" y="-75" rx="0" ry="0" width="800" height="150" style="stroke: none; stroke-width: 1; stroke-dasharray: none; stroke-linecap: butt; stroke-linejoin: miter; stroke-miterlimit: 10; fill: url(#SVGID_0); fill-rule: nonzero; opacity: 1;" transform="translate(400.5 75.5)"/>
</svg>
I am converting this to PDF using TCPDF:
$pdf->ImageSVG($file='images/testsvg.svg', $x=0, $y=0, $w='', $h='', $align='', $palign='', $border=0, $fitonpage=true);
As you can see from the image below, the gradient is applied wrongly.
Based on the illustrator image, it seems like TCPDF applies the center of the gradient to the bottom edge. If I manually move the center to the top edge then it looks very close to the original.
Any idea how I can fix this?

I think it's because of how the rect is positioned. It actually starts outside the viewbox and then both the gradient and the box get transformed. For example the box and the gradient x parameter are both -400. This is overly complex and I think some of the parameters are getting cancelled out by the $fitonpage=true parameter or the translations are being applied differently:
<rect x="-400" y="-75" rx="0" ry="0" width="800" height="150" style="... transform="translate(400.5 75.5)"/>
Based on the supplied examples there isn't really any point to this trickery. The only purpose appears to be modifying the gradient start and end colors. I would just move the rect to start at 0,0, remove the transforms, and then modify the gradient colors and stops to achieve the same effect the correct way:
<linearGradient y2="150" x2="0" y1="0" x1="0" gradientUnits="userSpaceOnUse" id="SVGID_0">
<stop
style="stop-color:#f13c73;stop-opacity:1"
id="stop4139"
offset="0" />
<stop
style="stop-color:#e6399b;stop-opacity:1"
id="stop4141"
offset="0.40" />
</linearGradient>
<rect
id="rect4143"
style="opacity:1;fill:url(#SVGID_0);fill-rule:nonzero;stroke:none;stroke-width:1;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:10;stroke-dasharray:none"
height="150"
width="800"
ry="0"
rx="0"
y="0"
x="0" />
The changes that matter are the removal of all the strange negative offsets on the rect and gradient and the change of the gradient stop colors and position to recreate the same gradient without the need to use cropping and transforms.

Related

react-native-svg doesn't detect transparent color

I am facing an annoying issue using react-native-svg to implement a radial gradient that from grey has a transparent color.
For some reason I can still see a grey-ish tint on the center of the gradient, that is supposed to be transparent, in order to see the image below without any color on top.
This is the code I am using:
<Svg
height="700"
width="1000"
style={{ position: "absolute", left: -400, top: -350 }}
>
<Defs>
<RadialGradient
id="cardCircleGradient"
cx="600"
cy="600"
rx="600"
ry="600"
fx="600"
fy="600"
gradientUnits="userSpaceOnUse"
>
<Stop offset="0" stopColor="#00000000" stopOpacity="0" /> <-HERE is where I cannot implement a transparent color
<Stop
offset="1"
stopColor="rgba(14, 14, 14, 0.45)"
stopOpacity="1"
/>
</RadialGradient>
</Defs>
<Circle cx="600" cy="600" r="600" fill="url(#cardCircleGradient)" />
</Svg>
Did someone experience a similar issue?

React Native SVG Mask child not transforming

I am drawing a couple circles ontop of a zoomable image. I want the circles to be hole views so the holes highlight the image behind it. So I am using a mask to draw the rect to darken the image and then circles to highlight parts of the image. I group together the mask and the rectangle and do the transformation on the mask. When the image is not zoomed in, everything is perfect. When I zoom in and pan the image, the circles stay where they are supposed to, but the background rect does not translate from the transformations.
<Svg
height={imageDetails.HEIGHT}
width={imageDetails.WIDTH}
pointerEvents='none'>
<G transform={{
translateX: delta.x,
translateY: delta.y,
scale: delta.zoom
}} >
<Defs>
<Mask id={`clip`} >
<Rect fill="#fff" strokeWidth='5' stroke='white'
originX={delta.x * delta.zoom}
width={imageDetails.WIDTH * delta.zoom} height={imageDetails.HEIGHT} />
{data.map(item =>
<Circle r={item.size} cx={item.x} cy={item.y} key={item.key} fill='#000' />
)}
</Mask>
</Defs>
<Rect width={imageDetails.WIDTH} height={imageDetails.HEIGHT} clipRule={'evenodd'}
fillRule={'nonzero'} stroke='blue' strokeWidth='2' vectorEffect='inherit'
fill="rgba(0,0,0,.5)" fillOpacity='.7' mask={`url(#clip)`} />
</G>
</Svg>
I have played around with trying to transform the mask and the rect inside, but nothing.
Here are my screen shots show the full image, then zooming in, and the zooming in even more.
So my knowledge of SVG is limited, but I am learning. What I believe was happening was that the transformation was being applied to the everything, but the masked rect width and height are set to a value. Upon doing a scale, the width and height stay the exact same, so that is why it is being cut off. I had to change it so the transformation was being applied to the mask circles. So the mask rect will fill the whole screen always, and the circles will receive the transformation. Hopefully this helps anyone facing this issue.
<Svg
height={imageDetails.HEIGHT}
width={imageDetails.WIDTH}
pointerEvents='none'>
<G >
<Defs>
<Mask id={`clip`} >
<Rect fill="#fff" width='100%' height='100%' />
{data.map(item =>
<Circle
transform={{
translateX: delta.x,
translateY: delta.y,
scale: delta.zoom
}}
r={item.size} cx={item.x} cy={item.y} key={item.key} fill='#000' />
)}
</Mask>
</Defs>
<Rect width='100%' height='100%'
fill="rgba(0,0,0,.5)" fillOpacity='.7' mask={`url(#clip)`} />
</Svg>

React native image shadow over clipPath

I'm trying to add a shadow to an image clipped by a clippath over SVG, how can it be done in React native?
if my original SVG is shadowed then the image covers it.
My current code:
<svg>
<Defs>
<ClipPath id='clip'>
<Path
d='M113.093,63.183c9.5-23.17,42.313-23.17,51.814,0l97.257,237.195A28,28,0,0,1,236.257,339H41.743a28,28,0,0,1-25.907-38.622Z'
transform='translate(340.5 -12.21) rotate(90)'
scale={scale}
/>
</ClipPath>
</Defs>
<Image
href={{
uri: uri,
}}
clipPath='url(#clip)'
width='100%'
height='100%'
preserveAspectRatio='xMidYMax slice'
/>
</Svg>
Thanks,
Erez
If you apply a shadow to the image and then you clip the image, you also clip the shadow off. In the next example I'm using the path and applying the shadow to the path. Next I'm drawing the image and clip the image.
svg{width:300px;}
<svg viewBox="150 -20 180 160" width="200">
<defs>
<filter id="f">
<feGaussianBlur in="SourceAlpha" stdDeviation="5" result="desenfoque"></feGaussianBlur>
<feOffset in="desenfoque" dx="3" dy="3" result="sombra"></feOffset>
<feMerge>
<feMergeNode in="sombra"></feMergeNode>
<feMergeNode in="SourceGraphic"></feMergeNode>
</feMerge>
</filter>
<clipPath id='clip'>
<path id="thePath" d='M113.093,63.183c9.5-23.17,42.313-23.17,51.814,0l97.257,237.195A28,28,0,0,1,236.257,339H41.743a28,28,0,0,1-25.907-38.622Z' transform='translate(340.5 -12.21) rotate(90) scale(.5)'/>
</clipPath>
</defs>
<use xlink:href="#thePath" filter="url(#f)" id="use" />
<image x="150" y="-20" xlink:href="https://assets.codepen.io/222579/castell.jpg" clip-path='url(#clip)' width='100%' height='100%' preserveAspectRatio='xMidYMax slice' />
</svg>
UPDATE
The OP is commenting:
I'm using react native with 'react-native-svg' library. this code doesn't seem to work in these circumstances. lacking support of 'feGaussianBlur' and other components
In this case if you have only this shape you can use a css filter to apply a shadow to the svg element:
svg{filter:drop-shadow(2px 2px 5px #000);}
<svg viewBox="150 -20 180 160" width="200">
<defs>
<clipPath id='clip'>
<path id="thePath" d='M113.093,63.183c9.5-23.17,42.313-23.17,51.814,0l97.257,237.195A28,28,0,0,1,236.257,339H41.743a28,28,0,0,1-25.907-38.622Z' transform='translate(340.5 -12.21) rotate(90) scale(.5)'/>
</clipPath>
</defs>
<image x="150" y="-20" xlink:href="https://assets.codepen.io/222579/castell.jpg" clip-path='url(#clip)' width='100%' height='100%' preserveAspectRatio='xMidYMax slice' />
</svg>

How to center element inside SVG polygon

I have SVG that is assembled from multiple polygons.
I am trying to put image/button inside polygon center but what ever I try it always put image in x=0 and y=0 of the screen.
<Svg width="546px" height="794px" viewBox="0 0 546 794" version="1.1" >
<G id="Page-1" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
<G id="item0" transform="translate(1.000000, 1.000000)" fill="#EDAF51" fill-rule="nonzero" stroke="#720D00">
<Polygon id="RX-03" points="206.65269...">
</Polygon>
<Circle x="0" y="0" cx="40" cy="40" r="15" fill="black" />
</G>
With this I get:
But if I put <Circle x="110" y="0" I get
And this is correct but I don't want to use x=110 I am trying to make this circle to be relative to it's parent polygon.
So I can set circle to x=0 y=0 and to keep it inside area of parent polygon.
New answer on the comment of the author of the question
In svg, with mutual positioning between elements, there is only absolute positioning.
Relative positioning in svg, as you want - there is no circle relative to the parent polygon.
Only absolute positioning of a circle will help to place it in the right place
You can create a circle once and clone it several times while positioning:
<use xlink:href="#crc1" x="100" y="150" />
<div class="container">
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="546px" height="794px" viewBox="0 0 546 794" >
<defs>
<circle id="crc1" cx="0" cy="0" r="15" stroke="red" />
</defs>
<image transform="translate(0, -300)" xlink:href="https://i.stack.imgur.com/q0PXl.png"
width="100%" height="100%"
/>
<use xlink:href="#crc1" x="100" y="150" />
<use xlink:href="#crc1" x="210" y="110" />
<use xlink:href="#crc1" x="300" y="190" />
<use xlink:href="#crc1" x="385" y="190" />
<use xlink:href="#crc1" x="500" y="190" />
</svg>
</div>
An image can be inserted into any SVG shape in several ways:
Using clipPath
Using mask
Using pattern
With any method of inserting an image, you need to focus on the shape of the template.
If the template has a symmetrical shape, it is necessary to select the original image with the same aspect ratio.
In other words, if the cropping pattern is a circle or regular polygons, then you need to select images with the same width and height.
I translated the React syntax into the regular SVG syntax. If necessary, you can go back
Selected round image badge
Insert this image into the hexagon`
1. Using clipPath
The hexagon acts as a cropping pattern.
<style>
.container {
width:50%;
height:50%;
}
</style>
<div class="container">
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" viewBox="0 0 546 794" >
<defs>
<clipPath id="clip">
<path fill="none" stroke="none" stroke-width="2" d="m275.9 190.9 175.6 101.4 0 202.7-175.6 101.4-175.6-101.4 0-202.7z" />
</clipPath>
</defs>
<image xlink:href="https://i.stack.imgur.com/gOrJU.png"
x="0"y="0"
width="100%" height="100%"
clip-path="url(#clip)" />
</svg>
</div>
2. Using mask
.container {
width:50%;
height:50%;
}
image {
mask:url(#msk1);
}
<div class="container">
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" viewBox="0 0 546 794" >
<defs>
<mask id="msk1">
<path fill="white" stroke="red" stroke-width="12" d="m275.9 190.9 175.6 101.4 0 202.7-175.6 101.4-175.6-101.4 0-202.7z" />
</mask>
</defs>
<image xlink:href="https://i.stack.imgur.com/gOrJU.png" x="0" y="0" width="100%" height="100%" />
</svg>
</div>
3. Using pattern
.container {
width:50%;
height:50%;
}
path {
fill:url(#ptn1);
stroke:#DBC176;
stroke-width:8;
}
<div class="container">
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" viewBox="0 0 546 794" >
<defs>
<pattern id="ptn1" width="1" height="1">
<image xlink:href="https://i.stack.imgur.com/gOrJU.png" x="-24" y="3" width="400px" height="400px" />
</pattern>
</defs>
<path d="m275.9 190.9 175.6 101.4 0 202.7-175.6 101.4-175.6-101.4 0-202.7z" />
</svg>
</div>

Does pdf (or svg) support linear interpolation of colors inside a vector graphics triangle?

We would like to blend colors specified at triangle vertices using linear interpolation (e.g. like OpenGL) in a vector graphics file like a pdf.
This example blends red, blue and green:
Is this possible in PDF? If not, then SVG or some other well-supported vector graphics file-format?
It seems gradient meshes are powerful, but can they be degenerated to exactly reproduce linear interpolation?
The pure answer to your question is "no". You can't do three-point gradients in SVG or PDF.
However you can reproduce that image easily enough with a couple of gradients and a mask.
<svg xmlns="http://www.w3.org/2000/svg" version="1.1" width="100%" height="100%" viewBox="0 0 100 86.6">
<defs>
<linearGradient id="bluegreen" gradientUnits="objectBoundingBox" x1="0.5" x2="1" y2="1">
<stop offset="0%" stop-color="#0000ff"/>
<stop offset="100%" stop-color="#00ff00"/>
</linearGradient>
<linearGradient id="fader" gradientUnits="objectBoundingBox" x1="0" y1="1" x2="0.75" y2="0.5">
<stop offset="0%" stop-color="white" />
<stop offset="100%" stop-color="black" />
</linearGradient>
<mask id="redmask" maskUnits="objectBoundingBox" maskContentUnits="objectBoundingBox">
<path d="M 0.5,0 L 1,1 0,1 Z" fill="url(#fader)" />
</mask>
</defs>
<g>
<path d="M 50,0 L 100,86.6 0,86.6 Z" fill="url(#bluegreen)"/>
<path d="M 50,0 L 100,86.6 0,86.6 Z" fill="#ff0000" mask="url(#redmask)"/>
</g>
</svg>
http://jsfiddle.net/P48FD/
Update: Actually, what was I thinking? :/ You don't need a mask. You can do it just with two linear gradients.
<svg xmlns="http://www.w3.org/2000/svg" version="1.1" width="100%" height="100%" viewBox="0 0 100 86.6">
<defs>
<linearGradient id="bluegreen" gradientUnits="objectBoundingBox" x1="0.5" y1="0" x2="1" y2="1">
<stop offset="0%" stop-color="#0000ff"/>
<stop offset="100%" stop-color="#00ff00"/>
</linearGradient>
<linearGradient id="redfade" gradientUnits="objectBoundingBox" x1="0" y1="1" x2="0.75" y2="0.5">
<stop offset="0%" stop-color="#ff0000" />
<stop offset="100%" stop-color="#ff0000" stop-opacity="0" />
</linearGradient>
</defs>
<g>
<path d="M 50,0 L 100,86.6 0,86.6 Z" fill="url(#bluegreen)"/>
<path d="M 50,0 L 100,86.6 0,86.6 Z" fill="url(#redfade)"/>
</g>
</svg>
http://jsfiddle.net/Q3qjB/