Skip to content

Commit

Permalink
feat(generator): .epub initial implementation
Browse files Browse the repository at this point in the history
  • Loading branch information
Novout committed Mar 24, 2023
1 parent 44f6de7 commit 733f77c
Show file tree
Hide file tree
Showing 19 changed files with 2,350 additions and 5 deletions.
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ A Open Source Word Processor
- ✅ Graph Control
- ✅ Plugin Friendly
- ✅ Mobile & Desktop (PWA)
- ✅ PDF, DOCX, HTML and TXT Generator
- ✅ PDF, DOCX, HTML, EPUB and TXT Generator

<br>

Expand Down
1 change: 1 addition & 0 deletions packages/better-write-app/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,7 @@
"better-write-languages": "^1.0.0",
"better-write-plugin-annotations": "^1.0.0",
"better-write-plugin-core": "^1.0.0",
"better-write-plugin-exporter-epub": "^1.0.0",
"better-write-plugin-exporter-docx": "^1.0.0",
"better-write-plugin-exporter-html": "^1.0.0",
"better-write-plugin-exporter-pdf": "^1.0.0",
Expand Down
2 changes: 2 additions & 0 deletions packages/better-write-app/src/App.vue
Original file line number Diff line number Diff line change
Expand Up @@ -12,12 +12,14 @@
import { TxtPlugin } from 'better-write-plugin-exporter-txt'
import { HtmlPlugin } from 'better-write-plugin-exporter-html'
import { AnnotationsPlugin } from 'better-write-plugin-annotations'
import { EpubPlugin } from 'better-write-plugin-exporter-epub'
useStart([
ThemePlugin(),
ImporterPlugin(),
PDFPlugin(),
DocxPlugin(),
EpubPlugin(),
TxtPlugin(),
HtmlPlugin(),
AnnotationsPlugin(),
Expand Down
4 changes: 4 additions & 0 deletions packages/better-write-app/src/components/icons/IconEPUB.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@

<template>
<svg xmlns="http://www.w3.org/2000/svg" width="1em" height="1em" viewBox="0 0 24 24"><path fill="currentColor" d="M20 2H4v20h16V2zM6 4h5v8l-2.5-1.5L6 12V4z"></path></svg>
</template>
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,14 @@
</template>
</EditorHeaderItem>
<EditorHeaderItemDiv />
<EditorHeaderItem
:text="t('editor.bar.epub.generate')"
@action="onEPUBGenerate"
>
<template #icon>
<IconEPUB class="mr-2 w-6 h-6" />
</template>
</EditorHeaderItem>
<EditorHeaderItem
:text="t('editor.bar.txt.generate')"
@action="plugin.emit('plugin-txt-generate')"
Expand Down Expand Up @@ -107,6 +115,12 @@
plugin.emit('plugin-html-generate')
}
const onEPUBGenerate = async () => {
await storage.normalize()
plugin.emit('plugin-epub-generate')
}
const onPDFGenerate = async () => {
if (PROJECT.type === 'creative') {
ABSOLUTE.pdf.generate = true
Expand Down
4 changes: 4 additions & 0 deletions packages/better-write-languages/src/en-US/editor.ts
Original file line number Diff line number Diff line change
Expand Up @@ -195,6 +195,10 @@ export default {
generate: 'Generate (.PDF)',
configuration: 'Configure (.PDF)',
},
epub: {
generate: 'Generate (.EPUB)',
table: 'Table of Content',
},
txt: {
generate: 'Generate (.TXT)',
},
Expand Down
4 changes: 4 additions & 0 deletions packages/better-write-languages/src/pt-BR/editor.ts
Original file line number Diff line number Diff line change
Expand Up @@ -195,6 +195,10 @@ export default {
generate: 'Gerar (.PDF)',
configuration: 'Configurar (.PDF)',
},
epub: {
generate: 'Gerar (.EPUB)',
table: 'Tabela de Conteúdos',
},
txt: {
generate: 'Gerar (.TXT)',
},
Expand Down
12 changes: 12 additions & 0 deletions packages/better-write-plugin-core/src/on.ts
Original file line number Diff line number Diff line change
Expand Up @@ -329,6 +329,17 @@ export const externals = () => {
})
}

const PluginEpubGenerate = (
emitter: PluginTypes.PluginEmitter,
content: PluginTypes.PluginContentOn
) => {
emitter.on('plugin-epub-generate', () => {
const created = content[0]

created && created()
})
}

const PluginHtmlGenerate = (
emitter: PluginTypes.PluginEmitter,
content: PluginTypes.PluginContentOn
Expand Down Expand Up @@ -459,6 +470,7 @@ export const externals = () => {
PluginPDFPreview,
PluginPDFGenerate,
PluginPDFInit,
PluginEpubGenerate,
PluginDocxGenerate,
PluginTxtGenerate,
PluginHtmlGenerate,
Expand Down
6 changes: 6 additions & 0 deletions packages/better-write-plugin-exporter-epub/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
.vscode
.DS_Store

node_modules
dist
yarn-error.log
6 changes: 6 additions & 0 deletions packages/better-write-plugin-exporter-epub/.prettierrc.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
{
"tabWidth": 2,
"singleQuote": true,
"semi": false,
"vueIndentScriptAndStyle": true
}
26 changes: 26 additions & 0 deletions packages/better-write-plugin-exporter-epub/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
{
"name": "better-write-plugin-exporter-epub",
"version": "1.0.0",
"author": "Novout",
"license": "MIT",
"main": "dist/index.js",
"module": "dist/index.mjs",
"types": "dist/index.d.ts",
"scripts": {
"test": "echo \"Error: no test specified\"",
"build": "tsup",
"lint": "prettier --write src/"
},
"files": [
"dist/**/*",
"package.json",
"LICENSE",
"README.md"
],
"dependencies": {
"better-write-plugin-core": "^1.0.0",
"better-write-types": "^1.0.0",
"better-write-contenteditable-ast": "^1.0.0",
"epub-gen-memory": "1.0.10"
}
}
6 changes: 6 additions & 0 deletions packages/better-write-plugin-exporter-epub/src/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
import { PluginTypes } from 'better-write-types'
import { createPlugin } from 'better-write-plugin-core'
import { PluginEpubSet } from './set'

export const EpubPlugin = (): PluginTypes.Plugin =>
createPlugin({ name: 'epub' }, [PluginEpubSet])
136 changes: 136 additions & 0 deletions packages/better-write-plugin-exporter-epub/src/set.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,136 @@
import { saveAs } from 'file-saver'
import { ContextState, Entity, PluginTypes } from 'better-write-types'
import { On } from 'better-write-plugin-core'
import { getRows, parse } from 'better-write-contenteditable-ast'
import EPUB, { Chapter } from 'epub-gen-memory/bundle'
import { getStyles } from './styles'

export const PluginEpubSet = (
emitter: PluginTypes.PluginEmitter,
stores: PluginTypes.PluginStores,
hooks: PluginTypes.PluginHooks
) => {
const entities = () => {
const headingOne = (entity: Entity) => {
return hooks.substitution.purge(entity.raw)
}

const headingTwo = (entity: Entity) => {
return `<h2>${hooks.substitution.purge(entity.raw)}</h2>`
}

const headingThree = (entity: Entity) => {
return `<h3>${hooks.substitution.purge(entity.raw)}</h3>`
}

const paragraph = (entity: Entity): string[] => {
if (
hooks.env.emptyLine() === entity.raw ||
entity.raw === '' ||
entity.raw === ' '
)
return [lineBreak()]

return getRows(entity.raw).map((row) => {
const target = parse(hooks.substitution.purge(row))

return target.reduce((acc, item) => {
return (acc += item.text.trim() ? `<span>${item.text}</span>` : '')
}, '')
})
}

const pageBreak = () => {
return `<span style="page-break-after: always"></span>`
}

const lineBreak = () => {
return '<span style="width: 100%;padding-top: 1rem;border: none;"></span>'
}

return {
paragraph,
headingOne,
headingTwo,
headingThree,
pageBreak,
lineBreak,
}
}

const contents = (): Chapter[] => {
const chapters: Chapter[] = []

stores.PROJECT.pages.forEach((page: ContextState) => {
const chapter = {
title: '',
content: '',
}

page.entities.forEach((entity: Entity) => {
switch (entity.type) {
case 'paragraph':
case 'list':
case 'checkbox':
entities()
.paragraph(entity)
?.forEach(
(paragraph) => (chapter.content += `<p>${paragraph}</p>`)
)
break
case 'heading-one':
chapter.title = entities().headingOne(entity)
break
case 'heading-two':
chapter.content += entities().headingTwo(entity)
break
case 'heading-three':
chapter.content += entities().headingThree(entity)
break
case 'page-break':
chapter.content += entities().pageBreak()
break
case 'line-break':
chapter.content += entities().lineBreak()
break
}
})

chapters.push(chapter)
})

return chapters
}

const generate = () => {
EPUB(
{
title: stores.PROJECT.nameRaw,
author: stores.PROJECT.creator,
description: stores.PROJECT.subject,
publisher: stores.PROJECT.producer,
tocTitle: hooks.i18n.t('editor.bar.epub.table') ?? undefined,
date: new Date().toString(),
css: getStyles(stores, hooks),
cover: stores.PDF.styles.base.background.data ?? undefined,
},
contents()
).then(
(content) => download(content),
(_) => hooks.toast.error(hooks.i18n.t('toast.generics.error'))
)
}

const download = (blob: Blob) => {
saveAs(blob, hooks.project.utils().exportFullName('epub'))

hooks.toast.success(hooks.i18n.t('toast.project.epub.generate'))
}

On.externals().PluginEpubGenerate(emitter, [
() => {
generate()
},
() => {},
])
}
27 changes: 27 additions & 0 deletions packages/better-write-plugin-exporter-epub/src/styles.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
import { PluginTypes } from 'better-write-types'

export const getStyles = (
stores: PluginTypes.PluginStores,
hooks: PluginTypes.PluginHooks
): string => `* {
margin: 0;
padding: 0;
outline: 0;
font-feature-settings: 'ss02' on, 'ss01' on;
-webkit-font-smoothing: antialiased;
}
p {
font-size: 18px;
text-indent: 1.5rem;
color: black;
}
p > a {
text-decoration: none;
color: black !important;
}
h1, h2, h3 {
text-align: center;
}`
45 changes: 45 additions & 0 deletions packages/better-write-plugin-exporter-epub/tsconfig.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
{
"include": [
"src/types/index.ts",
"src/**/*.ts",
"test/**/*.ts",
"demo/**/*.ts",
"demo/**/*.d.ts",
"demo/**/*.tsx",
"demo/**/*.vue"
],
"exclude": ["dist", "node_modules"],
"compilerOptions": {
"baseUrl": ".",
"rootDir": ".",
"outDir": "dist",
"sourceMap": false,
"noEmit": true,
"declaration": true,
"target": "esnext",
"module": "esnext",
"moduleResolution": "node",
"skipLibCheck": true,
"noUnusedLocals": true,
"strictNullChecks": true,
"noImplicitAny": true,
"noImplicitThis": true,
"noImplicitReturns": true,
"suppressImplicitAnyIndexErrors": true,
"strict": true,
"isolatedModules": false,
"experimentalDecorators": true,
"resolveJsonModule": true,
"esModuleInterop": true,
"removeComments": false,
"strictPropertyInitialization": false,
"jsx": "preserve",
"lib": ["esnext", "dom"],
"types": ["node"],
"plugins": [
{
"name": "@vuedx/typescript-plugin-vue"
}
]
}
}
9 changes: 9 additions & 0 deletions packages/better-write-plugin-exporter-epub/tsup.config.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
import { defineConfig } from 'tsup';

export default defineConfig({
entry: ['src/index.ts'],
format: ['esm', 'cjs'],
clean: true,
dts: true,
external: ['file-saver', 'epub-gen-memory']
})

0 comments on commit 733f77c

Please sign in to comment.