In a prior post I discussed using fonts in NativeScript. This morning I saw an issue in the NativeScript github repo about how to use the Font Awesome Duotone fonts.
Since I love puzzles, and I know the NativeScript font system fairly well, and I also own a subscription to the commercial Font Awesome -- I figured I could easily see if I can make it work. Immediately, I had a idea on how to solve the issue.
So I downloaded both the WebFonts and the Desktop fonts. Interestingly, enough the Desktop fonts actually have a warning that links to a post about Duotone fonts and the issues with the desktop... So after I read that post; it basically confirmed my understanding on how this font works and exactly how to make this work inside NativeScript.
So first things first, you need to update your app.css file; you want to add something like this to it:
.fad { font-family: fa-duotone-900, Font Awesome 5 Duotone; font-weight: 900; } .fad-op { opacity: .4; }
Why TWO names for font-family?
If you recall from my prior fonter post; you want to create the rule that has the name of the actual font file without the .TTF or .OTF extension. So since the font folder looks like this:
The last file in the example fonts folder, is fa-duotone-900.ttf; straight from the web download zip file that I downloaded this morning.
So you see the rule is:
font-family: fa-duotone-900, Font Awesome 5 Duotone;
This allows NativeScript to find the actual file name, in a lot of cases it can use just the filename to work its magic. However on iOS specifically, we want to include the actual internal font name. So double clicking on the font gives us this image under linux.
Under macintosh the title bar also shows the exact same information. So that is why I then typed "Font Awesome 5 Duotone" as the second part of the rule -- this must EXACTLY match. The two names covers all the cases we need, font file name to load it and actual internal font name to reference it later.
The second css rule we just added was fad-op, it is so that we can have some opacity for which ever section of the font we want or need opacity on.
We can do no opacity, solid/solid, and it would look like this:
Notice both parts are solid.
Now if we add opacity to section two; you can see the beak, legs on the bird are slightly transparent; and the acorn body is partially transparent.
Finally if we reverse the opacity to make section one have the opacity it looks like this:
You can also apply any opacity you want like 0.10 looks like this
And you can apply opacity to both sections and it looks like this:
All of them are using the EXACT same colors; just a difference in opacity. The sky is the limit; you are free to color and apply opacity on whatever amounts to each of the two sections.
So how do we do this magic?
Well technically the hard part is already done, that awesome css rule and dropping the font file into the app/fonts folder has 99% of the work done for us. The rest is simple stuff.
So the next thing you need to know is; which duotone icons do you want to use?
So first we need to know what is available for duotone; so we go to the font awesome icon page; and select the duotone filter. Once we have that; we can choose something; so lets look at the crow.
The crow at the top of the page lists this information:
Now at this point we can't really use "fa-crow" just because NativeScript doesn't know what that is; but the two pieces of information we do need to know to make it work is the f520 and the 10f520 these are the unicode values for the two sections of the icon we want.
So our xml needs to look like this:
This gives us the two characters in the same location on the screen. The first character is using the "fad" css rule; along with the text color blue. The second character is also using the "fad" css rule, and it adds the red text color and the "fad-op" rule for opacity.
Which gives us the duotone bird:
The key item is that you want the characters to print over each other; Gridlayout by default allows you to put multiple items in the same row and column. Since the characters are the same size; they by default will automatically line up and work.
Angular Flavor Issue
Angular's HTML parser has an issue which characters in the upper unicode -- it is great with any unicode value 65535 and below. Which of course if you have seen the first character of each of these duotone values is < 0xffff (65535). However the second character starts in the 0x100000 (1048576) range which is a lot larger than 0xffff. Unfortunately this means that in Angular even though you pass in 0x10f520 you actually get 0xf520 assigned. If you are paying really close attention, this is the same value as the very first code. So in angular you get two labels with the exact same unicode value. So how do we fix this?
Well ultimately; the underlying Angular parser should be fixed -- so if one of you wants to tackle this, track down where in the parser it breaks and push a PR; that would make a lot of people in the Angular community happy! However, since this is not something I am going to tackle and I still want you to be able to use Duotone fonts, I will present an alternate method to how to fix this on Angular.
In Angular; we can do it one of two ways; automagically; or direct assign. I prefer the automagically; so this is the HTML that we have:
<Label col="1" text="" class="larger fad "></Label>
<Label col="1" text="􏚮" class="larger fad blue fad-op" (loaded)="fixAngularParsingIssue($event)"></Label>
As you can see it is identical to the above NS-Core XML, except I added the (loaded) event. This event fires once in the lifecycle of the Label. And runs my handy dandy "fixAngularParsingIssue" function.
fixAngularParsingIssue(args: any): void {
let field = args.object;
let val = field.text.codePointAt(0);
// Check to see if this is already a valid unicode string above 0xffff.
if (val > 65535) { return; }
// We found a character we need to change...
val = val | 0x100000;
field.text = String.fromCodePoint(val);
}
The function is straight forward; we grab the current text value; verify it is actually still less than the unicode value we want as the parser could get fixed in a future version. Then we take that value and add our missing 0x100000; and then re-assign the text. Now this function could instead be written to take the actual value you want to assign rather than computing it, but I prefer simplicity as then I can just add this function to all labels that need it and just not think about it.
Angular Directive
I suggested in the issue that someone create a directive; and Pandishpan spent the time and converted my simple code into a directive for Angular making it even easier to use in angular! Thank you for your awesome work! So now, all you have to do is apply the "labelDuoTone" directive to the html, and it handles the rest of the work for you.
You can grab the code for the angular directive in the github issue here.
NativeScript Core demo application
You can download the NativeScript-Core fonter application from the github repo, https://github.com/NathanaelA/fonter
Please note the fa-duotone-900.ttf font is NOT shipped with the demo; you must download it yourself and copy it into the app/fonts folder.
Android (on left) and iOS (on right) looks like this: