Maintained by: David J. Birnbaum (djbpitt@gmail.com) Last modified: 2024-07-01T22:00:54+0000
The Russian poet Vladimir Vladimirovič Majakovskij laid out a large portion of his verse
in echeloned lines, which can be considered a graphic representation of his declamatory
style. One poem that utilizes this typographic layout is Majakovskij’s Stixi o
sovetskom pasporte
, translated into English as My Russian passport
. Echeloned layout was not unique to Majakovskij; it
was, for example, copied by the US poet Frank O’Hara in his Ave Maria
.
There are several challenges to reproducing echeloned layout in HTML. One challenge is
that HTML collapses sequences of space characters into a single space, which makes it
impossible to use multiple regular spaces to control the amount of indentation. This can
be overcome by using non-breaking spaces, which are not collapsed, but the developer
must then know how many non-breaking space characters to use for each indentation. Or
the developer can use regular spaces inside a <pre>
element (or apply
the appropriate CSS property to a different element), which prevents the collapse of
consecutive white-space characters. That task, using either of those strategies, is
relatively easy with a monospaced font, since the developer can just count the number of
characters in the preceding line(s), but typically developers would prefer to use
variable-spaced fonts because those are more culturally appropriate and expected in the
representation of poetic texts. The sample at http://pishi-stihi.ru/stihi-o-sovetskom-pasporte-mayakovskij.html uses a
monospaced font inside a <pre>
element, and you can view the source
to see the use of regular space characters. With a variable-spaced font it’s easy to get
it wrong, as at https://www.marxists.org/subject/art/literature/mayakovsky/1929/my-soviet-passport.htm,
where we can see that the indentations overlap where they shouldn’t, and you can view
the source to see the use of non-breaking space characters. Alternatively, the developer
can hard-code the amount of indentation for each line using CSS, but that requires
calculating the exact measurement, and it is not clear how that process could be
automated, which means that it must be done manually for each individual line. This is
what http://feb-web.ru/feb/mayakovsky/texts/ms0/msa/msa-068-.htm does; you can see
the underlying HTML and CSS by viewing the source.
What we want is to indent each line by the length of the preceding line(s) in an enviroment where the length of the preceding line is difficult to calculate. One type of simple solution, then, would be to use the length of the preceding line without knowing (or having to calculate) what that length is.
Suppose we have just the first echeloned set from the English version of the poem:
I'd tear
I'd tearlike a
wolf
I'd tear like a wolfat bureaucracy.
In the example above, we’ve measured the length of the preceding line(s) in the most
direct way possible—by not measuring them. What we’ve done instead is reproduce the
actual text in the place where the indentation is needed, so that the length is exactly
the length of the indentation, and we’ve made that place-filling text invisible by using
the CSS visibility
property with the value hidden
. Not only does
hidden visibility make the text invisible to the human eye (something we could
alternatively have done with white text on a white background), but it also makes it
invisible to searching, selecting, etc. (try searching or selecting that hidden text
now), so it behaves in the browser as if it didn’t exist except that it occupies
space, which is exactly the combination of behaviors we need. Here’s how it
looks under the hood, with the markup exposed:
I'd tear
<span
class="echeloned">I'd tear</span>like a wolf
<span class="echeloned">I'd tear like a
wolf</span>at bureaucracy.
and the associated CSS reads:
.echeloned {visibility: hidden;}
The most common form in which we’re likely to encounter echeloned poetry is plain text, so let’s assume that we've found some on line and we want to convert it to formatted HTML. We can do the entire transformation from the plain text using XSLT, but in order to concentrate here just on creating the HTML output, let’s convert the plain text first to XML using Find-and-replace operations inside <oXygen/>. Here’s the procedure, using the English-language example at https://www.marxists.org/subject/art/literature/mayakovsky/1929/my-soviet-passport.htm:
Case sensitive,
Wrap around, and
Regular expression. Under
Regular expression, be sure that
Dot matches allis unchecked (which is the default).
Findbox, type
.+
(a period followed by a plus sign) and
in the Replace withbox type
<line>\0</line>
. Then
hit the Replace Allbutton. This has the effect of wrapping every line in
<line>
tags. (If you aren’t familiar with using regular
expressions, you can read about how this works by exploring the tutorials at http://www.regular-expressions.info/quickstart.html.)<poem>
start tag at the very beginning and a
</poem>
end tag at the very end. Save the file with the
filename poem.xml(the
.xmlextension is important), close it out of <oXygen/>, and then open it. The reason you have to close and reopen it again is that you created it as plain text, and even though you’ve saved it as an XML file, <oXygen/> doesn’t know that it’s XML (and therefore can’t transform it with XSLT) until you reopen it as XML.
Each line that has no leading space characters should begin at the left margin, and subsequent lines should be indented by the aggregated length of all of the preceding line(s), but only going back to the first preceding non-indented line. In other words, each non-indented line plus any following indented lines can be considered an indentation group, and the group ends and the next one begins as soon as another non-indented line is encountered (or at the end of the poem).
Once we understand our indentation groups conceptually, we can identify them
programmatically by using the XSLT 2.0 <xsl:for-each-group>
element.
The version we’ll use is the following:
<xsl:for-each group select="line" group-starting-with="line[not(starts-with(.,' ')]">
This creates a new group for each sequence of lines that begins with a non-indented line. We then iterate over each of the lines in the group with:
<xsl:for-each select="current-group()">
For each line in the group we create a <p>
. We then test whether the
group member we’re processing at the moment is the first member of the group, that is,
the non-indented line. We use a negative test (if the position of the current group is
not 1), and if the test succeeds, we create a <span>
with a @class
attribute that has the value echeloned
. Inside that
<span>
, we apply templates to all members of the current group
that appear before the current line. Whether we have a preceding
<span>
or not, we always end the processing of a line by applying
templates to the current line itself, so that it will be rendered normally:
<xsl:for-each select="current-group()"> <p> <xsl:if test="position() ne 1"> <span class="echeloned"> <xsl:apply-templates select="current-group()[. << current()]"/> </span> </xsl:if> <xsl:apply-templates/> </p> </xsl:for-each>
Here is the full XSLT stylesheet:
<?xml version="1.0" encoding="UTF-8"?> <xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:xs="http://www.w3.org/2001/XMLSchema" exclude-result-prefixes="xs" version="2.0" xmlns="http://www.w3.org/1999/xhtml"> <xsl:output method="xml" indent="yes" doctype-system="about: legacy-compat"/> <xsl:template match="/"> <html> <head> <title>Echeloned layout example</title> <style type="text/css"> p{ margin-top:0; margin-bottom:0; } .echeloned{ visibility:hidden; }</style> </head> <body> <xsl:for-each-group select="//line" group-starting-with="//line[not(starts-with(., ' '))]"> <xsl:for-each select="current-group()"> <p> <xsl:if test="position() ne 1"> <span class="echeloned"> <xsl:apply-templates select="current-group()[. << current()]"/> </span> </xsl:if> <xsl:apply-templates/> </p> </xsl:for-each> </xsl:for-each-group> </body> </html> </xsl:template> </xsl:stylesheet>
You can try it yourself with poem.xml and poem.xsl. The output of our transformation is at poem.xhtml. If you run the transformation inside the <oXygen/> XSLT
debugger, the formatted HTML output will be garbled because the HTML viewer in the
debugger (which isn’t designed as a full-featured HTML browser) doesn’t support the CSS
visibility
property. But you can see in the text
view that the
output is correct, and if you save the transformation and open it in a real browser, the
indentation will be correct.