Конвертировать встроенный SVG в PNG на месте - PullRequest
31 голосов
/ 25 марта 2011

Я генерирую HTML из источника Docbook, используя SVG для изображений (преобразованный из MathML).Это отлично работает для некоторых браузеров, которые могут интерпретировать SVG, но не работает для других.

Что мне действительно нужно, так это добавить шаг постобработки, который преобразует SVG в PNG «на месте» (в HTML).).

Так что-то вроде этого:

<body>
    <svg xmlns="http://www.w3.org/2000/svg" version="1.1">
        <circle cx="50" cy="50" r="30" />
    </svg>
</body>

будет легко преобразовано в это:

<body>
    <img src="img0001.png" />
</body>

И я получу конвертированный PNG рядом.

Есть ли что-то, что сделает это?

Ответы [ 6 ]

41 голосов
/ 25 марта 2011

Демо: http://phrogz.net/SVG/svg_to_png.xhtml

  1. Создайте img и установите его src в SVG.
  2. Создайте холст HTML5 и используйте drawImage(), чтобы нарисовать это изображение на холсте.
  3. Используйте toDataURL() на холсте, чтобы получить PNG в кодировке base64.
  4. Создайте img и установите для его URL-адреса данные.
var mySVG    = document.querySelector('…'),      // Inline SVG element
    tgtImage = document.querySelector('…'),      // Where to draw the result
    can      = document.createElement('canvas'), // Not shown on page
    ctx      = can.getContext('2d'),
    loader   = new Image;                        // Not shown on page

loader.width  = can.width  = tgtImage.width;
loader.height = can.height = tgtImage.height;
loader.onload = function(){
  ctx.drawImage( loader, 0, 0, loader.width, loader.height );
  tgtImage.src = can.toDataURL();
};
var svgAsXML = (new XMLSerializer).serializeToString( mySVG );
loader.src = 'data:image/svg+xml,' + encodeURIComponent( svgAsXML );

Однако этот ответ (и все решения на стороне клиента) требует, чтобы браузер поддерживал SVG, что может сделать его бесполезным для ваших конкретных потребностей.

Редактировать : Этот ответ предполагает, что SVG доступен в виде отдельного URL. Из-за проблем, описанных в этом вопросе Я не могу заставить вышеуказанное работать с документом SVG, встроенным в тот же документ, выполняющий работу.

Редактировать 2 : Проблемы, описанные в этом другом вопросе, были преодолены благодаря улучшениям в Chrome и Firefox. Существует ограничение, что элемент <svg> должен иметь атрибуты width="…" height="…" для Firefox, чтобы он мог быть нарисован на холсте. А Safari в настоящее время портит весь холст всякий раз, когда вы рисуете на него SVG (независимо от источника), но , который должен скоро измениться .

4 голосов
/ 24 января 2014

Я столкнулся с этой проблемой за прошедшие выходные и закончил тем, что написал простую библиотеку, чтобы сделать более или менее то, что описывает Фрогз. Он также жестко кодирует стиль целевого SVG во избежание проблем с рендерингом. Надеюсь, следующий человек, который ищет способ сделать это, может просто использовать мой код и перейти к более интересным задачам!

P.S. Я проверял это только в Chrome.

// Takes an SVG element as target
function svg_to_png_data(target) {
  var ctx, mycanvas, svg_data, img, child;

  // Flatten CSS styles into the SVG
  for (i = 0; i < target.childNodes.length; i++) {
    child = target.childNodes[i];
    var cssStyle = window.getComputedStyle(child);
    if(cssStyle){
       child.style.cssText = cssStyle.cssText;
    }
  }

  // Construct an SVG image
  svg_data = '<svg xmlns="http://www.w3.org/2000/svg" width="' + target.offsetWidth +
             '" height="' + target.offsetHeight + '">' + target.innerHTML + '</svg>';
  img = new Image();
  img.src = "data:image/svg+xml," + encodeURIComponent(svg_data);

  // Draw the SVG image to a canvas
  mycanvas = document.createElement('canvas');
  mycanvas.width = target.offsetWidth;
  mycanvas.height = target.offsetHeight;
  ctx = mycanvas.getContext("2d");
  ctx.drawImage(img, 0, 0);

  // Return the canvas's data
  return mycanvas.toDataURL("image/png");
}

// Takes an SVG element as target
function svg_to_png_replace(target) {
  var data, img;
  data = svg_to_png_data(target);
  img = new Image();
  img.src = data;
  target.parentNode.replaceChild(img, target);
}
3 голосов
/ 13 мая 2017

Я взял код @Phrogz выше и сделал рабочий фрагмент.Не уверен, что mySVG.clientWidth работает в FF.Это также доступно здесь - https://jsfiddle.net/LLjLpo05/

var mySVG = document.querySelector('#svblock'),        // Inline SVG element
    tgtImage = document.querySelector('#diagram_png'), // Where to draw the result
    can = document.createElement('canvas'), // Not shown on page
    ctx = can.getContext('2d'),
    loader = new Image; // Not shown on page

