Back to Website
Kerri

Building Font Imager

Posted January 19, 2017 at 4:37 PM by Kerri

Posted in: Antelle

Tags:

Building Font Imager

Introduction

Hi, my Name's Kerri. I'm the brains behind the app you've most likely just come from and if you haven't you can find it [here]. I've called it 'Font Imager' for lack of a better name (I'm a programmer, not a product designer), but before I discuss how I built it, let me introduce you to why I built it.

Font Imager began as a solution for my boss, Tony, who was tired of browsing for sites like fa2png.io and downloading individual icon after icon in the same size and colour, he wanted a quick easy solution to get all the icons he could ever need in just a few quick seconds so he tasked me, an apprentice at the company, to come up with it. The final solution operates in a familiar wizard like fashion presenting each stage of the process step by step using floating panels and modals to convey each step to the user. The modals include narrative headers to explain and detail features of the wizard as well as a series of buttons to navigate and interact with the wizard throughout the process.

The App explained

The first step is where you decide what font you'd like to use to generate your icon set, simply click on the one you want and the wizard begins. Step 2 is the first modal, it introduces a series of form controls and a live preview of the first icon in the set. Two colour pickers will let you change the colour of the foreground (icon itself) and the background of the final icon, and two slider controls of my own devising inspired by the one seen on fa2png.io will let you change the size of the icon and the amount of space from the edge (margin) there will be between it and the icon. Pressing the 'Next' button will progress you to the third and final modal. A compact list of all the icons roughly how they'll appear in your downloaded package, each one with a little green tick in the bottom left corner representing that it has been selected for download. The user can use this panel to select any or all of the icons they want to download in their final package, downloaded by clicking the 'Download' button. Clicking off the modals will reset the wizard, along with the 'Cancel' buttons, and step 3 includes a button to take you back to the previous step. That's the app, so how did I build it?

Planning

I was maybe a little over-zealous in my ability during the planning stage, I told Tony I could get it built in 2 weeks with testing and whilst I did take just a little over that time there were various elements of my plan that immediately needed to be changed. For now I'll just say this. SVG fonts need standards. Anyway, the plan was simple:

  1. Get glyph data from the font's SVG file.
  2. Render glyph data to a HTML5 Canvas element via Javascript.
  3. Use the canvas' toDataURI function to send the images to the server.
  4. Use the server to package all the images sent into a .zip file and send that to the client.

So what went wrong?

Building

Fun with fonts

I started out working just with FontAwesome. At first I tried loading it through a CDN, but this introduced the first problem, SVG files couldn't be read by the client if downloaded via a CDN due to COR (Cross-Origin-Request) restrictions, meaning I now had to store all svg files on the server. No big deal, it just makes adding new fonts a manual process and prevents me from automating it for custom font requests.

Once I got it on the server I got to work, fortunately it contained all the metadata I needed, icon offsets, unicode attributes and even glyph names! But it was one of the better fonts and I managed to get the canvas responding to it reasonably well and that was fine until I added Glyphicons Halflings. Glyphicons Halflings was not as nice, there were no glyph names and the icon offsets were all over the place, however it did have a unicode attribute. Let me reiterate, SVG fonts need standards. 

At this point I went and downloaded a few other open source font sets to try including Devicons, Iconic and Weather Icons. After checking out the SVG files for all of them I found the only consistent attribute was the unicode attribute, and with that I found my solution. I quickly went back to all the font zips I'd downloaded and added in the CSS files to the server, using the unicode attribute of the glyphs I could get the canvas to render the character using the font from CSS instead of using the glyph from the SVG data itself. 

Once I finally got the canvas to render several different icons from several different fonts I was happy and built a simple interface, the first panel let you pick the font you wanted and the second one would let you change the foreground, background, size and margin and then provide you with a download link. I built the interface with bootstrap and jquery to handle UI because it was quick and easy and I gave myself 2 weeks. But the app wasn't complete yet the download link didn't do anything.

Help me with this zipper would ya?

I set up the server to run using .NET MVC 5, this let me easily handle post requests and models. I set up the packaged font as a model, binding the image data and size data from the form and attempted to use the in built zip options to build a zip file from memory. Handling the images using MemoryStreams wasn't a problem but trying to get the inbuilt zip working was a nightmare, I took to google and found DotNetZip which was the solution. DotNetZip works using MemoryStreams too and I was able to get a zip working. The only problem know was that I couldn't download a file via ajax, doh! 

