I'm trying to customize our developer guide pdf to have a decent looking first page. I'm totally fine with doing everything in photoshop as an image since asciidoc seems to be pretty limited in abilities here.
Unfortunately despite looking everywhere I can't seem to find a single way to customize the cover sheet or replace it with an image that works for asciidoctor-fopub.
The annoying thing is that this use case is specifically mentioned in the readme of the project https://github.com/asciidoctor/asciidoctor-fopub (under Custom XSL templates) but there is no mention on how to actually do it other than "its in the XSL". As someone who isn't familiar with docbook I have no idea where to begin.
asciidoctor-pdf has a tag for including an image but it barfs on our document and generates garbage (the ToC is painted over everything).
This can be done using division.xsl file under build/fopub/docbook/fo/
search for "Placeholder templates"
and paste below code there, you will be able to see cover image in your generated fopub
<!-- Placeholder templates -->
<xsl:template name="front.cover">
<xsl:call-template name="page.sequence">
<xsl:with-param name="master-reference">titlepage-cover-image</xsl:with-param>
<xsl:with-param name="content">
<fo:block text-align="center">
<fo:external-graphic src="url(path/to/cover.png)" content-height="250mm" content-width="176mm"/>
</fo:block>
</xsl:with-param>
</xsl:call-template>
</xsl:template>
Change height and width as per your book requirement.
Once that is done you need to provide margin for image on the cover page to be clear. Add below in pagesetup.xsl
<fo:simple-page-master master-name="titlepage-cover-image"
page-width="{$page.width}"
page-height="{$page.height}"
margin-top="{$title.page.margin.top}"
margin-bottom="{$page.margin.bottom}">
<xsl:attribute name="margin-{$direction.align.start}">
<xsl:value-of select="$page.margin.inner"/>
<xsl:if test="$fop.extensions != 0">
<xsl:value-of select="concat(' - (',$title.margin.left,')')"/>
</xsl:if>
</xsl:attribute>
<xsl:attribute name="margin-{$direction.align.end}">
<xsl:value-of select="$page.margin.outer"/>
</xsl:attribute>
<xsl:if test="$axf.extensions != 0">
<xsl:call-template name="axf-page-master-properties">
<xsl:with-param name="page.master">titlepage-cover-image</xsl:with-param>
</xsl:call-template>
</xsl:if>
<fo:region-body margin-bottom="{$body.margin.bottom}"
margin-top="{$title.body.margin.top}"
column-gap="{$column.gap.titlepage}"
column-count="{$column.count.titlepage}">
<xsl:attribute name="margin-{$direction.align.start}">
<xsl:value-of select="$body.margin.inner"/>
</xsl:attribute>
<xsl:attribute name="margin-{$direction.align.end}">
<xsl:value-of select="$body.margin.outer"/>
</xsl:attribute>
</fo:region-body>
<fo:region-before region-name="xsl-region-before-first"
extent="{$region.before.extent}"
precedence="{$region.before.precedence}"
display-align="before"/>
<fo:region-after region-name="xsl-region-after-first"
extent="{$region.after.extent}"
precedence="{$region.after.precedence}"
display-align="after"/>
<xsl:call-template name="region.inner">
<xsl:with-param name="sequence">first</xsl:with-param>
<xsl:with-param name="pageclass">titlepage</xsl:with-param>
</xsl:call-template>
<xsl:call-template name="region.outer">
<xsl:with-param name="sequence">first</xsl:with-param>
<xsl:with-param name="pageclass">titlepage</xsl:with-param>
</xsl:call-template>
</fo:simple-page-master>
Then add variables in fo-pdf.xsl to zeroed out your body top margin.
HTH.
Related
How do you replace an xml value, for example:
<name>Linda O'Connel</name>
to:
<name>Linda O''Connel</name>
via XSLT?
I need this because I have to pass this value in a powershell command line and to other platforms since the "double single quote" is needed to escape the apostrophe/single quotes.
Assuming an XSLT 1.0 processor, you will need to use a recursive named template for this, e.g:
<xsl:template name="replace">
<xsl:param name="text"/>
<xsl:param name="searchString">'</xsl:param>
<xsl:param name="replaceString">''</xsl:param>
<xsl:choose>
<xsl:when test="contains($text,$searchString)">
<xsl:value-of select="substring-before($text,$searchString)"/>
<xsl:value-of select="$replaceString"/>
<!-- recursive call -->
<xsl:call-template name="replace">
<xsl:with-param name="text" select="substring-after($text,$searchString)"/>
<xsl:with-param name="searchString" select="$searchString"/>
<xsl:with-param name="replaceString" select="$replaceString"/>
</xsl:call-template>
</xsl:when>
<xsl:otherwise>
<xsl:value-of select="$text"/>
</xsl:otherwise>
</xsl:choose>
</xsl:template>
Example of call:
<xsl:template match="name">
<xsl:copy>
<xsl:call-template name="replace">
<xsl:with-param name="text" select="."/>
</xsl:call-template>
</xsl:copy>
</xsl:template>
You can also try out the following.
<xsl:variable name="temp">'</xsl:variable>
<name>
<xsl:value-of select="concat(substring-before(name,$temp),$temp,$temp,substring-after(name,$temp))"/>
</name>
I have following xslt:
<xsl:template match="P">
<fo:block>
1.1 First Image
</fo:block>
<xsl:apply-templates />
</xsl:template>
<xsl:template match="figure">
<fo:block margin-top="-0.30in" keep-with-previous.within-page="always">
<fo:external-graphic>
<xsl:attribute name="src"> <xsl:value-of select="abc.svg" />
</xsl:attribute>
</fo:external-graphic>
</fo:block>
</xsl:template>
Both these templates will be called in a recursive method.
My output is :
P tag is coming in one page where it has space. Since figures are big, it shows in the next page. I want them to be together.
Tried solutions: keep-with, break-after, page-break-after.
Keep-with is not working. Others are creating new page for every P tag though there is space in the previous tag.
I am generating PDF output using FOP1.0.
Please help.
Thanks in advance.
change
<xsl:template match="P">
<fo:block>
1.1 First Image
</fo:block>
<xsl:apply-templates />
</xsl:template>
to
<xsl:template match="P">
<fo:block keep-with-next.within-page="always">
1.1 First Image
</fo:block>
<xsl:apply-templates />
</xsl:template>
I need to replace values of multiple elements of an input xml. The input file has around 300 elements of which I need to replace around 100 elements. I have used identity template before, but I think it would require me to write 100 different templates each for replacing a single element value. I am not very good at xslts, so, am I thinking it right, or is there a better an elegant approach? Please advice.
Edit
Here is the Link of sample input xml.
The output will have almost the same structure, but different values for some of the elements.
Well, something I did similar before before so I am happy to share ... modified to your example BUT does not do the value mapping, it does name mapping.
First create a simple mapping file like this (NOTE: you would need the namespaces to do this right
<env:map xmlns:env="http://www.w3.org/2003/05/soap-envelope">
<elem old="CardCode" new="CodeCardNEW"/>
<elem old="CardName" new="IAMNew"/>
</env:map>
Then one template will do you with a lookup. There are likely better ways to do the mapping, I am just for-each looping over them all ... a key would be better. But this gets you there.
Output from your file with above:
<env:Envelope xmlns:env="http://www.w3.org/2003/05/soap-envelope">
<env:Body>
<GetByKeyResponse>
<BOM>
<BO>
<AdmInfo>
<Object>oBusinessPartners</Object>
</AdmInfo>
<BusinessPartners>
<row>
<CodeCardNEW>CR43WEB</CodeCardNEW>
<IAMNew>Zack Burns</IAMNew>
<CardType>cCustomer</CardType>
...
And here's the XSL:
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:env="http://www.w3.org/2003/05/soap-envelope"
version="1.0">
<xsl:param name="map" select="document('map.xml')/env:map"></xsl:param>
<xsl:template match="text()" priority="1">
<xsl:value-of select="."/>
</xsl:template>
<xsl:template match="#*|node()">
<xsl:variable name="newname">
<xsl:call-template name="namesub">
<xsl:with-param name="name" select="name()"/>
</xsl:call-template>
</xsl:variable>
<xsl:element name="{$newname}">
<xsl:apply-templates select="#*|node()"/>
</xsl:element>
</xsl:template>
<xsl:template name="namesub">
<xsl:param name="name"/>
<xsl:variable name="newname">
<xsl:for-each select="$map/elem">
<xsl:choose>
<xsl:when test="#old = $name">
<xsl:value-of select="#new"/>
</xsl:when>
</xsl:choose>
</xsl:for-each>
</xsl:variable>
<xsl:choose>
<xsl:when test="string-length($newname) > 0">
<xsl:value-of select="$newname"/>
</xsl:when>
<xsl:otherwise>
<xsl:value-of select="$name"/>
</xsl:otherwise>
</xsl:choose>
</xsl:template>
</xsl:stylesheet>
You should be able to adapt this as long as you have a match on name ... if the name is the same in different hierarchies of the XML, you would need more work.
This question (and my problem) is similar to XSLT1 Get the first occurence of a specific tag... But I have an "Undefined variable" error.
I have a XML with refpos attribute, that can be make by this first XSLT,
<xsl:template match="p[#class='ref']">
<xsl:copy>
<xsl:attribute name="refpos">
<xsl:value-of select="position()"/>
</xsl:attribute>
<xsl:apply-templates />
</xsl:copy>
</xsl:template>
<xsl:template match="#*|node()" name="idt">
<xsl:copy><xsl:apply-templates select="#*|node()"/></xsl:copy>
</xsl:template>
Then, I a main (second) XSLT I have,
<xsl:variable name="firstRef">
<xsl:value-of select="(//p[#class='ref'])[1]/#refpos"/>
</xsl:variable>
<xsl:template match="p[#refpos=$firstRef]">
<title><xsl:apply-templates /></title>
</xsl:template>
<xsl:template match="#*|node()" name="idt">
<xsl:copy><xsl:apply-templates select="#*|node()"/></xsl:copy>
</xsl:template>
But here this XSLT not works!!
PS: I also believe that XSLT1 allow us to do everything in one step.
XSLT 1.0 does not allow variable references in template match expressions (XSLT 2.0 does). You'll have to move the check from the predicate inside the template:
<xsl:template match="p">
<xsl:choose>
<xsl:when test="#refpos=$firstRef">
<title><xsl:apply-templates /></title>
</xsl:when>
<xsl:otherwise>
<xsl:call-template name="idt" />
</xsl:otherwise>
</xsl:choose>
</xsl:template>
I've been looking for a method to strip my XML content of apostrophes (') since my DBMS is complaining of receiving those.
I need
<name> Jim O'Connor</name>
to become:
<name> Jim O''Connor</name>
By looking at the example described here, that is supposed to replace ' with '', I constructed the following script:
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output omit-xml-declaration="yes" indent="yes" />
<xsl:template match="node()|#*">
<xsl:copy>
<xsl:apply-templates select="node()|#*" />
</xsl:copy>
</xsl:template>
<xsl:template name="sqlApostrophe">
<xsl:param name="string" />
<xsl:variable name="apostrophe">'</xsl:variable>
<xsl:choose>
<xsl:when test="contains($string,$apostrophe)">
<xsl:value-of select="concat(substring-before($string,$apostrophe), $apostrophe,$apostrophe)"
disable-output-escaping="yes" />
<xsl:call-template name="sqlApostrophe">
<xsl:with-param name="string"
select="substring-after($string,$apostrophe)" />
</xsl:call-template>
</xsl:when>
<xsl:otherwise>
<xsl:value-of select="$string"
disable-output-escaping="yes" />
</xsl:otherwise>
</xsl:choose>
</xsl:template>
<xsl:template match="text()">
<xsl:call-template name="sqlApostrophe">
<xsl:with-param name="string" select="."/>
</xsl:call-template>
</xsl:template>
</xsl:stylesheet>
UPDATE: it works fine
Thanks for your help
The main problem is in your last template. As dacracot points out, xsl:apply-templates does not take a name attribute. To call a named template, you'd use xsl:call-template.
If you want to apply your SQL escaping to all text nodes, you could try replacing your last template with something like this:
<xsl:template match="text()">
<xsl:call-template name="sqlApostrophe">
<xsl:with-param name="string" select="."/>
</xsl:call-template>
</xsl:template>
Also, why disable-output-escaping? If the text contains special characters (<, >, &), you'll get malformed XML as the output.
You have a couple of issues syntactically...
You can't have a name attribute on an apply-templates tag.
Your xpath, "node()|#*", is ambiguous.
Have you run this through a debugger? I would suggest Oxygen.