<oXygen/> Author is highly customizable. Practically you can associate an entire class of documents (grouped logically by some common features like namespace, root element name or filename) to a bundle consisting of a CSS stylesheets, validation schemas, catalog files, templates for new files, transformation scenarios and even custom actions. This is called a Document Type Association.
In this section we will create a Document Type Association for a set of documents. As an example we will create a light documentation framework, similar to DocBook and create a complete customization of the Author editor.
You can find the complete files that were used in this tutorial in the Example Files Listings.
Our documentation framework will be very simple. The documents will
be either articles
or books
, both composed
of sections
. The sections
may contain
titles
, paragraphs
,
figures
, tables
and other
sections
. To complete the picture, each section will
include a def
element from another namespace.
The first schema file:
<?xml version="1.0" encoding="UTF-8"?> <xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema" targetNamespace="http://www.oxygenxml.com/sample/documentation" xmlns:doc="http://www.oxygenxml.com/sample/documentation" xmlns:abs="http://www.oxygenxml.com/sample/documentation/abstracts" elementFormDefault="qualified"> <xs:import namespace= "http://www.oxygenxml.com/sample/documentation/abstracts" schemaLocation= "abs.xsd"/>
The namespace of our documents will be
http://www.oxygenxml.com/sample/documentation
. The
namespace of the def
element is
http://www.oxygenxml.com/sample/documentation/abstracts
.
Now let's define the structure of the sections. They all start with a
title, then have the optional def
element then either a
sequence of other sections, or a mixture of paragraphs, images and
tables.
<xs:element name="book" type="doc:sectionType"/> <xs:element name="article" type="doc:sectionType"/> <xs:element name="section" type="doc:sectionType"/> <xs:complexType name="sectionType"> <xs:sequence> <xs:element name="title" type="xs:string"/> <xs:element ref="abs:def" minOccurs="0"/> <xs:choice> <xs:sequence> <xs:element ref="doc:section" maxOccurs="unbounded"/> </xs:sequence> <xs:choice maxOccurs="unbounded"> <xs:element ref="doc:para"/> <xs:element ref="doc:image"/> <xs:element ref="doc:table"/> </xs:choice> </xs:choice> </xs:sequence> </xs:complexType>
The paragraph contains text and other styling markup, such as bold
(b
) and italic (i
) elements.
<xs:element name="para" type="doc:paragraphType"/> <xs:complexType name="paragraphType" mixed="true"> <xs:choice minOccurs="0" maxOccurs="unbounded"> <xs:element name="b"/> <xs:element name="i"/> </xs:choice> </xs:complexType>
The image
element has an attribute with a reference to
the file containing image data.
<xs:element name="image"> <xs:complexType> <xs:attribute name="href" type="xs:anyURI" use="required"/> </xs:complexType> </xs:element>
The table
contains a header row and then a sequence of
rows (tr
elements) each of them containing the cells.
Each cell has the same content as the paragraphs.
<xs:element name="table"> <xs:complexType> <xs:sequence> <xs:element name="header"> <xs:complexType> <xs:sequence> <xs:element name="td" maxOccurs="unbounded" type="doc:paragraphType"/> </xs:sequence> </xs:complexType> </xs:element> <xs:element name="tr" maxOccurs="unbounded"> <xs:complexType> <xs:sequence> <xs:element name="td" type="doc:tdType" maxOccurs="unbounded"/> </xs:sequence> </xs:complexType> </xs:element> </xs:sequence> </xs:complexType> </xs:element> <xs:complexType name="tdType"> <xs:complexContent> <xs:extension base="doc:paragraphType"> <xs:attribute name="row_span" type="xs:integer"/> <xs:attribute name="column_span" type="xs:integer"/> </xs:extension> </xs:complexContent> </xs:complexType>
The def
element is defined as a text only element in the
imported schema abs.xsd
:
<?xml version="1.0" encoding="UTF-8"?> <xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema" targetNamespace= "http://www.oxygenxml.com/sample/documentation/abstracts"> <xs:element name="def" type="xs:string"/> </xs:schema>
Now that we defined our XML data structure, let's start styling it...
If you read the Simple Customization Tutorial then you already have some basic notions about creating simple styles. Our document contains elements from different namespaces, so we will use CSS Level 3 extensions supported by the <oXygen/> layout engine to associate specific properties with that element.
![]() | Note |
---|---|
Please note that the CSS Level 3 is a standard under development, and has not been released yet by the W3C. However, it addresses several important issues like selectors that are namespace aware and values for the CSS properties extracted from the attributes of the XML documents. Although not (yet) conforming with the current CSS standard these are supported by the <oXygen/> Author. |
We are now creating the basic layout of the rendered documents.
Elements that are stacked one on top of the other are:
book
, article
,
section
, title
,
figure
, table
, image
.
These elements are marked as having block
style for
display. Elements that are placed one after the other in a
flowing sequence are: b
, i
. These will
have inline
display.
/* Vertical flow */ book, section, para, title, image, ref { display:block; } /* Horizontal flow */ b,i { display:inline; }
![]() | Important |
---|---|
Having |
The title of any section must be bold and smaller than the title
of the parent section. To create this effect we have to create a
sequence of CSS rules. The *
operator matches any
element, so we can use it to match titles having progressive
depths in the document.
title{ font-size: 2.4em; font-weight:bold; } * * title{ font-size: 2.0em; } * * * title{ font-size: 1.6em; } * * * * title{ font-size: 1.2em; }
![]() | Note |
---|---|
CSS rules are combined as follows:
|
It's useful to have before the title a constant text, indicating
that it refers to a section. This text can include also the
current section number. To achieve we have to use the
:before
and :after
pseudo
elements, plus the CSS counters.
We declare a counter named sect
for each
book
or article
. The counter is
set to zero at the beginning of each such element:
book, article{ counter-reset:sect; }
The sect
counter is incremented with each
section
, that is the a direct child of a
book
or an article
element.
book > section, article > section{ counter-increment:sect; }
The "static" text that will prefix the section title is composed
of the constant "Section ", followed by the decimal value of the
sect
counter and a dot.
book > section > title:before, article > section > title:before{ content: "Section " counter(sect) ". "; }
To make the documents easy to read, we will add a margin to the sections. In this way the higher nesting level, the larger the left side indent. The margin is expressed relatively to the parent bounds:
section{ margin-left:1em; margin-top:1em; }
In the above screenshot you can see a sample XML document rendered by our CSS. The selection "avoids" the text that is generated by the CSS "content" property. This happens because the CSS generated text is not present in the XML document and is just a visual aid.
There are standard CSS properties used to indicate what elements are tables, table rows and table cells. What CSS is missing is the possibility to indicate the cell spanning. <oXygen/> Author offers support for adding an extension to solve this problem. This will be presented in the next chapters.
The table in this example is a simple one. The header must be formatted in a different way than the ordinary rows, so it will have a background color.
table{ display:table; border:1px solid navy; margin:1em; max-width:1000px; min-width:150px; } table[width]{ width:attr(width, length); } tr, header{ display:table-row; } header{ background-color: silver; color:inherit } td{ display:table-cell; border:1px solid navy; padding:1em; }
![]() | Note |
---|---|
Children elements with |
![]() | Note |
---|---|
Mixing elements having |
![]() | Note |
---|---|
Having child elements that do not have
|
Because in our schema the td
tag has the attributes
row_span and column_span that are not
automatically recognized by <oXygen/> Author, we will implement a
Java extension which will provide information about the cell
spanning. See the section Configuring a
Table Cell Span Provider.
Because the column widths are specified by the attributes
width of the elements
customcol
that are not automatically recognized
by <oXygen/> Author, it is necessary to implement a Java extension
which will provide information about the column widths. See the
section Configuring a Table Column Width Provider.
The "bold" style is obtained by using the
font-weight
CSS property with the value
bold
, while the "italic" style is specified by
the font-style
property:
b { font-weight:bold; } i { font-style:italic; }
In the CSS Level 1, 2, and 2.1 there is no way to specify if an element X from the namespace Y should be presented differently from the element X from the namespace Z. In the upcoming CSS Level 3, it is possible to differentiate elements by their namespaces. <oXygen/> Author supports this CSS Level 3 functionality. For more information see the Namespace Selectors section.
To match our def
element we will declare its
namespace, bind it to the abs
prefix, and then write a CSS rule:
@namespace abs "http://www.oxygenxml.com/sample/documentation/abstracts"; abs|def{ font-family:monospace; font-size:smaller; } abs|def:before{ content:"Definition:"; color:gray; }
The CSS 2.1 does not specify how an element can be rendered as an image. To overpass this limitation, <oXygen/> Author supports a CSS Level 3 extension allowing to load image data from an URL. The URL of the image must be specified by one of the element attributes.
![]() | Note |
---|---|
<oXygen/> Author recognizes the following image file formats: JPEG, GIF, PNG and SVG. The oXygen Author for Eclipse does not render the SVG files. |
image{ display:block; content: attr(href, url); margin-left:2em; }
Our image
element has the required attribute
href
of type xs:anyURI
. The
href
attribute contains an image location so
the rendered content is obtained by using the
function:
attr(href, url)
![]() | Important |
---|---|
The first argument is the name of the attribute pointing to
the image file. The second argument of the |
![]() | Important |
---|---|
<oXygen/> Author handles both absolute and relative specified URLs. If the image has an absolute URL location (e.g: "http://www.oasis-open.org/images/standards/oasis_standard.jpg") then it is loaded directly from this location. If the image URL is relative specified to the XML document (e.g: "images/my_screenshot.jpg") then the location is obtained by adding this value to the location of the edited XML document. |
An image can also be referenced by the name of a DTD entity which specifies the location of the image file. For example if the document declares an entity graphic which points to a JPEG image file:
<!ENTITY graphic SYSTEM "depo/keyboard_shortcut.jpg" NDATA JPEG>
and the image is referenced in the XML document by specifying the name of the entity as the value of an attribute:
<mediaobject> <imageobject> <imagedata entityref="graphic" scale="50"/> </imageobject> </mediaobject>
The CSS should use the functions url, attr and unparsed-entity-uri for displaying the image in the Author mode:
![]() | Note |
---|---|
Note that the scale attribute of the imagedata element will be considered without the need of a CSS customization and the image will be scaled accordingly. |
imagedata[entityref]{ content: url(unparsed-entity-uri(attr(entityref))); }
To take into account the value of the width
attribute of the imagedata
and use it for resizing
the image, the CSS can define the following rule:
imagedata[width]{ width:attr(width, length); }
You can specify what elements are collapsible. The collapsible
elements are rendered having a small triangle icon in the top
left corner. Clicking on this icon hides or shows the children of
the element. In our case, we will mark the section
elements as foldable. We will leave only the title
child elements visible.
section{ foldable:true; not-foldable-child: title; }
You can specify what elements are links. The text content
specified in the :before
pseudo element will be
underlined. When hovering the mouse over that content the mouse
pointer will change to indicate that it can follow the link.
Clicking on a link will result in the referred resource being
opened in an editor. In our case we will mark the
link
elements as links with the
href
attribute indicating the referred
location.
link[href]:before{ display:inline; link:attr(href); content: "Click to open: " attr(href); }
![]() | Note |
---|---|
If you plan to use IDs as references for links, the value of the link property should start with a sharp sign(#). This will ensure that the default link target reference finder implementation will work and clicking on the link will send you to the indicated location in the document. For more details about the link target reference finder read the section Configuring a Link target reference finder. Example 7.1. IDs as references for links link[linkend]:before{ display:inline; link: "#" attr(linkend); content: "Click to open: " attr(linkend); } |
Now that we have the XML Schema and the CSS stylesheet for the documents we intend to edit, we can proceed to create a distributable framework package for our content authors.
First create a new folder called sdf
(from "Simple Documentation Framework") in
{oXygen_installation_directory}/frameworks
. We
will use this folder to store all files related to our
documentation framework. Let's organise it a bit, by creating the
following folder structure:
oxygen frameworks sdf schema css
![]() | Important |
---|---|
The Each subdirectory contains files related to a specific type of XML documents: schemas, catalogs, stylesheets, CSS files, etc. Distributing a framework means delivering a framework directory. |
![]() | Important |
---|---|
We assume you have the right to create files and folder inside the oXygen installation directory. If you do not have this right, you will have to install another copy of the program in a folder you have access to, the home directory for instance, or your desktop. You can download the "all platforms" distribution from the oXygen website and extract it in the chosen folder. To test your framework distribution you will need to copy
it in the |
We should copy the created schema files abs.xsd
and sdf.xsd
,
sdf.xsd
being the master schema, to the
schema
directory and the CSS file
sdf.css
to the css
directory.
We must specify when oXygen should use the files created in the previous section by creating a document type association. Open the Document Type dialog by following the procedure:
Open the Options Dialog, and select the Document Types Association option pane.
Select the Developer user role from the User role combo box at the top of the dialog. This is important, because it will allow us to save the document type association in a file on disk, instead of <oXygen/> options.
Click on the New button.
In the displayed dialog, fill in the following data:
Enter SDF - This is the name of the document type.
Enter Simple Documentation Framework - This is a short description helping the other users understand the purpose of the Document Type.
The storage refers to the place where the Document Type settings are stored.
means the Document Types are stored in the default <oXygen/> preferences file. Since we want to share the Document Type to other users, we must select , and choose a file.The file must be in the {oXygen_installation_directory}/frameworks/sdf
directory. A possible location is /Users/{user_name}/Desktop/oxygen/frameworks/sdf/sdf.framework.
The framework directory structure will be:
oxygen frameworks sdf sdf.framework schema sdf.xsd css sdf.css
If a document opened in <oXygen/> matches one of the rules defined for the Document Type, then it is activated.
Press the http://www.oxygenxml.com/sample/documentation
.
The root name, file name or PublicID are not
relevant.
A document matches a rule when it fulfills the conditions imposed by each field of the rule:
the namespace of the root element declared in the XML documents of the current document type. A value of ANY_VALUE matches any namespace in an XML document. Value may contain wildcards(*, ?) and editor variables. Multiple values separated by comma(,) are accepted.
The local name of the root element of the XML documents of the current document type. A value of ANY_VALUE matches any local name of the root element. Value may contain wildcards(*, ?) and editor variables. Multiple values separated by comma(,) are accepted.
The file name of the XML documents of the current document type. A value of ANY_VALUE matches any file name. Value may contain wildcards(*, ?) and editor variables. Multiple values separated by comma(,) are accepted.
The public ID of the XML documents of the current document type (for a document validated against a DTD). A value of ANY_VALUE matches any public ID. Value may contain wildcards(*, ?) and editor variables. Multiple values separated by comma(,) are accepted.
The full name of a Java class that has access to all root element attributes and the above 4 values in order to decide if the document matches the rule.
An alternative to the rule we defined for our association is to write the entire logic in Java.
Create a new Java project, in your IDE.
Create the lib
directory in
the Java project directory and copy there the
oxygen.jar
file from the
{oXygen_installation_directory}/lib
.
The oxygen.jar
contains the
Java interfaces we have to implement and the
available Author API needed to access its
features.
Create the class
simple.documentation.framework.CustomRule
.
This class must implement the
ro.sync.ecss.extensions.api.DocumentTypeCustomRuleMatcher
interface.
The interface defines two methods:
matches
, and
getDescription
.
The matches
method
is the one that is invoked when the edited
document must be checked against the document type
association. It takes as arguments the root local
name, its namespace, the document location URI,
the PublicID and the root element attributes. It
must return true
when the document
matches the association.
The getDescription
method returns a description of the rule.
Here is the implementation of these two methods.
The implementation of
matches
is just a Java
equivalent of the rule we defined earlier.
public boolean matches( String systemID, String rootNamespace, String rootLocalName, String doctypePublicID, Attributes rootAttributes) { return "http://www.oxygenxml.com/sample/documentation" .equals(rootNamespace); } public String getDescription() { return "Checks if the current Document Type Association" + " is matching the document."; }
The complete source code is found in the Example Files Listings, the Java Files section.
Package the compiled class into a jar file. Here is an
example of an ANT script that packages the
classes
directory content
into a jar
archive named sdf.jar
:
<?xml version="1.0" encoding="UTF-8"?> <project name="project" default="dist"> <target name="dist"> <jar destfile="sdf.jar" basedir="classes"> <fileset dir="classes"> <include name="**/*"/> </fileset> </jar> </target> </project>
Copy the sdf.jar
file into
the frameworks/sdf
directory.
Add the sdf.jar
to the
Author classpath. To do this select SDF Document Type from the
Document Type
Association options page and press the
button.
Select the Classpath tab in the lower part of the dialog.
Press the frameworks
directory. If you
are in the process of developing the extension
actions you can also specify a path to a directory
which holds compiled Java classes.
Clear the rules we defined before by using the
button.Press the
button from the Rules section.Press the Java class value. The following dialog is displayed:
button that follows theTo test the association, open the sdf.xml
sample and validate it.
In the dialog for editing the Document Type properties, in the bottom section there are a series of tabs. The first one refers to the schema that is used for validation of the documents that match the defined association Rules.
![]() | Important |
---|---|
If the document refers a schema, using for instance a
|
Select from the combo box the value XML Schema.
Enter the value ${frameworks}/sdf/schema/sdf.xsd
. We
should use the ${frameworks}
editor variable in the schema URI path instead of
a full path in order to be valid for different
<oXygen/> installations.
![]() | Important |
---|---|
The |
Select the
tab from the Document Type edit dialog. By clicking on the label in the right part of the tab the list of associated CSS files is shown.Here you can also specify how should the CSSs defined in the
document type be treated when there are CSSs specified in the
document(with xml-stylesheet
processing
instructions). The CSSs from the document can either replace the
CSSs defined in the document type association or merge with
them.
Add the URI of the CSS file sdf.css
we
already defined. We should use the ${frameworks}
editor variable in the
file path.
The Title text field refers to a symbolic name for the stylesheet. When adding several stylesheets with different titles to a Document Type association, the content author can select what CSS will be used for editing from the Author CSS Alternatives toolbar.
This combo-box from the toolbar is also populated in case your
XML document refers CSS files directly using
xml-stylesheet
processing instructions, and the
processing instructions define titles for the CSS files.
![]() | Note |
---|---|
The CSS settings dialog allows to create a
virtual
|
<oXygen/> Author fully implements the W3C recommendation regarding "Associating Style Sheets with XML documents". For more information see: http://www.w3.org/TR/xml-stylesheet/ http://www.w3.org/TR/REC-html40/present/styles.html#h-14.3.2
To test the new Document Type create an XML instance that is
conforming with our Simple Document Format. We will not specify
an XML Schema location directly in the document, using an
xsi:schemaLocation
attribute; <oXygen/> will detect
instead its associated document type and use the specified
schema.
<book xmlns="http://www.oxygenxml.com/sample/documentation" xmlns:abs="http://www.oxygenxml.com/sample/documentation/abstracts"> <title>My Technical Book</title> <section> <title>XML</title> <abs:def>Extensible Markup Language</abs:def> <para>In this section of the book I will explain different XML applications.</para> </section> </book>
When trying to validate the document there should be no errors.
Now modify the title
to title2
.
Validate again. This time there should be one error:
cvc-complex-type.2.4.a: Invalid content was found starting with element 'title2'. One of '{"http://www.oxygenxml.com/sample/documentation":title}' is expected.
Undo the tag name change. Press on the
button at the bottom of the editing area. <oXygen/> should load the CSS from the document type association and create a layout similar to this:Using a file explorer, go to the <oXygen/>
frameworks
directory. Select the sdf
directory and make an archive from it. Move
it to another <oXygen/> installation (eventually on another
computer). Extract it in the frameworks
directory. Start <oXygen/> and test the association as explained
above.
If you create multiple document type associations and you have a complex directory structure it might be easy from the deployment point of view to use an <oXygen/> all platforms distribution. Add your framework files to it, repackage it and send it to the content authors.
![]() | Warning |
---|---|
When deploying your customized sdf directory please make sure that your sdf directory contains the sdf.framework file (that is the file defined as External Storage in Document Type Association dialog shall always be stored inside the sdf directory). If your external storage points somewhere else <oXygen/> will not be able to update the Document Type Association options automatically on the deployed computers. |