<?xml version="1.0" encoding="UTF-8"?>
<!-- Building d20 spell cards
     by Wordman
     http://asteroid.divnull.com/2007/07/spellcards/
     
     As this transformation  code does not contain d20 SRD information, it is released under the 
     Apache license (http://www.apache.org/licenses/LICENSE-2.0).
-->

<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
    xmlns:fo="http://www.w3.org/1999/XSL/Format" version="2.0">
    <xsl:output method="xml" encoding="UTF-8" indent="yes"/>

    <!--Styles -->
    <xsl:attribute-set name="title">
        <xsl:attribute name="font-size">12pt</xsl:attribute>
        <xsl:attribute name="font-weight">bold</xsl:attribute>
        <xsl:attribute name="wrap-option">no-wrap</xsl:attribute>
        <xsl:attribute name="color">#333</xsl:attribute>
    </xsl:attribute-set>
    <xsl:attribute-set name="stats">
        <xsl:attribute name="line-hieght">10pt</xsl:attribute>
    </xsl:attribute-set>

    <!-- Match toplevel element, emit basic document structure -->
    <xsl:template match="spells">
        <fo:root xmlns:fo="http://www.w3.org/1999/XSL/Format">
            <fo:layout-master-set>
                <!-- Set page size to Avery 5371 business cards. Every printer feeds differently, so you might
                    need to tweak the margins here and/or the padding (see below) to adjust. -->
                <fo:simple-page-master master-name="spellcards" page-width="8.5in"
                    page-height="11in" margin-top="0.55in" margin-bottom="0.45in"
                    margin-left="0.75in" margin-right="0.75in">
                    <fo:region-body column-count="2" column-gap="0pt"/>
                </fo:simple-page-master>
            </fo:layout-master-set>

            <fo:page-sequence master-reference="spellcards">
                <fo:flow flow-name="xsl-region-body" font-size="8pt" font-family="Times">
                    <!-- To build cards for all spells, use this... -->
                    <xsl:apply-templates select="spell">
                        <xsl:sort select="name"/>
                    </xsl:apply-templates>

                    <!-- If you want to do simple filtering instead, you can do it here
                        by commenting out the previous section, uncommenting this one,
                        and changing the selection attribute. This particular section
                        just uses all Sorcerer spells. 
                    <xsl:apply-templates select="spell[contains(child::level,'Sorcerer')]">
                        <xsl:sort select="name"/>
                    </xsl:apply-templates>
                    -->
                    
                    <!-- This filter selects only the spells from the healing or knowledge domains. Note that
                        source data is inconsistent in its use of the "subschool" tag, so you have to check the
                        level tag instead. 
                    <xsl:apply-templates select="spell[contains(child::level,'Healing') or contains(child::level,'Knowledge')]">
                         <xsl:sort select="name"/>
                    </xsl:apply-templates>
                    -->
                    
                    <!-- Select only 0 and 1st level Wizard spells.
                    <xsl:apply-templates select="spell[contains(child::level,'Sorcerer/Wizard 0') or contains(child::level,'Sorcerer/Wizard 1')]">
                        <xsl:sort select="name"/>
                    </xsl:apply-templates>
                    -->
                    
                    <!-- Select only 0 and 1st level Druid spells, showing 0 level's first. Not sure why this would
                        matter, since you'll be ripping apart the output anyway, but here's how it works.
                    <xsl:apply-templates select="spell[contains(child::level,'Druid 0')]">
                        <xsl:sort select="name"/>
                    </xsl:apply-templates>
                    <xsl:apply-templates select="spell[contains(child::level,'Druid 1')]">
                        <xsl:sort select="name"/>
                    </xsl:apply-templates>
                    -->
                    
                    <!-- Select only cantrups.
                    <xsl:apply-templates select="spell[contains(child::level,' 0')]">
                        <xsl:sort select="name"/>
                    </xsl:apply-templates>
                    -->
                    
                    <!-- Select only touch spells.
                    <xsl:apply-templates select="spell[range='Touch']">
                        <xsl:sort select="name"/>
                    </xsl:apply-templates>
                    -->
                    
                    <!-- Select only touch spells that do not require material components.
                    <xsl:apply-templates select="spell[range='Touch' or not(contains(child::components,'M'))]">
                        <xsl:sort select="name"/>
                    </xsl:apply-templates>
                    -->
                </fo:flow>
            </fo:page-sequence>
        </fo:root>
    </xsl:template>

    <xsl:template match="spell">
        <!-- You can do a great deal of filtering with the select statement above. In some cases, you might
            need to get more advanced. Right here you can check each spell as it is processed, so you could
            build complex checking logic if you needed it. A "shell" for this is provided here that creates
            a variable that is true if you want to include the spell, false if you don't. The code below
            always sets it to true, but you can alter it to do more complicated checks. Most often, you will
            not need to, however. -->
        <xsl:variable name="include">
            <xsl:choose>
                <xsl:when test="1">1</xsl:when>
                <xsl:otherwise>1</xsl:otherwise>
            </xsl:choose>
        </xsl:variable>

        <xsl:if test="$include">
            <!-- Each spell is a fixed, single cell table in the exact dimentions of a single card on the Avery layout. -->
            <fo:table table-layout="fixed" width="3.5in" height="2in">
                <fo:table-column column-width="3.5in"/>
                <fo:table-body>
                    <fo:table-row height="2in">
                        <!-- Tweak the padding to nudge the block of text around within the card. In
                        making tests. It may be useful to add a border using the following attributes:
                        border-width="thin" border-style="solid" -->
                        <fo:table-cell padding-top="0" padding-left="0.075in" padding-bottom="0"
                            padding-right="0.075in" height="2in" >
                            <fo:block xsl:use-attribute-sets="title">
                                <xsl:value-of select="name"/>
                            </fo:block>
                            <fo:block xsl:use-attribute-sets="stats">
                                <xsl:value-of select="school"/>
                                <xsl:if test="subschool">
                                    <xsl:text> (</xsl:text>
                                    <xsl:value-of select="subschool"/>
                                    <xsl:text>)</xsl:text>
                                </xsl:if>
                                <xsl:if test="descriptor">
                                    <xsl:text> [</xsl:text>
                                    <xsl:value-of select="descriptor"/>
                                    <xsl:text>]</xsl:text>
                                </xsl:if>
                                <xsl:text> &#x2022; </xsl:text>
                                <!-- Strip the spaces out of the components to save space. -->
                                <xsl:call-template name="replace-string">
                                    <xsl:with-param name="text" select="components"/>
                                    <xsl:with-param name="from" select="' '"/>
                                    <xsl:with-param name="to" select="''"/>
                                </xsl:call-template>
                            </fo:block>
                            <fo:block xsl:use-attribute-sets="stats">
                                <xsl:value-of select="level"/>
                                <xsl:text> &#x2022; </xsl:text>
                                <xsl:text>Rs: </xsl:text>
                                <xsl:value-of select="spell_resistance"/>
                                <xsl:text> &#x2022; </xsl:text>
                                <xsl:text>Sv: </xsl:text>
                                <xsl:value-of select="saving_throw"/>
                            </fo:block>
                            <fo:block xsl:use-attribute-sets="stats">
                                <xsl:text>Cast: </xsl:text>
                                <xsl:value-of select="casting_time"/>
                                <xsl:text> &#x2022; </xsl:text>
                                <xsl:text>Dur: </xsl:text>
                                <xsl:value-of select="duration"/>
                            </fo:block>
                            <fo:block xsl:use-attribute-sets="stats">
                                <xsl:text>Rng: </xsl:text>
                                <xsl:value-of select="range"/>
                            </fo:block>
                            <xsl:if test="target">
                                <fo:block xsl:use-attribute-sets="stats">
                                    <xsl:text>Target: </xsl:text>
                                    <xsl:value-of select="target"/>
                                </fo:block>
                            </xsl:if>

                            <!-- Use the description if it is not too long. If it looks like it might
                            overflow the card, use a smaller font. If even that looks like it might
                            overflow, use the short description instead. There is no way
                            to test for the final rendered height of the text, so the only option
                            is to guess based on the length of the descrption. This isn't exact,
                            so the numbers have to be conservative. Descriptions with more paragraph
                            breaks take up more space, so take that into account.-->

                            <xsl:variable name="paragraphs" select="count(description/p)"/>
                            <xsl:variable name="desclen"
                                select="string-length(description) + round(1.3*string-length(target)) + 20*($paragraphs - 1)"/>
                            <xsl:choose>
                                <xsl:when test="$desclen &lt; 650">
                                    <xsl:apply-templates select="description"/>
                                </xsl:when>
                                <xsl:when test="$desclen &lt; 875">
                                    <xsl:apply-templates select="description" mode="small"/>
                                </xsl:when>
                                <xsl:when test="$desclen &lt; 891 and $paragraphs &lt; 6">
                                    <xsl:apply-templates select="description" mode="small"/>
                                </xsl:when>
                                <xsl:when test="$desclen &lt; 935 and $paragraphs &lt; 4">
                                    <xsl:apply-templates select="description" mode="small"/>
                                </xsl:when>
                                <xsl:otherwise>
                                    <fo:block space-before="2pt">
                                        <xsl:value-of select="short_description"/>
                                    </fo:block>
                                    <!--
                                    <xsl:element name="fo:block">
                                        <xsl:attribute name="space-before">2pt</xsl:attribute>
                                        <xsl:if test="$desclen &lt; 950">
                                            <xsl:attribute name="color">red</xsl:attribute>
                                        </xsl:if>
                                        <xsl:text>Full text is </xsl:text>
                                        <xsl:value-of select="$desclen"/>
                                        <xsl:text> characters. Pargraphs = </xsl:text>
                                        <xsl:value-of select="$paragraphs"/>
                                    </xsl:element>
                                    -->
                                </xsl:otherwise>
                            </xsl:choose>
                        </fo:table-cell>
                    </fo:table-row>
                </fo:table-body>
            </fo:table>
        </xsl:if>
    </xsl:template>

    <xsl:template match="p">
        <fo:block space-before="2pt" line-height="9pt">
            <xsl:apply-templates/>
        </fo:block>
    </xsl:template>

    <xsl:template match="p" mode="small">
        <fo:block space-before="1pt" font-size="7pt" line-height="8pt">
            <xsl:apply-templates/>
        </fo:block>
    </xsl:template>

    <xsl:template match="b">
        <fo:inline font-weight="bold">
            <xsl:apply-templates/>
        </fo:inline>
    </xsl:template>

    <xsl:template match="i">
        <fo:inline font-style="italic">
            <xsl:apply-templates/>
        </fo:inline>
    </xsl:template>

    <!-- utility function to cover a stupid lack of a string replace function in some XSL implementations. -->
    <xsl:template name="replace-string">
        <xsl:param name="text"/>
        <xsl:param name="from"/>
        <xsl:param name="to"/>

        <xsl:choose>
            <xsl:when test="contains($text, $from)">

                <xsl:variable name="before" select="substring-before($text, $from)"/>
                <xsl:variable name="after" select="substring-after($text, $from)"/>
                <xsl:variable name="prefix" select="concat($before, $to)"/>

                <xsl:value-of select="$before"/>
                <xsl:value-of select="$to"/>
                <xsl:call-template name="replace-string">
                    <xsl:with-param name="text" select="$after"/>
                    <xsl:with-param name="from" select="$from"/>
                    <xsl:with-param name="to" select="$to"/>
                </xsl:call-template>
            </xsl:when>
            <xsl:otherwise>
                <xsl:value-of select="$text"/>
            </xsl:otherwise>
        </xsl:choose>
    </xsl:template>
</xsl:stylesheet>

