diff --git a/crates/citefind/src/parse.rs b/crates/citefind/src/parse.rs index c0dc96a..f334f48 100644 --- a/crates/citefind/src/parse.rs +++ b/crates/citefind/src/parse.rs @@ -990,6 +990,11 @@ fn is_name(text: &str) -> bool { if text.chars().any(|c| c.is_ascii_digit()) { return false; } + // A bracket or a colon belongs to an imprint or a title, never to a + // name: `Philosophy (Bloomington: Indiana University Press)` is not one. + if text.contains(['(', ')', ':', '[', ']']) { + return false; + } // An editor's or translator's credit is a role, not a name. if text.to_lowercase().contains("ed.") || text.to_lowercase().contains("trans.") { return false; diff --git a/crates/citefind/src/split.rs b/crates/citefind/src/split.rs index c9b1bde..2ef64c0 100644 --- a/crates/citefind/src/split.rs +++ b/crates/citefind/src/split.rs @@ -92,6 +92,13 @@ static LOCAL_SIGNAL: LazyLock = static GLUED_NUMBER: LazyLock = LazyLock::new(|| Regex::new(r#"^(\d{1,3})(?:\p{Lu}|\*|“|")"#).expect("a valid pattern")); +/// The copyright line a digitised book prints at the foot of every page: +/// `Cambridge Histories Online © Cambridge University Press, 2008`. It is +/// not a reference, and it is glued to the end of whatever note the page +/// ended on. +static COPYRIGHT_FOOTER: LazyLock = + LazyLock::new(|| Regex::new(r"©").expect("a valid pattern")); + /// A bullet or a blockquote marker, which conversion puts in front of a note /// often enough to matter. A leading `*` is left alone: it opens an italic /// title as often as it opens a list. @@ -255,7 +262,7 @@ fn unnumbered(text: &str) -> Vec { /// cut is then made in the original, so that an italic span marking a title /// further along survives. fn strip_marker(raw: &str) -> Entry { - let text = tidy(raw); + let text = cut_footer(&tidy(raw)); let text = BULLET.replace(&text, "").to_string(); let (bare, from) = visible(&text, 160); @@ -294,6 +301,20 @@ fn strip_marker(raw: &str) -> Entry { Entry { text, marker: None } } +/// Cut the copyright line off the end of an entry. +/// +/// It runs from wherever the sentence holding the `©` began, because the +/// words in front of it — the name of the digitising service — belong to it +/// and not to the note the page ended on. +fn cut_footer(text: &str) -> String { + let Some(found) = COPYRIGHT_FOOTER.find(text) else { + return text.to_string(); + }; + let head = &text[..found.start()]; + let cut = head.rfind(". ").map_or(0, |at| at + 1); + text[..cut].trim().to_string() +} + /// The opening of an entry with its tags dropped, and where each character /// that was kept came from in the original. /// @@ -474,6 +495,15 @@ Another Anonymous Work. Cambridge, 2001."; assert_eq!(entry.text, "Smith, John. *A Real Book*. Oxford, 1998."); } + #[test] + fn the_copyright_line_a_digitised_book_prints_is_cut_off() { + let entry = strip_marker( + "Holquist, Making War, pp. 211-12. 106 Cambridge Histories Online \ + © Cambridge University Press, 2008", + ); + assert_eq!(entry.text, "Holquist, Making War, pp. 211-12."); + } + #[test] fn an_entry_with_no_marker_keeps_all_of_itself() { let entry = strip_marker("Smith, John. The Title. Oxford, 1998.");