1<xsl:stylesheet version="1.0"
2  xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
3    xmlns:sfa="http://developer.apple.com/namespaces/sfa"
4  xmlns:sf="http://developer.apple.com/namespaces/sf"
5  xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
6  xmlns:appsl="http://developer.apple.com/namespaces/sl"
7  xmlns:dbk='http://docbook.org/ns/docbook'
8  xmlns:rnd='http://docbook.org/ns/docbook/roundtrip'
9  exclude-result-prefixes='sfa sf xsi appsl'>
10
11  <xsl:output method="xml" indent='yes'/>
12
13  <!-- ********************************************************************
14
15       This file is part of the XSL DocBook Stylesheet distribution.
16       See ../README or http://nwalsh.com/docbook/xsl/ for copyright
17       and other information.
18
19       ******************************************************************** -->
20
21  <xsl:strip-space elements='*'/>
22  <xsl:preserve-space elements='sf:span'/>
23
24  <xsl:key name='styles'
25	   match='sf:paragraphstyle[not(ancestor::appsl:section-prototypes)] |
26		  sf:characterstyle[not(ancestor::appsl:section-prototypes)] |
27                  sf:table-style'
28	   use='@sf:ident|@sfa:ID'/>
29
30  <xsl:key name='ids'
31    match='*'
32    use='@sfa:ID'/>
33
34  <xsl:template match='appsl:document'>
35    <dbk:article>
36      <!-- TODO: headers and footers -->
37      <xsl:apply-templates select='sf:text-storage'/>
38    </dbk:article>
39  </xsl:template>
40
41  <xsl:template match='sf:p'>
42    <xsl:choose>
43      <xsl:when test='sf:attachment-ref and
44                      count(*) = count(sf:attachment-ref|sf:br|sf:selection-start|sf:selection-end)'>
45        <xsl:apply-templates/>
46      </xsl:when>
47      <xsl:otherwise>
48        <dbk:para>
49          <xsl:variable name='style-name'>
50            <xsl:call-template name='rnd:find-style'/>
51          </xsl:variable>
52          <xsl:if test='$style-name != "" and
53                        $style-name != "para"'>
54            <xsl:attribute name='rnd:style'>
55              <xsl:value-of select='$style-name'/>
56            </xsl:attribute>
57          </xsl:if>
58
59          <xsl:apply-templates/>
60        </dbk:para>
61      </xsl:otherwise>
62    </xsl:choose>
63  </xsl:template>
64
65  <xsl:template match='sf:span'>
66    <xsl:variable name='style-name'
67		  select='key("styles", @sf:style)/self::sf:characterstyle/@sf:name'/>
68
69    <xsl:variable name='char-style'>
70      <xsl:call-template name='rnd:find-style'>
71        <xsl:with-param name='char-style-name' select='$style-name'/>
72      </xsl:call-template>
73    </xsl:variable>
74
75    <xsl:choose>
76      <xsl:when test='$style-name = "attribute-name"'>
77	<xsl:if test='not(preceding-sibling::node()[not(self::text()) or (self::text() and normalize-space() != "")])'>
78          <xsl:attribute name='{.}'>
79            <xsl:apply-templates select='following-sibling::*[1][self::sf:span]'
80              mode='attribute'/>
81          </xsl:attribute>
82	</xsl:if>
83      </xsl:when>
84      <xsl:when test='$style-name = "attribute-value"'/>
85      <xsl:when test='$style-name = ""'>
86        <xsl:apply-templates/>
87      </xsl:when>
88      <xsl:when test='$char-style = "superscript" or
89                      $char-style = "subscript"'>
90        <xsl:element name='{$char-style}'
91          namespace='http://docbook.org/ns/docbook'>
92          <xsl:apply-templates/>
93        </xsl:element>
94      </xsl:when>
95      <xsl:otherwise>
96	<dbk:emphasis>
97          <xsl:choose>
98            <xsl:when test='$char-style = "emphasis-bold" or
99                            $char-style = "emphasis-strong"'>
100              <xsl:attribute name='role'>bold</xsl:attribute>
101            </xsl:when>
102            <xsl:when test='$char-style != "" and
103                            $char-style != "emphasis"'>
104              <xsl:attribute name='rnd:style'>
105                <xsl:value-of select='$char-style'/>
106              </xsl:attribute>
107            </xsl:when>
108          </xsl:choose>
109
110	  <xsl:apply-templates/>
111        </dbk:emphasis>
112      </xsl:otherwise>
113    </xsl:choose>
114  </xsl:template>
115
116  <xsl:template match='*' mode='attribute'>
117    <xsl:variable name='style-name'
118		  select='key("styles", @sf:style)/self::sf:characterstyle/@sf:name'/>
119
120    <xsl:if test='$style-name = "attribute-value"'>
121      <xsl:apply-templates/>
122    </xsl:if>
123  </xsl:template>
124
125  <xsl:template match='sf:br'/>
126  <xsl:template match='sf:lnbr|sf:crbr'>
127    <xsl:text>&#xa;</xsl:text>
128  </xsl:template>
129  <xsl:template match='sf:tab'>
130    <xsl:text>        </xsl:text>
131  </xsl:template>
132  <xsl:template match='sf:link'>
133    <dbk:ulink url='{@href}'>
134      <xsl:apply-templates/>
135    </dbk:ulink>
136  </xsl:template>
137
138  <xsl:template match='sf:attachment-ref'>
139    <xsl:if test='@sf:kind = "tabular-attachment"'>
140      <xsl:apply-templates select='key("ids", @sfa:IDREF)'/>
141    </xsl:if>
142  </xsl:template>
143
144  <xsl:template match='sf:attachment[@sf:kind = "tabular-attachment"]'>
145    <xsl:variable name='model'
146      select='key("ids", sf:tabular-info/sf:tabular-model-ref/@sfa:IDREF)'/>
147
148    <xsl:variable name='num-cols' select='$model/sf:grid/@sf:numcols'/>
149    <xsl:variable name='num-rows' select='$model/sf:grid/@sf:numrows'/>
150
151    <xsl:variable name='border.top'
152      select='count($model/sf:grid/sf:horizontal-gridline-styles/*) = 0 or
153              not($model/sf:grid/sf:horizontal-gridline-styles/sf:style-run[@sf:gridline-index = "0"])'/>
154    <xsl:variable name='border.bottom'
155      select='count($model/sf:grid/sf:horizontal-gridline-styles/*) = 0 or
156              not($model/sf:grid/sf:horizontal-gridline-styles/sf:style-run[@sf:gridline-index = $num-rows - 1])'/>
157    <xsl:variable name='border.left'
158      select='count($model/sf:grid/sf:vertical-gridline-styles/*) = 0 or
159              not($model/sf:grid/sf:vertical-gridline-styles/sf:style-run[@sf:gridline-index = "0"])'/>
160    <xsl:variable name='border.right'
161      select='count($model/sf:grid/sf:vertical-gridline-styles/*) = 0 or
162              not($model/sf:grid/sf:vertical-gridline-styles/sf:style-run[@sf:gridline-index = $num-cols])'/>
163
164    <xsl:choose>
165      <xsl:when test='not($num-rows) or $num-rows = ""'>
166        <xsl:message> cannot determine number of rows in table</xsl:message>
167        <xsl:comment> cannot determine number of rows in table </xsl:comment>
168      </xsl:when>
169      <xsl:when test='not($num-cols) or $num-cols = ""'>
170        <xsl:message> cannot determine number of columns in table</xsl:message>
171        <xsl:comment> cannot determine number of columns in table </xsl:comment>
172      </xsl:when>
173
174      <xsl:otherwise>
175        <dbk:informaltable>
176          <xsl:choose>
177            <xsl:when test='$border.top and $border.bottom and
178                            $border.left and $border.right'>
179              <xsl:attribute name='frame'>all</xsl:attribute>
180            </xsl:when>
181            <xsl:when test='$border.top and $border.bottom'>
182              <xsl:attribute name='frame'>topbot</xsl:attribute>
183            </xsl:when>
184            <xsl:when test='$border.left and $border.right'>
185              <xsl:attribute name='frame'>sides</xsl:attribute>
186            </xsl:when>
187            <xsl:when test='$border.top'>
188              <xsl:attribute name='frame'>top</xsl:attribute>
189            </xsl:when>
190            <xsl:when test='$border.bottom'>
191              <xsl:attribute name='frame'>bottom</xsl:attribute>
192            </xsl:when>
193          </xsl:choose>
194          <dbk:tgroup cols='{$num-cols}'>
195            <xsl:apply-templates select='$model/sf:grid/sf:columns/sf:grid-column'
196              mode='rnd:colspec'/>
197            <xsl:if test='$model/@sf:num-header-rows != 0'>
198              <dbk:thead>
199                <xsl:call-template name='rnd:make-table-rows'>
200                  <xsl:with-param name='nodes'
201                    select='$model/sf:grid/sf:datasource/sf:text-cell[@sf:row &lt; $model/@sf:num-header-rows]'/>
202                  <xsl:with-param name='num-rows'
203                    select='$model/@sf:num-header-rows'/>
204                </xsl:call-template>
205              </dbk:thead>
206            </xsl:if>
207            <dbk:tbody>
208              <xsl:call-template name='rnd:make-table-rows'>
209                <xsl:with-param name='nodes'
210                  select='$model/sf:grid/sf:datasource/sf:text-cell[@sf:row >= $model/@sf:num-header-rows and
211                  @sf:row &lt; $num-rows - $model/@sf:num-footer-rows]'/>
212                <xsl:with-param name='num-rows' select='$num-rows - $model/@sf:num-header-rows - $model/@sf:num-footer-rows'/>
213                <xsl:with-param name='row' select='$model/@sf:num-header-rows'/>
214              </xsl:call-template>
215            </dbk:tbody>
216            <xsl:if test='$model/@sf:num-footer-rows != 0'>
217              <dbk:tfoot>
218                <xsl:call-template name='rnd:make-table-rows'>
219                  <xsl:with-param name='nodes'
220                    select='$model/sf:grid/sf:datasource/sf:text-cell[@sf:row &gt;= $num-rows - $model/@sf:num-footer-rows]'/>
221                  <xsl:with-param name='num-rows'
222                    select='$model/@sf:num-footer-rows'/>
223                  <xsl:with-param name='row'
224                    select='$num-rows - $model/@sf:num-footer-rows'/>
225                </xsl:call-template>
226              </dbk:tfoot>
227            </xsl:if>
228          </dbk:tgroup>
229        </dbk:informaltable>
230      </xsl:otherwise>
231    </xsl:choose>
232  </xsl:template>
233  <xsl:template match='sf:grid-column' mode='rnd:colspec'>
234    <dbk:colspec colwidth='{@sf:width}'
235      colname='column-{count(preceding-sibling::sf:grid-column) + 1}'/>
236  </xsl:template>
237  <xsl:template name='rnd:make-table-rows'>
238    <xsl:param name='num-rows' select='0'/>
239    <xsl:param name='nodes' select='/..'/>
240    <xsl:param name='row' select='0'/>
241
242    <xsl:choose>
243      <xsl:when test='not($nodes) and $num-rows != 0'>
244        <xsl:message>WARNING: insufficient table cells</xsl:message>
245        <xsl:comment> WARNING: insufficient table cells (num-rows <xsl:value-of select='$num-rows'/>, row <xsl:value-of select='$row'/>) </xsl:comment>
246      </xsl:when>
247      <xsl:when test='$nodes and $num-rows = 0'>
248        <xsl:message>WARNING: excess table cells</xsl:message>
249        <xsl:comment> WARNING: excess table cells (num-rows <xsl:value-of select='$num-rows'/>, row <xsl:value-of select='$row'/>) </xsl:comment>
250      </xsl:when>
251      <xsl:when test='not($nodes)'/>
252      <xsl:when test='$num-rows = 0'/>
253
254      <xsl:otherwise>
255        <dbk:row>
256          <xsl:apply-templates select='$nodes[@sf:row = $row]'/>
257        </dbk:row>
258        <xsl:call-template name='rnd:make-table-rows'>
259          <xsl:with-param name='num-rows'
260            select='$num-rows - 1'/>
261          <xsl:with-param name='row'
262            select='$row + 1'/>
263          <xsl:with-param name='nodes'
264            select='$nodes[@sf:row != $row]'/>
265        </xsl:call-template>
266      </xsl:otherwise>
267    </xsl:choose>
268  </xsl:template>
269  <xsl:template match='sf:text-cell'>
270    <dbk:entry>
271      <!-- Does this cell have no bottom border? -->
272      <xsl:variable name='horiz'
273        select='ancestor::sf:grid/sf:horizontal-gridline-styles'/>
274      <xsl:if test='not($horiz/*) or
275                    not($horiz/sf:style-run[@sf:gridline-index = current()/@sf:row + 1]/sf:vector-style-ref[@sf:start-index &lt;= current()/@sf:col and @sf:stop-index >= current()/@sf:col])'>
276        <xsl:attribute name='rowsep'>1</xsl:attribute>
277      </xsl:if>
278      <!-- Does this cell have no right border? -->
279      <xsl:variable name='vert'
280        select='ancestor::sf:grid/sf:vertical-gridline-styles'/>
281      <xsl:if test='not($vert/*) or
282                    not($vert/sf:style-run[@sf:gridline-index = current()/@sf:col + 1]/sf:vector-style-ref[@sf:start-index &lt;= current()/@sf:row and @sf:stop-index >= current()/@sf:row])'>
283        <xsl:attribute name='colsep'>1</xsl:attribute>
284      </xsl:if>
285
286      <xsl:choose>
287        <xsl:when test='sf:cell-text/@sfa:string'>
288          <dbk:para>
289            <xsl:apply-templates select='sf:cell-text/@sfa:string'/>
290          </dbk:para>
291        </xsl:when>
292        <xsl:otherwise>
293          <xsl:apply-templates select='sf:cell-text/sf:cell-storage/sf:text-body/*'/>
294        </xsl:otherwise>
295      </xsl:choose>
296    </dbk:entry>
297  </xsl:template>
298  <xsl:template match='sf:tableAttachmentTable |
299                       sf:tableModelCells'>
300    <xsl:apply-templates/>
301  </xsl:template>
302  <xsl:template match='sf:tableModelPartitionSource |
303                       sf:tableModelStyle-ref |
304                       sf:tableModelVectors |
305                       sf:tableCellArrayCellsByColumn |
306                       sf:tableModelTableID'/>
307
308  <xsl:template match='sf:text-storage |
309                       sf:text-body |
310                       sf:section |
311                       sf:layout'>
312    <xsl:apply-templates/>
313  </xsl:template>
314
315  <xsl:template match='sf:stylesheet|sf:stylesheet-ref |
316		       sf:container-hint |
317		       sf:page-start|sf:br |
318		       sf:selection-start|sf:selection-end |
319		       sf:insertion-point |
320		       sf:ghost-text |
321                       sf:attachments'/>
322
323  <xsl:template match='*'>
324    <xsl:message>element "<xsl:value-of select='name()'/>" not handled</xsl:message>
325  </xsl:template>
326
327  <xsl:template name='rnd:find-style'>
328    <xsl:param name='ident' select='@sf:style'/>
329    <xsl:param name='para-style-name'
330	       select='key("styles", $ident)/self::sf:paragraphstyle/@sf:name'/>
331    <xsl:param name='char-style-name'
332	       select='key("styles", $ident)/self::sf:characterstyle/@sf:name'/>
333
334    <xsl:choose>
335      <xsl:when test='$ident = "paragraph-style-default"'/>
336      <xsl:when test='$para-style-name != ""'>
337	<xsl:value-of select='$para-style-name'/>
338      </xsl:when>
339      <xsl:when test='key("styles", $ident)/self::sf:characterstyle/sf:property-map/sf:superscript/sf:number/@sfa:number = "1"'>superscript</xsl:when>
340      <xsl:when test='key("styles", $ident)/self::sf:characterstyle/sf:property-map/sf:subscript/sf:number/@sfa:number = "1"'>subscript</xsl:when>
341      <xsl:when test='$char-style-name != "" or
342		      key("styles", $ident)/self::sf:characterstyle/sf:property-map/*'>
343	<xsl:value-of select='$char-style-name'/>
344      </xsl:when>
345    </xsl:choose>
346  </xsl:template>
347</xsl:stylesheet>
348