# frozen_string_literal: true
# This file is part of Alexandria.
#
# See the file README.md for authorship and licensing information.
require "csv"
require "image_size"
require "tmpdir"
module Alexandria
class ExportLibrary
def initialize(library, sort_order)
@library = library
@sorted = sort_order.sort(library)
end
def cover(book)
@library.cover(book)
end
def final_cover(book)
@library.final_cover(book)
end
def copy_covers(dest)
@library.copy_covers(dest)
end
def name
@library.name
end
def each(&block)
@sorted.each(&block)
end
def export_as_onix_xml_archive(filename)
File.open(File.join(Dir.tmpdir, "onix.xml"), "w") do |io|
to_onix_document.write(io, 0)
end
copy_covers(File.join(Dir.tmpdir, "images"))
Dir.chdir(Dir.tmpdir) do
output = `tar -cjf \"#{filename}\" onix.xml images 2>&1`
raise output unless $CHILD_STATUS.success?
end
FileUtils.rm_rf(File.join(Dir.tmpdir, "images"))
FileUtils.rm(File.join(Dir.tmpdir, "onix.xml"))
end
def export_as_tellico_xml_archive(filename)
File.open(File.join(Dir.tmpdir, "tellico.xml"), "w") do |io|
to_tellico_document.write(io, 0)
rescue StandardError => ex
puts ex.message
puts ex.backtrace
raise ex
end
copy_covers(File.join(Dir.tmpdir, "images"))
Dir.chdir(Dir.tmpdir) do
output = `zip -q -r \"#{filename}\" tellico.xml images 2>&1`
raise output unless $CHILD_STATUS.success?
end
FileUtils.rm_rf(File.join(Dir.tmpdir, "images"))
FileUtils.rm(File.join(Dir.tmpdir, "tellico.xml"))
end
def export_as_isbn_list(filename)
File.open(filename, "w") do |io|
each do |book|
io.puts((book.isbn || ""))
end
end
end
def export_as_html(filename, theme)
FileUtils.mkdir(filename) unless File.exist?(filename)
Dir.chdir(filename) do
copy_covers("pixmaps")
FileUtils.cp_r(theme.pixmaps_directory, "pixmaps") if theme.has_pixmaps?
FileUtils.cp(theme.css_file, ".")
File.open("index.html", "w") do |io|
io << to_xhtml(File.basename(theme.css_file))
end
end
end
def export_as_bibtex(filename)
File.open(filename, "w") do |io|
io << to_bibtex
end
end
def export_as_ipod_notes(filename, _theme)
FileUtils.mkdir(filename) unless File.exist?(filename)
tempdir = Dir.getwd
Dir.chdir(filename)
copy_covers("pixmaps")
File.open("index.linx", "w") do |io|
io.puts "
" + name + ""
each do |book|
io.puts '' + book.title + ""
end
io.close
end
each do |book|
File.open(book.ident, "w") do |io|
io.puts "#{book.title} "
# put a link to the book's cover. only works on iPod 5G and above(?).
if File.exist?(cover(book))
io.puts '' + book.title + ""
else
io.puts book.title
end
io.puts book.authors.join(", ")
io.puts book.edition
io.puts((book.isbn || ""))
# we need to close the files so the iPod can be ejected/unmounted
# without us closing Alexandria
io.close
end
end
# Again, allow the iPod to unmount
Dir.chdir(tempdir)
end
def export_as_csv_list(filename)
CSV.open(filename, "w", col_sep: ";") do |csv|
csv << ["Title", "Authors", "Publisher", "Edition", "ISBN", "Year Published",
"Rating(#{Book::DEFAULT_RATING} to #{Book::MAX_RATING_STARS})", "Notes",
"Want?", "Read?", "Own?", "Tags"]
each do |book|
csv << [book.title, book.authors.join(", "), book.publisher, book.edition,
book.isbn, book.publishing_year, book.rating, book.notes,
(book.want ? "1" : "0"), (book.redd ? "1" : "0"), (book.own ? "1" : "0"),
(book.tags ? book.tags.join(", ") : "")]
end
end
end
private
ONIX_DTD_URL = "http://www.editeur.org/onix/2.1/reference/onix-international.dtd"
def to_onix_document
doc = REXML::Document.new
doc << REXML::XMLDecl.new
doc << REXML::DocType.new("ONIXMessage",
"SYSTEM \"#{ONIX_DTD_URL}\"")
msg = doc.add_element("ONIXMessage")
header = msg.add_element("Header")
header.add_element("FromCompany").text = "Alexandria"
header.add_element("FromPerson").text = Etc.getlogin
now = Time.now
header.add_element("SentDate").text = now.strftime("%Y%m%d%H%M")
header.add_element("MessageNote").text = name
@sorted.each_with_index do |book, idx|
# fields that are missing: edition and rating.
prod = msg.add_element("Product")
prod.add_element("RecordReference").text = idx
prod.add_element("NotificationType").text = "03" # confirmed
prod.add_element("RecordSourceName").text =
"Alexandria " + Alexandria::DISPLAY_VERSION
prod.add_element("ISBN").text = (book.isbn || "")
prod.add_element("ProductForm").text = "BA" # book
prod.add_element("DistinctiveTitle").text = book.title
unless book.authors.empty?
book.authors.each do |author|
elem = prod.add_element("Contributor")
# author
elem.add_element("ContributorRole").text = "A01"
elem.add_element("PersonName").text = author
end
end
if book.notes && !book.notes.empty?
elem = prod.add_element("OtherText")
# reader description
elem.add_element("TextTypeCode").text = "12"
elem.add_element("TextFormat").text = "00" # ASCII
elem.add_element("Text").text = book.notes
end
if File.exist?(cover(book))
elem = prod.add_element("MediaFile")
# front cover image
elem.add_element("MediaFileTypeCode").text = "04"
elem.add_element("MediaFileFormatCode").text =
(Library.jpeg?(cover(book)) ? "03" : "02")
# filename
elem.add_element("MediaFileLinkTypeCode").text = "06"
elem.add_element("MediaFileLink").text =
File.join("images", final_cover(book))
end
if book.isbn
BookProviders.each do |provider|
elem = prod.add_element("ProductWebsite")
elem.add_element("ProductWebsiteDescription").text =
provider.fullname
elem.add_element("ProductWebsiteLink").text =
provider.url(book)
end
end
elem = prod.add_element("Publisher")
elem.add_element("PublishingRole").text = "01"
elem.add_element("PublisherName").text = book.publisher
prod.add_element("PublicationDate").text = book.publishing_year
end
doc
end
def to_tellico_document
# For the Tellico format, see
# http://periapsis.org/tellico/doc/hacking.html
doc = REXML::Document.new
doc << REXML::XMLDecl.new
doc << REXML::DocType.new("tellico",
'PUBLIC "-//Robby Stephenson/DTD Tellico V7.0//EN"' \
' "http://periapsis.org/tellico/dtd/v7/tellico.dtd"')
tellico = doc.add_element("tellico")
tellico.add_attribute("syntaxVersion", "7")
tellico.add_namespace("http://periapsis.org/tellico/")
collection = tellico.add_element("collection")
collection.add_attribute("title", name)
collection.add_attribute("type", "2")
fields = collection.add_element("fields")
field1 = fields.add_element("field")
# a field named _default implies adding all default book
# collection fields
field1.add_attribute("name", "_default")
images = collection.add_element("images")
@sorted.each_with_index do |book, idx|
entry = collection.add_element("entry")
new_index = (idx + 1).to_s
entry.add_attribute("id", new_index)
# translate the binding
entry.add_element("title").text = book.title
entry.add_element("isbn").text = (book.isbn || "")
entry.add_element("pub_year").text = book.publishing_year
entry.add_element("binding").text = book.edition
entry.add_element("publisher").text = book.publisher
unless book.authors.empty?
authors = entry.add_element("authors")
book.authors.each do |author|
authors.add_element("author").text = author
end
end
entry.add_element("read").text = book.redd.to_s if book.redd
entry.add_element("loaned").text = book.loaned.to_s if book.loaned
unless book.rating == Book::DEFAULT_RATING
entry.add_element("rating").text = book.rating
end
entry.add_element("comments").text = book.notes if book.notes && !book.notes.empty?
if File.exist?(cover(book))
entry.add_element("cover").text = final_cover(book)
image = images.add_element("image")
image.add_attribute("id", final_cover(book))
image_s = ImageSize.new(IO.read(cover(book)))
image.add_attribute("height", image_s.height.to_s)
image.add_attribute("width", image_s.width.to_s)
image.add_attribute("format", image_s.format)
end
end
doc
end
def xhtml_escape(str)
escaped = str.dup
# used to occasionally use CGI.escapeHTML
escaped.gsub!(/&/, "&")
escaped.gsub!(/, "<")
escaped.gsub!(/>/, ">")
escaped.gsub!(/\"/, """)
escaped
end
def to_xhtml(css)
generator = "Alexandria " + Alexandria::DISPLAY_VERSION
xhtml = +""
xhtml << <<~EOS
#{xhtml_escape(name)}
#{xhtml_escape(name)}
EOS
each do |book|
xhtml << <<~EOS
#{book.isbn}
EOS
if File.exist?(cover(book))
image_s = ImageSize.new(IO.read(cover(book)))
xhtml << <<~EOS
EOS
else
xhtml << <<~EOS
EOS
end
unless book.title.nil?
xhtml << <<~EOS
#{xhtml_escape(book.title)}
EOS
end
unless book.authors.empty?
xhtml << '
'
book.authors.each do |author|
xhtml << <<~EOS
#{xhtml_escape(author)}
EOS
end
xhtml << "
"
end
unless book.edition.nil?
xhtml << <<~EOS
#{xhtml_escape(book.edition)}
EOS
end
unless book.publisher.nil?
xhtml << <<~EOS