Scrivener + Quarto: a technical/academic publishing workflow

I managed to fix it! To celebrate it, and the conclusion of my doctorate here is my augmented version of the excellent Scrivener + Quarto template.

Scrivener project file download

https://www.dropbox.com/s/tulnkeepgavck8r/Quarto-Augmented.scriv.zip?dl=1

Sample output

The changes to the original project are documented on page 22.

Changes to the original Scrivener project

Depth override

The section type Attributes .. Header & Text can be used to override the depth of the current binder item; that is to say, change the number of hashes before the title. Place the desired number of hashes in the Attributes metadata field with no space at the end (e.g. ###).

Automatic anchors

All of the Section Layouts received a new prefix: []{#scriv<$linkID>}. This allows us to create links to any binder item whatsoever. Particularly useful to link to sections that do not have a Header element.

Multiple bibliographies

There is a new Section Layout and a new Section Type to provide support for the Multibib/Multiple-bibliographies lua filter. In this way, we can create complex bibliographies with ease, spliting primary and secondary sources. Each bibliography section will be named after the binder item whence it originated, as it happens with any chapter of the text which makes it very easy to managem them.

Only the multiple-bibliographies filter is compatible with the template. The Multibib filter uses a single variable to store all of the file paths, so this creates problems for the template. To use: choose the Multibib section type, fill the Path custom meta-data field with the path to the bib file, and you’re set.

Indexes & glossaries

The multiple-bibliographies filter can be extended in interesting ways using the citation-backlinks filter that links back from the ref-entry in the bibliography to each inline citation in the text. This means that we get to make indexes using Citeproc that work both for LaTeX and HTML (see the output sample linked below).

Generating bibliography files

In order to create these separate bibliographies using Bookends, we could i. create a library for each bibliography section and sync the references to bibtex; ii. use an Applescript to export different Bookends groups as separate bibliography files.

Here is a script that will 1. ask for the prefered format, 2. the file extention and then 3. list all front library groups allowing the selection of multiple items. They will be exported to the folder especified in the BasePath variable.

Applescript for Bookends

set RevealAfterFinish to true
set UseGroupPaths to true -- {"Group 1", "Group 2"} OR {"Path/to/Group 1", "Path/to/Group 2"}
set BasePath to "/Users/bcdav/Dropbox/dev/pandoc/refs/"
set FlattenBool to {"Yes", "No"}
set MarkupList to {"Plain", "BibTeX", "HTML", "RTF"}
set FileExtensionOptions to {".bib", ".bex", ".json", ".md", ".qmd", ".ris", ".txt", ".xml", ".yml"}
set FormatList to {"BibTeX", "BibTeX4Zotero", "RIS", "Endnote XML", "Pandoc.fmt", "AAPG Bulletin.fmt", "ABNT Markdown.fmt", "ABNT MMD.fmt", "ABNT_MD.fmt", "ABNT.fmt", "Antiquity.fmt", "APA 5th Edition.fmt", "APA 6th Edition.fmt", "APA 7th Edition.fmt", "Berliner Blätter.fmt", "BibTeX 2.fmt", "BibTeX.fmt", "BibTeX2.fmt", "BibTeX4Export.fmt", "BibTeX4Export(2).fmt", "BibTeX4Pandoc.fmt", "Chicago 15th A.fmt", "Chicago 15th B.fmt", "Chicago 16th A.fmt", "Chicago 16th B.fmt", "Chicago 17th A.fmt", "Chicago 17th B.fmt", "Discourse Studies.fmt", "DOI.fmt", "DPG (Deutsche Gesellschaft für Psychologie).fmt", "DT3-URL.fmt", "MLA 6th Edition.fmt", "MLA 7th Edition.fmt", "MLA 8th Edition.fmt", "Nature.fmt", "Oxford Author-Date.fmt", "Oxford Notes.fmt", "Pandoc.fmt", "Printout.fmt", "Ref with Abstract.fmt", "Rename.fmt", "RIS 2.fmt", "RIS Export.fmt", "RIS.fmt", "RISwithNote.fmt", "ShortRef.fmt", "Turabian (7th ed).fmt", "Turabian Author-Date.fmt", "Turabian SBTS author-Date.fmt"}

tell application "Bookends"
	
	set theLibraries to library windows
	repeat with theLibrary in theLibraries
		if UseGroupPaths is true then
			set GroupList to my split(return, («event ToySRGPN» given «class PATH»:"true")) -- 1
			set GroupList to my simple_sort(GroupList) -- 1
		else
			set GroupList to name of every group item of theLibrary -- 2
		end if
		set GroupPaths to my get_opt("Bookends Groups", "Select the desidered groups to be exported as  individual bibliography files", GroupList, true)
		if GroupPaths is false then error "Please select some items."
		
		set theFormat to item 1 of my get_opt("The Format", "Select the desidered Bookends format", FormatList, false)
		set theMarkup to item 1 of my get_opt("The Markup", "", MarkupList, false)
		set theExtension to item 1 of my get_opt("The Extension", "The file extension the bibliography files should use", FileExtensionOptions, false)
		set Flatten to item 1 of my get_opt("Flatten", "Should the folder structure be flattened? 

If you select YES, then all of the files will be exported to BasePath.

If you select NO, then it will recreate the folder structure using /Path/to/Group.", FlattenBool, false)
		
		repeat with GroupPath in GroupPaths
			if UseGroupPaths is true then
				set theGroup to the last item of my split("/", GroupPath) -- 1
			else
				set theGroup to GroupPath
			end if
			set theResult to ""
			set theRefs to publication items of group item id theGroup of theLibrary
			
			repeat with theRef in theRefs
				if theMarkup is "BibTeX" then
					set theRef to format theRef using theFormat as BibTeX
				else if theMarkup is "HTML" then
					set theRef to format theRef using theFormat as HTML
				else if theMarkup is "RTF" then
					set theRef to format theRef using theFormat as RTF
				else
					set theRef to format theRef using theFormat
				end if
				-- Endnote XML | RIS | plain text
				-- Deprecated: set theRef to «event ToySGUID» theID given «class RRTF»:"false", string:"Bibtex"
				set theResult to theResult & theRef & linefeed
			end repeat
			
			if Flatten is "Yes" then set GroupPath to name of group item id theGroup of theLibrary
			set GroupPath to item 1 of (my split(".", GroupPath))
			
			--			set GroupPath to my gsub(my gsub(GroupPath, ".bib", ""), "Bib ", "")
			set ExportPath to BasePath & "" & GroupPath & theExtension
			--				return ExportPath
			my exportSource(theResult, ExportPath)
			--		return theGroup
			
		end repeat
		if RevealAfterFinish is true then
			tell me to do shell script "open " & quoted form of ExportPath & " -R"
		end if
	end repeat
end tell


on gsub(theString, old, new)
	set {TID, text item delimiters} to {text item delimiters, old}
	set theStringItems to text items of theString
	set text item delimiters to new
	set theString to theStringItems as text
	set text item delimiters to TID
	return theString
end gsub

on join(delim, theContent)
	set oldDelims to AppleScript's text item delimiters
	set AppleScript's text item delimiters to delim
	set theResult to theContent as string
	set AppleScript's text item delimiters to oldDelims
	return theResult
end join

on split(delim, theString)
	try
		set oldDelims to AppleScript's text item delimiters
		set AppleScript's text item delimiters to delim
		set delimitedList to every text item of theString
		set AppleScript's text item delimiters to oldDelims
	on error
		set AppleScript's text item delimiters to oldDelims
	end try
	return delimitedList
end split

on rb(rb_script)
	return do shell script "ruby -r ERB  <<EOF
" & rb_script & "
EOF"
end rb

on URLEncode(findThis)
	set findThis to paragraphs of findThis
	return my rb("puts ERB::Util.url_encode %Q{" & findThis & "}")
end URLEncode

on exportSource(theText, ExportedFile)
	set the clipboard to theText
	set theFolder to my join("/", (items 1 thru -2 of (my split("/", ExportedFile)))) as string
	tell me to do shell script "mkdir -p " & quoted form of theFolder
	set theScript to "touch " & quoted form of ExportedFile & " && LANG=pt_BR.UTF-8 pbpaste > " & quoted form of ExportedFile
	tell me to do shell script theScript
end exportSource

on simple_sort(my_list)
	set the index_list to {}
	set the sorted_list to {}
	repeat (the number of items in my_list) times
		set the low_item to ""
		repeat with i from 1 to (number of items in my_list)
			if i is not in the index_list then
				set this_item to item i of my_list as text
				if the low_item is "" then
					set the low_item to this_item
					set the low_item_index to i
				else if this_item comes before the low_item then
					set the low_item to this_item
					set the low_item_index to i
				end if
			end if
		end repeat
		set the end of sorted_list to the low_item
		set the end of the index_list to the low_item_index
	end repeat
	return the sorted_list
end simple_sort
on get_opt(theTitle, thePromt, theList, MultipleSelection)
	if MultipleSelection is not false and (count of theList) > 1 then
		set theOption to choose from list theList with title theTitle with prompt thePromt with multiple selections allowed
	else
		set theOption to choose from list theList with title theTitle with prompt thePromt
	end if
	if theOption is false then error "Aborted."
	return theOption as list
end get_opt

I hope these additions prove useful to someone at some point.

EDIT: Updated the Applescript to include new options (Markup) and fix some bugs.

4 Likes