Words in Boxes

Nouns, verbs, and occasionally adjectives.

Monday, December 22, 2008

XProc, Part III — Turtles All The Way Down (Steps, Reuse, and Encapsulation)

This is my third post on XProc. (To see the first two, go here and here.)  In this post, I'll talk about the different categories of steps, how to create your own steps and reuse them across pipelines, and how this all relates to the fundamental metaphor of XProc. 

One task I do frequently is removing Arbortext Editor change-tracking markup from documents. Here's a simple pipeline that does just that:

<p:pipeline type="u:accept-changes">
  <p:xslt>
    <p:input port="stylesheet">
      <p:document href="accept_changes.xslt"/>
    </p:input>
  </p:xslt>
</p:pipeline>

The fundamental unit of work in XProc is the "step," which you can think of in the same way you do subroutines in other programming languages. In general, a step takes XML as input, performs an operation on it, and outputs XML. There are three types of steps in XProc:

  • atomicAtomic steps. These are the most basic building blocks of XProc pipelines, and each carries out a single fundamental XML operation.  There are a number of built-in atomic steps, such as p:xslt (as shown above).  These built-in atomic steps will be the foundation for almost everything you do in XProc, so learn them.  

  • image Compound steps.  These are assembled from other XProc steps.  Sometimes they are just a series of implicitly connected atomic steps, and sometimes they use built-in logical control structures, such as p:for-each, to control their execution.

    It turns out that the above accept-changes pipeline — and in fact any pipeline you create — is also a compound step.  (The fact that p:pipeline is a shortcut for p:declare-step is a dead giveaway.)  This is an important idea, and we'll discuss it more below.  But for now, remember this:  a pipeline and a step are the same thing

  • Multi-container steps.  There are only two multi-container steps: p:choose and p:try.  These contain two or more alternate pipelines.  You cannot define your own custom multi-container step.

No doubt, in your day-to-day work, you'll never feel the need to stop and consider whether a certain step fits one category or the other.  But the difference does have consequences, so it's worth spending a bit of time on it.

Let's say that you want to use the above accept-changes step as a step within a larger pipeline — for example, a pipeline that removes all of the notes from 2nd-level sections in a document.  Here's how you would do that:

<p:pipeline name="delete-notes" 
    xmlns:p="http://www.w3.org/ns/xproc" 
    xmlns:atict="http://www.arbortext.com/namespace/atict"
    xmlns:u="http://www.wordsinboxes.com/xproc">

  <p:serialization port="result" encoding="utf-8" method="xml"  
    omit-xml-declaration="false"/>

  <!-- Declare accept-changes step -->

  <p:pipeline type="u:accept-changes">    
    <p:xslt>
      <p:input port="stylesheet">
        <p:document href="accept_changes.xslt"/>
      </p:input>
    </p:xslt>
  </p:pipeline>
  
  <!-- Pipeline flow starts here -->  

  <u:accept-changes name="remove-tracking-markup" />
  
  <p:delete name="delete-level-2-notes" 
    match="/chapter/section/section/note" />
     
</p:pipeline>

What's happening is that we are declaring a new step type called "u:accept-changes," and then invoking an instance of it as the first step in the pipeline.  A few notes:

  • The u:accept-changes step declaration itself is not executed directly; only it's instance on line 21 is.  More formally, a pipeline element that is the child of another pipeline element (unlike what we've seen before) is used to define a step that can be invoked later. As an analogy to other programming languages, you are defining a object type and then creating an instance of it.  As a result, there is no need in this pipeline for explicit piping; the default implicit connections suffice.
  • A step's type attribute is different than its name attribute.  The value you place in type becomes the name of the element you use to invoke it (lines 11 and 21).  That instance can itself be given a name, which can then be used when controlling the flow of XML using pipes.  So, a type refers to the object, and a name refers to the instance.
  • A step's type must be in a non-XProc namespace, which you must remember to declare. 
  • The p:serialization element defines how a specified pipeline output port serializes XML when outputting from the pipeline.  It is the equivalent of the xsl:output instruction in XSLT, and takes many of the same options.

Of course, if you're going to just reference your new step type once within the same pipeline that you've declared it in, there's not really much of a point.

The real power of declaring your own step type comes from the ability to place it in an external library and create instances of it within any number of pipelines.  Here's how you do that, using p:import.  First, the library document:

<p:library 
    xmlns:p="http://www.w3.org/ns/xproc" 
    xmlns:atict="http://www.arbortext.com/namespace/atict"
    xmlns:u="http://www.wordsinboxes.com/xproc">

  <p:pipeline type="u:accept-changes">    
    <p:xslt>
      <p:input port="stylesheet">
        <p:document href="accept_changes.xslt"/>
      </p:input>
    </p:xslt>
  </p:pipeline>
       
</p:library>

Note the root element p:library, which is used as a container for a collection of custom steps.  If I wanted, I could have made the p:pipeline element the root element, and imported that single step, but using p:library leaves more room for future growth.

Here's the final pipeline, which imports the above library:

<p:pipeline name="delete-notes" 
    xmlns:p="http://www.w3.org/ns/xproc" 
    xmlns:atict="http://www.arbortext.com/namespace/atict"
    xmlns:u="http://www.wordsinboxes.com/xproc">

  <p:serialization port="result" encoding="utf-8" method="xml"  
    omit-xml-declaration="false"/>

  <!-- Import accept-changes step -->

  <p:import href="lib.xpl" />
  
  <!-- Pipeline flow starts here -->  

  <u:accept-changes name="remove-tracking-markup" />
 
  <p:delete name="delete-level-2-notes" 
    match="/chapter/section/section/note" />
     
</p:pipeline>

There.  A nice and simple pipeline.  I (and anyone else) can reference this u:accept-changes step in any number of pipelines, and never worry about how it actually works.  (And, if you stored the transform inside the u:accept-changes definition using p:inline, it would be even more portable.)  Later, if I decide that I'm better served by using a Python script to accept changes instead of a transform, I can simply update the step declaration (using p:exec) once in the library.  My hope is that XProc will become a common language for sharing XML manipulation tasks. 

It's also worth noting that if I gave this note-deleting pipeline a type attribute, then it too could be invoked as step in another pipeline.  And that pipeline could be invoked as a step, and so on.  Turtles all the way down.

image

Here's where the difference between an atomic step and a compound step comes into play.  Although we defined our accept-changes step as a compound step (because it contains a subpipeline of one step), when we invoke an instance of it, that instance is an atomic step

If that is confusing (and it sure confused me at first), it helps if you think about the step from two separate perspectives — from within, looking at the implementation details, and without, looking at what actually enters and leaves the step.  We've already covered the inside.  From the outside, when you call an instance, that instance is a black box which XML enters and XML leaves — exactly like a built-in atomic step.  The fact that it's actually implemented by assembling XProc steps is irrelevant to the calling pipeline. 

There's one more important XProc fact I've talked around but never explicitly stated.  The only information that can flow between steps through pipes is XML. At first, I thought this a stifling restriction, but as I work more with XProc, I think that this prohibition is the key to its power. 

Because, when you combine all these ideas, you end up with a very high level of enforced encapsulation that provides a very simple yet powerful abstraction for working with XML.  It allows you to worry about what needs to be done instead of how.  When any pipeline can be used as an atomic step, you can work fractally, making parts out of the same raw material as the whole, creating higher and higher levels of abstraction.

image 

So, to summarize:

  • A pipeline is a step.
  • Although the declaration of a step may be a compound step, its invocation is not. 
  • XML is the only information that can flow between steps through pipes. 

That's it for now.  If you're looking for more XProc information, Dave Pawson has been working on his introduction to XProc, which is shaping up to be a great resource.

I may start posting my XProc-related material in a more suitable space, away from my blog.  I'm considering using Docbook Website system.  Any thoughts?

Tuesday, December 16, 2008

Recently In Markup

Sunday, December 07, 2008

Getting Started With XProc, Part II

In my previous post, I showed how to set up a batch script to run Calabash in Windows.  This time I'll show how to write a useful — if simple — XProc pipeline.  Here it is:

<p:pipeline xmlns:p="http://www.w3.org/ns/xproc">

  <p:xslt>
    <p:input port="stylesheet">
      <p:document href="sort.xslt" />
    </p:input>
  </p:xslt>

  <p:xslt>
    <p:input port="stylesheet">
      <p:document href="to_wordml.xslt" />
    </p:input>
  </p:xslt>

</p:pipeline>

So what happens here?  When you execute it from the command line using:

runcalabash -i "source=in.xml" -o "result=out.xml' sort-and-print.xpl

this pipeline reads the input XML (in.xml), executes the first transform on it, passes the result to the second transform, executes that transform, and the saves the result as out.xml.  But the syntax of XProc is clear enough you probably guessed that on your own. 

The fundamental metaphor of XProc is that XML data "flows" into a pipeline and between its steps through a series of connected ports.  The two most common and important ports are the primary input port (usually called "source") and the primary output port (usually called "result").  There are others, and not every step actually has both of these, but that's another discussion. 

This isn't obvious in the above pipeline because all of the input and output ports, and the connections between them, are implicit.  There are more explicit ways to state most of the pipeline.  For example, it turns out that p:pipeline is just syntactic sugar for p:declare-step.  In other words:

<p:pipeline xmlns:p="http://www.w3.org/ns/xproc" >
</p:pipeline>

is equivalent to:

<p:declare-step xmlns:p="http://www.w3.org/ns/xproc" >
  <p:input port="source" primary="true" />
  <p:input port="parameters" kind="parameter" />
  <p:output port="result" primary="true" />
</p:declare-step>

Expanding on this, here's a more explicit version of our original pipeline:

<p:declare-step name="sort-and-print" xmlns:p="http://www.w3.org/ns/xproc">
  <p:input port="source" primary="true"/>
  <p:output port="result" primary="true">
    <p:pipe step="print" port="result" />
  </p:output>
  <p:input port="parameters" kind="parameter" />
  
  <p:xslt name="sort">
    <p:input port="source">
      <p:pipe step="sort-and-print" port="source" />
    </p:input>
    <p:input port="stylesheet">
      <p:document href="sort.xslt" />
    </p:input>
    <p:input port="parameters" kind="parameter">
      <p:pipe step="sort-and-print" port="parameters" />
    </p:input>
  </p:xslt>

  <p:xslt name="print">
    <p:input port="source">
      <p:pipe step="sort" port="result"/>
    </p:input>
    <p:input port="stylesheet">
      <p:document href="to_wordml.xslt"/>
    </p:input>
    <p:input port="parameters" kind="parameter">
      <p:pipe step="sort-and-print" port="parameters" />
    </p:input>
  </p:xslt>
  
</p:declare-step>

That's quite a mouthful, but it exposes the relationships between all the steps.  There are a few things worth noting.  First, the p:pipe element connects the input of a step to another step.  In the above pipelines, these elements are redundant, since XProc automatically connects sequential sibling steps, but they become necessary when you need to connect non-sibiling steps in larger, more complicated pipelines.

Second, every step can be given a name using the "name" attribute, and that name must be unique within the pipeline.  These names can be referenced by the "step" attribute of p:pipe to explicitly connect ports.  If a step isn't given an explicit name, one is generated internally by the XProc processor. 

Third, the p:xslt step gets its stylesheet from a port that is just like any other port.  Most of the time, you will explicitly point to a stylesheet using a p:document element, but you have a lot more flexibility than that.  For example, you can programmatically create a stylesheet in one step, and then pipe it to a p:xslt to execute it.  This would be a great alternative to using saxon:compile-stylesheet() and saxon:transform() within a single, multiple pass stylesheet. 

Fourth, p:output works a bit differently than you might expect (as talked about recently on xproc-dev here and here).  The p:output step does not define where the output for a step goes; it defines where the output comes from.  So, while it may initially seem like this

<p:output port="result">
  <p:document href="out.xml" />
</p:output>

will send the output to out.xml, it instead sends the content of out.xml to the "result" output port.  What this means is that where the output port of a pipeline or a step goes cannot be defined within that pipeline or step.  It must be defined outside that step or pipeline — for example on the command line or in the input port of another step. 

(If you want the other effect, you can use p:store, which can be thought of as the equivalent of <xsl:result-document/> in XSLT, but in that case the primary output from is actually the name of the file you are writing and not the actual content of the output.) 

That's it for now.  Next time:  How to create and use a library of custom XProc steps.

Saturday, December 06, 2008

This Week in Google Reader

Friday, December 05, 2008

Typography and the New York City Subway

An AIGA article and a New York Times blog post about the history of the typography of the NYC subway system are both good reads: 

Helvetica was originally created in Switzerland. It was a neutral typeface from a neutral country and gained runaway popularity starting in the 1960s for its modern grace. But the subway system looked elsewhere.

“It was an incredibly courageous thing to do at a time when Helvetica was riding high,” Mr. Shaw said.

That's right.  Typography as an act of bravery. 

(via Daring Fireball)

Thursday, December 04, 2008

Getting Started With XSpec

Lately I've been working with XSpec, a new testing framework for XSLT created by Jeni Tennison. I dabbled with Jeni's old testing framework, Tennison Tests, but I never could muster the discipline to use it as much as I should. XSpec makes testing frictionless, which is really how it must be if I'm going to make it a habit. 

Here's how to integrate it into Oxygen:

xspec_toolbar

On Windows, XSpec is launched by running a batch file. It's simple to hook up this batch file to Oxygen as an external tool so that you can run an XSpec suite by clicking a button.  By being a little clever - and sticking to a consistent naming convention - we can set it up so that we can activate the tests either from the XSpec document itself, or the XSLT you're developing.  The caveat is that you must save your XSpec documents in the same folder as the XSLT, and give it the same name as that transform, except with the extension "xspec" instead of "xslt." (For example, my_stylesheet.xspec tests my_stylesheet.xslt.) If you want to follow a different convention, adjust the following instructions appropriately.

  1. Download XSpec and unzip it somewhere in your system path. I recommend getting the latest xspec.bat and transforms, since Jeni's fixed a few bugs from the initial release.
  2. Open up xspec.bat, and edit the third line to set the CP (or class path) variable to the location where Saxon is installed. (If you don't have Saxon, get it).
  3. Now we need to add XSpec as an external tool in Oxygen. To do so, go to Tools > External Tools > Preferences > New and fill out the fields like so:

    XSpec_oxygen

    (Make sure to change the paths to match those on your computer.)

  4. I like to tell Oxygen to make XSpec-namespaced elements a different color. To do that, go to Options > Preferences > Editor > Colors > Elements by Prefix.  This makes it easier to differentiate between XSpec instructions, and the inline XML test data.

Now you should have a button on your toolbar labeled "Run XSpec Test." If it doesn't show up, make sure to activate the "External Tools" toolbar.

Here's an example of a successful test report:

xspec_report

Tuesday, December 02, 2008

Creativity and “Courageous Sucking”

From 43 Folders (emphasis mine):

I laid on the sidewalk. All the way down. On my gut on 50° of western San Francisco concrete.

And, I took my time, thinking about the aperture (all the way open for depth of field) and the available light (very little, so I put the the camera right on the ground to steady it). I snapped a dozen or more shots with slightly different settings. No idea what I was doing. People walked by, cars passed, the L barreled by, but I kept shooting until I was satisfied that I might have something. Then, I grabbed the shoe, stood up, and trotted back up the hill, triumphant, with a recovered piece of footwear, plus what I suspected might be at least one pretty good photo.

I like how it turned out.

Yeah, I know, it’s no masterpiece, but I’m proud of it for reasons of my own. Because, last night, as I was splayed prone in the fog along Taraval Street, I realized I was getting a little better at this.

Not because I’d been magically touched with mythical creativity and skill, but because for a moment I was thinking more about how to use what I’d learned to get a good photo than I was about how I might have looked while doing it. And, that felt like a small turning point.

Friday, November 14, 2008

A Man and His Chili

Every man needs a chili recipe.  As of a week ago, I have mine.

This is based on one from Allrecipies, but I modified it by halving the beef, adding more beans, and doubling most of the spices except for the chili powder, which I halved.  To me, too much chili powder obscures the other tastes.

Ingredients

  • 1.25 pounds of lean ground beef
  • 30 oz of tomato juice, maybe less.  Put this in last so you don't make it too soupy.
  • 2 cups chopped onion
  • 1/2 cup chopped celery
  • 1/2 cup chopped green bell pepper
  • 1/8 - 1/4 cup Chili powder
  • 2 teaspoons ground cumin
  • 2 teaspoons garlic powder
  • 1 teaspoon salt
  • 1 teaspoon ground black pepper
  • 1/2 teaspoon oregano
  • 1/2 teaspoon white sugar
  • 1/4 teaspoon ground cayenne pepper (more if you want)
  • 2 cans of red beans, drained and rinsed
  • 1 can of pinto beans, drained and rinsed

Directions

  1. Brown ground beef on stove.  Drain and crumble.
  2. Add all ingredients to a large kettle.  Bring to a boil.  Reduce heat and simmer very slowly for at least 2 hours, preferably more.  If you have a crock pot, use that.
  3. Serve with sour cream or cheese. 

Wednesday, November 12, 2008

Getting Started With XProc and Calabash on Windows

I've been excited for a long time about the coming of XProc, an XML pipelining language. A lot of my work involves running XML documents through series of transformations, which often means hacking up batch files or, for more permanent pipelines, writing an Ant build. Both of these methods are clunky at best and unmaintainable nightmares at worst.

Norm Walsh has been hard at work on his XProc implementation, XML Calabash. I've been experimenting with it, and while there are still issues to work out (it's alpha, after all), I'm already doing useful work with it. But since XProc is so new, there's almost no documentation available. There's the spec, of course, but that's aimed more towards implementers and less toward end users. Other than that, the only resources I've found are the Calabash documentation page, xproc-dev mailing list, Norm Walsh's 2007 presentation, and a few blog posts.

So this is the first in hopefully a series of posts aimed to create at least some initial public documentation about how to actually get things done using XProc. I'll post things as I learn. But caveat emptor - these shouldn't be confused for "best practices."

Before you can do anything, you have to get and install Calabash. Once you download it, place it somewhere in your system path. (Mine's in c:\home\scripts\calabash\.)

The next step is to create a batch file to run Calabash, runcalabash.bat:

@echo off
set CALABASH_HOME="%SCRIPTS%\calabash\calabash.jar"
set SAXON_HOME=%SCRIPTS%\saxon
set APTCUSTOM=%ProgramFiles%\Arbortext5.3\Editor\custom

set RUN_CALABASH=java -Xbootclasspath/p:"%APTCUSTOM%\
classes\resolver.jar";"%APTCUSTOM%\scripts\Framework";
"%SAXON_HOME%\saxon9.jar";"%SAXON_HOME%\saxon9-s9api.jar";
%CALABASH_HOME% com.xmlcalabash.drivers.Main -E
org.apache.xml.resolver.tools.CatalogResolver -U
org.apache.xml.resolver.tools.CatalogResolver

rem Slurp the command line arguments.
set CMD_LINE_ARGS=%1
if ""%1""=="""" goto doneStart
shift
:setupArgs
if ""%1""=="""" goto doneStart
set CMD_LINE_ARGS=%CMD_LINE_ARGS% %1
shift
goto setupArgs

:doneStart
%RUN_CALABASH% %CMD_LINE_ARGS%

You'll need to adjust the first few lines to match the paths in your system. CALABASH_HOME points to the location of calabash.jar. SAXON_HOME points to the Saxon jars, which Calabash requires to run. APTCUSTOM stores the path to my installation of Arbortext Editor, which contains the DTDs of the document types I work with and their XML catalog. If you don't need a catalog resolver, then you can omit this.

RUN_CALABASH pulls these together, creating the command that launches Calabash with a classpath containing all the java programs we need. I'm passing it the -E and -U options to activate the URI and entity resolvers, but again, you can leave that out if you want.

The next block compensates for the fact that Windows batch files can only take 10 parameters at once. Lame. (This code comes from ant.bat distributed with Ant).

Once that is done, you're ready to run your first pipeline (The following discussion is adapted from Norm's). He conveniently provides one with Calabash, so we'll start there. Here's the pipeline:

<p:declare-step xmlns:p="http://www.w3.org/ns/xproc">
<p:input port="source">
<p:inline>
 <doc>Congratulations! You've run your first pipeline!</doc>
</p:inline>
</p:input>
<p:output port="result"/>
<p:identity/>
</p:declare-step>

It doesn't do much - just echoes the input. To run it from the command line, type:

runcalabash pipe.xpl

That's it. You'll get back:

<doc xmlns:p="http://www.w3.org/ns/xproc">
Congratulations! You've run your first pipeline!
</doc>

If you want to direct the output to a file instead of to the command line, you can use the -o flag:

runcalabash -o "result=out.xml" pipe.xpl

Note the quotation marks. They are necessary because Windows batch files drop "=" when processing parameters. Again, lame.

If you want to change the input to a file instead of the inline document, you can use the -i flag:

runcalabash -i "source=in.xml" -o "result=out.xml" pipe.xpl

Finally, if you want to explore on your own, execute runcalabash.bat with no options to get a usage summary. Next time: an example of a pipeline that's actually useful.

Thursday, October 30, 2008

Quark Joins XML Party

I've lamented the apparent lack of energy in the automated XML print publishing market before.  Now that Quark, a long-time desktop publishing name, has announced a new product, things might be getting more interesting.  Details are scarce, but I'm not encouraged by the fact  the name includes the word "Solution."

Wednesday, August 20, 2008

Emacs

It's been almost a year since I first talked about my periodic upgrading-the-tools-I-work-with-instead-of-actually-working kicks.  It's happened again.  This time, the method of my pseudo-procrastination is Emacs.

Emacs, for those of you fortunate to have better things to do with your time, is the grandfather of all text editors.  It was originally written in 1976 by Richard Stallman, who apparently hasn't shaved since:

 image

Scary guy.  That picture pretty much sums up everything you need to know about Emacs.  That, and the manifestos

So why Emacs?  It's not because it's easy to pick up - it's learning curve is more of a learning cliff.  But, if I sit in front of a computer for 8 hours a day, the time I spend learning a difficult can be amortized over hundreds or even thousands of hours.  In this case, usability - the ability for a trained user to be efficient - is much more important than learnability.  And Emacs, once learned, is lightning fast.

image

Both its strangeness and speed can be traced back to 1976.  Computing then was very different than it is now.  When Stallman wrote Emacs, the capability to edit lines in a text file in a different order than they existed in the file was a big deal.  There were no personal computers, no mouse, and no arrow keys on keyboards.  Users, mostly university researchers, logged into mainframes using text-only terminals over slow connections.  This result is an interface that is strange and filled with baroque key combinations - you can use CTRL-F to move the cursor to the left - but is above all designed to be fast. 

Keyboards haven't changed, and neither have people - so a program older than personal computing itself can actually be relevant today. 

That's not to say there aren't problems.  There are plenty.  It's a free program, developed and extended by volunteers.  It's missing many modern conveniences - such as drop-down Intellisense code-completion - and many of the independent extension efforts seem to have stalled - such as ECB - seem to have stalled several years ago.  It took me about 5 Google searches to find downloadable code to make line numbers show up. 

For me, the killer feature is progressive search.  Those of you with Firefox already know how it works.  As soon as you start typing your search term, it starts searching.  It's an incredibly fast way to navigate a document, and Emacs does it better than any other program.  Once you use it, navigating a document without it feels like swimming through Jello.

But really, for many specific applications (such as XSLT debugging), you're much better off using a paid commercial product to work in.  One that was actually written in this decade.  Money actually buys you things, amazingly enough.

Am I proud of learning it?  Not really.  In the end I might be marginally more productive, but maybe not enough to justify the loss brain real estate now devoted to remembering dozens of strange keyboard combinations.  At this point it's mostly a hobby, something to distract me when I should be doing real work.

Monday, August 11, 2008

E-books Are the New Horseless Carriages

A recent post on O'Reilly's TOC blog cites an idea I'd heard of but never knew the origin of, the "technology flip test:"

At a conference years back I was sitting on a panel that was asked to talk about future of the book. As the discussion was heating up about the inevitability of the electric media, someone on the panel (I wish it had been me) proposed a flip test. He said "Let's say the world has only e-books, then someone introduces this technology called 'paper.' It's cheap, portable, lasts essentially forever, and requires no batteries. You can't write over it once it's been written on, but you buy more very cheaply. Wouldn't that technology come to dominate the market?" It's fair to say that comment changed the direction of the panel.

It's a useful way to cut through the hype and actually think about how a new technology will actually be used.  Go forth and apply it.

So what about e-books?  The idea of a "book" as we think of it can't be separated from the physical object of the book.  Long pieces of writing were printed, bound, and mass-produced because that was much cheaper and more effective than printing, binding, mass-producing, and distributing small individual chapters.  The form of writing and the medium grew to fit each other. 

I'm beginning to think that the very idea of an "e-book" is just a mental stop-gap.  Kind of like the horseless carriage:

image

The first automobiles were literally carriages without horses.  The design of a horse carriage - top heavy, open - wasn't suited at all to motor-driven transport, but that's the only way inventors knew to frame the problem.  It wasn't until later they could take the next step and stop asking "How do I make a carriage move without a horse?" and start asking "How do I design an automobile?"  A century later, no car designer thinks about horses.

So the question "Is an electronic book better than a paper book?" is the wrong question.  There's an enormous amount of writing and reading going on on the Internet today, and almost no one thinks of it in terms of "books."  I'm not sure what the right question is, but maybe it's something like "How do we get words from the people who write them to the people who want to read them?" 

Monday, August 04, 2008

The Great E-mail List Subscription Purge

One of my projects this weekend was cleaning up my Gmail account. That mostly involved hitting "archive" about 200 times and replying to e-mails that I should have replied to weeks ago. But the most important thing I did was unsubscribe from most of the 17 discussion lists I'm a member of and delete all their messages.

Chances are, if you do any programming work, you're subscribed to several such an e-mail lists. And, chances are (if you're like me), you only actually participate in perhaps one or two. My typical use pattern for most lists is to filter them into folders, browse them while I'm procrastinating, and search them when I'm looking for information. Gmail, with it's near infinite storage, encourages this archive-and-forget attitude. But the annoyance of having this background information clogging up my results when searching for my own, actual e-mail was starting to wear on me.

But no more.

image

Although the logo makes it look like its from the early 90s, MarkMail is the Google of e-mail lists. It's a collected archive of thousands of discussion lists, with a great interface and a powerful search. It's also great for browsing current messages. Most of those lists already have public archives, but next to MarkMail, they all suck. A lot. Especially the third-party archives whose sole purpose is to increase the Google presence of their host. You know who I'm talking about. It's gotten to the point where I'm actually frustrated if an e-mail list isn't on MarkMail.

This lets me use my Gmail for what it was intended for — mail. Recommended.

Wednesday, July 23, 2008

Words, Arranged

Reading Adaptive Path, I ran into a link to Wordle. It's a really fun a web app for creating word art out of whatever text you feed it. The words are sized in proportion to their frequency, and there are all sorts of settings you can fiddle with, including fonts and colors. This is one I made with the first chapter of Orwell's 1984:

image

I love the idea of word art programs, but I always walk away disappointed. This one definitely lives up to the hype, though.

Tuesday, July 22, 2008

Four Hacks for Your Books

If you're like me, you have shelves full of books, most of them old, many of which you've forgotten you even own. Even though some say you should throw away old books, I can't bring myself to do it. So they sit, unorganized, collecting dust. Here are three things I could do with them:

1. Pay someone to list them in a spreadsheet.

Visual effects expert Ron Brinkman conducted an experiment:

For some reason I’ve pretty much always held onto the books I’ve read - something I started when young and at this point it seems like it would be a shame to stop now. And I do love to be able to pull a previously-read book off of the shelf and dive back into it, even if only for a few minutes. But I also can’t look at them without contemplating how everything one has read contributes to who they have become. You are what you read, right? There’s a little part of each one of these books up in my brain somewhere…

At any rate, I’ve always thought it would be handy to have a list of them. (Okay, I’m not really sure why it would be handy… it just seems like it would, okay?)

Ron, like any sane person, had no intention of actually typing all this information himself. Instead, he took high-resolution digital photographs, uploaded them to Flickr, and paid someone $50 on Elance to read the spines and enter all of the information into a spreadsheet.

image

Once he had the data, he uploaded it into Shelfari, which is one of several social book organization web sites out there. Very cool, in an information-nerd kind of way.

(Via a tweet by Tim O'Reilly, founder of O'Reilly Media)

2. Arrange them by color.

In 2007, Los Angeles writer and blogger Callie Miller decided it would take less time to arrange her books by color than it would to sort them by author and subject. So she did.

image

Apparently she caught a some flack:

Now - I hear you. I, too, have felt that people who use books as props in their home (cretans!) and those who use books as decor (charlatans!) without ever really cracking one open are not to be taken seriously...or even considered.

What her experience shows is that people don't actually read their books nearly as much as they think they will. As a writer, Miller certainly uses her books more than most people use theirs, but to her the visual effect is worth (mostly) the extra effort to hunt down whatever book she needs at the moment.

I think economic reality is that in 10 years most publishing will be done over the Internet. It's already happening with newspapers. And as soon as there's a portable reading device good enough and cheap enough, it will happen to book-length writing. When that day comes, the visual effect of a physical book will start to matter more than the now elsewhere-available writing inside.

3. Categorize them with color.

Miller also posted about design student Valérie Madill's project, Looking at Libraries:

image

Knowing there are only 21 main subject it was important to colour coding the labels. This is a major part of having the content design the space, it is the subject that "paints" the space. ... My favourite aspect of the colours is knowing a law library's colour palette will be dramatically different then that of a Art & Design library. The colours are assigned to the subjects as a rainbow gradient since there is no such thing as 21 unique colours and the classification system is linear.

This is really cool. I'm a big fan of using color to convey information supplemental to words. Words are great, but you have to read and parse them. Not true of color.

4. Make a chair.

image

When you're tired of reading books, sit on them. Artist David Karoff made a chair out of recycled rebar and books from a library book sale. (Via Print is Dead)

Wednesday, July 16, 2008

Documenting XSLT

Many XSLT developers — including me — are not programmers by formal CS training.  But because XSLT is a relatively small domain-specific language, this usually isn't a big deal.  In fact, it's almost an advantage, since XSLT's functional-programming weirdness is very different than the procedural languages that most programmers use.  And while we XSLT developers don't always conform to software development best practices, it usually doesn't matter, since most transforms are more doghouse than skyscraper.

Usually is the key word.  When you start to build anything larger than about 500 lines of code (or more than one physical file), the quick-and-dirty approach starts to become less quick and more dirty.  Those best practices start to look ... better.

That's why I'm really excited by a couple of projects.  Most "real" languages have a method generate project documentation from comments embedded across multiple source documents.  XSLT does not.  That's the gap these projects attempt to fill. 

The first is DOXSL, which is being developed by Jim Earley.  (Jim did some excellent consulting work for my employer a couple years ago).  He sums up the need really well:

... diagnosing XSLT is a real headache. Templates can be scattered through dozens of physical files linked together through a maze of imports and includes, and knowing exactly which template is firing for a particular context can be enigmatic.

This is really frustrating for developers who want to extend an XSLT stylesheet application or modify the output behavior for a few elements. Which templates, parameters, attribute-sets, or other core components do I override? What is the logical flow? Which file is this named template located in? To answer these kinds of questions, developers can waste hours digging into the code trying to understand the logic. Even with a good set of tools to search the countless number of stylesheets for a specific text string (e.g., a template named 'foo'), there is no guarantee that there will only be a single instance given XSLT's import precedence behavior.  Developers have to trace through the import and include stack to determine which template will be fired.

When you run DOXSL, it creates a report (HTML, DITA, or Docbook) of an application's stylesheets, templates, functions, and parameters.  The coolest bit is that the report tells you if any given template overrides or is overridden by another.  If you use DOXSL elements (instead of standard XML comments) to comment your code, those comments will also be in the report.

Even if you lack the discipline to comment your code with those elements, it DOXSL is still useful — I've already put it to use interpreting Arbortext Editor's Styler-to-XSL application.  I definitely recommend checking DOXSL out, and I look forward to future developments.

The second project is XSLTdoc.  I don't have enough experience with it to have a solid opinion, but it looks promising.  XSLTdoc is modeled closely off of Javadoc.  Compared to DOXSL, the default output looks better, but that's nothing a bit of css work couldn't fix.  The big difference — and this is huge — is that it doesn't tell you when a template in one stylesheet is overridden by another in a different file.  Still, XSLTdoc is under active development, so it's worth checking back to see how it progresses.

Friday, July 11, 2008

Recent Reading

image After Dark, by Haruki Murakami. This is a strange book. I bought it while traveling in Boston, and it turned out to be the perfect book to read in a hotel alone. The dialog is atrocious, and the plot doesn't make much sense, but its dark and sparse surreality holds its own.
image The Master and Margarita, by Mikahail Bulgakov. This is an even stranger book. I was convinced to read it by this post. I didn't understand it as much as I hoped; the plot seems to rely on arbitrary magic. Maybe this guide would help.
image Dreams from My Father, by Barack Obama. It's interesting to read a book by a politician written before he was a public figure. Content and politics aside, this book proves that the possible future president doesn't need a ghost writer to assemble words into sentences.

Wednesday, July 09, 2008

Words in Boxes

From now on, this site will be called Words in Boxes.  It even has a fancy new domain name — www.wordsinboxes.com.

So, why the change?

image

Almost exactly three years ago today I started posting on this blog.  I gave it the name Indigo Flats — the name of my then-apartment building — because I lacked the creativity to do better, and I kept that name because I lacked the motivation to change it.  I posted sporadically, about random topics, and wrote with widely varying quality.  While I usually didn't worry myself much about gaining an audience, I did share the delusion of millions of bloggers that I had something to say that was worth other people's time.  The problem was that a lot of the time, I didn't.

If I'm going to blog, I should follow the example of those who do it well.  The blogs I respect and enjoy most are written by people who have a strong idea of what they want to say.  Marginal Revolution is about economics.  Coding Horror is about software development.  Cocktail Party Physics and Uncertain Principles are about physics.  The O'Reilly XML Blog is about XML.  Even kottke, who posts about seemingly everything, keeps a strong focus on design.  These blogs are fun and informative.  They justify their existence.

My blog should do the same — it must be interesting to you.  But even more importantly, it must be interesting to me.  Make no mistake, this blog is still a selfish exercise; writing it takes real time, so I need more in return than the slim prospect of minor Internet fame.

I like Jeff Atwood's explanation of why he blogs:

Mostly for selfish reasons. I needed a way to keep track of software development over time-- whatever I am thinking about or working on. I research things I find interesting, then document my research with a public blog post, which I can easily find and refer to later. Hopefully other people will find these posts helpful, relevant, or interesting. I firmly believe that blogs are a two way conversation, so I welcome email and comments-- as long as they're on topic, more or less.

The process of writing for the blog forces me to actually think about my topic — in other words, to learn.  And if I'm learning as I write, then it's still worth my time even if nobody reads it.  That's important.

So what is it that I have to say?  I work as a software developer at a book publisher.  I spend most of my time working with XML documents, and I consider myself somewhat proficient at designing XML schemas manipulating XML markup with XSLT.  Before that I worked as a copy and layout editor, and (very) occasionally as a freelance writer.  So I'm particularly interested in how the technical business of text fits in with actual people doing actual reading and writing — and of course, in the words themselves.

The name "Words in Boxes" tries to capture that theme.  If you want something more literal, you can think of an XML document as a collection of text-containing boxes:

<section><p>This is a <b>great</b> paragraph.</p></section>

So that's it.  The old name had a certain ring to it, and I will miss it.  But I'm excited about the new one.

A few technical notes:  The transition should be seamless.  The old address should direct to the new one, and the RSS feed should continue uninterrupted.  I still recommend updating any bookmarks you may have. 

Saturday, July 05, 2008

Splitting Grammatical Hairs

I never wanted to write a monster post about grammar, and I'm pretty sure no one else wanted me to either. But here we go.

Amber, in a recent post, complains that many otherwise respectable publications — such as the Economist, the New York Times, and the Wall Street Journal — commit two cardinal grammatical sins: (1) splitting infinitives, and (2) splitting compound verb phrases.

Now, what I say next is meant with all possible respect.

Who cares?

I actually care about grammar. Having an agreed-upon set of rules so that people disconnected in time and space can agree on precisely what a sentence means is vitally important. No matter what kind of writing you're doing, grammar is indispensable for saying what you mean and meaning what you say. Anyone who writes is well-served by studying it.

But there are many rules of grammar that are unimportant, and in fact aren't actually rules at all. These prohibitions are two of them. Two additional false prohibitions are (3) ending a sentence with a preposition, and (4) beginning a sentence with a conjunction.

Each one of these constructions is acceptable. Often they are preferred to their alternatives. Spending energy splitting these particular hairs is at best a waste of time, and at worst actively detrimental to good, clear writing. And the experts agree.

image

Splitting Infinitives

An infinitive is the "to be" form of a verb (to drop or to contradict). When you split an infinitive, you place one or more words between to and the verb (to quietly drop or to directly contradict). Most of us have been told at one point or another, mostly by our high-school English teachers, never to do this.

The Chicago Manual of Style disagrees (§5.160):

Although from 1850 to 1925 many grammarians stated otherwise, it is now widely acknowledged that adverbs sometimes justifiably separate the to from the principal verb.

Bill Walsh, copy chief of the national desk at the Washington Post, goes a bit further. Well, a lot further:

A lot of editors waste a lot of time and energy making editing changes that, if you'll excuse the disgusting imagery, do nothing but shove a rod up the backside of good, conversational writing.

Perhaps the best example of this is the un-splitting fetish. No matter how many knuckles have been whacked with rulers over the "split infinitive," grammar experts will testify that there is no rule — and never has been a rule — against inserting a word between the to and the verb in an infinitive (as in to boldly go where no man has gone before). Somebody somewhere made up this "rule" because infinitives were never split in Latin. Of course they weren't: In Latin, infinitives are single words.

Yeah! Grammar smack-down! Take that, editors!

image

Splitting Compound-Verb Phrases

A compound verb phrase is the combination of two or more verbs into a single verb phrase, for example would support and will go unchallenged. When you split the verb phrase, you place an adverb between these verbs, ending up with would generally support and will go increasingly unchallenged.

If you think these sound perfectly fine, you're not alone. If you were to unsplit these verb phrases, you would end up with generally would support and increasingly will go unchallenged. At least to my ear, these "corrected" constructions are harder to parse than the originals.

An even better example comes from Patricia T. O'Connor's Woe is I: you cannot change Your landlord expects to more than double your rent into Your landlord expects more than to double your rent and make any sense.

Bryan Garner, in his Garner's Modern American Usage, opens his entry on adverbs with the following (page 23):

Many writers fall into awkward, unidiomatic sentences when they misguidedly avoid splitting up verb phrases. Although most authorities squarely say that the best place for the adverb is in the midst of the verb phrase, many writers nevertheless harbor a misplaced aversion, probably because they confuse a split verb phrase with the split infinitive. H.W. Fowler explained long ago what writers still have a problem understanding: "When an adverb is to be used with [a compound] verb, its normal place is between the auxiliary (or sometimes the first auxiliary if there are two or more) and the rest. Not only is there no objection to thus splitting a compound verb ..., but any other position for the adverb requires special justification."

Garner then goes on to quote six more independent grammar authorities who agree. Apparently, this "rule" really gets his goat.

The Chicago Manual says the following (§5.160):

When an adverb qualifies a verb phrase, the natural place for the adverb is between the auxiliary verb and the principal verb. ... There is no rule against adverbial modifiers between the parts of a verb phrase. In fact, it's typically preferable to put them there. ... Recasting a sentence just to eliminate a split infinitive or avoid splitting the infinitive can alter the nuance or meaning: for example, it's best always to get up early (always modified get up) is not quite the same as it's always best to get up early (always modified best). Or an unnatural phrasing can result: it's best to get up early always.

In case it's not clear, that's Chicago Manual speak for "grammar smack down!"

Ending a Sentence with a Preposition

Bryan Garner opens his discussion of sentence-ending prepositions thusly:

The spurious rule about not ending sentences with prepositions is a remnant of Latin grammar, in which a preposition was the one word that a writer could not end a sentence with.

Snap! Didn't see that one coming, did you?

A preposition is a word that links nouns or pronouns in a sentence, usually indicating a relationship between them. Typical prepositions include on, beneath, during, and with. Some consider placing one at the end of a sentence a faux pas, but really, why would you choose to write "a habit to which I want to stick" instead of "a habit I want to stick to"?

Garner continues:

But Latin grammar should never straitjacket English grammar. If the superstition is a "rule" at all, it is a rule of rhetoric and not of grammar, the idea being to end sentences with strong words that drive a point home. That principle is sound, of course, but not to the extent of meriting lockstep adherence or flouting established idiom.

The Chicago Manual (§5.169) concurs:

The "rule" prohibiting terminal prepositions was an ill-founded superstition.

And so does Winston Churchill:

That is the kind of arrant pedantry up with which I shall not put.

image

Indeed.

Beginning a Sentence with a Conjunction

Conjunctions are words such as and or but that join two clauses, phrases, or words together. Some people believe it's bad form to begin a sentence with one. But they're wrong. When you place a conjunction at the start of a sentence, you emphasize that sentence's relationship to the previous thought. Used properly, conjunction-initiated sentences improve the timing and dramatic structure of your writing.

The Chicago Manual states (§5.191):

[A] substantial percentage (often as many as 10 percent) of the sentences in first-rate writing begin with conjunctions. It has been so for centuries, and even the most conservative grammarians have followed this practice.

It goes on to quote grammarian Charles Allen Lloyd:

Next to the groundless notion that it is incorrect to end an English sentence with a preposition, perhaps the most wide-spread of the many false beliefs about the use of our language is the equally groundless notion that it is incorrect to begin one with 'but' or 'and.' ...[N]o textbook supports it, but apparently about half of our teachers of English go out of their way to handicap their pupils by inculcating it. One cannot help wondering whether those who teach such a monstrous doctrine ever read any English themselves.

So, yeah. Don't mess with him, either.

Concluding this Post

So there you have it. Overwhelmingly, experts agree that none of these four "rules" are really rules at all. But the most important take-away message is this: Do not annoy grammarians with spurious rules, or they will write snarky things about you in reference books.

In a sense, whether such-and-such a grammatical practice is technically correct is beside the point. You should never lose sight of the fundamental purpose of writing: to communicate ideas. Grammar is a convention users of a language have agreed upon to serve that singular purpose. Any "error" that does not introduce ambiguity or distract your readers is probably not actually an error, or at least not one you should worry about.

When you bend your diction to avoid breaking one of these "rules," you're actually doing your reader a disservice. Uncommon or overly-formal constructions are road-blocks that force your reader to stop and pay attention to your language rather than to your ideas.

Wednesday, July 02, 2008

The Unsolvable Problem

(There are experts in my field.  I'm not one of them.  By far.  Remember that when I start selling you broad generalizations.) 

Rick Jelliffe, in two recent posts on the O'Reilly XML blog, discusses some of the technologies and problems behind taking XML content and programmatically laying it out on a printed page.  He doesn't say it, but I will — if you really and truly care about quality, it's a fundamentally impossible problem to solve.

One of my big work projects for the past year has been developing such a system for our books.  By "books" I mean law books, heavy thousand-page tomes full of section symbols and case law citations.  Like a lot of technical publishers, our content is stored in XML.  Well, mostly.  We're still converting it from Word documents and refactoring our schemas, which is part of the charm and frustration of the whole enterprise.

XML turns out to work pretty well for data-heavy documents.  As opposed to a Word document, you identify information not by how it is styled (bold or italic), but instead what it is (a phone number or recipe ingredient).  In other words, you're taking the semantic information that is usually only implicitly available through a document's formatting and making it explicit.  Two advantages of this, for those who haven't drunk the XML Kool-Aid, are the following. 

  • First, it's much easier to extract information from that document to create things like reports or tables of contents. 
  • Second, by separating the content from the presentation, it's easy to publish that document in multiple formats — for example, in a printed book or on the Internet. 

There's a whole philosophy on designing markup schemas, but now's not the time to get into that.

But having divorced content from presentation, you're now faced with a very serious problem: How do you put them back together?

In theory, it's simple — you've defined what each chunk of content is, so you should be able to define in a program how each of those chunks should be displayed, and how it should interact with the other chunks of information near it in the document.

In reality, for print, this can be incredibly frustrating.  Jelliffe provides a good representative example:

To give an idea of what I mean by expert (also known as “quality” or “industrial”) typesetting and decision-making, consider the case of typesetting a Yellow Pages (phone directory for businesses categorized by type of business.) Imagine you have to produce a Yellow Pages document using your favorite tool. The page designer and sales force come up with a design and timetable. The layout will be five columns. Entries may not span pages. Some entries take up part of a column and should be put as near to alphabetical order as possible, but rather than break they can be placed before or after their alphabetical position with previous or subsequent entries swapped before them. And there may be two, three, four or five column display ads, which also have this arrangement. And there can even be ads that take a half page but span over two pages.

And it is important that ads should not be orphaned or widowed, with one ad on a previous page by itself or on a subsequent page. And there are 6,000 pages of this. And you get the final data 24 hours before you have to deliver it.

As those of you who have used InDesign or FrameMaker know, laying out a book or a newspaper isn't too difficult; it's just a series of aesthetic judgment calls.  A person, given enough time, would have absolutely no problem arranging page elements in the above telephone book.  However, when you try to create deterministic rules to do it, you start to run into problems.

yellow pages

One is simply the available tools — inexplicably, despite the explosion of XML usage in publishing in the last 5 years, the state of the art in automated XML layout software has not kept pace.  The system we use was launched in 1983, and still employs the same Unix user interface that we have to emulate in Windows.  It's very powerful, and gets the job done well, but does it in the same way a two-decade-old Ford pickup reliably hauls bales of hay. 

A second problem is limited lookahead ability.  In other words, how much space should you reserve for an automatically generated page number in a cross reference to an upcoming chapter?  You don't know the page number until you lay out every page up until that upcoming chapter.  If you leave too little space — say for a two-digit number instead of a three-digit number — then content will be bumped forward, which means you have to re-lay-out the rest of the book, which means that the page number will change... you can see the problem.

But the biggest issue is that page layout belongs to that class of problems that are really easy for a human but really hard for a computer.  Fundamentally it's an aesthetic problem, and aesthetic problems can never be fully solved by a series of deterministic rules. 

That's a bit dramatic.  I'm actually proud of the results I'm getting with my combination of XSLT 2 and XPP, and there are plenty of people out there doing far better work.  If you're willing to limit your markup, and limit what you expect your output to look like, you can actually get very good results using today's technology.

That's the key — accepting limits.  Your XML schema delimits the entire universe of what your content can say, and your programmed layout rules delimit the entire universe of what your pages can look like.  Remember, the entire point of XML is that you mark your content with semantic meaning, not with formatting information.  So you can never really deviate from that one-to-one relationship between semantics and formatting.

Going back to the telephone book, let's say for some reason that one company's listing, unlike all of the others, really needs to list an e-mail address.  It's really easy for a person manually laying out those pages to adjust on the fly and do these things.  But if you haven't accounted for a particular type of information both in your schema and in your layout rules, you just can't do it.  In other words, if your phone book schema doesn't include an <email> element, then you can't put an e-mail address into your phone book, and you certainly can't style it.

(Well, you can, by putting the e-mail address in a <phone> element because they look the same on the page.  But this creative tagging is very, very, very bad for reasons I shouldn't need to explain.)

Now, I'm purposely calling the layout problem unsolvable to make a point.  There are plenty of companies doing it and making money (including mine).  But they all, I suspect without exception, made compromises to get there.  All have solutions approaching the ideal, but never reach it.  Truly automating page layout is fundamentally impossible — but that's what makes it so interesting.

Monday, June 30, 2008

The District Needs a Bath

It's often argued that a divided government — where one party controls Congress and the other controls the White House — results in more balanced, centrist governance than that of a united government.  In other words, by turning on both the hot and cold water faucets in the federal bathtub, we can all bathe in water of a pleasing temperature. 

This argument concludes a column recently shared by Amber:

Divided government doesn't ensure good government, but it may limit bad government by checking the worst instincts of both parties.

I'm sympathetic to the principle, but it's a principle that sounds a lot cleaner and loftier as an abstract term than it ends up being in the dirty politics of real life.  A better metaphor might be two equally matched teams trapped in a tug-of-war stalemate.

Take for example the recent confirmation of Elizabeth Duke to the Federal Reserve Board (also posted about by Amber).  The seat had been vacant for a long time, and it's only after extended wrangling between the Senate and the White House that a compromise was reached and anything was done.  This isn't uncommon.  Towards the end of 2007, NPR and other media were full of stories of relatively non-political government agencies and domestic safety programs underfunded and unable to adequately plan their next fiscal year because the Federal budget was so delayed because of just this sort of stalemate.

Maybe Congress is to blame.  Maybe the White House is.  But it doesn't matter.  The fact that the Fed is non-partisan and yet nothing got done for so long is exactly the point — divided government may prevent bad governance, but it too often prevents any governance at all. 

In any major undertaking, professional or political, participants should disagree.  They should call out each other's bullshit.  They should question each other's assumptions.  But all of this requires a constructive working relationship, and if a private company's leadership had relationships as dysfunctional as our federal government does, the company would have been out of business a long time ago.

Of course, whether or not you actually agree with what gets done in a united government is a completely different question.  But if you want a divided government, you have be willing to put up with the mess.

I'm James Sulak, a software developer in Houston, Texas.

You can also find me on Twitter, or if you're curious, on my old-fashioned home page. If you want to contact me directly, you can e-mail comments@wordsinboxes.com.