0

What steps should I take in creating a placeholder in docbook xml files that will use font awesome fonts in the generated HTML output. Looking for xslt examples that use font awesome fonts in HTML output generated from DocBook.

Daniel Haley
  • 51,389
  • 6
  • 69
  • 95
Jeff Cunningham
  • 145
  • 1
  • 11
  • Actually I think the approach I want to take is create an entity file for all the unicode characters representing the awesome fonts and then reference that entity file from a DOCTYPE ENTITY statement in the xml header. Currently characters appear as a # pound symbol in the output. Guessing I need to add something to custom xsl layer that translates to correct awesome icon, not sure. Any ideas? – Jeff Cunningham May 05 '15 at 12:17
  • You also need to specify the FontAwesome font-family. To do this without changing the surrounding font, you'll end up adding a span anyway. If you use entity references, might as well put the entire markup in the entity: `<!ENTITY cake "">` (Don't forget that you also need the FontAwesome font so the font-family can be resolved.) – Daniel Haley May 05 '15 at 17:21
  • Thanks for info it is getting me closer, however the docbook validation and xsl files don't allow the i element. Appears that I will need to find which docbook element and attributes will be converted to the i element and class attribute in the HTML output by the docbook xsl, or attempt to customize the xsl. – Jeff Cunningham May 05 '15 at 22:47
  • <!ENTITY coffee "<i class='fa fa-coffee'></i>"> If I use Dec hex or html unicode the html output also contains the < and > instead of actual characters. If I use entity statement as you indicated, then the tag is empty - no fa class iinfo. – Jeff Cunningham May 06 '15 at 00:53
  • To clarify, no matter how I represent the less than and greater than symbols in the ENTITY declaration the xsl transformed html source contains < and >. If I manually change them to < and > for the i element the page is displayed correctly with awesome font. Is there a way to force the html output to contain < and >. – Jeff Cunningham May 06 '15 at 03:20
  • It wasn't clear to me what you get in the HTML when you leave the literal `<` and `>` in the declaration. Do you get any `i` element at all? Is it just the element with no `class` attribute? – Daniel Haley May 06 '15 at 15:47
  • If I leave the literal < and > in the declaration I get this in html '<i></i>' If I replace < and > with < and > in the declaration the html source looks like ' <i class='fa fa-coffee'></i>' Need literal characters to appear in HTML source and it will work fine. Also, since is not a valid docbook element, validation error appears. Need a way to pass through literal characters. Tried escaping with \ but did not work – Jeff Cunningham May 06 '15 at 17:13
  • That's because the XSLT doesn't have a template to handle the `i` element. How are you running the XSLT? What processor are you using? I have 2 possible ways to get this working. – Daniel Haley May 06 '15 at 18:17
  • If you have a xsl template that handles the i element, I could try adding it to my xsl custom layer. If not, I will attempt to create template. I am using Oxygen 16 and saxon processor, also from a command line using ant scripts and docbook webhelp xsl files. Even if a new xsl template handles i element - will < and > show up in html source. – Jeff Cunningham May 06 '15 at 18:54
  • I will go ahead and post both options as an answer. I'm testing with oxygen 15 so they should work for you. It will be a while before I can post though. – Daniel Haley May 06 '15 at 18:58

1 Answers1

3

Hopefully this answer isn't tl;dr. If you would rather me break this out into 3 separate answers, please let me know.

Option 1

The first option is to use the HTML markup in the entity declaration like I first mentioned in the comments.

Pros

  • XSLT 1.0 so minimal XSLT changes to docbook stylesheets

Cons

  • i html element isn't valid so you'll have validation errors in your docs
  • feels like a hack

What you'll need to do:

  1. Change your entity declarations to look like this:

    <!ENTITY fa-birthday-cake "<i class='fa fa-birthday-cake' xmlns=''></i>">
    

    This is slightly different from what I had in my first comment. I added an empty namespace so that the i element wasn't automatically in the default namespace.

  2. Add the link to the font-awesome css in the head. (I have it pointing to font-awesome locally.)

    <link rel="stylesheet" href="font-awesome-4.3.0/css/font-awesome.min.css"/>
    

    For testing I modified frameworks/docbook/xsl/html/profile-docbook.xsl. I added the link around line 460 in the match="*" mode="process.root" template.

  3. Add the template to match the i element so it doesn't get replaced.

    <xsl:template match="i">
        <xsl:copy-of select="."/>
    </xsl:template>
    

Example...

Docbook Input

<!DOCTYPE section [
<!ENTITY fa-birthday-cake "<i class='fa fa-birthday-cake' xmlns=''></i>">
]>
<section xmlns="http://docbook.org/ns/docbook" version="5.0">
    <title>Section Template Title</title>
    <para>birthday cake: &fa-birthday-cake;</para>
</section>

