How can BonsaiJS load external SVG files? - svg

I am learning to work with BonsaiJS and would like to import an external SVG image on the stage.
At http://docs.bonsaijs.org/overview/Path.html I learnt that there are three ways to create a new Path() sending SVG paths, path commands or path points as arguments, but for more complex SVG files this is too much of a hassle to make this work.
At How Can I Animate my SVG files with a JS Library - Is Bonsai Ideal for this? I read one can use the new Bitmap() method, but SVG's are turned into... bitmaps.
Am I missing something? Thanks in advance!

At the time it seems impossible for BonsaiJS to load external SVG's like it is possible to load external bitmap images. The BonsaiJS docs (http://docs.bonsaijs.org/overview/Path.html) do provide three methods to manually handle paths from an SVG file separately.
Path nodes
If your SVG contains <path> nodes (tags), get the value of the d attribute and apply the fill manually:
SVG
<path id="pathId" fill="#ff6600" d="M54.651,316.878c9.098,19.302,3.208,46.085,11.193,67.117 c0.687,2.48-4.441-0.06-2.581,5.486c9.171,20.188,10.61,52.755,17.331,79.04l-1.88-0.961c1.652,5.139,7.946,8.864,10.469,13.37">
BonsaiJS
var myShape1 = new Path("M54.651,316.878c9.098,19.302,3.208,46.085,11.193,67.117 c0.687,2.48-4.441-0.06-2.581,5.486c9.171,20.188,10.61,52.755,17.331,79.04l-1.88-0.961c1.652,5.139,7.946,8.864,10.469,13.37");
myShape1.fill('#ff6600');
Polygon nodes
If your SVG ie. contains <polygon> nodes, get the string value of the points attribute, split this string into an array and turn all string elements into floats. Again also apply the fill manually:
SVG
<polygon id="polygonId" fill="#FFFFFF" points="76.5,253.5 94.5,165.5 108.5,125.5 128.5,93.5 164.5,66.5 195.891,44.719 254.5,36.5 299.656,36.5 348.5,41.5 414,66 459,102 488,150.5 505.5,218.5 508,331.5 508,390 504.5,424.5 480,463.5 450,509.5 378,554.5 300,566 226,556.5 168,535.5 109.5,476 87,419.5 76.5,339.5 "/>
BonsaiJS
var points = "76.5,253.5 94.5,165.5 108.5,125.5 128.5,93.5 164.5,66.5 195.891,44.719 254.5,36.5 299.656,36.5 348.5,41.5 414,66 459,102 488,150.5 505.5,218.5 508,331.5 508,390 504.5,424.5 480,463.5 450,509.5 378,554.5 300,566 226,556.5 168,535.5 109.5,476 87,419.5 76.5,339.5";
points = points.split(/[\s,]+/).map(function(point) {
return parseFloat(point);
});
var myShape2 = new Path(points).fill('#ffffff');
Then...
After defining all parts, one can create a group and add all parts to 'reconstruct' the original SVG image, ie.:
var myGroup = new Group().addTo(stage);
myShape1.addTo(myGroup);
myShape2.addTo(myGroup);

At http://docs.bonsaijs.org/overview/Path.html I learnt that there are three ways to create a new Path() sending SVG paths, path commands or path points as arguments, but for more complex SVG files this is too much of a hassle to make this work.
Yes, Bonsai let you import SVG (like) Paths.
At How Can I Animate my SVG files with a JS Library - Is Bonsai Ideal for this? I read one can use the new Bitmap() method, but SVG's are turned into... bitmaps.
Bonsai doesn't let you import SVGs in general. Only paths. Which means that Bonsai doesn't give you API for accessing the inner nodes of your SVG file.
Using the Bitmap API is not a way of importing your SVG. The SVG file is treated as any regular image format. It depends on the platform and renderer whether it is going to be rastered or not. It can be compared with using DOM's HTMLImageElement.

Related

Add SVG image to PdfSignatureAppearance with iText7

