diff --git a/MANUAL.txt b/MANUAL.txt
index 716ffb199a28..537361929ae5 100644
--- a/MANUAL.txt
+++ b/MANUAL.txt
@@ -5923,6 +5923,33 @@ they cannot contain multiple paragraphs). The syntax is as follows:
Inline and regular footnotes may be mixed freely.
+## Endnotes
+
+You can use the following convention to specify endnotes: surround a
+footnote with a `Span` of class "endnote", like this:
+
+ Here' and endnote[[^1]]{.endnote}.
+
+ [^1]: This is the endnote text.
+
+When a reader or a writer does not know about this convention, those
+notes are just regular footnotes, just with a transparent `Span` wrapper
+around them.
+
+### Extension: `endnotes` ###
+
+Enabling this extension tells some readers and writers to use that
+convention to distinguish endnotes from footnotes.
+
+This extension is supported by the docx and odt readers:
+when you convert with `-f docx+endnotes` or `-f odt+endnotes`,
+the endnotes of the docx or odt file will become
+notes embedded in a span with class "endnote".
+
+It is also supported by the docx and odt writers: when you convert
+with `-t docx+endnotes` or `-t odt+endnotes`, all the notes embedded
+in a span of class "endnote" will be endnotes in the docx or odt file.
+
## Citation syntax
### Extension: `citations` ###
diff --git a/data/docx/word/endnotes.xml b/data/docx/word/endnotes.xml
new file mode 100644
index 000000000000..a9bb2c2174c1
--- /dev/null
+++ b/data/docx/word/endnotes.xml
@@ -0,0 +1,21 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Endnote Text.
+
+
+
+ Endnote Block Text
+
+
+
diff --git a/data/docx/word/settings.xml b/data/docx/word/settings.xml
index 19bd52257d8a..26136d89cb4d 100644
--- a/data/docx/word/settings.xml
+++ b/data/docx/word/settings.xml
@@ -16,6 +16,10 @@
+
+
+
+
diff --git a/pandoc-lua-engine/test/lua/module/pandoc-format.lua b/pandoc-lua-engine/test/lua/module/pandoc-format.lua
index 6a1915a163f7..d2a10b94e675 100644
--- a/pandoc-lua-engine/test/lua/module/pandoc-format.lua
+++ b/pandoc-lua-engine/test/lua/module/pandoc-format.lua
@@ -24,6 +24,7 @@ return {
'citations',
'east_asian_line_breaks',
'empty_paragraphs',
+ 'endnotes',
'gfm_auto_identifiers',
'native_numbering',
'styles',
diff --git a/src/Text/Pandoc/Extensions.hs b/src/Text/Pandoc/Extensions.hs
index 7fcb214cdbf4..6ef19648d5c2 100644
--- a/src/Text/Pandoc/Extensions.hs
+++ b/src/Text/Pandoc/Extensions.hs
@@ -68,6 +68,7 @@ data Extension =
| Ext_element_citations -- ^ Use element-citation elements for JATS citations
| Ext_emoji -- ^ Support emoji like :smile:
| Ext_empty_paragraphs -- ^ Allow empty paragraphs
+ | Ext_endnotes -- ^ Endnotes support when footnotes are embedded in a Span.endnote
| Ext_epub_html_exts -- ^ Recognise the EPUB extended version of HTML
| Ext_escaped_line_breaks -- ^ Treat a backslash at EOL as linebreak
| Ext_example_lists -- ^ Markdown-style numbered examples
@@ -532,12 +533,14 @@ getAllExtensions f = universalExtensions <> getAll f
[ Ext_raw_markdown ]
getAll "docx" = autoIdExtensions <> extensionsFromList
[ Ext_empty_paragraphs
+ , Ext_endnotes
, Ext_native_numbering
, Ext_styles
, Ext_citations
]
getAll "opendocument" = extensionsFromList
[ Ext_empty_paragraphs
+ , Ext_endnotes
, Ext_native_numbering
, Ext_xrefs_name
, Ext_xrefs_number
diff --git a/src/Text/Pandoc/Readers/Docx.hs b/src/Text/Pandoc/Readers/Docx.hs
index da0cca1dc506..152c9584eaf9 100644
--- a/src/Text/Pandoc/Readers/Docx.hs
+++ b/src/Text/Pandoc/Readers/Docx.hs
@@ -52,7 +52,9 @@ implemented, [-] means partially implemented):
- [X] Link (links to an arbitrary bookmark create a span with the target as
id and "anchor" class)
- [X] Image
- - [X] Note (Footnotes and Endnotes are silently combined.)
+ - [X] Note (Footnotes and Endnotes are silently combined,
+ unless Ext_endnotes is enabled: in that case a Note is embedded
+ in a Span with class "endnote")
-}
module Text.Pandoc.Readers.Docx
@@ -342,7 +344,12 @@ runToInlines (Run rs runElems)
transform <- runStyleToTransform rPr
return $ transform ils
runToInlines (Footnote bps) = note . smushBlocks <$> mapM bodyPartToBlocks bps
-runToInlines (Endnote bps) = note . smushBlocks <$> mapM bodyPartToBlocks bps
+runToInlines (Endnote bps) = do
+ isEndnotesExtEnabled <- asks (isEnabled Ext_endnotes . docxOptions)
+ noteInlines <- note . smushBlocks <$> mapM bodyPartToBlocks bps
+ return $ if isEndnotesExtEnabled
+ then spanWith ("", ["endnote"], []) noteInlines
+ else noteInlines
runToInlines (InlineDrawing fp title alt bs ext) = do
(lift . lift) $ P.insertMedia fp Nothing bs
return $ imageWith (extentToAttr ext) (T.pack fp) title $ text alt
diff --git a/src/Text/Pandoc/Readers/ODT.hs b/src/Text/Pandoc/Readers/ODT.hs
index e4c2900ff5be..3b94b112e469 100644
--- a/src/Text/Pandoc/Readers/ODT.hs
+++ b/src/Text/Pandoc/Readers/ODT.hs
@@ -70,20 +70,20 @@ makeFigure x = x
readODT' :: ReaderOptions
-> B.ByteString
-> Either PandocError (Pandoc, MediaBag)
-readODT' _ bytes = bytesToODT bytes-- of
+readODT' opts bytes = bytesToODT opts bytes-- of
-- Right (pandoc, mediaBag) -> Right (pandoc , mediaBag)
-- Left err -> Left err
--
-bytesToODT :: B.ByteString -> Either PandocError (Pandoc, MediaBag)
-bytesToODT bytes = case toArchiveOrFail bytes of
- Right archive -> archiveToODT archive
+bytesToODT :: ReaderOptions -> B.ByteString -> Either PandocError (Pandoc, MediaBag)
+bytesToODT opts bytes = case toArchiveOrFail bytes of
+ Right archive -> archiveToODT opts archive
Left err -> Left $ PandocParseError
$ "Could not unzip ODT: " <> T.pack err
--
-archiveToODT :: Archive -> Either PandocError (Pandoc, MediaBag)
-archiveToODT archive = do
+archiveToODT :: ReaderOptions -> Archive -> Either PandocError (Pandoc, MediaBag)
+archiveToODT opts archive = do
let onFailure msg Nothing = Left $ PandocParseError msg
onFailure _ (Just x) = Right x
contentEntry <- onFailure "Could not find content.xml"
@@ -101,7 +101,7 @@ archiveToODT archive = do
let (dir, name) = splitFileName fp
in (dir == "Pictures/") || (dir /= "./" && name == "content.xml")
let media = filteredFilesFromArchive archive filePathIsODTMedia
- let startState = readerState styles media
+ let startState = readerState opts styles media
either (\_ -> Left $ PandocParseError "Could not convert opendocument") Right
(runConverter' read_body startState contentElem)
diff --git a/src/Text/Pandoc/Readers/ODT/ContentReader.hs b/src/Text/Pandoc/Readers/ODT/ContentReader.hs
index 2e192f3e3230..5c951fa1f2fe 100644
--- a/src/Text/Pandoc/Readers/ODT/ContentReader.hs
+++ b/src/Text/Pandoc/Readers/ODT/ContentReader.hs
@@ -44,6 +44,7 @@ import Text.Pandoc.Builder hiding (underline)
import Text.Pandoc.MediaBag (MediaBag, insertMedia)
import Text.Pandoc.Shared
import Text.Pandoc.Extensions (extensionsFromList, Extension(..))
+import Text.Pandoc.Options (isEnabled, ReaderOptions)
import qualified Text.Pandoc.UTF8 as UTF8
import Text.Pandoc.Readers.Docx.Combine (combineBlocks)
@@ -96,11 +97,13 @@ data ReaderState
, envMedia :: Media
-- | Hold binary resources used in the document
, odtMediaBag :: MediaBag
+ -- | Read endnotes as Note inside a Span of class "endnote"
+ , readEndnotes :: Bool
}
deriving ( Show )
-readerState :: Styles -> Media -> ReaderState
-readerState styles media = ReaderState styles [] 0 M.empty Nothing M.empty media mempty
+readerState :: ReaderOptions -> Styles -> Media -> ReaderState
+readerState opts styles media = ReaderState styles [] 0 M.empty Nothing M.empty media mempty (isEnabled Ext_endnotes opts)
--
pushStyle' :: Style -> ReaderState -> ReaderState
@@ -788,13 +791,23 @@ fixRelativeLink uri =
_ -> uri
-------------------------
--- Footnotes
+-- Footnotes and Endnotes
-------------------------
+endnote :: Blocks -> Inlines
+endnote blocks = spanWith ("", ["endnote"], []) $ note blocks
+
+whichnote :: Bool -> T.Text -> (Blocks -> Inlines)
+whichnote True "endnote" = endnote
+whichnote _ _ = note
+
read_note :: InlineMatcher
read_note = matchingElement NsText "note"
- $ liftA note
+ $ liftA3 whichnote readingEndnotes noteClass
$ matchChildContent' [ read_note_body ]
+ where
+ noteClass = findAttrTextWithDefault NsText "note-class" ""
+ readingEndnotes = getExtraState >>^ readEndnotes
read_note_body :: BlockMatcher
read_note_body = matchingElement NsText "note-body"
diff --git a/src/Text/Pandoc/Writers/Docx.hs b/src/Text/Pandoc/Writers/Docx.hs
index 782833965591..6f6a8f87e9a4 100644
--- a/src/Text/Pandoc/Writers/Docx.hs
+++ b/src/Text/Pandoc/Writers/Docx.hs
@@ -105,7 +105,7 @@ writeDocx opts doc = do
}
-- Phase 5: Relationship extraction
- (baserels, headers, footers, newMaxRelId) <- extractRelationships refArchive distArchive
+ (baserels, headers, footers, newMaxRelId) <- extractRelationships opts refArchive distArchive
let initialSt = defaultWriterState {
stStyleMaps = styleMaps
@@ -123,7 +123,7 @@ writeDocx opts doc = do
[ mknode "w:numRestart" [("w:val","eachSect")] () ]
]
- ((contents, footnotes, comments), st) <- runStateT
+ ((contents, footnotes, endnotes, comments), st) <- runStateT
(runReaderT
(writeOpenXML opts{ writerWrapText = WrapNone }
doc')
@@ -137,12 +137,14 @@ writeDocx opts doc = do
-- because Word sometimes changes these files when a reference.docx is modified,
-- e.g. deleting the reference to footnotes.xml or removing default entries
-- for image content types.
- let contentTypesEntry = mkContentTypesEntry epochtime imgs headers footers refArchive
+ let contentTypesEntry = mkContentTypesEntry epochtime imgs headers footers endnotes refArchive
let relEntry = mkDocumentRelsEntry epochtime baserels imgs (stExternalLinks st)
let contentEntry = toEntry "word/document.xml" epochtime
(BL.fromStrict $ UTF8.fromText contents)
let footnotesEntry = mkFootnotesEntry epochtime footnotes
let footnoteRelEntry = mkFootnoteRelsEntry epochtime (stExternalLinks st)
+ let endnotesEntry = mkEndnotesEntry epochtime endnotes
+ let endnoteRelEntry = mkEndnoteRelsEntry epochtime (stExternalLinks st)
let commentsEntry = mkCommentsEntry epochtime comments
let styleEntry = mkStylesEntry epochtime styledoc styleMaps st opts
numEntry <- mkNumberingEntry refArchive distArchive epochtime (stLists st)
@@ -165,10 +167,12 @@ writeDocx opts doc = do
let archive = foldr addEntryToArchive emptyArchive $
contentTypesEntry : relsEntry : contentEntry : relEntry :
footnoteRelEntry : numEntry : styleEntry : footnotesEntry :
- commentsEntry :
- docPropsEntry : customPropsEntry :
- settingsEntry :
- imageEntries ++ refEntries
+ commentsEntry : docPropsEntry : customPropsEntry : settingsEntry :
+ imageEntries
+ ++ refEntries
+ ++ if (isEnabled Ext_endnotes opts)
+ then [endnoteRelEntry, endnotesEntry]
+ else []
return $ fromArchive archive
newParaPropToOpenXml :: ParaStyleName -> Element
@@ -525,9 +529,9 @@ extractPageLayout refArchive distArchive = do
-- | Parse and augment relationships from reference.docx
extractRelationships :: PandocMonad m
- => Archive -> Archive
+ => WriterOptions -> Archive -> Archive
-> m ([Element], [Element], [Element], Int)
-extractRelationships refArchive distArchive = do
+extractRelationships opts refArchive distArchive = do
let isImageNode e = findAttr (QName "Type" Nothing Nothing) e == Just "http://schemas.openxmlformats.org/officeDocument/2006/relationships/image"
let isHeaderNode e = findAttr (QName "Type" Nothing Nothing) e == Just "http://schemas.openxmlformats.org/officeDocument/2006/relationships/header"
let isFooterNode e = findAttr (QName "Type" Nothing Nothing) e == Just "http://schemas.openxmlformats.org/officeDocument/2006/relationships/footer"
@@ -554,7 +558,7 @@ extractRelationships refArchive distArchive = do
,("Target",target')] () : rels)
_ -> (maxId, rels)
- let (newMaxRelId, baserels) = foldr addBaseRel (maxRelId, parsedRels)
+ let (newMaxRelId, baserels) = foldr addBaseRel (maxRelId, parsedRels) $
[("http://schemas.openxmlformats.org/officeDocument/2006/relationships/numbering",
"numbering.xml")
,("http://schemas.openxmlformats.org/officeDocument/2006/relationships/styles",
@@ -571,7 +575,8 @@ extractRelationships refArchive distArchive = do
"footnotes.xml")
,("http://schemas.openxmlformats.org/officeDocument/2006/relationships/comments",
"comments.xml")
- ]
+ ] ++ [("http://schemas.openxmlformats.org/officeDocument/2006/relationships/endnotes",
+ "endnotes.xml") | isEnabled Ext_endnotes opts]
return (baserels, headers, footers, newMaxRelId)
@@ -595,6 +600,26 @@ mkFootnoteRelsEntry epochtime externalLinks =
[("xmlns","http://schemas.openxmlformats.org/package/2006/relationships")]
linkrels
+-- | Create endnotes XML entry
+mkEndnotesEntry :: Integer -> [Element] -> Entry
+mkEndnotesEntry epochtime endnotes =
+ let notes = mknode "w:endnotes" stdAttributes endnotes
+ in toEntry "word/endnotes.xml" epochtime $ renderXml notes
+
+-- | Create endnote relationships entry
+mkEndnoteRelsEntry :: Integer -> M.Map Text Text -> Entry
+mkEndnoteRelsEntry epochtime externalLinks =
+ let linkrels = map toLinkRel $ M.toList externalLinks
+ toLinkRel (src, ident) = mknode "Relationship"
+ [("Type","http://schemas.openxmlformats.org/officeDocument/2006/relationships/hyperlink")
+ ,("Id",ident)
+ ,("Target",src)
+ ,("TargetMode","External")] ()
+ in toEntry "word/_rels/endnotes.xml.rels" epochtime
+ $ renderXml $ mknode "Relationships"
+ [("xmlns","http://schemas.openxmlformats.org/package/2006/relationships")]
+ linkrels
+
-- | Create comments XML entry
mkCommentsEntry :: Integer -> [Element] -> Entry
mkCommentsEntry epochtime comments =
@@ -627,9 +652,10 @@ mkContentTypesEntry :: Integer
-> [(String, String, Maybe MimeType, B.ByteString)] -- imgs
-> [Element] -- headers
-> [Element] -- footers
+ -> [Element] -- endnotes
-> Archive -- refArchive
-> Entry
-mkContentTypesEntry epochtime imgs headers footers refArchive =
+mkContentTypesEntry epochtime imgs headers footers endnotes refArchive =
let mkOverrideNode (part', contentType') = mknode "Override"
[("PartName", T.pack part')
,("ContentType", contentType')] ()
@@ -638,6 +664,11 @@ mkContentTypesEntry epochtime imgs headers footers refArchive =
fromMaybe "application/octet-stream" mbMimeType)
mkMediaOverride imgpath =
mkOverrideNode ("/" <> imgpath, getMimeTypeDef imgpath)
+ endnotesOverride = if null endnotes
+ then [("/word/endnotes.xml",
+ "application/vnd.openxmlformats-officedocument.wordprocessingml.endnotes+xml")]
+ else []
+
overrides = map mkOverrideNode (
[("/word/webSettings.xml",
"application/vnd.openxmlformats-officedocument.wordprocessingml.webSettings+xml")
@@ -664,6 +695,7 @@ mkContentTypesEntry epochtime imgs headers footers refArchive =
,("/word/footnotes.xml",
"application/vnd.openxmlformats-officedocument.wordprocessingml.footnotes+xml")
] ++
+ endnotesOverride ++
map (\x -> (maybe "" (T.unpack . ("/word/" <>)) (extractTarget x),
"application/vnd.openxmlformats-officedocument.wordprocessingml.header+xml")) headers ++
map (\x -> (maybe "" (T.unpack . ("/word/" <>)) (extractTarget x),
@@ -723,6 +755,7 @@ mkStylesEntry epochtime styledoc styleMaps st opts =
(\sty -> not $ hasStyleName sty $ smCharStyle styleMaps)
(Set.toList $ stDynamicTextProps st)
+ -- TODO: add styles for endnotes, when Ext_endnotes is enabled
newstyles = map newParaPropToOpenXml newDynamicParaProps ++
map newTextPropToOpenXml newDynamicTextProps ++
(case writerHighlightMethod opts of
@@ -836,7 +869,8 @@ collectReferenceEntries refArchive distArchive headers footers = do
, "word/_rels/" `isPrefixOf` eRelativePath e
, ".xml.rels" `isSuffixOf` eRelativePath e
, eRelativePath e /= "word/_rels/document.xml.rels"
- , eRelativePath e /= "word/_rels/footnotes.xml.rels" ]
+ , eRelativePath e /= "word/_rels/footnotes.xml.rels"
+ , eRelativePath e /= "word/_rels/endnotes.xml.rels" ]
let otherMediaEntries = [ e | e <- zEntries refArchive
, "word/media/" `isPrefixOf` eRelativePath e ]
return $ docPropsAppEntry : themeEntry : fontTableEntry : webSettingsEntry
diff --git a/src/Text/Pandoc/Writers/Docx/OpenXML.hs b/src/Text/Pandoc/Writers/Docx/OpenXML.hs
index 5490a732de52..1fc28e7ea32b 100644
--- a/src/Text/Pandoc/Writers/Docx/OpenXML.hs
+++ b/src/Text/Pandoc/Writers/Docx/OpenXML.hs
@@ -229,11 +229,11 @@ sectionSeparator = do
Nothing -> pure
Nothing
--- | Convert Pandoc document to rendered document contents plus two lists of
--- OpenXML elements (footnotes and comments).
+-- | Convert Pandoc document to rendered document contents plus three lists of
+-- OpenXML elements (footnotes, endnotes and comments).
writeOpenXML :: PandocMonad m
=> WriterOptions -> Pandoc
- -> WS m (Text, [Element], [Element])
+ -> WS m (Text, [Element], [Element], [Element])
writeOpenXML opts (Pandoc meta blocks) = do
setupTranslations meta
let includeTOC = writerTableOfContents opts || lookupMetaBool "toc" meta
@@ -264,6 +264,7 @@ writeOpenXML opts (Pandoc meta blocks) = do
doc' <- setFirstPara >> blocksToOpenXML opts blocks
let body = vcat $ map (literal . showContent) doc'
notes' <- gets (reverse . stFootnotes)
+ endnotes' <- gets (reverse . stEndnotes)
comments <- gets (reverse . stComments)
let toComment (kvs, ils) = do
annotation <- inlinesToOpenXML opts ils
@@ -322,7 +323,7 @@ writeOpenXML opts (Pandoc meta blocks) = do
$ metadata
tpl <- maybe (lift $ compileDefaultTemplate "openxml") pure $ writerTemplate opts
let rendered = render Nothing $ renderTemplate tpl context
- return (rendered, notes', comments')
+ return (rendered, notes', endnotes', comments')
-- | Convert a list of Pandoc blocks to OpenXML.
blocksToOpenXML :: (PandocMonad m) => WriterOptions -> [Block] -> WS m [Content]
@@ -755,6 +756,13 @@ inlineToOpenXML' opts SoftBreak = inlineToOpenXML opts (Str " ")
inlineToOpenXML' opts (Span ("",["mark"],[]) ils) =
withTextProp (mknode "w:highlight" [("w:val","yellow")] ()) $
inlinesToOpenXML opts ils
+inlineToOpenXML' opts (Span (_,["endnote"],_) ils) = if isEnabled Ext_endnotes opts
+ then (do
+ modify $ \s -> s { stInEndnote = isEnabled Ext_endnotes opts }
+ endnote <- inlinesToOpenXML opts ils
+ modify $ \s -> s { stInEndnote = False }
+ return endnote)
+ else inlinesToOpenXML opts ils
inlineToOpenXML' opts (Span ("",["csl-block"],[]) ils) =
inlinesToOpenXML opts ils
inlineToOpenXML' opts (Span ("",["csl-left-margin"],[]) ils) =
@@ -841,11 +849,13 @@ inlineToOpenXML' opts (Span (ident,classes,kvs) ils) = do
langmod $ inlinesToOpenXML opts ils
wrapBookmark ident contents
inlineToOpenXML' opts (Strong lst) =
- withTextProp (mknode "w:bCs" [] ()) $ -- needed for LTR, #6911
+ withTextProp (mknode "w:bCs" [] ()) $ -- needed for LTR, #6911 -- needed for LTR, #6911
+ -- needed for LTR, #6911
withTextProp (mknode "w:b" [] ()) $
inlinesToOpenXML opts lst
inlineToOpenXML' opts (Emph lst) =
- withTextProp (mknode "w:iCs" [] ()) $ -- needed for LTR, #6911
+ withTextProp (mknode "w:iCs" [] ()) $ -- needed for LTR, #6911 -- needed for LTR, #6911
+ -- needed for LTR, #6911
withTextProp (mknode "w:i" [] ()) $
inlinesToOpenXML opts lst
inlineToOpenXML' opts (Underline lst) =
@@ -905,28 +915,39 @@ inlineToOpenXML' opts (Code attrs str) = do
Skylighting _ -> highlighted
_ -> unhighlighted
inlineToOpenXML' opts (Note bs) = do
- notes <- gets stFootnotes
+ isEndnote <- gets stInEndnote
+ notes <- gets $ if isEndnote then stEndnotes else stFootnotes
notenum <- getUniqueId
- footnoteStyle <- rStyleM "Footnote Reference"
+ footnoteStyle <- rStyleM $ if isEndnote
+ then "Endnote Reference"
+ else "Footnote Reference"
+ let noteRefNodeName = if isEndnote then "w:endnoteRef" else "w:footnoteRef"
let notemarker = mknode "w:r" []
[ mknode "w:rPr" [] footnoteStyle
- , mknode "w:footnoteRef" [] () ]
+ , mknode noteRefNodeName [] () ]
let notemarkerXml = RawInline (Format "openxml") $ ppElement notemarker
let insertNoteRef (Plain ils : xs) = Plain (notemarkerXml : Space : ils) : xs
insertNoteRef (Para ils : xs) = Para (notemarkerXml : Space : ils) : xs
insertNoteRef xs = Para [notemarkerXml] : xs
+ let noteTextStyleName = if isEndnote then "Endnote Text" else "Footnote Text"
contents <- local (\env -> env{ envListLevel = -1
, envParaProperties = mempty
, envTextProperties = mempty
, envInNote = True })
- (withParaPropM (pStyleM "Footnote Text") $
+ (withParaPropM (pStyleM noteTextStyleName) $
blocksToOpenXML opts $ insertNoteRef bs)
- let newnote = mknode "w:footnote" [("w:id", notenum)] contents
- modify $ \s -> s{ stFootnotes = newnote : notes }
+ let noteNodeName = if isEndnote then "w:endnote" else "w:footnote"
+ let newnote = mknode noteNodeName [("w:id", notenum)] contents
+ modify $ \s -> if isEndnote
+ then s{ stEndnotes = newnote : notes }
+ else s{ stFootnotes = newnote : notes }
+ let noteReferenceNodeName = if isEndnote
+ then "w:endnoteReference"
+ else "w:footnoteReference"
return [ Elem $ mknode "w:r" []
[ mknode "w:rPr" [] footnoteStyle
- , mknode "w:footnoteReference" [("w:id", notenum)] () ] ]
+ , mknode noteReferenceNodeName [("w:id", notenum)] () ] ]
-- internal link:
inlineToOpenXML' opts (Link _ txt (T.uncons -> Just ('#', xs),_)) = do
contents <- withTextPropM (rStyleM "Hyperlink") $ inlinesToOpenXML opts txt
diff --git a/src/Text/Pandoc/Writers/Docx/Types.hs b/src/Text/Pandoc/Writers/Docx/Types.hs
index febef6603dd3..d985d98a16a0 100644
--- a/src/Text/Pandoc/Writers/Docx/Types.hs
+++ b/src/Text/Pandoc/Writers/Docx/Types.hs
@@ -111,6 +111,7 @@ defaultWriterEnv = WriterEnv
data WriterState = WriterState{
stFootnotes :: [Element]
+ , stEndnotes :: [Element]
, stComments :: [([(Text, Text)], [Inline])]
, stSectionIds :: Set.Set Text
, stExternalLinks :: M.Map Text Text
@@ -126,6 +127,7 @@ data WriterState = WriterState{
-- Should only be used once, for the first paragraph.
, stInTable :: Bool
, stInList :: Bool
+ , stInEndnote :: Bool
, stTocTitle :: [Inline]
, stDynamicParaProps :: Set.Set ParaStyleName
, stDynamicTextProps :: Set.Set CharStyleName
@@ -137,6 +139,7 @@ data WriterState = WriterState{
defaultWriterState :: WriterState
defaultWriterState = WriterState{
stFootnotes = defaultFootnotes
+ , stEndnotes = defaultEndnotes
, stComments = []
, stSectionIds = Set.empty
, stExternalLinks = M.empty
@@ -151,6 +154,7 @@ defaultWriterState = WriterState{
, stNumIdUsed = False
, stInTable = False
, stInList = False
+ , stInEndnote = False
, stTocTitle = [Str "Table of Contents"]
, stDynamicParaProps = Set.empty
, stDynamicTextProps = Set.empty
@@ -180,6 +184,21 @@ defaultFootnotes = [ mknode "w:footnote"
[ mknode "w:r" []
[ mknode "w:continuationSeparator" [] ()]]]]
+-- TODO: verify whether Word behaves the same with endnotes as it does with footnotes.
+-- For now, let's do the same as for footnotes
+defaultEndnotes :: [Element]
+-- defaultEndnotes = []
+defaultEndnotes = [ mknode "w:endnote"
+ [("w:type", "separator"), ("w:id", "-1")]
+ [ mknode "w:p" []
+ [mknode "w:r" []
+ [ mknode "w:separator" [] ()]]]
+ , mknode "w:endnote"
+ [("w:type", "continuationSeparator"), ("w:id", "0")]
+ [ mknode "w:p" []
+ [ mknode "w:r" []
+ [ mknode "w:continuationSeparator" [] ()]]]]
+
pStyleM :: (PandocMonad m) => ParaStyleName -> WS m XML.Element
pStyleM styleName = do
pStyleMap <- gets (smParaStyle . stStyleMaps)
diff --git a/src/Text/Pandoc/Writers/OpenDocument.hs b/src/Text/Pandoc/Writers/OpenDocument.hs
index 0d49d72e10a4..79feab6e8cdf 100644
--- a/src/Text/Pandoc/Writers/OpenDocument.hs
+++ b/src/Text/Pandoc/Writers/OpenDocument.hs
@@ -64,6 +64,7 @@ data ReferenceType
data WriterState =
WriterState { stNotes :: [Doc Text]
+ , stEndnotes :: [Doc Text]
, stTableStyles :: [Doc Text]
, stParaStyles :: [Doc Text]
, stListStyles :: [(Int, [Doc Text])]
@@ -72,6 +73,7 @@ data WriterState =
, stTextStyleAttr :: Set.Set TextStyle
, stIndentPara :: Int
, stInDefinition :: Bool
+ , stInEndnote :: Bool
, stTight :: Bool
, stFirstPara :: Bool
, stImageId :: Int
@@ -83,6 +85,7 @@ data WriterState =
defaultWriterState :: WriterState
defaultWriterState =
WriterState { stNotes = []
+ , stEndnotes = []
, stTableStyles = []
, stParaStyles = []
, stListStyles = []
@@ -90,6 +93,7 @@ defaultWriterState =
, stTextStyleAttr = Set.empty
, stIndentPara = 0
, stInDefinition = False
+ , stInEndnote = False
, stTight = False
, stFirstPara = False
, stImageId = 1
@@ -636,6 +640,11 @@ inlineToOpenDocument o ils
Span ("", ["mark"], []) xs ->
inTags False "text:span" [("text:style-name","Highlighted")] <$>
inlinesToOpenDocument o xs
+ Span (_, ["endnote"], _) xs | isEnabled Ext_endnotes o -> do
+ modify (\st -> st{ stInEndnote = True })
+ s <- inlinesToOpenDocument o xs
+ modify (\st -> st{ stInEndnote = False })
+ return s
Span attr xs -> mkSpan attr xs
LineBreak -> return $ selfClosingTag "text:line-break" []
Str s -> return $ handleSpaces $ escapeStringForXML s
@@ -701,15 +710,21 @@ inlineToOpenDocument o ils
if T.null ident
then i
else fmap mkBookmarkedSpan i
- mkNote l = do
- n <- length <$> gets stNotes
- let footNote t = inTags False "text:note"
- [ ("text:id" , "ftn" <> tshow n)
- , ("text:note-class", "footnote" )] $
+ mkNote l = do
+ inEndnote <- gets stInEndnote
+ nFootnote <- length <$> gets stNotes
+ nEndnote <- length <$> gets stEndnotes
+ let n = if inEndnote then nEndnote else nFootnote
+ idn = nFootnote + nEndnote
+ noteClass = if inEndnote then "endnote" else "footnote"
+ pStyleName = if inEndnote then "Endnote" else "Footnote"
+ footNote t = inTags False "text:note"
+ [ ("text:id" , "ftn" <> tshow idn)
+ , ("text:note-class", noteClass )] $
inTagsSimple "text:note-citation" (text . show $ n + 1) <>
inTagsSimple "text:note-body" t
nn <- footNote <$> withAlteredTextStyles (const mempty)
- (withParagraphStyle o "Footnote" l)
+ (withParagraphStyle o pStyleName l)
addNote nn
return nn
diff --git a/test/command/11501.md b/test/command/11501.md
new file mode 100644
index 000000000000..7ac324c72651
--- /dev/null
+++ b/test/command/11501.md
@@ -0,0 +1,26 @@
+```
+% pandoc -f markdown -t docx+endnotes -o - | pandoc -f docx+endnotes -t markdown
+First paragraph with an endnote[[^e1]]{.endnote} and a footnote[^1].
+
+Second paragraph with a footnote[^2] and an endnote [[^e2]]{.endnote}.
+
+[^1]: First footnote.
+
+[^2]: Second footnote.
+
+[^e1]: First endnote.
+
+[^e2]: Second endnote.
+^D
+First paragraph with an endnote[[^1]]{.endnote} and a footnote[^2].
+
+Second paragraph with a footnote[^3] and an endnote [[^4]]{.endnote}.
+
+[^1]: First endnote.
+
+[^2]: First footnote.
+
+[^3]: Second footnote.
+
+[^4]: Second endnote.
+```
\ No newline at end of file
diff --git a/test/command/11516.md b/test/command/11516.md
new file mode 100644
index 000000000000..844d15fc12ed
--- /dev/null
+++ b/test/command/11516.md
@@ -0,0 +1,32 @@
+```
+% pandoc -f markdown -t odt+endnotes -o - | pandoc -f odt+endnotes -t markdown
+First paragraph with an endnote[[^e1]]{.endnote} and a footnote[^1].
+
+Second paragraph with a footnote[^2] and an endnote [[^e2]]{.endnote}.
+
+[^1]: First footnote.
+
+[^2]: Second footnote.
+
+[^e1]: First endnote.
+
+[^e2]: Second endnote.
+^D
+First paragraph with an endnote[[^1]]{.endnote} and a footnote[^2].
+
+Second paragraph with a footnote[^3] and an endnote [[^4]]{.endnote}.
+
+[^1]: First endnote.
+
+[^2]: First footnote.
+
+[^3]: Second footnote.
+
+[^4]: Second endnote.
+```
+```
+% pandoc -f markdown -t odt+endnotes -o - | pandoc -f odt+endnotes -t markdown --wrap=none
+A paragraph with a [span]{.endnote} with an endnote class.
+^D
+A paragraph with a span with an endnote class.
+```