HTML Output (using DocBook HTML transformation scenario)

<html>
    <head>
        <meta http-equiv="Content-Type" content="text/html; charset=ISO-8859-1">
        <link rel="stylesheet" href="font-awesome-4.3.0/css/font-awesome.min.css">
        <title>Section Template Title</title>
        <meta name="generator" content="DocBook XSL Stylesheets V1.78.1">
    </head>
    <body bgcolor="white" text="black" link="#0000FF" vlink="#840084" alink="#0000FF">
        <div class="section">
            <div class="titlepage">
                <div>
                    <div>
                        <h2 class="title" style="clear: both">
                        <a name="d56e3"></a>Section Template Title</h2>
                    </div>
                </div>
                <div></div>
                <hr>
            </div>
            <p>birthday cake: <i class='fa fa-birthday-cake'></i></p>
        </div>
    </body>
</html>

Rendererd HTML

Rendered HTML

Option 2

The second option is to use font awesome class in the entity declaration and use the symbol element, with a special role attribute, to hold the reference.

Pros

  • XSLT 1.0 so minimal XSLT changes to docbook stylesheets
  • symbol is a DocBook element so you shouldn't have validation issues

Cons

  • symbol might not be available in all of the places you need to use a font-awesome icon
  • feels like a hacky use of symbol (probably not as hacky as the first option though)