I want to add a SVG image to PdfSignatureAppearance. The method setSignatureGraphic has an ImageData parameter now in iText7. I couldn't find a way to create an imageData from SVG because ImageDataFactory is not supporting this format.
Can you please guide me on how to do that?
Note that with iText5 I was able to add svg after converting it to PDF and import it to a PDFTemplate then create an image after instantiate the PDFTemplate. setSignatureGraphic was accepting com.itextpdf.text.Image as parameter
Your question could be split into 2 more precise and simple ones:
How to process an SVG with iText?
How to create an ImageData instance out of the result of point 1?
As for question 1: one can use SvgConverter class (part of iTextCore's svg module). Unfortunately there are only PDF-related methods there: an SVG could be converted either to Image (class of layout module), or to PdfFormXObject (again PDF-related) or to a PDF file.
// to PDF
SvgConverter.convertToImage(new FileInputStream(sourceFolder + "your_svg.svg"), signer.getDocument()); // the mentioned `signer` is the instance of PdfSigner which you use to sign the document
// to Image
SvgConverter.convertToImage(new FileInputStream(sourceFolder + "your_svg.svg"), new File(destinationFolder + "svgAsPdf.pdf"));
As for question 2, there are several answers:
a) Suppose that you want to use this Image as the PdfSignatureAppearance's graphics data. For now the class doesn't provide a convenient setter, however, you could use some low level methods - either getLayer0 or getLayer2 to get the signature's background or foreground. They are represented by PDfFormXObject, hence you can use Canvas to add your image to them:
Image svg = SvgConverter.convertToImage(new FileInputStream(sourceFolder + "your_svg.svg"), signer.getDocument());
Canvas canvas = new Canvas(appearance.getLayer0(), signer.getDocument());
canvas.add(svg);
canvas.close();
b) Suppose that your goal is to use the rendered bitmap as the PdfSignatureAppearance's graphics data. Then there is a specific iText product - pdfRender - which converts PDF files to images. The following code could be applied:
PdfToImageRenderer.renderPdf(new File(destinationFolder + "svgAsPdf.pdf"), new File(folderForTheResultantImage));
Now you can create an ImageData instance out of the resultant image file (by default a PDF is converted to a series of images with the format "pdfnamePAGE_NUMBER.jpg", but one could customize either the name or the output image format). In your case the PDF consist of just one page (which represents the converted SVG) and its name is "image1.jpg". The rest is obvious:
appearance.setSignatureGraphic(ImageDataFactory.create(destinationFolder + "image1.jpg"));

Can I import svg into Gideros?

Ok I will admit to being a noob with Gideros. I am trying to import an svg file. I followed the same procedure as you would do for a png file, that is create a texture using:
myTexture = Texture.new("orangeSquare.svg")
orangeSquare = Bitmap.new(myTexture)
stage:addChild(orangeSquare)
Unsurprisingly it doesn't work (output gives orangeSquare.svg: Error while reading image file. and program does not run) as it is not a bitmap file. Is there an alternative to import svg files? It is probably a very simple solution but I have been searching for some time without an answer. I am considering giving up on Gideros if I don't find an answer to what should be a simple problem.
You can not import the svg directly, you need to pass the svg data:
--Banana shape, SVG path format
local banana = "M8.64,223.948c0,0,143.468,3.431,185.777-181.808c2.673-11.702-1.23-20.154,1.316-33.146h16.287c0,0-3.14,17.248,1.095,30.848c21.392,68.692-4.179,242.343-204.227,196.59L8.64,223.948z"
p=Path2D.new()
p:setSvgPath(banana) --Set the path from a SVG path description
p:setLineThickness(5) -- Outline width
p:setFillColor(0xFFFF80,0.7) --Fill color
p:setLineColor(0x404000) --Line color
p:setAnchorPosition(100,100)
stage:addChild(p)
You can find more info on their wiki: https://wiki.giderosmobile.com/index.php?search=svg&title=Special%3ASearch&go=Go&fulltext=1
They also have it as a sample project when you open gideros studio.

Python extract part of SVG to PNG

