1

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

在进入代码之前,问题是:

  • 总体方法是否合理?
  • canvasContext.drawImage(...)图像最终缩放或裁剪不正确,我做错了什么?目标是不应用缩放,而是裁剪一些多余的空白区域。

步骤总结

  • 复制svgcanvas使用canvg这行得通。
    • 不应用缩放
    • 偏移量用于将图像移动到左上角,为将来裁剪剩余的空白区域(右下区域)做准备。
  • 获取新创建的canvas并将裁剪区域绘制到辅助canvas使用canvasContext.drawImage. 这失败了。画布尺寸错误但缩放正确(即没有)或画布尺寸正确但缩放错误(放大)。
  • 用于pica.resizeCanvas以最小的质量损失应用缩放。 还没有真正尝试过。
  • 用于canvasContext.toDataURL(...)将画布转换为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;
}
4

1 回答 1

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>

于 2017-02-22T13:03:15.917 回答