export docx
This commit is contained in:
14
package.json
14
package.json
@@ -34,14 +34,19 @@
|
|||||||
"zone.js": "~0.10.2"
|
"zone.js": "~0.10.2"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
|
"@angular-devkit/architect": "<0.900 || ^0.900.0-0 || ^9.0.0-0",
|
||||||
"@angular-devkit/build-angular": "~0.901.1",
|
"@angular-devkit/build-angular": "~0.901.1",
|
||||||
"@angular/cli": "~9.1.1",
|
"@angular/cli": "~9.1.1",
|
||||||
"@angular/compiler-cli": "~9.1.2",
|
"@angular/compiler-cli": "~9.1.2",
|
||||||
"@angular/language-service": "~9.1.2",
|
"@angular/language-service": "~9.1.2",
|
||||||
"@types/node": "^12.11.1",
|
|
||||||
"@types/jasmine": "~3.3.8",
|
"@types/jasmine": "~3.3.8",
|
||||||
"@types/jasminewd2": "~2.0.3",
|
"@types/jasminewd2": "~2.0.3",
|
||||||
|
"@types/node": "^12.12.36",
|
||||||
"codelyzer": "^5.1.2",
|
"codelyzer": "^5.1.2",
|
||||||
|
"firebase-tools": "^7.12.0",
|
||||||
|
"fuzzy": "^0.1.3",
|
||||||
|
"inquirer": "^6.2.2",
|
||||||
|
"inquirer-autocomplete-prompt": "^1.0.1",
|
||||||
"jasmine-core": "~3.4.0",
|
"jasmine-core": "~3.4.0",
|
||||||
"jasmine-spec-reporter": "~4.2.1",
|
"jasmine-spec-reporter": "~4.2.1",
|
||||||
"karma": "~4.1.0",
|
"karma": "~4.1.0",
|
||||||
@@ -52,11 +57,6 @@
|
|||||||
"protractor": "~5.4.0",
|
"protractor": "~5.4.0",
|
||||||
"ts-node": "~7.0.0",
|
"ts-node": "~7.0.0",
|
||||||
"tslint": "~5.15.0",
|
"tslint": "~5.15.0",
|
||||||
"typescript": "~3.8.3",
|
"typescript": "~3.8.3"
|
||||||
"@angular-devkit/architect": "<0.900 || ^0.900.0-0 || ^9.0.0-0",
|
|
||||||
"firebase-tools": "^7.12.0",
|
|
||||||
"fuzzy": "^0.1.3",
|
|
||||||
"inquirer": "^6.2.2",
|
|
||||||
"inquirer-autocomplete-prompt": "^1.0.1"
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,10 +1,170 @@
|
|||||||
import {Injectable} from '@angular/core';
|
import {Injectable} from '@angular/core';
|
||||||
|
import {Document, HeadingLevel, Packer, Paragraph} from 'docx';
|
||||||
|
import {ShowService} from './show.service';
|
||||||
|
import {ShowTypePipe} from '../../../widget-modules/pipes/show-type-translater/show-type.pipe';
|
||||||
|
import {first} from 'rxjs/operators';
|
||||||
|
import {ShowSongService} from './show-song.service';
|
||||||
|
import {Line, LineType, Section, TextRenderingService} from '../../songs/services/text-rendering.service';
|
||||||
|
import {Song} from '../../songs/services/song';
|
||||||
|
import {SongService} from '../../songs/services/song.service';
|
||||||
|
import {ShowSong} from './showSong';
|
||||||
|
import {Show} from './show';
|
||||||
|
import {ChordMode} from '../../../widget-modules/components/song-text/song-text.component';
|
||||||
|
|
||||||
|
|
||||||
@Injectable({
|
@Injectable({
|
||||||
providedIn: 'root'
|
providedIn: 'root'
|
||||||
})
|
})
|
||||||
export class DocxService {
|
export class DocxService {
|
||||||
|
|
||||||
constructor() {
|
constructor(
|
||||||
|
private showService: ShowService,
|
||||||
|
private showSongService: ShowSongService,
|
||||||
|
private songService: SongService,
|
||||||
|
private textRenderingService: TextRenderingService
|
||||||
|
) {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public async create(showId: string): Promise<any> {
|
||||||
|
const {show, songs} = await this.prepareData(showId);
|
||||||
|
|
||||||
|
const paragraphs = [
|
||||||
|
...this.renderTitle(show),
|
||||||
|
...this.renderSongs(songs),
|
||||||
|
];
|
||||||
|
console.log(paragraphs);
|
||||||
|
|
||||||
|
const document = this.prepareNewDocument();
|
||||||
|
document.addSection({
|
||||||
|
properties: {top: 400, bottom: 400, left: 400, right: 400},
|
||||||
|
children: paragraphs,
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
const blob = await Packer.toBlob(document);
|
||||||
|
|
||||||
|
// saveAs from FileSaver will download the file
|
||||||
|
this.saveAs(blob, 'example.docx');
|
||||||
|
}
|
||||||
|
|
||||||
|
private prepareNewDocument(): Document {
|
||||||
|
return new Document({
|
||||||
|
creator: 'TODO',
|
||||||
|
title: 'Gottesdienst',
|
||||||
|
description: '... mit Beschreibung',
|
||||||
|
styles: {
|
||||||
|
paragraphStyles: [
|
||||||
|
{
|
||||||
|
id: 'songtext',
|
||||||
|
name: 'Song Text',
|
||||||
|
basedOn: 'Normal',
|
||||||
|
next: 'Normal',
|
||||||
|
quickFormat: true,
|
||||||
|
run: {font: 'courier new'},
|
||||||
|
paragraph: {indent: {left: 0}},
|
||||||
|
},
|
||||||
|
]
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
private renderSongs(songs: { showSong: ShowSong; song: Song; sections: Section[] }[]): Paragraph[] {
|
||||||
|
return songs.reduce((p, song) => [...p, ...this.renderSong(song.showSong, song.song, song.sections)], [])
|
||||||
|
}
|
||||||
|
|
||||||
|
private renderSong(showSong: ShowSong, song: Song, sections: Section[]): Paragraph[] {
|
||||||
|
const songTitle = this.renderSongTitle(song);
|
||||||
|
const songText = this.renderSongText(sections, showSong.chordMode);
|
||||||
|
|
||||||
|
return [
|
||||||
|
songTitle,
|
||||||
|
...songText
|
||||||
|
]
|
||||||
|
}
|
||||||
|
|
||||||
|
private renderSongText(sections: Section[], chordMode: ChordMode) {
|
||||||
|
return sections.reduce((p, section) => [...p, ...this.renderSection(section, chordMode)], []);
|
||||||
|
}
|
||||||
|
|
||||||
|
private renderSongTitle(song: Song): Paragraph {
|
||||||
|
return new Paragraph({
|
||||||
|
text: song.title,
|
||||||
|
heading: HeadingLevel.HEADING_2,
|
||||||
|
thematicBreak: true,
|
||||||
|
spacing: {
|
||||||
|
before: 200,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
private renderSection(section: Section, chordMode: ChordMode): Paragraph[] {
|
||||||
|
return section.lines
|
||||||
|
.filter(line => {
|
||||||
|
if (line.type === LineType.text) return true;
|
||||||
|
switch (chordMode) {
|
||||||
|
case 'show':
|
||||||
|
return true;
|
||||||
|
case 'hide':
|
||||||
|
return false;
|
||||||
|
case 'onlyFirst':
|
||||||
|
return section.number === 0;
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.map((line, i) => this.renderLine(line, i === 0));
|
||||||
|
}
|
||||||
|
|
||||||
|
private renderLine(line: Line, isFirstLine: boolean): Paragraph {
|
||||||
|
const spacing = isFirstLine ? {before: 200} : {};
|
||||||
|
return new Paragraph({
|
||||||
|
text: line.text,
|
||||||
|
style: 'songtext',
|
||||||
|
spacing
|
||||||
|
});
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
private renderTitle(show: Show): Paragraph[] {
|
||||||
|
const type = new ShowTypePipe().transform(show.showType);
|
||||||
|
|
||||||
|
const songTitle = new Paragraph({
|
||||||
|
text: type,
|
||||||
|
heading: HeadingLevel.HEADING_1,
|
||||||
|
thematicBreak: true,
|
||||||
|
});
|
||||||
|
|
||||||
|
return [songTitle]
|
||||||
|
}
|
||||||
|
|
||||||
|
private async prepareData(showId: string): Promise<{ songs: ({ showSong: ShowSong, song: Song, sections: Section[] })[]; show: Show }> {
|
||||||
|
const show = await this.showService.read$(showId).pipe(first()).toPromise();
|
||||||
|
|
||||||
|
const showSongs = await this.showSongService.list$(showId).pipe(first()).toPromise();
|
||||||
|
const songsAsync = await showSongs.map(async showSong => {
|
||||||
|
const song = await this.songService.read(showSong.songId).pipe(first()).toPromise();
|
||||||
|
const sections = this.textRenderingService.parse(song.text);
|
||||||
|
return {
|
||||||
|
showSong,
|
||||||
|
song,
|
||||||
|
sections
|
||||||
|
}
|
||||||
|
})
|
||||||
|
const songs = await Promise.all(songsAsync);
|
||||||
|
return {songs, show};
|
||||||
|
}
|
||||||
|
|
||||||
|
private saveAs(blob, fileName) {
|
||||||
|
const a = document.createElement('a') as any;
|
||||||
|
document.body.appendChild(a);
|
||||||
|
a.style = 'display: none';
|
||||||
|
|
||||||
|
const url = window.URL.createObjectURL(blob);
|
||||||
|
a.href = url;
|
||||||
|
a.download = fileName;
|
||||||
|
a.click();
|
||||||
|
window.URL.revokeObjectURL(url);
|
||||||
|
|
||||||
|
document.body.removeChild(a);
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -31,6 +31,9 @@
|
|||||||
<button (click)="onArchive(false)" *ngIf="show.archived" mat-button>Wiederherstellen</button>
|
<button (click)="onArchive(false)" *ngIf="show.archived" mat-button>Wiederherstellen</button>
|
||||||
<button (click)="onPublish(true)" *ngIf="!show.published" mat-button>Veröffentlichen</button>
|
<button (click)="onPublish(true)" *ngIf="!show.published" mat-button>Veröffentlichen</button>
|
||||||
<button (click)="onPublish(false)" *ngIf="show.published" mat-button>Veröffentlichung zurückziehen</button>
|
<button (click)="onPublish(false)" *ngIf="show.published" mat-button>Veröffentlichung zurückziehen</button>
|
||||||
|
|
||||||
|
<button (click)="onDownload()" mat-button>Herunterladen</button>
|
||||||
|
|
||||||
</app-button-row>
|
</app-button-row>
|
||||||
</app-card>
|
</app-card>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -9,6 +9,7 @@ import {Song} from '../../songs/services/song';
|
|||||||
import {MatSelectChange} from '@angular/material/select';
|
import {MatSelectChange} from '@angular/material/select';
|
||||||
import {ShowSongService} from '../services/show-song.service';
|
import {ShowSongService} from '../services/show-song.service';
|
||||||
import {ShowSong} from '../services/showSong';
|
import {ShowSong} from '../services/showSong';
|
||||||
|
import {DocxService} from '../services/docx.service';
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'app-show',
|
selector: 'app-show',
|
||||||
@@ -27,6 +28,7 @@ export class ShowComponent implements OnInit {
|
|||||||
private showService: ShowService,
|
private showService: ShowService,
|
||||||
private songService: SongService,
|
private songService: SongService,
|
||||||
private showSongService: ShowSongService,
|
private showSongService: ShowSongService,
|
||||||
|
private docxService: DocxService,
|
||||||
) {
|
) {
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -83,4 +85,8 @@ export class ShowComponent implements OnInit {
|
|||||||
if (show.reported) return 'gemeldet';
|
if (show.reported) return 'gemeldet';
|
||||||
return 'entwurf';
|
return 'entwurf';
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public async onDownload(): Promise<void> {
|
||||||
|
await this.docxService.create(this.showId);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -12,5 +12,5 @@
|
|||||||
"sizes": "192x192",
|
"sizes": "192x192",
|
||||||
"type": "image/png"
|
"type": "image/png"
|
||||||
}
|
}
|
||||||
],
|
]
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,7 +2,9 @@
|
|||||||
"extends": "./tsconfig.json",
|
"extends": "./tsconfig.json",
|
||||||
"compilerOptions": {
|
"compilerOptions": {
|
||||||
"outDir": "./out-tsc/app",
|
"outDir": "./out-tsc/app",
|
||||||
"types": []
|
"types": [
|
||||||
|
"node"
|
||||||
|
]
|
||||||
},
|
},
|
||||||
"files": [
|
"files": [
|
||||||
"src/main.ts",
|
"src/main.ts",
|
||||||
|
|||||||
Reference in New Issue
Block a user