Skip to content

Commit

Permalink
Use \cite and \bibitem to link up citations, even with citeproc.
Browse files Browse the repository at this point in the history
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.
  • Loading branch information
jgm committed Aug 29, 2023
1 parent 9d28e60 commit 353177f
Show file tree
Hide file tree
Showing 3 changed files with 54 additions and 35 deletions.
32 changes: 18 additions & 14 deletions data/templates/default.latex
Original file line number Diff line number Diff line change
Expand Up @@ -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}}
Expand Down
55 changes: 34 additions & 21 deletions src/Text/Pandoc/Writers/LaTeX.hs
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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 }
Expand Down
2 changes: 2 additions & 0 deletions src/Text/Pandoc/Writers/LaTeX/Types.hs
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -61,6 +62,7 @@ startingState options =
, stInMinipage = False
, stInItem = False
, stInFigure = False
, stInCite = False
, stNotes = []
, stOLLevel = 1
, stOptions = options
Expand Down

0 comments on commit 353177f

Please sign in to comment.