2017-02-20 3 views
0

Мне нужно преобразовать видимую область SVG в статическое изображение для печати и аналогичных целей. Я понимаю, что подход должен состоять в том, чтобы сначала преобразовать SVG в холст с помощью canvg, а затем преобразовать холст в изображение с помощью canvas.toDataURL. Я могу выполнить эти основные требования, однако мой viewbox намного меньше, чем мой SVG, таким образом, обрезка и масштабирование становятся проблемами, и именно здесь я сейчас спотыкаюсь. Моей мыслью было вставить еще два шага перед преобразованием в изображение, где я использую canvasContext.drawImage(...), чтобы обрезать немасштабированный холст в область, определенную в окне просмотра SVG. Этот последний шаг не работает для меня: я не могу обрезать изображение и поддерживать текущий масштаб. Затем я намерен использовать pica.resizeCanvas для достижения высокого качества масштабирования изображения (чтобы он соответствовал печатаемой странице).Преобразование SVG в холст, за исключением области вне окна просмотра SVG

Перед тем, как в коде на вопросы:

  • ли над всем подход звука?
  • Что я делаю неправильно с canvasContext.drawImage(...), что изображение либо заканчивается масштабированным, либо обрезанным неправильно? Цель состоит в том, чтобы не применять масштабирование, а обрезать лишнее пустое пространство.

Резюме шагов

  • Копирование svg в canvas с помощью canvg. Это работает.
    • масштабирование не применяется
    • Смещения используются для перемещения изображения в верхнем левом углу, в рамках подготовки к будущей кадрирование оставшегося белого пространства (нижняя правая область).
  • Овладейте вновь созданного canvas и привлечь обрезается область к вторичному canvas с помощью canvasContext.drawImage. Это не удается. Либо холст - это неправильный размер, но масштабирование является правильным (т.е. none), или холст имеет нужный размер, но масштабирование неверно (масштабируется).
  • Используйте 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; 
} 
+1

Вы всегда можете применить клип или clipPath к Viewbox. –

+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>