gly 0.0.2 → 0.0.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/lib/gly.rb +2 -0
- data/lib/gly/cli.rb +53 -2
- data/lib/gly/document.rb +24 -0
- data/lib/gly/document_gabc_convertor.rb +7 -3
- data/lib/gly/document_ly_convertor.rb +58 -0
- data/lib/gly/document_ly_convertor.rb~ +37 -0
- data/lib/gly/options.rb~ +15 -0
- data/lib/gly/parser.rb +26 -6
- data/lib/gly/preview_builder.rb +38 -0
- data/lib/gly/preview_builder.rb~ +38 -0
- data/lib/gly/preview_generator.rb +86 -15
- data/lib/gly/templates/lualatex_document.tex +29 -5
- data/tests/examples/gly/expected/explicit_music.gabc +2 -0
- data/tests/examples/gly/expected/explicit_music.gabc~ +1 -0
- data/tests/examples/gly/given/explicit_music.gly +3 -0
- data/tests/examples/gly/given/explicit_music.gly~ +3 -0
- data/tests/string_helpers_test.rb +3 -1
- metadata +12 -106
- data/tests/examples/gly/expected/empty.gabc +0 -1
- data/tests/examples/gly/given/empty.gly +0 -0
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: c2057f1f3a833d1b6bb382573386d7519fd1f83a
|
4
|
+
data.tar.gz: 2c770e056bc7c3c0a6a7aeb689a9888615334f17
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 6860d75f1e55af109e10535f76b404d074b6bc9bafad0b13784f98ca706f448705a1116216598052b364802e6c7e3b3b4dde87557d5926d13417dbb69ab488ba
|
7
|
+
data.tar.gz: fd1df56ea96ca42c779086e8b495fcb4da4dddeb77fa13a87238506dd5044802f9486ba220a617efef925671fee73afa7cb98f620ea9f508c7df70c0a7df0180
|
data/lib/gly.rb
CHANGED
data/lib/gly/cli.rb
CHANGED
@@ -1,16 +1,38 @@
|
|
1
1
|
require 'thor'
|
2
2
|
|
3
|
+
begin
|
4
|
+
require 'grely'
|
5
|
+
rescue LoadError
|
6
|
+
end
|
7
|
+
|
3
8
|
module Gly
|
4
9
|
# implements the 'gly' executable
|
5
10
|
class CLI < Thor
|
11
|
+
class_option :separator, aliases: :s, banner: 'syllable separator (default is double dash "--")'
|
12
|
+
|
6
13
|
desc 'gabc FILE ...', 'convert gly to gabc'
|
7
14
|
def gabc(*files)
|
8
|
-
files.each
|
15
|
+
files.each do |f|
|
16
|
+
DocumentGabcConvertor.new(parser.parse(f)).convert
|
17
|
+
end
|
9
18
|
end
|
10
19
|
|
11
20
|
desc 'preview FILE ...', 'convert to gabc AND generate pdf preview'
|
21
|
+
option :no_build, type: :boolean, aliases: :B, banner: 'only generate preview assets, don\'t compile them'
|
22
|
+
option :no_document, type: :boolean, aliases: :D, banner: 'produce main LaTeX file without document definition; in this case --no-build is applied automatically'
|
23
|
+
option :full_headers, type: :boolean, aliases: :h, banner: 'include full document and score headers'
|
24
|
+
option :template, aliases: :t, banner: 'use custom document template'
|
12
25
|
def preview(*files)
|
13
|
-
|
26
|
+
tpl = nil
|
27
|
+
tpl = File.read(options[:template]) if options[:template]
|
28
|
+
|
29
|
+
opts = options.to_h
|
30
|
+
opts[:suffix_always] = true
|
31
|
+
|
32
|
+
files.each do |f|
|
33
|
+
gen = PreviewGenerator.new template: tpl, options: opts
|
34
|
+
gen.process(parser.parse(f))
|
35
|
+
end
|
14
36
|
end
|
15
37
|
|
16
38
|
desc 'list FILE ...', 'list scores contained in files'
|
@@ -37,5 +59,34 @@ module Gly
|
|
37
59
|
|
38
60
|
exit(lister.error? ? 1 : 0)
|
39
61
|
end
|
62
|
+
|
63
|
+
desc 'ly FILE ...', 'transform gly document to lilypond document'
|
64
|
+
def ly(*files)
|
65
|
+
unless defined? LilypondConvertor
|
66
|
+
STDERR.puts "'lygre' gem not found. Please, install lygre in order to run 'gly ly'."
|
67
|
+
exit 1
|
68
|
+
end
|
69
|
+
|
70
|
+
files.each do |f|
|
71
|
+
DocumentLyConvertor.new(parser.parse(f)).convert
|
72
|
+
end
|
73
|
+
end
|
74
|
+
|
75
|
+
private
|
76
|
+
|
77
|
+
def parser
|
78
|
+
Parser.new options[:separator]
|
79
|
+
end
|
80
|
+
|
81
|
+
class << self
|
82
|
+
# override Thor's default handler
|
83
|
+
def handle_no_command_error(command, has_namespace=$thor_runner)
|
84
|
+
if has_namespace
|
85
|
+
fail Thor::UndefinedCommandError, "Could not find command #{command.inspect} in #{namespace.inspect} namespace."
|
86
|
+
else
|
87
|
+
fail Thor::UndefinedCommandError, "Could not find command #{command.inspect}. Did you mean 'gly preview #{command}' ?"
|
88
|
+
end
|
89
|
+
end
|
90
|
+
end
|
40
91
|
end
|
41
92
|
end
|
data/lib/gly/document.rb
CHANGED
@@ -5,11 +5,35 @@ module Gly
|
|
5
5
|
class Document
|
6
6
|
def initialize
|
7
7
|
@scores = []
|
8
|
+
@scores_by_id = {}
|
8
9
|
@header = Headers.new
|
9
10
|
@path = nil
|
10
11
|
end
|
11
12
|
|
12
13
|
attr_reader :scores, :header
|
13
14
|
attr_accessor :path
|
15
|
+
|
16
|
+
def <<(score)
|
17
|
+
@scores << score
|
18
|
+
|
19
|
+
sid = score.headers['id']
|
20
|
+
if sid
|
21
|
+
if @scores_by_id.has_key? sid
|
22
|
+
raise ArgumentError.new("More than one score with id '#{sid}'.")
|
23
|
+
end
|
24
|
+
|
25
|
+
@scores_by_id[sid] = score
|
26
|
+
end
|
27
|
+
|
28
|
+
self
|
29
|
+
end
|
30
|
+
|
31
|
+
def [](key)
|
32
|
+
if key.is_a? Integer
|
33
|
+
@scores[key]
|
34
|
+
else
|
35
|
+
@scores_by_id[key]
|
36
|
+
end
|
37
|
+
end
|
14
38
|
end
|
15
39
|
end
|
@@ -1,7 +1,8 @@
|
|
1
1
|
module Gly
|
2
2
|
class DocumentGabcConvertor
|
3
|
-
def initialize(document)
|
3
|
+
def initialize(document, **options)
|
4
4
|
@doc = document
|
5
|
+
@options = options
|
5
6
|
end
|
6
7
|
|
7
8
|
def convert
|
@@ -24,8 +25,11 @@ module Gly
|
|
24
25
|
private
|
25
26
|
|
26
27
|
def output_fname(score, score_index=nil)
|
27
|
-
|
28
|
-
|
28
|
+
if @doc.scores.size == 1 && !@options[:suffix_always]
|
29
|
+
score_id = ''
|
30
|
+
else
|
31
|
+
score_id = '_' + (score.headers['id'] || score_index.to_s)
|
32
|
+
end
|
29
33
|
|
30
34
|
File.basename(@doc.path)
|
31
35
|
.sub(/\.gly\Z/i, "#{score_id}.gabc")
|
@@ -0,0 +1,58 @@
|
|
1
|
+
require 'stringio'
|
2
|
+
|
3
|
+
module Gly
|
4
|
+
# Converts gly to modern-notation LilyPond
|
5
|
+
#
|
6
|
+
# expects the 'lygre' gem
|
7
|
+
# (which is only an optional dependency of gly)
|
8
|
+
class DocumentLyConvertor
|
9
|
+
def initialize(document)
|
10
|
+
@doc = document
|
11
|
+
end
|
12
|
+
|
13
|
+
def convert
|
14
|
+
gabcor = GabcConvertor.new
|
15
|
+
parser = GabcParser.new
|
16
|
+
lilyor = LilypondConvertor.new cadenza: true, version: false
|
17
|
+
|
18
|
+
ly_output = StringIO.new
|
19
|
+
|
20
|
+
ly_output.puts '\version "2.18.0"'
|
21
|
+
ly_output.puts
|
22
|
+
ly_output.puts header @doc.header
|
23
|
+
ly_output.puts
|
24
|
+
ly_output.puts default_style
|
25
|
+
ly_output.puts
|
26
|
+
|
27
|
+
@doc.scores.each do |score|
|
28
|
+
gabc = gabcor.convert(score).string
|
29
|
+
parsed_score = parser.parse gabc
|
30
|
+
begin
|
31
|
+
ly_output.puts lilyor.convert parsed_score.create_score
|
32
|
+
rescue NoMethodError
|
33
|
+
ly_output.puts "\\markup{error processing score \\italic{#{score.lyrics.readable}}}"
|
34
|
+
end
|
35
|
+
|
36
|
+
ly_output.puts
|
37
|
+
end
|
38
|
+
|
39
|
+
out_fname = File.basename(@doc.path) + '.ly'
|
40
|
+
File.open(out_fname, 'w') do |fw|
|
41
|
+
fw.puts ly_output.string
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
private
|
46
|
+
|
47
|
+
def header(h)
|
48
|
+
fields = h.each_pair.collect do |k,v|
|
49
|
+
" #{k} = \"#{v}\""
|
50
|
+
end
|
51
|
+
"\\header {\n#{fields.join("\n")}\n}\n"
|
52
|
+
end
|
53
|
+
|
54
|
+
def default_style
|
55
|
+
'\layout { \context Score \override TimeSignature #\'stencil = ##f }'
|
56
|
+
end
|
57
|
+
end
|
58
|
+
end
|
@@ -0,0 +1,37 @@
|
|
1
|
+
module Gly
|
2
|
+
class DocumentLyConvertor < DocumentGabcConvertor
|
3
|
+
def initialize(document)
|
4
|
+
@doc = document
|
5
|
+
end
|
6
|
+
|
7
|
+
def convert
|
8
|
+
each_score_with_gabcname do |score, out_fname|
|
9
|
+
File.open(out_fname, 'w') do |fw|
|
10
|
+
GabcConvertor.new.convert score, fw
|
11
|
+
end
|
12
|
+
yield score, out_fname if block_given?
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
# iterates over document scores,
|
17
|
+
# yields score and filename of it's generated gabc file
|
18
|
+
def each_score_with_gabcname
|
19
|
+
@doc.scores.each_with_index do |score, si|
|
20
|
+
yield score, output_fname(score, si)
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
private
|
25
|
+
|
26
|
+
def output_fname(score, score_index=nil)
|
27
|
+
if @doc.scores.size == 1
|
28
|
+
score_id = ''
|
29
|
+
else
|
30
|
+
score_id = '_' + (score.headers['id'] || score_index.to_s)
|
31
|
+
end
|
32
|
+
|
33
|
+
File.basename(@doc.path)
|
34
|
+
.sub(/\.gly\Z/i, "#{score_id}.gabc")
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
data/lib/gly/options.rb~
ADDED
data/lib/gly/parser.rb
CHANGED
@@ -3,8 +3,8 @@ require 'stringio'
|
|
3
3
|
module Gly
|
4
4
|
# parses gly source
|
5
5
|
class Parser
|
6
|
-
def initialize(syllable_separator=
|
7
|
-
@syllable_separator = syllable_separator
|
6
|
+
def initialize(syllable_separator=nil)
|
7
|
+
@syllable_separator = syllable_separator || '--'
|
8
8
|
end
|
9
9
|
|
10
10
|
def parse(source)
|
@@ -28,7 +28,7 @@ module Gly
|
|
28
28
|
end
|
29
29
|
|
30
30
|
def parse_str(str)
|
31
|
-
parse_io(StringIO.new(
|
31
|
+
parse_io(StringIO.new(str))
|
32
32
|
end
|
33
33
|
|
34
34
|
def parse_io(io)
|
@@ -52,6 +52,10 @@ module Gly
|
|
52
52
|
@score = @doc.header
|
53
53
|
elsif header_line? line
|
54
54
|
parse_header line
|
55
|
+
elsif explicit_lyrics? line
|
56
|
+
parse_lyrics line
|
57
|
+
elsif explicit_music? line
|
58
|
+
parse_music line
|
55
59
|
elsif lyrics_line? line
|
56
60
|
parse_lyrics line
|
57
61
|
else
|
@@ -88,8 +92,18 @@ module Gly
|
|
88
92
|
|
89
93
|
EXPLICIT_LYRICS_RE = /\A\\l(yrics)?\s+/
|
90
94
|
|
95
|
+
def explicit_lyrics?(str)
|
96
|
+
str =~ EXPLICIT_LYRICS_RE
|
97
|
+
end
|
98
|
+
|
99
|
+
EXPLICIT_MUSIC_RE = /\A\\m(usic)?\s+/
|
100
|
+
|
101
|
+
def explicit_music?(str)
|
102
|
+
str =~ EXPLICIT_MUSIC_RE
|
103
|
+
end
|
104
|
+
|
91
105
|
def lyrics_line?(str)
|
92
|
-
str
|
106
|
+
!contains_square_brackets?(str) && (str.include?(@syllable_separator) || contains_unmusical_letters?(str))
|
93
107
|
end
|
94
108
|
|
95
109
|
def in_header_block?
|
@@ -98,7 +112,11 @@ module Gly
|
|
98
112
|
|
99
113
|
def contains_unmusical_letters?(str)
|
100
114
|
letters = str.gsub(/[\W\d_]+/, '')
|
101
|
-
letters !~ /\A[a-
|
115
|
+
letters !~ /\A[a-morsvwxz]*\Z/i # incomplete gabc music letters!
|
116
|
+
end
|
117
|
+
|
118
|
+
def contains_square_brackets?(str)
|
119
|
+
str.include? '['
|
102
120
|
end
|
103
121
|
|
104
122
|
def parse_header(str)
|
@@ -118,6 +136,8 @@ module Gly
|
|
118
136
|
end
|
119
137
|
|
120
138
|
def parse_music(str)
|
139
|
+
str = str.sub(EXPLICIT_MUSIC_RE, '')
|
140
|
+
|
121
141
|
# music chunks: split by whitespace out of brackets
|
122
142
|
StringHelpers.music_split(str).each do |chunk|
|
123
143
|
@score.music << chunk
|
@@ -126,7 +146,7 @@ module Gly
|
|
126
146
|
|
127
147
|
def push_score
|
128
148
|
if @score.is_a?(ParsedScore) && !@score.empty?
|
129
|
-
@doc
|
149
|
+
@doc << @score
|
130
150
|
end
|
131
151
|
end
|
132
152
|
end
|
@@ -0,0 +1,38 @@
|
|
1
|
+
module Gly
|
2
|
+
# *builds* the pdf preview from assets prepared
|
3
|
+
# by PreviewGenerator
|
4
|
+
class PreviewBuilder
|
5
|
+
def initialize
|
6
|
+
@gabcs = []
|
7
|
+
@main_tex = nil
|
8
|
+
end
|
9
|
+
|
10
|
+
def add_gabc(path)
|
11
|
+
@gabcs << path
|
12
|
+
end
|
13
|
+
|
14
|
+
attr_accessor :main_tex
|
15
|
+
|
16
|
+
def build
|
17
|
+
@gabcs.each do |g|
|
18
|
+
exec 'gregorio', g
|
19
|
+
end
|
20
|
+
|
21
|
+
exec 'lualatex', @main_tex
|
22
|
+
end
|
23
|
+
|
24
|
+
private
|
25
|
+
|
26
|
+
def exec(progname, *args)
|
27
|
+
ok = system progname, *args
|
28
|
+
unless ok
|
29
|
+
case $?.exitstatus
|
30
|
+
when 127
|
31
|
+
STDERR.puts "'#{progname}' is required for this gly command to work, but it was not found. Please, ensure that '#{progname}' is installed in one of the directories listed in your PATH and try again."
|
32
|
+
else
|
33
|
+
STDERR.puts "'#{progname}' exited with exit code #{$?.to_i}. Let's continue and see what happens ..."
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
@@ -0,0 +1,38 @@
|
|
1
|
+
module Gly
|
2
|
+
# *builds* the pdf preview from assets prepared
|
3
|
+
# by PreviewGenerator
|
4
|
+
class PreviewBuilder
|
5
|
+
def initialize
|
6
|
+
@gabcs = []
|
7
|
+
@main_tex = nil
|
8
|
+
end
|
9
|
+
|
10
|
+
def add_gabc(path)
|
11
|
+
@gabcs << path
|
12
|
+
end
|
13
|
+
|
14
|
+
attr_writer :main_tex
|
15
|
+
|
16
|
+
def build
|
17
|
+
@gabcs.each do |g|
|
18
|
+
exec 'gregorio', g
|
19
|
+
end
|
20
|
+
|
21
|
+
exec 'lualatex', @main_tex
|
22
|
+
end
|
23
|
+
|
24
|
+
private
|
25
|
+
|
26
|
+
def exec(progname, *args)
|
27
|
+
ok = system progname, *args
|
28
|
+
unless ok
|
29
|
+
case $?.exitstatus
|
30
|
+
when 127
|
31
|
+
STDERR.puts "'#{progname}' is required for this gly command to work, but it was not found. Please, ensure that '#{progname}' is installed in one of the directories listed in your PATH and try again."
|
32
|
+
else
|
33
|
+
STDERR.puts "'#{progname}' exited with exit code #{$?.to_i}. Let's continue and see what happens ..."
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
@@ -4,18 +4,38 @@ module Gly
|
|
4
4
|
# Takes Gly::Document, builds a pdf preview
|
5
5
|
# (or at least generates all necessary assets)
|
6
6
|
class PreviewGenerator
|
7
|
+
def initialize(**options)
|
8
|
+
@preview_dest = nil
|
9
|
+
|
10
|
+
@template = options.delete(:template) || default_template
|
11
|
+
@builder = options.delete(:builder) || PreviewBuilder.new
|
12
|
+
@options = options.delete(:options) || {}
|
13
|
+
end
|
14
|
+
|
15
|
+
# IO to which the main LaTeX document should be written.
|
16
|
+
# If not set, a file will be created with name based on
|
17
|
+
# the source file name.
|
18
|
+
attr_accessor :preview_dest
|
19
|
+
|
7
20
|
def process(document)
|
8
|
-
|
9
|
-
|
21
|
+
convertor = DocumentGabcConvertor.new(document, **@options)
|
22
|
+
convertor.convert
|
10
23
|
|
11
24
|
doc_body = fw = StringIO.new
|
12
25
|
|
13
|
-
|
26
|
+
if @options[:full_headers]
|
27
|
+
fw.puts header_table document.header
|
28
|
+
end
|
29
|
+
|
14
30
|
convertor.each_score_with_gabcname do |score, gabc_fname|
|
15
|
-
|
31
|
+
@builder.add_gabc gabc_fname
|
32
|
+
|
33
|
+
if @options[:full_headers]
|
34
|
+
fw.puts header_table score.headers
|
35
|
+
end
|
36
|
+
|
16
37
|
gtex_fname = gabc_fname.sub /\.gabc/i, ''
|
17
|
-
piece_title = %w(book manuscript arranger author).collect do |m|
|
18
|
-
score.headers[m]
|
38
|
+
piece_title = %w(book manuscript arranger author).collect do |m| score.headers[m]
|
19
39
|
end.delete_if(&:nil?).join ', '
|
20
40
|
fw.puts "\\commentary{\\footnotesize{#{piece_title}}}\n" unless piece_title.empty?
|
21
41
|
|
@@ -30,19 +50,70 @@ module Gly
|
|
30
50
|
fw.puts "\\includescore{#{gtex_fname}}\n\\vspace{1cm}"
|
31
51
|
end
|
32
52
|
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
53
|
+
if @options['no_document']
|
54
|
+
tex = doc_body.string
|
55
|
+
else
|
56
|
+
replacements = {
|
57
|
+
glyvars: header_variables(document.header),
|
58
|
+
body: doc_body.string
|
59
|
+
}
|
60
|
+
tex = @template % replacements
|
61
|
+
end
|
62
|
+
|
63
|
+
with_preview_io(document.path) do |fw|
|
64
|
+
@builder.main_tex = fw.path if fw.respond_to? :path
|
39
65
|
|
40
|
-
out_fname = File.basename(document.path).sub(/\.gly\Z/i, '.tex')
|
41
|
-
File.open(out_fname, 'w') do |fw|
|
42
66
|
fw.puts tex
|
43
67
|
end
|
44
68
|
|
45
|
-
|
69
|
+
build_disabled = @options['no_build'] || @options['no_document']
|
70
|
+
if @builder.main_tex && !build_disabled
|
71
|
+
@builder.build
|
72
|
+
end
|
73
|
+
end
|
74
|
+
|
75
|
+
private
|
76
|
+
|
77
|
+
def with_preview_io(src_name)
|
78
|
+
if @preview_dest
|
79
|
+
yield @preview_dest
|
80
|
+
return
|
81
|
+
end
|
82
|
+
|
83
|
+
File.open(preview_fname(src_name), 'w') do |fw|
|
84
|
+
yield fw
|
85
|
+
end
|
86
|
+
end
|
87
|
+
|
88
|
+
def preview_fname(src_name)
|
89
|
+
File.basename(src_name).sub(/\.gly\Z/i, '.tex')
|
90
|
+
end
|
91
|
+
|
92
|
+
def default_template
|
93
|
+
File.read(File.join(File.dirname(__FILE__), 'templates/lualatex_document.tex'))
|
94
|
+
end
|
95
|
+
|
96
|
+
# full header of a score/file as table
|
97
|
+
def header_table(header)
|
98
|
+
return '' if header.empty?
|
99
|
+
|
100
|
+
cols = header.each_pair.collect {|k,v| "#{k} & #{v} \\\\" }
|
101
|
+
|
102
|
+
"\\begin{tabular}{ | r | l | } \\hline %s \\hline \\end{tabular}\n\n" % cols.join("\\hline\n")
|
103
|
+
end
|
104
|
+
|
105
|
+
# transforms header to LaTeX command definitions
|
106
|
+
def header_variables(header)
|
107
|
+
header.each_pair.collect do |k,v|
|
108
|
+
'\newcommand{\%s}{%s}' % [latex_cmd_name(k), v]
|
109
|
+
end.join("\n")
|
110
|
+
end
|
111
|
+
|
112
|
+
def latex_cmd_name(header_name)
|
113
|
+
sanitized = header_name
|
114
|
+
.gsub(/\s+/, '')
|
115
|
+
.gsub(/[-_]+(\w)/) {|m| m[1].upcase }
|
116
|
+
prefixed = 'gly' + sanitized[0].upcase + sanitized[1..-1]
|
46
117
|
end
|
47
118
|
end
|
48
119
|
end
|
@@ -5,23 +5,47 @@
|
|
5
5
|
\usepackage[left=2cm, right=2cm, top=2cm, bottom=2cm]{geometry}
|
6
6
|
|
7
7
|
\usepackage{fontspec}
|
8
|
+
\usepackage[hidelinks]{hyperref}
|
9
|
+
\usepackage{etoolbox}
|
8
10
|
|
9
11
|
%% for gregorio
|
10
12
|
\usepackage{luatextra}
|
11
13
|
\usepackage{graphicx}
|
12
14
|
\usepackage{gregoriotex}
|
13
15
|
|
14
|
-
|
16
|
+
%{glyvars}
|
17
|
+
|
18
|
+
%% header fields like title, transcription-date etc.
|
19
|
+
%% are translated by 'gly preview' to latex commands like
|
20
|
+
%% \glyTitle \glyTranscriptionDate etc.
|
21
|
+
%% (All of your document header fields are handled this way.)
|
22
|
+
%% These commands can be used for various purposes,
|
23
|
+
%% but we will use them only to assemble document title:
|
24
|
+
\newcommand{\glyPreviewTitle}{
|
25
|
+
\begin{center}
|
26
|
+
\ifdef{\glyTitle}{{\huge \glyTitle}}{}
|
27
|
+
|
28
|
+
\ifdef{\glyAuthor}{\emph{\glyAuthor}}{}
|
29
|
+
|
30
|
+
\ifdef{\glyYear}{\glyYear}{}
|
31
|
+
|
32
|
+
\vspace{6mm}
|
33
|
+
\end{center}
|
34
|
+
}
|
35
|
+
|
36
|
+
\newcommand{\glyPreviewTagline}{
|
37
|
+
\begin{center}
|
38
|
+
\texttt{\href{https://github.com/igneus/gly}{gly preview} \today}
|
39
|
+
\end{center}
|
40
|
+
}
|
15
41
|
|
16
42
|
\begin{document}
|
17
43
|
|
18
|
-
|
44
|
+
\glyPreviewTitle
|
19
45
|
|
20
46
|
%{body}
|
21
47
|
|
22
48
|
%% tagline
|
23
49
|
\vfill
|
24
|
-
\
|
25
|
-
\texttt{gly preview https://github.com/igneus/gly}
|
26
|
-
\end{center}
|
50
|
+
\glyPreviewTagline
|
27
51
|
\end{document}
|
@@ -0,0 +1 @@
|
|
1
|
+
\music č ř š
|
@@ -7,8 +7,10 @@ class StringHelpersTest < GlyTest
|
|
7
7
|
['single_chunk', 'a', ['a']],
|
8
8
|
['simple_whitespace', 'aa aa', ['aa', 'aa']],
|
9
9
|
['leading_trailing_whitespace', ' a a ', ['a', 'a']],
|
10
|
+
['repeated_inner_whitespace', 'a a', ['a', 'a']],
|
10
11
|
['bracketted', '(a)', ['a']],
|
11
|
-
['empty', '()', ['']]
|
12
|
+
['empty', '()', ['']],
|
13
|
+
['nested', '(a(b))', ['a(b', ')']] # nesting isn't supported
|
12
14
|
]
|
13
15
|
|
14
16
|
examples.each do |e|
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: gly
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.0.
|
4
|
+
version: 0.0.3
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Jakub Pavlík
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date:
|
11
|
+
date: 2016-01-12 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: thor
|
@@ -38,108 +38,7 @@ dependencies:
|
|
38
38
|
- - "~>"
|
39
39
|
- !ruby/object:Gem::Version
|
40
40
|
version: '1'
|
41
|
-
description:
|
42
|
-
is an acronym of \"Gregorio for liLYponders\" or\n\"Gregorio with separate LYrics.\n\n##
|
43
|
-
Why\n\nOne of the most popular solutions for typesetting quadratic\nnotation used
|
44
|
-
for the Gregorian chant is [Gregorio][gregorio].\n\nHowever, I have several objections
|
45
|
-
against it's input format.\nThat led me to designing an alternative, Gregorio-inspired\nnotation
|
46
|
-
format, which compiles to pure Gregorio GABC\n(which I don't like writing manually).\n\n##
|
47
|
-
Features\n\n* music separated from lyrics\n * no need of the ubiquitous and tedious
|
48
|
-
parentheses\n * separation of \"material and form\" -> easy copying of the music
|
49
|
-
or\n lyrics alone is possible (useful for a composer)\n * syllabified lyrics
|
50
|
-
entered in a format inspired by LilyPond\n* music and lyrics can be interspersed
|
51
|
-
as needed\n* no semicolons in the header\n* custom header fields supported (commented
|
52
|
-
out in the GABC output,\n as gregorio doesn't tolerate headers it doesn't know)\n*
|
53
|
-
several scores per file (when compiled to gabc, each becomes\n a separate file)\n*
|
54
|
-
compile pdf preview by a single command, without writing any (La)TeX\n\n## Real
|
55
|
-
world examples\n\n* [Proper Divine Office chants of Bohemian Premonstratensian houses][opraem_boh]\n\n##
|
56
|
-
Basic examples\n\nTypical GABC source of an antiphon looks like this:\n\n name:
|
57
|
-
Nativitas gloriosae;\n office-part: laudes, 1. ant.;\n occasion: In Nativitate
|
58
|
-
B. Mariae Virginis;\n book: Antiphonale Romanum 1912, pg. 704;\n mode: 8;\n
|
59
|
-
\ initial-style: 1;\n %%\n \n (c4) NA(g)TI(g)VI(g)TAS(gd) glo(f)ri(gh)ó(g)sae(g)
|
60
|
-
* (,)\n Vír(g)gi(g)nis(hi) Ma(gh)rí(gf)ae,(f) (;)\n ex(f) sé(g)mi(h)ne(h)
|
61
|
-
A(hiwji)bra(hg)hae,(g) (;)\n or(gh~)tae(g) de(g) tri(g)bu(fe/fgf) Ju(d)da,(d)
|
62
|
-
(;)\n cla(df!gh)ra(g) ex(f) stir(hg~)pe(hi) Da(h)vid.(g) (::)\n\nCorresponding
|
63
|
-
GLY may look like this:\n\n name: Nativitas gloriosae\n office-part: laudes,
|
64
|
-
1. ant.\n occasion: In Nativitate B. Mariae Virginis\n book: Antiphonale Romanum
|
65
|
-
1912, pg. 704\n mode: 8\n initial-style: 1\n \n c4 g g g gd f gh g g
|
66
|
-
,\n g g hi gh gf f ;\n f g h h hiwji hg g ;\n gh~ g g g fe/fgf d d ;\n
|
67
|
-
\ df!gh g f hg~ hi h g ::\n \n NA -- TI -- VI -- TAS glo -- ri -- ósae *\n
|
68
|
-
\ Vír -- gi -- nis Ma -- rí -- ae,\n ex sé -- mi -- ne A -- bra -- hae,\n or
|
69
|
-
-- tae de tri -- bu Ju -- da,\n cla -- ra ex stir -- pe Da -- vid.\n\nOr, with
|
70
|
-
music and lyrics interlaced\n(this arrangement may be handy for larger scores,\nlike
|
71
|
-
full-notated hymns, sequences or nocturnal responsories):\n\n name: Nativitas
|
72
|
-
gloriosae\n office-part: laudes, 1. ant.\n occasion: In Nativitate B. Mariae
|
73
|
-
Virginis\n book: Antiphonale Romanum 1912, pg. 704\n mode: 8\n initial-style:
|
74
|
-
1\n \n c4 g g g gd f gh g g ,\n NA -- TI -- VI -- TAS glo -- ri -- ósae
|
75
|
-
*\n \n g g hi gh gf f ;\n Vír -- gi -- nis Ma -- rí -- ae,\n \n f
|
76
|
-
g h h hiwji hg g ;\n ex sé -- mi -- ne A -- bra -- hae,\n \n gh~ g g g
|
77
|
-
fe/fgf d d ;\n or -- tae de tri -- bu Ju -- da,\n \n df!gh g f hg~ hi h
|
78
|
-
g ::\n cla -- ra ex stir -- pe Da -- vid.\n\nOther arrangements are also possible.
|
79
|
-
Order of music and lyrics\nis actually ignored during processing.\n\n## Installation\n\nInstall
|
80
|
-
Ruby (some 2.x version) runtime. Then install as any ruby gem:\n\n`gem install gly`\n\n##
|
81
|
-
Usage\n\nThis gem provides executable `gly`. Run `gly help` for full list\nof subcommands.
|
82
|
-
The most important ones are:\n\n`gly gabc FILE1 ...`\n\nconverts given gly file(s)
|
83
|
-
to one or more gabc files (one per score,\ni.e. one gly may spawn a bunch of gabcs).\n\n`gly
|
84
|
-
preview FILE1 ...`\n\nAttempts to create a pdf document with all scores contained
|
85
|
-
in each\ngly file. Expects `gregorio` and `lualatex` to be in PATH\nand `gregoriotex`
|
86
|
-
to be installed and accessible by `lualatex`.\n\n## Tools\n\n[Emacs mode with syntax
|
87
|
-
highlighting for gly][elisp]\n\n\n\n##
|
88
|
-
Syntax reference\n\nGly syntax is line-based.\nThe interpreter reads the input line
|
89
|
-
by line,\nand depending on context it interprets each line as\ne.g. music, lyrics
|
90
|
-
or header field.\n\nThe syntax is quite permissive, not requiring a lot of delimiters\nor
|
91
|
-
hints for the parser concerning what each line means.\nMostly the parser guesses
|
92
|
-
the meaning correctly.\nWhere not, meaning of each line can be stated explicitly.\n\n###
|
93
|
-
1. Comments\n\nWhen a `%` sign is encountered, everything until the end of line\nis
|
94
|
-
considered a comment and not interpreted.\n(Comment syntax is the same as in gabc.)\n\nPlease
|
95
|
-
note, that when compiling to gabc, comments are dropped\nand don't appear in the
|
96
|
-
resulting gabc file.\n\n### 2. Whitespace\n\nEmpty lines are ignored.\n\n### 3.
|
97
|
-
Scores\n\nA new score begins at the beginning of a file or at a line containing\na
|
98
|
-
single keyword '\\score'.\n\nIt consists of\na header (optional, only permitted
|
99
|
-
at the beginning of a score)\nand music- and lyrics-lines.\nLines with music and
|
100
|
-
lyrics may appear in any order.\n\nScore ends with end of file or with explicit
|
101
|
-
beginning of a new score\nor another top-level element.\n\n#### 3.1 Score header\n\nScore
|
102
|
-
header consists of fields.\n\nEach header field is on it's own (one) line and consists
|
103
|
-
of\nidentifier, colon and value:\n\n`mode: 8`\n\nHeader field identifier may only
|
104
|
-
consist of alphanumeric characters,\nminus-sign and underscore. Value can contain
|
105
|
-
anything.\n\nScore header ends with first non-empty line identified by the parser\nas
|
106
|
-
music or lyrics.\n\nHeader field 'id' is special: if present, it is used as suffix\nof
|
107
|
-
the generated gabc file (instead of the default, which is\nnumeric position of the
|
108
|
-
score in the source document).\n\n#### 3.2 Lyrics\n\nSyntax of lyrics is inspired
|
109
|
-
by LilyPond.\nLyrics have to be manually syllabified. Default syllable delimiter\nis
|
110
|
-
double dash (minus) `--` with optional whitespace around.\n\n`cla -- ra ex stir
|
111
|
-
-- pe Da -- vid.`\n\nThe parser guesses meaning of the line by attempting to find\nsyllable
|
112
|
-
separator in it and by looking if it's alphanumeric\ncharacters contains something
|
113
|
-
that cannot be interpreted as music.\nIf any of these conditions is met, the line
|
114
|
-
is interpreted as lyrics.\n\nIf gly fails to guess your lyrics line correctly and
|
115
|
-
interprets\nit as music, place `\\lyrics` or short `\\l` at the beginning\nof the
|
116
|
-
unhappy line:\n\n`\\l a a a`\n\n#### 3.3 Music\n\nAny line appearing in a score
|
117
|
-
and not identified as header field\nor lyrics is music by default.\n\nMusic line
|
118
|
-
contains one or more music chunks separated by whitespace.\nFor music syntax see
|
119
|
-
[official gabc documentation][gabc] -\ngly doesn't change anything in this respect.\n\nMusic
|
120
|
-
chunks may be enclosed in parentheses as in gabc.\n(Of course you don't like the
|
121
|
-
parentheses and are happy that gly\nlet's you leave them out. But in some special
|
122
|
-
cases they come handy.)\n\n#### 3.4 Matching lyrics to music\n\nWhen processing
|
123
|
-
the gly source and producing gabc, music chunks\nare matched to lyric syllables.\n\nThere
|
124
|
-
are, however, a few special cases, to make it work conveniently:\n\nThese special
|
125
|
-
cases of music chunks don't get lyric syllable:\n\n* clef\n* music chunk containing
|
126
|
-
only a division - i.e. music chunk containing\n one of `,` , `;` , `:` , `::` alone\n\nException
|
127
|
-
to this rule are 'nonlyrical lyrics chunks'.\nCurrently there is only one built-in
|
128
|
-
nonlyrical lyric chunk:\nasterisk `*`.\nNormally it is treated as any other syllable,\nbut
|
129
|
-
if it meets a division, it is set as it's lyrics, while\na normal syllable wouldn't
|
130
|
-
be.\n\nIf you need to set some other syllable under a division,\nmake it 'nonlyrical'
|
131
|
-
by placing\nan exclamation mark at it's beginning, e.g. `!<i>Ps.</i>`\n\nIn the
|
132
|
-
other direction it is sometimes necessary to set a syllable\nnot matching any music
|
133
|
-
at all. In such cases empty music chunk\n`()` is what you need.\n\n### 4. Document
|
134
|
-
header\n\nEach gly document may optinally contain a document header.\nIt may appear
|
135
|
-
anywhere in the document, but best practice is to place\nit at the very beginning.\n\nDocument
|
136
|
-
header starts with keyword `\\header` and ends\nat the end of file or at the beginning
|
137
|
-
of another top-level element.\nThe syntax of it's content is the same\nas for [Score
|
138
|
-
header][].\n\nField 'title' in the document header is, if present,\nused by `gly
|
139
|
-
preview` as title of the generated pdf.\n\n## Run tests\n\nby executing `tests/run.rb`\n\n##
|
140
|
-
License\n\nMIT\n\n[gregorio]: https://github.com/gregorio-project/gregorio\n[gabc]:
|
141
|
-
http://gregorio-project.github.io/gabc/index.html\n[elisp]: /tree/master/elisp\n\n[opraem_boh]:
|
142
|
-
https://gist.github.com/igneus/1aed0b36e9b23b51526d\n"
|
41
|
+
description:
|
143
42
|
email: jkb.pavlik@gmail.com
|
144
43
|
executables:
|
145
44
|
- gly
|
@@ -156,6 +55,8 @@ files:
|
|
156
55
|
- lib/gly/document.rb~
|
157
56
|
- lib/gly/document_gabc_convertor.rb
|
158
57
|
- lib/gly/document_gabc_convertor.rb~
|
58
|
+
- lib/gly/document_ly_convertor.rb
|
59
|
+
- lib/gly/document_ly_convertor.rb~
|
159
60
|
- lib/gly/gabc_convertor.rb
|
160
61
|
- lib/gly/gabc_convertor.rb~
|
161
62
|
- lib/gly/headers.rb
|
@@ -164,10 +65,13 @@ files:
|
|
164
65
|
- lib/gly/lister.rb~
|
165
66
|
- lib/gly/lyrics.rb
|
166
67
|
- lib/gly/lyrics.rb~
|
68
|
+
- lib/gly/options.rb~
|
167
69
|
- lib/gly/parsed_score.rb
|
168
70
|
- lib/gly/parsed_score.rb~
|
169
71
|
- lib/gly/parser.rb
|
170
72
|
- lib/gly/parser.rb~
|
73
|
+
- lib/gly/preview_builder.rb
|
74
|
+
- lib/gly/preview_builder.rb~
|
171
75
|
- lib/gly/preview_generator.rb
|
172
76
|
- lib/gly/preview_generator.rb~
|
173
77
|
- lib/gly/string_helpers.rb
|
@@ -181,9 +85,10 @@ files:
|
|
181
85
|
- tests/examples/gly/expected/differentiae.gabc~
|
182
86
|
- tests/examples/gly/expected/document_header.gabc
|
183
87
|
- tests/examples/gly/expected/document_header.gabc~
|
184
|
-
- tests/examples/gly/expected/empty.gabc
|
185
88
|
- tests/examples/gly/expected/explicit_lyrics.gabc
|
186
89
|
- tests/examples/gly/expected/explicit_lyrics.gabc~
|
90
|
+
- tests/examples/gly/expected/explicit_music.gabc
|
91
|
+
- tests/examples/gly/expected/explicit_music.gabc~
|
187
92
|
- tests/examples/gly/expected/explicit_score.gabc
|
188
93
|
- tests/examples/gly/expected/force_divisio_lyrics.gabc
|
189
94
|
- tests/examples/gly/expected/header.gabc
|
@@ -203,8 +108,9 @@ files:
|
|
203
108
|
- tests/examples/gly/given/differentiae.gly
|
204
109
|
- tests/examples/gly/given/differentiae.gly~
|
205
110
|
- tests/examples/gly/given/document_header.gly
|
206
|
-
- tests/examples/gly/given/empty.gly
|
207
111
|
- tests/examples/gly/given/explicit_lyrics.gly
|
112
|
+
- tests/examples/gly/given/explicit_music.gly
|
113
|
+
- tests/examples/gly/given/explicit_music.gly~
|
208
114
|
- tests/examples/gly/given/explicit_score.gly
|
209
115
|
- tests/examples/gly/given/force_divisio_lyrics.gly
|
210
116
|
- tests/examples/gly/given/header.gly
|
@@ -1 +0,0 @@
|
|
1
|
-
%%
|
File without changes
|