abcjs 6.0.0-beta.9 → 6.0.0
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.
- package/.github/workflows/tests.yml +29 -0
- package/CODE_OF_CONDUCT.md +76 -0
- package/CONTRIBUTING.md +1 -0
- package/LICENSE.md +1 -1
- package/README.md +88 -7
- package/RELEASE.md +939 -1
- package/abcjs-audio.css +14 -5
- package/dist/.gitignore +1 -2
- package/dist/abcjs-basic-min.js +3 -0
- package/dist/abcjs-basic-min.js.LICENSE +23 -0
- package/dist/abcjs-basic.js +28231 -0
- package/dist/abcjs-basic.js.map +1 -0
- package/dist/abcjs-plugin-min.js +3 -0
- package/dist/abcjs-plugin-min.js.LICENSE +23 -0
- package/dist/report-basic.html +37 -0
- package/dist/report-before-glyph-compress.html +37 -0
- package/dist/report-brown-ts-target-es5.html +37 -0
- package/dist/report-dev-orig-no-babel.html +37 -0
- package/dist/report-synth.html +37 -0
- package/docker-build.sh +1 -0
- package/glyphs.json +1 -0
- package/index.js +27 -2
- package/{static-wrappers/license.js → license.js} +1 -1
- package/package.json +26 -29
- package/{src/plugin/abc_plugin.js → plugin.js} +31 -19
- package/src/api/abc_animation.js +1 -17
- package/src/api/abc_tablatures.js +144 -0
- package/src/api/abc_timing_callbacks.js +216 -117
- package/src/api/abc_tunebook.js +18 -67
- package/src/api/abc_tunebook_svg.js +38 -46
- package/src/data/abc_tune.js +232 -972
- package/src/data/deline-tune.js +199 -0
- package/src/edit/abc_editarea.js +112 -0
- package/src/edit/abc_editor.js +95 -221
- package/src/midi/abc_midi_create.js +48 -50
- package/src/parse/abc_common.js +0 -14
- package/src/parse/abc_parse.js +167 -1321
- package/src/parse/abc_parse_book.js +62 -0
- package/src/parse/abc_parse_directive.js +164 -41
- package/src/parse/abc_parse_header.js +116 -145
- package/src/parse/abc_parse_key_voice.js +26 -20
- package/src/parse/abc_parse_music.js +1337 -0
- package/src/parse/abc_tokenizer.js +21 -15
- package/src/parse/abc_transpose.js +3 -15
- package/src/parse/tune-builder.js +896 -0
- package/src/parse/wrap_lines.js +205 -453
- package/src/synth/abc_midi_flattener.js +1292 -0
- package/src/{midi → synth}/abc_midi_renderer.js +44 -17
- package/src/synth/abc_midi_sequencer.js +648 -0
- package/src/synth/active-audio-context.js +3 -14
- package/src/synth/cents-to-factor.js +10 -0
- package/src/synth/create-note-map.js +21 -32
- package/src/synth/create-synth-control.js +20 -103
- package/src/synth/create-synth.js +185 -77
- package/src/synth/download-buffer.js +7 -21
- package/src/synth/get-midi-file.js +13 -20
- package/src/synth/images/{loading.svg → loading.svg.js} +4 -0
- package/src/synth/images/loop.svg.js +65 -0
- package/src/synth/images/pause.svg.js +10 -0
- package/src/synth/images/play.svg.js +9 -0
- package/src/synth/images/{reset.svg → reset.svg.js} +5 -1
- package/src/synth/instrument-index-to-name.js +1 -16
- package/src/synth/load-note.js +37 -76
- package/src/synth/pitch-to-note-name.js +0 -15
- package/src/synth/pitches-to-perc.js +64 -0
- package/src/synth/place-note.js +78 -68
- package/src/synth/play-event.js +17 -18
- package/src/synth/register-audio-context.js +11 -23
- package/src/synth/sounds-cache.js +0 -15
- package/src/synth/supports-audio.js +9 -23
- package/src/synth/synth-controller.js +80 -49
- package/src/synth/synth-sequence.js +20 -34
- package/src/tablatures/instruments/guitar/guitar-fonts.js +19 -0
- package/src/tablatures/instruments/guitar/guitar-patterns.js +23 -0
- package/src/tablatures/instruments/guitar/tab-guitar.js +50 -0
- package/src/tablatures/instruments/string-patterns.js +277 -0
- package/src/tablatures/instruments/string-tablature.js +56 -0
- package/src/tablatures/instruments/tab-note.js +282 -0
- package/src/tablatures/instruments/tab-notes.js +41 -0
- package/src/tablatures/instruments/violin/tab-violin.js +47 -0
- package/src/tablatures/instruments/violin/violin-fonts.js +19 -0
- package/src/tablatures/instruments/violin/violin-patterns.js +23 -0
- package/src/tablatures/tab-absolute-elements.js +310 -0
- package/src/tablatures/tab-common.js +29 -0
- package/src/tablatures/tab-renderer.js +243 -0
- package/src/tablatures/transposer.js +110 -0
- package/src/test/abc_midi_lint.js +5 -22
- package/src/test/abc_midi_sequencer_lint.js +11 -14
- package/src/test/abc_parser_lint.js +136 -32
- package/src/test/abc_vertical_lint.js +94 -32
- package/src/test/rendering-lint.js +38 -5
- package/src/write/abc_absolute_element.js +112 -120
- package/src/write/abc_abstract_engraver.js +102 -253
- package/src/write/abc_beam_element.js +30 -290
- package/src/write/abc_brace_element.js +12 -121
- package/src/write/abc_create_clef.js +21 -32
- package/src/write/abc_create_key_signature.js +8 -26
- package/src/write/abc_create_note_head.js +107 -0
- package/src/write/abc_create_time_signature.js +2 -21
- package/src/write/abc_crescendo_element.js +3 -50
- package/src/write/abc_decoration.js +7 -30
- package/src/write/abc_dynamic_decoration.js +3 -37
- package/src/write/abc_ending_element.js +1 -57
- package/src/write/abc_engraver_controller.js +111 -234
- package/src/write/abc_glyphs.js +8 -18
- package/src/write/abc_relative_element.js +57 -97
- package/src/write/abc_renderer.js +10 -832
- package/src/write/abc_spacing.js +0 -15
- package/src/write/abc_staff_group_element.js +14 -349
- package/src/write/abc_tempo_element.js +9 -117
- package/src/write/abc_tie_element.js +5 -68
- package/src/write/abc_triplet_element.js +6 -124
- package/src/write/abc_voice_element.js +7 -222
- package/src/write/add-chord.js +103 -0
- package/src/write/add-text-if.js +33 -0
- package/src/write/bottom-text.js +79 -0
- package/src/write/calcHeight.js +17 -0
- package/src/write/classes.js +100 -0
- package/src/write/draw/absolute.js +68 -0
- package/src/write/draw/beam.js +56 -0
- package/src/write/draw/brace.js +106 -0
- package/src/write/draw/crescendo.js +38 -0
- package/src/write/draw/debug-box.js +8 -0
- package/src/write/draw/draw.js +56 -0
- package/src/write/draw/dynamics.js +20 -0
- package/src/write/draw/ending.js +46 -0
- package/src/write/draw/group-elements.js +66 -0
- package/src/write/draw/horizontal-line.js +25 -0
- package/src/write/draw/non-music.js +50 -0
- package/src/write/draw/print-line.js +24 -0
- package/src/write/draw/print-path.js +7 -0
- package/src/write/draw/print-stem.js +30 -0
- package/src/write/draw/print-symbol.js +59 -0
- package/src/write/draw/print-vertical-line.js +18 -0
- package/src/write/draw/relative.js +77 -0
- package/src/write/draw/round-number.js +5 -0
- package/src/write/draw/selectables.js +59 -0
- package/src/write/draw/separator.js +16 -0
- package/src/write/draw/set-paper-size.js +45 -0
- package/src/write/{sprintf.js → draw/sprintf.js} +0 -0
- package/src/write/draw/staff-group.js +226 -0
- package/src/write/draw/staff-line.js +9 -0
- package/src/write/draw/staff.js +33 -0
- package/src/write/draw/tab-line.js +40 -0
- package/src/write/draw/tempo.js +45 -0
- package/src/write/draw/text.js +71 -0
- package/src/write/draw/tie.js +97 -0
- package/src/write/draw/triplet.js +46 -0
- package/src/write/draw/voice.js +102 -0
- package/src/write/format-jazz-chord.js +15 -0
- package/src/write/free-text.js +41 -0
- package/src/write/get-font-and-attr.js +37 -0
- package/src/write/get-text-size.js +56 -0
- package/src/write/highlight.js +11 -0
- package/src/write/layout/VoiceElements.js +121 -0
- package/src/write/layout/beam.js +213 -0
- package/src/write/layout/get-left-edge-of-staff.js +56 -0
- package/src/write/layout/getBarYAt.js +6 -0
- package/src/write/layout/layout.js +94 -0
- package/src/write/layout/setUpperAndLowerElements.js +232 -0
- package/src/write/layout/staffGroup.js +146 -0
- package/src/write/layout/triplet.js +75 -0
- package/src/write/layout/voice.js +137 -0
- package/src/write/selection.js +188 -70
- package/src/write/separator.js +10 -0
- package/src/write/set-class.js +21 -0
- package/src/write/subtitle.js +12 -0
- package/src/write/svg.js +95 -43
- package/src/write/top-text.js +54 -0
- package/src/write/unhighlight.js +11 -0
- package/temp.txt +17 -0
- package/test.js +27 -64
- package/types/index.d.ts +1095 -0
- package/version.js +1 -1
- package/.babelrc +0 -5
- package/.eslintrc +0 -3
- package/.gitmodules +0 -3
- package/Dockerfile +0 -8
- package/abcjs-midi.css +0 -166
- package/build-utils/loadPresets.js +0 -14
- package/build-utils/presets/webpack.analyze.js +0 -6
- package/build-utils/presets/webpack.optimize.js +0 -30
- package/build-utils/webpack.development.js +0 -14
- package/build-utils/webpack.production.js +0 -35
- package/deploy-docs.sh +0 -25
- package/docker-compose.yml +0 -13
- package/docs/README.md +0 -33
- package/fix-versions.sh +0 -23
- package/midi.js +0 -62
- package/src/api/abc_tunebook_midi.js +0 -116
- package/src/midi/abc_midi_controls.js +0 -701
- package/src/midi/abc_midi_flattener.js +0 -1119
- package/src/midi/abc_midi_js_preparer.js +0 -243
- package/src/midi/abc_midi_sequencer.js +0 -401
- package/src/midi/abc_midi_ui_generator.js +0 -86
- package/src/plugin/abc_plugin_midi.js +0 -220
- package/src/synth/images/loop.svg +0 -61
- package/src/synth/images/pause.svg +0 -6
- package/src/synth/images/play.svg +0 -5
- package/src/transform/abc2abc_write.js +0 -395
- package/static-wrappers/basic.js +0 -2
- package/static-wrappers/midi.js +0 -2
- package/static-wrappers/plugin-midi.js +0 -6
- package/static-wrappers/plugin.js +0 -6
- package/webpack.config.js +0 -29
|
@@ -1,43 +1,20 @@
|
|
|
1
1
|
// abc_renderer.js: API to render to SVG/Raphael/whatever rendering engine
|
|
2
|
-
// Copyright (C) 2010-2020 Gregory Dyke (gregdyke at gmail dot com)
|
|
3
|
-
//
|
|
4
|
-
// Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated
|
|
5
|
-
// documentation files (the "Software"), to deal in the Software without restriction, including without limitation
|
|
6
|
-
// the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and
|
|
7
|
-
// to permit persons to whom the Software is furnished to do so, subject to the following conditions:
|
|
8
|
-
//
|
|
9
|
-
// The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
|
|
10
|
-
//
|
|
11
|
-
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING
|
|
12
|
-
// BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
|
13
|
-
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
|
|
14
|
-
// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
15
|
-
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
|
16
2
|
|
|
3
|
+
/*global Math */
|
|
17
4
|
|
|
18
|
-
/*global Math, console */
|
|
19
|
-
|
|
20
|
-
var glyphs = require('./abc_glyphs');
|
|
21
5
|
var spacing = require('./abc_spacing');
|
|
22
|
-
var sprintf = require('./sprintf');
|
|
23
6
|
var Svg = require('./svg');
|
|
24
|
-
var AbsoluteElement = require('./abc_absolute_element');
|
|
25
7
|
|
|
26
8
|
/**
|
|
27
9
|
* Implements the API for rendering ABCJS Abstract Rendering Structure to a canvas/paper (e.g. SVG, Raphael, etc)
|
|
28
10
|
* @param {Object} paper
|
|
29
|
-
* @param {bool} doRegression
|
|
30
11
|
*/
|
|
31
|
-
var Renderer = function(paper
|
|
12
|
+
var Renderer = function(paper) {
|
|
32
13
|
this.paper = new Svg(paper);
|
|
33
|
-
this.controller = null;
|
|
14
|
+
this.controller = null;
|
|
34
15
|
|
|
35
16
|
this.space = 3*spacing.SPACE;
|
|
36
17
|
this.padding = {}; // renderer's padding is managed by the controller
|
|
37
|
-
this.doRegression = doRegression;
|
|
38
|
-
this.shouldAddClasses = shouldAddClasses;
|
|
39
|
-
if (this.doRegression)
|
|
40
|
-
this.regressionLines = [];
|
|
41
18
|
this.reset();
|
|
42
19
|
};
|
|
43
20
|
|
|
@@ -46,144 +23,25 @@ Renderer.prototype.reset = function() {
|
|
|
46
23
|
this.paper.clear();
|
|
47
24
|
this.y = 0;
|
|
48
25
|
this.abctune = null;
|
|
49
|
-
this.lastM = null;
|
|
50
|
-
this.ingroup = false;
|
|
51
26
|
this.path = null;
|
|
52
27
|
this.isPrint = false;
|
|
53
28
|
this.initVerticalSpace();
|
|
54
|
-
if (this.doRegression)
|
|
55
|
-
this.regressionLines = [];
|
|
56
|
-
// HACK-PER: There was a problem in Raphael where every path string that was sent to it was cached.
|
|
57
|
-
// That was causing the browser's memory to steadily grow until the browser went slower and slower until
|
|
58
|
-
// it crashed. The fix to that was a patch to Raphael, so it is only patched on the versions of this library that
|
|
59
|
-
// bundle Raphael with it. Also, if Raphael gets an update, then that patch will be lost. On version 2.1.2 of Raphael,
|
|
60
|
-
// the patch is on line 1542 and 1545 and it is:
|
|
61
|
-
// p[ps].sleep = 1;
|
|
62
29
|
};
|
|
63
30
|
|
|
64
31
|
Renderer.prototype.newTune = function(abcTune) {
|
|
65
32
|
this.abctune = abcTune; // TODO-PER: this is just to get the font info.
|
|
66
33
|
this.setVerticalSpace(abcTune.formatting);
|
|
67
|
-
this.measureNumber = null;
|
|
68
|
-
this.noteNumber = null;
|
|
69
|
-
this.
|
|
34
|
+
//this.measureNumber = null;
|
|
35
|
+
//this.noteNumber = null;
|
|
36
|
+
this.isPrint = abcTune.media === 'print';
|
|
70
37
|
this.setPadding(abcTune);
|
|
71
38
|
};
|
|
72
39
|
|
|
73
|
-
Renderer.prototype.createElemSet = function(options) {
|
|
74
|
-
return this.paper.openGroup(options);
|
|
75
|
-
};
|
|
76
|
-
|
|
77
|
-
Renderer.prototype.closeElemSet = function() {
|
|
78
|
-
return this.paper.closeGroup();
|
|
79
|
-
};
|
|
80
|
-
|
|
81
|
-
/**
|
|
82
|
-
* Set whether we are formatting this for the screen, or as a preview for creating a PDF version.
|
|
83
|
-
* @param {bool} isPrint
|
|
84
|
-
*/
|
|
85
|
-
Renderer.prototype.setPrintMode = function (isPrint) {
|
|
86
|
-
this.isPrint = isPrint;
|
|
87
|
-
};
|
|
88
|
-
|
|
89
|
-
/**
|
|
90
|
-
* Set the size of the canvas.
|
|
91
|
-
* @param {object} maxwidth
|
|
92
|
-
* @param {object} scale
|
|
93
|
-
*/
|
|
94
|
-
Renderer.prototype.setPaperSize = function (maxwidth, scale, responsive) {
|
|
95
|
-
var w = (maxwidth+this.padding.right)*scale;
|
|
96
|
-
var h = (this.y+this.padding.bottom)*scale;
|
|
97
|
-
if (this.isPrint)
|
|
98
|
-
h = Math.max(h, 1056); // 11in x 72pt/in x 1.33px/pt
|
|
99
|
-
// TODO-PER: We are letting the page get as long as it needs now, but eventually that should go to a second page.
|
|
100
|
-
if (this.doRegression)
|
|
101
|
-
this.regressionLines.push("PAPER SIZE: ("+w+","+h+")");
|
|
102
|
-
|
|
103
|
-
// for accessibility
|
|
104
|
-
var text = "Sheet Music";
|
|
105
|
-
if (this.abctune && this.abctune.metaText && this.abctune.metaText.title)
|
|
106
|
-
text += " for \"" + this.abctune.metaText.title + '"';
|
|
107
|
-
this.paper.setTitle(text);
|
|
108
|
-
|
|
109
|
-
// for dragging - don't select during drag
|
|
110
|
-
var styles = [
|
|
111
|
-
"-webkit-touch-callout: none;",
|
|
112
|
-
"-webkit-user-select: none;",
|
|
113
|
-
"-khtml-user-select: none;",
|
|
114
|
-
"-moz-user-select: none;",
|
|
115
|
-
"-ms-user-select: none;",
|
|
116
|
-
"user-select: none;"
|
|
117
|
-
];
|
|
118
|
-
this.paper.insertStyles(".abcjs-dragging-in-progress text, .abcjs-dragging-in-progress tspan {" + styles.join(" ") + ")}");
|
|
119
|
-
|
|
120
|
-
var parentStyles = { overflow: "hidden" };
|
|
121
|
-
if (responsive === 'resize') {
|
|
122
|
-
this.paper.setResponsiveWidth(w, h);
|
|
123
|
-
} else {
|
|
124
|
-
parentStyles.width = "";
|
|
125
|
-
parentStyles.height = h + "px";
|
|
126
|
-
if (scale < 1) {
|
|
127
|
-
parentStyles.width = w + "px";
|
|
128
|
-
this.paper.setSize(w / scale, h / scale);
|
|
129
|
-
} else
|
|
130
|
-
this.paper.setSize(w, h);
|
|
131
|
-
}
|
|
132
|
-
this.paper.setScale(scale);
|
|
133
|
-
this.paper.setParentStyles(parentStyles);
|
|
134
|
-
};
|
|
135
|
-
|
|
136
|
-
function getClassSet(el) {
|
|
137
|
-
var oldClass = el.getAttribute('class');
|
|
138
|
-
if (!oldClass)
|
|
139
|
-
oldClass = "";
|
|
140
|
-
var klasses = oldClass.split(" ");
|
|
141
|
-
var obj = {};
|
|
142
|
-
for (var i = 0; i < klasses.length; i++)
|
|
143
|
-
obj[klasses[i]] = true;
|
|
144
|
-
return obj;
|
|
145
|
-
}
|
|
146
|
-
|
|
147
|
-
function setClassSet(el, klassSet) {
|
|
148
|
-
var klasses = [];
|
|
149
|
-
for (var key in klassSet) {
|
|
150
|
-
if (klassSet.hasOwnProperty(key))
|
|
151
|
-
klasses.push(key);
|
|
152
|
-
}
|
|
153
|
-
el.setAttribute('class', klasses.join(' '));
|
|
154
|
-
}
|
|
155
|
-
|
|
156
|
-
Renderer.prototype.addGlobalClass = function (klass) {
|
|
157
|
-
// Can't use classList on IE
|
|
158
|
-
if (this.paper) {
|
|
159
|
-
var obj = getClassSet(this.paper.svg);
|
|
160
|
-
obj[klass] = true;
|
|
161
|
-
setClassSet(this.paper.svg, obj);
|
|
162
|
-
}
|
|
163
|
-
};
|
|
164
|
-
|
|
165
|
-
Renderer.prototype.removeGlobalClass = function (klass) {
|
|
166
|
-
// Can't use classList on IE
|
|
167
|
-
if (this.paper) {
|
|
168
|
-
var obj = getClassSet(this.paper.svg);
|
|
169
|
-
delete obj[klass];
|
|
170
|
-
setClassSet(this.paper.svg, obj);
|
|
171
|
-
}
|
|
172
|
-
};
|
|
173
|
-
|
|
174
|
-
/**
|
|
175
|
-
* Set the padding
|
|
176
|
-
* @param {object} params
|
|
177
|
-
*/
|
|
178
40
|
Renderer.prototype.setPaddingOverride = function(params) {
|
|
179
41
|
this.paddingOverride = { top: params.paddingtop, bottom: params.paddingbottom,
|
|
180
42
|
right: params.paddingright, left: params.paddingleft };
|
|
181
43
|
};
|
|
182
44
|
|
|
183
|
-
/**
|
|
184
|
-
* Set the padding
|
|
185
|
-
* @param {object} params
|
|
186
|
-
*/
|
|
187
45
|
Renderer.prototype.setPadding = function(abctune) {
|
|
188
46
|
// If the padding is set in the tune, then use that.
|
|
189
47
|
// Otherwise, if the padding is set in the override, use that.
|
|
@@ -243,7 +101,7 @@ Renderer.prototype.initVerticalSpace = function() {
|
|
|
243
101
|
text: 18.9, // Set the vertical space above the history.
|
|
244
102
|
title: 7.56, // Set the vertical space above the title.
|
|
245
103
|
top: 30.24, //Set the vertical space above the tunes and on the top of the continuation pages.
|
|
246
|
-
vocal:
|
|
104
|
+
vocal: 0, // Set the vertical space above the lyrics under the staves.
|
|
247
105
|
words: 0 // Set the vertical space above the lyrics at the end of the tune.
|
|
248
106
|
};
|
|
249
107
|
/*
|
|
@@ -301,460 +159,11 @@ Renderer.prototype.setVerticalSpace = function(formatting) {
|
|
|
301
159
|
this.spacing.words = formatting.wordsspace *4/3;
|
|
302
160
|
};
|
|
303
161
|
|
|
304
|
-
/**
|
|
305
|
-
* Leave space at the top of the paper
|
|
306
|
-
* @param {object} abctune
|
|
307
|
-
*/
|
|
308
|
-
Renderer.prototype.topMargin = function(abctune) {
|
|
309
|
-
this.moveY(this.padding.top);
|
|
310
|
-
};
|
|
311
|
-
|
|
312
|
-
/**
|
|
313
|
-
* Leave space before printing the music
|
|
314
|
-
*/
|
|
315
|
-
Renderer.prototype.addMusicPadding = function() {
|
|
316
|
-
this.moveY(this.spacing.music);
|
|
317
|
-
};
|
|
318
162
|
|
|
319
163
|
/**
|
|
320
164
|
* Leave space before printing a staff system
|
|
321
165
|
*/
|
|
322
|
-
Renderer.prototype.addStaffPadding = function(lastStaffGroup, thisStaffGroup) {
|
|
323
|
-
var lastStaff = lastStaffGroup.staffs[lastStaffGroup.staffs.length-1];
|
|
324
|
-
var lastBottomLine = -(lastStaff.bottom - 2); // The 2 is because the scale goes to 2 below the last line.
|
|
325
|
-
var nextTopLine = thisStaffGroup.staffs[0].top - 10; // Because 10 represents the top line.
|
|
326
|
-
var naturalSeparation = nextTopLine + lastBottomLine; // This is how far apart they'd be without extra spacing
|
|
327
|
-
var separationInPixels = naturalSeparation * spacing.STEP;
|
|
328
|
-
if (separationInPixels < this.spacing.staffSeparation)
|
|
329
|
-
this.moveY(this.spacing.staffSeparation-separationInPixels);
|
|
330
|
-
};
|
|
331
|
-
|
|
332
|
-
/**
|
|
333
|
-
* Text that goes above the score
|
|
334
|
-
* @param {number} width
|
|
335
|
-
* @param {object} abctune
|
|
336
|
-
*/
|
|
337
|
-
Renderer.prototype.engraveTopText = function(width, abctune) {
|
|
338
|
-
var el;
|
|
339
|
-
if (abctune.metaText.header && this.isPrint) {
|
|
340
|
-
// Note: whether there is a header or not doesn't change any other positioning, so this doesn't change the Y-coordinate.
|
|
341
|
-
// This text goes above the margin, so we'll temporarily move up.
|
|
342
|
-
var headerTextHeight = this.getTextSize("XXXX", "headerfont", 'abcjs-header abcjs-meta-top').height;
|
|
343
|
-
this.y -=headerTextHeight;
|
|
344
|
-
this.outputTextIf(this.padding.left, abctune.metaText.header.left, 'headerfont', 'header meta-top', 0, null, 'start');
|
|
345
|
-
this.outputTextIf(this.padding.left + width / 2, abctune.metaText.header.center, 'headerfont', 'header meta-top', 0, null, 'middle');
|
|
346
|
-
this.outputTextIf(this.padding.left + width, abctune.metaText.header.right, 'headerfont', 'header meta-top', 0, null, 'end');
|
|
347
|
-
this.y += headerTextHeight;
|
|
348
|
-
}
|
|
349
|
-
if (this.isPrint)
|
|
350
|
-
this.moveY(this.spacing.top);
|
|
351
|
-
if (abctune.metaText.title) {
|
|
352
|
-
this.wrapInAbsElem({el_type: "title", startChar: -1, endChar: -1, text: abctune.metaText.title}, 'title meta-top', function() {
|
|
353
|
-
return this.outputTextIf(this.padding.left + width / 2, abctune.metaText.title, 'titlefont', 'title meta-top', this.spacing.title, 0, 'middle')[2];
|
|
354
|
-
});
|
|
355
|
-
}
|
|
356
|
-
if (abctune.lines[0] && abctune.lines[0].subtitle) {
|
|
357
|
-
this.wrapInAbsElem({el_type: "subtitle", startChar: -1, endChar: -1, text: abctune.lines[0].subtitle}, 'text meta-top subtitle', function() {
|
|
358
|
-
return this.outputTextIf(this.padding.left + width / 2, abctune.lines[0].subtitle, 'subtitlefont', 'text meta-top subtitle', this.spacing.subtitle, 0, 'middle')[2];
|
|
359
|
-
});
|
|
360
|
-
}
|
|
361
|
-
|
|
362
|
-
if (abctune.metaText.rhythm || abctune.metaText.origin || abctune.metaText.composer) {
|
|
363
|
-
this.moveY(this.spacing.composer);
|
|
364
|
-
var rSpace;
|
|
365
|
-
if (abctune.metaText.rhythm && abctune.metaText.rhythm.length > 0) {
|
|
366
|
-
this.wrapInAbsElem({el_type: "rhythm", startChar: -1, endChar: -1, text: abctune.metaText.rhythm}, 'meta-top rhythm', function() {
|
|
367
|
-
rSpace = this.outputTextIf(this.padding.left, abctune.metaText.rhythm, 'infofont', 'meta-top rhythm', 0, null, "start");
|
|
368
|
-
return rSpace[2];
|
|
369
|
-
});
|
|
370
|
-
}
|
|
371
|
-
var composerLine = "";
|
|
372
|
-
if (abctune.metaText.composer) composerLine += abctune.metaText.composer;
|
|
373
|
-
if (abctune.metaText.origin) composerLine += ' (' + abctune.metaText.origin + ')';
|
|
374
|
-
if (composerLine.length > 0) {
|
|
375
|
-
var space;
|
|
376
|
-
this.wrapInAbsElem({el_type: "composer", startChar: -1, endChar: -1, text: composerLine}, 'meta-top rhythm', function() {
|
|
377
|
-
space = this.outputTextIf(this.padding.left + width, composerLine, 'composerfont', 'meta-top composer', 0, null, "end");
|
|
378
|
-
return space[2];
|
|
379
|
-
});
|
|
380
|
-
this.moveY(space[1]);
|
|
381
|
-
} else {
|
|
382
|
-
this.moveY(rSpace[1]);
|
|
383
|
-
}
|
|
384
|
-
// TODO-PER: The following is a hack to make the elements line up with abcm2ps. Don't know where the extra space is coming from.
|
|
385
|
-
this.moveY(-6);
|
|
386
|
-
//} else if (this.isPrint) {
|
|
387
|
-
// // abcm2ps adds this space whether there is anything to write or not.
|
|
388
|
-
// this.moveY(this.spacing.composer);
|
|
389
|
-
// var space2 = this.getTextSize("M", 'composerfont', 'meta-top');
|
|
390
|
-
// this.moveY(space2.height);
|
|
391
|
-
}
|
|
392
|
-
|
|
393
|
-
if (abctune.metaText.author && abctune.metaText.author.length > 0) {
|
|
394
|
-
var space3;
|
|
395
|
-
this.wrapInAbsElem({el_type: "author", startChar: -1, endChar: -1, text: abctune.metaText.author}, 'meta-top author', function() {
|
|
396
|
-
space3 = this.outputTextIf(this.padding.left + width, abctune.metaText.author, 'composerfont', 'meta-top author', 0, 0, "end");
|
|
397
|
-
return space3[2];
|
|
398
|
-
});
|
|
399
|
-
}
|
|
400
|
-
|
|
401
|
-
if (abctune.metaText.partOrder && abctune.metaText.partOrder.length > 0) {
|
|
402
|
-
var space4;
|
|
403
|
-
this.wrapInAbsElem({el_type: "partOrder", startChar: -1, endChar: -1, text: abctune.metaText.partOrder}, 'meta-top part-order', function() {
|
|
404
|
-
space4 = this.outputTextIf(this.padding.left, abctune.metaText.partOrder, 'partsfont', 'meta-top part-order', 0, 0, "start");
|
|
405
|
-
return space4[2];
|
|
406
|
-
});
|
|
407
|
-
}
|
|
408
|
-
};
|
|
409
|
-
|
|
410
|
-
Renderer.prototype.wrapInAbsElem = function(abcelem, klass, creator) {
|
|
411
|
-
this.controller.currentAbsEl = new AbsoluteElement(abcelem, 0, 0, klass, this.controller.engraver.tuneNumber, {});
|
|
412
|
-
var el = creator.bind(this)();
|
|
413
|
-
this.controller.currentAbsEl.elemset = [el];
|
|
414
|
-
};
|
|
415
|
-
|
|
416
|
-
/**
|
|
417
|
-
* Text that goes below the score
|
|
418
|
-
* @param {number} width
|
|
419
|
-
* @param {object} abctune
|
|
420
|
-
*/
|
|
421
|
-
Renderer.prototype.engraveExtraText = function(width, abctune) {
|
|
422
|
-
this.lineNumber = null;
|
|
423
|
-
this.measureNumber = null;
|
|
424
|
-
this.noteNumber = null;
|
|
425
|
-
this.voiceNumber = null;
|
|
426
|
-
|
|
427
|
-
if (abctune.metaText.unalignedWords && abctune.metaText.unalignedWords.length > 0) {
|
|
428
|
-
this.wrapInAbsElem({el_type: "unalignedWords", startChar: -1, endChar: -1}, 'meta-bottom extra-text', function () {
|
|
429
|
-
var hash = this.getFontAndAttr("wordsfont", 'meta-bottom unaligned-words');
|
|
430
|
-
var space = this.getTextSize("i", 'wordsfont', 'meta-bottom unaligned-words');
|
|
431
|
-
|
|
432
|
-
this.moveY(this.spacing.words, 1);
|
|
433
|
-
var historyLen = this.controller.history.length;
|
|
434
|
-
this.createElemSet({klass: "abcjs-meta-bottom abcjs-unaligned-words"});
|
|
435
|
-
for (var j = 0; j < abctune.metaText.unalignedWords.length; j++) {
|
|
436
|
-
if (abctune.metaText.unalignedWords[j] === '')
|
|
437
|
-
this.moveY(hash.font.size, 1);
|
|
438
|
-
else if (typeof abctune.metaText.unalignedWords[j] === 'string') {
|
|
439
|
-
this.outputTextIf(this.padding.left + spacing.INDENT, abctune.metaText.unalignedWords[j], 'wordsfont', 'meta-bottom unaligned-words', 0, 0, "start", true);
|
|
440
|
-
} else {
|
|
441
|
-
var largestY = 0;
|
|
442
|
-
var offsetX = 0;
|
|
443
|
-
for (var k = 0; k < abctune.metaText.unalignedWords[j].length; k++) {
|
|
444
|
-
var thisWord = abctune.metaText.unalignedWords[j][k];
|
|
445
|
-
var type = (thisWord.font) ? thisWord.font : "wordsfont";
|
|
446
|
-
this.renderText({x: this.padding.left + spacing.INDENT + offsetX, y: this.y, text: thisWord.text, type: type, klass: 'meta-bottom unaligned-words', anchor: 'start', noClass: true});
|
|
447
|
-
var size = this.getTextSize(thisWord.text, type, 'meta-bottom unaligned-words');
|
|
448
|
-
largestY = Math.max(largestY, size.height);
|
|
449
|
-
offsetX += size.width;
|
|
450
|
-
// If the phrase ends in a space, then that is not counted in the width, so we need to add that in ourselves.
|
|
451
|
-
if (thisWord.text[thisWord.text.length - 1] === ' ') {
|
|
452
|
-
offsetX += space.width;
|
|
453
|
-
}
|
|
454
|
-
}
|
|
455
|
-
this.moveY(largestY, 1);
|
|
456
|
-
}
|
|
457
|
-
}
|
|
458
|
-
this.moveY(hash.font.size, 2);
|
|
459
|
-
var g = this.closeElemSet();
|
|
460
|
-
this.controller.combineHistory(this.controller.history.length-historyLen, g);
|
|
461
|
-
return g;
|
|
462
|
-
});
|
|
463
|
-
}
|
|
464
|
-
|
|
465
|
-
var extraText = "";
|
|
466
|
-
if (abctune.metaText.book) extraText += "Book: " + abctune.metaText.book + "\n";
|
|
467
|
-
if (abctune.metaText.source) extraText += "Source: " + abctune.metaText.source + "\n";
|
|
468
|
-
if (abctune.metaText.discography) extraText += "Discography: " + abctune.metaText.discography + "\n";
|
|
469
|
-
if (abctune.metaText.notes) extraText += "Notes: " + abctune.metaText.notes + "\n";
|
|
470
|
-
if (abctune.metaText.transcription) extraText += "Transcription: " + abctune.metaText.transcription + "\n";
|
|
471
|
-
if (abctune.metaText.history) extraText += "History: " + abctune.metaText.history + "\n";
|
|
472
|
-
if (abctune.metaText['abc-copyright']) extraText += "Copyright: " + abctune.metaText['abc-copyright'] + "\n";
|
|
473
|
-
if (abctune.metaText['abc-creator']) extraText += "Creator: " + abctune.metaText['abc-creator'] + "\n";
|
|
474
|
-
if (abctune.metaText['abc-edited-by']) extraText += "Edited By: " + abctune.metaText['abc-edited-by'] + "\n";
|
|
475
|
-
if (extraText.length > 0) {
|
|
476
|
-
this.wrapInAbsElem({el_type: "extraText", startChar: -1, endChar: -1}, 'meta-bottom extra-text', function() {
|
|
477
|
-
var el = this.outputTextIf(this.padding.left, extraText, 'historyfont', 'meta-bottom extra-text', this.spacing.info, 0, "start");
|
|
478
|
-
return el[2];
|
|
479
|
-
});
|
|
480
|
-
}
|
|
481
|
-
|
|
482
|
-
if (abctune.metaText.footer && this.isPrint) {
|
|
483
|
-
this.controller.currentAbsEl = { tuneNumber: this.controller.engraver.tuneNumber, elemset: [], abcelem: { el_type: "footer", startChar: -1, endChar: -1, text: "" }};
|
|
484
|
-
// Note: whether there is a footer or not doesn't change any other positioning, so this doesn't change the Y-coordinate.
|
|
485
|
-
el = this.outputTextIf(this.padding.left, abctune.metaText.footer.left, 'footerfont', 'header meta-bottom', 0, null, 'start');
|
|
486
|
-
if (el[2])
|
|
487
|
-
this.controller.currentAbsEl.elemset.push(el[2]);
|
|
488
|
-
el = this.outputTextIf(this.padding.left + width / 2, abctune.metaText.footer.center, 'footerfont', 'header meta-bottom', 0, null, 'middle');
|
|
489
|
-
if (el[2])
|
|
490
|
-
this.controller.currentAbsEl.elemset.push(el[2]);
|
|
491
|
-
el = this.outputTextIf(this.padding.left + width, abctune.metaText.footer.right, 'footerfont', 'header meta-bottom', 0, null, 'end');
|
|
492
|
-
if (el[2])
|
|
493
|
-
this.controller.currentAbsEl.elemset.push(el[2]);
|
|
494
|
-
}
|
|
495
|
-
};
|
|
496
|
-
|
|
497
|
-
Renderer.prototype.outputFreeText = function (text, vskip) {
|
|
498
|
-
this.controller.currentAbsEl = { tuneNumber: this.controller.engraver.tuneNumber, elemset: [], abcelem: { el_type: "free-text", startChar: -1, endChar: -1, text: text }};
|
|
499
|
-
if (vskip)
|
|
500
|
-
this.moveY(vskip);
|
|
501
|
-
var hash = this.getFontAndAttr('textfont', 'defined-text');
|
|
502
|
-
if (text === "") { // we do want to print out blank lines if they have been specified.
|
|
503
|
-
this.moveY(hash.attr['font-size'] * 2); // move the distance of the line, plus the distance of the margin, which is also one line.
|
|
504
|
-
} else if (typeof text === 'string') {
|
|
505
|
-
this.moveY(hash.attr['font-size']/2); // TODO-PER: move down some - the y location should be the top of the text, but we output text specifying the center line.
|
|
506
|
-
this.outputTextIf(this.padding.left, text, 'textfont', 'defined-text', 0, 0, "start");
|
|
507
|
-
} else {
|
|
508
|
-
var str = "";
|
|
509
|
-
var isCentered = false; // The structure is wrong here: it requires an array to do centering, but it shouldn't have.
|
|
510
|
-
for (var i = 0; i < text.length; i++) {
|
|
511
|
-
if (text[i].font)
|
|
512
|
-
str += "FONT(" + text[i].font + ")";
|
|
513
|
-
str += text[i].text;
|
|
514
|
-
if (text[i].center)
|
|
515
|
-
isCentered = true;
|
|
516
|
-
}
|
|
517
|
-
var alignment = isCentered ? 'middle' : 'start';
|
|
518
|
-
var x = isCentered ? this.controller.width / 2 : this.padding.left;
|
|
519
|
-
this.outputTextIf(x, str, 'textfont', 'defined-text', 0, 1, alignment);
|
|
520
|
-
}
|
|
521
|
-
};
|
|
522
|
-
|
|
523
|
-
Renderer.prototype.outputSeparator = function (separator) {
|
|
524
|
-
if (!separator.lineLength)
|
|
525
|
-
return;
|
|
526
|
-
this.moveY(separator.spaceAbove);
|
|
527
|
-
this.printSeparator(separator.lineLength);
|
|
528
|
-
this.moveY(separator.spaceBelow);
|
|
529
|
-
};
|
|
530
|
-
|
|
531
|
-
/**
|
|
532
|
-
* Output an extra subtitle that is defined later in the tune.
|
|
533
|
-
*/
|
|
534
|
-
Renderer.prototype.outputSubtitle = function (width, subtitle) {
|
|
535
|
-
this.outputTextIf(this.padding.left + width / 2, subtitle, 'subtitlefont', 'text subtitle', this.spacing.subtitle, 0, 'middle');
|
|
536
|
-
};
|
|
537
|
-
|
|
538
|
-
/**
|
|
539
|
-
* Begin a group of glyphs that will always be moved, scaled and highlighted together
|
|
540
|
-
*/
|
|
541
|
-
Renderer.prototype.beginGroup = function () {
|
|
542
|
-
this.path = [];
|
|
543
|
-
this.lastM = [0,0];
|
|
544
|
-
this.ingroup = true;
|
|
545
|
-
};
|
|
546
166
|
|
|
547
|
-
/**
|
|
548
|
-
* Add a path to the current group
|
|
549
|
-
* @param {Array} path
|
|
550
|
-
* @private
|
|
551
|
-
*/
|
|
552
|
-
Renderer.prototype.addPath = function (path) {
|
|
553
|
-
path = path || [];
|
|
554
|
-
if (path.length===0) return;
|
|
555
|
-
path[0][0]="m";
|
|
556
|
-
path[0][1]-=this.lastM[0];
|
|
557
|
-
path[0][2]-=this.lastM[1];
|
|
558
|
-
this.lastM[0]+=path[0][1];
|
|
559
|
-
this.lastM[1]+=path[0][2];
|
|
560
|
-
this.path.push(path[0]);
|
|
561
|
-
for (var i=1,ii=path.length;i<ii;i++) {
|
|
562
|
-
if (path[i][0]==="m") {
|
|
563
|
-
this.lastM[0]+=path[i][1];
|
|
564
|
-
this.lastM[1]+=path[i][2];
|
|
565
|
-
}
|
|
566
|
-
this.path.push(path[i]);
|
|
567
|
-
}
|
|
568
|
-
};
|
|
569
|
-
|
|
570
|
-
/**
|
|
571
|
-
* End a group of glyphs that will always be moved, scaled and highlighted together
|
|
572
|
-
*/
|
|
573
|
-
Renderer.prototype.endGroup = function (klass) {
|
|
574
|
-
this.ingroup = false;
|
|
575
|
-
if (this.path.length===0) return null;
|
|
576
|
-
var path = "";
|
|
577
|
-
for (var i = 0; i < this.path.length; i++)
|
|
578
|
-
path += this.path[i].join(" ");
|
|
579
|
-
var ret = this.paper.path({path: path, stroke:"none", fill:"#000000", 'class': this.addClasses(klass)});
|
|
580
|
-
this.controller.recordHistory(ret);
|
|
581
|
-
this.path = [];
|
|
582
|
-
if (this.doRegression) this.addToRegression(ret);
|
|
583
|
-
|
|
584
|
-
return ret;
|
|
585
|
-
};
|
|
586
|
-
|
|
587
|
-
/**
|
|
588
|
-
* gets scaled
|
|
589
|
-
* @param {number} x1 start x
|
|
590
|
-
* @param {number} x2 end x
|
|
591
|
-
* @param {number} pitch pitch the stave line is drawn at
|
|
592
|
-
*/
|
|
593
|
-
Renderer.prototype.printStaveLine = function (x1,x2, pitch, klass) {
|
|
594
|
-
var isIE=/*@cc_on!@*/false;//IE detector
|
|
595
|
-
var dy = 0.35;
|
|
596
|
-
var fill = "#000000";
|
|
597
|
-
if (isIE) {
|
|
598
|
-
dy = 1;
|
|
599
|
-
fill = "#666666";
|
|
600
|
-
}
|
|
601
|
-
var y = this.calcY(pitch);
|
|
602
|
-
var pathString = sprintf("M %f %f L %f %f L %f %f L %f %f z", x1, y-dy, x2, y-dy,
|
|
603
|
-
x2, y+dy, x1, y+dy);
|
|
604
|
-
var options = {path:pathString, stroke:"none", fill:fill};
|
|
605
|
-
if (klass)
|
|
606
|
-
options['class'] = klass;
|
|
607
|
-
var ret = this.paper.pathToBack(options);
|
|
608
|
-
if (this.doRegression) this.addToRegression(ret);
|
|
609
|
-
|
|
610
|
-
return ret;
|
|
611
|
-
};
|
|
612
|
-
|
|
613
|
-
/**
|
|
614
|
-
* gets scaled if not in a group
|
|
615
|
-
* @param {number} x x coordinate of the stem
|
|
616
|
-
* @param {number} dx stem width
|
|
617
|
-
* @param {number} y1 y coordinate of the stem bottom
|
|
618
|
-
* @param {number} y2 y coordinate of the stem top
|
|
619
|
-
*/
|
|
620
|
-
Renderer.prototype.printStem = function (x, dx, y1, y2) {
|
|
621
|
-
if (dx<0) { // correct path "handedness" for intersection with other elements
|
|
622
|
-
var tmp = y2;
|
|
623
|
-
y2 = y1;
|
|
624
|
-
y1 = tmp;
|
|
625
|
-
}
|
|
626
|
-
var isIE=/*@cc_on!@*/false;//IE detector
|
|
627
|
-
var fill = "#000000";
|
|
628
|
-
if (isIE && dx<1) {
|
|
629
|
-
dx = 1;
|
|
630
|
-
fill = "#666666";
|
|
631
|
-
}
|
|
632
|
-
if (~~x === x) x+=0.05; // raphael does weird rounding (for VML)
|
|
633
|
-
var pathArray = [["M",x,y1],["L", x, y2],["L", x+dx, y2],["L",x+dx,y1],["z"]];
|
|
634
|
-
if (!isIE && this.ingroup) {
|
|
635
|
-
this.addPath(pathArray);
|
|
636
|
-
} else {
|
|
637
|
-
var path = "";
|
|
638
|
-
for (var i = 0; i < pathArray.length; i++)
|
|
639
|
-
path += pathArray[i].join(" ");
|
|
640
|
-
var ret = this.paper.pathToBack({path:path, stroke:"none", fill:fill, 'class': this.addClasses('stem')});
|
|
641
|
-
if (this.doRegression) this.addToRegression(ret);
|
|
642
|
-
|
|
643
|
-
return ret;
|
|
644
|
-
}
|
|
645
|
-
};
|
|
646
|
-
|
|
647
|
-
function kernSymbols(lastSymbol, thisSymbol, lastSymbolWidth) {
|
|
648
|
-
// This is just some adjustments to make it look better.
|
|
649
|
-
var width = lastSymbolWidth;
|
|
650
|
-
if (lastSymbol === 'f' && thisSymbol === 'f')
|
|
651
|
-
width = width*2/3;
|
|
652
|
-
if (lastSymbol === 'p' && thisSymbol === 'p')
|
|
653
|
-
width = width*5/6;
|
|
654
|
-
if (lastSymbol === 'f' && thisSymbol === 'z')
|
|
655
|
-
width = width*5/8;
|
|
656
|
-
return width;
|
|
657
|
-
}
|
|
658
|
-
|
|
659
|
-
/**
|
|
660
|
-
* assumes this.y is set appropriately
|
|
661
|
-
* if symbol is a multichar string without a . (as in scripts.staccato) 1 symbol per char is assumed
|
|
662
|
-
* not scaled if not in printgroup
|
|
663
|
-
*/
|
|
664
|
-
Renderer.prototype.printSymbol = function (x, offset, symbol, scalex, scaley, klass) {
|
|
665
|
-
var el;
|
|
666
|
-
var ycorr;
|
|
667
|
-
if (!symbol) return null;
|
|
668
|
-
if (symbol.length > 1 && symbol.indexOf(".") < 0) {
|
|
669
|
-
this.paper.openGroup({klass: klass});
|
|
670
|
-
var dx = 0;
|
|
671
|
-
for (var i = 0; i < symbol.length; i++) {
|
|
672
|
-
var s = symbol.charAt(i);
|
|
673
|
-
ycorr = glyphs.getYCorr(s);
|
|
674
|
-
el = glyphs.printSymbol(x + dx, this.calcY(offset + ycorr), s, this.paper, '');
|
|
675
|
-
if (el) {
|
|
676
|
-
if (this.doRegression) this.addToRegression(el);
|
|
677
|
-
if (i < symbol.length - 1)
|
|
678
|
-
dx += kernSymbols(s, symbol.charAt(i + 1), glyphs.getSymbolWidth(s));
|
|
679
|
-
} else {
|
|
680
|
-
this.renderText({ x: x, y: this.y, text: "no symbol:" + symbol, type: "debugfont", klass: 'debug-msg', anchor: 'start'});
|
|
681
|
-
}
|
|
682
|
-
}
|
|
683
|
-
var g = this.paper.closeGroup();
|
|
684
|
-
this.controller.recordHistory(g);
|
|
685
|
-
return g;
|
|
686
|
-
} else {
|
|
687
|
-
ycorr = glyphs.getYCorr(symbol);
|
|
688
|
-
if (this.ingroup) {
|
|
689
|
-
this.addPath(glyphs.getPathForSymbol(x, this.calcY(offset + ycorr), symbol, scalex, scaley));
|
|
690
|
-
} else {
|
|
691
|
-
el = glyphs.printSymbol(x, this.calcY(offset + ycorr), symbol, this.paper, klass);
|
|
692
|
-
this.controller.recordHistory(el);
|
|
693
|
-
if (el) {
|
|
694
|
-
if (this.doRegression) this.addToRegression(el);
|
|
695
|
-
return el;
|
|
696
|
-
} else
|
|
697
|
-
this.renderText({ x: x, y: this.y, text: "no symbol:" + symbol, type: "debugfont", klass: 'debug-msg', anchor: 'start'});
|
|
698
|
-
}
|
|
699
|
-
return null;
|
|
700
|
-
}
|
|
701
|
-
};
|
|
702
|
-
|
|
703
|
-
Renderer.prototype.scaleExistingElem = function (elem, scaleX, scaleY, x, y) {
|
|
704
|
-
this.paper.setAttributeOnElement(elem, { style: "transform:scale("+scaleX+","+scaleY + ");transform-origin:" + x + "px " + y + "px;"});
|
|
705
|
-
};
|
|
706
|
-
|
|
707
|
-
Renderer.prototype.printPath = function (attrs, params) {
|
|
708
|
-
var ret = this.paper.path(attrs);
|
|
709
|
-
if (!params || !params.history)
|
|
710
|
-
this.controller.recordHistory(ret);
|
|
711
|
-
else if (params.history === 'not-selectable')
|
|
712
|
-
this.controller.recordHistory(ret, true);
|
|
713
|
-
|
|
714
|
-
if (this.doRegression) this.addToRegression(ret);
|
|
715
|
-
return ret;
|
|
716
|
-
};
|
|
717
|
-
|
|
718
|
-
Renderer.prototype.drawArc = function(x1, x2, pitch1, pitch2, above, klass, isTie) {
|
|
719
|
-
// If it is a tie vs. a slur, draw it shallower.
|
|
720
|
-
var spacing = isTie ? 1.2 : 1.5;
|
|
721
|
-
|
|
722
|
-
x1 = x1 + 6;
|
|
723
|
-
x2 = x2 + 4;
|
|
724
|
-
pitch1 = pitch1 + ((above)?spacing:-spacing);
|
|
725
|
-
pitch2 = pitch2 + ((above)?spacing:-spacing);
|
|
726
|
-
var y1 = this.calcY(pitch1);
|
|
727
|
-
var y2 = this.calcY(pitch2);
|
|
728
|
-
|
|
729
|
-
//unit direction vector
|
|
730
|
-
var dx = x2-x1;
|
|
731
|
-
var dy = y2-y1;
|
|
732
|
-
var norm= Math.sqrt(dx*dx+dy*dy);
|
|
733
|
-
var ux = dx/norm;
|
|
734
|
-
var uy = dy/norm;
|
|
735
|
-
|
|
736
|
-
var flatten = norm/3.5;
|
|
737
|
-
var maxFlatten = isTie ? 10 : 25; // If it is a tie vs. a slur, draw it shallower.
|
|
738
|
-
var curve = ((above)?-1:1)*Math.min(maxFlatten, Math.max(4, flatten));
|
|
739
|
-
|
|
740
|
-
var controlx1 = x1+flatten*ux-curve*uy;
|
|
741
|
-
var controly1 = y1+flatten*uy+curve*ux;
|
|
742
|
-
var controlx2 = x2-flatten*ux-curve*uy;
|
|
743
|
-
var controly2 = y2-flatten*uy+curve*ux;
|
|
744
|
-
var thickness = 2;
|
|
745
|
-
var pathString = sprintf("M %f %f C %f %f %f %f %f %f C %f %f %f %f %f %f z", x1, y1,
|
|
746
|
-
controlx1, controly1, controlx2, controly2, x2, y2,
|
|
747
|
-
controlx2-thickness*uy, controly2+thickness*ux, controlx1-thickness*uy, controly1+thickness*ux, x1, y1);
|
|
748
|
-
if (klass)
|
|
749
|
-
klass += ' slur';
|
|
750
|
-
else
|
|
751
|
-
klass = 'slur';
|
|
752
|
-
var ret = this.paper.path({path:pathString, stroke:"none", fill:"#000000", 'class': this.addClasses(klass)});
|
|
753
|
-
this.controller.recordHistory(ret);
|
|
754
|
-
if (this.doRegression) this.addToRegression(ret);
|
|
755
|
-
|
|
756
|
-
return ret;
|
|
757
|
-
};
|
|
758
167
|
/**
|
|
759
168
|
* Calculates the y for a given pitch value (relative to the stave the renderer is currently printing)
|
|
760
169
|
* @param {number} ofs pitch value (bottom C on a G clef = 0, D=1, etc.)
|
|
@@ -763,244 +172,13 @@ Renderer.prototype.calcY = function(ofs) {
|
|
|
763
172
|
return this.y - ofs*spacing.STEP;
|
|
764
173
|
};
|
|
765
174
|
|
|
766
|
-
/**
|
|
767
|
-
* Print @param {number} numLines. If there is 1 line it is the B line. Otherwise the bottom line is the E line.
|
|
768
|
-
*/
|
|
769
|
-
Renderer.prototype.printStave = function (startx, endx, numLines) {
|
|
770
|
-
var klass = "abcjs-top-line";
|
|
771
|
-
this.paper.openGroup({ prepend: true, klass: "abcjs-staff abcjs-l" + this.lineNumber });
|
|
772
|
-
// If there is one line, it is the B line. Otherwise, the bottom line is the E line.
|
|
773
|
-
if (numLines === 1) {
|
|
774
|
-
this.printStaveLine(startx,endx,6, klass);
|
|
775
|
-
} else {
|
|
776
|
-
for (var i = numLines - 1; i >= 0; i--) {
|
|
777
|
-
this.printStaveLine(startx, endx, (i + 1) * 2, klass);
|
|
778
|
-
klass = undefined;
|
|
779
|
-
}
|
|
780
|
-
}
|
|
781
|
-
var ret = this.paper.closeGroup();
|
|
782
|
-
this.controller.currentAbsEl = { tuneNumber: this.controller.engraver.tuneNumber, elemset: [ret], abcelem: { el_type: "staff", startChar: -1, endChar: -1 }};
|
|
783
|
-
this.controller.recordHistory(ret, true);
|
|
784
|
-
};
|
|
785
|
-
|
|
786
|
-
/**
|
|
787
|
-
*
|
|
788
|
-
* @private
|
|
789
|
-
*/
|
|
790
|
-
Renderer.prototype.addClasses = function (c) {
|
|
791
|
-
if (!this.shouldAddClasses)
|
|
792
|
-
return "";
|
|
793
|
-
var ret = [];
|
|
794
|
-
if (c && c.length > 0) ret.push(c);
|
|
795
|
-
if (this.lineNumber !== null && this.lineNumber !== undefined) ret.push("l"+this.lineNumber);
|
|
796
|
-
if (this.measureNumber !== null && this.measureNumber !== undefined) ret.push("m"+this.measureNumber);
|
|
797
|
-
if (this.voiceNumber !== null && this.voiceNumber !== undefined) ret.push("v"+this.voiceNumber);
|
|
798
|
-
if (c && (c.indexOf('note') >= 0 || c.indexOf('rest') >= 0 || c.indexOf('lyric') >= 0 ) && this.noteNumber !== null && this.noteNumber !== undefined) ret.push("n"+this.noteNumber);
|
|
799
|
-
// add a prefix to all classes that abcjs adds.
|
|
800
|
-
if (ret.length > 0) {
|
|
801
|
-
ret = ret.join(' '); // Some strings are compound classes - that is, specify more than one class in a string.
|
|
802
|
-
ret = ret.split(' ');
|
|
803
|
-
for (var i = 0; i < ret.length; i++) {
|
|
804
|
-
if (ret[i].indexOf('abcjs-') !== 0 && ret[i].length > 0) // if the prefix doesn't already exist and the class is not blank.
|
|
805
|
-
ret[i] = 'abcjs-' + ret[i];
|
|
806
|
-
}
|
|
807
|
-
}
|
|
808
|
-
return ret.join(' ');
|
|
809
|
-
};
|
|
810
|
-
|
|
811
|
-
Renderer.prototype.getFontAndAttr = function(type, klass) {
|
|
812
|
-
var font;
|
|
813
|
-
if (typeof type === 'string') {
|
|
814
|
-
font = this.abctune.formatting[type];
|
|
815
|
-
// Raphael deliberately changes the font units to pixels for some reason, so we need to change points to pixels here.
|
|
816
|
-
if (font)
|
|
817
|
-
font = {face: font.face, size: Math.round(font.size * 4 / 3), decoration: font.decoration, style: font.style, weight: font.weight, box: font.box};
|
|
818
|
-
else
|
|
819
|
-
font = {face: "Arial", size: Math.round(12 * 4 / 3), decoration: "underline", style: "normal", weight: "normal"};
|
|
820
|
-
} else
|
|
821
|
-
font = {face: type.face, size: Math.round(type.size * 4 / 3), decoration: type.decoration, style: type.style, weight: type.weight, box: type.box};
|
|
822
|
-
|
|
823
|
-
var attr = {"font-size": font.size, 'font-style': font.style,
|
|
824
|
-
"font-family": font.face, 'font-weight': font.weight, 'text-decoration': font.decoration,
|
|
825
|
-
'class': this.addClasses(klass) };
|
|
826
|
-
//attr.font = ""; // There is a spurious font definition that is put on all text elements. This overwrites it.
|
|
827
|
-
return { font: font, attr: attr };
|
|
828
|
-
};
|
|
829
|
-
|
|
830
|
-
Renderer.prototype.getTextSize = function(text, type, klass, el) {
|
|
831
|
-
var hash = this.getFontAndAttr(type, klass);
|
|
832
|
-
var size = this.paper.getTextSize(text, hash.attr, el);
|
|
833
|
-
if (hash.font.box) {
|
|
834
|
-
return { height: size.height + 8, width: size.width + 8 };
|
|
835
|
-
}
|
|
836
|
-
return size;
|
|
837
|
-
};
|
|
838
|
-
|
|
839
|
-
Renderer.prototype.renderText = function(params) {
|
|
840
|
-
var hash = this.getFontAndAttr(params.type, params.klass);
|
|
841
|
-
if (params.anchor)
|
|
842
|
-
hash.attr["text-anchor"] = params.anchor;
|
|
843
|
-
hash.attr.x = params.x;
|
|
844
|
-
hash.attr.y = params.y + 7; // TODO-PER: Not sure why the text appears to be 7 pixels off.
|
|
845
|
-
if (!params.centerVertically)
|
|
846
|
-
hash.attr.dy = "0.5em";
|
|
847
|
-
if (params.type === 'debugfont') {
|
|
848
|
-
console.log("Debug msg: " + params.text);
|
|
849
|
-
hash.attr.stroke = "#ff0000";
|
|
850
|
-
}
|
|
851
|
-
|
|
852
|
-
var text = params.text.replace(/\n\n/g, "\n \n");
|
|
853
|
-
text = text.replace(/^\n/, "\xA0\n");
|
|
854
|
-
|
|
855
|
-
var klass2 = hash.attr['class'];
|
|
856
|
-
if (hash.font.box) {
|
|
857
|
-
hash.attr.x += 2;
|
|
858
|
-
hash.attr.y += 4;
|
|
859
|
-
delete hash.attr['class'];
|
|
860
|
-
this.createElemSet({klass: klass2, fill: "#000000"});
|
|
861
|
-
}
|
|
862
|
-
if (params.noClass)
|
|
863
|
-
delete hash.attr['class'];
|
|
864
|
-
var el = this.paper.text(text, hash.attr);
|
|
865
|
-
var elem = el;
|
|
866
|
-
|
|
867
|
-
if (hash.font.box) {
|
|
868
|
-
var size = this.getTextSize(text, params.type, params.klass); // This size already has the box factored in, so the needs to be taken into consideration.
|
|
869
|
-
var padding = 2;
|
|
870
|
-
this.paper.rect({ x: params.x - padding, y: params.y, width: size.width - padding, height: size.height - 8});
|
|
871
|
-
elem = this.closeElemSet();
|
|
872
|
-
}
|
|
873
|
-
if (!params.history)
|
|
874
|
-
this.controller.recordHistory(elem);
|
|
875
|
-
else if (params.history === 'not-selectable')
|
|
876
|
-
this.controller.recordHistory(elem, true);
|
|
877
|
-
if (this.doRegression) this.addToRegression(el);
|
|
878
|
-
return elem;
|
|
879
|
-
};
|
|
880
|
-
|
|
881
175
|
Renderer.prototype.moveY = function (em, numLines) {
|
|
882
176
|
if (numLines === undefined) numLines = 1;
|
|
883
|
-
this.y += em*numLines;
|
|
884
|
-
};
|
|
885
|
-
|
|
886
|
-
Renderer.prototype.skipSpaceY = function () {
|
|
887
|
-
this.y += this.space;
|
|
177
|
+
this.y += em * numLines;
|
|
888
178
|
};
|
|
889
179
|
|
|
890
|
-
|
|
891
|
-
|
|
892
|
-
// and alignment being "start", "middle", or "end".
|
|
893
|
-
Renderer.prototype.outputTextIf = function(x, str, kind, klass, marginTop, marginBottom, alignment, noClass) {
|
|
894
|
-
if (str) {
|
|
895
|
-
if (marginTop)
|
|
896
|
-
this.moveY(marginTop);
|
|
897
|
-
var el = this.renderText({x: x, y: this.y, text: str, type: kind, klass: klass, anchor: alignment, noClass: noClass});
|
|
898
|
-
var bb = this.getTextSize(str, kind, klass, el);
|
|
899
|
-
var width = isNaN(bb.width) ? 0 : bb.width;
|
|
900
|
-
var height = isNaN(bb.height) ? 0 : bb.height;
|
|
901
|
-
var hash = this.getFontAndAttr(kind, klass);
|
|
902
|
-
if (hash.font.box) {
|
|
903
|
-
width += 8;
|
|
904
|
-
height += 8;
|
|
905
|
-
}
|
|
906
|
-
if (marginBottom !== null) {
|
|
907
|
-
var numLines = str.split("\n").length;
|
|
908
|
-
if (!isNaN(bb.height))
|
|
909
|
-
this.moveY(height/numLines, (numLines + marginBottom));
|
|
910
|
-
}
|
|
911
|
-
return [width, height, el];
|
|
912
|
-
}
|
|
913
|
-
return [0,0];
|
|
914
|
-
};
|
|
915
|
-
|
|
916
|
-
Renderer.prototype.addInvisibleMarker = function (className) {
|
|
917
|
-
var y = Math.round(this.y);
|
|
918
|
-
this.paper.pathToBack({path:"M 0 " + y + " L 0 0", stroke:"none", fill:"none", "stroke-opacity": 0, "fill-opacity": 0, 'class': this.addClasses(className), 'data-vertical': y });
|
|
919
|
-
};
|
|
920
|
-
|
|
921
|
-
Renderer.prototype.printSeparator = function(width) {
|
|
922
|
-
var fill = "rgba(0,0,0,255)";
|
|
923
|
-
var stroke = "rgba(0,0,0,0)";
|
|
924
|
-
var y = Math.round(this.y);
|
|
925
|
-
var staffWidth = this.controller.width;
|
|
926
|
-
var x1 = (staffWidth - width)/2;
|
|
927
|
-
var x2 = x1 + width;
|
|
928
|
-
var pathString = 'M ' + x1 + ' ' + y +
|
|
929
|
-
' L ' + x2 + ' ' + y +
|
|
930
|
-
' L ' + x2 + ' ' + (y+1) +
|
|
931
|
-
' L ' + x1 + ' ' + (y+1) +
|
|
932
|
-
' L ' + x1 + ' ' + y + ' z';
|
|
933
|
-
this.paper.pathToBack({path:pathString, stroke:stroke, fill:fill, 'class': this.addClasses('defined-text')});
|
|
934
|
-
};
|
|
935
|
-
|
|
936
|
-
// For debugging, it is sometimes useful to know where you are vertically.
|
|
937
|
-
Renderer.prototype.printHorizontalLine = function (width, vertical, comment) {
|
|
938
|
-
var dy = 0.35;
|
|
939
|
-
var fill = "rgba(0,0,255,.4)";
|
|
940
|
-
var y = this.y;
|
|
941
|
-
if (vertical) y = vertical;
|
|
942
|
-
y = Math.round(y);
|
|
943
|
-
this.paper.text(""+Math.round(y), {x: 10, y: y, "text-anchor": "start", "font-size":"18px", fill: fill, stroke: fill });
|
|
944
|
-
var x1 = 50;
|
|
945
|
-
var x2 = width;
|
|
946
|
-
var pathString = sprintf("M %f %f L %f %f L %f %f L %f %f z", x1, y-dy, x1+x2, y-dy,
|
|
947
|
-
x2, y+dy, x1, y+dy);
|
|
948
|
-
this.paper.pathToBack({path:pathString, stroke:"none", fill:fill, 'class': this.addClasses('staff')});
|
|
949
|
-
for (var i = 1; i < width/100; i++) {
|
|
950
|
-
pathString = sprintf("M %f %f L %f %f L %f %f L %f %f z", i*100-dy, y-5, i*100-dy, y+5,
|
|
951
|
-
i*100+dy, y-5, i*100+dy, y+5);
|
|
952
|
-
this.paper.pathToBack({path:pathString, stroke:"none", fill:fill, 'class': this.addClasses('staff')});
|
|
953
|
-
}
|
|
954
|
-
if (comment)
|
|
955
|
-
this.paper.text(comment, {x: width+70, y: y, "text-anchor": "start", "font-size":"18px", fill: fill, stroke: fill });
|
|
956
|
-
};
|
|
957
|
-
|
|
958
|
-
Renderer.prototype.printShadedBox = function (x, y, width, height, color, opacity, comment) {
|
|
959
|
-
var box = this.paper.rect({ x: x, y: y, width: width, height: height, fill: color, stroke: color, "fill-opacity": opacity, "stroke-opacity": opacity });
|
|
960
|
-
if (comment)
|
|
961
|
-
this.paper.text(comment, {x: 0, y: y+7, "text-anchor": "start", "font-size":"14px", fill: "rgba(0,0,255,.4)", stroke: "rgba(0,0,255,.4)" });
|
|
962
|
-
return box;
|
|
963
|
-
};
|
|
964
|
-
|
|
965
|
-
Renderer.prototype.printVerticalLine = function (x, y1, y2) {
|
|
966
|
-
var dy = 0.35;
|
|
967
|
-
var fill = "#00aaaa";
|
|
968
|
-
var pathString = sprintf("M %f %f L %f %f L %f %f L %f %f z", x - dy, y1, x - dy, y2,
|
|
969
|
-
x + dy, y1, x + dy, y2);
|
|
970
|
-
this.paper.pathToBack({path: pathString, stroke: "none", fill: fill, 'class': this.addClasses('staff')});
|
|
971
|
-
pathString = sprintf("M %f %f L %f %f L %f %f L %f %f z", x - 20, y1, x - 20, y1+3,
|
|
972
|
-
x, y1, x, y1+3);
|
|
973
|
-
this.paper.pathToBack({path: pathString, stroke: "none", fill: fill, 'class': this.addClasses('staff')});
|
|
974
|
-
pathString = sprintf("M %f %f L %f %f L %f %f L %f %f z", x + 20, y2, x + 20, y2+3,
|
|
975
|
-
x, y2, x, y2+3);
|
|
976
|
-
this.paper.pathToBack({path: pathString, stroke: "none", fill: fill, 'class': this.addClasses('staff')});
|
|
977
|
-
|
|
978
|
-
};
|
|
979
|
-
|
|
980
|
-
/**
|
|
981
|
-
* @private
|
|
982
|
-
*/
|
|
983
|
-
Renderer.prototype.addToRegression = function (el) {
|
|
984
|
-
var box;
|
|
985
|
-
try {
|
|
986
|
-
box = el.getBBox();
|
|
987
|
-
} catch(e) {
|
|
988
|
-
box = { width: 0, height: 0 };
|
|
989
|
-
}
|
|
990
|
-
//var str = "("+box.x+","+box.y+")["+box.width+","+box.height+"] "
|
|
991
|
-
var str = el.type + ' ' + box.toString() + ' ';
|
|
992
|
-
var attrs = [];
|
|
993
|
-
for (var key in el.attrs) {
|
|
994
|
-
if (el.attrs.hasOwnProperty(key)) {
|
|
995
|
-
if (key === 'class')
|
|
996
|
-
str = el.attrs[key] + " " + str;
|
|
997
|
-
else
|
|
998
|
-
attrs.push(key+": "+el.attrs[key]);
|
|
999
|
-
}
|
|
1000
|
-
}
|
|
1001
|
-
attrs.sort();
|
|
1002
|
-
str += "{ " +attrs.join(" ") + " }";
|
|
1003
|
-
this.regressionLines.push(str);
|
|
180
|
+
Renderer.prototype.absolutemoveY = function (y) {
|
|
181
|
+
this.y = y;
|
|
1004
182
|
};
|
|
1005
183
|
|
|
1006
184
|
module.exports = Renderer;
|