From 353177f9e7eadd60d66846325abd537b85fd46bb Mon Sep 17 00:00:00 2001 From: John MacFarlane Date: Sun, 27 Aug 2023 10:54:59 -0700 Subject: [PATCH] Use `\cite` and `\bibitem` to link up citations, even with citeproc. See #9031 and discussion in #9020. This will give us better accessibility; when tagging is enabled, the citation can be linked to the bibliography entry. This changes some of the details of the layout and the default template. We now make CSLReferences a special enumitem list that will contain `\bibitem`s. Internal links inside citations to ids beginning in `ref-` are put inside a `\cite` instead of `\hyperref`. Closes #9031. --- data/templates/default.latex | 32 ++++++++------- src/Text/Pandoc/Writers/LaTeX.hs | 55 ++++++++++++++++---------- src/Text/Pandoc/Writers/LaTeX/Types.hs | 2 + 3 files changed, 54 insertions(+), 35 deletions(-) diff --git a/data/templates/default.latex b/data/templates/default.latex index e2cfce1f14ec..c177c00e84e7 100644 --- a/data/templates/default.latex +++ b/data/templates/default.latex @@ -335,24 +335,28 @@ $if(pagestyle)$ \pagestyle{$pagestyle$} $endif$ $if(csl-refs)$ +% definitions for citeproc citations +\NewDocumentCommand\citeproctext{}{} +\NewDocumentCommand\citeproc{mm}{% + \begingroup\def\citeproctext{#2}\cite{#1}\endgroup} +% avoid brackets around text for \cite: +\makeatletter + \def\@biblabel#1{} + \def\@cite#1#2{{#1\if@tempswa , #2\fi}} +\makeatother \newlength{\cslhangindent} \setlength{\cslhangindent}{1.5em} \newlength{\csllabelwidth} \setlength{\csllabelwidth}{3em} -\newlength{\cslentryspacingunit} % times entry-spacing -\setlength{\cslentryspacingunit}{\parskip} -\newenvironment{CSLReferences}[2] % #1 hanging-ident, #2 entry spacing - {% don't indent paragraphs - \setlength{\parindent}{0pt} - % turn on hanging indent if param 1 is 1 - \ifodd #1 - \let\oldpar\par - \def\par{\hangindent=\cslhangindent\oldpar} - \fi - % set entry spacing - \setlength{\parskip}{#2\cslentryspacingunit} - }% - {} +\newlength{\cslentryspacing} +\setlength{\cslentryspacing}{0em} +\usepackage{enumitem} +\newlist{CSLReferences}{itemize}{1} +\setlist[CSLReferences]{label={}, + leftmargin=\cslhangindent, + itemindent=-1\cslhangindent, + parsep=\parskip, + itemsep=\cslentryspacing} \usepackage{calc} \newcommand{\CSLBlock}[1]{#1\hfill\break} \newcommand{\CSLLeftMargin}[1]{\parbox[t]{\csllabelwidth}{#1}} diff --git a/src/Text/Pandoc/Writers/LaTeX.hs b/src/Text/Pandoc/Writers/LaTeX.hs index d12890bd5ce8..bdfa96c1a4cc 100644 --- a/src/Text/Pandoc/Writers/LaTeX.hs +++ b/src/Text/Pandoc/Writers/LaTeX.hs @@ -339,22 +339,30 @@ blockToLaTeX (Div (identifier,classes,kvs) bs) = do then do modify $ \st -> st{ stHasCslRefs = True } inner <- blockListToLaTeX bs - return $ "\\begin{CSLReferences}" <> - (if "hanging-indent" `elem` classes - then braces "1" - else braces "0") <> - (case lookup "entry-spacing" kvs of - Nothing -> braces "0" - Just s -> braces (literal s)) + return $ (if "hanging-indent" `notElem` classes + then "\\setlength{\\cslhangindent}{0em}" + else mempty) + $$ ("\\setlength{\\cslentryspacing}" <> braces + (case lookup "entry-spacing" kvs of + Nothing -> "0em" + Just s -> (literal s <> "\\baselineskip"))) + $$ "\\begin{CSLReferences}" $$ inner $+$ "\\end{CSLReferences}" else blockListToLaTeX bs modify $ \st -> st{ stIncremental = oldIncremental } - linkAnchor <- hypertarget identifier - let wrapNotes txt = if beamer && "notes" `elem` classes - then "\\note" <> braces txt -- speaker notes - else linkAnchor $$ txt - wrapNotes <$> wrapDiv (identifier,classes,kvs) result + let wrap txt + | beamer && "notes" `elem` classes + = pure ("\\note" <> braces txt) -- speaker notes + | "ref-" `T.isPrefixOf` identifier + = do + lab <- toLabel identifier + pure $ ("\\bibitem" <> brackets "\\citeproctext" + <> braces (literal lab)) $$ txt + | otherwise = do + linkAnchor <- hypertarget identifier + pure $ linkAnchor $$ txt + wrapDiv (identifier,classes,kvs) result >>= wrap blockToLaTeX (Plain lst) = inlineListToLaTeX lst -- . . . indicates pause in beamer slides @@ -816,12 +824,14 @@ inlineToLaTeX (Subscript lst) = inlineToLaTeX (SmallCaps lst) = inCmd "textsc"<$> inlineListToLaTeX lst inlineToLaTeX (Cite cits lst) = do - st <- get - let opts = stOptions st - case writerCiteMethod opts of - Natbib -> citationsToNatbib inlineListToLaTeX cits - Biblatex -> citationsToBiblatex inlineListToLaTeX cits - _ -> inlineListToLaTeX lst + opts <- gets stOptions + modify $ \st -> st{ stInCite = True } + res <- case writerCiteMethod opts of + Natbib -> citationsToNatbib inlineListToLaTeX cits + Biblatex -> citationsToBiblatex inlineListToLaTeX cits + _ -> inlineListToLaTeX lst + modify $ \st -> st{ stInCite = False } + pure res inlineToLaTeX (Code (_,classes,kvs) str) = do opts <- gets stOptions @@ -945,11 +955,14 @@ inlineToLaTeX (Link (id',_,_) txt (src,_)) = Just ('#', ident) -> do contents <- inlineListToLaTeX txt lab <- toLabel ident + inCite <- gets stInCite beamer <- gets stBeamer return $ - if beamer - then text "\\hyperlink" <> braces (literal lab) <> braces contents - else text "\\hyperref" <> brackets (literal lab) <> braces contents + if inCite && "#ref-" `T.isPrefixOf` src + then "\\citeproc" <> braces (literal lab) <> braces contents + else if beamer + then "\\hyperlink" <> braces (literal lab) <> braces contents + else "\\hyperref" <> brackets (literal lab) <> braces contents _ -> case txt of [Str x] | unEscapeString (T.unpack x) == unEscapeString (T.unpack src) -> -- autolink do modify $ \s -> s{ stUrl = True } diff --git a/src/Text/Pandoc/Writers/LaTeX/Types.hs b/src/Text/Pandoc/Writers/LaTeX/Types.hs index 97ac1dcf9b90..0e0582948682 100644 --- a/src/Text/Pandoc/Writers/LaTeX/Types.hs +++ b/src/Text/Pandoc/Writers/LaTeX/Types.hs @@ -26,6 +26,7 @@ data WriterState = , stInHeading :: Bool -- ^ true if in a section heading , stInItem :: Bool -- ^ true if in \item[..] , stInFigure :: Bool -- ^ true if in figure environment + , stInCite :: Bool -- ^ true if in a Cite , stNotes :: [Doc Text] -- ^ notes in a minipage , stOLLevel :: Int -- ^ level of ordered list nesting , stOptions :: WriterOptions -- ^ writer options, so they don't have to @@ -61,6 +62,7 @@ startingState options = , stInMinipage = False , stInItem = False , stInFigure = False + , stInCite = False , stNotes = [] , stOLLevel = 1 , stOptions = options