This took a bit more time to figure out because I didn't know where I needed to look for answers, I tried to split the request up between a Get and Post request so that I could immediately redirect the Post to a Get and download the file that way, it didn't work. But after I found a few people online who had the same issues I was introduced to the 'content-disposition' HTTP header, this header let's the browser know that the request should result in a file download. Using this header I was able to append the zip file from memory, give it a name and a content-type of 'application/zip' and then I was able to dispose of everything before the request ended. 

Problem solved...

Revisions

Round One

Or so I thought. I'll admit the design was crude and there was no narrative to guide you through the process but it worked. I refactored the client code first, creating a module for the packaging solution that built the canvas elements and sent the resulting image data to the server, and created a widget for my bound sliders, so called because they bound a range input control with a number input control providing the best of both. This code was reviewed and the review fortunately left me with very few things to correct, however Tony wanted the option to select which icons from the font to download. Okay, I thought, I'll just render all the icons below the control and update every time the generate button is pressed. This solution worked, there were issues with styling as the canvas element can't contain psuedo ::before or ::after elements but this was resolved by wrapping the elements in an <a> tag (I felt this was semantically best for the purpose, though a div would have worked just as well). The changes went through fine, nothing needed changing on the server and it was published to a test environment.

Round Two

Upon considering it's potential for external Use Tony came back to me with some desired improvements. He took note of how the process was three steps and could better be presented through the final Wizard like interface, asked me to add some narrative to it so that a user could better understand the interface they were presented with and also wanted some company branding added in. These changes took about an afternoon to implement, most of them were further refactoring the way my IconPackager worked, now it's extensible so if I wanted to add more steps in it would be easy. Then the rest was markup. One problem that had occurred that was missed up until this point was some missing images from the color picker I had decided to use, my fault, I forgot to add them to the project in source control so they were only available on my local machine. This error resulted in my choice of color picker being questioned, but I clarified the issue and assured Tony I felt it was the best available pre-packaged tool for the job. This revision also included persisting the colour choices through cookies so that someone who frequently needs to use the same colour will be able to get it quickly without trying to remember what colour it was they'd used.

The end result of this revision is what's gone up. It appears to be performant enough for external use as well providing a much clearer interface.

Thoughts for the Future

