Applying more than one text (character) style to a portion
I am using LibreOffice 4.1 on OS X 10.8.
It is indeed possible to apply more than one character style to a given portion of text. Take the following example with the two styles you mention (Source Text and Emphasis):
<text:p text:style-name="P1">This is what
<text:span text:style-name="T1">emphasized </text:span>
<text:span text:style-name="Source_20_Text">
<text:span text:style-name="Emphasis">code</text:span></text:span>
looks like.</text:p>
In this first example, I have added the string emphasized afterwards, so that it is enclosed in its own span element with an automatic text style T1. Reminder: styles that apply to text strings are called character styles in LibO and text styles in the ODF spec. The T1 style is defined thus:
<style:style style:name="T1" style:family="text">
<style:text-properties officeooo:rsid="000c8b60"/>
</style:style>
The only defined attribute is an officeooo:rsid, which shows that this style’s only purpose is document comparison (this makes me grumpy). Apart from that, we can see that it is quite possible to apply two character styles to the same portion. In fact, there are two ways to say it:
- LibO speak: It is possible to apply multiple different character styles to one text portion;
- Spec speak: It is possible to enclose a given text node in multiple nested
span elements with different text:style-name attributes.
Remark on LibO’s behaviour: the effects are cumulative, so that the word code in this example is displayed in monospaced oblique type (for a monospaced font, the proper term is oblique, as there is no italic shape to speak of, but of course LibO does what is expected and chooses the oblique font).
Remark on implementation: according to the ODF 1.2 spec (part 1, section 19.770), a text:class-names attribute exists for the purpose of applying more than one text style to a node:
A text:class-names attribute specifies a white space separated list of style names. The referenced styles are applied in the order they are contained in the list.
If both text:style-name and text:class-names are present, the style referenced by the text:style-name attribute is applied before the styles referenced by text:class-names attribute. If a conditional style is specified together with a text:class-names attribute, but without a text:style-name attribute, the text:style-name attribute is assumed to have the value of the first style name in the list defined by the text:class-name attribute.
I find that very useful and desirable. One potential use case is to apply a style that defines the visual presentation of the element with a text:style-name attribute (say, "Emphasis" to denote a change in tone with italic type) and also apply other styles with a purely semantic meaning and without any particular visual distinction, with a text:class-names attribute (e.g. "Archaic Eponym"). Perhaps that sounds convoluted, but it’s the kind of thing I have to do in my work and I am sure I am not alone.
Unfortunately, in my experience, LibO does not write text:class-names attributes in the ODF it produces. Perhaps I just haven’t experienced enough, though.
Let us try with user-defined styles now. Here is the second paragraph in my document:
<text:p text:style-name="P2">I like
<text:span text:style-name="Plant">
<text:span text:style-name="Drink">tea</text:span></text:span>
very much.</text:p>
I have defined two character styles in LibO for this document: Plant and Drink. Plant makes the text green and Drink makes it italicized. I have applied them both to the word tea because tea is the name for both the plant and the drink made from the plant ;-). Again, the effects are cumulative: the word tea in my document is now both green and italicized, but again we have two span elements instead of one element with both a text:style-name and a text:class-names attribute. Note that I applied Plant first and then Drink and, accordingly, the span with Plant encloses the one with Drink (the span with the Plant attribute comes first in the document’s hierarchy).
Remark on the paragraph element in this second example: it has a different style (P2), even though the first and the second paragraph have the same level, the same appearance and the same purpose. Makes me grumpy. For people who want to produce structured and semantic documents, it is a pain. But this will change again in a future LibreOffice version, won’t it? 
Now, let us try to understand how LibreOffice represents this document to itself.
How do we inquire about the character styles applied to a text portion?
A distinction that must be made clearly here is that, much like most applications that consume some kind of XML, LibreOffice does not use XML data structures “natively”. It is still true that ODF is LibO’s native format in a certain sense, but any application that supports many document formats needs some kind of internal representation. In our case, we could call it a StarWriter document object or something like that (but that might summon some old ghosts, so I’m not going to insist on that).
In order to give a full account, I would have to define concepts which are specific to OpenOffice/LibreOffice, but this is beyond my means. Suffice it to say that in the application, the text document contains objects that have properties and methods and that the objects that “wrap” others together are called services. The OpenOffice Developer’s Guide (a resource of very uneven quality) has a page that distinguishes further in Appendix A. To put it relatively simply, the structure of text paragraphs is so:
-
Paragraphs are services that include a TextContent service.
- The actual text is stored in
TextPortion services. The description of the TextPortion service in the API documentation is very unclear about the actual status of the service and its relation to the Paragraph service.
- In any case, the
TextPortion is not the thing that has a style: the TextPortion service includes a TextRange service. This service is the one that has the actual CharacterProperties service.
- In the
CharacterProperties service, there are two properties that are relevant to our inquiry: CharStyleName and CharStyleNames. Both are optional. The data type of the CharStyleName property is “string”, while the data type of the CharStyleNames property is a sequence of strings.
Logically, the CharStyleName property should correspond to the text:style-name ODF attribute and the CharStyleNames property should correspond to text:class-names. This is more or less the case, except that the description of the CharStylesnames property says:
It is not guaranteed that the order in the sequence reflects the order of the evaluation of the character style attributes.
If the ODF spec says that “[t]he referenced styles are applied in the order they are contained in the list”, then why is that? I have no answer…
However, now we know what properties we must inquire about. Here is a very dumb subroutine that only works in a very specific situation. When selecting a single styled word in the document, the subroutine prints the values of the style properties. Actually, it generates an error if the selection only has one style applied. The only purpose of this lousy code is to print the styles of a portion which we know has two styles:
sub print_char_styles_of_viewcursor()
dim CurrentDoc as object
dim FirstStyleofSelection as string
dim StyleListofSelection(2) as string
CurrentDoc = ThisComponent.CurrentController
FirstStyleofSelection = CurrentDoc.getViewCursor().CharStyleName
StyleListofSelection() = CurrentDoc.getViewCursor().CharStyleNames()
print "The values of the selected portion’s character style properties are: “"; _
FirstStyleofSelection; "” for CharStyleName; “"; _
StyleListofSelection(0); ", "; StyleListofSelection(1); "” for CharStyleNames."
end sub
Remarks on the code:
- Even though I said that character styles actually apply to text ranges, here I am using StarBasic to access properties, I am working on portions and I do get the character styles.
- I mentioned that in the API (IDL reference), the
CharStyleNames property is defined as a sequence of strings. Here, from the perspective of StarBasic, it is an array of strings. My assignment of StyleListofSelection to the value of CurrentDoc.getViewCursor().CharStyleNames() is basically the assignment of an array to another array. When you do this in real life, you need to be careful.
When I select the word tea and then run this subroutine, the following message is printed:
The values of the selected portion’s character style properties are: “Drink” for CharStyleName; “Plant, Drink” for CharStyleNames.
When I select the word code, this message is printed:
The values of the selected portion’s character style properties are: “Emphasis” for CharStyleName; “Source Text, Emphasis” for CharStyleNames.
In both cases, the value of the CharStyleName property is the style that was applied last, but the order of the styles in CharStyleNames corresponds to the order in which they were applied. This suggests that the order is indeed not respected when it comes to deciding which style has priority.
This is all very interesting, but the question remains: how do we accurately control the assignment of character properties? When writing the ODF, LibO does respect the order of assignment, but this is not reflected in the document’s data structures inside the application. I think a bug should be raised about the specific issue of the character styles’ priority. If the CharStyleNames property was mapped to the text:class-names attribute, that would be a big improvement.