0

I would like to know how to render SVG paths and div elements in one image.

Example:

<div class="objects ui-droppable ui-sortable">
        <div class="b_s _jsPlumb_endpoint_anchor _jsPlumb_connected" id="start_block">START</div> 

        <div class="b_1 block ui-draggable ui-draggable-handle _jsPlumb_endpoint_anchor _jsPlumb_connected" id="n_block4" title="test"><span>test</span></div>

        <div class="b_s _jsPlumb_endpoint_anchor _jsPlumb_connected" id="end_block">STOP</div><div class="_jsPlumb_endpoint _jsPlumb_endpoint_anchor jsplumb-draggable jsplumb-droppable" style="display: block; width: 10px; height: 0px; position: absolute; left: 530px; top: 52px; background: transparent;"></div>

        <div class="_jsPlumb_endpoint _jsPlumb_endpoint_anchor jsplumb-draggable jsplumb-droppable" style="display: block; width: 10px; height: 0px; position: absolute; left: 529.5px; top: 82px; background: transparent;"></div>

        <svg style="position:absolute;left:519.5px;top:48px" width="20" height="38" pointer-events="none" position="absolute" version="1.1" xmlns="http://www.w3.org/1999/xhtml" class="_jsPlumb_connector"><path d="M 0.5 -1 L 0.5 31 M 1.5 30 L -0.75 30 M 0.25 31 L 0.25 -1 M 1.25 0 L -1 0 M 0 0 L 0 30 " transform="translate(9.999999999999998,4)" pointer-events="visibleStroke" version="1.1" xmlns="http://www.w3.org/1999/xhtml" fill="none" stroke="rgb(7,152,216)" style="" stroke-width="2"></path><path pointer-events="all" version="1.1" xmlns="http://www.w3.org/1999/xhtml" d="M4.684274006738627e-16,32.650000000000006 L10.000000000000002,7.650000000000007 L1.4221210955098638e-15,17.075000000000006 L-9.999999999999998,7.650000000000005 L4.684274006738627e-16,32.650000000000006" class="" stroke="rgb(7,152,216)" fill="rgb(7,152,216)" transform="translate(9.999999999999998,4)"></path></svg>

        <div class="_jsPlumb_endpoint _jsPlumb_endpoint_anchor jsplumb-draggable jsplumb-droppable" style="display: block; width: 10px; height: 0px; position: absolute; left: 529.5px; top: 126px; background: transparent;"></div>

        <div class="_jsPlumb_endpoint _jsPlumb_endpoint_anchor jsplumb-draggable jsplumb-droppable" style="display: block; width: 10px; height: 0px; position: absolute; left: 530px; top: 156px; background: transparent;"></div>

        <svg style="position:absolute;left:520px;top:122px" width="20" height="38" pointer-events="none" position="absolute" version="1.1" xmlns="http://www.w3.org/1999/xhtml" class="_jsPlumb_connector"><path d="M 0 -1 L 0 31 M -1 30 L 1.25 30 M 0.25 31 L 0.25 -1 M -0.75 0 L 1.5 0 M 0.5 0 L 0.5 30 " transform="translate(9.499999999999998,4)" pointer-events="visibleStroke" version="1.1" xmlns="http://www.w3.org/1999/xhtml" fill="none" stroke="rgb(7,152,216)" style="" stroke-width="2"></path><path pointer-events="all" version="1.1" xmlns="http://www.w3.org/1999/xhtml" d="M0.5000000000000004,32.650000000000006 L10.500000000000002,7.650000000000007 L0.5000000000000014,17.075000000000006 L-9.499999999999998,7.650000000000005 L0.5000000000000004,32.650000000000006" class="" stroke="rgb(7,152,216)" fill="rgb(7,152,216)" transform="translate(9.499999999999998,4)"></path></svg>
</div>

Current JS code:

$("#btnExp").click(function() {
    //Canvas Count
    var CCount = 1;

    //prekopiramo vse elemente v nov div. Vmes SVG pretvorimo v canvas.
    $("body").append("<div class='ghost_img'></div>");

    //Gremo cez vse elemente. Jih pripnemo v ghost_img, svg->canvas canvas pozicioniramo kot svg.
    $(".objects").find("*").each(function(index) {
        var object = $(this);
        //Ce je DIV ga kloniramo
        if(this.tagName=='DIV'){
            $(".ghost_img").append(object.clone());
        //Ce je SVG dobimo kordinate, ga transformiramo v canvas in pripnemo, canvasu dodamo -> style: position:absolute, top:y, left:x
        }else if(this instanceof SVGElement){
            $(".ghost_img").append("<canvas id='c"+CCount+'></canvas>');
            var position = $(this).position();
            //alert(position.left + ", top: "+ position.top);
            var oSerializer = new XMLSerializer();
            var sXML = oSerializer.serializeToString(object);

            canvg(document.getElementById('c'+CCount), sXML);
            $('#c'+CCount).css("position","absolute");
            $('#c'+CCount).css("top",position.top);
            $('#c'+CCount).css("left",position.left);
            CCount = CCount+1;

        }
    });

    //Renderamo celoten ghost_img
    html2canvas($(".ghost_img"), {
        onrendered: function(canvas) {
            theCanvas = canvas;
            document.body.appendChild(canvas);
        }
    });

});

With the code above, every element gets rendered, but the picture is not right. How elements get rendered , how they should render.

Edit of the code above #1: Okay so here's my idea. I would like to append all the elements to a ghost div, while appending them one by one i would convert the svgs to canvas. I would also set position(left,top) and position absolute to canvases i create. Then i would like to render the whole ghost div.

Demo link: Demo