I have been doing a ton of searching but cant quite find the answer to this one.
I have a series of relatively simple SVG images. I have drawn SVG rectangles over key areas of the images that I am interested in and would now like to extract those areas as PNG images. I have no idea the best way to approach this problem.
Idea 1) Convert the whole SVG to PNG then use say PIL to crop the image down after somehow converting the SVG rect coordinates to PNG coordinates. I am starting to work towards this method now, but I am hoping there is a better, and/or easier way to do this!
I am using Python 3.7 for this.
Edit 1:
This is a screen shot of what I am looking at. The original image is SVG, I would like to extract the areas under the green rectangles as PNG images.
Edit 2:
Working from Idea 1, I have the following code that basically sets the viewBox on the SVG image to one of the green rectangles, then sets the width and height of it. From there I am using CairoSVG to export the SVG as PNG.
import cairosvg
import xml.etree.ElementTree as ET
...
with gzip.open(fileObj.filePath,'rb') as file:
svg=file.read()
svg=svg.decode('utf-8')
svgRoot=ET.fromstring(svg)
ET.register_namespace("","http://www.w3.org/2000/svg")
ET.register_namespace('xlink', "http://www.w3.org/1999/xlink")
annots = meta['annots']
for a in annots:
r = ET.fromstring(a['g'])
vb=" ".join([r.get('x'),r.get('y'),r.get('width'),r.get('height')])
svgRoot.set("viewBox",vb)
svgRoot.set("width",'128px')
svgRoot.set("height",'128px')
svg = ET.tostring(svgRoot, encoding="unicode")
cairosvg.svg2png(svg,write_to="/home/test.png")
Unfortunately it is EXTREMELY slow! On the order of more than a minute to extract two PNGs. The SVG files are quite large (2 - 3 mb zipped) and very detailed. I am not certain how CairoSVG works, but does it render everything in the SVG even if it isnt visible before saving the visible part to PNG?
Any advise on optimising or speeding this up would be a huge help.
This worked for me in the end, though it is quite slow on larger SVG images:
import gzip
import cairosvg
import xml.etree.ElementTree as ET
...
with gzip.open(fileObj.filePath,'rb') as file:
svg=file.read()
svg=svg.decode('utf-8')
svgRoot=ET.fromstring(svg)
ET.register_namespace("","http://www.w3.org/2000/svg")
ET.register_namespace('xlink', "http://www.w3.org/1999/xlink")
annots = meta['annots']
for a in annots:
r = ET.fromstring(a['g'])
vb=" ".join([r.get('x'),r.get('y'),r.get('width'),r.get('height')])
svgRoot.set("viewBox",vb)
svgRoot.set("width",'128px')
svgRoot.set("height",'128px')
svg = ET.tostring(svgRoot, encoding="unicode")
cairosvg.svg2png(svg,write_to="/home/test.png")

Converting PDF/SVG to PNG with antialias off using Magick++

I've tried to convert a SVG file to PNG with antialiasing off in Magick++ but I wasn't successful. But I was able to convert the SVG file to PDF with another program and the use the ImageMagick convert command to convert the PDF file to PNG.
How can I use ImageMagick to do it? The command I use for converting PDF to PNG is this:
convert +antialias -interpolate Nearest -filter point -resize 1000x1000 "img.pdf" PNG24:"filter.png"
Is there any way to use Magick++ to do that or better, convert SVG to PNG directly with antialiasing off?
Thanks in advance.
Edit:
The answer given in this post doesn't work for me. Possible because I'm using a colored SVG instead of 1-bit alpha channel. Also I mentioned in my question that I'm also looking for a way to do this in Magick++.
Magick++ has the Magick::Image::textAntiAlias & Magick::Image::strokeAntiAlias methods available, but they would only be useful if your parsing the SVG and rebuilding the image (i.e. roll-your-own SVG engine) one SVG element at a time.
As #ccprog pointed out in the comments; once the decoder utility rasters the vectors, the damage is done & setting the flags would not have an effect on the resulting resize.
Without seeing the SVG, I can only speculate what the problem is. I would suggest setting the document size before reading the SVG content.
For example, read the image at a smaller size than resample up.
Magick::Image img;
img.size(Magick::Geometry(100, 100)); // Decode to a small context
img.read("input.svg");
img.interpolate(Magick::NearestInterpolatePixel);
img.filterType(Magick::PointFilter);
img.resize(Magick::Geometry(600, 600));
img.write("PNG24:output#100x100.png");
Or render at larger size then the finial image.
Magick::Image img;
img.size(Magick::Geometry(1000, 1000)); // Decode to a larger context
img.read("input.svg");
img.interpolate(Magick::NearestInterpolatePixel);
img.filterType(Magick::PointFilter);
img.resize(Magick::Geometry(600, 600));
img.write("PNG24:output#1000x1000.png");
Update from comments
For Postscript (PDF) & True-Type antialiasing, you would set Magick::Image::textAntiAlias (or Magick::Image::antiAlias if using IM6) to false. Just ensure that the density is set to allow any overhead.
Magick::Image img;
img.density(Magick::Point(300));
if (MagickLibVersion < 0x700) {
img.antiAlias(false);
} else {
img.textAntiAlias(false);
}
img.interpolate(Magick::NearestInterpolatePixel);
img.filterType(Magick::PointFilter);
img.read("input.pdf");
img.resize(Magick::Geometry(1000, 1000));
img.write("PNG24:output.png");