It's possible that I will revisit this app simply because there are ways to improve the interface further (A redesign without bootstrap that's fully responsive would be nice), as well as server responsiveness. Server responsiveness could be improved by caching packages in a database allowing the users to download them faster (Right now it can take several seconds to respond which isn't ideal, though is expected). 

I would also like to find a way to have users download their images by providing a .zip file of their own font, or a url to a hosted font solution but with COR restrictions and lack of hosting solution for custom zip files this is a problem for a much later day. However, if SVG fonts ever start conforming to a standard programmatically generating the glyphs without the font files could help with this solution greatly.

In conclusion

This project was a lot of fun to develop, I learnt a lot of things about handling data in memory as well as handling SVG data via javascript. It resulted in a working project and I didn't take much longer than I expected I would and so far there have been no major issues with the solution.

Thanks for the read.

- Kerri

Credits

https://github.com/itsjavi/bootstrap-colorpicker/ For the colour picker used.
https://bootswatch.com For the bootstrap theme (Superhero)
and http://fa2png.io for the bound slider idea and introducing the problem Font Imager solves.

Kerri

A search for words, fonts and icons.

Posted January 4, 2017 at 11:10 AM by Kerri

Posted in: Antelle

Hey there! 

It has been a couple of months since my inaugural entry to this blog and since then I have accomplished a couple of things.  I've approached Web APIs and MVC in asp.net, which I was able to use to program a wordsearch generator and I've most recently I was tasked with creating a web service to turn an icon webfont into a somewhat customisable package of png images.

The Search for Words

The wordsearch was admittedly more difficult than I had initially suspected it to be, I learnt a lot about how MVC works in .NET through it's development however and found it to be an enjoyable experience. I approached it by detailing the logic with pen and paper, logic for which had to meet specific requirements. "Words need a minimum length", "Words can only intersect one other word." and "Words should be reversible, a percentage of which is defined by the user.".

I got most of the logic working with my initial attempt, but was unhappy with the presentation of it and the lack of flexibility. I was using the razor syntax to write the wordsearch to the page in a table, and using CSS3 psuedo-elements to draw the solution. This method wasn't printable and couldn't be easily configured into an actual game. Behind the scenes a lot of the code was basic, immutable and bulky so I revised it by attempting a more object oriented approach by handling state changes of objects through the controller allowing the controller to handle just the logic. I will admit this level of detail was likely unnecessary and potentially overkill, however it taught me a lot about how a more complex C# application could be written.

For the presentation of this view I opted to use the HTML5 canvas element and javascript populated by the razer syntax in the view so that I could create a more dynamic and reliable representation of the wordsearch on the page.

Font Imager? Icon Packager? Webfont Icon Generator?

You can read more about this here http://blog.antelle.com/post/building-font-imager 

Overall...

I'm happy with the progress I've been making. I'm looking forward to becoming a certified developer at some point, headway on which I am planning to make as soon as possible and I'm hoping my future tasks will provide just as many opportunities to discover something new like Attribute Routing for Web APIs or HTTP headers as a solution to a problem.

 

Thanks for reading,

 

- Kerri

Kerri

An Introductive Summary

Posted October 25, 2016 at 9:36 AM by Kerri

Posted in: Antelle

Tags:

Hi, I'm Kerrigan, the newest apprentice at Antelle. 

 
Today marks my thirtieth day with the company, and my first month started me off running. Whilst I'm not particularly new to programming or website design, I am new to the microsoft stack and I spent the first two weeks knee deep in Pluralsight courses. 
 
It didn't take me too long to ask for a task to complete and so I was given one. The task in question was to provide a word search generator written in C# using the MVC framework. 
 
Now, previously being a PHP programmer, MVC wasn't a new concept to me. It's a pattern I'm familiar with, and one I've had experience with. However, the C# approach was vastly different to anything I've seen before with so many IDE shortcuts that it took a while to get used too. In an early attempt instead of passing a view in my controller, I attempted to redirect to an action and carry the model along through a get request accidentally and spent two days trying to figure out why the model wasn't serialising. Not my brightest moment. 
 
 That being said, within a couple of weeks I submitted it for my first code review and besides my consistently incorrect conventions there were very few things that needed discussing. Mostly just proper use of private, public and internal access modifiers as well as a major logic loop I'd set up as a for loop with moments where the loop iterator would decrement itself in order to take a step back. I was advised to alter the loop to use a while instead which was something past experience in other languages I'd been advised to avoid. The changes were made and the program still functions as expected. I tidied up some classes, cleaned up some functions and rewrote method comments using the C# guidelines. 
 
There's still plenty of adaptation to go yet, how and when to use delegates for example, or even just memorising all the IDE shortcuts in visual studio. It's not that I've never used an IDE before though, I've used intelligent text-editors with intellisense and CTags as well as auto-complete and snippets along with package managers, but none of them have been able to do as much as VS can at compile time to improve programming efficiency. 
 
To me it seems that the .NET package is less about traditional programming and more about following the conventions. Which, whilst well documented, are difficult to get used too when you've been a programmer in the past writing your own PHP frameworks because the existing ones don't work, are too bulky or are missing a feature you deem vital. 
 
To support my opinion I've been introduced to Umbraco. The go to .NET CMS, which unlike Wordpress offers far more in customisability when it comes to what kind of content you can produce and despite all the options available to you, it offers one of the largest groups of users who all seem to be able to integrate with each other because of the conventions that have been developed. 
 
So that's my experience so far at Antelle, despite being a programmer prior to starting my apprenticeship I believe that there's always more that can be learned and the best way to learn is by doing. Looking forward to my first exam on JS, HTML and CSS too, as those are topics that I feel I'm more than capable of but have never been certified in. 
 
 I guess this it for my first blog entry, no doubt there'll be more in future.
 
 - Kerri
Ben

Web API: A Song of Trial and Error

Posted September 1, 2016 at 1:12 PM by Ben

The Middle Man

It is easy to leap from input to output when it comes to data.  Anyone could draw a bar chart based on a spreadsheet.

When anyone does that they become the equivalent of a Web API without knowing it.  They look at what needs to presented, they trawl the spreadsheet for the correct group of information and then return to the bar chart and with that information in hand.  Only then can someone begin to work.

I've written posts about the front end of a Web Service, a variety of situations involving data, I've written about retrieving data from an API that I have then used to overlay information on a Google Map. 

You can probably see where this is going, I've used them, designed things with the use of an API in mind.  Until recently however I hadn't rolled my sleeves up and built my own.  I made many mistakes, turned out builds that were completely and hopelessly inept at their desired function.

Now, however I have something that works exactly how I want, something that can do the whole range of Create, Read, Update and Delete (CRUD).

Lessons Learned

The Big One

Direction is really important, it's no use setting out to plan and build a Web API without being at least 95% sure of what it needs to do for you and anything that interacts with it.

I'm close to doubling down and posting a multi-part overview of the project this API was built for.  The mistakes and successes of that work came very much so due to initially having a poor idea of what I wanted and needed to build, that was the entire project and not just the API.

By meticulously planning out the source of your data and what the final product of your API needs to be you can save yourself an astounding amount of pain and frustration, never mind the dozens of StackOverflow visits every hour when your API isn't behaving the way you need it to.

Mistakes are going to happen. Embrace them.

At various points throughout building this API I made amateur-hour mistakes.  The code review was full of disbelief at some of the poor approaches I took to achieving the desired effect within Controllers or Models in the API.

At the time, these methods were operational, they definitely worked how I wanted it to do things, looking back now I can shake my head in disbelief as well. Refactoring code is hard when you don't know better ways to approach things and more often than not the best way to learn is to fail.  You're failure is only complete when you don't learn from it.  In this case it wasn't a case of a slight misuse of a method that could have been solved another way, it was a complete failure to understand manipulating data in a correct and clear way.

On to the good stuff


This Web API takes HTTP calls to do it's work.  POST, GET, PUT and DELETE. These correspond to the CRUD functions.

POST sends a JSON object containing all of the information that a record holds, this object is built from user inputs to create a new record in the database.  When the JSON object arrives it maps itself to the "Update" object and it's properties and that object is then used to construct an INSERT statement in the database and write a new record.

GET retrieves a group of 15 records from a database when given a page number and delivers those back to the web page to be displayed in a table.  Each of these 15 records is added to a list of Record objects and they are then returned to the webpage as a JSON object.

PUT receives a specific record, however that record comes with new information that the API must tell the database to update with.  As with POST this JSON object is used map a new Update object that in turn is used to construct an UPDATE statement where the UpdateID of the record is passed in the WHERE clause to change that record only.

DELETE receives one thing, the UpdateID of the record the user on the front end is trying to delete, a simple DELETE statement WHERE the UpdateID is matched is deleted, this is unique across the CRUD functions of the API as doesn't require any objects to be constructed, the only thing being passed in is an int value.

Routing

With ASP.NET there is an option to change how API calls work from the front-end.  In the case of this particular API, first the API's host is listed, then the API, then the controller, then the method. Optionally the user can pass in an integer at the end of this call as data.  A final URI (Uniform Resource Identifier) would look like such: "www.examplehost.com/api/updates/delete/4".

This URI would result in the API deleting the record with an ID of 4.

Routing is controlled by the designer of the API, I could have set up those URI components in any order I wanted just to mess with people but alas such things are frowned upon.

The worst possible Analogy...

I spent far too long thinking of a way, in layman's terms of how I feel about the interaction between a Web API and the Front End it is being used by.

So, imagine if you will that the front end is a screw and the API is a screwdriver.  You have the screw you want to use and you look into your toolbox to see what screwdriver will get the job done.  If you reach in blindly and pick out the first screwdriver you find, you've got a pretty big chance that it won't work, a small chance of picking out something that will do the job adequately and an even small chance of nailing it first time.

Understand that this works both ways, if you're using a Web API that has been around for years you may need to change the screw to get the front end to work.

The better a match for your Front-End the API you use or design is the less hoops you have to go through to get to where you need to be.  It's always worth checking out alternatives to see if what you're using isn't what's right for the job.

Conclusion


It's a weird and wonderful thing to be at the stage of my training where I can be asked to visualise, plan, design and code a full web service, albeit a fair amount of poor work going into that service.  It's in equal parts a measure of how far I've come in the past year and of how much more ground there is to cover ahead as I go on from here, there are new things to learn in C# every time I try something new.  As I often repeat myself saying, it is up to me to try to cover that ground moving forward.

One full year, it's hard to think about the sheer density of things that have happened both in work and personally over that time since I first turned up to Antelle.  I'd like to think good things about that time for the most part, ignoring the flooding of our old office of course. I prefer to look back on struggling with the first HTML assignments, of getting my first web page finished just a few months later.  I think I'm settled and in a position to push myself further and start helping with other projects more, to see more of how the most complex projects are designed and planned.

Until the next time, thanks for reading.

Our Commitment

We strive to deliver a level of service that exceeds the expectations of our customers.

If you have any questions about our products or services, please do not hesitate to contact us

Microsoft Silver Partner in Application Development

Stay Connected

Antelle IT Ltd
Unit 17b
Carrs Lane
Tromode Estate
Douglas
Isle of Man
IM4 4RG

Phone: +44 (0)1624 660613
Email:info@antelle.com
skype:antelleit