Showing posts with label css. Show all posts
Showing posts with label css. Show all posts

Friday, June 28, 2013

Getting iBooks to respect image width

So, I wrote a long time ago about how iBooks doesn't apply the CSS width property to images properly. Despite the fact that the spec says that width applies to all elements except non-replaced inline elements, and that images are replaced inline elements, up til now, iBooks wouldn't apply width to img. There is a workaround: applying the width to the image's parent, and setting the width of the image itself to 100%, as I describe in that article.

What's more frustrating is that since InDesign follows the rules, dutifully applying the pixel or percentage dimensions in the CSS directly to the image—which again, should work but doesn't in iBooks—those images are not displayed properly in iBooks.

So here's a document in InDesign: (I'm showing you the example in InDesign, but I would have the same issue if I created the code myself from scratch.)

InDesign Width

Here's the code that InDesign generates when I choose "Preserve appearance" and "Relative to page"

idGeneratedStyles.css

And here's how iBooks displays that page:

iBooks and width before

I wrote Douglas Waterfall, Engineering Architect on InDesign, about the problem and he pointed me to the solution, right there in the iBooks Asset Guide (which you can download if you have an iBookstore account).

The solution consists of creating a super-charged or magic CSS class that you can then use to properly apply the width property to img elements in iBooks. And it won't confuse ereaders that already do.

First, in the content.opf file, you have to make sure you have declared the iBooks namespace in the package element. You need this namespace if you use iBooks versioning as well. And it's completely harmless for non iBooks ereaders. (Note that I'm doing this in EPUB 3. I don't know and haven't tested it in EPUB 2.)

<package xmlns="http://www.idpf.org/2007/opf"
prefix="ibooks: http://vocabulary.itunes.apple.com/rdf/ibooks/vocabulary-extensions-1.0/" version="3.0" unique-identifier="bookid">


Next, in the meta section of the content.opf file, define which class should be able to apply the width property properly.

<meta property="ibooks:respect-image-size-class">respect</meta>

I called my class respect, but you can call it whatever you like.

Next, in the HTML, apply the respect class to your images.

<p class="Basic-Paragraph"><span><img class="respect" src="image/imagefolder-2_fmt.png" alt="imagefolder-2.jpg" /></span></p>

You can actually create multiple classes, as long as they are equal to, or prefaced with the name you declared in the meta element. So I could use respect, respect-wide, respect-small, or whatever.

Finally, create a rule in your CSS that references the respect class and applies the width property:

img.respect {
width:51%;
}


