2017-02-20 258 views
0

我需要将SVG的可见区域转换为静态图像以进行打印和类似用途。我从阅读中得知,该方法应该是先使用canvg将SVG转换为画布,然后使用canvas.toDataURL将画布转换为图像。我能够完成这些基本要求,但是我的viewbox比我的SVG小得多,因此裁剪和缩放成为问题,而这正是我目前磕磕绊绊的地方。 我的想法是在转换为使用canvasContext.drawImage(...)的图像之前插入另外两个步骤,将未缩放的画布裁剪到由SVG视图框定义的区域。这最后一步对我不起作用:我无法设法裁剪图像并保持当前的比例。然后,我打算使用pica.resizeCanvas来实现图像的高质量缩放(使其适合可打印页面)。将SVG转换为画布,同时排除SVG视图框外的区域

之前进入代码的问题是:

  • 将是对所有进场声?
  • 我在做什么错误canvasContext.drawImage(...)该图像结束了缩放或裁剪不正确?目标是不应用缩放,而是裁剪一些多余的空白区域。步骤

    • 复印svg

    概述以便canvas使用canvg这工作。

    • 无缩放施加
    • 偏移被用于将图像移动到左上角,准备剩余的白色空间(右下区域)的未来裁剪。
  • 获取新创建canvas保持和使用canvasContext.drawImage绘制裁剪区域的二次canvas这失败了。画布尺寸不正确,但缩放比例正确(即无)或画布尺寸正确,但缩放比例不正确(缩放)。
  • 使用pica.resizeCanvas可应用缩放,质量损失最小。 还没有真正尝试过。
  • 使用canvasContext.toDataURL(...)转换canvas到png这工作。

代码

function MakeImage() { 

    //min-x, min-y, width and height 
    var viewBox = parseViewBox(); //defined below 
    var vbMinX = viewBox[0]; 
    var vbMinY = viewBox[1]; 
    var vbWidth = viewBox[2]; 
    var vbHeight = viewBox[3]; 

    var svgUnitRatio = getSvgUnitRatio(vbHeight); //defined below 

    //xMin,yMin,xMax,yMax 
    var boundingBox = getBounds(); //defined below 
    var bbXmin = boundingBox[0]; 
    var bbYmin = boundingBox[1]; 
    var bbXmax = boundingBox[2]; 
    var bbYmax = boundingBox[3]; 

    var offsetX = (vbMinX - bbXmin) * svgUnitRatio; 
    var offsetY = (vbMinY - bbYmin) * svgUnitRatio; 

    var adjustedWidth = (bbXmax - bbXmin) * svgUnitRatio; 
    var adjustedHeight = (bbYmax - bbYmin) * svgUnitRatio; 

    var options = { 
     ignoreDimensions: false, //allow it to resize the canvas based on the svg size 
     offsetX: offsetX, 
     offsetY: offsetY 
    }; 

    //first we copy the svg to a canvas w/o applying any scaling 
    window.canvg("workspaceCanvas", $("#mysvg").parent().html(), options); 

    //now we crop according the svg viewbox 
    var canvas = document.getElementById("canvas"); 
    var workspaceCanvas = document.getElementById("workspaceCanvas"); 

    var context = canvas.getContext('2d'); 
    context.drawImage(workspaceCanvas, 0, 0, adjustedWidth, adjustedHeight, 0, 0, adjustedWidth, adjustedHeight); //something is wrong here i guess??? 

    //next we do a high quality scaling of the canvas 
    var pOptions = {   //maybe this has problems but i won't kow until i get the previous step right 
     quality: 3, 
     alpha: true, 
     unsharpAmount: 50, 
     unsharpRadius: 0.5, 
     unsharpThreshold: 0 
    }; 

    //holding off on trying this for now 
    window.pica.resizeCanvas(workspaceCanvas, canvas, pOptions, function(err) { /*this is a mandatory argument*/ }); 

    var img = canvas.toDataURL("image/png,1"); 

    //do stuff with image data 
} 

function getSvgUnitRatio(viewboxHeight) { 
    //shouldnt need to worry about width since the aspect ratio should be locked 
    var height = parseFloat(d3.select("#mysvg").attr("height")); 

    return height/viewboxHeight; 
} 

function getBounds() { 

    //xMin,yMin,xMax,yMax 
    var boundingBox = []; 

    var xMin = Number.MAX_SAFE_INTEGER; 
    var yMin = Number.MAX_SAFE_INTEGER; 
    var xMax = Number.MIN_SAFE_INTEGER; 
    var yMax = Number.MIN_SAFE_INTEGER; 

    window.svg.selectAll(".elementsICareAbout").nodes().forEach(function(d) { 
     var dx = parseFloat(d.getAttribute("x")); 
     var dy = parseFloat(d.getAttribute("y")); 
     var width = parseFloat(d.getAttribute("width")); 
     var height = parseFloat(d.getAttribute("height")); 

     if (dx + width > xMax) { 
      xMax = dx + width; 
     } 

     if (dx < xMin) { 
      xMin = dx; 
     } 

     if (dy + height > yMax) { 
      yMax = dy + height; 
     } 

     if (dy < yMin) { 
      yMin = dy; 
     } 
    }); 

    var padding = 25; //add some fluff 

    //xMin,yMin,xMax,yMax 
    boundingBox = [ 
     xMin - padding, yMin - padding, xMax + padding, yMax + padding 
    ]; 

    return boundingBox; 
} 

