📜 ⬆️ ⬇️

Generate pictures for Android applications from SVG

Introduction


Hello. Not so long ago, I decided to try myself as a developer under the well-known Android platform. On the way of development, I ran into many problems and one of them was the creation of images for different screen resolutions. At first I tried to create pictures in PhotoShop, but it turned out to be a chore, you need to save 5 changes in any resolutions at any change, and I am not the genius of this program. And then the thought occurred to me to draw pictures in SVG, before that I had experience with this format, I did visualization for cursors on queuing systems (emoticons ran to the cashier and sometimes accumulated in the queue).

Implementation


So, in Sublime Text I put the necessary pictures, everything is great and scalable, but it’s like to save these SVGs in different resolutions automatically. For Java, there are libraries that convert this kind of graphics, but it turns out they don’t understand, for example, the <use> tags. As a result, I thought and decided to write an application myself that would save everything in the necessary permissions according to its folders. The language my choice fell on is JavaScript and the Node.js platform, since I have a primary employment with them.

I started by searching for a module for Node.js, which could convert SVG to regular PNG images, but the modules found saved the image only in the resolution specified by the width and height parameters of the image. Having spat, I decided to write everything myself. A small list of tasks assigned to the application:


')
Client part

The following is the algorithm of the client part.

  1. Download information about the images and the permissions into which they need to be converted.
  2. We load the original SVG in the img tag and hang on the load event.
  3. After loading, draw this image on the <canvas> element
  4. Pull the image from <canvas> in text format by calling the function toDataURL ('image / png')
  5. We send the text of the picture to the server and, if we still have something to convert, go back to step 2 or 3, depending on whether you need to upload another picture, otherwise close the page


(function() { var DRAWABLE_RES_TYPES = [ 'ldpi', 'mdpi', 'hdpi', 'xhdpi', 'xxhdpi' ] , SVG_IMAGE_NAME_REGEXP = /[.*]*\/([^\/]+)\.svg$/i; var canvas = document.querySelector('canvas') , context = canvas.getContext('2d') , image = document.getElementById('origin_image'); sendMsg('get_image_list', {}, function (imageInfo) { imageInfo.forEachAsync(function(group, callback) { group.imageList.forEachAsync(function(imagePath, callback) { var imageName = imagePath.match(SVG_IMAGE_NAME_REGEXP)[1]; image.setAttribute('src', imagePath); image.addEventListener('load', function () { image.removeEventListener('load', arguments.callee); DRAWABLE_RES_TYPES.forEachAsync(function(drawableType, callback) { if (!(drawableType in group.sizes)) return callback(); canvas.width = group.sizes[drawableType].width; canvas.height = group.sizes[drawableType].height; context.drawImage(image, 0, 0, canvas.width, canvas.height); sendMsg('save_image', { name: imageName, type: drawableType, body: canvas.toDataURL('image/png') }, callback); }, callback); }); }, callback); }, function() { sendMsg('all_is_done', {}, function() { window.close(); }); }); }); })(); 

Secondary functions
 function sendMsg(action, data, callback) { var xhr = new XMLHttpRequest(); xhr.open('POST', 'http://127.0.0.1:1337/' + action, true); xhr.addEventListener('readystatechange', function () { if (xhr.readyState === xhr.DONE) { if (xhr.status === 200) { try { var parsedAnswer = JSON.parse(xhr.responseText); } catch (e) { console.log(e); } callback(parsedAnswer); } else console.log('Error with code ' + xhr.status + ': ' + xhr.statusText); } }); xhr.send(JSON.stringify(data)); } Array.prototype.forEachAsync = function (handler, callback) { var self = this , index = -1 , tempCallback = function () { index++; if (self.length === index) callback && callback(); else handler(self[index], tempCallback); }; tempCallback(); } 


Server part

Everything is clear, the usual server on Node.js, which responds to client requests and saves pictures that came from the client. Attention, in my opinion, only the image saving function deserves, the rest can be viewed on GitHub, the link to the repository will be posted below. So the save function looks like this:

 function saveImage(options, callback) { var regex = /^data:.+\/(.+);base64,(.*)$/ , matches = options.body.match(regex) , imageExt = matches[1] , imageBody = new Buffer(matches[2], 'base64') , imagePath = config.outputFolder + '\\drawable-' + options.type + '\\' + options.name + '.' + imageExt; if (!fs.existsSync(config.outputFolder + '\\drawable-' + options.type)) fs.mkdirSync(config.outputFolder + '\\drawable-' + options.type); fs.writeFile(imagePath, imageBody, function(err) { if (err) throw err; var msg = 'File ' + imagePath + ' is created.'; console.log(msg); callback(err, { msg: msg }); }); } 


It's all quite simple, in the options parameter come the parameters from the client, containing the text of the picture, the name and type of resources for Android. Data is processed and written to a file. On the server side, the fs, http module and one third-party module called open is used , which provides a function for opening a link in the default browser.

config.json

This file contains basic information. Server information with port and hostname fields. Path to the folder with the application resources for which you want to generate images. And of course, information about SVG, their path and size for different resolutions. For an example, there is only one object in which a list of files is placed, permission information and a description that is not used anywhere, but serves merely as a convenience. If other images need their own resolution, we simply create a new object in the input data. Example:

 { "server": { "port": 1337, "hostname": "127.0.0.1" }, "input": [{ "description": "Action bar icons", "imageList": [ "/svg/ic_bar_add.svg", "/svg/other_picture.scg" ], "sizes": { "ldpi": { "width": 24, "height": 24 }, "mdpi": { "width": 32, "height": 32 }, "hdpi": { "width": 48, "height": 48 }, "xhdpi": { "width": 64, "height": 64 }, "xxhdpi": { "width": 96, "height": 96 } } }], "outputFolder": "C:\\svg-android\\out" } 


Conclusion


The project can be downloaded from GitHub at this link .

To start, enter the following line in the console:
node index.js

Do not forget to specify the folder in which you want to save the images, it must exist.

Source: https://habr.com/ru/post/229395/


All Articles