//loader.width  = can.width  = tgtImage.width = mySVG.getBBox().width;
//loader.height = can.height = tgtImage.height = mySVG.getBBox().height;

loader.width = can.width = tgtImage.width = mySVG.clientWidth;
loader.height = can.height = tgtImage.height = mySVG.clientHeight;

loader.onload = function() {
  ctx.drawImage(loader, 0, 0, loader.width, loader.height);
  tgtImage.src = can.toDataURL();
};
var svgAsXML = (new XMLSerializer).serializeToString(mySVG);
loader.src = 'data:image/svg+xml,' + encodeURIComponent(svgAsXML);
<div id="diagram_image">
  <svg id="svblock" xmlns="http://www.w3.org/2000/svg" xmlns:inkspace="http://www.inkscape.org/namespaces/inkscape" xmlns:xlink="http://www.w3.org/1999/xlink" viewBox="0 0 640 120">
    <defs id="defs_block">
      <filter height="1.504" id="filter_blur" inkspace:collect="always" width="1.1575" x="-0.07875" y="-0.252">
        <feGaussianBlur id="feGaussianBlur3780" inkspace:collect="always" stdDeviation="4.2" />
      </filter>
    </defs>
    <title>blockdiag</title>
    <desc/>
    <rect fill="rgb(0,0,0)" height="40" stroke="rgb(0,0,0)" style="filter:url(#filter_blur);opacity:0.7;fill-opacity:1" width="128" x="67" y="46" />
    <rect fill="rgb(0,0,0)" height="40" stroke="rgb(0,0,0)" style="filter:url(#filter_blur);opacity:0.7;fill-opacity:1" width="128" x="259" y="46" />
    <rect fill="rgb(0,0,0)" height="40" stroke="rgb(0,0,0)" style="filter:url(#filter_blur);opacity:0.7;fill-opacity:1" width="128" x="451" y="46" />
    <rect fill="rgb(255,255,255)" height="40" stroke="rgb(0,0,0)" width="128" x="64" y="40" />
    <text fill="rgb(0,0,0)" font-family="sans-serif" font-size="11" font-style="normal" font-weight="normal" text-anchor="middle" textLength="55" x="128" y="66">discovery</text>
    <rect fill="rgb(255,255,255)" height="40" stroke="rgb(0,0,0)" width="128" x="256" y="40" />
    <text fill="rgb(0,0,0)" font-family="sans-serif" font-size="11" font-style="normal" font-weight="normal" text-anchor="middle" textLength="55" x="320" y="66">execution</text>
    <rect fill="rgb(255,255,255)" height="40" stroke="rgb(0,0,0)" width="128" x="448" y="40" />
    <text fill="rgb(0,0,0)" font-family="sans-serif" font-size="11" font-style="normal" font-weight="normal" text-anchor="middle" textLength="55" x="512" y="66">reporting</text>
    <path d="M 192 60 L 248 60" fill="none" stroke="rgb(0,0,0)" />
    <polygon fill="rgb(0,0,0)" points="255,60 248,56 248,64 255,60" stroke="rgb(0,0,0)" />
    <path d="M 384 60 L 440 60" fill="none" stroke="rgb(0,0,0)" />
    <polygon fill="rgb(0,0,0)" points="447,60 440,56 440,64 447,60" stroke="rgb(0,0,0)" />
  </svg>
</div>

<img id="diagram_png" src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAUAAAAFCAYAAACNbyblAAAAHElEQVQI12P4
//8/w38GIAXDIBKE0DHxgljNBAAO9TXL0Y4OHwAAAABJRU5ErkJggg==" />

ОБНОВЛЕНИЕ : немного переработано - https://jsfiddle.net/e4r8sk18/1/

ОБНОВЛЕНИЕ2 : преобразовано в преобразователькласс - https://jsfiddle.net/07a93Lt6/5/

3 голосов
/ 25 марта 2011

ФОП и батик

http://xmlgraphics.apache.org/fop/

http://xmlgraphics.apache.org/batik/

FOP от Apache включает Batik, также от Apache. Batik имеет инструмент визуализации SVG, который будет генерировать ваши PNG. FOP также является инструментом для создания документов.

1 голос
/ 31 декабря 2014

это довольно старый, но я нашел более простой фрагмент, который делает работу правильно в современных браузерах: https://gist.github.com/Caged/4649511

1 голос
/ 25 марта 2011

если вы хотите сделать это исключительно на стороне клиента, вам потребуется два шага:

  1. конвертирование SVG в Canvas (http://code.google.com/p/canvas-svg/ или некоторые другие инструменты)
  2. конвертировать Canvas в PNG (http://www.nihilogic.dk/labs/canvas2image/ или некоторые другие инструменты)

это, очевидно, будет работать только в браузерах с поддержкой HTML5.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...