function parseViewBox() { 
    var str = d3.select("#mysvg").attr("viewBox"); 

    var parts = str.split(" "); 

    var parsed = []; 

    parts.forEach(function(p) { 
     parsed.push(parseFloat(p)); 
    }); 

    return parsed; 
} 
+1

你总是可以应用剪裁或clipPath到视框。 –

+1

如果您想要提供全尺寸内联svg进行打印,那么您可以使用事件window.onbeforeprint,window.onafterprint和window.matchmedia(Chrome)。这提供了操纵svg以填充窗口的能力,然后在打印后重置​​它。 (这不需要画布)。我可以举一个例子,你想看到这种方法。 –

+0

@FrancisHemsher如果你有一个方便的例子,我很乐意看到它。 –

回答

1

如果您想为打印的全尺寸联SVG,那么你可以使用事件window.onbeforeprint,window.onafterprint和window.matchmedia器(Chrome )。这提供了操纵svg以填充窗口的能力,然后在打印后重置​​它。 (这不需要画布)。以下是这种方法的一个例子。

注意:要测试此项,必须将以下内容复制到计算机上的HTML文件中并调用到浏览器中。

<!DOCTYPE html> 
 
<html xmlns="http://www.w3.org/1999/xhtml"> 
 
<head> 
 
    <title>Print SVG Full Size</title> 
 

 
</head> 
 
<body style='padding:10px;font-family:arial'> 
 
<center> 
 
<h4>Print SVG Full Size</h4> 
 
<div style='width:90%;background-color:gainsboro;text-align:justify;padding:10px;border-radius:6px;'> 
 
You can print the inline SVG segment of your web page as full size. This uses the browser's 'Print..' feature, the window events <b>onbeforeprint</b>, <b>onafterprint</b>, plus <b>window.matchMedia</b>. 
 
</div> 
 
<table><tr> 
 
<td> 
 
<div style="padding:10px;width:400px;text-align:justify"> 
 

 
<b>Scenerio:</b><br /> 
 
Select the browser's <b>Print..</b><br> <br> 
 
The function <b>beforePrint</b> hides all elements except the DIV containing the inline SVG, plus the DIV is postioned to top/left at 0 px. The SVG and the DIV are sized at 100%. 
 
<br><br> 
 
The function <b>afterPrint</b> returns the elements to their original visibility and locatons.<br> <br> 
 
The event <b>window.matchMedia</b> automatically calls the above functions for Chrome.<br> 
 
Both IE and FF use the window events <b>onbeforeprint</b> and <b>onafterprint</b>. 
 
<p></p> 
 
Note: A Print 'Save as PDF' has the nice feature called 'Snapshot' that can be used to clip just the SVG portion of the PDF and save it, via any image editor, as a .png file. 
 
</div> 
 
</td> 
 
<td> 
 
<div id="svgDiv" style='width:400px;height:400px;'> 
 
<svg id="mySVG" width="400" height="400"> 
 
<rect x=0 y=0 width="400" height="400" stroke="none" fill="red" /> 
 
<circle cx=200 cy=200 fill=yellow r=150 stroke=none /> 
 
</svg> 
 
</div> 
 

 
</td> 
 
</tr></table> 
 
<script> 
 
function beforePrint() 
 
{ 
 
    document.body.style.visibility="hidden" 
 
    svgDiv.style.visibility='visible' 
 
    svgDiv.style.position="absolute" 
 
    svgDiv.style.top="0px" 
 
    svgDiv.style.left="0px" 
 
    svgDiv.style.width="100%" 
 
    svgDiv.style.height="100%" 
 

 
    var bb=mySVG.getBBox() 
 
    var bbx=bb.x 
 
    var bby=bb.y 
 
    var bbw=bb.width 
 
    var bbh=bb.height 
 

 
    mySVG.setAttribute("viewBox",bbx+" "+bby+" "+bbw+" "+bbh) 
 
    mySVG.setAttribute("width","100%") 
 
    mySVG.setAttribute("height","100%") 
 
} 
 

 
function afterPrint() 
 
{ 
 
    document.body.style.visibility="" 
 
    svgDiv.style.visibility='' 
 
    svgDiv.style.position="" 
 
    svgDiv.style.top="" 
 
    svgDiv.style.left="" 
 
    mySVG.removeAttribute("viewBox") 
 
    mySVG.setAttribute("width","400") 
 
    mySVG.setAttribute("height","400") 
 
} 
 
//---Chrome Browser--- 
 
if (window.matchMedia) 
 
{ 
 
     var mediaQueryList = window.matchMedia('print'); 
 
     mediaQueryList.addListener(function(mql) 
 
      { 
 
       if (mql.matches) 
 
       { 
 
        beforePrint(); 
 
       } 
 
       else 
 
       { 
 
        afterPrint(); 
 
       } 
 
      } 
 
     ); 
 
} 
 

 
    //---IE & FF--- 
 
window.onbeforeprint = beforePrint 
 
window.onafterprint = afterPrint; 
 
</script> 
 

 

 

 
</body> 
 

 
</html>