Skip to content

Commit

Permalink
feat: generate cover
Browse files Browse the repository at this point in the history
  • Loading branch information
yjl9903 committed Mar 2, 2023
1 parent 1ad2747 commit cc88ee5
Show file tree
Hide file tree
Showing 7 changed files with 85 additions and 17 deletions.
1 change: 1 addition & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -41,3 +41,4 @@ jobs:
pnpm test:ci
unzip epubcheck-5.0.0.zip
java -jar ./epubcheck-5.0.0/epubcheck.jar .output/test-core.epub
java -jar ./epubcheck-5.0.0/epubcheck.jar .output/test-cover.epub
2 changes: 1 addition & 1 deletion packages/core/src/bundle/bundle.ts
Original file line number Diff line number Diff line change
Expand Up @@ -156,7 +156,7 @@ export function makePackageDocument(opf: PackageDocument): string {
{
'@_refines': '#' + opf.creator().uid,
'@_property': 'file-as',
'#text': opf.creator()?.fileAs ?? ''
'#text': opf.creator()?.fileAs ?? opf.creator().name
}
]
};
Expand Down
4 changes: 4 additions & 0 deletions packages/core/src/epub/item.ts
Original file line number Diff line number Diff line change
Expand Up @@ -123,6 +123,10 @@ export abstract class Item {
return this.file;
}

public relative(from: string) {
return path.relative(path.dirname(from), this.file);
}

public update(info: Partial<{ properties: string }>) {
if (info.properties) {
this._properties = info.properties;
Expand Down
19 changes: 16 additions & 3 deletions packages/core/src/theme.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,22 @@
export interface Theme {
name: string;
import { Image } from './epub';
import { XHTMLBuilder } from './render';

type Prettify<T> = {
[K in keyof T]: T[K];
} & {};

pages: string[];
export type PageTemplate<T = any> = (file: string, props: T) => XHTMLBuilder;

export interface Theme<P extends Record<string, PageTemplate<any>>> {
name: string;

styles: string[];

images: string[];

pages: Prettify<
{
cover(file: string, props: { image: Image }): XHTMLBuilder;
} & P
>;
}
56 changes: 45 additions & 11 deletions packages/epubook/src/epubook.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
import {
type Author,
type Theme,
type PageTemplate,
type ImageMediaType,
Html,
Epub,
Image,
ImageExtension
Expand All @@ -14,30 +16,35 @@ import { loadTheme } from './theme';
export interface EpubookOption {
title: string;

description: string;

language: string;

author: Author[];

theme: string;
}

const TextDir = 'text';
const ImageDir = 'images';

export class Epubook {
export class Epubook<P extends Record<string, PageTemplate> = {}> {
private container: Epub;

private option: EpubookOption;

private theme!: Theme;
private theme!: Theme<P>;

private content: Html[] = [];

private counter = {
image: 0,
page: 0
private counter: Record<string, number> = {
image: 1
};

private constructor(option: Partial<EpubookOption>) {
this.option = {
title: '',
title: 'unknown',
description: 'unknown',
language: 'zh-CN',
author: [{ name: 'unknown' }],
theme: '@epubook/theme-default',
Expand All @@ -47,13 +54,15 @@ export class Epubook {
this.container = new Epub(this.option);
}

public static async create(option: Partial<EpubookOption>) {
public static async create<P extends Record<string, PageTemplate> = {}>(
option: Partial<EpubookOption>
): Promise<Epubook<P>> {
const epubook = new Epubook(option);
return await epubook.loadTheme();
return (await epubook.loadTheme()) as Epubook<P>;
}

private async loadTheme() {
await loadTheme(this.option.theme);
this.theme = (await loadTheme(this.option.theme)) as Theme<P>;
return this;
}

Expand Down Expand Up @@ -89,6 +98,7 @@ export class Epubook {
typeof img === 'string'
? await this.loadImage(`${ImageDir}/image-${this.counter.image}.${ext}`, img)
: await this.loadImage(`${ImageDir}/image-${this.counter.image}.${ext}`, ext!);
this.counter.image++;
return image;
}

Expand All @@ -104,14 +114,31 @@ export class Epubook {
: await this.loadImage(`${ImageDir}/cover.${ext}`, ext!);
if (image) {
image.update({ properties: 'cover-image' });
const page = this.page('cover', { image }, { file: `${TextDir}/cover.xhtml` });
this.content.unshift(page);
return image;
} else {
return undefined;
}
}

public page() {
return this;
public page<T extends string & keyof Theme<P>['pages']>(
template: T,
props: Parameters<Theme<P>['pages'][T]>[1],
option: { file?: string } = {}
) {
const render: Theme<P>['pages'][T] = this.theme.pages[template];
const file = option.file ?? `${TextDir}/${template}-${this.counter[template] ?? 1}.xhtml`;
const builder = render(file, props);
const content = builder.build();
if (!this.counter[template]) {
this.counter[template] = 2;
} else {
this.counter[template]++;
}
const xhtml = new Html(file, content);
this.container.addItem(xhtml);
return xhtml;
}

public toc() {
Expand All @@ -122,11 +149,18 @@ export class Epubook {
return this;
}

private async preBundle() {
this.container.toc(this.content.map((c) => ({ text: c.id(), item: c })));
this.container.spine(...this.content);
}

public async bundle() {
this.preBundle();
return this.container.bundle();
}

public async writeFile(file: string) {
this.preBundle();
return this.container.writeFile(file);
}
}
17 changes: 16 additions & 1 deletion packages/epubook/src/theme.ts
Original file line number Diff line number Diff line change
@@ -1 +1,16 @@
export async function loadTheme(pkg: string) {}
import { Theme, XHTMLBuilder } from '@epubook/core';

export async function loadTheme(pkg: string): Promise<Theme<{}>> {
return {
name: 'theme-default',
images: [],
styles: [],
pages: {
cover(file, { image }) {
return new XHTMLBuilder()
.title('cover')
.body({ tag: 'img', attrs: { src: image.relative(file) } });
}
}
};
}
3 changes: 2 additions & 1 deletion packages/epubook/test/index.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,8 @@ import { Epubook } from '../src';
describe('epubook', () => {
it('should write epub with cover', async () => {
const book = await Epubook.create({
title: 'cover'
title: 'cover',
description: 'This is generated for testing cover image'
});

await book.cover('../../assets/cover.jpg');
Expand Down

0 comments on commit cc88ee5

Please sign in to comment.