2017-02-20 52 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>