Skip to content

Commit

Permalink
BibTeX: escape special characters ($%&_#{}).
Browse files Browse the repository at this point in the history
For example, some journal titles specifically use &, which would produce a
broken BibTeX entry if left bare. Also, it's quite possible that publication
titles, entity names, aliases, etc could use certain special characters, so
escape them all---at least the easy ones for now.

Fixes #124.
  • Loading branch information
hainesr committed Jan 16, 2024
1 parent dcdc01c commit 9ca1348
Show file tree
Hide file tree
Showing 10 changed files with 119 additions and 11 deletions.
32 changes: 21 additions & 11 deletions lib/cff/formatters/bibtex.rb
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# frozen_string_literal: true

# Copyright (c) 2018-2023 The Ruby Citation File Format Developers.
# Copyright (c) 2018-2024 The Ruby Citation File Format Developers.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
Expand Down Expand Up @@ -45,13 +45,17 @@ class BibTeX < Formatter # :nodoc:
month.downcase unless month.nil?
end.freeze

# We need to escape these characters in titles and names, as per
# https://tex.stackexchange.com/questions/34580/escape-character-in-latex
ESCAPE_CHARS = Regexp.new(/([&%$#_{}])/)

def self.format(model:, preferred_citation: true) # rubocop:disable Metrics
model = select_and_check_model(model, preferred_citation)
return if model.nil?

values = {}
values['author'] = actor_list(model.authors)
values['title'] = "{#{model.title}}"
values['title'] = "{#{l(model.title)}}"

publication_type = bibtex_type(model)
publication_data_from_model(model, publication_type, values)
Expand Down Expand Up @@ -79,7 +83,7 @@ def self.format(model:, preferred_citation: true) # rubocop:disable Metrics
def self.publication_data_from_model(model, type, fields)
ENTRY_TYPE_MAP[type].each do |field|
if model.respond_to?(field)
fields[field] = model.send(field).to_s
fields[field] = l(model.send(field).to_s)
else
field = field.chomp('!')
fields[field] = send("#{field}_from_model", model)
Expand Down Expand Up @@ -108,9 +112,9 @@ def self.address_from_model(model)
# BibTeX 'institution' could be grabbed from an author's affiliation, or
# provided explicitly.
def self.institution_from_model(model)
return model.institution.name unless model.institution.empty?
return l(model.institution.name) unless model.institution.empty?

model.authors.first.affiliation
l(model.authors.first.affiliation)
end

# BibTeX 'school' is CFF 'institution'.
Expand All @@ -125,7 +129,7 @@ def self.type_from_model(model)

# BibTeX 'booktitle' is CFF 'collection-title'.
def self.booktitle_from_model(model)
model.collection_title
l(model.collection_title)
end

# BibTeX 'editor' is CFF 'editors' or 'editors-series'.
Expand All @@ -138,11 +142,11 @@ def self.editor_from_model(model)
end

def self.publisher_from_model(model)
model.publisher.empty? ? '' : model.publisher.name
model.publisher.empty? ? '' : l(model.publisher.name)
end

def self.series_from_model(model)
model.conference.empty? ? '' : model.conference.name
model.conference.empty? ? '' : l(model.conference.name)
end

# If we're citing a conference paper, try and use the date of the
Expand Down Expand Up @@ -182,9 +186,9 @@ def self.bibtex_type(model) # rubocop:disable Metrics/CyclomaticComplexity
end
end

def self.format_actor(author)
return "{#{author.name}}" if author.is_a?(Entity)
return author.alias if author.family_names.empty? && author.given_names.empty?
def self.format_actor(author) # rubocop:disable Metrics/AbcSize
return "{#{l(author.name)}}" if author.is_a?(Entity)
return l(author.alias) if author.family_names.empty? && author.given_names.empty?

particle =
author.name_particle.empty? ? '' : "#{author.name_particle} "
Expand All @@ -209,6 +213,12 @@ def self.generate_citekey(fields)

Util.parameterize(reference)
end

# Escape a string to preserve special characters in LaTeX output.
# Used in many places, so short method name to preserve reading flow.
def self.l(string)
string.gsub(ESCAPE_CHARS, '\\\\\1')
end
end
end
end
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Haines, R., & The {curly_braces} Collective. (2021). CFF is #1, 100% [Conference paper]. Software Credit for Fun & Profit ($$$). https://doi.org/10.5281/zenodo.1184077
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
@inproceedings{Haines_CFF_is_1_2021,
author = {Haines, Robert and {The \{curly\_braces\} Collective}},
booktitle = {Software Credit for Fun \& Profit (\$\$\$)},
doi = {10.5281/zenodo.1184077},
month = sep,
publisher = {The CFF \& Friends},
series = {Software Credit \#1},
title = {{CFF is \#1, 100\%}},
url = {https://github.com/citation-file-format/ruby-cff},
year = {2021}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Haines, R. (2024). CFF for the $%&#_{} win. Journal of Maths & Philosophy, 1(1)
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
@article{Haines_CFF_for_the_2024,
author = {Haines, Robert},
journal = {Journal of Maths \& Philosophy},
month = jan,
number = {1},
title = {{CFF for the \$\%\&\#\_\{\} win}},
volume = {1},
year = {2024}
}
1 change: 1 addition & 0 deletions test/files/formatted/software-with-escapes.apa
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Haines, R., & The {curly_braces} Collective. (2024). Software that uses the following symbols: &, %, $, # (Version 0.0.1-alpha) [Computer software]
7 changes: 7 additions & 0 deletions test/files/formatted/software-with-escapes.bibtex
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
@software{Haines_Software_that_uses_2024,
author = {Haines, Robert and {The \{curly\_braces\} Collective}},
month = jan,
title = {{Software that uses the following symbols: \&, \%, \$, \#}},
version = {0.0.1-alpha},
year = {2024}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
# A report preferred-citation

cff-version: 1.2.0
message: If you use this software in your work, please cite it using the following metadata
title: Ruby CFF Library
authors:
- family-names: Haines
given-names: Robert
affiliation: The University of Manchester
keywords:
- ruby
- credit
- citation
- cff
version: 0.9.0
date-released: 2021-09-21
license: Apache-2.0
repository-artifact: https://rubygems.org/gems/cff
type: software
preferred-citation:
type: conference-paper
conference:
name: "Software Credit #1"
title: "CFF is #1, 100%"
authors:
- family-names: Haines
given-names: Robert
affiliation: The University of Manchester
- name: "The {curly_braces} Collective"
publisher:
name: The CFF & Friends
collection-title: Software Credit for Fun & Profit ($$$)
month: 9
year: 2021
url: https://github.com/citation-file-format/ruby-cff
doi: 10.5281/zenodo.1184077
23 changes: 23 additions & 0 deletions test/files/formatter/preferred-citation-journal-with-escapes.cff
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
# Note this is a made up entry.

cff-version: 1.2.0
message: If you use this software in your work, please cite it using the following metadata
title: Ruby-CFF
authors:
- family-names: Haines
given-names: Robert
version: 0.1
date-released: 2024-01-16

# JOSS paper as preferred citation
preferred-citation:
authors:
- family-names: Haines
given-names: Robert
journal: Journal of Maths & Philosophy
month: 1
title: "CFF for the $%&#_{} win"
type: article
volume: 1
issue: 1
year: 2024
9 changes: 9 additions & 0 deletions test/files/formatter/software-with-escapes.cff
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
cff-version: 1.2.0
message: "If you use this software, please cite it as below."
authors:
- family-names: Haines
given-names: Robert
- name: "The {curly_braces} Collective"
title: "Software that uses the following symbols: &, %, $, #"
version: 0.0.1-alpha
date-released: 2024-01-16

0 comments on commit 9ca1348

Please sign in to comment.