propresenter-parser
Version:
Parses ProPresenter 4, 5, and 6 files to extract the data, and can build ProPresenter 5 and 6 files
282 lines (281 loc) • 12.7 kB
JavaScript
"use strict";
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
var desc = Object.getOwnPropertyDescriptor(m, k);
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
desc = { enumerable: true, get: function() { return m[k]; } };
}
Object.defineProperty(o, k2, desc);
}) : (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
o[k2] = m[k];
}));
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
Object.defineProperty(o, "default", { enumerable: true, value: v });
}) : function(o, v) {
o["default"] = v;
});
var __importStar = (this && this.__importStar) || (function () {
var ownKeys = function(o) {
ownKeys = Object.getOwnPropertyNames || function (o) {
var ar = [];
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
return ar;
};
return ownKeys(o);
};
return function (mod) {
if (mod && mod.__esModule) return mod;
var result = {};
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
__setModuleDefault(result, mod);
return result;
};
})();
Object.defineProperty(exports, "__esModule", { value: true });
exports.v6Builder = void 0;
const fast_xml_parser_1 = require("fast-xml-parser");
const js_base64_1 = require("js-base64");
const shared_model_1 = require("../shared.model");
const Utils = __importStar(require("../utils"));
class v6Builder {
constructor(options) {
this.winFontData = `<?xml version="1.0" encoding="utf-16"?><RVFont xmlns:i="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://schemas.datacontract.org/2004/07/ProPresenter.Common"><Kerning>0</Kerning><LineSpacing>0</LineSpacing><OutlineColor xmlns:d2p1="http://schemas.datacontract.org/2004/07/System.Windows.Media"><d2p1:A>255</d2p1:A><d2p1:B>0</d2p1:B><d2p1:G>0</d2p1:G><d2p1:R>0</d2p1:R><d2p1:ScA>1</d2p1:ScA><d2p1:ScB>0</d2p1:ScB><d2p1:ScG>0</d2p1:ScG><d2p1:ScR>0</d2p1:ScR></OutlineColor><OutlineWidth>0</OutlineWidth><Variants>Normal</Variants></RVFont>`;
this.defaultTransitionObj = {
'@rvXMLIvarName': 'transitionObject',
'@transitionType': shared_model_1.IProTransitionType.None,
'@transitionDirection': 0,
'@transitionDuration': 1,
'@motionEnabled': false,
'@motionDuration': 0,
'@motionSpeed': 0,
'@groupIndex': 0,
'@orderIndex': 0,
'@slideBuildAction': 0,
'@slideBuildDelay': 0,
};
this.xmlBuilder = new fast_xml_parser_1.XMLBuilder({
attributeNamePrefix: '@',
format: true,
ignoreAttributes: false,
processEntities: false,
suppressUnpairedNode: false,
suppressBooleanAttributes: false,
unpairedTags: ['array', 'RVTransition'],
});
this.options = options;
const defaultProperties = {
CCLIArtistCredits: '',
CCLIAuthor: '',
CCLIDisplay: false,
CCLIPublisher: '',
category: 'Song',
notes: '',
height: 720,
width: 1280,
};
this.options.properties = Object.assign(Object.assign({}, defaultProperties), this.options.properties);
const defaultSlideTextFormatting = {
textColor: { r: 255, g: 255, b: 255 },
textPadding: 20,
fontName: 'Arial',
textSize: 60,
textShadow: {
angle: 135,
color: { r: 0, g: 0, b: 0 },
enabled: false,
length: 7,
radius: 10,
},
};
this.options.slideTextFormatting = Object.assign(Object.assign({}, defaultSlideTextFormatting), this.options.slideTextFormatting);
}
build() {
var _a, _b;
const documentObj = {
'?xml': {
'@version': '1.0',
'@encoding': 'utf-8',
},
RVPresentationDocument: {
'@CCLIArtistCredits': this.options.properties.CCLIArtistCredits,
'@CCLIAuthor': this.options.properties.CCLIAuthor,
'@CCLICopyrightYear': (_a = this.options.properties.CCLICopyrightYear) !== null && _a !== void 0 ? _a : '',
'@CCLIDisplay': this.options.properties.CCLIDisplay,
'@CCLIPublisher': this.options.properties.CCLIPublisher,
'@CCLISongNumber': (_b = this.options.properties.CCLISongNumber) !== null && _b !== void 0 ? _b : '',
'@CCLISongTitle': this.options.properties.CCLISongTitle,
'@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',
'@buildNumber': 6016,
'@chordChartPath': '',
'@docType': 0,
'@drawingBackgroundColor': false,
'@resourcesDirectory': '',
'@selectedArrangementID': '',
'@os': 1,
'@usedCount': 0,
'@versionNumber': 600,
RVTransition: this.getTransitions(),
RVTimeline: {
'@rvXMLIvarName': 'timeline',
'@timeOffset': 0,
'@duration': 0,
'@selectedMediaTrackIndex': 0,
'@loop': false,
array: [{ '@rvXMLIvarName': 'timeCues' }, { '@rvXMLIvarName': 'mediaTracks' }],
},
array: [
{
'@rvXMLIvarName': 'groups',
RVSlideGrouping: this.buildSlideGroups(),
},
{
'@rvXMLIvarName': 'arrangements',
RVSongArrangement: [],
},
],
},
};
return this.xmlBuilder.build(documentObj).trim();
}
getTransitions() {
if (this.options.transitions) {
const transitionsCopy = Object.assign({}, this.defaultTransitionObj);
transitionsCopy['@transitionDuration'] = this.options.transitions.duration;
transitionsCopy['@transitionType'] = this.options.transitions.type;
return transitionsCopy;
}
return this.defaultTransitionObj;
}
buildSlideGroups() {
var _a;
const xmlSlideGroups = [];
for (const group of this.options.slideGroups) {
xmlSlideGroups.push({
'@name': group.label,
'@uuid': Utils.getUniqueID(),
'@color': Utils.normalizeColorToRgbaString((_a = group.groupColor) !== null && _a !== void 0 ? _a : '0 0 0 0'),
array: {
'@rvXMLIvarName': 'slides',
RVDisplaySlide: this.buildSlidesForGroup(group),
},
});
}
return xmlSlideGroups;
}
buildSlidesForGroup(thisGroup) {
var _a, _b;
const xmlSlides = [];
for (const slide of thisGroup.slides) {
let highlightColor = '0 0 0 0';
let label = '';
let text;
if (typeof slide === 'string') {
text = slide;
}
else {
highlightColor = Utils.normalizeColorToRgbaString((_a = slide.slideColor) !== null && _a !== void 0 ? _a : highlightColor);
label = (_b = slide.label) !== null && _b !== void 0 ? _b : '';
text = slide.text;
}
xmlSlides.push({
'@backgroundColor': '0 0 0 0',
'@highlightColor': highlightColor,
'@drawingBackgroundColor': false,
'@enabled': true,
'@hotKey': '',
'@label': label,
'@notes': '',
'@UUID': Utils.getUniqueID(),
'@chordChartPath': '',
array: [
{ '@rvXMLIvarName': 'cues' },
{
'@rvXMLIvarName': 'displayElements',
RVTextElement: [this.buildTextElement(text)],
},
],
});
}
return xmlSlides;
}
buildTextElement(text) {
const rtfText = Utils.formatRtf(text, this.options.slideTextFormatting.fontName, this.options.slideTextFormatting.textSize, Utils.normalizeColorToRgbObj(this.options.slideTextFormatting.textColor));
return {
'@displayName': 'Default',
'@UUID': Utils.getUniqueID(),
'@typeID': 0,
'@displayDelay': 0,
'@locked': false,
'@persistent': 0,
'@fromTemplate': false,
'@opacity': 1,
'@source': '',
'@bezelRadius': 0,
'@rotation': 0,
'@drawingFill': false,
'@drawingShadow': this.options.slideTextFormatting.textShadow.enabled,
'@drawingStroke': false,
'@fillColor': '1 1 1 0',
'@adjustsHeightToFit': false,
'@verticalAlignment': 0,
'@revealType': 0,
RVRect3D: {
'@rvXMLIvarName': 'position',
'#text': this.getTextElementPosition(),
},
shadow: { '@rvXMLIvarName': 'shadow', '#text': this.getElementShadow() },
dictionary: {
'@rvXMLIvarName': 'stroke',
NSColor: {
'@rvXMLDictionaryKey': 'RVShapeElementStrokeColorKey',
'#text': '0 0 0 1',
},
NSNumber: {
'@rvXMLDictionaryKey': 'RVShapeElementStrokeWidthKey',
'@hint': 'double',
'#text': 0,
},
},
NSString: [
{ '@rvXMLIvarName': 'PlainText', '#text': js_base64_1.Base64.encode(text) },
{ '@rvXMLIvarName': 'RTFData', '#text': js_base64_1.Base64.encode(rtfText) },
{ '@rvXMLIvarName': 'WinFlowData', '#text': js_base64_1.Base64.encode(this.getWinFlowDocument(text)) },
{ '@rvXMLIvarName': 'WinFontData', '#text': js_base64_1.Base64.encode(this.winFontData) },
],
};
}
getTextElementPosition() {
const posX = this.options.slideTextFormatting.textPadding;
const posY = this.options.slideTextFormatting.textPadding;
const width = this.options.properties.width - this.options.slideTextFormatting.textPadding * 2;
const height = this.options.properties.height - this.options.slideTextFormatting.textPadding * 2;
return `{${posX} ${posY} 0 ${width} ${height}}`;
}
getElementShadow() {
const radius = this.options.slideTextFormatting.textShadow.radius;
const color = Utils.normalizeColorToRgbaString(this.options.slideTextFormatting.textShadow.color);
const angle = this.options.slideTextFormatting.textShadow.angle;
const length = this.options.slideTextFormatting.textShadow.length;
const posX = Math.sin(angle * (Math.PI / 180)) * length;
const posY = Math.cos(angle * (Math.PI / 180)) * length;
return `${radius}|${color}|{${posX}, ${posY}}`;
}
getWinFlowDocument(text) {
const fontName = this.options.slideTextFormatting.fontName;
const size = this.options.slideTextFormatting.textSize;
const hexColor = Utils.normalizeColorToHex(this.options.slideTextFormatting.textColor);
const paragraphs = text
.split(/[\n\r]/g)
.filter((line) => line !== '')
.map((line) => `<Paragraph Margin="0,0,0,0" TextAlignment="Center" FontFamily="${fontName}" FontSize="${size}"><Run FontFamily="${fontName}" FontSize="${size}" Foreground="#${hexColor}FF" Block.TextAlignment="Center">${line}</Run></Paragraph>`)
.join('');
return `<FlowDocument TextAlignment="Center" PagePadding="5,0,5,0" AllowDrop="True" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation">${paragraphs}</FlowDocument>`;
}
}
exports.v6Builder = v6Builder;