Chapters Four and Five, Styling and Layout respectively, explore the ways in which you can manipulate the presentation of content using CSS. For the majority of e-book producers, the material covered in those chapters is sufficient for them to produce their books.
However, some producers need to present content of a more technical or structured nature, where, for example, the ability to style lists and tables is essential. This appendix covers those areas along with questions over issues such as pagination control.
Chapter Four shows initially that selectors are expressions that direct a user agent to apply their corresponding definitions to the correct elements in a given tract of HTML. That chapter then explores the use of the element, ID and class selectors, which are the simplest, and which suffice for the majority of one's requirements when producing the majority of e-books.
In fact, the CSS 3 standard specifies many more selectors, but most of these are unlikely to be useful to e-book producers. However, a few of the more-advanced selectors are of great value in certain areas, and these are the ‘descendant’, ‘:first-child’ and ‘:last-child’ selectors. This section explores the principles that underlie these, and subsequent sections cover their application in the styling of tables.
In the case of the descendant selector, Amazon's documentation is silent currently – it neither affirms nor denies support – yet tests show that it does work. The descendent selector is powerful and hence very useful, in that it allows you to apply a definition to an element according solely to its parentage. For example:
p span { color : #909090; }
This states that all <span> elements that are children of all paragraphs should have a text colour of #909090. You do not use a keyword or special symbol; you simply cite one selector after another, separating them with a space. Note that it applies even if a given <span> is an immediate child of a paragraph or a child of a child (and so on) of a paragraph. For example, you might nest one span within another, where the outer <span> is the child of a paragraph, in order to combine two forms of styling in a given word or phrase (asshown in Chapter Four).
In the case of the :first-child and :last-child selectors, Amazon's documentation is incorrect currently, as it states that Kindles do not support them when tests show that they do. These operate as you would expect, which is that they allow you to apply styling to only the first or last child of something else, although you should note that the syntax is somewhat confusing for the neophyte.
For example, the following selector:
*.Sidebar:first-child { ... }
…looks like it nominates all first children of any element that has a class-attribute value of ‘Sidebar’, when it actually stipulates the first child that is an element of class Sidebar. The equivalent principle applies to the last-child selector.
Despite their counter-intuitive syntax, these are valuable selectors as you can combine them with the descendant-selector principle so that you can address the first or last child of a particular element. Moreover, you can identify that element according to its type, class, ID, or even identify it as a descendant or first-/last-child of something else.
This gives great articulation, and the following:
table.StudentScores tr:first-child { ... }
…nominates the first table-row child of a table with an class-attribute value of StudentScores (assuming you are publishing a book on, say, educational theory). This shows that the value of these selectors flows from the fact that they allow us to avoid cluttering our HTML with a mass of tedious and verbose class and id attributes, especially when applying styling to complex, nested structures in HTML.
A final point in this section: note that if you have a construction such as the following in your HTML:
<div> <span>Lorem<span><br/> <span>Ipsum<span><br/> <span>Dolor<span><br/> </div>
…then you may attempt to target the final <span> using the following selector:
*:last-child { ... }
Given that the asterisk means ‘anything’, this will in fact select the <br/> that follows, because the user agent will see that element as the last child, even though it represents simply a break. The nature of the child elements in the <div> is of no relevance, as they could be anything, nor is the fact that the parent is a <div>, as that containing element could be anything too. What matters is that the <br/> comes last.
Yes, you would be correct in thinking that trailing <br/> is redundant (it is very easy for such things to creep in through an indiscriminate copy/paste operation), and you would also be correct in thinking that the solution is to delete that trailing <br/>.
Version Three of the CSS standard introduced the concept of ‘media queries’. These are a way of applying definitions conditionally, thus allowing you to tailor your presentational rules for specific devices. That is, people consume content on a diverse range of static and mobile devices that possess widely varying screen sizes and resolutions, along with renderers that interpret CSS rules in slightly different ways. Media queries address this by allowing you to create different rule sets for different device types, thus allowing you to deploy a web site that appears in one way on, say, the screen of a smart-phone, while appearing in a different way on a desktop machine.
Modern Kindles support media queries, which means that you can tailor your e-book deliverable such that it renders in accordance with the type of e-reader on which it finds itself. The following example gives the essential idea:
@media amzn-kf8 { ... }
In this code, the @media keyword states that a conditional block of styling definitions follows in the lines between the opening and closing brace. The amzn-kf8 part says, in effect:
If the device that is evaluating this code is a Kindle that can read KF8 file then the following definition(s) apply.
If, however, the device is not KF8-enabled, it will not evaluate the block of definitions, and so the code above allows you to publish a book that will render correctly on both an older Kindle and a more modern device. That is, you can place styling definitions before the @media statement, which will act as defaults and which the media query block will then override if the device happens to be, say, a Kindle Fire.
To use an example that Amazon gives in its documentation, you can place an SVG file in your content, and wrap it in a div. First- and second-generation Kindles do not support SVG, so you can define a background image for the div as a default, which should be a JPG version of the SVG image. The JPG will appear on those early Kindles instead of the SVG, but KF8-enabled devices will evaluate the conditional block of definitions, within which you place a definition that gives the background property of that div a value of ‘none’. This will cause the SVG rather than the JPG to appear.
However, there are disincentives to such ideas:
1.
Increased Complexity. As noted elsewhere in this text, it is best to keep things as simple as possible when it comes to CSS and HTML, and this point is far more serious than one might believe, because introducing complexity increases the probability of encountering problems at a far greater rate than intuition suggests. By using media queries, you increase the complexity of the CSS you create, and thus lumber yourself with having to reason about different conditions.
Moreover, the example above is very simple, but the media-query logic can be far more extensive (‘boolean’ conditions and the like), and so any serious foray into this area could create many complications. After all, it can be hard to get things operating correctly when working with just a single set of definitions.
2.
Harder Testing. Chapter Two, Resources, notes that you cannottrust e-reader emulators to replicate their hardware equivalents perfectly. It also notes that those equivalents are far from perfect too. These inconsistencies increase our testing burden as book producers, because the correct rendering of a book on, say, Kindle Previewer does not guarantee correctness on a hardware equivalent (or indeed Kindle for PCs). This is why the section entitled Physical E-Reader(s) in Chapter Twoadvises testing on hardware devices as well as purely software-based implementations.
3.
Increased Redundancy. Examining Amazon's example above specifically, such an approach comes at an ever more serious cost as you increase the number of images in a given book. Such fall-back JPGs are redundant on a modern Kindle, and thus constitute simple dead-weight that increases the delivery fee that Amazon deducts from the gross revenue under the 70% option.
CSS allows us to apply sophisticated styling to tables, thus allowing the presentation of tabular data in attractive and informative ways. For example, the following shows how to style a table header:
th { background-color : #808080; }
<table> <tr> <th>Col 1</th> <th>Col 2</th> <th>Col 3</th> </tr> <tr> <td>A1 </td> <td>A2 </td> <td>A3 </td> </tr> <tr> <td>B1 </td> <td>B2 </td> <td>B3 </td> </tr> <tr> <td>C1 </td> <td>C2 </td> <td>C3 </td> </tr> </table>
Col 1 | Col 2 | Col 3 |
---|---|---|
A1 | A2 | A3 |
B1 | B2 | B3 |
C1 | C2 | C3 |
Drawing upon the coverage above of complex selectors, this means that you could combine the ‘th’ selector in the example with a class or ID selector, to target a given definition precisely. For example, the following:
#MyTable th { ... }
…states that the user agent should apply the definition to only the <th> elements of a table that possesses the ID attribute-value of ‘MyTable’, whereas the following:
table.RaceResults th { ... }
…applies the definition only to the <th> elements of tables that have a class attribute-value of ‘RaceResults’ (assuming, for example, that you are producing a book on horse racing that, one hopes, will romp home at 300-1).
It follows that you can also apply definitions to <tfoot> elements in much the same way, although there is no <tf> element in HTML, so you would say instead:
tfoot td { ... }
…which addresses all <td> elements within all <tfoot> elements in the HTML.
Now consider the issue of <caption> elements. Here we can use the caption-side property to force a table's caption to appear below rather than above it. For example:
table { caption-side : bottom; }
<table> <caption>Lorem Ipsum</caption> <tr> <td>A1</td> <td>A2</td> <td>A3</td> </tr> <tr> <td>B1</td> <td>B2</td> <td>B3</td> </tr> <tr> <td>C1</td> <td>C2</td> <td>C3</td> </tr> </table>
A1 | A2 | A3 |
B1 | B2 | B3 |
C1 | C2 | C3 |
You can also apply the color property to all <table> elements directly (or to tables of a certain class or ID), which will set the colour of any text in a given table, as the next example demonstrates.
td { color : #303030; }
<table> <tr> <td>A1</td> <td>A2</td> <td>A3</td> </tr> <tr> <td>B1</td> <td>B2</td> <td>B3</td> </tr> <tr> <td>C1</td> <td>C2</td> <td>C3</td> </tr> </table>
A1 | A2 | A3 |
B1 | B2 | B3 |
C1 | C2 | C3 |
The same approach also suffices when you wish to manipulate the alignment of text held within table cells. For example, the following definition will cause all text within the cells of a table with an id-attribute value of MyTable to align to the right:
#MyTable { text-align : right; }
Control over the column widths in a table is a common requirement, and there is a clear path to achieving this in the form of the ‘table-layout’ property that CSS supports. This takes one of two values, ‘auto’ (the default) and ‘fixed’. By applying the latter value to a given table, the widths that you set for the cells in its top row determine the widths of the columns below, and, as with all other size values when using CSS in an e-book, you should use ems or percentages.
table { table-layout : fixed; } td { background-color : #808080; } #Col_0 { width : 50%; } #Col_1 { width : 30%; } #Col_2 { width : 20%; }
<table> <tr> <td id = 'Col_0'>A1</td> <td id = 'Col_1'>A2</td> <td id = 'Col_2'>A3</td> </tr> <tr> <td >B1</td> <td >B2</td> <td >B3</td> </tr> <tr> <td >C1</td> <td >C2</td> <td >C3</td> </tr> </table>
A1 | A2 | A3 |
B1 | B2 | B3 |
C1 | C2 | C3 |
Styling table borders is not quite as simple as one would expect because giving a table a border is qualitatively different to giving borders to its cells. Consider the following:
table { border : 0.1em solid black; }
<table> <tr> <td>A1</td> <td>A2</td> <td>A3</td> </tr> <tr> <td>B1</td> <td>B2</td> <td>B3</td> </tr> <tr> <td>C1</td> <td>C2</td> <td>C3</td> </tr> </table>
A1 | A2 | A3 |
B1 | B2 | B3 |
C1 | C2 | C3 |
This shows that applying the border property to a table element gives a border to only the table itself, and so, to furnish a table with inter-cell borders, you must address the cells directly instead, as the next example shows:
td { border : 0.1em solid black; }
A1 | A2 | A3 |
B1 | B2 | B3 |
C1 | C2 | C3 |
This raises two points of note: first, the selector is just a bare ‘td’, and so it will affect all cells in every table indiscriminately, which may not be your aim. To address this, you could target the definition above at a given set of cells by giving each cell a class attribute, but that is extremely tedious with large tables, and it results in dreadfully verbose and unwieldy code (something we seek to avoid).
Instead, you can use a descendent selector, which is by far the better choice, and so the following gives borders to the cells of a table that possesses the id-attribute value of ‘MyTable’ (although you could use, say, a class selector):
#MyTable td { border : 0.1em solid black; }
However, this does not address the second point of note, which is that you may not desire the gaps that you can see between the cells in the example above. To remedy this, you could use the border-collapse property that CSS defines, giving it a value of ‘collapse’, but while this would work, trying then to giving rounded corners to the same table using the border-radius property (see below) would fail, as the corners would remain resolutely sharp.
Instead, you should use a property called ‘border-spacing,’ which you should apply with a value of zero, as the next example shows:
table { border-spacing : 0; } td { border : 0.1em solid black; }
A1 | A2 | A3 |
B1 | B2 | B3 |
C1 | C2 | C3 |
That eliminates the gaps, but now the internal borders are doubled-up, making them thicker, which again may not be the desired outcome. The fix for this is to apply borders to only the left and top of each cell (or right and bottom, or whatever permutation suits), as the next example shows:
table { border-spacing : 0; } td { border-left : 0.1em solid black; border-top : 0.1em solid black; }
A1 | A2 | A3 |
B1 | B2 | B3 |
C1 | C2 | C3 |
This fixes the double internal borders, but now requires right and bottom borders for the right- and bottom-most cells. This is where the first-child and last-child selectors come into their own, as the next example shows:
table { border-spacing : 0; } tr:last-child td { border-bottom : 0.1em solid black; } td:last-child { border-right : 0.1em solid black; } td { border-left : 0.1em solid black; border-top : 0.1em solid black; }
A1 | A2 | A3 |
B1 | B2 | B3 |
C1 | C2 | C3 |
Remember from the section above that covers complex selectors that the expression ‘tr:last-child’ means the table row that is the last child, not the last child of all table rows. It follows that ‘tr:last-child td’ means ‘all <td> elements that are descendents of the table row that is the last child of its containing element’.
Note that ‘of its containing element’ means implicitly ‘of its containing table’ because, within a tract of valid HTML, table rows are only ever children of table elements. Reprising a point made above, this means that the selectors in the last example apply to all tables, but you could target them at (a) specific table(s) simply by prefixing those selectors with the value of a class- or id-attribute value. Alternatively, you could use another descendent selector, or even a first-/last-child selector.
To show this, the following, which augments the CSS code in the previous example, ensures that the definitions affect only those tables with a class-attribute value of ‘GestationTimes’ (in, say, a book on veterinary medicine):
table.GestationTimes { border-spacing : 0; } table.GestationTimes tr:last-child td { border-bottom : 0.1em solid black; } table.GestationTimes td:last-child { border-right : 0.1em solid black; } table.GestationTimes td { border-left : 0.1em solid black; border-top : 0.1em solid black; }
In spite of these points, you may think that you can give rounded corners to a given table simply by applying the border-radius property to it. This will not work however, because, as pointed out above, it addresses the border of the table itself, not the cell borders. In fact, if you give a border to a table and to its cells, and try to round the table's corners, you get the effect demonstrated in the next example:
table { border-spacing : 0; border : 0.1em solid black; border-radius : 1.0em; } td { border-left : 0.1em solid black; border-top : 0.1em solid black; } tr:last-child td { border-bottom : 0.1em solid black; } td:last-child { border-right : 0.1em solid black; }
A1 | A2 | A3 |
B1 | B2 | B3 |
C1 | C2 | C3 |
As you can see, this simply adds a second border to the table's boundaries, which competes with the still-square cell borders. The only way therefore to give a table rounded corners and to give internal borders to its cells is to address the top-left, top-right etc. cells directly. Here, the first-child and last-child selectors prove their worth again, and the next example shows the requisite code:
table { border-spacing : 0; } td:last-child { border-right : 0.1em solid black; } tr:last-child td { border-bottom : 0.1em solid black; } tr:first-child td:first-child { border-top-left-radius : 1em; } tr:first-child td:last-child { border-top-right-radius : 1em; } tr:last-child td:first-child { border-bottom-left-radius : 1em; } tr:last-child td:last-child { border-bottom-right-radius : 1em; } td { border-left : 0.1em solid black; border-top : 0.1em solid black; }
A1 | A2 | A3 |
B1 | B2 | B3 |
C1 | C2 | C3 |
This combines the principles covered above to give left and top borders to all cells, along with right and bottom borders for the right- and bottom-most cells, along with rounded corners for the top-left, top-right etc. cells. Granted, it is rather verbose, but the code is legal, it works, and there is no other more-efficient way to do it.
This leaves only one further refinement. The values in the cells in the last example sit rather snugly within their cells, and so the final example in this section applies an aesthetic touch in the form of a little padding:
/* Assume the presence here of the definitions in the previous example. */ td { padding : 0.5em; border-left : 0.1em solid black; border-top : 0.1em solid black; }
A1 | A2 | A3 |
B1 | B2 | B3 |
C1 | C2 | C3 |
Do remember that you need use only those points above that satisfy your needs. For example, giving a table rounded corners mandates neither internal borders, nor a border that surrounds the table completely. Similarly, giving lateral inter-cell borders does not require vertical borders too (and vice versa), and so you should experiment to find the mix of factors that gives the styling and layout that delivers your goals.
In view of the points in the previous section, it may well be that you wish to colour table backgrounds, or even to give them some form of background image. In many cases therefore, applying the background property to a table directly will suffice, but if you combine this with the rounded-border technique given in the previous section, you will find that the background is visible outside the corner rounding and thus restores the table's square appearance.
table { background : url('Images/TexturedBackground.png'); color : #FFFFFF; border-spacing : 0; } td { padding : 0.5em; border-left : 0.1em solid black; border-top : 0.1em solid black; } td:last-child { border-right : 0.1em solid black; } tr:last-child td { border-bottom : 0.1em solid black; } tr:first-child td:first-child { border-top-left-radius : 1em; } tr:first-child td:last-child { border-top-right-radius : 1em; } tr:last-child td:first-child { border-bottom-left-radius : 1em; } tr:last-child td:last-child { border-bottom-right-radius : 1em; }
<table> <tr> <td>Cell A1</td> <td>Cell A2</td> <td>Cell A3</td> </tr> <tr> <td>Cell B1</td> <td>Cell B2</td> <td>Cell B3</td> </tr> <tr> <td>Cell C1</td> <td>Cell C2</td> <td>Cell C3</td> </tr> </table>
Cell A1 | Cell A2 | Cell A3 |
Cell B1 | Cell B2 | Cell B3 |
Cell C1 | Cell C2 | Cell C3 |
The solution is simple: you need only apply the background property to the table cells, rather than the table itself, just as you would when applying rounded borders, as the next example shows (giving only the modified td definition).
td { background : url('Images/TexturedBackground.png'); padding : 0.5em; border-left : 0.1em solid black; border-top : 0.1em solid black; }
Cell A1 | Cell A2 | Cell A3 |
Cell B1 | Cell B2 | Cell B3 |
Cell C1 | Cell C2 | Cell C3 |
You may have a need to use the same approach to labelling rows and columns as the largerevenue-table in Chapter One uses, which, in cut-down form looks like the following, and where the Size/Price label is the object of interest:
Size
Price | 1MB |
£1.50 | 0.98 |
The technique is relatively simple, and relies on the following points:
Obviously, you would use legends other than Size and Price, depending on what you want the table in question to document. Aside from that, however, the requisite CSS and HTML code is as follows, where the HTML shows the code for just a single cell, and omits the border, padding etc. rules that replication of the example above would require:
#SizePriceCell { background-image : url('Images/DiagonalLine.svg'); background-size : 100% 100%; } #SizePriceCell div { overflow : hidden; text-overflow : ellipsis; } #Label_Price { text-align : left; } #Label_Size { text-align : right; margin-left : 0.8em; }
<table> <tr> <td id = 'SizePriceCell'> <div id = 'Label_Size' >Size </div> <div id = 'Label_Price'>Price</div> </td> </tr> </table>
This has the desirable property of ensuring that the Price label always clings to the left of the cell, while the Size label always clings to the right, irrespective of the size of the table. Likewise, the background-size rule ensures that the diagonal line always stretches from the top-left to bottom-right corners of the cell, irrespective of that cell's size, and the overflow rules ensure that the labels do not overflow the right-hand boundary of the cell when the cell is very narrow in relation to the font size.
Drop capitals, illuminations and rubrics have been employed for centuries in print to decorate works, to emphasise semantically, to add some pizazz, and to lend a given text greater authority. Indeed, the use of decorative initial letters pre-dates moveable type, and such techniques are certainly of interest in the production of e-books, for all of the reasons above. Moreover, there is a certain gratification in preserving a venerable tradition, and in giving an electronic book a ‘literary’ feel.
There is some irony here, however, as searching the Internet for information on illuminations in e-books is a rather frustrating experience – the search terms ‘e-book illumination’ yield a blizzard of links to pages that discuss back-lit e-reading devices…
That aside, rubrics are achieved trivially, as CSS has supported the ‘::first-line’ ‘pseudo element’ (a special form of selector, note the double colon) since its first version. Amazon states currently that the Kindle devices do not support this selector, even though a test shows that they do, and the next example shows it in action:
p.Rubric::first-line { font-size : 1.2em; font-weight : bold; }
<p class = 'Rubric'> Lorem ipsum dolor ... ea commodo consequat. </p>
As an alternative to emboldening the first line, you could use small capitals or ‘small caps’ as they are known. If you are unfamiliar with this concept, the search-terms suggestion is:
The code to implement them is trivial, as CSS supports the ‘font-variant’ property. That is:
p.Rubric::first-line { font-variant : small-caps; }
<p class = 'Rubric'> Lorem ipsum dolor ... ea commodo consequat. </p>
In the case of drop capitals and illuminations, you can take one of three approaches. First, the ‘::first-letter’ pseudo element would appear to fit the bill, but technicalities preclude its use for illuminations. Alternatively, you can try enclosing the first letter in a <div>, and then floating that to the left (assuming the western left-to-right reading direction). This works visually for drop capitals and illuminations, but fails for visually impaired consumers of your book when they use the text to speech function that their devices may possess.
This is because the following:
<p> <div>D</div>rop capitals provide emphasis </p>
…will read like this:
Dee. Rop capitals provide emphasis…
That is, the speech synthesiser sees the ‘D’ as being a separate entity from the rest of the first word. The solution, which is the approach that Amazon recommends, is to use a <span> rather than a <div>. This solves the speech synthesis problem, as the span is ‘invisible’ to speech enabled user agents – they see the entire construction as if no span were present.
The second element to this approach is to float the span to the left. This gives it block element behaviour implicitly, which means that properties such as width, height, padding and margin then become available fully, thus giving us full control over the placement of the drop capital's glyph. Crucially, it also causes text that is in the adjacent element to flow up and around the floated element, which is just what we want for drop capitals and illuminations.
For example:
*.DropCap { font-size : 4.80em; margin : -0.27em 0.05em -0.4em 0; float : left; }
<p> <span class = 'DropCap'>L</span> orem ipsum ... nulla pariatur. </p>
Note the negative margin-top property (mentioned inChapter Five, Layout), which has the effect of pulling the drop capital up so that it is level with the top of the rest of the text. Similarly, the negative margin-bottom has the effect of pulling text below the drop capital up so that it fits snugly underneath the capital letter. Note too that, if you do attempt this technique, it is better to go for a larger font size in relation to the body text, as that gives you more latitude when fine-tuning the parameters.
The problem, however, with this approach, apart from the tedious mass of experimentation needed to achieve correct alignment, is that it works inconsistently across different user agents. Settings that give correct alignment in, say, Kindle Previewer in e-ink mode, give incorrect alignment on that user agent in Fire mode. Moreover, settings that are correct in Previewer's e-ink mode do not operate correctly on a hardware e-ink reader like the Kindle Touch! Indeed, the example above probably does not look spot-on on your user agent, but is rendered here anyway in order to give you an idea of the challenge this technique presents.
Undaunted, you may find some settings that (just about) work acceptably on all user agents, and then try to embellish things by turning the drop capital into a full-blown illumination. You might think that the best approach here would be to give the floated span a suitably elaborate graphic as a background image, but this causes still further fine-tuning and inconsistency woes. The better approach is to apply a little lateral thinking, and give the paragraph in question the background graphic, positioning that carefully so that it falls under the enlarged, floated first letter.
The following example give the code for this, where the background image is a simple grey square (given the ‘thin-border’ treatmentcovered in Chapter Six, Composition), and the letter glyph is rendered using the letterpress effectdescribed in Chapter Four, Style:
p.Illuminated { background-image : url('Images/Illumination_Background.svg'); background-repeat : no-repeat; background-size : 6.3em 6.3em; background-color : #FFFFFF; background-position : 1.0em 1.0em; width : 70%; padding : 1.0em; margin : 0 auto; } span.Illumination { font-family : Tangerine; font-size : 8.4em; line-height : 1.2; color : #606060; text-shadow : -0.01em -0.01em 0 #000000, 0.01em 0.01em 0.01em #FFFFFF; padding-right : 0.1em; margin : -0.15em 0.03em -0.4em 0; float : left; }
<p class = 'Illuminated'> <span class = 'Illumination'>L</span> orem ipsum ... pariatur. </p>
This code renders on Firefox as shown in the following screen-shot:
…where the reason for a screen-shot rather than a direct rendering of the code above is that, while the technique is a challenge to get right on one user agent, making it work consistently across all user agents is impossible. As with the first approach given above, this is due to inconsistencies in the way that the various renderers calculate em sizes, line heights and the like, and these inconsistencies make the technique unusable.
Given this, and if you are hell-bent on cracking the aesthetic whip, the best way is to create (or acquire) a graphic that will stand-in as the first letter, and make that the background image for the floated span. You then make the colour of the first letter transparent, which will render it invisible to visual consumers of your book (they will see only the graphic), while it remains ‘visible’ to any speech synthesiser, thus satisfying all constraints optimally.
The next example demonstrates this:
span.Illumination_Graphic_L { background : url('Images/Illumination_L.png'); background-repeat : no-repeat; background-size : 100% 100%; width : 4.2em; height : 4.2em; color : transparent; float : left; }
<p> <span class = 'Illumination_L'>L</span> orem ipsum ... pariatur. </p>
This is by far the easiest and most reliable approach, as you can control almost everything within your graphical editor – the only critical parameter you need to set in the CSS is the height and width of the graphic, which ameliorates the consistency problems considerably.
Moreover, using a fancy typeface in the first two approaches covered above could cost you in terms of file size, which, under Amazon's 70% revenue model will cost you money. This is because embedding a font file in your e-book means that you import all the information needed to render all the glyphs that the font-file supports, yet the book will not use many of those glyphs, and so they constitute simple dead-weight.
That is, if you look at the first letter of each chapter in your content, you may find that, out of, say, ten chapters, the set of first letters is, for example: A, O, O, T, N, T, T, E, D, D. That is, one A, one E, two Ds, two Os and three Ts. The human reader will see only five glyphs for which the font file caters, while all the other letters in the alphabet that the font file implements will simply bloat your book file redundantly.
Conversely, by using a graphic to display the letter in question (plus any ornamentation if you want to illuminate things), you increase the book's file size by only the amount of information needed to effect that drop-capital/illumination. In the example cited in the previous paragraph, you will increase your book's file size with the bulk of only five graphics. Moreover, you can use an SVG graphic, rather than a PNG or JPG, which will be the most efficient implementation. You can also convert the first letter's shape to a path (seeCapturing Font Glyphs in Appendix E), instead of using an embedded PNG or JPG image of the letter. This will give smooth scaling on all hardware resolutions, with maximal efficiency.
Drop capitals and illuminations will never work seamlessly across all user agents until there is rigid standardisation of the typographical parameters that apply to rendering text. Until that happens, however (probably on the day that Satan is seen skating to work), the third approach given here is your best option.
During the development of this guide, some of the biggest obstacles lay not in writing the content, but in overcoming certain odious bugs in the Kindle readers (this guide was published first as an e-book available on Amazon – see the introduction).
As will be obvious, this guide presents its code examples as dark text on a lighter background, with slightly rounded corners. Given that code lines that wrap-around ruin readability and look terrible, the CSS code applies the following CSS rules (among others) to the <pre> elements that carry such examples:
word-wrap : normal; overflow : hidden; text-overflow : ellipsis; border-radius : 0.55em;
In addition to giving slightly rounded corners to the example panels, this ensures that code lines do not run beyond the right-hand limit of the <pre> element within which they reside. It also puts an ellipsis on the end of such truncated lines in order to tell the reader that there is more code than can be displayed (as the introductiondocuments). Note here that the ‘word-wrap : normal’ rule should be unnecessary, but is present in order to address another bug in the Kindle software, which is that long lines within a <pre> element wrap-around at the right-hand boundary of the element, even though they should not (asmentioned in Chapter Three, Structure).
However, this set of rules introduced a problem that was particularly hard to diagnose. Everything looked fine on a Kindle Touch, as it did for the e-ink mode in Amazon's Kindle Previewer, but in Fire mode, the code examples were unrecognisable, and instead of looking like the following (taken from Chapter Four):
<p> Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. </p> <p> Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. </p>
…they looked like this:
Moreover, the problem manifested in the Kindle for PCs application too, and even affected text in SVG images. This was unacceptable, as people might purchase the e-book version of this guide intending to read it on a PC as well as (or instead of) a dedicated e-reader – you might have this guide open in one window as you read this paragraph, while you work on your own book in another. Whatever the scenario, however, unreadable code examples are out of the question, and investigation revealed that the following rule:
overflow : hidden;
…appeared to be the cause of the trouble, as removing this rendered the code samples readable. However, the entire point of applying that rule was to prevent long lines from running beyond the edge of their containing element, as that looked dreadfully unprofessional. Searching online for a solution was hopeless, and only considerable experimentation showed ultimately that the cause lay in an interaction between the overflow and border-radius rules. That is, application of overflow : hidden and a border-radius rule to the same element renders that element's text illegible.
With the dawn of understanding, resolution was simple and swift, if damnably inelegant. Every <pre> element that forms a code example should be nested within a <div>, where the CSS should apply overflow : hidden to the <pre> element, and should apply the border-radius rule (along with a little padding) to the <div>.
Problem solved; but it is true to say that things would have been a lot sweeter for one particular e-book producer in the absence of such an annoyingly stupid bug.
Notably, final realisation of the real cause of the trouble proved illuminating, as, armed with the appropriate search terms, it enabled a retroactive search for relevant pages that turned up nothing. This suggests that, as of summer 2015, few people had tried to render elements with a combination of overflow : hidden and border-radius. If a significant number had, it is likely that someone somewhere would have documented it online.
CSS defines four ‘positioning models’, where the default is ‘static’ positioning, an example of which you are looking at now. That is, the inline elements in this chapter flow across and down, and the block-flow elements flow simply downwards; and it is called ‘static’ positioning because the flow of elements is governed implicitly by the static (i.e. pre-defined) structure of the underlying HTML. CSS augments this with the ability to float elements to the left or right (asconsidered in Chapter Five, Layout), which yields all that the great majority of e-book producers require.
The other three positioning models comprise ‘absolute’, ‘fixed’ and ‘relative’ (which you stipulate using the display property). Fixed and relative positioning are of value in web sites (although the use of the relative form is uncommon), but they appear to be of no use in e-books simply because the concept of a book – a static presentation of content, rather than a possibly-interactive user interface – does not engender their use. Moreover, fixed positioning does not operate reliably on at least some of the Kindle devices, and Amazon's KindleGen tool emits the following warning message for both:
Value specified for CSS property in content is not supported by Kindle readers.
Notably, that tool also generates the same warning for absolute positioning, even though Amazon recommends its use when creating fixed layout products like children's books and graphical novels. If you want details on the other positioning models, you should consult a comprehensive CSS reference; and you should consult Amazon's documentation if you need guidance on producing fixed layout books. The search-terms suggestion here is:
A sizable number of dedicated e-reading devices support speech synthesis or ‘text to speech’ as it is known more commonly. Not least, this suggests the potential to make textual books (rather than image-centric books) available to people who are visually impaired, with no need to produce, say, Braille versions of a given work. It also suggests the idea of sighted book-consumers just sitting back while their user agents read to them.
The science of speech synthesis has intrigued people from long before the advent of modern computation systems. Indeed, there are legends that go back over 1000 years, but the first computationally-based systems appeared in the 1950s. Initial attempts resulted in speech that was of very poor quality and which was often barely intelligible, and while there has been much progress since then, it is still easy to distinguish natural speech from the synthesised variety.
The aim in improving such systems centres therefore on making the generated speech sound as natural as possible, thus improving intelligibility. However, a system that simply converts textual words and phrases into sounds, as the early synthesisers did, can never produce completely natural speech. When we speak, our understanding of the words drives our phrasing and intonation (the rise and fall in pitch). We see the content in question as an integrated whole, rather than a simple agglomeration of words, phrases and sentences; and this is why achieving synthesised speech that is indistinguishable from the natural form is such an exceptionally tall order.
If it were possible to implement ‘understanding’ by purely computational means, a myriad challenges in artificial intelligence (a branch of programming) would evaporate overnight. However, the work of logicians such as Alan Turing and Kurt Gödel (carried out prior to the advent of modern computers) shows that things such as understanding and creative problem solving cannot be achieved algorithmically. (A tangential point here: contrary to popular perception, Turing did not invent computers, and Ada Lovelace was not the first programmer).
Given this, the next best approach is to make synthesisers factor punctuation into the speech generation process, and to adorn textual content with special tags. Those tags act as hints to a speech synthesiser that tell it when to render pauses and rests; when to spell out character sequences (like acronyms) rather than read them as words; and when to stress particular syllables.
In pursuit of this aim, the W3C specified the Speech Synthesis Mark-up Language, which, just like HTML, allows you to tag content elements, thus imbuing them with prosodic information. CSS also defines twenty speech-related properties, but support for these in the rendering engines that abound is either absent (the majority) or barely more than experimental.
For example, listening to a Kindle Touch reading sections of this guide makes clear that code listings should not be read aloud (code is clearly a visual thing, which underscores again the need for good layout). In view of this, the following code should suppress the reading of code examples in the e-book version of this guide:
pre.Example { speakability : none; /* Other rules expressed here */ }
That is, every code sample you see herein is contained within a <pre> element (in order to preserve layout), and so the definition above, when applied globally (i.e. across the entire book) should prevent the vocalisation of any code sample.
However, experiments show that absolutely none of the aural CSS properties work on a Kindle Touch. Moreover, the <em> and <strong> tags that HTML supports should also create vocal emphasis and stress. To interpret the published standard: an <em> element suggests to the user agent that the text it encloses should be vocalised with some stress, whereas <strong> elements should be vocalised in a more ‘commanding’ way. These do not work either on the Kindle Touch, and it is notable that later versions of the Kindle lack support for text to speech. Presumably, this is due to a lack of demand.