zidarm
  • 25
  • 1
  • 11
  • Possible duplicate of [Drawing a SVG file on a HTML5 canvas](http://stackoverflow.com/questions/3768565/drawing-a-svg-file-on-a-html5-canvas) – Thomas R. Koll Apr 09 '16 at 12:44
  • could you provide a working example, with the css included. – Kaiido Apr 10 '16 at 05:50
  • I added the demo link above. – zidarm Apr 10 '16 at 09:07
  • It seems your problem comes from the absolute positioning on your svgs. Neither canvg nor html2canvas are able to render these. I don't think it's a good practice to lead you to some libraries, so I'll leave this as a comment for now, but using [`SVG2Bitmap.js`](https://github.com/Kaiido/SVG2Bitmap) (*disclaimer : I wrote it*) I am able to get your svg rendered : https://jsfiddle.net/ubtycqg4/1/ Main issue however, the canvas will now be tainted in IE – Kaiido Apr 10 '16 at 13:54
  • Demo isn't working for me. I get this error: Couldn't export svg, please check that the svgElement passed is a valid svg document. – zidarm Apr 11 '16 at 11:58
  • Worked fine on my FF, sorry I missed the failure on chrome. The problem came from the incorrect `xmlns` attribute which was set to "http://www.w3.org/1999/xhtml" which the HTML namespace, svg one should be "http://www.w3.org/2000/svg". [Here is the updated fiddle.](https://jsfiddle.net/ubtycqg4/2/) – Kaiido Apr 11 '16 at 14:37

1 Answers1

0

The main issue with your code is the absolute positioning of your svg elements.

canvg will be able to redraw it on a canvas, but it won't set back the correct styling on the canvas element. Which is the why everything is going down in the page.

html2canvas should be able to render these svg itself, at least in latest versions, but once again because of this inlined absolute positioning on the root <svg> itself, the image will get off of the <img> used to draw on the canvas.


So the solution would be to do some work by yourself before calling canvg and to keep track of the positions of your svg elements and reapply these once converted, before calling html2canvas.

var $svg = $('svg');
var svgStyles = [];
$svg.each(function() {
  svgStyles.push(this.getAttribute('style'))
})
canvg();
$('canvas').each(function(e) {
  $(this).attr('style', svgStyles[e])
})
html2canvas(...

Snippet using canvg after saving the styles :

$("#Exp").click(function() {
   var $svg = $('svg');
   var svgStyles = [];
   $svg.each(function() {
     svgStyles.push(this.getAttribute('style'))
   })
   canvg();
   $('canvas').each(function(e) {
     $(this).attr('style', svgStyles[e])
   })
   html2canvas($(".objects"), {
     onrendered: function(canvas) {
       theCanvas = canvas;
       document.body.appendChild(canvas);
     }
   });
 });
/****************** DEFAULT ***********************/

html,
body {
  font-family: 'Open Sans', sans-serif;
  margin: 0;
  height: 100%;
}
[id^="block"] {
  margin: 35px auto;
  border-width: 1px;
}
/****************** DIAGRAM POTEKA ****************/

.objects {
  width: 520px;
  margin: 0 auto;
  position: relative;
  z-index: 100;
  border-radius: 30px;
  text-align: center;
}
/******************* GHOST-BLOCKS *****************/

[class^="empty_block_l"] {
  height: 1px;
  width: 1px;
  margin-right: 20px;
}
[class^="empty_block_r"] {
  height: 1px;
  width: 1px;
  margin-left: 20px;
}
[class^="empty_block_c"] {
  height: 1px;
  width: 1px;
  margin: 10px auto;
}
/************ NASTAVI-OBJEKTE-NA-SREDINO *********/

[id^="n_block"].b_2,
[id^="n_block"].b_4 {
  margin: 25px auto;
}
/************ OBLIKUJE-OBJEKTE *******************/

._jsPlumb_endpoint _jsPlumb_endpoint_anchor jsplumb-draggable jsplumb-droppable,
svg {
  z-index: 888;
}
.subdiv_loop {
  padding: 0 10px;
  border: 1px dotted black;
  min-height: 100px;
  min-width: 195px;
  height: auto;
  width: auto;
  background-color: white;
  background-image: url(../slike/loop.gif);
  background-position: center;
  background-repeat: no-repeat;
}
.true {
  margin-right: 13px;
  padding: 0 10px;
}
.false {
  margin-left: 21px;
  padding: 0 10px;
}
.true,
.false {
  display: inline-block;
  border: 1px dotted black;
  min-height: 100px;
  min-width: 150px;
  height: auto;
  background-color: white;
  padding: 0 5px;
  background-position: center;
  background-repeat: no-repeat;
}
.b_s {
  background-color: rgb(167, 206, 73);
  z-index: 888;
  width: 100px;
  height: 50px;
  margin: 30px auto;
  border: 1px solid black;
  line-height: 50px;
  text-align: center;
  font-weight: bold;
  -webkit-border-radius: 50px / 25px;
  -moz-border-radius: 50px / 25px;
  border-radius: 50px / 25px;
}
.b_2,
.b_4 {
  background-color: white;
  border: 2px solid rgb(7, 152, 216);
  z-index: 888;
  line-height: 80px;
  height: 80px;
  width: 82px;
  border-radius: 10px;
  -moz-transform: rotate(45deg);
  -webkit-transform: rotate(45deg);
  -o-transform: rotate(45deg);
  -ms-transform: rotate(45deg);
  transform: rotate(45deg);
}
.SpodnjaTocka {
  width: 1px;
  height: 1px;
  margin: 15px auto;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.0/jquery.min.js"></script>
<script src="https://gabelerner.github.io/canvg/canvg.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/html2canvas/0.4.1/html2canvas.js"></script>
<div class="objects ui-droppable ui-sortable">
  <div class="b_s _jsPlumb_endpoint_anchor _jsPlumb_connected" id="start_block">START</div>
  <div class="b_2 block ui-draggable ui-draggable-handle zanka _jsPlumb_endpoint_anchor _jsPlumb_connected" id="n_block2" title="" style="width: 82px; height: 80px;"><span id="rotate_2"></span>
  </div>
  <div id="ovoj" class="ovoj n_block2">
    <div class="true n_block2 ui-droppable _jsPlumb_endpoint_anchor _jsPlumb_connected" id="jsPlumb_1_28"></div>
    <div class="false n_block2 ui-droppable _jsPlumb_endpoint_anchor _jsPlumb_connected" id="jsPlumb_1_39"></div>
  </div>
  <div class="SpodnjaTocka n_block2 _jsPlumb_endpoint_anchor _jsPlumb_connected" id="jsPlumb_1_29"></div>
  <div class="b_s _jsPlumb_endpoint_anchor _jsPlumb_connected" id="end_block">STOP</div>
  <div class="_jsPlumb_endpoint _jsPlumb_endpoint_anchor jsplumb-draggable jsplumb-droppable" style="display: block; width: 10px; height: 0px; position: absolute; left: 261px; top: 52px; background: transparent;"></div>
  <div class="_jsPlumb_endpoint _jsPlumb_endpoint_anchor jsplumb-draggable jsplumb-droppable" style="display: block; width: 10px; height: 0px; position: absolute; left: 261px; top: 82px; background: transparent;"></div>
  <svg style="position:absolute;left:251px;top:48px" width="20" height="38" pointer-events="none" position="absolute" version="1.1" class="_jsPlumb_connector">
    <path d="M 0 -1 L 0 31 M 0 31 L 0 -1 M 0 0 L 0 30 " transform="translate(9.999999999999998,4)" pointer-events="visibleStroke" version="1.1" fill="none" stroke="rgb(7,152,216)" style="" stroke-width="2"></path>
    <path pointer-events="all" version="1.1" d="M4.959819536546782e-16,33.1 L10.000000000000002,8.100000000000001 L1.4496756484906794e-15,17.525000000000002 L-9.999999999999998,8.100000000000001 L4.959819536546782e-16,33.1" class="" stroke="rgb(7,152,216)"
    fill="rgb(7,152,216)" transform="translate(9.999999999999998,4)"></path>
  </svg>
  <div class="_jsPlumb_endpoint _jsPlumb_endpoint_anchor jsplumb-draggable jsplumb-droppable" style="display: block; width: 10px; height: 0px; position: absolute; left: 163px; top: 303px; background: transparent;"></div>
  <div class="_jsPlumb_endpoint _jsPlumb_endpoint_anchor jsplumb-draggable jsplumb-droppable" style="display: block; width: 10px; height: 0px; position: absolute; left: 261.5px; top: 328px; background: transparent;"></div>
  <svg style="position:absolute;left:159px;top:299px" width="115.55000000000001" height="32" pointer-events="none" position="absolute" version="1.1" class="_jsPlumb_connector">
    <path d="M 0 -1 L 0 13.5 M -1 12.5 L 50.25 12.5 M 48.25 12.5 L 99.5 12.5 M 98.5 12.5 L 98.5 25 " transform="translate(4,4)" pointer-events="visibleStroke" version="1.1" fill="none" stroke="rgb(7,152,216)" style="" stroke-width="2"></path>
    <path pointer-events="all" version="1.1" d="M111.55000000000001,12.5 L86.55000000000001,22.5 L95.97500000000001,12.5 L86.55000000000001,2.5 L111.55000000000001,12.5" class="" stroke="rgb(7,152,216)" fill="rgb(7,152,216)" transform="translate(4,4)"></path>
  </svg>
  <div class="_jsPlumb_endpoint _jsPlumb_endpoint_anchor jsplumb-draggable jsplumb-droppable" style="display: block; width: 10px; height: 0px; position: absolute; left: 359px; top: 303px; background: transparent;"></div>
  <div class="_jsPlumb_endpoint _jsPlumb_endpoint_anchor jsplumb-draggable jsplumb-droppable" style="display: block; width: 10px; height: 0px; position: absolute; left: 261.5px; top: 328px; background: transparent;"></div>
  <svg style="position:absolute;left:248.35px;top:299px" width="114.65" height="32" pointer-events="none" position="absolute" version="1.1" class="_jsPlumb_connector">
    <path d="M 97.5 -1 L 97.5 13.5 M 98.5 12.5 L 47.75 12.5 M 49.75 12.5 L -1 12.5 M 0 12.5 L 0 25 " transform="translate(13.150000000000013,4)" pointer-events="visibleStroke" version="1.1" fill="none" stroke="rgb(7,152,216)" style="" stroke-width="2"></path>
    <path pointer-events="all" version="1.1" d="M-13.150000000000013,12.5 L11.849999999999987,2.5 L2.4249999999999865,12.5 L11.849999999999987,22.5 L-13.150000000000013,12.5" class="" stroke="rgb(7,152,216)" fill="rgb(7,152,216)" transform="translate(13.150000000000013,4)"></path>
  </svg>
  <div class="_jsPlumb_endpoint _jsPlumb_endpoint_anchor jsplumb-draggable jsplumb-droppable" style="display: block; width: 10px; height: 0px; position: absolute; left: 218px; top: 124px; background: transparent;"></div>
  <div class="_jsPlumb_endpoint _jsPlumb_endpoint_anchor jsplumb-draggable jsplumb-droppable" style="display: block; width: 10px; height: 0px; position: absolute; left: 163px; top: 201px; background: transparent;"></div>
  <svg style="position:absolute;left:153px;top:120px" width="69" height="84" pointer-events="none" position="absolute" version="1.1" class="_jsPlumb_connector">
    <path d="M 56 0 L 24 0 M 26 0 L -1 0 M 0 -1 L 0 48 M 0 47 L 0 77 " transform="translate(9.999999999999998,4)" pointer-events="visibleStroke" version="1.1" fill="none" stroke="rgb(7,152,216)" style="" stroke-width="2"></path>
    <path pointer-events="all" version="1.1" d="M2.2655965784226036e-16,75.7 L10.000000000000002,50.7 L1.1802533526782616e-15,60.125 L-9.999999999999998,50.7 L2.2655965784226036e-16,75.7" class="" stroke="rgb(7,152,216)" fill="rgb(7,152,216)" transform="translate(9.999999999999998,4)"></path>
  </svg>
  <div class="_jsPlumb_endpoint _jsPlumb_endpoint_anchor jsplumb-draggable jsplumb-droppable" style="display: block; width: 10px; height: 0px; position: absolute; left: 304px; top: 124px; background: transparent;"></div>
  <div class="_jsPlumb_endpoint _jsPlumb_endpoint_anchor jsplumb-draggable jsplumb-droppable" style="display: block; width: 10px; height: 0px; position: absolute; left: 359px; top: 201px; background: transparent;"></div>
  <svg style="position:absolute;left:300px;top:120px" width="69" height="84" pointer-events="none" position="absolute" version="1.1" class="_jsPlumb_connector">
    <path d="M -1 0 L 31 0 M 29 0 L 56 0 M 55 -1 L 55 48 M 55 47 L 55 77 " transform="translate(4,4)" pointer-events="visibleStroke" version="1.1" fill="none" stroke="rgb(7,152,216)" style="" stroke-width="2"></path>
    <path pointer-events="all" version="1.1" d="M55,75.7 L65,50.7 L55,60.125 L45,50.7 L55,75.7" class="" stroke="rgb(7,152,216)" fill="rgb(7,152,216)" transform="translate(4,4)"></path>
  </svg>
  <div class="_jsPlumb_endpoint _jsPlumb_endpoint_anchor jsplumb-draggable jsplumb-droppable" style="display: block; width: 10px; height: 0px; position: absolute; left: 261.5px; top: 329px; background: transparent;"></div>
  <div class="_jsPlumb_endpoint _jsPlumb_endpoint_anchor jsplumb-draggable jsplumb-droppable" style="display: block; width: 10px; height: 0px; position: absolute; left: 261px; top: 359px; background: transparent;"></div>
  <svg style="position:absolute;left:251px;top:325px" width="20" height="38" pointer-events="none" position="absolute" version="1.1" class="_jsPlumb_connector">
    <path d="M 0.5 -1 L 0.5 31 M 1.5 30 L -0.75 30 M 0.25 31 L 0.25 -1 M 1.25 0 L -1 0 M 0 0 L 0 30 " transform="translate(9.999999999999998,4)" pointer-events="visibleStroke" version="1.1" fill="none" stroke="rgb(7,152,216)" style="" stroke-width="2"></path>
    <path pointer-events="all" version="1.1" d="M4.684274006738627e-16,32.650000000000006 L10.000000000000002,7.650000000000007 L1.4221210955098638e-15,17.075000000000006 L-9.999999999999998,7.650000000000005 L4.684274006738627e-16,32.650000000000006" class=""
    stroke="rgb(7,152,216)" fill="rgb(7,152,216)" transform="translate(9.999999999999998,4)"></path>
  </svg>
</div>

<button type="submit" id="Exp">ExportAsImage</button>

Ps: Note that your namespace attributes were wrongly set to "http://www.w3.org/1999/xhtml"instead of "http://www.w3.org/2000/svg"

Kaiido
  • 123,334
  • 13
  • 219
  • 285
  • Hm.. strange, the svg is generated by a library called JsPlumb. It's for the svg arrows. – zidarm Apr 11 '16 at 15:48
  • May i ask what does the xmln mean? – zidarm Apr 11 '16 at 19:59
  • xmlns is an acronym for Extensible Markup Language NameSpace. The attribute defines how the browser will parse the markup, since XML, XHTML, SVG and probably a few others, can be mixed in the same document, and that they may share the same elements or attribute names, which should act differently. https://developer.mozilla.org/en/docs/Namespaces – Kaiido Apr 11 '16 at 23:38
  • For me it's not a big surprise a library produces incorrect markups, unfortunately? Libraries are written by human beings. Also, it becomes a problem only in a few cases like the rendering into an tag or as a standalone document. – Kaiido Apr 11 '16 at 23:40
  • How could i change the xmnl to the ".../2000/svg" on the svg elements? – zidarm Apr 12 '16 at 12:43
  • The clean way would be to set it correctly at creation of the nodes. The dirty way could be `$('*').each(function(){ if(this.getAttribute('xmlns').indexOf('xhtml')>-1){ this.setAttribute('xmlns', 'http://www.w3.org/2000/svg') } })` but if some of your elements really need this namespace, it'll screw them too. So yep, try to fix at creation. – Kaiido Apr 12 '16 at 14:04
  • Okay, i fixed it in the original jsplumb.js file. Changed the xhtml to svg. – zidarm Apr 12 '16 at 14:51
  • it seems i hit another problem where the arrows get disordered. [example](http://puu.sh/ofDuO/ee82328a04.png), could it be a css problem? – zidarm Apr 12 '16 at 15:18