{-# LANGUAGE PatternGuards #-}{-
Copyright (C) 2008 Andrea Rossato <andrea.rossato@ing.unitn.it>
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
-}{- |
Module : Text.Pandoc.Writers.OpenDocument
Copyright : Copyright (C) 2008 Andrea Rossato
License : GNU GPL, version 2 or above
Maintainer : Andrea Rossato <andrea.rossato@ing.unitn.it>
Stability : alpha
Portability : portable
Conversion of 'Pandoc' documents to OpenDocument XML.
-}moduleText.Pandoc.Writers.OpenDocument(writeOpenDocument)whereimportText.Pandoc.DefinitionimportText.Pandoc.SharedimportText.Pandoc.XMLimportText.Pandoc.Templates(renderTemplate)importText.Pandoc.Readers.TeXMathimportText.PrettyPrint.HughesPJhiding(Str)importText.Printf(printf)importControl.Applicative((<$>))importControl.Arrow((***),(>>>))importControl.Monad.Statehiding(when)importData.Char(chr)-- | Auxiliary function to convert Plain block to Para.plainToPara::Block->BlockplainToPara(Plainx)=ParaxplainToParax=x---- OpenDocument writer--dataWriterState=WriterState{stNotes::[Doc],stTableStyles::[Doc],stParaStyles::[Doc],stListStyles::[(Int,[Doc])],stTextStyles::[Doc],stTextStyleAttr::[(TextStyle,[(String,String)])],stIndentPara::Int,stInDefinition::Bool,stTight::Bool}defaultWriterState::WriterStatedefaultWriterState=WriterState{stNotes=[],stTableStyles=[],stParaStyles=[],stListStyles=[],stTextStyles=[],stTextStyleAttr=[],stIndentPara=0,stInDefinition=False,stTight=False}when::Bool->Doc->Docwhenpa=ifpthenaelseemptyaddTableStyle::Doc->StateWriterState()addTableStylei=modify$\s->s{stTableStyles=i:stTableStyless}addNote::Doc->StateWriterState()addNotei=modify$\s->s{stNotes=i:stNotess}addParaStyle::Doc->StateWriterState()addParaStylei=modify$\s->s{stParaStyles=i:stParaStyless}addTextStyle::Doc->StateWriterState()addTextStylei=modify$\s->s{stTextStyles=i:stTextStyless}addTextStyleAttr::(TextStyle,[(String,String)])->StateWriterState()addTextStyleAttri=modify$\s->s{stTextStyleAttr=i:stTextStyleAttrs}rmTextStyleAttr::StateWriterState()rmTextStyleAttr=modify$\s->s{stTextStyleAttr=rmHead(stTextStyleAttrs)}wherermHeadl=ifl/=[]thentaillelse[]increaseIndent::StateWriterState()increaseIndent=modify$\s->s{stIndentPara=1+stIndentParas}resetIndent::StateWriterState()resetIndent=modify$\s->s{stIndentPara=(stIndentParas)-1}inTightList::StateWriterStatea->StateWriterStateainTightListf=modify(\s->s{stTight=True})>>f>>=\r->modify(\s->s{stTight=False})>>returnrsetInDefinitionList::Bool->StateWriterState()setInDefinitionListb=modify$\s->s{stInDefinition=b}inParagraphTags::Doc->DocinParagraphTags=inTagsFalse"text:p"[("text:style-name","Text_20_body")]inParagraphTagsWithStyle::String->Doc->DocinParagraphTagsWithStylesty=inTagsFalse"text:p"[("text:style-name",sty)]inSpanTags::String->Doc->DocinSpanTagss=inTagsFalse"text:span"[("text:style-name",s)]withTextStyle::TextStyle->StateWriterStatea->StateWriterStateawithTextStylesf=addTextStyleAttr(s,textStyleAttrs)>>f>>=\r->rmTextStyleAttr>>returnrinTextStyle::Doc->StateWriterStateDocinTextStyled=doat<-getsstTextStyleAttrifat==[]thenreturndelsedotn<-(+)1.length<$>getsstTextStylesaddTextStyle$inTagsFalse"style:style"[("style:name","T"++showtn),("style:family","text")]$selfClosingTag"style:text-properties"(concatMapsndat)return$inTagsFalse"text:span"[("text:style-name","T"++showtn)]dinHeaderTags::Int->Doc->DocinHeaderTagsi=inTagsFalse"text:h"[("text:style-name","Heading_20_"++showi),("text:outline-level",showi)]inQuotes::QuoteType->Doc->DocinQuotesSingleQuotes=text"&#8216;"<>s<>text"&#8217;"inQuotesDoubleQuotes=text"&#8220;"<>s<>text"&#8221;"handleSpaces::String->DochandleSpacess|(' ':_)<-s=genTags|('\t':x)<-s=selfClosingTag"text:tab"[]<>rmx|otherwise=rmswheregenTag=span(==' ')>>>tag.length***rm>>>uncurry(<>)tagn=when(n/=0)$selfClosingTag"text:s"[("text:c",shown)]rm(' ':xs)=char' '<>genTagxsrm('\t':xs)=selfClosingTag"text:tab"[]<>genTagxsrm(x:xs)=charx<>rmxsrm[]=empty-- | Convert Pandoc document to string in OpenDocument format.writeOpenDocument::WriterOptions->Pandoc->StringwriteOpenDocumentopts(Pandoc(Metatitleauthorsdate)blocks)=let((doc,title',authors',date'),s)=fliprunStatedefaultWriterState$dotitle''<-inlinesToOpenDocumentoptstitleauthors''<-mapM(inlinesToOpenDocumentopts)authorsdate''<-inlinesToOpenDocumentoptsdatedoc''<-blocksToOpenDocumentoptsblocksreturn(doc'',title'',authors'',date'')body'=renderdocstyles=stTableStyless++stParaStyless++stTextStylesslistStyle(n,l)=inTagsTrue"text:list-style"[("style:name","L"++shown)](vcatl)listStyles=maplistStyle(stListStyless)automaticStyles=inTagsIndented"office:automatic-styles"$vcat$reverse$styles++listStylescontext=writerVariablesopts++[("body",body'),("automatic-styles",renderautomaticStyles),("title",rendertitle'),("date",renderdate')]++[("author",rendera)|a<-authors']inifwriterStandaloneoptsthenrenderTemplatecontext$writerTemplateoptselsebody'withParagraphStyle::WriterOptions->String->[Block]->StateWriterStateDocwithParagraphStyleos(b:bs)|Paral<-b=go=<<inParagraphTagsWithStyles<$>inlinesToOpenDocumentol|otherwise=go=<<blockToOpenDocumentobwheregoi=(<>)i<$>withParagraphStyleosbswithParagraphStyle__[]=returnemptyinPreformattedTags::String->StateWriterStateDocinPreformattedTagss=don<-paraStyle"Preformatted_20_Text"[]return.inParagraphTagsWithStyle("P"++shown).handleSpaces$sorderedListToOpenDocument::WriterOptions->Int->[[Block]]->StateWriterStateDocorderedListToOpenDocumentopnbs=vcat.map(inTagsIndented"text:list-item")<$>mapM(orderedItemToOpenDocumentopn.mapplainToPara)bsorderedItemToOpenDocument::WriterOptions->Int->[Block]->StateWriterStateDocorderedItemToOpenDocumenton(b:bs)|OrderedListal<-b=newLevelal|Paral<-b=go=<<inParagraphTagsWithStyle("P"++shown)<$>inlinesToOpenDocumentol|otherwise=go=<<blockToOpenDocumentobwheregoi=($$)i<$>orderedItemToOpenDocumentonbsnewLevelal=donn<-length<$>getsstParaStylesls<-head<$>getsstListStylesmodify$\s->s{stListStyles=orderedListLevelStyleals:tail(stListStyless)}inTagsIndented"text:list"<$>orderedListToOpenDocumentonnlorderedItemToOpenDocument__[]=returnemptyisTightList::[[Block]]->BoolisTightList[]=FalseisTightList(b:_)|Plain{}:_<-b=True|otherwise=FalsenewOrderedListStyle::Bool->ListAttributes->StateWriterState(Int,Int)newOrderedListStyleba=doln<-(+)1.length<$>getsstListStylesletnbs=orderedListLevelStylea(ln,[])pn<-ifbtheninTightList(paraListStyleln)elseparaListStylelnmodify$\s->s{stListStyles=nbs:stListStyless}return(ln,pn)bulletListToOpenDocument::WriterOptions->[[Block]]->StateWriterStateDocbulletListToOpenDocumentob=doln<-(+)1.length<$>getsstListStyles(pn,ns)<-ifisTightListbtheninTightList(bulletListStyleln)elsebulletListStylelnmodify$\s->s{stListStyles=ns:stListStyless}is<-listItemsToOpenDocument("P"++showpn)obreturn$inTagsTrue"text:list"[("text:style-name","L"++showln)]islistItemsToOpenDocument::String->WriterOptions->[[Block]]->StateWriterStateDoclistItemsToOpenDocumentsois=vcat.map(inTagsIndented"text:list-item")<$>mapM(withParagraphStyleos.mapplainToPara)isdeflistItemToOpenDocument::WriterOptions->([Inline],[[Block]])->StateWriterStateDocdeflistItemToOpenDocumento(t,d)=doletts=ifisTightListdthen"Definition_20_Term_20_Tight"else"Definition_20_Term"ds=ifisTightListdthen"Definition_20_Definition_20_Tight"else"Definition_20_Definition"t'<-withParagraphStyleots[Parat]d'<-liftMvcat$mapM(withParagraphStyleods.(mapplainToPara))dreturn$t'$$d'inBlockQuote::WriterOptions->Int->[Block]->StateWriterStateDocinBlockQuoteoi(b:bs)|BlockQuotel<-b=doincreaseIndentni<-paraStyle"Quotations"[]go=<<inBlockQuoteoni(mapplainToParal)|Paral<-b=dogo=<<inParagraphTagsWithStyle("P"++showi)<$>inlinesToOpenDocumentol|otherwise=dogo=<<blockToOpenDocumentobwheregoblock=($$)block<$>inBlockQuoteoibsinBlockQuote__[]=resetIndent>>returnempty-- | Convert a list of Pandoc blocks to OpenDocument.blocksToOpenDocument::WriterOptions->[Block]->StateWriterStateDocblocksToOpenDocumentob=vcat<$>mapM(blockToOpenDocumento)b-- | Convert a Pandoc block element to OpenDocument.blockToOpenDocument::WriterOptions->Block->StateWriterStateDocblockToOpenDocumentobs|Plainb<-bs=inParagraphTags<$>inlinesToOpenDocumentob|Parab<-bs=inParagraphTags<$>inlinesToOpenDocumentob|Headerib<-bs=inHeaderTagsi<$>inlinesToOpenDocumentob|BlockQuoteb<-bs=mkBlockQuoteb|CodeBlock_s<-bs=preformatteds|RawHtml_<-bs=returnempty|DefinitionListb<-bs=defListb|BulletListb<-bs=bulletListToOpenDocumentob|OrderedListab<-bs=orderedListab|Tablecawhr<-bs=tablecawhr|Null<-bs=returnempty|HorizontalRule<-bs=return$selfClosingTag"text:p"[("text:style-name","Horizontal_20_Line")]|otherwise=returnemptywheredefListb=dosetInDefinitionListTruer<-vcat<$>mapM(deflistItemToOpenDocumento)bsetInDefinitionListFalsereturnrpreformatteds=vcat<$>mapM(inPreformattedTags.escapeStringForXML)(liness)mkBlockQuoteb=doincreaseIndenti<-paraStyle"Quotations"[]inBlockQuoteoi(mapplainToParab)orderedListab=do(ln,pn)<-newOrderedListStyle(isTightListb)ainTagsTrue"text:list"[("text:style-name","L"++showln)]<$>orderedListToOpenDocumentopnbtablecawhr=dotn<-length<$>getsstTableStylespn<-length<$>getsstParaStylesletgenIds=mapchr[65..]name="Table"++show(tn+1)columnIds=zipgenIdswmkColumnn=selfClosingTag"table:table-column"[("table:style-name",name++"."++[fstn])]columns=mapmkColumncolumnIdsparaHStyles=paraTableStyles"Heading"pnaparaStyles=paraTableStyles"Contents"(pn+length(newParaparaHStyles))anewPara=mapsnd.filter(not.isEmpty.snd)addTableStyle$tableStyletncolumnIdsmapM_addParaStyle.newPara$paraHStyles++paraStylescaptionDoc<-ifnullcthenreturnemptyelsewithParagraphStyleo"Caption"[Parac]th<-ifallnullhthenreturnemptyelsecolHeadsToOpenDocumentoname(mapfstparaHStyles)htr<-mapM(tableRowToOpenDocumentoname(mapfstparaStyles))rreturn$inTagsTrue"table:table"[("table:name",name),("table:style-name",name)](vcatcolumns$$th$$vcattr)$$captionDoccolHeadsToOpenDocument::WriterOptions->String->[String]->[[Block]]->StateWriterStateDoccolHeadsToOpenDocumentotnnshs=inTagsIndented"table:table-header-rows".inTagsIndented"table:table-row".vcat<$>mapM(tableItemToOpenDocumentotn)(zipnshs)tableRowToOpenDocument::WriterOptions->String->[String]->[[Block]]->StateWriterStateDoctableRowToOpenDocumentotnnscs=inTagsIndented"table:table-row".vcat<$>mapM(tableItemToOpenDocumentotn)(zipnscs)tableItemToOpenDocument::WriterOptions->String->(String,[Block])->StateWriterStateDoctableItemToOpenDocumentotn(n,i)=leta=[("table:style-name",tn++".A1"),("office:value-type","string")]ininTagsTrue"table:table-cell"a<$>withParagraphStyleon(mapplainToParai)-- | Convert a list of inline elements to OpenDocument.inlinesToOpenDocument::WriterOptions->[Inline]->StateWriterStateDocinlinesToOpenDocumentol=hcat<$>mapM(inlineToOpenDocumento)l-- | Convert an inline element to OpenDocument.inlineToOpenDocument::WriterOptions->Inline->StateWriterStateDocinlineToOpenDocumentoils|Ellipses<-ils=inTextStyle$text"&#8230;"|EmDash<-ils=inTextStyle$text"&#8212;"|EnDash<-ils=inTextStyle$text"&#8211;"|Apostrophe<-ils=inTextStyle$text"&#8217;"|Space<-ils=inTextStyle$char' '|LineBreak<-ils=return$selfClosingTag"text:line-break"[]|Strs<-ils=inTextStyle$handleSpaces$escapeStringForXMLs|Emphl<-ils=withTextStyleItalic$inlinesToOpenDocumentol|Strongl<-ils=withTextStyleBold$inlinesToOpenDocumentol|Strikeoutl<-ils=withTextStyleStrike$inlinesToOpenDocumentol|Superscriptl<-ils=withTextStyleSup$inlinesToOpenDocumentol|Subscriptl<-ils=withTextStyleSub$inlinesToOpenDocumentol|SmallCapsl<-ils=withTextStyleSmallC$inlinesToOpenDocumentol|Quotedtl<-ils=inQuotest<$>inlinesToOpenDocumentol|Codes<-ils=preformatteds|Math_s<-ils=inlinesToOpenDocumento(readTeXMaths)|Cite_l<-ils=inlinesToOpenDocumentol|TeXs<-ils=preformatteds|HtmlInlines<-ils=preformatteds|Linkl(s,t)<-ils=mkLinkst<$>inlinesToOpenDocumentol|Image_(s,_)<-ils=return$mkImgs|Notel<-ils=mkNotel|otherwise=returnemptywherepreformatted=return.inSpanTags"Teletype".handleSpaces.escapeStringForXMLmkLinkst=inTagsFalse"text:a"[("xlink:type","simple"),("xlink:href",s),("office:name",t)].inSpanTags"Definition"mkImgs=inTagsFalse"draw:frame"[]$selfClosingTag"draw:image"[("xlink:href",s),("xlink:type","simple"),(" xlink:show","embed"),("xlink:actuate","onLoad")]mkNotel=don<-length<$>getsstNotesletfootNotet=inTagsFalse"text:note"[("text:id","ftn"++shown),("text:note-class","footnote")]$inTagsSimple"text:note-citation"(text.show$n+1)<>inTagsSimple"text:note-body"tnn<-footNote<$>withParagraphStyleo"Footnote"laddNotennreturnnnbulletListStyle::Int->StateWriterState(Int,(Int,[Doc]))bulletListStylel=letdoStylesi=inTagsTrue"text:list-level-style-bullet"[("text:level",show(i+1)),("text:style-name","Bullet_20_Symbols"),("style:num-suffix","."),("text:bullet-char",[bulletList!!i])](listLevelStyle(1+i))bulletList=mapchr$cycle[8226,8227,8259]listElStyle=mapdoStyles[0..9]indopn<-paraListStylelreturn(pn,(l,listElStyle))orderedListLevelStyle::ListAttributes->(Int,[Doc])->(Int,[Doc])orderedListLevelStyle(s,n,d)(l,ls)=letsuffix=casedofOneParen->[("style:num-suffix",")")]TwoParens->[("style:num-prefix","("),("style:num-suffix",")")]_->[("style:num-suffix",".")]format=casenofUpperAlpha->"A"LowerAlpha->"a"UpperRoman->"I"LowerRoman->"i"_->"1"listStyle=inTagsTrue"text:list-level-style-number"([("text:level",show$1+lengthls),("text:style-name","Numbering_20_Symbols"),("style:num-format",format),("text:start-value",shows)]++suffix)(listLevelStyle(1+lengthls))in(l,ls++[listStyle])listLevelStyle::Int->DoclistLevelStylei=letindent=show(0.25*fromIntegrali::Double)inselfClosingTag"style:list-level-properties"[("text:space-before",indent++"in"),("text:min-label-width","0.25in")]tableStyle::Int->[(Char,Double)]->DoctableStylenumwcs=lettableId="Table"++show(num+1)table=inTagsTrue"style:style"[("style:name",tableId)]$selfClosingTag"style:table-properties"[("table:align","center")]colStyle(c,0)=selfClosingTag"style:style"[("style:name",tableId++"."++[c]),("style:family","table-column")]colStyle(c,w)=inTagsTrue"style:style"[("style:name",tableId++"."++[c]),("style:family","table-column")]$selfClosingTag"style:table-column-properties"[("style:rel-column-width",printf"%d*"$(floor$w*65535::Integer))]cellStyle=inTagsTrue"style:style"[("style:name",tableId++".A1"),("style:family","table-cell")]$selfClosingTag"style:table-cell-properties"[("fo:border","none")]columnStyles=mapcolStylewcsintable$$vcatcolumnStyles$$cellStyleparaStyle::String->[(String,String)]->StateWriterStateIntparaStyleparentattrs=dopn<-(+)1.length<$>getsstParaStylesi<-(*)0.5.fromIntegral<$>getsstIndentPara::StateWriterStateDoubleb<-getsstInDefinitiont<-getsstTightletstyleAttr=[("style:name","P"++showpn),("style:family","paragraph"),("style:parent-style-name",parent)]indentVal=flip(++)"in".show$ifbthen(max0.5i)elseitight=iftthen[("fo:margin-top","0in"),("fo:margin-bottom","0in")]else[]indent=when(i/=0||b||t)$selfClosingTag"style:paragraph-properties"$[("fo:margin-left",indentVal),("fo:margin-right","0in"),("fo:text-indent","0in"),("style:auto-text-indent","false")]++tightaddParaStyle$inTagsTrue"style:style"(styleAttr++attrs)indentreturnpnparaListStyle::Int->StateWriterStateIntparaListStylel=paraStyle"Text_20_body"[("style:list-style-name","L"++showl)]paraTableStyles::String->Int->[Alignment]->[(String,Doc)]paraTableStyles__[]=[]paraTableStylests(a:xs)|AlignRight<-a=(pNames,ress"end"):paraTableStylest(s+1)xs|AlignCenter<-a=(pNames,ress"center"):paraTableStylest(s+1)xs|otherwise=("Table_20_"++t,empty):paraTableStylestsxswherepNamesn="P"++show(sn+1)ressnx=inTagsTrue"style:style"[("style:name",pNamesn),("style:family","paragraph"),("style:parent-style-name","Table_20_"++t)]$selfClosingTag"style:paragraph-properties"[("fo:text-align",x),("style:justify-single-word","false")]dataTextStyle=Italic|Bold|Strike|Sub|Sup|SmallCderiving(Eq)textStyleAttr::TextStyle->[(String,String)]textStyleAttrs|Italic<-s=[("fo:font-style","italic"),("style:font-style-asian","italic"),("style:font-style-complex","italic")]|Bold<-s=[("fo:font-weight","bold"),("style:font-weight-asian","bold"),("style:font-weight-complex","bold")]|Strike<-s=[("style:text-line-through-style","solid")]|Sub<-s=[("style:text-position","sub 58%")]|Sup<-s=[("style:text-position","super 58%")]|SmallC<-s=[("fo:font-variant","small-caps")]|otherwise=[]