Home > CSS, JavaScript, OP4JS, Smart Mobile Studio > SMS fonts, once and for all, part 1

SMS fonts, once and for all, part 1

While Smart Mobile Studio makes HTML5 app development fun and enjoyable, there are aspects of HTML5 and CSS3 which quite frankly cannot be simplified without a lot of research. This has nothing to with the top-level programming language, be it Smart Pascal or Typescript, but rather how browsers are designed, their abhorrent need for legacy support for all manner of whimsical ideas over the past 15 years – and last but not least, shortcomings of CSS and Javascript combined.

One of these topics is font management, which (at the present moment is driving me insane) you could be mistaken for being an issue of the 90’s and not 2014. But you will quickly realize that despite all the advancements of HTML5 there are still huge holes in the .. eh, whole WC3 scheme of things.

Now there used to be a time when you could, anywhere in a HTML segment, simply get away with:

<font name="verdana" size="14px">Some text</font>

But alas those days are gone (and we should be happy about that), and the free-standing font tag has been marked as obsolete for quite some time.

For Smart Pascal, how you style your components is really not the issue, but rather – getting concurrent and reliable information from the browser is. You should think that something as trivial as asking what font is used for an element would be simple and straight-forward, but sadly that is not the case.

For instance, you should imagine that you could do something like this:

var mSize = element.style.fontSize; //works
var mName = element.style.fontName; //nope

The above wont even work, not even the first. In order to read a calculated style you have to access the browser’s internal workings like this:

   mObj := BrowserAPI.Document.defaultView.getComputedStyle(tagRef, Null);
   if (mObj) then
   Result := (mObj).getPropertyValue(aStyleName);

But all this aside. The point of this article is to make more sense of fonts under HTML5. As the designer of SMS up to version 1.0 I know perfectly well how much work has gone into this, and also how many aspects of SMS that still needs a polish. And font management is absolutely one of them. Not because we did anything wrong, but because coding clever font detection and/or analysis requires more work than anyone could be expected to anticipate. Smart gives you a huge advantage over raw HTML5 authoring in plain JavaScript – but it can be done better and more accurate. Which is what I am going to do now once and for all (insert sound of trumpet’s going off in the distance here).

Before we dive into some code, here are a few things you can do to alleviate the problem straight away.

Clean up the default stylesheet

At the very top of the stylesheet for your app, is a strange segment which looks like this:

* {
  //content here
}

Just like under SQL, the “*” (star) character means “all”. So whatever you define within those curly-brackets’s is automatically applied to ALL elements (read: all controls) in your app.
So this is the perfect place to clearly define the font-name you want to use in your app, and it’s size.

So if you do like this:

* {
  font-family: "Segoe UI", "Helvetica neue";
  font-color: Transparent;
}

Then the default font for the document and all the elements created by Smart will be (and this is important) either “Segoe UI” (Windows 7 and 8) or, if that is not available, “Helvetica neue” (which is the font for iPhone and iPad). You may want to add Helvetica to that list, just in case your app is running under android.

The next step is to completely remove all “font-family” declarations besides that one, from the entire CSS file. This will ensure that unless you have hard-coded a font into your app, you will be able to control font handling from your stylesheet completely. Which is very handy!

Getting the name and size of a font

Let me explain the problem clearly. When you define what font should be used, there is no guarantee that this font is indeed installed on the system running you app. This is why the CSS “font-family” accepts more than one font name. And the rule is, that if “Segoe UI” (first in list) is not installed, then the browser tries to use the next one – and so fourth.

The problem? Whenever you try to read-back what font was actually selected, well.. there are no such function (!) The only thing you can do is to read back the whole “font-family” string which contains exactly what you already know. I was hoping the computed style contained just the applied font and not the whole list, but no such luck.

So what is the solution? Well, there are JS libraries for checking if a font is installed on the system – and the only thing you can actually do about this, is to iterate through the font-family string and use the first one which is valid.

One such solution is FontUnStack, which sadly has a dependency on jQuery. Which is not something I want to ship with Smart Mobile Studio to be perfectly frank. A second solution is Font Detector which, since it has no dependencies, is the one I have re-implemented in Smart Pascal and which we will be using.

But we are still not out of the woods, because while detecting if a font is installed is all nice and dandy, we still have to make a little snippet which uses the font detector to compare against the font-family list we know. Then, and only then, will we have fixed the problem of being able to determine what font is used for an element (phew!).

But let us begin with the font detection class, which turned out very small (based on the 0.3 branch):

type

  TQTXFontDetector = Class(TObject)
  private
    FBaseFonts:     array of string;
    FtestString:    String = "mmmmmmmmmmlli";
    FtestSize:      String = '72px';
    Fh:             THandle;
    Fs:             THandle;
    FdefaultWidth:  Variant;
    FdefaultHeight: Variant;
  public
    function    Detect(aFont:String):Boolean;
    Constructor Create;virtual;
  End;

//############################################################################
// TQTXFontDetector
//############################################################################

Constructor TQTXFontDetector.Create;
var
  x:  Integer;
begin
  inherited Create;
  FBaseFonts.add('monospace');
  FBaseFonts.add('sans-serif');
  FBaseFonts.add('serif');

  Fh:=browserApi.Document.getElementsByTagName("body")[0];

  Fs:=browserApi.document.createElement("span");
  Fs.style.fontSize:=FtestSize;
  Fs.innerHTML := FtestString;
  FDefaultWidth:=TVariant.createObject;
  FDefaultHeight:=TVariant.createObject;

  if FBaseFonts.Count>0 then
  for x:=FBaseFonts.low to FBaseFonts.high do
  begin
    Fs.style.fontFamily := FbaseFonts[x];
    Fh.appendChild(Fs);
    FdefaultWidth[FbaseFonts[x]]  :=  Fs.offsetWidth;
    FdefaultHeight[FbaseFonts[x]] :=  Fs.offsetHeight;
    Fh.removeChild(Fs);
  end;
end;

function TQTXFontDetector.Detect(aFont:String):Boolean;
var
  x:  Integer;
Begin
  aFont:=trim(aFont);
  if aFont.Length>0 then
  Begin
    if FBaseFonts.Count>0 then
    for x:=FBaseFonts.low to FBaseFonts.high do
    begin
      Fs.style.fontFamily:=aFont + ',' + FbaseFonts[x];
      Fh.appendChild(Fs);
      result:= (Fs.offsetWidth  <> FdefaultWidth[FBaseFonts[x]])
          and  (Fs.offsetHeight <> FdefaultHeight[FBaseFonts[x]]);
      Fh.removeChild(Fs);
      if result then
      break;
    end;
  end;
end;

You can try this out yourself, and you will find that it absolutely works and easily detects if a font is installed on your system. So great, that’s the first part of the equation finished.

More to follow soon..

Advertisements
  1. No comments yet.
  1. No trackbacks yet.

Leave a Reply

Please log in using one of these methods to post your comment:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

%d bloggers like this: