git.iko.soy/typeable/pandoc
on master help sign in
Merge pull request #7042 from tarleb/jats-element-citations

JATS writer: use element citations

commit83d7804b8fc04eb44c8c158f84fe37373b4eb5d9 authorJohn MacFarlane <jgm@berkeley.edu> dateFri 22 Jan 2021, 10:39 (6 years ago) parentfa952c8b4b3560
8 files changed+367−25
MANUAL.txt+7 pandoc.cabal+1 src/Text/Pandoc/Citeproc.hs+21−18 src/Text/Pandoc/Extensions.hs+14 src/Text/Pandoc/Writers/JATS.hs+15−6 src/Text/Pandoc/Writers/JATS/References.hs+160 src/Text/Pandoc/Writers/JATS/Types.hs+3−1 test/command/7042.md+146
MANUAL.txt+7
@@ -3095,6 +3095,13 @@ output format.
30953095Some aspects of [Pandoc's Markdown citation syntax](#citations)
30963096are also accepted in `org` input.
30973097
3098+#### Extension: `element_citations` ####
3099+
3100+In the `jats` output formats, this causes reference items to
3101+be replaced with `<element-citation>` elements. These
3102+elements are not influenced by CSL styles, but all information
3103+on the item is included in tags.
3104+
30983105#### Extension: `ntb` ####
30993106
31003107In the `context` output format this enables the use of [Natural Tables
pandoc.cabal+1
@@ -639,6 +639,7 @@ library
639639 Text.Pandoc.Readers.Metadata,
640640 Text.Pandoc.Readers.Roff,
641641 Text.Pandoc.Writers.Docx.StyleMap,
642+ Text.Pandoc.Writers.JATS.References,
642643 Text.Pandoc.Writers.JATS.Table,
643644 Text.Pandoc.Writers.JATS.Types,
644645 Text.Pandoc.Writers.LaTeX.Caption,
src/Text/Pandoc/Citeproc.hs+21−18
@@ -18,33 +18,36 @@ import Text.Pandoc.Citeproc.CslJson (cslJsonToReferences)
1818import Text.Pandoc.Citeproc.BibTeX (readBibtexString, Variant(..))
1919import Text.Pandoc.Citeproc.MetaValue (metaValueToReference, metaValueToText)
2020import Text.Pandoc.Readers.Markdown (yamlToRefs)
21−import Text.Pandoc.Class (setResourcePath, getResourcePath, getUserDataDir)
2221import qualified Text.Pandoc.BCP47 as BCP47
23−import Data.ByteString (ByteString)
24−import qualified Data.ByteString.Lazy as L
22+import Text.Pandoc.Builder (Inlines, Many(..), deleteMeta, setMeta)
23+import qualified Text.Pandoc.Builder as B
2524import Text.Pandoc.Definition as Pandoc
26−import Text.Pandoc.Walk
27−import Text.Pandoc.Builder as B
28−import Text.Pandoc (PandocMonad(..), PandocError(..),
29− readDataFile, ReaderOptions(..), pandocExtensions,
30− report, LogMessage(..), fetchItem)
25+import Text.Pandoc.Class (PandocMonad(..), getResourcePath, getUserDataDir,
26+ fetchItem, readDataFile, report, setResourcePath)
27+import Text.Pandoc.Error (PandocError(..))
28+import Text.Pandoc.Extensions (pandocExtensions)
29+import Text.Pandoc.Logging (LogMessage(..))
30+import Text.Pandoc.Options (ReaderOptions(..))
3131import Text.Pandoc.Shared (stringify, ordNub, blocksToInlines, tshow)
3232import qualified Text.Pandoc.UTF8 as UTF8
33+import Text.Pandoc.Walk (query, walk, walkM)
34+import Control.Applicative ((<|>))
35+import Control.Monad.Except (catchError, throwError)
36+import Control.Monad.State (State, evalState, get, put, runState)
3337import Data.Aeson (eitherDecode)
34−import Data.Default
35−import Data.Ord ()
38+import Data.ByteString (ByteString)
39+import qualified Data.ByteString.Lazy as L
40+import Data.Char (isPunctuation, isUpper)
41+import Data.Default (Default(def))
42+import qualified Data.Foldable as Foldable
3643import qualified Data.Map as M
44+import Data.Maybe (mapMaybe, fromMaybe)
45+import Data.Ord ()
46+import qualified Data.Sequence as Seq
3747import qualified Data.Set as Set
38−import Data.Char (isPunctuation, isUpper)
3948import Data.Text (Text)
4049import qualified Data.Text as T
41−import Control.Monad.State
42−import qualified Data.Sequence as Seq
43−import qualified Data.Foldable as Foldable
44−import System.FilePath
45−import Control.Applicative
46−import Control.Monad.Except
47−import Data.Maybe (mapMaybe, fromMaybe)
50+import System.FilePath (takeExtension)
4851import Safe (lastMay, initSafe)
4952-- import Debug.Trace as Trace (trace, traceShowId)
5053
src/Text/Pandoc/Extensions.hs+14
@@ -88,6 +88,7 @@ data Extension =
8888 -- does not affect readers/writers directly; it causes
8989 -- the eastAsianLineBreakFilter to be applied after
9090 -- parsing, in Text.Pandoc.App.convertWithOpts.
91+ | Ext_element_citations -- ^ Use element-citation elements for JATS citations
9192 | Ext_emoji -- ^ Support emoji like :smile:
9293 | Ext_empty_paragraphs -- ^ Allow empty paragraphs
9394 | Ext_epub_html_exts -- ^ Recognise the EPUB extended version of HTML
@@ -412,6 +413,11 @@ getDefaultExtensions "textile" = extensionsFromList
412413 Ext_smart,
413414 Ext_raw_html,
414415 Ext_auto_identifiers]
416+getDefaultExtensions "jats" = extensionsFromList
417+ [Ext_auto_identifiers]
418+getDefaultExtensions "jats_archiving" = getDefaultExtensions "jats"
419+getDefaultExtensions "jats_publishing" = getDefaultExtensions "jats"
420+getDefaultExtensions "jats_articleauthoring" = getDefaultExtensions "jats"
415421getDefaultExtensions "opml" = pandocExtensions -- affects notes
416422getDefaultExtensions _ = extensionsFromList
417423 [Ext_auto_identifiers]
@@ -554,6 +560,14 @@ getAllExtensions f = universalExtensions <> getAll f
554560 , Ext_smart
555561 , Ext_raw_tex
556562 ]
563+ getAll "jats" =
564+ extensionsFromList
565+ [ Ext_auto_identifiers
566+ , Ext_element_citations
567+ ]
568+ getAll "jats_archiving" = getAll "jats"
569+ getAll "jats_publishing" = getAll "jats"
570+ getAll "jats_articleauthoring" = getAll "jats"
557571 getAll "opml" = allMarkdownExtensions -- affects notes
558572 getAll "twiki" = autoIdExtensions <>
559573 extensionsFromList
src/Text/Pandoc/Writers/JATS.hs+15−6
@@ -29,6 +29,7 @@ import Data.Maybe (fromMaybe)
2929import Data.Time (toGregorian, Day, parseTimeM, defaultTimeLocale, formatTime)
3030import qualified Data.Text as T
3131import Data.Text (Text)
32+import Text.Pandoc.Citeproc (getReferences)
3233import Text.Pandoc.Class.PandocMonad (PandocMonad, report)
3334import Text.Pandoc.Definition
3435import Text.Pandoc.Highlighting (languages, languagesByExtension)
@@ -40,6 +41,7 @@ import Text.DocLayout
4041import Text.Pandoc.Shared
4142import Text.Pandoc.Templates (renderTemplate)
4243import Text.DocTemplates (Context(..), Val(..))
44+import Text.Pandoc.Writers.JATS.References (referencesToJATS)
4345import Text.Pandoc.Writers.JATS.Table (tableToJATS)
4446import Text.Pandoc.Writers.JATS.Types
4547import Text.Pandoc.Writers.Math
@@ -71,15 +73,19 @@ writeJATS = writeJatsArchiving
7173
7274-- | Convert a @'Pandoc'@ document to JATS.
7375writeJats :: PandocMonad m => JATSTagSet -> WriterOptions -> Pandoc -> m Text
74−writeJats tagSet opts d =
75− runReaderT (evalStateT (docToJATS opts d) initialState)
76− environment
77− where initialState = JATSState { jatsNotes = [] }
78− environment = JATSEnv
76+writeJats tagSet opts d = do
77+ refs <- if extensionEnabled Ext_element_citations $ writerExtensions opts
78+ then getReferences Nothing d
79+ else pure []
80+ let environment = JATSEnv
7981 { jatsTagSet = tagSet
8082 , jatsInlinesWriter = inlinesToJATS
8183 , jatsBlockWriter = blockToJATS
84+ , jatsReferences = refs
8285 }
86+ let initialState = JATSState { jatsNotes = [] }
87+ runReaderT (evalStateT (docToJATS opts d) initialState)
88+ environment
8389
8490-- | Convert Pandoc document to string in JATS format.
8591docToJATS :: PandocMonad m => WriterOptions -> Pandoc -> JATS m Text
@@ -258,7 +264,10 @@ blockToJATS opts (Div (ident,_,_) [Para lst]) | "ref-" `T.isPrefixOf` ident =
258264 inTagsSimple "mixed-citation" <$>
259265 inlinesToJATS opts lst
260266blockToJATS opts (Div ("refs",_,_) xs) = do
261− contents <- blocksToJATS opts xs
267+ refs <- asks jatsReferences
268+ contents <- if null refs
269+ then blocksToJATS opts xs
270+ else referencesToJATS opts refs
262271 return $ inTagsIndented "ref-list" contents
263272blockToJATS opts (Div (ident,[cls],kvs) bs) | cls `elem` ["fig", "caption", "table-wrap"] = do
264273 contents <- blocksToJATS opts bs
src/Text/Pandoc/Writers/JATS/References.hsnew file+160
@@ -0,0 +1,160 @@
1+{-# LANGUAGE LambdaCase #-}
2+{-# LANGUAGE OverloadedStrings #-}
3+{- |
4+ Module : Text.Pandoc.Writers.JATS.References
5+ Copyright : © 2021 Albert Krewinkel
6+ License : GNU GPL, version 2 or above
7+
8+ Maintainer : Albert Krewinkel <tarleb@zeitkraut.de>
9+ Stability : alpha
10+ Portability : portable
11+
12+Creation of a bibliography list using @<element-citation>@ elements in
13+reference items.
14+-}
15+module Text.Pandoc.Writers.JATS.References
16+ ( referencesToJATS
17+ , referenceToJATS
18+ ) where
19+
20+import Citeproc.Pandoc ()
21+import Citeproc.Types
22+ ( Date (..), DateParts (..), ItemId (..), Name (..), Reference (..)
23+ , Val (..) , lookupVariable, valToText
24+ )
25+import Data.Text (Text)
26+import Text.DocLayout (Doc, empty, isEmpty, literal, vcat)
27+import Text.Pandoc.Class.PandocMonad (PandocMonad)
28+import Text.Pandoc.Builder (Inlines)
29+import Text.Pandoc.Options (WriterOptions)
30+import Text.Pandoc.Shared (tshow)
31+import Text.Pandoc.Writers.JATS.Types
32+import Text.Pandoc.XML (inTags)
33+import qualified Data.Text as T
34+
35+referencesToJATS :: PandocMonad m
36+ => WriterOptions
37+ -> [Reference Inlines]
38+ -> JATS m (Doc Text)
39+referencesToJATS opts =
40+ fmap (inTags True "ref-list" [] . vcat) . mapM (referenceToJATS opts)
41+
42+referenceToJATS :: PandocMonad m
43+ => WriterOptions
44+ -> Reference Inlines
45+ -> JATS m (Doc Text)
46+referenceToJATS _opts ref = do
47+ let refType = referenceType ref
48+ let pubType = [("publication-type", refType) | not (T.null refType)]
49+ let wrap = inTags True "ref" [("id", "ref-" <> unItemId (referenceId ref))]
50+ . inTags True "element-citation" pubType
51+ return . wrap . vcat $
52+ [ authors
53+ , "title" `varInTag`
54+ if refType == "book"
55+ then "source"
56+ else "article-title"
57+ , if refType == "book"
58+ then empty
59+ else "container-title" `varInTag` "source"
60+ , editors
61+ , "publisher" `varInTag` "publisher-name"
62+ , "publisher-place" `varInTag` "publisher-loc"
63+ , yearTag
64+ , accessed
65+ , "volume" `varInTag` "volume"
66+ , "issue" `varInTag` "issue"
67+ , "page-first" `varInTag` "fpage"
68+ , "page-last" `varInTag` "lpage"
69+ , "pages" `varInTag` "page-range"
70+ , "ISBN" `varInTag` "isbn"
71+ , "ISSN" `varInTag` "issn"
72+ , varInTagWith "doi" "pub-id" [("pub-id-type", "doi")]
73+ , varInTagWith "pmid" "pub-id" [("pub-id-type", "pmid")]
74+ ]
75+ where
76+ varInTag var tagName = varInTagWith var tagName []
77+
78+ varInTagWith var tagName tagAttribs =
79+ case lookupVariable var ref >>= valToText of
80+ Nothing -> mempty
81+ Just val -> inTags' tagName tagAttribs $ literal val
82+
83+ authors = case lookupVariable "author" ref of
84+ Just (NamesVal names) ->
85+ inTags True "person-group" [("person-group-type", "author")] . vcat $
86+ map toNameElements names
87+ _ -> empty
88+
89+ editors = case lookupVariable "editor" ref of
90+ Just (NamesVal names) ->
91+ inTags True "person-group" [("person-group-type", "editor")] . vcat $
92+ map toNameElements names
93+ _ -> empty
94+
95+ yearTag =
96+ case lookupVariable "issued" ref of
97+ Just (DateVal date) -> toDateElements date
98+ _ -> empty
99+
100+ accessed =
101+ case lookupVariable "accessed" ref of
102+ Just (DateVal d) -> inTags' "date-in-citation"
103+ [("content-type", "access-date")]
104+ (toDateElements d)
105+ _ -> empty
106+
107+toDateElements :: Date -> Doc Text
108+toDateElements date =
109+ case dateParts date of
110+ dp@(DateParts (y:m:d:_)):_ -> yearElement y dp <>
111+ monthElement m <>
112+ dayElement d
113+ dp@(DateParts (y:m:_)):_ -> yearElement y dp <> monthElement m
114+ dp@(DateParts (y:_)):_ -> yearElement y dp
115+ _ -> empty
116+
117+yearElement :: Int -> DateParts -> Doc Text
118+yearElement year dp =
119+ inTags' "year" [("iso-8601-date", iso8601 dp)] $ literal (fourDigits year)
120+
121+monthElement :: Int -> Doc Text
122+monthElement month = inTags' "month" [] . literal $ twoDigits month
123+
124+dayElement :: Int -> Doc Text
125+dayElement day = inTags' "day" [] . literal $ twoDigits day
126+
127+iso8601 :: DateParts -> Text
128+iso8601 = T.intercalate "-" . \case
129+ DateParts (y:m:d:_) -> [fourDigits y, twoDigits m, twoDigits d]
130+ DateParts (y:m:_) -> [fourDigits y, twoDigits m]
131+ DateParts (y:_) -> [fourDigits y]
132+ _ -> []
133+
134+twoDigits :: Int -> Text
135+twoDigits n = T.takeEnd 2 $ '0' `T.cons` tshow n
136+
137+fourDigits :: Int -> Text
138+fourDigits n = T.takeEnd 4 $ "000" <> tshow n
139+
140+toNameElements :: Name -> Doc Text
141+toNameElements name =
142+ if not (isEmpty nameTags)
143+ then inTags' "name" [] nameTags
144+ else nameLiteral name `inNameTag` "string-name"
145+ where
146+ inNameTag val tag = maybe empty (inTags' tag [] . literal) val
147+ surnamePrefix = maybe mempty (`T.snoc` ' ') $
148+ nameNonDroppingParticle name
149+ givenSuffix = maybe mempty (T.cons ' ') $
150+ nameDroppingParticle name
151+ nameTags = mconcat
152+ [ ((surnamePrefix <>) <$> nameFamily name) `inNameTag` "surname"
153+ , ((<> givenSuffix) <$> nameGiven name) `inNameTag` "given-names"
154+ , nameSuffix name `inNameTag` "suffix"
155+ ]
156+
157+-- | Put the supplied contents between start and end tags of tagType,
158+-- with specified attributes.
159+inTags' :: Text -> [(Text, Text)] -> Doc Text -> Doc Text
160+inTags' = inTags False
src/Text/Pandoc/Writers/JATS/Types.hs+3−1
@@ -17,11 +17,12 @@ module Text.Pandoc.Writers.JATS.Types
1717 )
1818where
1919
20+import Citeproc.Types (Reference)
2021import Control.Monad.Reader (ReaderT)
2122import Control.Monad.State (StateT)
2223import Data.Text (Text)
2324import Text.DocLayout (Doc)
24−import Text.Pandoc.Definition (Block, Inline)
25+import Text.Pandoc.Builder (Block, Inline, Inlines)
2526import Text.Pandoc.Options (WriterOptions)
2627
2728-- | JATS tag set variant
@@ -40,6 +41,7 @@ data JATSEnv m = JATSEnv
4041 { jatsTagSet :: JATSTagSet
4142 , jatsInlinesWriter :: WriterOptions -> [Inline] -> JATS m (Doc Text)
4243 , jatsBlockWriter :: WriterOptions -> Block -> JATS m (Doc Text)
44+ , jatsReferences :: [Reference Inlines]
4345 }
4446
4547-- | JATS writer type
test/command/7042.mdnew file+146
@@ -0,0 +1,146 @@
1+```
2+% pandoc -f markdown -t jats_publishing+element_citations --citeproc -s
3+---
4+nocite: "[@*]"
5+references:
6+- author:
7+ - family: Jane
8+ given: Doe
9+ container-title: Public Library of Tests
10+ id: year-month
11+ issued: 1999-08
12+ title: Year and month
13+ type: article-journal
14+- accessed: 1999-01-22
15+ author:
16+ - family: Negidius
17+ given: Numerius
18+ container-title: Public Library of Tests
19+ id: access-date
20+ issued: 1911-10-03
21+ title: Entry with access date
22+ type: article-journal
23+- author:
24+ - family: Beethoven
25+ given: Ludwig
26+ dropping-particle: van
27+ - family: Bray
28+ given: Jan
29+ non-dropping-particle: de
30+ container-title: Public Library of Tests
31+ id: name-particles
32+ issued: 1820
33+ title: Name particles, dropping and non-dropping
34+ type: article-journal
35+- author:
36+ - 宮水 三葉
37+ - 立花 瀧
38+ title: Big Book of Tests
39+ id: book-with-japanese-authors
40+ issued: 2016
41+ type: book
42+- author:
43+ - family: Watson
44+ given: J. D.
45+ - family: Crick
46+ given: F. H. C.
47+ container-title: Nature
48+ doi: '10.1038/171737a0'
49+ id: full-journal-article-entry
50+ issue: 4356
51+ issued: '1953-04-01'
52+ pages: 737-738
53+ pmid: 13054692
54+ title: 'Molecular Structure of Nucleic Acids: A Structure for Deoxyribose Nucleic Acid'
55+ type: article-journal
56+ volume: 171
57+...
58+^D
59+<?xml version="1.0" encoding="utf-8" ?>
60+<!DOCTYPE article PUBLIC "-//NLM//DTD JATS (Z39.96) Journal Publishing DTD v1.2 20190208//EN"
61+ "JATS-publishing1.dtd">
62+<article xmlns:mml="http://www.w3.org/1998/Math/MathML" xmlns:xlink="http://www.w3.org/1999/xlink" dtd-version="1.2" article-type="other">
63+<front>
64+<journal-meta>
65+<journal-id></journal-id>
66+<journal-title-group>
67+</journal-title-group>
68+<issn></issn>
69+<publisher>
70+<publisher-name></publisher-name>
71+</publisher>
72+</journal-meta>
73+<article-meta>
74+</article-meta>
75+</front>
76+<body>
77+
78+</body>
79+<back>
80+<ref-list>
81+ <ref-list>
82+ <ref id="ref-year-month">
83+ <element-citation publication-type="article-journal">
84+ <person-group person-group-type="author">
85+ <name><surname>Jane</surname><given-names>Doe</given-names></name>
86+ </person-group>
87+ <article-title>Year and month</article-title>
88+ <source>Public Library of Tests</source>
89+ <year iso-8601-date="1999-08">1999</year><month>08</month>
90+ </element-citation>
91+ </ref>
92+ <ref id="ref-access-date">
93+ <element-citation publication-type="article-journal">
94+ <person-group person-group-type="author">
95+ <name><surname>Negidius</surname><given-names>Numerius</given-names></name>
96+ </person-group>
97+ <article-title>Entry with access date</article-title>
98+ <source>Public Library of Tests</source>
99+ <year iso-8601-date="1911-10-03">1911</year><month>10</month><day>03</day>
100+ <date-in-citation content-type="access-date"><year iso-8601-date="1999-01-22">1999</year><month>01</month><day>22</day></date-in-citation>
101+ </element-citation>
102+ </ref>
103+ <ref id="ref-name-particles">
104+ <element-citation publication-type="article-journal">
105+ <person-group person-group-type="author">
106+ <name><surname>Beethoven</surname><given-names>Ludwig van</given-names></name>
107+ <name><surname>de Bray</surname><given-names>Jan</given-names></name>
108+ </person-group>
109+ <article-title>Name particles, dropping and non-dropping</article-title>
110+ <source>Public Library of Tests</source>
111+ <year iso-8601-date="1820">1820</year>
112+ </element-citation>
113+ </ref>
114+ <ref id="ref-book-with-japanese-authors">
115+ <element-citation publication-type="book">
116+ <person-group person-group-type="author">
117+ <string-name>宮水 三葉</string-name>
118+ <string-name>立花 瀧</string-name>
119+ </person-group>
120+ <source>Big Book of Tests</source>
121+ <year iso-8601-date="2016">2016</year>
122+ </element-citation>
123+ </ref>
124+ <ref id="ref-full-journal-article-entry">
125+ <element-citation publication-type="article-journal">
126+ <person-group person-group-type="author">
127+ <name><surname>Watson</surname><given-names>J. D.</given-names></name>
128+ <name><surname>Crick</surname><given-names>F. H. C.</given-names></name>
129+ </person-group>
130+ <article-title>Molecular Structure of Nucleic Acids: A Structure for Deoxyribose Nucleic Acid</article-title>
131+ <source>Nature</source>
132+ <year iso-8601-date="1953-04-01">1953</year><month>04</month><day>01</day>
133+ <volume>171</volume>
134+ <issue>4356</issue>
135+ <fpage>737</fpage>
136+ <page-range>737-738</page-range>
137+ <pub-id pub-id-type="doi">10.1038/171737a0</pub-id>
138+ <pub-id pub-id-type="pmid">13054692</pub-id>
139+ </element-citation>
140+ </ref>
141+ </ref-list>
142+</ref-list>
143+</back>
144+</article>
145+
146+```