Reputation: 105
I am looking to convert SVG from assets to png and write it to File.
I have managed to write SVG to file, but I want to write the image to a file as a PNG
code that writes SVG to file :
final bytes = await rootBundle.load('assets/images/example.svg');
final String tempPath = (await getTemporaryDirectory()).path;
final File file = File('$tempPath/profile.svg');
await file.writeAsBytes(
bytes.buffer.asUint8List(bytes.offsetInBytes, bytes.lengthInBytes));
return file;
}
Upvotes: 8
Views: 7508
Reputation: 262
i am providing here latest solution according to the latest plugin.flutter_svg To assign the exact width and height to the output image, you need to extract the width and height values from the SVG by parsing it, use xml plugin..
import 'package:xml/xml.dart' as xml;
import 'package:flutter_svg/flutter_svg.dart';
Future<Uint8List?> svgToPng(String svgString) async {
// first parse svgString to get width and height using xml plugin
final document = xml.XmlDocument.parse(svgString);
final svgElement = document.rootElement;
final widthAttribute = svgElement.getAttribute('width')!.replaceAll("px", "");
final heightAttribute = svgElement.getAttribute('height')!.replaceAll("px", "");
final svgDrawableRoot = await vg.loadPicture(SvgStringLoader(svgString), null);
// same thing
// Convert to ui.Image. toImage() takes width and height as parameters
// you need to find the best size to suit your needs and take into account the screen DPI
final image = await svgDrawableRoot.picture.toImage(int.parse(widthAttribute.toString()),int.parse( heightAttribute.toString()));
ByteData? bytes = await image.toByteData(format: ImageByteFormat.png);
return bytes?.buffer.asUint8List();
}
Upvotes: 2
Reputation: 974
DrawableRoot isnt available in newer Versions of flutter_svg
. The new way of doing it is by using the VectorGraphicUtilities:
final pictureInfo = await vg.loadPicture(SvgStringLoader(...));
final image = await pictureInfo.picture.toImage(...);
The result is a Bitmap-Image and can be turned into any other supported Format e.g. png:
final byteData = await image.toByteData(format: ImageByteFormat.png);
final pngBytes = byteData!.buffer.asUint8List();
And can then be saved as a Img on the Device:
final file = await File('test.png').create();
await file.writeAsBytes(pngBytes);
So everything put together:
import 'package:flutter_svg/flutter_svg.dart';
Future<Uint8List> svgToPng(String svgString, BuildContext context) async {
final pictureInfo = await vg.loadPicture(SvgStringLoader(svgString), context);
final image = await pictureInfo.picture.toImage(100, 100);
final byteData = await image.toByteData(format: ImageByteFormat.png);
if (byteData == null) {
throw Exception('Unable to convert SVG to PNG');
}
final pngBytes = byteData.buffer.asUint8List();
return pngBytes;
}
Upvotes: 4
Reputation: 318
@pierre-hubert answer is nice. But I had to do some changes to get it working.
import 'dart:typed_data';
import 'dart:ui';
import 'package:flutter/material.dart';
import 'package:flutter_svg/flutter_svg.dart';
import 'package:xml/xml.dart';
Future<Uint8List> svgToPng(BuildContext context, String svgString,
{int? svgWidth, int? svgHeight}) async {
DrawableRoot svgDrawableRoot = await svg.fromSvgString(svgString, "key");
// Parse the SVG file as XML
XmlDocument document = XmlDocument.parse(svgString);
// Getting size of SVG
final svgElement = document.findElements("svg").first;
final svgWidth = double.parse(svgElement.getAttribute("width")!);
final svgHeight = double.parse(svgElement.getAttribute("height")!);
// toPicture() and toImage() don't seem to be pixel ratio aware, so we calculate the actual sizes here
double devicePixelRatio = MediaQuery.of(context).devicePixelRatio;
double width = svgHeight * devicePixelRatio;
double height = svgWidth * devicePixelRatio;
// Convert to ui.Picture
final picture = svgDrawableRoot.toPicture(size: Size(width, height));
// Convert to ui.Image. toImage() takes width and height as parameters
// you need to find the best size to suit your needs and take into account the screen DPI
final image = await picture.toImage(width.toInt(), height.toInt());
ByteData? bytes = await image.toByteData(format: ImageByteFormat.png);
return bytes!.buffer.asUint8List();
}
You can then use Image.memory()
widget to render the result
Upvotes: 1
Reputation: 521
I found a solution to your problem on another answer.
You will need to add flutter_svg
as a dependency of your project.
This is almost a copy past of the code given on the referenced answer:
Future<Uint8List> svgToPng(BuildContext context, String svgString,
{int svgWidth, int svgHeight}) async {
DrawableRoot svgDrawableRoot = await svg.fromSvgString(svgString, null);
// to have a nice rendering it is important to have the exact original height and width,
// the easier way to retrieve it is directly from the svg string
// but be careful, this is an ugly fix for a flutter_svg problem that works
// with my images
String temp = svgString.substring(svgString.indexOf('height="') + 8);
int originalHeight =
svgHeight ?? int.parse(temp.substring(0, temp.indexOf('p')));
temp = svgString.substring(svgString.indexOf('width="') + 7);
int originalWidth =
svgWidth ?? int.parse(temp.substring(0, temp.indexOf('p')));
// toPicture() and toImage() don't seem to be pixel ratio aware, so we calculate the actual sizes here
double devicePixelRatio = MediaQuery.of(context).devicePixelRatio;
double width = originalHeight *
devicePixelRatio; // where 32 is your SVG's original width
double height = originalWidth * devicePixelRatio; // same thing
// Convert to ui.Picture
final picture = svgDrawableRoot.toPicture(size: Size(width, height));
// Convert to ui.Image. toImage() takes width and height as parameters
// you need to find the best size to suit your needs and take into account the screen DPI
final image = await picture.toImage(width.toInt(), height.toInt());
ByteData bytes = await image.toByteData(format: ImageByteFormat.png);
return bytes.buffer.asUint8List();
}
Upvotes: 4