├── databases
└── index.php
├── output
└── index.php
├── .gitignore
├── resources
├── propresenter6_group_element.xml
├── propresenter6_slide_text.txt
├── propresenter6_file_wrapper.xml
├── propresenter6_slide_element.xml
├── Amazing Grace.pro6
└── Amazing Grace expanded.pro6
├── process.php
├── config.php
├── README.md
└── functions.php
/databases/index.php:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/output/index.php:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | *.dat
2 | *.db
3 | *.zip
4 | databases/*/
5 | output/*/*.txt
6 | output/*/*.pro6
7 | test/
--------------------------------------------------------------------------------
/resources/propresenter6_group_element.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | %%SLIDES%%
4 |
5 |
--------------------------------------------------------------------------------
/resources/propresenter6_slide_text.txt:
--------------------------------------------------------------------------------
1 | {\rtf1\ansi\ansicpg1252\cocoartf1504\cocoasubrtf830
2 | {\fonttbl\f0\fswiss\fcharset0 ArialMT;}
3 | {\colortbl;\red255\green255\blue255;\red255\green255\blue255;\red51\green51\blue51;}
4 | {\*\expandedcolortbl;;\csgray\c100000;\csgenericrgb\c20000\c20000\c20000;}
5 | \pard\slleading320\pardirnatural\qc\partightenfactor0
6 |
7 | \f0\b\fs127\fsmilli63999 \cf2 \expnd0\expndtw0\kerning0
8 | \outl0\strokewidth-80 \strokec3 %%TEXT%%}
--------------------------------------------------------------------------------
/resources/propresenter6_file_wrapper.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 | %%GROUPS%%
9 |
10 |
11 |
--------------------------------------------------------------------------------
/resources/propresenter6_slide_element.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | {0 100 0 1920 864}
6 | 0.000000|0 0 0 0.3333333432674408|{4.0000002002606152, -3.9999995259110506}
7 |
8 | 0 0 0 1
9 | 1.000000
10 |
11 | %%TEXT%%
12 |
13 |
14 |
--------------------------------------------------------------------------------
/process.php:
--------------------------------------------------------------------------------
1 |
9 | * @version 0.2
10 | * @link https://github.com/jamesinglis/ew61-export
11 | */
12 |
13 | require 'config.php';
14 | require 'functions.php';
15 |
16 | $connection_songs = 'sqlite:' . $song_db_path;
17 | $dbh = new PDO('sqlite:' . $song_db_path) or die("cannot open the database");
18 | $dbh_lyrics = new PDO('sqlite:' . $song_words_db_path) or die("cannot open the database");
19 | $query = "SELECT * FROM song";
20 |
21 | if ($test_single_song_id && is_numeric($test_single_song_id)) {
22 | $query .= " WHERE rowid = " . $test_single_song_id;
23 | }
24 |
25 | $songs = [];
26 |
27 | foreach ($dbh->query($query) as $song) {
28 | $songs[$song['rowid']] = [
29 | 'id' => $song['rowid'],
30 | 'title' => process_ew_title($song['title'])
31 | ];
32 | }
33 |
34 | $counter = 0;
35 |
36 | foreach ($songs as $id => $song) {
37 | $query2 = "SELECT words FROM word WHERE song_id = $id";
38 | foreach ($dbh_lyrics->query($query2) as $lyrics) {
39 | $songs[$id]['text'] = process_ew_lyrics($lyrics['words']);
40 | if ($file_export_type === 'propresenter6') {
41 | $songs[$id]['text'] = generate_prop6_file_contents($songs[$id]);
42 | }
43 | save_text_file($songs[$id], $file_export_type);
44 | }
45 | if ($test_mode && $counter > 8) {
46 | break;
47 | }
48 | $counter++;
49 | }
--------------------------------------------------------------------------------
/config.php:
--------------------------------------------------------------------------------
1 | false, // Capitalize some property names, defined in $words_to_capitalize
13 | 'remove_end_punctuation' => false, // Remove line-ending punctuation
14 | 'fix_mid_line_punctuation' => false, // Fix mid-line punctuation
15 | 'straighten_curly_quotes' => false, // Straighten curly quotes
16 | 'remove_x2' => false, // Remove 'x2' type references and empty parentheses
17 | 'start_with_capital' => false, // Begin all lines with capital letter
18 | 'standardize_song_sections' => false, // Standardize the names of the song sections to fit ProPresenter's defaults
19 | 'standardize_title_format' => false, // Standardize the formatting of the name of the songs
20 | 'prevent_overwrites' => true, // Prevent overwriting files - adds a '(1)' style suffix
21 | 'add_metadata_to_export_files' => true, // Adds the metadata block to the top of the export files
22 | 'condense_slide_breaks' => false, // Condense all slide breaks
23 | 'reflow_large_blocks' => false, // Reflow large blocks (target line length defined in $reflow_max_lines)
24 | 'output_subdirectory' => true, // Output files in a timestamp labelled sub-directory
25 | 'aggressive_text_encoding' => false, // Aggressively convert songs to ISO-8859-1 character set - this will most likely break songs with non-Latin characters!
26 | 'prop6_add_blank_intro' => false, // Adds a blank "Intro" slide to ProPresenter files
27 | 'prop6_add_blank_end' => false, // Adds a blank "End" slide to ProPresenter files
28 | 'prop6_add_hotkeys' => false, // Adds hot keys to ProPresenter files
29 | );
30 |
31 | $reflow_max_lines = 2; // How many lines should we try to 'reflow' the text to?
32 | $file_export_type = 'plain_text'; // set to 'propresenter6' to use the experimental ProPresenter6 output
33 |
34 | $song_section_names = array('Verse', 'Chorus', 'Pre-Chorus', 'Bridge', 'Tag', 'Intro', 'End');
35 | $words_to_capitalize = array('Jesus', 'God', 'Gud', 'Lord', 'You', 'Your', 'Du', 'Din', 'Ditt', 'Han', 'Hans', 'Ham', 'Holy Spirit', 'Father');
36 |
37 | // This array controls which hot keys are assigned to which sections in ProPresenter exports
38 | $propresenter_hotkey_map = array(
39 | 'Title' => 'T',
40 | 'Intro' => 'I',
41 | 'Verse 1' => 'A',
42 | 'Verse 2' => 'S',
43 | 'Verse 3' => 'D',
44 | 'Verse 4' => 'F',
45 | 'Verse 5' => 'G',
46 | 'Verse 6' => 'H',
47 | 'Verse 7' => 'J',
48 | 'Verse 8' => 'K',
49 | 'Verse 9' => 'L',
50 | 'Pre-Chorus' => 'X',
51 | 'Chorus 1' => 'C',
52 | 'Chorus 2' => 'V',
53 | 'Chorus 3' => '',
54 | 'Bridge 1' => 'B',
55 | 'Bridge 2' => 'M',
56 | 'Tag' => 'N',
57 | 'End' => 'Z',
58 | );
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # EasyWorship 6.1 Exporter
2 |
3 | * Author: James Inglis
4 | * URL: https://github.com/jamesinglis/ew61-export
5 | * License: MIT (i.e. do whatever you want with it, but no warranty!)
6 |
7 |
8 | ## Overview
9 |
10 | This PHP-based solution accesses the EasyWorship 6.1 database and exports all of the songs as plain text files suitable for importing into another lyric projection system (tested with ProPresenter 6.1).
11 |
12 | Previous versions of EasyWorship (including 6.0) used a different database storage engine. EasyWorship 6.1 now stores its databases in SQLite3 format, which meant that all other export utilities no longer worked (see below for Other Export Methods that work with previous versions).
13 |
14 | This solution was conceived when faced with an EasyWorship 6.1 library of 800+ songs which no-one wanted to retype! It caused me to look closely at the database format and come to the conclusion that the best solution was to extract the data from the tables and clean up the contents programmatically.
15 |
16 |
17 | ## Other Export Methods
18 |
19 | Previous versions of EasyWorship have had different 3rd party solutions that have sprung up.
20 |
21 | For further information about these, the following links may be useful:
22 |
23 | * EasySearch - http://www.headwest-productions.co.uk/Software.htm
24 | * OpenLP's song importer - https://manual.openlp.org/songs.html
25 | * Video Psalm - http://myvideopsalm.weebly.com/how-to-import-songs-and-bibles-from-other-sources.html
26 | * https://forums.openlp.org/discussion/831/import-easyworship-songs
27 |
28 |
29 | ## Getting Started
30 |
31 | To use this solution, you'll need to be comfortable with running PHP scripts. It doesn't have a graphical user interface, however you can run this from the command line or from a web browser.
32 |
33 | * Ensure that PHP has PDO SQLite support and MBString support (see https://github.com/jamesinglis/ew61-export/issues/1 for details - thanks jonathantjm!)
34 | * Clone this repository to a location on your computer that PHP has write access to.
35 | * Locate the EasyWorship database files in the EasyWorship data directory
36 | * This is commonly in C:\Users\Public\Softouch\EasyWorship\Default\Databases\Data\ but may vary
37 | * On my installation, the installer for EasyWorship 6.1 had created a new subfolder for the 6.1 profile at C:\Users\Public\Softouch\EasyWorship\Default\6.1\
38 | * Copy the following files from your EasyWorship data directory to [this repository root]/databases/:
39 | * Songs.db
40 | * SongWords.db
41 | * Review the custom formatting options in config.php and set the values to true or false
42 | * The default settings will export your songs in plain text files. If you want to use the experimental ProPresenter6 export feature, change `$file_export_type = 'plain_text';` to `$file_export_type = 'propresenter6';` in config.php.
43 | * From the command line, change directory to the repository root and run:
44 | * php ./process.php
45 | * Alternatively, if the repository in a directory served by a web server, you may run process.php in a web browser.
46 | * The progress of the conversion is displayed on the screen
47 | * The exported files can be found in [this repository root]/output/
48 |
49 | Note: this script does not write anything to the EasyWorship database files, however accidents can happen. To safeguard your existing data, make sure that you run this script on copies of your database files. I take no responsibility for any data loss that may occur directly or indirectly from using this script!
50 |
51 |
52 | ## Features
53 |
54 | * Exports all songs and lyrics from EasyWorship 6.1 database files into a date-labeled sub-directory in /output directory
55 | * Attempts to strip all formatting from song words to make for a clean import into target system
56 | * Outputs to text files by default with experimental ProPresenter6 export (specify $file_export_type in config.php)
57 | * Attempts to handle inconsistencies with text encoding
58 | * Reflow song sections over 2 lines long (can be changed in config file)
59 | * Custom formatting functions (needs to be enabled in config file)
60 | * 'capitalize_names' - Capitalize some property names (defined in $words_to_capitalize variable)
61 | * 'remove_end_punctuation' - Remove line-ending punctuation
62 | * 'fix_mid_line_punctuation' - Fixes mid-line punctuation - replaces '.' with a line break, and makes sure ',;\?!' is always followed by a space
63 | * 'straighten_curly_quotes' - Straighten curly quotes
64 | * 'remove_x2' - Remove 'x2' type references and empty parentheses
65 | * 'start_with_capital' - Begin all lines with capital letter
66 | * 'standardize_song_sections' - Standardize the names of the song sections to fit ProPresenter's default 'Groups'
67 | * 'standardize_title_format' - Standardize the formatting of the name of the songs
68 | * 'prevent_overwrites' - Prevent overwriting files - adds a '(1)' style suffix
69 | * 'add_metadata_to_export_files' - Adds the metadata block to the top of the export files
70 | * 'condense_slide_breaks' - Condense all slide breaks
71 | * 'reflow_large_blocks' - Reflow large blocks (target line length defined in $reflow_max_lines)
72 | * 'output_subdirectory' - Output files in a timestamp labelled sub-directory
73 | * 'aggressive_text_encoding' - Aggressively convert songs to ISO-8859-1 character set - this will most likely break songs with non-Latin characters!
74 | * ProPresenter-specific settings
75 | * 'prop6_add_blank_intro' - Adds a blank "Intro" slide to ProPresenter files (useful for adding cues and arrangements)
76 | * 'prop6_add_blank_end' - Adds a blank "End" slide to ProPresenter files (useful for adding cues and arrangements)
77 | * 'prop6_add_hotkeys' - Assign hot keys to slide groups
78 |
79 | ### NEW in v0.2 - Export direct to ProPresenter6 .pro6 fileformat
80 |
81 | * Creates a directory of .pro6 song files
82 | * Creates ProPresenter slide groups from EasyWorship song sections
83 | * Adds hot keys to ProPresenter slide groups following [Luke McElroy's 2014 recommendation](https://www.worshiphousemedia.com/church-media-blog/software/simple-effective-propresenter-hot-key-system/)
84 | * Title - T
85 | * Intro - I
86 | * Verse 1 - A
87 | * Verse 2 - S
88 | * Verse 3 - D
89 | * Verse 4 - F
90 | * Verse 5 - G
91 | * Verse 6 - H
92 | * Verse 7 - J
93 | * Verse 8 - K
94 | * Verse 9 - L
95 | * Pre-Chorus - X
96 | * Chorus 1 - C
97 | * Chorus 2 - V
98 | * Chorus 3 -
99 | * Bridge 1 - B
100 | * Bridge 2 - M
101 | * Tag - N
102 | * End - Z
103 |
104 | ### NEW in v0.3 - Better text export
105 |
106 | * I didn't realize until now that the data is stored in EasyWorship in standard RTF format! My manual functions to strip this formatting didn't work for everyone. Implementing a more robust RTF to text formatting solution.
107 | * ProPresenter hot keys are now managed in the config file - easier to override!
108 |
109 | ## To Do in Future
110 | (if there's a demand for it)
111 |
112 | * Clean up the code so it's cleaner and more extensible
113 | * Not the best coding - this started as a quick and dirty solution!
114 | * Export to other file formats suitable for other projection
115 | * Include all song metadata
116 | * My test library didn't have any song metadata so it wasn't a priority!
117 |
118 |
119 | ## Questions & Answers
120 |
121 | * Why is this written in PHP? Surely [insert language/framework here] would be a better choice!
122 | * You may be right, however this script was developed to meet an immediate need for myself and PHP is the language in which I am most proficient!
123 | * What version of PHP do I need to run this?
124 | * The solution was written for PHP 5.6. I have run conversions with it in December 2021 with PHP 8.0.12 (with minor change made in v0.3.2).
125 | * Why am I getting strange characters in my export files?
126 | * It's most likely a text-encoding issue. This script was developed for a non-English EasyWorship library so unicode characters should be handled correctly, however I can't make any promises!
127 | * For what it's worth, my CLI version of PHP 5.6 garbles the "Å" character but the web server version of the same PHP installation has no problems encoding this character.
128 | * Why am I getting random references to fonts at the top of my song exports?
129 | * This is a remnant of the RTF formatting - I'm manually stripping out references to Arial and Tahoma, but if you used a different font in EasyWorship, that font name may show up. If this happens, add the name of your font to the regex on line 130 of functions.php.
130 |
131 | ## Version history
132 |
133 | ### 0.3.2 (2021-12-07)
134 | * Tweak for PHP8 compatibility
135 | * Adds reference to PHP library dependencies answered in jonathantjm's issue
136 | * Update config and readme documentation
137 |
138 | ### 0.3.1 (2018-10-21)
139 | * Adds missing ProPresenter slide text template
140 | * Update config and readme documentation
141 |
142 | ### 0.3 (2018-04-11)
143 |
144 | * Adds real RTF processing to the song data from EasyWorship!
145 | * Move ProPresenter hot keys to config file.
146 |
147 | ### 0.2 (2018-03-02)
148 |
149 | * Numerous bug fixes from real-world use
150 | * Adds experimental ProPresenter6 export feature
151 |
152 | ### 0.1 (2016-09-03)
153 |
154 | * Initial version
--------------------------------------------------------------------------------
/resources/Amazing Grace.pro6:
--------------------------------------------------------------------------------
1 | {51 108 0 1816 864}0.000000|0 0 0 0.3333333432674408|{4.0000002002606152, -3.9999995259110506}0 0 0 11.000000e1xydGYxXGFuc2lcYW5zaWNwZzEyNTJcY29jb2FydGYxNTA0XGNvY29hc3VicnRmODMwCntcZm9udHRibH0Ke1xjb2xvcnRibDtccmVkMjU1XGdyZWVuMjU1XGJsdWUyNTU7fQp7XCpcZXhwYW5kZWRjb2xvcnRibDs7fQp9{51 108 0 1816 864}0.000000|0 0 0 0.3333333432674408|{4.0000002002606152, -3.9999995259110506}0 0 0 11.000000e1xydGYxXGFuc2lcYW5zaWNwZzEyNTJcY29jb2FydGYxNTA0XGNvY29hc3VicnRmODMwCntcZm9udHRibFxmMFxmc3dpc3NcZmNoYXJzZXQwIEFyaWFsTVQ7fQp7XGNvbG9ydGJsO1xyZWQyNTVcZ3JlZW4yNTVcYmx1ZTI1NTtccmVkMjU1XGdyZWVuMjU1XGJsdWUyNTU7XHJlZDUxXGdyZWVuNTFcYmx1ZTUxO30Ke1wqXGV4cGFuZGVkY29sb3J0Ymw7O1xjc2dyYXlcYzEwMDAwMDtcY3NnZW5lcmljcmdiXGMyMDAwMFxjMjAwMDBcYzIwMDAwO30KXHBhcmRcc2xsZWFkaW5nMzIwXHBhcmRpcm5hdHVyYWxccWNccGFydGlnaHRlbmZhY3RvcjAKClxmMFxiXGZzMTI3XGZzbWlsbGk2Mzk5OSBcY2YyIFxleHBuZDBcZXhwbmR0dzBca2VybmluZzAKXG91dGwwXHN0cm9rZXdpZHRoLTgwIFxzdHJva2VjMyBBbWF6aW5nIGdyYWNlISBIb3cgc3dlZXQgdGhlIHNvdW5kXApUaGF0IHNhdmVkIGEgd3JldGNoIGxpa2UgbWUhfQ=={51 108 0 1816 864}0.000000|0 0 0 0.3333333432674408|{4.0000002002606152, -3.9999995259110506}0 0 0 11.000000e1xydGYxXGFuc2lcYW5zaWNwZzEyNTJcY29jb2FydGYxNTA0XGNvY29hc3VicnRmODMwCntcZm9udHRibFxmMFxmc3dpc3NcZmNoYXJzZXQwIEFyaWFsTVQ7fQp7XGNvbG9ydGJsO1xyZWQyNTVcZ3JlZW4yNTVcYmx1ZTI1NTtccmVkMjU1XGdyZWVuMjU1XGJsdWUyNTU7XHJlZDUxXGdyZWVuNTFcYmx1ZTUxO30Ke1wqXGV4cGFuZGVkY29sb3J0Ymw7O1xjc2dyYXlcYzEwMDAwMDtcY3NnZW5lcmljcmdiXGMyMDAwMFxjMjAwMDBcYzIwMDAwO30KXHBhcmRcc2xsZWFkaW5nMzIwXHBhcmRpcm5hdHVyYWxccWNccGFydGlnaHRlbmZhY3RvcjAKClxmMFxiXGZzMTI3XGZzbWlsbGk2Mzk5OSBcY2YyIFxleHBuZDBcZXhwbmR0dzBca2VybmluZzAKXG91dGwwXHN0cm9rZXdpZHRoLTgwIFxzdHJva2VjMyBJIG9uY2Ugd2FzIGxvc3QsIGJ1dCBub3cgYW0gZm91bmQ7XApXYXMgYmxpbmQsIGJ1dCBub3cgSSBzZWUufQ=={51 108 0 1816 864}0.000000|0 0 0 0.3333333432674408|{4.0000002002606152, -3.9999995259110506}0 0 0 11.000000e1xydGYxXGFuc2lcYW5zaWNwZzEyNTJcY29jb2FydGYxNTA0XGNvY29hc3VicnRmODMwCntcZm9udHRibFxmMFxmc3dpc3NcZmNoYXJzZXQwIEFyaWFsTVQ7fQp7XGNvbG9ydGJsO1xyZWQyNTVcZ3JlZW4yNTVcYmx1ZTI1NTtccmVkMjU1XGdyZWVuMjU1XGJsdWUyNTU7XHJlZDUxXGdyZWVuNTFcYmx1ZTUxO30Ke1wqXGV4cGFuZGVkY29sb3J0Ymw7O1xjc2dyYXlcYzEwMDAwMDtcY3NnZW5lcmljcmdiXGMyMDAwMFxjMjAwMDBcYzIwMDAwO30KXHBhcmRcc2xsZWFkaW5nMzIwXHBhcmRpcm5hdHVyYWxccWNccGFydGlnaHRlbmZhY3RvcjAKClxmMFxiXGZzMTI3XGZzbWlsbGk2Mzk5OSBcY2YyIFxleHBuZDBcZXhwbmR0dzBca2VybmluZzAKXG91dGwwXHN0cm9rZXdpZHRoLTgwIFxzdHJva2VjMyBcJzkyVHdhcyBncmFjZSB0aGF0IHRhdWdodCBteSBoZWFydCB0byBmZWFyLFwKQW5kIGdyYWNlIG15IGZlYXJzIHJlbGlldmVkO30={51 108 0 1816 864}0.000000|0 0 0 0.3333333432674408|{4.0000002002606152, -3.9999995259110506}0 0 0 11.000000e1xydGYxXGFuc2lcYW5zaWNwZzEyNTJcY29jb2FydGYxNTA0XGNvY29hc3VicnRmODMwCntcZm9udHRibFxmMFxmc3dpc3NcZmNoYXJzZXQwIEFyaWFsTVQ7fQp7XGNvbG9ydGJsO1xyZWQyNTVcZ3JlZW4yNTVcYmx1ZTI1NTtccmVkMjU1XGdyZWVuMjU1XGJsdWUyNTU7XHJlZDUxXGdyZWVuNTFcYmx1ZTUxO30Ke1wqXGV4cGFuZGVkY29sb3J0Ymw7O1xjc2dyYXlcYzEwMDAwMDtcY3NnZW5lcmljcmdiXGMyMDAwMFxjMjAwMDBcYzIwMDAwO30KXHBhcmRcc2xsZWFkaW5nMzIwXHBhcmRpcm5hdHVyYWxccWNccGFydGlnaHRlbmZhY3RvcjAKClxmMFxiXGZzMTI3XGZzbWlsbGk2Mzk5OSBcY2YyIFxleHBuZDBcZXhwbmR0dzBca2VybmluZzAKXG91dGwwXHN0cm9rZXdpZHRoLTgwIFxzdHJva2VjMyBIb3cgcHJlY2lvdXMgZGlkIHRoYXQgZ3JhY2UgYXBwZWFyXApUaGUgaG91ciBJIGZpcnN0IGJlbGlldmVkLn0={51 108 0 1816 864}0.000000|0 0 0 0.3333333432674408|{4.0000002002606152, -3.9999995259110506}0 0 0 11.000000e1xydGYxXGFuc2lcYW5zaWNwZzEyNTJcY29jb2FydGYxNTA0XGNvY29hc3VicnRmODMwCntcZm9udHRibFxmMFxmc3dpc3NcZmNoYXJzZXQwIEFyaWFsTVQ7fQp7XGNvbG9ydGJsO1xyZWQyNTVcZ3JlZW4yNTVcYmx1ZTI1NTtccmVkMjU1XGdyZWVuMjU1XGJsdWUyNTU7XHJlZDUxXGdyZWVuNTFcYmx1ZTUxO30Ke1wqXGV4cGFuZGVkY29sb3J0Ymw7O1xjc2dyYXlcYzEwMDAwMDtcY3NnZW5lcmljcmdiXGMyMDAwMFxjMjAwMDBcYzIwMDAwO30KXHBhcmRcc2xsZWFkaW5nMzIwXHBhcmRpcm5hdHVyYWxccWNccGFydGlnaHRlbmZhY3RvcjAKClxmMFxiXGZzMTI3XGZzbWlsbGk2Mzk5OSBcY2YyIFxleHBuZDBcZXhwbmR0dzBca2VybmluZzAKXG91dGwwXHN0cm9rZXdpZHRoLTgwIFxzdHJva2VjMyBUaHJvdWdoIG1hbnkgZGFuZ2VycywgdG9pbHMgYW5kIHNuYXJlcyxcCkkgaGF2ZSBhbHJlYWR5IGNvbWU7fQ=={51 108 0 1816 864}0.000000|0 0 0 0.3333333432674408|{4.0000002002606152, -3.9999995259110506}0 0 0 11.000000e1xydGYxXGFuc2lcYW5zaWNwZzEyNTJcY29jb2FydGYxNTA0XGNvY29hc3VicnRmODMwCntcZm9udHRibFxmMFxmc3dpc3NcZmNoYXJzZXQwIEFyaWFsTVQ7fQp7XGNvbG9ydGJsO1xyZWQyNTVcZ3JlZW4yNTVcYmx1ZTI1NTtccmVkMjU1XGdyZWVuMjU1XGJsdWUyNTU7XHJlZDUxXGdyZWVuNTFcYmx1ZTUxO30Ke1wqXGV4cGFuZGVkY29sb3J0Ymw7O1xjc2dyYXlcYzEwMDAwMDtcY3NnZW5lcmljcmdiXGMyMDAwMFxjMjAwMDBcYzIwMDAwO30KXHBhcmRcc2xsZWFkaW5nMzIwXHBhcmRpcm5hdHVyYWxccWNccGFydGlnaHRlbmZhY3RvcjAKClxmMFxiXGZzMTI3XGZzbWlsbGk2Mzk5OSBcY2YyIFxleHBuZDBcZXhwbmR0dzBca2VybmluZzAKXG91dGwwXHN0cm9rZXdpZHRoLTgwIFxzdHJva2VjMyBcJzkyVGlzIGdyYWNlIGhhdGggYnJvdWdodCBtZSBzYWZlIHRodXMgZmFyLFwKQW5kIGdyYWNlIHdpbGwgbGVhZCBtZSBob21lLn0={51 108 0 1816 864}0.000000|0 0 0 0.3333333432674408|{4.0000002002606152, -3.9999995259110506}0 0 0 11.000000e1xydGYxXGFuc2lcYW5zaWNwZzEyNTJcY29jb2FydGYxNTA0XGNvY29hc3VicnRmODMwCntcZm9udHRibFxmMFxmc3dpc3NcZmNoYXJzZXQwIEFyaWFsTVQ7fQp7XGNvbG9ydGJsO1xyZWQyNTVcZ3JlZW4yNTVcYmx1ZTI1NTtccmVkMjU1XGdyZWVuMjU1XGJsdWUyNTU7XHJlZDUxXGdyZWVuNTFcYmx1ZTUxO30Ke1wqXGV4cGFuZGVkY29sb3J0Ymw7O1xjc2dyYXlcYzEwMDAwMDtcY3NnZW5lcmljcmdiXGMyMDAwMFxjMjAwMDBcYzIwMDAwO30KXHBhcmRcc2xsZWFkaW5nMzIwXHBhcmRpcm5hdHVyYWxccWNccGFydGlnaHRlbmZhY3RvcjAKClxmMFxiXGZzMTI3XGZzbWlsbGk2Mzk5OSBcY2YyIFxleHBuZDBcZXhwbmR0dzBca2VybmluZzAKXG91dGwwXHN0cm9rZXdpZHRoLTgwIFxzdHJva2VjMyBUaGUgTG9yZCBoYXMgcHJvbWlzZWQgZ29vZCB0byBtZSxcCkhpcyBXb3JkIG15IGhvcGUgc2VjdXJlczt9{51 108 0 1816 864}0.000000|0 0 0 0.3333333432674408|{4.0000002002606152, -3.9999995259110506}0 0 0 11.000000e1xydGYxXGFuc2lcYW5zaWNwZzEyNTJcY29jb2FydGYxNTA0XGNvY29hc3VicnRmODMwCntcZm9udHRibFxmMFxmc3dpc3NcZmNoYXJzZXQwIEFyaWFsTVQ7fQp7XGNvbG9ydGJsO1xyZWQyNTVcZ3JlZW4yNTVcYmx1ZTI1NTtccmVkMjU1XGdyZWVuMjU1XGJsdWUyNTU7XHJlZDUxXGdyZWVuNTFcYmx1ZTUxO30Ke1wqXGV4cGFuZGVkY29sb3J0Ymw7O1xjc2dyYXlcYzEwMDAwMDtcY3NnZW5lcmljcmdiXGMyMDAwMFxjMjAwMDBcYzIwMDAwO30KXHBhcmRcc2xsZWFkaW5nMzIwXHBhcmRpcm5hdHVyYWxccWNccGFydGlnaHRlbmZhY3RvcjAKClxmMFxiXGZzMTI3XGZzbWlsbGk2Mzk5OSBcY2YyIFxleHBuZDBcZXhwbmR0dzBca2VybmluZzAKXG91dGwwXHN0cm9rZXdpZHRoLTgwIFxzdHJva2VjMyBIZSB3aWxsIG15IFNoaWVsZCBhbmQgUG9ydGlvbiBiZSxcCkFzIGxvbmcgYXMgbGlmZSBlbmR1cmVzLn0={51 108 0 1816 864}0.000000|0 0 0 0.3333333432674408|{4.0000002002606152, -3.9999995259110506}0 0 0 11.000000e1xydGYxXGFuc2lcYW5zaWNwZzEyNTJcY29jb2FydGYxNTA0XGNvY29hc3VicnRmODMwCntcZm9udHRibFxmMFxmc3dpc3NcZmNoYXJzZXQwIEFyaWFsTVQ7fQp7XGNvbG9ydGJsO1xyZWQyNTVcZ3JlZW4yNTVcYmx1ZTI1NTtccmVkMjU1XGdyZWVuMjU1XGJsdWUyNTU7XHJlZDUxXGdyZWVuNTFcYmx1ZTUxO30Ke1wqXGV4cGFuZGVkY29sb3J0Ymw7O1xjc2dyYXlcYzEwMDAwMDtcY3NnZW5lcmljcmdiXGMyMDAwMFxjMjAwMDBcYzIwMDAwO30KXHBhcmRcc2xsZWFkaW5nMzIwXHBhcmRpcm5hdHVyYWxccWNccGFydGlnaHRlbmZhY3RvcjAKClxmMFxiXGZzMTI3XGZzbWlsbGk2Mzk5OSBcY2YyIFxleHBuZDBcZXhwbmR0dzBca2VybmluZzAKXG91dGwwXHN0cm9rZXdpZHRoLTgwIFxzdHJva2VjMyBZZWEsIHdoZW4gdGhpcyBmbGVzaCBhbmQgaGVhcnQgc2hhbGwgZmFpbCxcCkFuZCBtb3J0YWwgbGlmZSBzaGFsbCBjZWFzZSx9{51 108 0 1816 864}0.000000|0 0 0 0.3333333432674408|{4.0000002002606152, -3.9999995259110506}0 0 0 11.000000e1xydGYxXGFuc2lcYW5zaWNwZzEyNTJcY29jb2FydGYxNTA0XGNvY29hc3VicnRmODMwCntcZm9udHRibFxmMFxmc3dpc3NcZmNoYXJzZXQwIEFyaWFsTVQ7fQp7XGNvbG9ydGJsO1xyZWQyNTVcZ3JlZW4yNTVcYmx1ZTI1NTtccmVkMjU1XGdyZWVuMjU1XGJsdWUyNTU7XHJlZDUxXGdyZWVuNTFcYmx1ZTUxO30Ke1wqXGV4cGFuZGVkY29sb3J0Ymw7O1xjc2dyYXlcYzEwMDAwMDtcY3NnZW5lcmljcmdiXGMyMDAwMFxjMjAwMDBcYzIwMDAwO30KXHBhcmRcc2xsZWFkaW5nMzIwXHBhcmRpcm5hdHVyYWxccWNccGFydGlnaHRlbmZhY3RvcjAKClxmMFxiXGZzMTI3XGZzbWlsbGk2Mzk5OSBcY2YyIFxleHBuZDBcZXhwbmR0dzBca2VybmluZzAKXG91dGwwXHN0cm9rZXdpZHRoLTgwIFxzdHJva2VjMyBJIHNoYWxsIHBvc3Nlc3MsIHdpdGhpbiB0aGUgdmVpbCxcCkEgbGlmZSBvZiBqb3kgYW5kIHBlYWNlLn0={51 108 0 1816 864}0.000000|0 0 0 0.3333333432674408|{4.0000002002606152, -3.9999995259110506}0 0 0 11.000000e1xydGYxXGFuc2lcYW5zaWNwZzEyNTJcY29jb2FydGYxNTA0XGNvY29hc3VicnRmODMwCntcZm9udHRibFxmMFxmc3dpc3NcZmNoYXJzZXQwIEFyaWFsTVQ7fQp7XGNvbG9ydGJsO1xyZWQyNTVcZ3JlZW4yNTVcYmx1ZTI1NTtccmVkMjU1XGdyZWVuMjU1XGJsdWUyNTU7XHJlZDUxXGdyZWVuNTFcYmx1ZTUxO30Ke1wqXGV4cGFuZGVkY29sb3J0Ymw7O1xjc2dyYXlcYzEwMDAwMDtcY3NnZW5lcmljcmdiXGMyMDAwMFxjMjAwMDBcYzIwMDAwO30KXHBhcmRcc2xsZWFkaW5nMzIwXHBhcmRpcm5hdHVyYWxccWNccGFydGlnaHRlbmZhY3RvcjAKClxmMFxiXGZzMTI3XGZzbWlsbGk2Mzk5OSBcY2YyIFxleHBuZDBcZXhwbmR0dzBca2VybmluZzAKXG91dGwwXHN0cm9rZXdpZHRoLTgwIFxzdHJva2VjMyBUaGUgZWFydGggc2hhbGwgc29vbiBkaXNzb2x2ZSBsaWtlIHNub3csXApUaGUgc3VuIGZvcmJlYXIgdG8gc2hpbmU7fQ=={51 108 0 1816 864}0.000000|0 0 0 0.3333333432674408|{4.0000002002606152, -3.9999995259110506}0 0 0 11.000000e1xydGYxXGFuc2lcYW5zaWNwZzEyNTJcY29jb2FydGYxNTA0XGNvY29hc3VicnRmODMwCntcZm9udHRibFxmMFxmc3dpc3NcZmNoYXJzZXQwIEFyaWFsTVQ7fQp7XGNvbG9ydGJsO1xyZWQyNTVcZ3JlZW4yNTVcYmx1ZTI1NTtccmVkMjU1XGdyZWVuMjU1XGJsdWUyNTU7XHJlZDUxXGdyZWVuNTFcYmx1ZTUxO30Ke1wqXGV4cGFuZGVkY29sb3J0Ymw7O1xjc2dyYXlcYzEwMDAwMDtcY3NnZW5lcmljcmdiXGMyMDAwMFxjMjAwMDBcYzIwMDAwO30KXHBhcmRcc2xsZWFkaW5nMzIwXHBhcmRpcm5hdHVyYWxccWNccGFydGlnaHRlbmZhY3RvcjAKClxmMFxiXGZzMTI3XGZzbWlsbGk2Mzk5OSBcY2YyIFxleHBuZDBcZXhwbmR0dzBca2VybmluZzAKXG91dGwwXHN0cm9rZXdpZHRoLTgwIFxzdHJva2VjMyBCdXQgR29kLCB3aG8gY2FsbGVkIG1lIGhlcmUgYmVsb3csXApXaWxsIGJlIGZvcmV2ZXIgbWluZS59{51 108 0 1816 864}0.000000|0 0 0 0.3333333432674408|{4.0000002002606152, -3.9999995259110506}0 0 0 11.000000e1xydGYxXGFuc2lcYW5zaWNwZzEyNTJcY29jb2FydGYxNTA0XGNvY29hc3VicnRmODMwCntcZm9udHRibFxmMFxmc3dpc3NcZmNoYXJzZXQwIEFyaWFsTVQ7fQp7XGNvbG9ydGJsO1xyZWQyNTVcZ3JlZW4yNTVcYmx1ZTI1NTtccmVkMjU1XGdyZWVuMjU1XGJsdWUyNTU7XHJlZDUxXGdyZWVuNTFcYmx1ZTUxO30Ke1wqXGV4cGFuZGVkY29sb3J0Ymw7O1xjc2dyYXlcYzEwMDAwMDtcY3NnZW5lcmljcmdiXGMyMDAwMFxjMjAwMDBcYzIwMDAwO30KXHBhcmRcc2xsZWFkaW5nMzIwXHBhcmRpcm5hdHVyYWxccWNccGFydGlnaHRlbmZhY3RvcjAKClxmMFxiXGZzMTI3XGZzbWlsbGk2Mzk5OSBcY2YyIFxleHBuZDBcZXhwbmR0dzBca2VybmluZzAKXG91dGwwXHN0cm9rZXdpZHRoLTgwIFxzdHJva2VjMyBXaGVuIHdlXCc5MnZlIGJlZW4gdGhlcmUgdGVuIHRob3VzYW5kIHllYXJzLFwKQnJpZ2h0IHNoaW5pbmcgYXMgdGhlIHN1bix9{51 108 0 1816 864}0.000000|0 0 0 0.3333333432674408|{4.0000002002606152, -3.9999995259110506}0 0 0 11.000000e1xydGYxXGFuc2lcYW5zaWNwZzEyNTJcY29jb2FydGYxNTA0XGNvY29hc3VicnRmODMwCntcZm9udHRibFxmMFxmc3dpc3NcZmNoYXJzZXQwIEFyaWFsTVQ7fQp7XGNvbG9ydGJsO1xyZWQyNTVcZ3JlZW4yNTVcYmx1ZTI1NTtccmVkMjU1XGdyZWVuMjU1XGJsdWUyNTU7XHJlZDUxXGdyZWVuNTFcYmx1ZTUxO30Ke1wqXGV4cGFuZGVkY29sb3J0Ymw7O1xjc2dyYXlcYzEwMDAwMDtcY3NnZW5lcmljcmdiXGMyMDAwMFxjMjAwMDBcYzIwMDAwO30KXHBhcmRcc2xsZWFkaW5nMzIwXHBhcmRpcm5hdHVyYWxccWNccGFydGlnaHRlbmZhY3RvcjAKClxmMFxiXGZzMTI3XGZzbWlsbGk2Mzk5OSBcY2YyIFxleHBuZDBcZXhwbmR0dzBca2VybmluZzAKXG91dGwwXHN0cm9rZXdpZHRoLTgwIFxzdHJva2VjMyBXZVwnOTJ2ZSBubyBsZXNzIGRheXMgdG8gc2luZyBHb2RcJzkycyBwcmFpc2VcClRoYW4gd2hlbiB3ZVwnOTJkIGZpcnN0IGJlZ3VuLn0={51 108 0 1816 864}0.000000|0 0 0 0.3333333432674408|{4.0000002002606152, -3.9999995259110506}0 0 0 11.000000e1xydGYxXGFuc2lcYW5zaWNwZzEyNTJcY29jb2FydGYxNTA0XGNvY29hc3VicnRmODMwCntcZm9udHRibH0Ke1xjb2xvcnRibDtccmVkMjU1XGdyZWVuMjU1XGJsdWUyNTU7fQp7XCpcZXhwYW5kZWRjb2xvcnRibDs7fQp9
--------------------------------------------------------------------------------
/functions.php:
--------------------------------------------------------------------------------
1 |
9 | * @version 0.2
10 | * @link https://github.com/jamesinglis/ew61-export
11 | */
12 |
13 |
14 | // Set the file encoding for everything to UTF-8
15 | mb_detect_order(array('UTF-8', 'ISO-8859-1', 'ASCII'));
16 | ini_set('default_charset', 'utf-8');
17 | mb_internal_encoding('UTF-8');
18 | mb_http_output('UTF-8');
19 | mb_http_input('S');
20 | mb_regex_encoding('UTF-8');
21 |
22 | define('EW6_EXPORT_EOL', "\r\n");
23 |
24 | // Output files in a timestamp labelled sub-directory
25 | if ($custom_settings['output_subdirectory']) {
26 | $output_directory = __DIR__ . '/output/' . date('YmdHis') . '_' . $file_export_type . '/';
27 | mkdir($output_directory, 0777, true);
28 | }
29 |
30 |
31 | /**
32 | * Formats the title of the song
33 | *
34 | * @param $title Raw title from EasyWorship data store
35 | * @return string Formatted title
36 | */
37 | function process_ew_title($title)
38 | {
39 | global $custom_settings;
40 |
41 | $title = trim($title); // We always trim, whether you like it or not!
42 | $title = preg_replace('/\s+/', ' ', $title); // We also always condense any whitespace characters down to single spaces
43 |
44 | if ($custom_settings['standardize_title_format']) {
45 | $title = ucwords($title);
46 | $title = preg_replace("/\s{2,}/", ' ', $title);
47 | $lc_words = ['and', 'of', 'the'];
48 | foreach ($lc_words as $lc_word) {
49 | $title = str_replace(ucwords($lc_word), $lc_word, $title);
50 | }
51 | $title = ucfirst($title);
52 | }
53 |
54 | return $title;
55 | }
56 |
57 | /**
58 | * All processing and formatting for song text
59 | *
60 | * @param $text Raw text blob from EasyWorship data store
61 | * @return string Cleaned and formatted song lyric text
62 | */
63 | function process_ew_lyrics($text)
64 | {
65 | global $custom_settings;
66 |
67 | // Process as much of the RTF content as possible before we break up into lines
68 | $text = process_unicode($text);
69 | $text = rtf2text($text);
70 | $text = str_replace(array('\r\n', '\r', '\n'), PHP_EOL, $text);
71 |
72 | // First pass - basic processing
73 | $text_lines = explode(PHP_EOL, $text);
74 | foreach ($text_lines as $line_number => &$text_line) {
75 | $text_line = process_ew_lyrics_line($text_line);
76 | }
77 | unset($text_line);
78 | $text = implode(PHP_EOL, $text_lines);
79 |
80 | // Second loop - makes sure we catch any newlines that were previously marked as "\line" in the original storage
81 | $text_lines = explode(PHP_EOL, trim($text));
82 | foreach ($text_lines as $line_number => &$text_line) {
83 | $text_line = process_ew_lyrics_line_custom($text_line);
84 | }
85 | unset($text_line);
86 | $text = implode(PHP_EOL, $text_lines);
87 |
88 | // Remove any double line breaks - common with the RTF input
89 | $text = preg_replace('/(?<=[^\n])\n{2}(?=[^\n])/', PHP_EOL, trim($text));
90 |
91 | // Condense all slide breaks (removes all paragraph breaks - will add in relevant newlines later)
92 | $line_breaks_to_condense = 3;
93 | $condensed_line_breaks = 2;
94 | if ($custom_settings['condense_slide_breaks']) {
95 | $line_breaks_to_condense--;
96 | $condensed_line_breaks--;
97 | }
98 |
99 | $text = preg_replace('/(\n{' . $line_breaks_to_condense . ',})/', str_repeat(PHP_EOL, $condensed_line_breaks), trim($text)); // Any time there is a block of newlines > 2, condense it to just two
100 |
101 | // Third loop
102 | $text_lines = explode(PHP_EOL, $text);
103 | foreach ($text_lines as $line_number => &$text_line) {
104 | $text_line = process_ew_lyrics_line_song_parts($text_line);
105 | }
106 | unset($text_line);
107 | $text = implode(PHP_EOL, $text_lines);
108 |
109 | $line_breaks_to_condense = 3;
110 | $condensed_line_breaks = 2;
111 | $text = preg_replace('/(\n{' . $line_breaks_to_condense . ',})/', str_repeat(PHP_EOL, $condensed_line_breaks), trim($text)); // Any time there is a block of newlines > 2, condense it to just two
112 |
113 | return process_ew_blocks($text); // Trim because that's what we do to keep our files clean, and also remove large quantities of newlines at start and end of the block
114 | }
115 |
116 | /**
117 | * Basic processing of text dump from EasyWorship database - cleans all unnecessary formatting data
118 | * This used to manually strip out RTF details - now we just clean up the remnants
119 | *
120 | * @param $text_line Single line of raw text from EasyWorship data store
121 | * @return string Cleaned text
122 | */
123 | function process_ew_lyrics_line($text_line)
124 | {
125 | $text_line = process_unicode($text_line);
126 | $text_line = trim($text_line);
127 | $text_line = preg_replace('/^\.$/', PHP_EOL, $text_line);
128 | $text_line = preg_replace('/^(\(|\))$/', PHP_EOL, $text_line);
129 | $text_line = preg_replace('/^\(\)$/', PHP_EOL, $text_line);
130 | $text_line = preg_replace('/(Arial|Tahoma);/', PHP_EOL, $text_line);
131 |
132 | return trim($text_line); // Seeing a recurring theme here?
133 | }
134 |
135 | /**
136 | * Formatting functions according to custom settings array in config
137 | *
138 | * @param $text_line Single line of cleaned text
139 | * @return string Formatted line of text
140 | */
141 | function process_ew_lyrics_line_custom($text_line)
142 | {
143 | global $custom_settings;
144 | global $words_to_capitalize;
145 |
146 | // Capitalize some property names
147 | if ($custom_settings['capitalize_names']) {
148 | foreach ($words_to_capitalize as $word) {
149 | $text_line = preg_replace('/( |^)(' . $word . ')( |$)/i', '$1' . $word . '$3', $text_line);
150 | }
151 | }
152 |
153 | // Remove line-ending punctuation
154 | if ($custom_settings['remove_end_punctuation']) {
155 | $text_line = preg_replace('/[.,;]+ ?$/', '', $text_line);
156 | }
157 |
158 | // Fix mid-line punctuation issues
159 | if ($custom_settings['fix_mid_line_punctuation']) {
160 | $text_line = preg_replace('/\./', "\n", $text_line);
161 | $text_line = preg_replace('/([,;\?!])([^ ])/', '$1 $2', $text_line);
162 | }
163 |
164 | // Straighten curly quotes
165 | if ($custom_settings['straighten_curly_quotes']) {
166 | $text_line = str_replace(['’', '´', '‘', '“', '”'], ["'", "'", "'", '"', '"'], $text_line);
167 | }
168 |
169 | // Remove 'x2' references and empty parentheses
170 | if ($custom_settings['remove_x2']) {
171 | $text_line = preg_replace('/(x\d+|\d+x)/i', '', $text_line);
172 | $text_line = str_replace(['()', '[]', '[ ]'], '', $text_line);
173 | }
174 |
175 | // Begin all lines with capital letter
176 | if ($custom_settings['start_with_capital']) {
177 | $text_line = ucfirst($text_line); // Always start with a capital
178 | }
179 |
180 | $text_line = trim($text_line); // We trim before the standardized song sections because we might actually want to keep some trailing newlines
181 |
182 | return $text_line;
183 | }
184 |
185 | /**
186 | * Standardize the names of the song sections to fit ProPresenter's defaults
187 | *
188 | * @param $text_line
189 | * @return string $text_line
190 | */
191 | function process_ew_lyrics_line_song_parts($text_line)
192 | {
193 | global $custom_settings;
194 | global $song_section_names;;
195 |
196 | $line_breaks = str_repeat(PHP_EOL, 2);
197 |
198 | // Standardize the names of the song sections to fit ProPresenter's defaults
199 | if ($custom_settings['standardize_song_sections']) {
200 | if (preg_match('/^(Verse|Chorus|Bridge)\s?(\d+)$/i', $text_line, $matches)) {
201 | $text_line = ucwords(strtolower($matches[1])) . ' ' . $matches[2];
202 | } elseif (preg_match('/^(Verse|Chorus|Bridge)$/i', $text_line, $matches)) {
203 | $text_line = ucwords(strtolower($matches[1])) . ' 1'; // When just a plain 'Chorus'
204 | } elseif (preg_match('/^Pre\-?Chorus/i', $text_line, $matches)) {
205 | $text_line = 'Pre-Chorus';
206 | } elseif (preg_match('/^(Tag|Intro) ?\d+?$/i', $text_line, $matches)) {
207 | $text_line = ucwords(strtolower($matches[1]));
208 | }
209 | }
210 |
211 | // Add double line break before each song part
212 | if (preg_match('/^(' . implode('|', $song_section_names) . ')/i', $text_line, $matches)) {
213 | $text_line = $line_breaks . $text_line;
214 | }
215 |
216 | return $text_line;
217 | }
218 |
219 | /**
220 | * Prepare the metadata to add to the export
221 | *
222 | * @param array $song Song array
223 | * @return string Song metadata
224 | */
225 | function process_ew_lyrics_metadata(array $song)
226 | {
227 | $meta_fields = array(
228 | 'title' => 'Title',
229 | 'author' => 'Author',
230 | 'copyright' => 'Copyright',
231 | 'ccli_no' => '',
232 | );
233 | $meta_fields_added = 0;
234 | $metadata = '';
235 |
236 | // Add the fields from the $song array to the meta block
237 | foreach ($meta_fields as $array_key => $label) {
238 | if (array_key_exists($array_key, $song)) {
239 | // The line format is the same for everything except the CCLI Number
240 | $line_format = '%s: %s';
241 | if ($array_key === 'ccli_no') {
242 | $line_format = '%s[S A%s]';
243 | }
244 |
245 | $metadata .= sprintf($line_format, $label, $song[$array_key]) . PHP_EOL;
246 | $meta_fields_added++;
247 | }
248 | }
249 |
250 | // If we have meta fields, add line breaks to separate from the next section
251 | if ($meta_fields_added > 0) {
252 | $metadata .= str_repeat(PHP_EOL, 2);
253 | }
254 |
255 | return $metadata;
256 | }
257 |
258 | function process_ew_blocks($text_lines)
259 | {
260 | $text_blocks = explode(str_repeat(PHP_EOL, 2), trim($text_lines));
261 | $text_blocks_array = array();
262 | $text_blocks_output_array = array();
263 |
264 | foreach ($text_blocks as &$text_block) {
265 | $text_block_array = process_ew_single_block($text_block);
266 | $text_blocks_array[] = $text_block_array;
267 | $text_blocks_output_array[] = $text_block_array['output'];
268 | }
269 | unset($text_block);
270 |
271 | return implode(str_repeat(PHP_EOL, 2), $text_blocks_output_array);
272 | }
273 |
274 | function process_ew_single_block($text_block)
275 | {
276 | global $custom_settings;
277 | global $song_section_names;
278 | global $reflow_max_lines;
279 |
280 | $text_lines = explode(PHP_EOL, $text_block);
281 | $text_block_array['heading'] = false;
282 |
283 | if (preg_match('/(' . implode('|', $song_section_names) . ') ?(\d+?)/', $text_lines[0], $matches)) {
284 | $text_block_array['heading'] = array(
285 | 'label' => $matches[0],
286 | 'type' => $matches[1],
287 | 'number' => $matches[2],
288 | );
289 | unset($text_lines[0]);
290 | }
291 |
292 | $text_block_array['lines'] = array_values($text_lines);
293 | $text_block_array['line_count'] = count($text_lines);
294 |
295 | $group = 0;
296 | $group_size = count($text_lines);
297 | if ($custom_settings['reflow_large_blocks']) {
298 | $group_size = $reflow_max_lines;
299 | }
300 |
301 | // If there isn't an even split with the $reflow_max_lines number of lines, see if one line less helps
302 | if ($group_size > 2 && count($text_lines) % $group_size !== 0 && count($text_lines) % ($group_size - 1) === 0) {
303 | $group_size = $group_size - 1;
304 | }
305 |
306 | // Loop through the
307 | for ($i = 0; $i < count($text_lines); $i++) {
308 | $text_block_array['groups'][$group][] = $text_block_array['lines'][$i];
309 | if (($i + 1) % $group_size === 0) {
310 | $group++;
311 | }
312 | }
313 |
314 | $temp_group_array = array();
315 |
316 | if (array_key_exists('groups', $text_block_array) && count($text_block_array) > 0) {
317 | foreach ($text_block_array['groups'] as $group) {
318 | $temp_group_array[] = implode(str_repeat(PHP_EOL, 1), $group);
319 | }
320 | }
321 |
322 | // Build up the output by condensing arrays
323 | $text_block_array['output'] = $text_block_array['heading'] !== false ? $text_block_array['heading']['label'] . PHP_EOL : '';
324 | $text_block_array['output'] .= implode(str_repeat(PHP_EOL, 2), $temp_group_array);
325 |
326 | return $text_block_array;
327 | }
328 |
329 | /**
330 | * Worker function that constructs the file contents and saves it to the output directory
331 | *
332 | * @param $song Song array
333 | */
334 | function save_text_file($song, $file_export_type)
335 | {
336 | global $output_directory;
337 | global $custom_settings;
338 |
339 | $filename = sprintf('%s', $song['title']); // Filename is simply the song's title
340 | $filename = str_replace('/', ' -- ', $filename);
341 | $filename = preg_replace('/\s+/', ' ', trim($filename)); // Make sure we don't have any crazy spaces in the file name
342 | $filename = process_unicode($filename);
343 | $file_extension = ".txt";
344 | if ($file_export_type === "propresenter6") {
345 | $file_extension = '.pro6';
346 | }
347 |
348 | // Prevent overwriting files - adds a '(1)' style suffix
349 | if ($custom_settings['prevent_overwrites']) {
350 | $counter = 1;
351 | while (file_exists($output_directory . $filename . $file_extension)) {
352 | $filename = trim(preg_replace('/\(\d+\)/', '', $filename));
353 | $filename .= sprintf(' (%d)', $counter);
354 | $counter++;
355 | }
356 | }
357 |
358 | // Set up the file contents
359 | $contents = '';
360 |
361 | if ($file_export_type === "propresenter6") {
362 | $contents .= $song['text'];
363 | $break_char = php_sapi_name() === "cli" ? PHP_EOL : "
";
364 | echo sprintf('Converting "%s" to ProPresenter6 (filename "%s%s", id: %d)...', $song['title'], $filename, $file_extension, $song['id']) . $break_char;
365 | } else {
366 | // Add the meta data to the top of the file if requested
367 | if ($custom_settings['add_metadata_to_export_files']) {
368 | $contents .= process_ew_lyrics_metadata($song);
369 | }
370 |
371 | $contents .= $song['text'];
372 |
373 | if ($custom_settings['aggressive_text_encoding']) {
374 | // Desperate attempt to get rid of any lingering unicode formatting issues! Forces text to ISO-8859-1 character set
375 | $contents = iconv("ISO-8859-1", "UTF-8", iconv("UTF-8", "ISO-8859-1//IGNORE", $contents));
376 | }
377 |
378 | $break_char = php_sapi_name() === "cli" ? PHP_EOL : "
";
379 | echo sprintf('Converting "%s" to plain text (filename "%s%s", id: %d)...', $song['title'], $filename, $file_extension, $song['id']) . $break_char;
380 | }
381 |
382 | file_put_contents($output_directory . $filename . $file_extension, "\xEF\xBB\xBF" . str_replace(PHP_EOL, EW6_EXPORT_EOL, $contents));
383 | }
384 |
385 | function generateRandomHex($number = 1)
386 | {
387 | return strtoupper(substr(bin2hex(openssl_random_pseudo_bytes($number)), 0, $number));
388 | }
389 |
390 | function convert_non_ascii_chars_to_hex($text)
391 | {
392 | $text = utf8_decode(mb_convert_encoding($text, "UTF-8"));
393 | $characters = utf8_decode(mb_convert_encoding("æøåÆØÅ", "UTF-8"));
394 | foreach (str_split($characters) as $char) {
395 | $converted_char = '\\\'' . bin2hex($char);
396 | $text = str_replace($char, $converted_char, $text);
397 | }
398 | return $text;
399 | }
400 |
401 | function generate_propresenter_guid()
402 | {
403 | // UUID Format: 94A8AD6C-2A51-44ED-9CAF-79DE1B722190
404 | return sprintf('%s-%s-%s-%s', generateRandomHex(8), generateRandomHex(4), generateRandomHex(4), generateRandomHex(12));
405 | }
406 |
407 | function get_propresenter_section_color($section_name)
408 | {
409 | $color_map = array(
410 | 'Intro' => '0 0 0 1',
411 | 'Verse 1' => '0 0 1 1',
412 | 'Verse 2' => '0 0.501960814 1 1',
413 | 'Verse 3' => '0 1 1 1',
414 | 'Verse 4' => '0 1 0.501960814 1',
415 | 'Verse 5' => '0 1 0 1',
416 | 'Verse 6' => '0.501960814 1 0 1',
417 | 'Pre-Chorus' => '1 0.400000006 0.400000006 1',
418 | 'Chorus 1' => '1 0 0 1',
419 | 'Chorus 2' => '0.501960814 0 0 1',
420 | 'Chorus 3' => '0.501960814 0 0.250980407 1',
421 | 'Bridge 1' => '0.501960814 0 1 1',
422 | 'Bridge 2' => '0.8000000119 0.400000006 1 1',
423 | 'Tag' => '0 0 0 1',
424 | 'End' => '0 0 0 1',
425 | );
426 |
427 | if (array_key_exists($section_name, $color_map)) {
428 | return $color_map[$section_name];
429 | }
430 | return '';
431 | }
432 |
433 | function get_propresenter_section_hotkey($section_name)
434 | {
435 | global $propresenter_hotkey_map;
436 |
437 | if (array_key_exists($section_name, $propresenter_hotkey_map)) {
438 | return strtolower($propresenter_hotkey_map[$section_name]);
439 | }
440 | return '';
441 | }
442 |
443 | /**
444 | * Worker function that constructs the file contents and saves it to the output directory
445 | *
446 | * @param $song Song array
447 | */
448 | function generate_prop6_file_contents($song)
449 | {
450 | global $custom_settings;
451 | $prop6_file_template = file_get_contents(dirname(__FILE__) . '/resources/propresenter6_file_wrapper.xml');
452 | $prop6_group_template = file_get_contents(dirname(__FILE__) . '/resources/propresenter6_group_element.xml');
453 | $prop6_slide_template = file_get_contents(dirname(__FILE__) . '/resources/propresenter6_slide_element.xml');
454 | $prop6_slide_text = file_get_contents(dirname(__FILE__) . '/resources/propresenter6_slide_text.txt');
455 |
456 | $slide_elements = array();
457 |
458 | if ($custom_settings['prop6_add_blank_intro'] == true) {
459 | $slide_elements['Intro'] = array(
460 | 'name' => 'Intro',
461 | 'guid' => generate_propresenter_guid(),
462 | 'color' => get_propresenter_section_color('Intro'),
463 | 'hotkey' => get_propresenter_section_hotkey('Intro'),
464 | 'slides' => array(
465 | array(
466 | 'guid' => generate_propresenter_guid(),
467 | 'text' => ''
468 | )
469 | )
470 | );
471 | }
472 |
473 | $section_counter = 1;
474 | $current_group_name = 'Verse ' . $section_counter;
475 | $slide_break = false;
476 | $has_groups = false;
477 | if (preg_match('/(Verse|Chorus|Bridge)/i', $song['text'], $matches)) {
478 | $has_groups = true;
479 | }
480 |
481 | $text_lines = explode(PHP_EOL, $song['text']);
482 |
483 | // Loop through the lines
484 | foreach ($text_lines as $text_line) {
485 |
486 | if ($has_groups === false && empty($text_line)) {
487 | $section_counter++;
488 | $current_group_name = 'Verse ' . $section_counter;
489 | $slide_break = true;
490 | continue;
491 | } elseif ($has_groups === true && empty($text_line)) {
492 | $slide_break = true;
493 | continue;
494 | }
495 |
496 | // Create a new group whenever a heading is found
497 | if (preg_match('/^(Verse|Chorus|Bridge)\s?(\d+)$/i', $text_line, $matches)) {
498 | $current_group_name = ucwords(strtolower($matches[1])) . ' ' . $matches[2];
499 | } elseif (preg_match('/^(Verse|Chorus|Bridge)$/i', $text_line, $matches)) {
500 | $current_group_name = ucwords(strtolower($matches[1])) . ' 1'; // When just a plain 'Chorus'
501 | } elseif (preg_match('/^Pre\-?Chorus/i', $text_line, $matches)) {
502 | $current_group_name = 'Pre-Chorus';
503 | } elseif (preg_match('/^(Tag|Intro) ?\d?$/i', $text_line, $matches)) {
504 | $current_group_name = ucwords(strtolower($matches[1]));
505 | }
506 |
507 | if (array_key_exists($current_group_name, $slide_elements) === false) {
508 | $slide_elements[$current_group_name] = array(
509 | 'name' => $current_group_name,
510 | 'guid' => generate_propresenter_guid(),
511 | 'color' => get_propresenter_section_color($current_group_name),
512 | 'hotkey' => get_propresenter_section_hotkey($current_group_name),
513 | 'slides' => array()
514 | );
515 | continue;
516 | }
517 |
518 | // Create a slide for every chunk of lines
519 | if ($slide_break || count($slide_elements[$current_group_name]['slides']) === 0) {
520 | $slide_elements[$current_group_name]['slides'][] = array(
521 | 'guid' => generate_propresenter_guid(),
522 | 'text' => $text_line
523 | );
524 | } else {
525 | $slide_index = count($slide_elements[$current_group_name]['slides']) - 1;
526 | $slide_elements[$current_group_name]['slides'][$slide_index]['text'] .= PHP_EOL . $text_line;
527 | }
528 |
529 | $slide_break = false;
530 | }
531 |
532 | if ($custom_settings['prop6_add_blank_end'] == true) {
533 | $slide_elements['End'] = array(
534 | 'name' => 'End',
535 | 'guid' => generate_propresenter_guid(),
536 | 'color' => get_propresenter_section_color('End'),
537 | 'hotkey' => get_propresenter_section_hotkey('End'),
538 | 'slides' => array(
539 | array(
540 | 'guid' => generate_propresenter_guid(),
541 | 'text' => ''
542 | )
543 | )
544 | );
545 | }
546 |
547 | $groups_xml = '';
548 | foreach ($slide_elements as $group) {
549 | $slides_xml = '';
550 | foreach ($group['slides'] as $index => $slide) {
551 | $slide_build = $prop6_slide_template;
552 | if ($index === 0 && $custom_settings['prop6_add_hotkeys'] == true) {
553 | $slide_build = str_replace('%%HOTKEY%%', $group['hotkey'], $slide_build);
554 | } else {
555 | $slide_build = str_replace('%%HOTKEY%%', '', $slide_build);
556 | }
557 | $slide_build = str_replace('%%GUID%%', $slide['guid'], $slide_build);
558 | $slide_build = str_replace('%%TEXTGUID%%', generate_propresenter_guid(), $slide_build);
559 | $slide_text = str_replace(PHP_EOL, '\\' . PHP_EOL, $slide['text']);
560 | $slide_text = convert_non_ascii_chars_to_hex($slide_text);
561 | $slide_text = str_replace('%%TEXT%%', $slide_text, $prop6_slide_text);
562 | $slide_build = str_replace('%%TEXT%%', base64_encode($slide_text), $slide_build);
563 | $slides_xml .= $slide_build;
564 | }
565 |
566 | $group_build = $prop6_group_template;
567 | $group_build = str_replace('%%NAME%%', $group['name'], $group_build);
568 | $group_build = str_replace('%%COLOR%%', $group['color'], $group_build);
569 | $group_build = str_replace('%%GUID%%', $group['guid'], $group_build);
570 | $group_build = str_replace('%%SLIDES%%', $slides_xml, $group_build);
571 | $groups_xml .= $group_build;
572 | }
573 |
574 | $prop6_file_template = str_replace('%%TITLE%%', $song['title'], $prop6_file_template);
575 | $prop6_file_template = str_replace('%%GUID%%', generate_propresenter_guid(), $prop6_file_template);
576 | $prop6_file_template = str_replace('%%GROUPS%%', $groups_xml, $prop6_file_template);
577 |
578 | return $prop6_file_template;
579 | }
580 |
581 | /**
582 | * Custom function to try to handle various unicode formatting issues that will come our way from EasyWorship
583 | *
584 | * @param $string
585 | * @return string
586 | */
587 | function process_unicode($string)
588 | {
589 | $string = preg_replace('/\\\\x([0-9A-F]{2})\?/', '$1;', $string);
590 | $string = preg_replace('/\\\\u(\d+)\?/', '$1;', $string);
591 | return html_entity_decode($string, ENT_NOQUOTES, 'UTF-8');
592 | }
593 |
594 | /* Functions adapted from http://webcheatsheet.com/php/reading_the_clean_text_from_rtf.php */
595 |
596 | function rtf_isPlainText($s)
597 | {
598 | $arrfailAt = array("*", "fonttbl", "colortbl", "datastore", "themedata");
599 | for ($i = 0; $i < count($arrfailAt); $i++)
600 | if (!empty($s[$arrfailAt[$i]])) return false;
601 | return true;
602 | }
603 |
604 | function rtf2text($text)
605 | {
606 | if (!strlen($text))
607 | return "";
608 |
609 | // Create empty stack array.
610 | $document = "";
611 | $stack = array();
612 | $j = -1;
613 | // Read the data character-by- character…
614 | for ($i = 0, $len = strlen($text); $i < $len; $i++) {
615 | $c = $text[$i];
616 |
617 | // Depending on current character select the further actions.
618 | switch ($c) {
619 | // the most important key word backslash
620 | case "\\":
621 | // read next character
622 | $nc = $text[$i + 1];
623 |
624 | // If it is another backslash or nonbreaking space or hyphen,
625 | // then the character is plain text and add it to the output stream.
626 | if ($nc == '\\' && rtf_isPlainText($stack[$j])) $document .= '\\';
627 | elseif ($nc == '~' && rtf_isPlainText($stack[$j])) $document .= ' ';
628 | elseif ($nc == '_' && rtf_isPlainText($stack[$j])) $document .= '-';
629 | // If it is an asterisk mark, add it to the stack.
630 | elseif ($nc == '*') $stack[$j]["*"] = true;
631 | // If it is a single quote, read next two characters that are the hexadecimal notation
632 | // of a character we should add to the output stream.
633 | elseif ($nc == "'") {
634 | $hex = substr($text, $i + 2, 2);
635 | if (rtf_isPlainText($stack[$j]))
636 | $document .= html_entity_decode("" . hexdec($hex) . ";");
637 | //Shift the pointer.
638 | $i += 2;
639 | // Since, we’ve found the alphabetic character, the next characters are control word
640 | // and, possibly, some digit parameter.
641 | } elseif ($nc >= 'a' && $nc <= 'z' || $nc >= 'A' && $nc <= 'Z') {
642 | $word = "";
643 | $param = null;
644 |
645 | // Start reading characters after the backslash.
646 | for ($k = $i + 1, $m = 0; $k < strlen($text); $k++, $m++) {
647 | $nc = $text[$k];
648 | // If the current character is a letter and there were no digits before it,
649 | // then we’re still reading the control word. If there were digits, we should stop
650 | // since we reach the end of the control word.
651 | if ($nc >= 'a' && $nc <= 'z' || $nc >= 'A' && $nc <= 'Z') {
652 | if (empty($param))
653 | $word .= $nc;
654 | else
655 | break;
656 | // If it is a digit, store the parameter.
657 | } elseif ($nc >= '0' && $nc <= '9')
658 | $param .= $nc;
659 | // Since minus sign may occur only before a digit parameter, check whether
660 | // $param is empty. Otherwise, we reach the end of the control word.
661 | elseif ($nc == '-') {
662 | if (empty($param))
663 | $param .= $nc;
664 | else
665 | break;
666 | } else
667 | break;
668 | }
669 | // Shift the pointer on the number of read characters.
670 | $i += $m - 1;
671 |
672 | // Start analyzing what we’ve read. We are interested mostly in control words.
673 | $toText = "";
674 | switch (strtolower($word)) {
675 | // If the control word is "u", then its parameter is the decimal notation of the
676 | // Unicode character that should be added to the output stream.
677 | // We need to check whether the stack contains \ucN control word. If it does,
678 | // we should remove the N characters from the output stream.
679 | case "u":
680 | $toText .= html_entity_decode("" . dechex($param) . ";");
681 | $ucDelta = @$stack[$j]["uc"];
682 | if ($ucDelta > 0)
683 | $i += $ucDelta;
684 | break;
685 | // Select line feeds, spaces and tabs.
686 | case "par":
687 | case "page":
688 | case "column":
689 | case "line":
690 | case "lbr":
691 | $toText .= "\n";
692 | break;
693 | case "emspace":
694 | case "enspace":
695 | case "qmspace":
696 | $toText .= " ";
697 | break;
698 | case "tab":
699 | $toText .= "\t";
700 | break;
701 | // Add current date and time instead of corresponding labels.
702 | case "chdate":
703 | $toText .= date("m.d.Y");
704 | break;
705 | case "chdpl":
706 | $toText .= date("l, j F Y");
707 | break;
708 | case "chdpa":
709 | $toText .= date("D, j M Y");
710 | break;
711 | case "chtime":
712 | $toText .= date("H:i:s");
713 | break;
714 | // Replace some reserved characters to their html analogs.
715 | case "emdash":
716 | $toText .= html_entity_decode("—");
717 | break;
718 | case "endash":
719 | $toText .= html_entity_decode("–");
720 | break;
721 | case "bullet":
722 | $toText .= html_entity_decode("");
723 | break;
724 | case "lquote":
725 | $toText .= html_entity_decode("‘");
726 | break;
727 | case "rquote":
728 | $toText .= html_entity_decode("’");
729 | break;
730 | case "ldblquote":
731 | $toText .= html_entity_decode("«");
732 | break;
733 | case "rdblquote":
734 | $toText .= html_entity_decode("»");
735 | break;
736 | // Add all other to the control words stack. If a control word
737 | // does not include parameters, set ¶m to true.
738 | default:
739 | $stack[$j][strtolower($word)] = empty($param) ? true : $param;
740 | break;
741 | }
742 | // Add data to the output stream if required.
743 | if (array_key_exists($j, $stack) && rtf_isPlainText($stack[$j]))
744 | $document .= $toText;
745 | }
746 |
747 | $i++;
748 | break;
749 | // If we read the opening brace {, then new subgroup starts and we add
750 | // new array stack element and write the data from previous stack element to it.
751 | case "{":
752 | $j++;
753 | if (array_key_exists($j, $stack)) {
754 | array_push($stack, $stack[$j]);
755 | }
756 | break;
757 | // If we read the closing brace }, then we reach the end of subgroup and should remove
758 | // the last stack element.
759 | case "}":
760 | array_pop($stack);
761 | $j--;
762 | break;
763 | // Skip “trash”.
764 | case '\0':
765 | case '\r':
766 | case '\f':
767 | case '\n':
768 | break;
769 | // Add other data to the output stream if required.
770 | default:
771 | if (array_key_exists($j, $stack) && rtf_isPlainText($stack[$j]))
772 | $document .= $c;
773 | break;
774 | }
775 | }
776 | // Return result.
777 | return $document;
778 | }
--------------------------------------------------------------------------------
/resources/Amazing Grace expanded.pro6:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 | {51 108 0 1816 864}
15 | 0.000000|0 0 0 0.3333333432674408|{4.0000002002606152, -3.9999995259110506}
16 |
17 | 0 0 0 1
18 | 1.000000
19 |
20 | e1xydGYxXGFuc2lcYW5zaWNwZzEyNTJcY29jb2FydGYxNTA0XGNvY29hc3VicnRmODMwCntcZm9udHRibH0Ke1xjb2xvcnRibDtccmVkMjU1XGdyZWVuMjU1XGJsdWUyNTU7fQp7XCpcZXhwYW5kZWRjb2xvcnRibDs7fQp9
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 | {51 108 0 1816 864}
33 | 0.000000|0 0 0 0.3333333432674408|{4.0000002002606152, -3.9999995259110506}
34 |
35 | 0 0 0 1
36 | 1.000000
37 |
38 | e1xydGYxXGFuc2lcYW5zaWNwZzEyNTJcY29jb2FydGYxNTA0XGNvY29hc3VicnRmODMwCntcZm9udHRibFxmMFxmc3dpc3NcZmNoYXJzZXQwIEFyaWFsTVQ7fQp7XGNvbG9ydGJsO1xyZWQyNTVcZ3JlZW4yNTVcYmx1ZTI1NTtccmVkMjU1XGdyZWVuMjU1XGJsdWUyNTU7XHJlZDUxXGdyZWVuNTFcYmx1ZTUxO30Ke1wqXGV4cGFuZGVkY29sb3J0Ymw7O1xjc2dyYXlcYzEwMDAwMDtcY3NnZW5lcmljcmdiXGMyMDAwMFxjMjAwMDBcYzIwMDAwO30KXHBhcmRcc2xsZWFkaW5nMzIwXHBhcmRpcm5hdHVyYWxccWNccGFydGlnaHRlbmZhY3RvcjAKClxmMFxiXGZzMTI3XGZzbWlsbGk2Mzk5OSBcY2YyIFxleHBuZDBcZXhwbmR0dzBca2VybmluZzAKXG91dGwwXHN0cm9rZXdpZHRoLTgwIFxzdHJva2VjMyBBbWF6aW5nIGdyYWNlISBIb3cgc3dlZXQgdGhlIHNvdW5kXApUaGF0IHNhdmVkIGEgd3JldGNoIGxpa2UgbWUhfQ==
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 | {51 108 0 1816 864}
47 | 0.000000|0 0 0 0.3333333432674408|{4.0000002002606152, -3.9999995259110506}
48 |
49 | 0 0 0 1
50 | 1.000000
51 |
52 | e1xydGYxXGFuc2lcYW5zaWNwZzEyNTJcY29jb2FydGYxNTA0XGNvY29hc3VicnRmODMwCntcZm9udHRibFxmMFxmc3dpc3NcZmNoYXJzZXQwIEFyaWFsTVQ7fQp7XGNvbG9ydGJsO1xyZWQyNTVcZ3JlZW4yNTVcYmx1ZTI1NTtccmVkMjU1XGdyZWVuMjU1XGJsdWUyNTU7XHJlZDUxXGdyZWVuNTFcYmx1ZTUxO30Ke1wqXGV4cGFuZGVkY29sb3J0Ymw7O1xjc2dyYXlcYzEwMDAwMDtcY3NnZW5lcmljcmdiXGMyMDAwMFxjMjAwMDBcYzIwMDAwO30KXHBhcmRcc2xsZWFkaW5nMzIwXHBhcmRpcm5hdHVyYWxccWNccGFydGlnaHRlbmZhY3RvcjAKClxmMFxiXGZzMTI3XGZzbWlsbGk2Mzk5OSBcY2YyIFxleHBuZDBcZXhwbmR0dzBca2VybmluZzAKXG91dGwwXHN0cm9rZXdpZHRoLTgwIFxzdHJva2VjMyBJIG9uY2Ugd2FzIGxvc3QsIGJ1dCBub3cgYW0gZm91bmQ7XApXYXMgYmxpbmQsIGJ1dCBub3cgSSBzZWUufQ==
53 |
54 |
55 |
56 |
57 |
58 |
59 |
60 |
61 |
62 |
63 |
64 | {51 108 0 1816 864}
65 | 0.000000|0 0 0 0.3333333432674408|{4.0000002002606152, -3.9999995259110506}
66 |
67 | 0 0 0 1
68 | 1.000000
69 |
70 | e1xydGYxXGFuc2lcYW5zaWNwZzEyNTJcY29jb2FydGYxNTA0XGNvY29hc3VicnRmODMwCntcZm9udHRibFxmMFxmc3dpc3NcZmNoYXJzZXQwIEFyaWFsTVQ7fQp7XGNvbG9ydGJsO1xyZWQyNTVcZ3JlZW4yNTVcYmx1ZTI1NTtccmVkMjU1XGdyZWVuMjU1XGJsdWUyNTU7XHJlZDUxXGdyZWVuNTFcYmx1ZTUxO30Ke1wqXGV4cGFuZGVkY29sb3J0Ymw7O1xjc2dyYXlcYzEwMDAwMDtcY3NnZW5lcmljcmdiXGMyMDAwMFxjMjAwMDBcYzIwMDAwO30KXHBhcmRcc2xsZWFkaW5nMzIwXHBhcmRpcm5hdHVyYWxccWNccGFydGlnaHRlbmZhY3RvcjAKClxmMFxiXGZzMTI3XGZzbWlsbGk2Mzk5OSBcY2YyIFxleHBuZDBcZXhwbmR0dzBca2VybmluZzAKXG91dGwwXHN0cm9rZXdpZHRoLTgwIFxzdHJva2VjMyBcJzkyVHdhcyBncmFjZSB0aGF0IHRhdWdodCBteSBoZWFydCB0byBmZWFyLFwKQW5kIGdyYWNlIG15IGZlYXJzIHJlbGlldmVkO30=
71 |
72 |
73 |
74 |
75 |
76 |
77 |
78 | {51 108 0 1816 864}
79 | 0.000000|0 0 0 0.3333333432674408|{4.0000002002606152, -3.9999995259110506}
80 |
81 | 0 0 0 1
82 | 1.000000
83 |
84 | e1xydGYxXGFuc2lcYW5zaWNwZzEyNTJcY29jb2FydGYxNTA0XGNvY29hc3VicnRmODMwCntcZm9udHRibFxmMFxmc3dpc3NcZmNoYXJzZXQwIEFyaWFsTVQ7fQp7XGNvbG9ydGJsO1xyZWQyNTVcZ3JlZW4yNTVcYmx1ZTI1NTtccmVkMjU1XGdyZWVuMjU1XGJsdWUyNTU7XHJlZDUxXGdyZWVuNTFcYmx1ZTUxO30Ke1wqXGV4cGFuZGVkY29sb3J0Ymw7O1xjc2dyYXlcYzEwMDAwMDtcY3NnZW5lcmljcmdiXGMyMDAwMFxjMjAwMDBcYzIwMDAwO30KXHBhcmRcc2xsZWFkaW5nMzIwXHBhcmRpcm5hdHVyYWxccWNccGFydGlnaHRlbmZhY3RvcjAKClxmMFxiXGZzMTI3XGZzbWlsbGk2Mzk5OSBcY2YyIFxleHBuZDBcZXhwbmR0dzBca2VybmluZzAKXG91dGwwXHN0cm9rZXdpZHRoLTgwIFxzdHJva2VjMyBIb3cgcHJlY2lvdXMgZGlkIHRoYXQgZ3JhY2UgYXBwZWFyXApUaGUgaG91ciBJIGZpcnN0IGJlbGlldmVkLn0=
85 |
86 |
87 |
88 |
89 |
90 |
91 |
92 |
93 |
94 |
95 |
96 | {51 108 0 1816 864}
97 | 0.000000|0 0 0 0.3333333432674408|{4.0000002002606152, -3.9999995259110506}
98 |
99 | 0 0 0 1
100 | 1.000000
101 |
102 | e1xydGYxXGFuc2lcYW5zaWNwZzEyNTJcY29jb2FydGYxNTA0XGNvY29hc3VicnRmODMwCntcZm9udHRibFxmMFxmc3dpc3NcZmNoYXJzZXQwIEFyaWFsTVQ7fQp7XGNvbG9ydGJsO1xyZWQyNTVcZ3JlZW4yNTVcYmx1ZTI1NTtccmVkMjU1XGdyZWVuMjU1XGJsdWUyNTU7XHJlZDUxXGdyZWVuNTFcYmx1ZTUxO30Ke1wqXGV4cGFuZGVkY29sb3J0Ymw7O1xjc2dyYXlcYzEwMDAwMDtcY3NnZW5lcmljcmdiXGMyMDAwMFxjMjAwMDBcYzIwMDAwO30KXHBhcmRcc2xsZWFkaW5nMzIwXHBhcmRpcm5hdHVyYWxccWNccGFydGlnaHRlbmZhY3RvcjAKClxmMFxiXGZzMTI3XGZzbWlsbGk2Mzk5OSBcY2YyIFxleHBuZDBcZXhwbmR0dzBca2VybmluZzAKXG91dGwwXHN0cm9rZXdpZHRoLTgwIFxzdHJva2VjMyBUaHJvdWdoIG1hbnkgZGFuZ2VycywgdG9pbHMgYW5kIHNuYXJlcyxcCkkgaGF2ZSBhbHJlYWR5IGNvbWU7fQ==
103 |
104 |
105 |
106 |
107 |
108 |
109 |
110 | {51 108 0 1816 864}
111 | 0.000000|0 0 0 0.3333333432674408|{4.0000002002606152, -3.9999995259110506}
112 |
113 | 0 0 0 1
114 | 1.000000
115 |
116 | e1xydGYxXGFuc2lcYW5zaWNwZzEyNTJcY29jb2FydGYxNTA0XGNvY29hc3VicnRmODMwCntcZm9udHRibFxmMFxmc3dpc3NcZmNoYXJzZXQwIEFyaWFsTVQ7fQp7XGNvbG9ydGJsO1xyZWQyNTVcZ3JlZW4yNTVcYmx1ZTI1NTtccmVkMjU1XGdyZWVuMjU1XGJsdWUyNTU7XHJlZDUxXGdyZWVuNTFcYmx1ZTUxO30Ke1wqXGV4cGFuZGVkY29sb3J0Ymw7O1xjc2dyYXlcYzEwMDAwMDtcY3NnZW5lcmljcmdiXGMyMDAwMFxjMjAwMDBcYzIwMDAwO30KXHBhcmRcc2xsZWFkaW5nMzIwXHBhcmRpcm5hdHVyYWxccWNccGFydGlnaHRlbmZhY3RvcjAKClxmMFxiXGZzMTI3XGZzbWlsbGk2Mzk5OSBcY2YyIFxleHBuZDBcZXhwbmR0dzBca2VybmluZzAKXG91dGwwXHN0cm9rZXdpZHRoLTgwIFxzdHJva2VjMyBcJzkyVGlzIGdyYWNlIGhhdGggYnJvdWdodCBtZSBzYWZlIHRodXMgZmFyLFwKQW5kIGdyYWNlIHdpbGwgbGVhZCBtZSBob21lLn0=
117 |
118 |
119 |
120 |
121 |
122 |
123 |
124 |
125 |
126 |
127 |
128 | {51 108 0 1816 864}
129 | 0.000000|0 0 0 0.3333333432674408|{4.0000002002606152, -3.9999995259110506}
130 |
131 | 0 0 0 1
132 | 1.000000
133 |
134 | e1xydGYxXGFuc2lcYW5zaWNwZzEyNTJcY29jb2FydGYxNTA0XGNvY29hc3VicnRmODMwCntcZm9udHRibFxmMFxmc3dpc3NcZmNoYXJzZXQwIEFyaWFsTVQ7fQp7XGNvbG9ydGJsO1xyZWQyNTVcZ3JlZW4yNTVcYmx1ZTI1NTtccmVkMjU1XGdyZWVuMjU1XGJsdWUyNTU7XHJlZDUxXGdyZWVuNTFcYmx1ZTUxO30Ke1wqXGV4cGFuZGVkY29sb3J0Ymw7O1xjc2dyYXlcYzEwMDAwMDtcY3NnZW5lcmljcmdiXGMyMDAwMFxjMjAwMDBcYzIwMDAwO30KXHBhcmRcc2xsZWFkaW5nMzIwXHBhcmRpcm5hdHVyYWxccWNccGFydGlnaHRlbmZhY3RvcjAKClxmMFxiXGZzMTI3XGZzbWlsbGk2Mzk5OSBcY2YyIFxleHBuZDBcZXhwbmR0dzBca2VybmluZzAKXG91dGwwXHN0cm9rZXdpZHRoLTgwIFxzdHJva2VjMyBUaGUgTG9yZCBoYXMgcHJvbWlzZWQgZ29vZCB0byBtZSxcCkhpcyBXb3JkIG15IGhvcGUgc2VjdXJlczt9
135 |
136 |
137 |
138 |
139 |
140 |
141 |
142 | {51 108 0 1816 864}
143 | 0.000000|0 0 0 0.3333333432674408|{4.0000002002606152, -3.9999995259110506}
144 |
145 | 0 0 0 1
146 | 1.000000
147 |
148 | e1xydGYxXGFuc2lcYW5zaWNwZzEyNTJcY29jb2FydGYxNTA0XGNvY29hc3VicnRmODMwCntcZm9udHRibFxmMFxmc3dpc3NcZmNoYXJzZXQwIEFyaWFsTVQ7fQp7XGNvbG9ydGJsO1xyZWQyNTVcZ3JlZW4yNTVcYmx1ZTI1NTtccmVkMjU1XGdyZWVuMjU1XGJsdWUyNTU7XHJlZDUxXGdyZWVuNTFcYmx1ZTUxO30Ke1wqXGV4cGFuZGVkY29sb3J0Ymw7O1xjc2dyYXlcYzEwMDAwMDtcY3NnZW5lcmljcmdiXGMyMDAwMFxjMjAwMDBcYzIwMDAwO30KXHBhcmRcc2xsZWFkaW5nMzIwXHBhcmRpcm5hdHVyYWxccWNccGFydGlnaHRlbmZhY3RvcjAKClxmMFxiXGZzMTI3XGZzbWlsbGk2Mzk5OSBcY2YyIFxleHBuZDBcZXhwbmR0dzBca2VybmluZzAKXG91dGwwXHN0cm9rZXdpZHRoLTgwIFxzdHJva2VjMyBIZSB3aWxsIG15IFNoaWVsZCBhbmQgUG9ydGlvbiBiZSxcCkFzIGxvbmcgYXMgbGlmZSBlbmR1cmVzLn0=
149 |
150 |
151 |
152 |
153 |
154 |
155 |
156 |
157 |
158 |
159 |
160 | {51 108 0 1816 864}
161 | 0.000000|0 0 0 0.3333333432674408|{4.0000002002606152, -3.9999995259110506}
162 |
163 | 0 0 0 1
164 | 1.000000
165 |
166 | e1xydGYxXGFuc2lcYW5zaWNwZzEyNTJcY29jb2FydGYxNTA0XGNvY29hc3VicnRmODMwCntcZm9udHRibFxmMFxmc3dpc3NcZmNoYXJzZXQwIEFyaWFsTVQ7fQp7XGNvbG9ydGJsO1xyZWQyNTVcZ3JlZW4yNTVcYmx1ZTI1NTtccmVkMjU1XGdyZWVuMjU1XGJsdWUyNTU7XHJlZDUxXGdyZWVuNTFcYmx1ZTUxO30Ke1wqXGV4cGFuZGVkY29sb3J0Ymw7O1xjc2dyYXlcYzEwMDAwMDtcY3NnZW5lcmljcmdiXGMyMDAwMFxjMjAwMDBcYzIwMDAwO30KXHBhcmRcc2xsZWFkaW5nMzIwXHBhcmRpcm5hdHVyYWxccWNccGFydGlnaHRlbmZhY3RvcjAKClxmMFxiXGZzMTI3XGZzbWlsbGk2Mzk5OSBcY2YyIFxleHBuZDBcZXhwbmR0dzBca2VybmluZzAKXG91dGwwXHN0cm9rZXdpZHRoLTgwIFxzdHJva2VjMyBZZWEsIHdoZW4gdGhpcyBmbGVzaCBhbmQgaGVhcnQgc2hhbGwgZmFpbCxcCkFuZCBtb3J0YWwgbGlmZSBzaGFsbCBjZWFzZSx9
167 |
168 |
169 |
170 |
171 |
172 |
173 |
174 | {51 108 0 1816 864}
175 | 0.000000|0 0 0 0.3333333432674408|{4.0000002002606152, -3.9999995259110506}
176 |
177 | 0 0 0 1
178 | 1.000000
179 |
180 | e1xydGYxXGFuc2lcYW5zaWNwZzEyNTJcY29jb2FydGYxNTA0XGNvY29hc3VicnRmODMwCntcZm9udHRibFxmMFxmc3dpc3NcZmNoYXJzZXQwIEFyaWFsTVQ7fQp7XGNvbG9ydGJsO1xyZWQyNTVcZ3JlZW4yNTVcYmx1ZTI1NTtccmVkMjU1XGdyZWVuMjU1XGJsdWUyNTU7XHJlZDUxXGdyZWVuNTFcYmx1ZTUxO30Ke1wqXGV4cGFuZGVkY29sb3J0Ymw7O1xjc2dyYXlcYzEwMDAwMDtcY3NnZW5lcmljcmdiXGMyMDAwMFxjMjAwMDBcYzIwMDAwO30KXHBhcmRcc2xsZWFkaW5nMzIwXHBhcmRpcm5hdHVyYWxccWNccGFydGlnaHRlbmZhY3RvcjAKClxmMFxiXGZzMTI3XGZzbWlsbGk2Mzk5OSBcY2YyIFxleHBuZDBcZXhwbmR0dzBca2VybmluZzAKXG91dGwwXHN0cm9rZXdpZHRoLTgwIFxzdHJva2VjMyBJIHNoYWxsIHBvc3Nlc3MsIHdpdGhpbiB0aGUgdmVpbCxcCkEgbGlmZSBvZiBqb3kgYW5kIHBlYWNlLn0=
181 |
182 |
183 |
184 |
185 |
186 |
187 |
188 |
189 |
190 |
191 |
192 | {51 108 0 1816 864}
193 | 0.000000|0 0 0 0.3333333432674408|{4.0000002002606152, -3.9999995259110506}
194 |
195 | 0 0 0 1
196 | 1.000000
197 |
198 | e1xydGYxXGFuc2lcYW5zaWNwZzEyNTJcY29jb2FydGYxNTA0XGNvY29hc3VicnRmODMwCntcZm9udHRibFxmMFxmc3dpc3NcZmNoYXJzZXQwIEFyaWFsTVQ7fQp7XGNvbG9ydGJsO1xyZWQyNTVcZ3JlZW4yNTVcYmx1ZTI1NTtccmVkMjU1XGdyZWVuMjU1XGJsdWUyNTU7XHJlZDUxXGdyZWVuNTFcYmx1ZTUxO30Ke1wqXGV4cGFuZGVkY29sb3J0Ymw7O1xjc2dyYXlcYzEwMDAwMDtcY3NnZW5lcmljcmdiXGMyMDAwMFxjMjAwMDBcYzIwMDAwO30KXHBhcmRcc2xsZWFkaW5nMzIwXHBhcmRpcm5hdHVyYWxccWNccGFydGlnaHRlbmZhY3RvcjAKClxmMFxiXGZzMTI3XGZzbWlsbGk2Mzk5OSBcY2YyIFxleHBuZDBcZXhwbmR0dzBca2VybmluZzAKXG91dGwwXHN0cm9rZXdpZHRoLTgwIFxzdHJva2VjMyBUaGUgZWFydGggc2hhbGwgc29vbiBkaXNzb2x2ZSBsaWtlIHNub3csXApUaGUgc3VuIGZvcmJlYXIgdG8gc2hpbmU7fQ==
199 |
200 |
201 |
202 |
203 |
204 |
205 |
206 | {51 108 0 1816 864}
207 | 0.000000|0 0 0 0.3333333432674408|{4.0000002002606152, -3.9999995259110506}
208 |
209 | 0 0 0 1
210 | 1.000000
211 |
212 | e1xydGYxXGFuc2lcYW5zaWNwZzEyNTJcY29jb2FydGYxNTA0XGNvY29hc3VicnRmODMwCntcZm9udHRibFxmMFxmc3dpc3NcZmNoYXJzZXQwIEFyaWFsTVQ7fQp7XGNvbG9ydGJsO1xyZWQyNTVcZ3JlZW4yNTVcYmx1ZTI1NTtccmVkMjU1XGdyZWVuMjU1XGJsdWUyNTU7XHJlZDUxXGdyZWVuNTFcYmx1ZTUxO30Ke1wqXGV4cGFuZGVkY29sb3J0Ymw7O1xjc2dyYXlcYzEwMDAwMDtcY3NnZW5lcmljcmdiXGMyMDAwMFxjMjAwMDBcYzIwMDAwO30KXHBhcmRcc2xsZWFkaW5nMzIwXHBhcmRpcm5hdHVyYWxccWNccGFydGlnaHRlbmZhY3RvcjAKClxmMFxiXGZzMTI3XGZzbWlsbGk2Mzk5OSBcY2YyIFxleHBuZDBcZXhwbmR0dzBca2VybmluZzAKXG91dGwwXHN0cm9rZXdpZHRoLTgwIFxzdHJva2VjMyBCdXQgR29kLCB3aG8gY2FsbGVkIG1lIGhlcmUgYmVsb3csXApXaWxsIGJlIGZvcmV2ZXIgbWluZS59
213 |
214 |
215 |
216 |
217 |
218 |
219 |
220 |
221 |
222 |
223 |
224 | {51 108 0 1816 864}
225 | 0.000000|0 0 0 0.3333333432674408|{4.0000002002606152, -3.9999995259110506}
226 |
227 | 0 0 0 1
228 | 1.000000
229 |
230 | e1xydGYxXGFuc2lcYW5zaWNwZzEyNTJcY29jb2FydGYxNTA0XGNvY29hc3VicnRmODMwCntcZm9udHRibFxmMFxmc3dpc3NcZmNoYXJzZXQwIEFyaWFsTVQ7fQp7XGNvbG9ydGJsO1xyZWQyNTVcZ3JlZW4yNTVcYmx1ZTI1NTtccmVkMjU1XGdyZWVuMjU1XGJsdWUyNTU7XHJlZDUxXGdyZWVuNTFcYmx1ZTUxO30Ke1wqXGV4cGFuZGVkY29sb3J0Ymw7O1xjc2dyYXlcYzEwMDAwMDtcY3NnZW5lcmljcmdiXGMyMDAwMFxjMjAwMDBcYzIwMDAwO30KXHBhcmRcc2xsZWFkaW5nMzIwXHBhcmRpcm5hdHVyYWxccWNccGFydGlnaHRlbmZhY3RvcjAKClxmMFxiXGZzMTI3XGZzbWlsbGk2Mzk5OSBcY2YyIFxleHBuZDBcZXhwbmR0dzBca2VybmluZzAKXG91dGwwXHN0cm9rZXdpZHRoLTgwIFxzdHJva2VjMyBXaGVuIHdlXCc5MnZlIGJlZW4gdGhlcmUgdGVuIHRob3VzYW5kIHllYXJzLFwKQnJpZ2h0IHNoaW5pbmcgYXMgdGhlIHN1bix9
231 |
232 |
233 |
234 |
235 |
236 |
237 |
238 | {51 108 0 1816 864}
239 | 0.000000|0 0 0 0.3333333432674408|{4.0000002002606152, -3.9999995259110506}
240 |
241 | 0 0 0 1
242 | 1.000000
243 |
244 | e1xydGYxXGFuc2lcYW5zaWNwZzEyNTJcY29jb2FydGYxNTA0XGNvY29hc3VicnRmODMwCntcZm9udHRibFxmMFxmc3dpc3NcZmNoYXJzZXQwIEFyaWFsTVQ7fQp7XGNvbG9ydGJsO1xyZWQyNTVcZ3JlZW4yNTVcYmx1ZTI1NTtccmVkMjU1XGdyZWVuMjU1XGJsdWUyNTU7XHJlZDUxXGdyZWVuNTFcYmx1ZTUxO30Ke1wqXGV4cGFuZGVkY29sb3J0Ymw7O1xjc2dyYXlcYzEwMDAwMDtcY3NnZW5lcmljcmdiXGMyMDAwMFxjMjAwMDBcYzIwMDAwO30KXHBhcmRcc2xsZWFkaW5nMzIwXHBhcmRpcm5hdHVyYWxccWNccGFydGlnaHRlbmZhY3RvcjAKClxmMFxiXGZzMTI3XGZzbWlsbGk2Mzk5OSBcY2YyIFxleHBuZDBcZXhwbmR0dzBca2VybmluZzAKXG91dGwwXHN0cm9rZXdpZHRoLTgwIFxzdHJva2VjMyBXZVwnOTJ2ZSBubyBsZXNzIGRheXMgdG8gc2luZyBHb2RcJzkycyBwcmFpc2VcClRoYW4gd2hlbiB3ZVwnOTJkIGZpcnN0IGJlZ3VuLn0=
245 |
246 |
247 |
248 |
249 |
250 |
251 |
252 |
253 |
254 |
255 |
256 | {51 108 0 1816 864}
257 | 0.000000|0 0 0 0.3333333432674408|{4.0000002002606152, -3.9999995259110506}
258 |
259 | 0 0 0 1
260 | 1.000000
261 |
262 | e1xydGYxXGFuc2lcYW5zaWNwZzEyNTJcY29jb2FydGYxNTA0XGNvY29hc3VicnRmODMwCntcZm9udHRibH0Ke1xjb2xvcnRibDtccmVkMjU1XGdyZWVuMjU1XGJsdWUyNTU7fQp7XCpcZXhwYW5kZWRjb2xvcnRibDs7fQp9
263 |
264 |
265 |
266 |
267 |
268 |
269 |
270 |
--------------------------------------------------------------------------------