What you'll need to do:

  1. Change your entity declarations to look like this:

    <!ENTITY fa-birthday-cake "fa-birthday-cake">
    
  2. Add the link to the font-awesome css in the head. (I have it pointing to font-awesome locally.)

    <link rel="stylesheet" href="font-awesome-4.3.0/css/font-awesome.min.css"/>
    

    For testing I modified frameworks/docbook/xsl/html/profile-docbook.xsl. I added the link around line 460 in the match="*" mode="process.root" template.

  3. Add the template to match the symbol element with the 'fa' role and output the i. (d is bound to the http://docbook.org/ns/docbook namespace in profile-docbook.xsl)

    <xsl:template match="d:symbol[@role='fa']">
        <i class="fa {.}"></i>
    </xsl:template>
    

Example...

Docbook Input

<!DOCTYPE section [
<!ENTITY fa-birthday-cake "fa-birthday-cake">
]>
<section xmlns="http://docbook.org/ns/docbook" version="5.0">
    <title>Section Template Title</title>
    <para>birthday cake: <symbol role="fa">&fa-birthday-cake;</symbol></para>
</section>

HTML Output (using DocBook HTML transformation scenario)

<html>
    <head>
        <meta http-equiv="Content-Type" content="text/html; charset=ISO-8859-1">
        <link rel="stylesheet" href="font-awesome-4.3.0/css/font-awesome.min.css">
        <title>Section Template Title</title>
        <meta name="generator" content="DocBook XSL Stylesheets V1.78.1">
    </head>
    <body bgcolor="white" text="black" link="#0000FF" vlink="#840084" alink="#0000FF">
        <div class="section">
            <div class="titlepage">
                <div>
                    <div>
                        <h2 class="title" style="clear: both">
                        <a name="d56e3"></a>Section Template Title</h2>
                    </div>
                </div>
                <div></div>
                <hr>
            </div>
            <p>birthday cake: <i class='fa fa-birthday-cake'></i></p>
        </div>
    </body>
</html>

Rendererd HTML

Rendered HTML

Option 3

The third option is to switch to XSLT 2.0 and use an xsl:character-map.

Pros

  • Simple concept
  • No additional markup needed in the docbook instance or in the entity declarations
  • Feels good (not hacky)

Cons

  • XSLT 2.0 so will need to use a 2.0 processor
  • There might be additional XSLT changes after changing to the 2.0 processor. (For example, in my testing I had to remove 3 exslt:node-set() uses in profile-docbook.xsl.)

What you'll need to do:

  1. Keep your entity declarations looking like this (based on your other question https://stackoverflow.com/questions/30055181/how-do-i-insert-fonts-as-an-entity-in-docbook):

    <!ENTITY fa-birthday-cake "&#xf1fd;">
    
  2. Add the link to the font-awesome css in the head. (I have it pointing to font-awesome locally.)

    <link rel="stylesheet" href="font-awesome-4.3.0/css/font-awesome.min.css"/>
    

    For testing I modified frameworks/docbook/xsl/html/profile-docbook.xsl. I added the link around line 460 in the match="*" mode="process.root" template.

  3. Change the xsl:stylesheet version to 2.0.

  4. Import the xsl:character-map.

    <xsl:include href="font-awesome.xsl"/>
    

    I've included an example "font-awesome.xsl". I have the complete version based on the font-awesome cheatsheet today (2015-05-06). Adding the entire contents pushes my answer over the character limit; let me know if you need it.

    <xsl:stylesheet version="2.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
        <xsl:character-map name="fa">
            <xsl:output-character string="&lt;i class='fa fa-birthday-cake'&gt;&lt;/i&gt;" character="&#xf1fd;"/>
        </xsl:character-map>
    </xsl:stylesheet>
    
  5. Reference the character map (with use-character-maps) in the xsl:output.

    <xsl:output method="html" encoding="ISO-8859-1" indent="no" use-character-maps="fa"/>
    
  6. Possible additional changes.

    Like mentioned in the "cons" section, you might need to make some changes to the docbook stylesheets depending on what processor you use. I used Saxon-HE 9.5.1.3. I did this by duplicating the DocBook HTML transformation scenario and changing the processor.

Example...

Docbook Input

<!DOCTYPE section [
<!ENTITY fa-birthday-cake "&#xf1fd;">
]>
<section xmlns="http://docbook.org/ns/docbook" version="5.0">
    <title>Section Template Title</title>
    <para>birthday cake: &fa-birthday-cake;</para>
</section>

HTML Output (using the modified DocBook HTML transformation scenario)

<html>
    <head>
        <meta http-equiv="Content-Type" content="text/html; charset=ISO-8859-1">
        <link rel="stylesheet" href="font-awesome-4.3.0/css/font-awesome.min.css">
        <title>Section Template Title</title>
        <meta name="generator" content="DocBook XSL Stylesheets V1.78.1">
    </head>
    <body bgcolor="white" text="black" link="#0000FF" vlink="#840084" alink="#0000FF">
        <div class="section">
            <div class="titlepage">
                <div>
                    <div>
                        <h2 class="title" style="clear: both">
                        <a name="d56e3"></a>Section Template Title</h2>
                    </div>
                </div>
                <div></div>
                <hr>
            </div>
            <p>birthday cake: <i class='fa fa-birthday-cake'></i></p>
        </div>
    </body>
</html>

Rendererd HTML

Rendered HTML

Daniel Haley
  • 51,389
  • 6
  • 69
  • 95
  • This sounds great. I think this is something a lot company pubs depts could use. I would like a copy of your font-awesome.xsl file. I also experimented with option 2 using the docbook literal element, but decided it too may have limitations on where it could be used. I will try this out tomorrow. Thanks once again. – Jeff Cunningham May 07 '15 at 01:18
  • @JeffCunningham - If you send me an email (dhaley77 gmail) I will send it to you. – Daniel Haley May 07 '15 at 01:24
  • Ok sent you email from jeffcc. Thanks – Jeff Cunningham May 07 '15 at 01:47
  • I had to put a space before the closing tag in the entity. If not there the html source leaves off the closing tag as puts in /> empty tag closure format. This causes multiple icons to appear in browser if is missing. – Jeff Cunningham May 07 '15 at 03:06
  • @JeffCunningham - That shouldn't happen if the `xsl:output` method is html. Make sure you don't have any `xsl:output`'s that have a different method (or are missing a method attribute; the default is xml). Glad you have a workaround though; the offending `xsl:output` might actually be in the docbook xslt. – Daniel Haley May 07 '15 at 03:12
  • I noticed that using the span tag rather than the i tag in option one renders like it should in a browser when the tag is closed as an empty tag in the html source.. – Jeff Cunningham May 08 '15 at 17:12
  • For option one, If I use the literallayout element in the entity declaration and assign fa fa-beer to the role attribute and code in xsl to generate a custom class name `` `` `` I get this in html source `

    ` If I use markup element in entity declaration and role attribute set to font then class name does not change in html source ``

    – Jeff Cunningham May 08 '15 at 18:52
  • 1
    This is what I am going with for now until we move to an XSLT 2.0 processor. Entity declaration: `<!ENTITY fa-beer "">` In xml file: `&fa-beer; Assistant` In xsl file: `` ` ` `` HTML output: `` – Jeff Cunningham May 08 '15 at 19:54
  • Made slight change to first line in xsl match statement to match role attribute that contains fa- hopefully this will not interfere with other uses of the markup element. `` – Jeff Cunningham May 08 '15 at 21:27
  • @JeffCunningham - Something that might be a little safer is to add another role to `markup`. Something super unique; something like `` and then update your match: `d:markup[contains(@role, '[jcunningham]')]`. Then you can just strip out the extra role when you build your `span`. Something like: `normalize-space(substring-before(@role,'jcunningham'))`. – Daniel Haley May 08 '15 at 21:44
  • That was supposed to be `normalize-space(substring-before(@role,'[jcunningham]'))` – Daniel Haley May 09 '15 at 02:16
  • If possible, my next step is to have awesome fonts to appear in generated pdf output. I am reading up on how to add a font to the Apache FOP processor and adding statements to xsl custom layer, any thoughts on this subject would be appreciated. – Jeff Cunningham May 11 '15 at 00:21