You’re welcome!
Yes, this is possible. Generally speaking, when asking if XSLT can do “X”, the answer is yes. It’s mainly a matter of how much you want to dig into the language. The key idea here is to somehow create an exception to the rule. We want a set of table codes that does something one way for most of them, and another way for the special ones. Tables are pretty good for this, as we have a valuable tool at our disposal: IDs. When you supply a caption to a table with a bracketed phrase after the table, as demonstrated above in MrGruff’s code sample, the square bracket becomes the visible caption, but it is also compressed and used as the ID of the caption within the table in the XHTML—and with XSLT we can search for IDs very easily. We can even search for parts of them, which is how I would approach this particular problem. This will get a little more tricky, but what it will do is allow you to typeset a table in simple mode, simply by giving it a special name prefix, a prefix that is then stripped out of the final reader accessible caption.
The ‘match’ attribute is where you supply the XSLT template with criteria. Currently, the ones you’ve created in this wrapper file are targeting all tables and their components. We want to target only some of them and leave the rest alone. Let’s use the ID prefix “SIMPLE” in all caps. This means if you want a table to be typeset without rules, it should look a bit like this (and incidentally, this also left-aligns; pay heed to the style of dashes used below the header row. A colon on the left means left-aligned, on the right, right-aligned. No colon means default centre alignment).
| Test | Table |
| :--- | :---- |
| A | B |
| C | D |
[SIMPLE And here is the public caption.]
The result we want is the “SIMPLE ” part stripped out so only “And here is the public caption.” shows up in the final render. Note that letter case is completely ignored. I have the word all-capped to make it stand out in Scrivener, but the final internal result of this will be ‘simpleandhereisthepubliccaption’. So naturally, if any of your captions start with ‘Simple…’ you might want to pick something that isn’t or doesn’t form a dictionary word when compressed, like SIMTAB.
The first thing to do is adjust the ‘match’ values on these templates. Here they are as-is (without the rest of the code, of course):
<xsl:template match="html:thead">
<xsl:template match="html:table">
<xsl:template match="html:tbody">
The easiest way to handle this is via modes. Modes are a way of supplying alternate templates for the same element matches, depending on context. You saw one of these already. The ‘thead’ template calls for the ‘html:tr’ template in ‘header’ mode, which does something different than the template for ‘html:tr’ without a mode (and both of those are handled in the base XSLT, not in our custom one). We’ll do the same thing here so that the complex match only needs to be performed once.
So, working in the same pattern of small changes and testing before proceeding, let’s set up the basic network and let it fail first. Change these four template items to the following, and while you’re at it, copy in the ‘html:caption’ template and modify it as below, too.
<xsl:template match="html:thead" mode="simpleTable">
<xsl:template match="html:table[child::html:caption[starts-with(@id, 'simple')]]">
<xsl:template match="html:tbody" mode="simpleTable">
<xsl:template match="html:caption" mode="simpleTable">
Okay, the heavy lifting here is done by the ‘html:table’ template, which is now featuring a criteria in brackets that must be matched for this template to trigger. If the table does not have a caption, or the caption doesn’t start with ‘simple’ then it skips over this and uses the main ‘html:table’ template in the other file (and with it, ruled output). Meanwhile the secondary templates have all had their modes changed to “simpleTable”, which means they will no longer be used by anything—we haven’t fixed the routing yet.
Briefly, the criteria works like this: if you put a square bracketted expression into the match pattern, you can narrow things down by the criteria within the brackets. For example:
match="html:span[@class='annotation']"
That will catch all of the annotation spans that Scrivener puts into a compiled document, letting you do something different with the annotation, if you so pleased.
So in this scenario here we are supplying a double-criteria: First, the table must have a caption child, and secondly, that caption child needs to have an ID that starts with the text ‘simple’. We use the special [b]starts-with()[/b]
function to determine that. The syntax is [b]starts-with(haystack, needle)[/b]
; so in our case, [b]starts-with(@id, 'simple')[/b]
. Using starts-with keeps the caption free for other (real) usages as well. We can embed functional directives into an otherwise freeform field.
Compile and make sure that fails (you get a ruled table). That means the selector is working. Now add a caption to your table starting with ‘SIMPLE’ and run it again. You should get a table without the \toprule, but the rest will be normal. That’s because we haven’t told the alternate table template to use the new simpleTable mode for its children, so only the basic skeleton of the table is altered by this exception.
So the next step is to wire this all up. Here is what the modified table code should look like:
[code] <xsl:template match=“html:table[child::html:caption[starts-with(@id, ‘simple’)]]”>
xsl:text\begin{table}[htbp]
\begin{minipage}{\linewidth}
\setlength{\tymax}{0.5\linewidth}
\centering
\small
</xsl:text>
<xsl:apply-templates select=“html:caption” mode=“simpleTable”/>
xsl:text\begin{tabulary}{\linewidth}{@{}</xsl:text>
<xsl:apply-templates select=“html:col”/>
xsl:text@{}} \ </xsl:text>
<xsl:apply-templates select=“html:thead” mode=“simpleTable”/>
<xsl:apply-templates select=“html:tbody” mode=“simpleTable”/>
<xsl:apply-templates select=“html:tr”/>
xsl:text\end{tabulary}
\end{minipage}
\end{table}
</xsl:text>
</xsl:template>[/code]
This will check for the existence of a caption starting with ‘simple’ and then proceeds to handle the table without rules, by invoking the respective templates that use the simpleTable mode. Note for those templates you haven’t brought over and modified, we leave the mode alone as the stock stuff is fine.
If you compile with that, you should get a clean table. Strip out the caption, or remove the keyword from the front, and you’ll get a normal table. Great, that’s most of the problem. Now we just need to strip out the keyword that was used to set the mode for this type of table. Otherwise your readers will see “SIMPLE Blah blah blah” beneath the table in the book. 
For that we’ll use a handy search and replace function that MMD uses at a higher level. The code for it is, I think, in the main xhtml2latex.xslt file, but wherever it is, it is upwards in the include chain, so we have access to it.
We’ll put this into the custom html:caption template we already copied over. Here is the adjusted template:
<xsl:template match="html:caption" mode="simpleTable">
<xsl:param name="strippedCaption">
<xsl:call-template name="replace-substring">
<xsl:with-param name="original">
<xsl:apply-templates select="node()"/>
</xsl:with-param>
<xsl:with-param name="substring">
<xsl:text>SIMPLE </xsl:text>
</xsl:with-param>
<xsl:with-param name="replacement">
<xsl:text/>
</xsl:with-param>
</xsl:call-template>
</xsl:param>
<xsl:if test="not(@id='simple')">
<xsl:text>\caption{</xsl:text>
<xsl:value-of select="$strippedCaption"/>
<xsl:text>}
</xsl:text>
<xsl:if test="@id">
<xsl:text>\label{</xsl:text>
<xsl:value-of select="@id"/>
<xsl:text>}
</xsl:text>
</xsl:if>
</xsl:if>
</xsl:template>
Basically all of the changes are at the top with two exceptions. The first 12 lines within the template invoke the replace-substring template defined elsewhere, and assigns the result to a parameter. The replace-substring template takes three parameters, the original text, the text you are searching for, and finally the text you wish to replace the searched text with. It’s just a basic search and replace tool. We’ve given the contents of the caption element, [b]node()[/b]
as the original text, tell it to look for “SIMPLE ” (note the space), and replace it with an empty text value—stripping it out. This is all done inside of a parameter declaration which we’ve called “strippedCaption”.
By itself that would do nothing. The $strippedCaption parameter would cease to exist after this template is done and that would be that. So we need to also change the line where the caption itself is emitted. Before, it took the contents of the caption element, just as we provided to the search and replace function. We want it to use the parameter we created, instead, since that variable now holds the clean version of the caption. So this line is changed to [b]<xsl:value-of select="$strippedCaption">[/b]
. And that is how you insert the contents of a variable into a precise location.
We don’t do this for the ID, because the ID might very well be used elsewhere. Internally, MMD uses these to cross-reference, so if you want to refer to this table elsewhere in the text, you’ll be using its full caption as you see in Scrivener. You could change the \label portion to use the cleaned version, but then you’d have to remember to not copy and paste the SIMPLE part of any table you link to. That’s just up to you, really. The ID/label is effectively invisible once the .tex file is rendered, so it’s no big deal to have the simple keyword in it.
I like to leave this stuff intact because it makes the document more portable. If I don’t want to use this XSLT for some reason, I can switch it off and the IDs will still match up. If I was relying on the XSLT to clean the \label, the MMD document would no longer be internally consistent once that XSLT is removed and the label is no longer cleaned.
A third alternative is tracking down everything that involves cross reference and insert similar code to strip out keywords from each one. Again, I don’t care for this approach because it isn’t very scalable and introduces a lot of maintenance. Say you want to add another type of table (or header, or anything else that needs the cross-referencing system). You’ll have to insert chains of these cleaners in every spot, every time you add an exception like this.
Keeping the ID as-is, we don’t have to worry about any of that. Only the visible \caption is fixed.
Finally, we added another xsl:if check around the emission portion of this. If the caption was originally only “[SIMPLE]”, then all of the rest is skipped. The table will have no visible caption, and it will not be labelled. This matches typical MMD behaviour. If you don’t supply a caption, this stuff would never fire off at all. If the @id is anything but ‘simple’, though, then it runs, and uses the cleaned version of the contents for the visible caption.
That’s it. You should now be able to caption your tables with SIMPLE (and if you do change that keyword to something else, be sure to edit the original ‘html:table’ match, the sub-string search in ‘html:caption’ and the if test statement within it as well) to remove the ruling on them; actual visual caption optional.
P.S. I just realised I might have misread your last query. I provided the trick for left-alignment within cells, but not the entire table. If you want the entire table to be pinned to the left side of the text block then you’ll need to adjust the LaTeX code in the XSLT. I think that removing \centering is all you need to do.