how to draw shapes in a PDF using NodeJS

I have some existing PDF files and what I want is to highlight some content by overlaying circles or straight lines. I've looked at some NodeJS PDF libraries but couldn't find a solution (some libraries allow creating a PDF from scratch and draw into it; other libraries can modify existing PDFs, but do not support drawing).
A (Linux / OSX) command line solution (e.g. using ImageMagick or some other library) would be perfectly fine, too.
Edit I've since found out that with Image/GraphicsMagick I can in fact do sth. like gm convert -draw "rectangle 20,20 150,100" xxx.pdf[7] xxx2.pdf, but this (1) either draws on all pages or else only on a single one, but then the resulting PDF will only contain that page; (2) the output PDF will contain a bitmap image where I would prefer a PDF with text content.
Edit I've just found HummusJS which is a NodeJS library to manipulate PDF files via declarative JSON objects. Unfortunately, apart from the scrace documentation, the unwieldy API (see below), the tests fail consistently and across the board with Unable to create PDF file, make sure that output file target is available.
completely OT not sure what it is that makes people think such utterly obfuscated APIs are better than simple ones:
var settings = {modifiedFilePath:'./output/BasicJPGImagesTestPageModified.pdf'}
var pdfWriter = hummus.createWriterToModify('./TestMaterials/BasicJPGImagesTest.PDF',settings);
var pageModifier = new hummus.PDFPageModifier(pdfWriter,0);
pageModifier.startContext().getContext().writeText('Test Text', ...
...
var copyingContext = inPDFWriter.createPDFCopyingContextForModifiedFile();
var thirdPageID = copyingContext.getSourceDocumentParser().getPageObjectID(2);
var thirdPageObject = copyingContext.getSourceDocumentParser().parsePage(2).getDictionary().toJSObject();
var objectsContext = inPDFWriter.getObjectsContext();
objectsContext.startModifiedIndirectObject(thirdPageID);
var modifiedPageObject = inPDFWriter.getObjectsContext().startDictionary();
A couple of helpers with HummusJS, to assist with what you are trying to do:
Adding content to existing pages - https://github.com/galkahana/HummusJS/wiki/Modification#adding-content-to-existing-pages
Draw shapes - https://github.com/galkahana/HummusJS/wiki/Show-primitives
using both, this is how to add a circle at 'centerx,centery' with 'radius' and border width of 1, to the first page of 'myFile.pdf'. The end result, in this case will be placed in 'modifiedCopy.pdf':
var pdfWriter = hummus.createWriterToModify(
'myfile.pdf',
{modifiedFilePath:'modifiedCopy.pdf'});
var pageModifier = new hummus.PDFPageModifier(pdfWriter,0);
var cxt = pageModifier.startContext().getContext();
cxt.drawCircle(
centerX,
centerY,
radius,
{
type:stroke,
width:1,
color:'black'
});
pageModifier.endContext().writePage();
pdfWriter.end();
General documentation - https://github.com/galkahana/HummusJS/wiki
If the tests fail, check that an "output" folder exists next to the script being executed, and that there are permissions to write there.

Resources