Skip to content

Commit

Permalink
add books exporter for jats (bits) format, fiduswriter/fiduswriter#1275
Browse files Browse the repository at this point in the history
  • Loading branch information
johanneswilm committed Jul 25, 2024
1 parent d006bc4 commit afba2a3
Show file tree
Hide file tree
Showing 4 changed files with 159 additions and 1 deletion.
111 changes: 111 additions & 0 deletions fiduswriter/book/static/js/modules/books/exporter/bits/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,111 @@
import download from "downloadjs"
import pretty from "pretty"

import {createSlug} from "../../../exporter/tools/file"
import {JATSExporterConverter} from "../../../exporter/jats/convert"
import {ZipFileCreator} from "../../../exporter/tools/zip"
import {darManifest} from "../../../exporter/jats/templates"
import {getMissingChapterData} from "../tools"
import {addAlert} from "../../../common"

import {bitsTemplate} from "./templates"


export class BITSExporter {
constructor(schema, csl, book, user, docList, updated) {
this.schema = schema
this.csl = csl
this.book = book
this.user = user
this.docList = docList
this.updated = updated
this.type = "book"
this.textFiles = []
this.httpFiles = []

}

init() {
if (this.book.chapters.length === 0) {
addAlert(
"error",
gettext("Book cannot be exported due to lack of chapters.")
)
return false
}
return getMissingChapterData(this.book, this.docList, this.schema).then(
() => this.export()
)
}

export() {
this.book.chapters.sort((a, b) => (a.number > b.number ? 1 : -1))

const imageDict = {}
Promise.all(this.book.chapters.map(chapter => {
const doc = this.docList.find(doc => doc.id === chapter.text)
const converter = new JATSExporterConverter(
this.type,
doc,
{db: doc.images},
{db: doc.bibliography}
)
return converter.init().then(({front, body, back, imageIds}) => {
imageIds.forEach(
imageId => (imageDict[imageId] = doc.images[imageId])
)
return {front, body, back}
})
})).then(
chapters => this.createFiles(chapters, imageDict)
)
}

createFiles(chapters, imageDict) {
const images = Object.values(imageDict).map(image => ({
filename: image.image.split("/").pop().split("?")[0],
url: image.image.split("?")[0],
title: image.title
}))

this.zipFileName = `${createSlug(this.book.title)}.bits.zip`

this.textFiles = [
{
filename: "manuscript.xml",
contents: pretty(bitsTemplate(chapters), {ocd: true})
},
{
filename: "manifest.xml",
contents: pretty(darManifest({
title: this.book.title,
type: this.type,
images
}), {ocd: true})
}
]

images.forEach(image => {
this.httpFiles.push({filename: image.filename, url: image.url})
})

return this.createZip()
}

createZip() {
const zipper = new ZipFileCreator(
this.textFiles,
this.httpFiles,
undefined,
undefined,
this.updated
)
return zipper.init().then(
blob => this.download(blob)
)
}

download(blob) {
return download(blob, this.zipFileName, "application/zip")
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
const bookPartTemplate = ({front, body, back}) =>
`<book-part book-part-type="chapter" xmlns:xlink="http://www.w3.org/1999/xlink" xmlns:ali="http://www.niso.org/schemas/ali/1.0/">${front}${body}${back}</book-part>`


export const bitsTemplate = (chapters) =>
`<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE book PUBLIC "-//NLM//DTD BITS Book Interchange DTD v2.1 20220202//EN" "https://jats.nlm.nih.gov/extensions/bits/2.1/BITS-book2-1.dtd">
<book dtd-version="2.1" xmlns:xlink="http://www.w3.org/1999/xlink" xmlns:ali="http://www.niso.org/schemas/ali/1.0/">
${chapters.map((chapter) => bookPartTemplate(chapter)).join("\n")}
</book>`
2 changes: 1 addition & 1 deletion fiduswriter/book/static/js/modules/books/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -232,7 +232,7 @@ export class BookOverview {
this.lastSort = {column, dir}
})

this.dtBulk.init(this.table.table)
this.dtBulk.init(this.table.dom)
}

createTableRow(book, subdirs, searching) {
Expand Down
37 changes: 37 additions & 0 deletions fiduswriter/book/static/js/modules/books/menu.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import {BookAccessRightsDialog} from "./accessrights"
import {BITSExporter} from "./exporter/bits"
import {HTMLBookExporter, SingleFileHTMLBookExporter} from "./exporter/html"
import {LatexBookExporter} from "./exporter/latex"
import {EpubBookExporter} from "./exporter/epub"
Expand Down Expand Up @@ -72,6 +73,22 @@ const exportEpub = (book, overview) => {
return exporter.init()
}

const exportBITS = (book, overview) => {
addAlert(
"info",
book.title + ": " + gettext("BITS export has been initiated.")
)
const exporter = new BITSExporter(
overview.schema,
overview.app.csl,
book,
overview.user,
overview.documentList,
new Date(book.updated * 1000)
)
return exporter.init()
}

const exportHTML = (book, overview) => {
addAlert(
"info",
Expand Down Expand Up @@ -224,6 +241,18 @@ export const bulkMenuModel = () => ({
},
disabled: overview => !overview.getSelected().length
},
{
title: gettext("Export selected as BITS"),
tooltip: gettext("Export selected books as BITS."),
action: overview => {
const ids = overview.getSelected()
ids.forEach(id => {
const book = overview.bookList.find(book => book.id === id)
exportBITS(book, overview)
})
},
disabled: overview => !overview.getSelected().length
},
{
title: gettext("Export selected as Epub"),
tooltip: gettext("Export selected books as Epub."),
Expand Down Expand Up @@ -289,6 +318,14 @@ export const bulkMenuModel = () => ({

export const exportMenuModel = () => ({
content: [
{
type: "action",
title: gettext("Export as BITS"),
tooltip: gettext("Export book as Book Interchange Tag Set."),
action: ({saveBook, book, overview}) => {
saveBook().then(() => exportBITS(book, overview))
}
},
{
type: "action",
title: gettext("Export as Epub"),
Expand Down

0 comments on commit afba2a3

Please sign in to comment.