UNPKG

propresenter-parser

Version:

Parses ProPresenter 4, 5, and 6 files to extract the data, and can build ProPresenter 5 and 6 files

237 lines (236 loc) 9.21 kB
import { XMLBuilder } from 'fast-xml-parser'; import { Base64 } from 'js-base64'; import { IProTransitionType } from '../shared.model'; import * as Utils from '../utils'; export class v5Builder { xmlBuilder; options; defaultTransitionObj = { '@transitionType': IProTransitionType.None, '@transitionDuration': 1, '@motionEnabled': 0, '@motionDuration': 20, '@motionSpeed': 100, }; constructor(options) { this.xmlBuilder = new XMLBuilder({ attributeNamePrefix: '@', format: true, ignoreAttributes: false, processEntities: false, suppressUnpairedNode: false, unpairedTags: [ 'arrangements', 'timeCues', 'mediaTracks', 'bibleReference', 'cues', '_-RVProTransitionObject-_transitionObject', '_-RVRect3D-_position', 'NSColor', 'NSNumber', 'NSMutableString', ], }); this.options = options; const defaultProperties = { album: '', artist: '', author: '', category: 'Song', ccliDisplay: false, notes: '', publisher: '', height: 720, width: 1280, }; this.options.properties = { ...defaultProperties, ...this.options.properties }; const defaultSlideTextFormatting = { textColor: { r: 255, g: 255, b: 255 }, textPadding: 20, }; this.options.slideTextFormatting = { ...defaultSlideTextFormatting, ...this.options.slideTextFormatting, }; } build() { const documentObj = { RVPresentationDocument: { '@CCLIArtistCredits': this.options.properties.artist, '@CCLICopyrightInfo': this.options.properties.copyrightYear ?? '', '@CCLIDisplay': this.options.properties.ccliDisplay ? 1 : 0, '@CCLILicenseNumber': this.options.properties.ccliNumber ?? '', '@CCLIPublisher': this.options.properties.publisher, '@CCLISongTitle': this.options.properties.title, '@album': this.options.properties.album, '@artist': this.options.properties.artist, '@author': this.options.properties.author, '@category': this.options.properties.category, '@notes': this.options.properties.notes, '@lastDateUsed': Utils.getIsoDateString(), '@height': this.options.properties.height, '@width': this.options.properties.width, '@backgroundColor': '0 0 0 1', '@creatorCode': 0, '@chordChartPath': '', '@docType': 0, '@drawingBackgroundColor': 0, '@resourcesDirectory': '', '@usedCount': 0, '@versionNumber': 500, timeline: { '@timeOffSet': 0, '@selectedMediaTrackIndex': 0, '@unitOfMeasure': 60, '@duration': 0, '@loop': 0, timeCues: { '@containerClass': 'NSMutableArray', }, mediaTracks: { '@containerClass': 'NSMutableArray', }, }, bibleReference: { '@containerClass': 'NSMutableDictionary', }, '_-RVProTransitionObject-_transitionObject': this.getTransitions(), groups: { '@containerClass': 'NSMutableArray', RVSlideGrouping: this.buildSlideGroups(), }, arrangements: { '@containerClass': 'NSMutableArray', RVSongArrangement: [], }, }, }; return this.xmlBuilder.build(documentObj).trim(); } getTransitions() { if (this.options.transitions) { const transitionsCopy = { ...this.defaultTransitionObj }; transitionsCopy['@transitionDuration'] = this.options.transitions.duration; transitionsCopy['@transitionType'] = this.options.transitions.type; return transitionsCopy; } return this.defaultTransitionObj; } buildSlideGroups() { const xmlSlideGroups = []; for (let i = 0; i < this.options.slideGroups.length; i++) { const group = this.options.slideGroups[i]; xmlSlideGroups.push({ '@name': group.label, '@uuid': Utils.getUniqueID(), '@color': Utils.normalizeColorToRgbaString(group.groupColor ?? '0 0 0 0'), '@serialization-array-index': i, slides: { '@containerClass': 'NSMutableArray', RVDisplaySlide: this.buildSlidesForGroup(group), }, }); } return xmlSlideGroups; } buildSlidesForGroup(thisGroup) { const xmlSlides = []; for (let i = 0; i < thisGroup.slides.length; i++) { const slide = thisGroup.slides[i]; let highlightColor = '0 0 0 0'; let label = ''; let text; if (typeof slide === 'string') { text = slide; } else { highlightColor = Utils.normalizeColorToRgbaString(slide.slideColor ?? highlightColor); label = slide.label ?? ''; text = slide.text; } xmlSlides.push({ '@backgroundColor': '0 0 0 0', '@enabled': 1, '@highlightColor': highlightColor, '@hotKey': '', '@label': label, '@notes': '', '@slideType': 1, '@sort_index': i, '@UUID': Utils.getUniqueID(), '@drawingBackgroundColor': 0, '@chordChartPath': '', '@serialization-array-index': i, cues: { '@containerClass': 'NSMutableArray', }, displayElements: { '@containerClass': 'NSMutableArray', RVTextElement: [this.buildTextElement(text)], }, '_-RVProTransitionObject-_transitionObject': this.defaultTransitionObj, }); } return xmlSlides; } buildTextElement(text) { const rtfText = Utils.formatRtf(text, this.options.slideTextFormatting.fontName, this.options.slideTextFormatting.textSize, Utils.normalizeColorToRgbObj(this.options.slideTextFormatting.textColor)); return { '@displayDelay': 0, '@displayName': 'Default', '@locked': 0, '@persistent': 0, '@typeID': 0, '@fromTemplate': 0, '@bezelRadius': 0, '@drawingFill': 0, '@drawingShadow': 1, '@drawingStroke': 0, '@fillColor': '1 1 1 1', '@rotation': 0, '@source': '', '@adjustsHeightToFit': 0, '@verticalAlignment': 0, '@RTFData': Base64.encode(rtfText), '@revealType': 0, '@serialization-array-index': 0, stroke: { '@containerClass': 'NSMutableDictionary', NSColor: { '@serialization-native-value': '0 0 0 1', '@serialization-dictionary-key': 'RVShapeElementStrokeColorKey', }, NSNumber: { '@serialization-native-value': 1, '@serialization-dictionary-key': 'RVShapeElementStrokeWidthKey', }, }, '_-D-_serializedShadow': { '@containerClass': 'NSMutableDictionary', NSMutableString: { '@serialization-native-value': `{3.4641016, -2}`, '@serialization-dictionary-key': 'shadowOffset', }, NSNumber: { '@serialization-native-value': 4, '@serialization-dictionary-key': 'shadowBlurRadius', }, NSColor: { '@serialization-native-value': '0 0 0 1', '@serialization-dictionary-key': 'shadowColor', }, }, '_-RVRect3D-_position': this.getElementPosition(), }; } getElementPosition() { return { '@x': this.options.slideTextFormatting.textPadding, '@y': this.options.slideTextFormatting.textPadding, '@z': 0, '@width': this.options.properties.width - this.options.slideTextFormatting.textPadding * 2, '@height': this.options.properties.height - this.options.slideTextFormatting.textPadding * 2, }; } }