(Of course, if you're using InDesign, you can simply create a style called respect and let InDesign create your HTML and CSS.)

And then, miracle of miracles, iBooks applies the width property, just as it should.

iBooks does width!

Seems crazy that we have to help iBooks follow the spec properly, but at least we can.

If you like this post, please consider subscribing!

Monday, October 22, 2012

Presenting “The Monarch Butterfly: An Interactive Picture Book”

Over the weekend, I finally finished my first serious attempt at an interactive picture ebook. I've been working on it off and on for several years, first as I tried to take photographs that were worthy of being published, and second as I taught myself enough Javascript to create the effects that I thought were necessary to show the photographs and information in a fun and beautiful way.

Here's a short sampling of the book:



You can download a sample, or buy the whole book on the iBookstore. It works great on both iPad 2 and 3, as well as newer iPhones. I haven't tested it in other readers besides iBooks (yet).

I am writing a new miniguide that explains how to create interactive ebooks with Javascript and CSS and will announce that as soon as it's available.

If you would like a review copy, please contact me.

Tuesday, May 15, 2012

Customizing InDesign's EPUB files without cracking them open!

I'm working on my presentation about Enhanced EPUB for PePcon on Tuesday afternoon and I was playing with the newly released InDesign CS 6 to make sure that everything works as expected. But it didn't. For some reason, when you place an audio file, iBooks displays the controls so small that the only thing you can do is choose Airplay, but not the actual Play button (if you're on an Airplay network). It's pretty frustrating and would completely ruin InDesign's much improved multimedia function if not for one thing.

You can fix it without cracking the EPUB file open. One of InDesign CS 6's other new features is that you can choose additional CSS files to apply to your exported EPUB. All you have to do is create a CSS file that fixes the code that InDesign supplies, and voilĂ , the audio controls reappear. You don't have to unzip, or worse, rezip.

The key lies in the letter "C" of "CSS". It stands for Cascade. The Cascade means that when there are multiple sets of style rules for a given bit of text, there is a defined hierarchy for which rules will win out. I actually see it as a sort of waterfall in my head with the more “important” rules flowing over and covering the lesser ones.

To make the cascade work, you have to understand the slightly complicated topic of CSS inheritance: given two competing rules, which one wins? I talk about this in some detail in my HTML book. The very abbreviated answer is that you have to take into account specificity (that is #id overrules .class overrules p) and location (that is, inline overrides local overrides earlier, overrides imported), as well as taking into account the !important tag.

In our example, InDesign creates this code for audio elements. Note that this is the default size if you don't change the poster image for the audio element.

audio.frame-5 {
height:60px;
width:60px;
}


But when I open that document in iBooks on the iPad, this is what I see:

Default audio controls

And when I click it, all I can select is which Airplay device I want. I can't actually play the audio.

Default audio controls-airplay

So, I open a new text document. InDesign 6 lets me add additional CSS documents and will automatically load them after the CSS that it creates itself. That means that since my styles are later, as long as the specificity is the same, my styles will override InDesign's.

But if you look at the code above, notice that the selector that InDesign uses, audio.frame-5 has a class, and thus is more specific than a plain audio.

The answer lies in the HTML that InDesign creates. By default it generates an id element for each audio (and video) element that it creates, set to the filename. So, you can use that filename in the CSS to make your selector more specific, and thus override InDesign's CSS.

My audio file is called "bondia.mp3", and if you know CSS, you know there's a problem already. That period in the filename has special meaning in CSS (it means that what follows is a class name). Since we don't want that special meaning, we have to escape the period with a backslash:

audio#bondia\.mp3 {
height:26px;
width: 150px;
}


These values will now override the height and width values that InDesign generated. Save this text file as "extras.css"

How do I get them into my EPUB file? Go to Export in ID6, choose EPUB, click the Advanced panel, and then choose Add Style Sheet, then find your extras.css file. Export as usual.

EPUB Export Options

If you looked inside your EPUB file (though you don't have to), you'll see this:

Two CSS files

That's it! When you open your book in iBooks, you'll see that the audio controls are as you specified (and not as InDesign specified), even though you never had to deal with Terminal, never had to unzip or rezip or anything.

Fixed audio controls

The best part about this is that you can use this technique to override any of the CSS that InDesign either creates in a way that you don't like or that it doesn't yet support. For example, suppose you want to add a border around a video. You can do that with an extras.css file. (Indeed, you can add borders to all of your videos just by using video as a selector instead of targeting just a single video.) Suppose you want to add media-queries so that your document will work as best as it can in different ereaders. Just put the media-queries in your extras.css file. The possibilities are really enormous.

Thanks to Adobe for incorporating this amazing new feature!!

Wednesday, March 28, 2012

Kindle ignores everything when ID gives it multiple classes

I'm about to publish an anthology of stories based in Barcelona (more news soon!) and keep bumping into an annoying issue with (old) Kindle: if your InDesign document contains a styled paragraph with local formatting, InDesign will export the resulting EPUB with multiple classes, one for the base style and one "override" class with the extra bits. Unfortunately, Kindle throws up its hands at multiple classes and not only ignores the extra classes but actually ignores the base class as well, leaving the paragraph with no formatting at all. Ugh.

Here's an example from my new book.

Kindle-multiple classes

The paragraph that begins "Moving between the shadows" has no styling. It should be in italics with no indent, as shown in iBooks:

iBooks - multiple classes

I go look at the code, and find that InDesign has added an "override" style:

code-multiple classes

Why? Because I had added some local formatting in the original InDesign file. Oops. (You can tell by putting your cursor in a paragraph and looking at the Paragraph Styles panel... if there's a plus sign next to the style name, there's local formatting.)

InDesign-local formatting

Really, InDesign is completely within its rights by exporting multiple classes, they are perfectly standard. It is Kindle that is at fault here.

The easiest solutions is to avoid local formatting. This is a good idea in general, although I'll admit that when I just need to nudge something in my print version, I am not always strictly orthodox.

If you forget, and/or don't want to go back and change the print version, the second solution is to create a new style that contains the characteristics from the two applied styles, and apply it both to the HTML and to the CSS. Kindle can't say no to that.

screen_shot-27125

In general, I recommend doing a search through your XHTML files in an InDesign-generated EPUB document to find all of the override classes. Then, substitute them with better-labeled and less troublesome individual styles.

Also note that it is "old" Kindle that can't handle multiple classes, Kindle Fire supports them just fine.


Friday, March 23, 2012

Centering in ebooks across ereaders

A long time ago, I realized that the most basic parts of childrearing—eating, sleeping, and peeing and pooping—were by far the hardest to figure out. So it is with ebooks. It seems almost ridiculous to have to write a big, long complicated post about centering—something so essential and so fundamental—but I keep bumping up against it, so write I will. I hope I'll save you some sleepless nights.

Centering in ebooks involves CSS, the box model, inheritance, and ereader inconsistencies. The main problem with centering lies in not understanding how these aspects relate to each other.

The text-align property

I'll start with the simple. There is a CSS property called text-align whose possible values are left, right, justify, and center. But there is one very important caveat: text-align only affects inline-level objects within the block-level objects to which text-align has been applied. Let's look at that closely, since it is the source of many misunderstandings and thus errors.

Here is a simple p element that contains text.

<body>
<p class="center">Here is some text that should be centered</p>
</body>


And here is the CSS:

.center {text-align:center}

And here is what it looks like:

Preview: centeringex.html

You may think that it is the p element that has been centered, but you'd be wrong. It's actually the inline contents of the p element, an anonymous inline block, if you can stand the CSS parlance, or simply the text itself (if you can't), that is being centered. The p element itself is not centered.

How can you tell? We'll set the width of the p element to 60%, and then set the background color to yellow, so you can actually see it.

.center {text-align:center; width: 60%; background: yellow}

Preview: centeringex.html

You can now clearly see that the p element is still aligned left (which is the default in most browsers and ereaders) even while the inline text that it contains is aligned center within the p element.

Centering block-level elements

So how do you center a block-level element? The canonical way is to set the right and left margins to auto. According to the CSS spec, when both left and right margins are auto, they should be equal, and thus the item will be centered. And don't forget that unless your block element has a width that is smaller than the window size, you won't be able to tell if it's centered or not.

So, to center our p element, we'd have this:

.center {text-align:center; width: 60%; background: yellow; margin-right: auto; margin-left:auto}

And we would get this:

Preview: centeringex.html

This works on most major browsers and ereaders:

Centering-iBooks iPad

And is converted properly for Kindle Fire:

Kindle Fire Previewer - CenteringAcrossEreaders

(Regular Kindle doesn't support width and thus can't center a block-level element at all.)

CSS bug in ADE, NOOK, etc.

Unfortunately, ADE, and the ereaders based on it (like NOOK, NOOK Color, and presumably Sony Reader though I didn't test this last one), don't properly interpret the auto value for left and right margins, and the result is that the blocks are not centered:

NOOK Color centering

Independence of alignment of contents and of the block that contains them

Before we get to the solution, I just want to underline how the text-align properties and centering of the block level elements using automatic right and left margins are independent of each other.

That is, the alignment of the inline-level elements within a block-level element need not match the alignment of the block-level element. That is, we can center the p element within the window and then left-align the contents of the p element:

.mismatch {text-align: left; width: 40%; background: red;margin-right: auto; margin-left:auto }

And that should look like this:

Aligned left within centered block

Notice how the image and the text are aligned to the left within a box that is centered on the page.

Again, ADE, NOOK, and presumably Sony Reader, don't properly interpret the right and left margins set to auto:

NOOK centering mismatch

The Solution

The solution hinges on the fact that text-align only affects inline elements. (This is why simply enclosing an inner div in an outer div whose text-align property is set to center will not work.) We'll enclose the p elements in a wrapper div, whose text-align property we'll set to center, and then we'll set the display property for the p elements themselves to inline-block. The inline-block value is sort of a hybrid between inline and block, that for our purposes keeps the p elements starting their own paragraphs (like regular block-level elements) but allows them to be affected by text-align (like inline-level elements).

Here's the CSS with the new bits in bold:

.wrap {text-align:center}
.center {text-align:center; width: 60%; background: yellow; display:inline-block}
.mismatch {text-align: left; width: 40%; background: red;display:inline-block }


And here's our new HTML:

<div class="wrap">
<p class="center">Here is some text that should be centered</p>
<p class="mismatch"><img src="momo.jpg" alt="momo" width="200" height="150" /><br />This image and text will be aligned to the left within the centered block that contains them.</p>
</div>


Let's look first at iBooks on iPhone: (I've put it in landscape orientation just so it's easier to see, it looks the same in portrait.)

iPhone Centering

Everything is as it should be. Our boxes are centered, but the contents within them are centered and left-aligned respectively.

How about NOOK Color?

NOOK Color-centering inline-block

NOOK Color also likes this solution, even when Publisher Defaults are turned off (although then you have other margin issues).

And Kindle Previewer converts the file just fine for KF8 and the Kindle Fire ("old" Kindle can't center divs but maintains the text-alignment for the inline level elements):

Kindle Fire Previewer - CenteringAcrossEreaders6

But what happens when you view the ebook in iBooks on iPad?

iBooks iPad Centering

The boxes are centered as desired. But the text and image that should be left aligned in the lower box are now justified! Look at those ugly spaces between the words. What happened?

Now we've bumped up against iPad's default overrides. Remember that by default, Full Justification is set to ON, hidden away in the iBooks area of the Settings panel. I don't think most people who use iBooks will ever know this setting exists so ebook designers have to plan for it. You used to be able to add a span tag around the text that you wish to have aligned to the left, but that hack no longer works.

Thankfully, there is still a workaround. If you add the com.apple.ibooks.display-options.xml file to the META-INF folder, with the <option name="specified-fonts">true</option>, the justification that you choose in your CSS will be maintained, regardless of the Full Justification setting chosen. The user can still override your choices by choosing a different font in the Fonts menu, but at least the book will start out the way you want it:

iBooks iPad

To resume:

The text-align property only applies to inline elements within the block-level element to which the property is applied. It is inherited by block level elements that may be enclosed in the outer block-level element and in turn applied to the inline elements that the inner block level elements contain. If you need more info on block level vs inline, see the CSS spec or my my HTML/CSS book.

The canonical way to center a block-level element is by setting both margin-right and margin-left to auto. The fact that this is not supported by ADE or NOOK (and presumably not by Sony Reader or other ereaders based on ADE) is a serious bug.

You can center block-level elements by wrapping them in another block level element (a div), setting the text-align to the outer div to center, and then setting the display property of the inner div (or p or whatever) to inline-block.

iBooks on iPad has a default setting of Full Justification for text, which you can override by adding the com file to the META-INF folder. This has ramifications with respect to font choice, of course, as well.

iBooks on iPhone has the Full Justification setting, but by default it is set to OFF (presumably because of the narrow screen).

This should be much simpler.

And yes, it's true that I have no AT&T coverage in my office!


Wednesday, February 8, 2012

Cross-platform multiple indents in ebooks

Sometimes I worry that I don't do enough real world work to know what the actual problems that ebook production people face. So when someone poses an interesting question on #eprdctn, I like to take a look.

Today, @ebookartisans was trying to get multiple indents to work on old Kindle, new Kindle, ADE, NOOK, and iBooks from a single EPUB file (obviously KindleGenned into a mobi for Kindles). She was using a combination of code from Joshua Tallent and myself. It turns out my code only works on first level indents on Kindle. Unfortunately, Rick Gordon (another #eprdctn regular) reports that the NOOK won't look at any code that comes after a media query, and so she was having trouble getting NOOK to do what she wanted.

Does this give you flashbacks of hacks for hiding CSS from IE? Well, it's the same show, so many years later. So, if you remember, you're getting old like me.

Thankfully, we have somewhat better tools this time around, in the form of media queries, which I started explaining a few weeks ago.

The problem with indents is that Kindle has some built-in funky behavior. So, if you were setting up the code just for old Kindle (e.g., not Fire) you might use the following:

<style type="text/css" media="amzn-mobi">
.level1 {text-align:left; text-indent: -30px; }
.level2 {text-align: left; text-indent: -60px; }
.level3 {text-align: left; text-indent: -90px; }
</style>


You'd also have to add extra spaces in front of each level2 and level3 (and successive levels) to give a left margin to the first line. This is Joshua Tallent's hack (actually he uses &#xa0;, but @ebookartisans says that doesn't work on NOOK):

<p class="level3"><span class="spaces">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span>•&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;The mean aunt had her own traumatic childhood, deserving of treatment in a Roald Dahl novel.</p>

Kindle- multiple indents

Though I salute Joshua's ingenuity, I admit I hate the spaces, and would only use such a hack in extreme need. Instead, I'd advise against using multiple indents. But in case the need arises, we'll forge on!

Since NOOK completely ignores the "not amzn-mobi" media query and any CSS that follows it, the trick is to put the NOOK (and other ereader code) in a stylesheet without a media query, and before the aforementioned old Kindle stylesheet. I'll also slip in some code that hides the extra spaces everywhere except in old Kindle.

<style type="text/css">
.level1 {margin:0 0 0 2em; text-indent: -2em;}
.level2 {margin:0 0 0 4em; text-indent: -2em;}
.level3 {margin:0 0 0 6em; text-indent: -2em;}
.spaces {display:none}

</style>


<style type="text/css" media="amzn-mobi">
.level1 {text-align:left; text-indent: -30px; }
.level2 {text-align: left; text-indent: -60px; }
.level3 {text-align: left; text-indent: -90px; }
.spaces {display:inline}
</style>


Here's where you have to keep track of CSS inheritance rules. Remember that later CSS overrides earlier CSS as long as it has equal importance. So, the text-indent in old Kindle will override the earlier CSS. That's what we want for text-indent but not for margin-left. So, just add in margin-left: 0; for each style in the Kindle stylesheet:

<style type="text/css">
.level1 {margin:0 0 0 2em; text-indent: -2em;}
.level2 {margin:0 0 0 4em; text-indent: -2em;}
.level3 {margin:0 0 0 6em; text-indent: -2em;}
.spaces {display:none}</style>


<style type="text/css" media="amzn-mobi">
.level1 {margin-left:0;text-align:left; text-indent: -30px; }
.level2 {margin-left:0;text-align: left; text-indent: -60px; }
.level3 {margin-left:0;text-align: left; text-indent: -90px; }
.spaces {display:inline}
</style>


That code works in old Kindle, Kindle Fire, NOOK, ADE, and iBooks:

Kindle-Multiple indentsKindleFire-Multiple Indents

NOOK-multiple indentsiPad-Multiple indents

ADE-multiple indents

It doesn't take a sharp eye to see that the words in the first line don't quite line up with the second and subsequent ones, AND differ from ereader to ereader. There is no tab in EPUB (or HTML), and each ereader seems to measure spaces its own way. If you want the lines perfectly aligned, you'll have to use a regular old list. It looks beautiful as long as you don't need to control what the bullet character looks like, as old Kindle will only give you a bullet:

Kindle-lists

Did someone say "EPUB in the Wild"?

Here are both the EPUB file as well as the Mobi file that I generated from it with Kindle Previewer 3.

Friday, November 11, 2011

Keeping it together - Page breaks in iBooks

I've been doing a lot of testing with InDesign CS 5.5 and was surprised this morning to find that the CSS it uses to create page breaks in EPUB documents actually works in iBooks. It used to be that iBooks only supported page breaks when they were in local CSS, and then it supported it in external CSS, and then for a while it didn't support even that, but now it looks like full iBooks support for page breaks is back.

Maybe this is a good time to go over the page breaks capabilities in CSS and what iBooks supports. While they don't give absolute control over where elements appear on a page, they have an enormous influence on how professional the page looks. (Note the design of these pages is bare-bones, the emphasis is on page breaks.)

The three CSS properties are page-break-before, page-break-after, and page-break-inside. Each one can take values of auto, always, and avoid, and the first two also accept left and right, though iBooks does not yet support those values.

If you want a title to appear alone on a page, you could use

.title {
     page-break-before: always;
     page-break-after: always;
     }

but note that if that is the first page in your EPUB, iBooks will show a blank page at the beginning (since it creates a page break before your title). In which case, you should only use:

.title {
     page-break-after: always;
     }

page-break-after


In order to make a header always start at the top of the page (together with the content that follows it), use:

.header {
     page-break-before: always; 
     page-break-after: avoid;
     }


page-break-before

And if you want to keep a photograph with its caption, you could use

.group {
page-break-inside: avoid;
}

assuming that you've got a div with class="group" that contains both the image and the caption.

page-break-inside-avoid

It would be really, really nice if iBooks would support the CSS widows and orphans properties. But it doesn't yet. (NOOK does, yay.)




Tuesday, April 6, 2010

Wrapping Text in an ePub for the iPad

Yesterday, I noted that Apple's sample Winnie-the-Pooh eBook had text that wrapped around the image at the beginning of each chapter, and that I'd like to know how they did that. According to the OPS spec, CSS position properties (like absolute and fixed) are strongly discouraged. I originally interpreted that as applying to the float property as well, but that does not seem to be the case.

Indeed, the float property works just fine, and I assume that's what Winnie is relying on. Here is my own example:

Wrapped text in iPad

The code is absolutely standard and familiar:
img.float {float:right; margin-left:10px}

It works in Adobe Digital Editions also.

Seems like it opens up a world of possibilities. I'll be spending the day looking at them, but I'd love to see what you come up with.

One thing to keep in mind is that when the iPad is rotated, the image does not change size. And the wrapped text can get pretty squished:

Wrap text horizontal

The answer to that problem is probably keeping the image small enough to work on the horizontal spread, and big enough to be helpful on the vertical.

Don't be distracted by the italics and small caps. Those are tests as well. And yes, that is Walden Pond.

Monday, November 30, 2009

Why is position:relative necessary when you're positioning absolutely?

So I'm reading Dan Cederholm's new book, Handcrafted CSS: More Bulletproof Web Design). I'm really liking it.

There was a small example that deals with absolute positioning that I want to highlight. He's talking about formatting a list of coffee drinks that have a title and a price. The goal is to have the link be the entire line so that it makes it easy for readers to click on their desired cup of coffee.

The original code looks like this:

<ul class="lst">   
<li><a href="#"><em>2.79</em> Latte</a></li>
<li><a href="#"><em>2.99</em> Cappuccino</a></li>
<li><a href="#"><em>1.80</em> Cafe Americano</a></li>
<li><a href="#"><em>2.00</em> Espresso</a></li>
<li><a href="#"><em>10.49</em> Caramel Macchiato</a></li>
</ul>


Cederholm suggests using absolute positioning, with code much like the following (his is part of a larger web page and so I've adjusted it to appear on its own):

ul.lst li {
     list-style-type:none
     }

ul.lst li a   {
     position: relative;
     display:block;
     padding: 7px;
     border-bottom: 1px solid #f3f2e8;
     width:200px;
     text-decoration:none;
     font: .9em Verdana;
     }
    
ul.lst li a:hover {
     background-color:#e0e0e0;
     }
   
ul.lst li a em  {
     position: absolute;
     right: 7px;
     top: 7px;
     font-style:normal;
     }


The result can be seen here.

And though Cederholm says the position:relative line is necessary, he doesn't explain why. The reason is that when you position an element absolutely—in this case the coffee prices, which are em elements—their position is calculated with respect to the nearest positioned ancestor, or if there is none, to the body element. Since he wants the prices to go in the same line as the a element that contains them, he must position the a element. By adding position:relative to the definition for the a element, it becomes positioned, and any descendants that are absolutely positioned will be positioned relative to it. This happens even though he's not defining any offsets; the a elements stay in their natural place in the flow.

You can find more information about relative and absolute positioning on pages 178-179 in my HTML, XHTML, and CSS, Sixth Edition.

Also note that Cederholm concludes this example by showing how absolute positioning is not very flexible for text elements. That is, when the text is enlarged, the name of the coffee and its price overlap each other. Instead, absolute positioning makes more sense if one of the pieces is a static image whose size can always be accounted for.

More of my books