abcjs-rails 1.11 → 2.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/app/assets/javascripts/abcjs/api/abc_animation.js +41 -1
- data/app/assets/javascripts/abcjs/api/abc_tunebook.js +5 -0
- data/app/assets/javascripts/abcjs/data/abc_tune.js +4 -3
- data/app/assets/javascripts/abcjs/edit/abc_editor.js +10 -0
- data/app/assets/javascripts/abcjs/parse/abc_parse.js +120 -19
- data/app/assets/javascripts/abcjs/parse/abc_parse_directive.js +456 -115
- data/app/assets/javascripts/abcjs/raphael.js +2 -2
- data/app/assets/javascripts/abcjs/write/abc_absolute_element.js +111 -4
- data/app/assets/javascripts/abcjs/write/abc_abstract_engraver.js +899 -0
- data/app/assets/javascripts/abcjs/write/abc_beam_element.js +263 -37
- data/app/assets/javascripts/abcjs/write/abc_create_clef.js +76 -0
- data/app/assets/javascripts/abcjs/write/abc_create_key_signature.js +41 -0
- data/app/assets/javascripts/abcjs/write/abc_create_time_signature.js +51 -0
- data/app/assets/javascripts/abcjs/write/{abc_cresendo_element.js → abc_crescendo_element.js} +32 -1
- data/app/assets/javascripts/abcjs/write/abc_decoration.js +321 -0
- data/app/assets/javascripts/abcjs/write/abc_dynamic_decoration.js +22 -1
- data/app/assets/javascripts/abcjs/write/abc_ending_element.js +31 -1
- data/app/assets/javascripts/abcjs/write/abc_engraver_controller.js +359 -0
- data/app/assets/javascripts/abcjs/write/abc_glyphs.js +119 -9
- data/app/assets/javascripts/abcjs/write/abc_layout.js +2 -2
- data/app/assets/javascripts/abcjs/write/abc_relative_element.js +106 -8
- data/app/assets/javascripts/abcjs/write/abc_renderer.js +754 -0
- data/app/assets/javascripts/abcjs/write/abc_staff_group_element.js +249 -9
- data/app/assets/javascripts/abcjs/write/abc_tempo_element.js +104 -0
- data/app/assets/javascripts/abcjs/write/abc_tie_element.js +69 -22
- data/app/assets/javascripts/abcjs/write/abc_triplet_element.js +77 -10
- data/app/assets/javascripts/abcjs/write/abc_voice_element.js +100 -8
- data/app/assets/javascripts/abcjs/write/abc_write.js +64 -68
- data/lib/abcjs-rails/version.rb +1 -1
- metadata +12 -4
@@ -447,7 +447,7 @@ ABCJS.write.Layout.prototype.printNote = function(elem, nostem, dontDraw) { //st
|
|
447
447
|
window.ABCJS.parse.each(elem.lyric, function(ly) {
|
448
448
|
lyricStr += ly.syllable + ly.divider + "\n";
|
449
449
|
});
|
450
|
-
abselem.addRight(new ABCJS.write.RelativeElement(lyricStr, 0, lyricStr.length*5, 0, {type:"
|
450
|
+
abselem.addRight(new ABCJS.write.RelativeElement(lyricStr, 0, lyricStr.length*5, 0, {type:"lyric"}));
|
451
451
|
}
|
452
452
|
|
453
453
|
if (!dontDraw && elem.gracenotes !== undefined) {
|
@@ -507,7 +507,7 @@ ABCJS.write.Layout.prototype.printNote = function(elem, nostem, dontDraw) { //st
|
|
507
507
|
}
|
508
508
|
|
509
509
|
if (elem.barNumber) {
|
510
|
-
abselem.addChild(new ABCJS.write.RelativeElement(elem.barNumber, -
|
510
|
+
abselem.addChild(new ABCJS.write.RelativeElement(elem.barNumber, -15, 0, 13, {type:"barNumber"}));
|
511
511
|
}
|
512
512
|
|
513
513
|
// ledger lines
|
@@ -34,39 +34,137 @@ ABCJS.write.RelativeElement = function(c, dx, w, pitch, opt) {
|
|
34
34
|
this.type = opt.type || "symbol"; // cheap types.
|
35
35
|
this.pitch2 = opt.pitch2;
|
36
36
|
this.linewidth = opt.linewidth;
|
37
|
-
this.
|
38
|
-
this.top = pitch
|
39
|
-
this.
|
37
|
+
this.klass = opt.klass;
|
38
|
+
this.top = pitch;
|
39
|
+
if (this.pitch2 !== undefined && this.pitch2 > this.top) this.top = this.pitch2;
|
40
|
+
this.bottom = pitch;
|
41
|
+
if (this.pitch2 !== undefined && this.pitch2 < this.bottom) this.bottom = this.pitch2;
|
42
|
+
if (opt.thickness) {
|
43
|
+
this.top += opt.thickness/2;
|
44
|
+
this.bottom -= opt.thickness/2;
|
45
|
+
}
|
46
|
+
if (opt.stemHeight) {
|
47
|
+
if (opt.stemHeight > 0)
|
48
|
+
this.top += opt.stemHeight;
|
49
|
+
else
|
50
|
+
this.bottom += opt.stemHeight;
|
51
|
+
}
|
52
|
+
//if (this.type === "symbol") {
|
53
|
+
// var offset = ABCJS.write.glyphs.getYCorr(this.c);
|
54
|
+
// this.top += offset;
|
55
|
+
// this.bottom += offset;
|
56
|
+
//}
|
57
|
+
this.centerVertically = false;
|
58
|
+
// TODO-PER: this should use the current font to determine the height. That requires the font to be passed in here, so refactor to store the font now instead of resolving it at draw time. This will allow the font to be changed mid-line, too.
|
59
|
+
var multiplier;
|
60
|
+
switch (this.type) {
|
61
|
+
case "debug":
|
62
|
+
this.chordHeightAbove = 3;
|
63
|
+
break;
|
64
|
+
case "lyric":
|
65
|
+
multiplier = this.c.split("\n").length;
|
66
|
+
if (opt.position && opt.position === 'below')
|
67
|
+
this.lyricHeightBelow = 3*multiplier;
|
68
|
+
else
|
69
|
+
this.lyricHeightAbove = 3*multiplier;
|
70
|
+
break;
|
71
|
+
case "chord":
|
72
|
+
multiplier = this.c.split("\n").length;
|
73
|
+
if (opt.position && opt.position === 'below')
|
74
|
+
this.chordHeightBelow = 4*multiplier;
|
75
|
+
else
|
76
|
+
this.chordHeightAbove = 4*multiplier;
|
77
|
+
break;
|
78
|
+
case "text":
|
79
|
+
multiplier = this.c.split("\n").length;
|
80
|
+
if (this.pitch === undefined) {
|
81
|
+
if (opt.position && opt.position === 'below')
|
82
|
+
this.chordHeightBelow = 4*multiplier;
|
83
|
+
else
|
84
|
+
this.chordHeightAbove = 4*multiplier;
|
85
|
+
} else
|
86
|
+
this.centerVertically = true;
|
87
|
+
break;
|
88
|
+
case "part": this.partHeightAbove = 6; break;
|
89
|
+
}
|
40
90
|
};
|
41
91
|
|
92
|
+
<<<<<<< HEAD
|
93
|
+
ABCJS.write.RelativeElement.prototype.setX = function (x) {
|
94
|
+
=======
|
42
95
|
ABCJS.write.RelativeElement.prototype.draw = function (renderer, x, bartop) {
|
96
|
+
>>>>>>> origin/master
|
43
97
|
this.x = x+this.dx;
|
98
|
+
};
|
99
|
+
|
100
|
+
ABCJS.write.RelativeElement.prototype.draw = function (renderer, bartop) {
|
101
|
+
if (this.pitch === undefined)
|
102
|
+
window.console.error(this.type + " Relative Element y-coordinate not set.");
|
103
|
+
var y = renderer.calcY(this.pitch);
|
44
104
|
switch(this.type) {
|
45
105
|
case "symbol":
|
46
106
|
if (this.c===null) return null;
|
107
|
+
<<<<<<< HEAD
|
108
|
+
var klass = "symbol";
|
109
|
+
if (this.klass) klass += " " + this.klass;
|
110
|
+
this.graphelem = renderer.printSymbol(this.x, this.pitch, this.c, this.scalex, this.scaley, renderer.addClasses(klass)); break;
|
111
|
+
case "debug":
|
112
|
+
this.graphelem = renderer.renderText(this.x, renderer.calcY(15), ""+this.c, "debugfont", 'debug-msg', 'start'); break;
|
113
|
+
case "barNumber":
|
114
|
+
this.graphelem = renderer.renderText(this.x, y, ""+this.c, "measurefont", 'bar-number', "start");
|
115
|
+
break;
|
116
|
+
case "lyric":
|
117
|
+
this.graphelem = renderer.renderText(this.x, y, this.c, "vocalfont", 'abc-lyric', "middle");
|
118
|
+
break;
|
119
|
+
case "chord":
|
120
|
+
this.graphelem = renderer.renderText(this.x, y, this.c, 'gchordfont', "chord", "middle");
|
121
|
+
break;
|
122
|
+
case "decoration":
|
123
|
+
this.graphelem = renderer.renderText(this.x, y, this.c, 'annotationfont', "annotation", "middle", true);
|
124
|
+
break;
|
125
|
+
case "text":
|
126
|
+
this.graphelem = renderer.renderText(this.x, y, this.c, 'annotationfont', "annotation", "start", this.centerVertically);
|
127
|
+
break;
|
128
|
+
case "part":
|
129
|
+
this.graphelem = renderer.renderText(this.x, y, this.c, 'partsfont', "part", "start");
|
130
|
+
break;
|
131
|
+
case "bar":
|
132
|
+
this.graphelem = renderer.printStem(this.x, this.linewidth, y, (bartop)?bartop:renderer.calcY(this.pitch2)); break; // bartop can't be 0
|
133
|
+
case "stem":
|
134
|
+
this.graphelem = renderer.printStem(this.x, this.linewidth, y, renderer.calcY(this.pitch2)); break;
|
135
|
+
=======
|
47
136
|
this.graphelem = renderer.printSymbol(this.x, this.pitch, this.c, this.scalex, this.scaley, renderer.addClasses('symbol')); break;
|
48
137
|
case "debug":
|
49
|
-
this.graphelem = renderer.
|
50
|
-
case "
|
51
|
-
this.graphelem = renderer.
|
138
|
+
this.graphelem = renderer.renderText(this.x, this.y, this.c, "debugfont", 'debug-msg', 'start'); break;
|
139
|
+
case "barNumber":
|
140
|
+
this.graphelem = renderer.renderText(this.x, renderer.calcY(this.pitch), this.c, "measurefont", 'bar-number', "start");
|
141
|
+
break;
|
142
|
+
case "lyric":
|
143
|
+
this.graphelem = renderer.renderText(this.x, renderer.calcY(renderer.layouter.minY-7), this.c, "vocalfont", 'abc-lyric');
|
144
|
+
break;
|
52
145
|
case "chord":
|
53
|
-
this.graphelem = renderer.
|
146
|
+
this.graphelem = renderer.renderText(this.x, renderer.calcY(this.pitch), this.c, 'gchordfont', "start", "chord");
|
54
147
|
break;
|
55
148
|
case "text":
|
56
|
-
this.graphelem = renderer.
|
149
|
+
this.graphelem = renderer.renderText(this.x, renderer.calcY(this.pitch), this.c, 'annotationfont', "start", "annotation");
|
57
150
|
break;
|
58
151
|
case "bar":
|
59
152
|
this.graphelem = renderer.printStem(this.x, this.linewidth, renderer.calcY(this.pitch), (bartop)?bartop:renderer.calcY(this.pitch2)); break; // bartop can't be 0
|
60
153
|
case "stem":
|
61
154
|
this.graphelem = renderer.printStem(this.x, this.linewidth, renderer.calcY(this.pitch), renderer.calcY(this.pitch2)); break;
|
155
|
+
>>>>>>> origin/master
|
62
156
|
case "ledger":
|
63
157
|
this.graphelem = renderer.printStaveLine(this.x, this.x+this.w, this.pitch); break;
|
64
158
|
}
|
65
159
|
if (this.scalex!==1 && this.graphelem) {
|
160
|
+
<<<<<<< HEAD
|
161
|
+
this.graphelem.scale(this.scalex, this.scaley, this.x, y);
|
162
|
+
=======
|
66
163
|
this.graphelem.scale(this.scalex, this.scaley, this.x, renderer.calcY(this.pitch));
|
67
164
|
}
|
68
165
|
if (this.attributes) {
|
69
166
|
this.graphelem.attr(this.attributes);
|
167
|
+
>>>>>>> origin/master
|
70
168
|
}
|
71
169
|
return this.graphelem;
|
72
170
|
};
|
@@ -0,0 +1,754 @@
|
|
1
|
+
// abc_renderer.js: API to render to SVG/Raphael/whatever rendering engine
|
2
|
+
// Copyright (C) 2010 Gregory Dyke (gregdyke at gmail dot com)
|
3
|
+
//
|
4
|
+
// This program is free software: you can redistribute it and/or modify
|
5
|
+
// it under the terms of the GNU General Public License as published by
|
6
|
+
// the Free Software Foundation, either version 3 of the License, or
|
7
|
+
// (at your option) any later version.
|
8
|
+
//
|
9
|
+
// This program is distributed in the hope that it will be useful,
|
10
|
+
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
11
|
+
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
12
|
+
// GNU General Public License for more details.
|
13
|
+
//
|
14
|
+
// You should have received a copy of the GNU General Public License
|
15
|
+
// along with this program. If not, see <http://www.gnu.org/licenses/>.
|
16
|
+
|
17
|
+
|
18
|
+
/*global window, ABCJS, Math, console */
|
19
|
+
|
20
|
+
if (!window.ABCJS)
|
21
|
+
window.ABCJS = {};
|
22
|
+
|
23
|
+
if (!window.ABCJS.write)
|
24
|
+
window.ABCJS.write = {};
|
25
|
+
|
26
|
+
/**
|
27
|
+
* Implements the API for rendering ABCJS Abstract Rendering Structure to a canvas/paper (e.g. SVG, Raphael, etc)
|
28
|
+
* @param {Object} paper
|
29
|
+
* @param {bool} doRegression
|
30
|
+
*/
|
31
|
+
ABCJS.write.Renderer = function(paper, doRegression) {
|
32
|
+
this.paper = paper;
|
33
|
+
this.controller = null; //TODO-GD only used when drawing the ABCJS ARS to connect the controller with the elements for highlighting
|
34
|
+
|
35
|
+
this.space = 3*ABCJS.write.spacing.SPACE;
|
36
|
+
this.padding = {}; // renderer's padding is managed by the controller
|
37
|
+
this.doRegression = doRegression;
|
38
|
+
if (this.doRegression)
|
39
|
+
this.regressionLines = [];
|
40
|
+
this.reset();
|
41
|
+
};
|
42
|
+
|
43
|
+
ABCJS.write.Renderer.prototype.reset = function() {
|
44
|
+
|
45
|
+
this.paper.clear();
|
46
|
+
this.y = 0;
|
47
|
+
this.abctune = null;
|
48
|
+
this.lastM = null;
|
49
|
+
this.ingroup = false;
|
50
|
+
this.path = null;
|
51
|
+
this.isPrint = false;
|
52
|
+
this.initVerticalSpace();
|
53
|
+
if (this.doRegression)
|
54
|
+
this.regressionLines = [];
|
55
|
+
// HACK-PER: There was a problem in Raphael where every path string that was sent to it was cached.
|
56
|
+
// That was causing the browser's memory to steadily grow until the browser went slower and slower until
|
57
|
+
// it crashed. The fix to that was a patch to Raphael, so it is only patched on the versions of this library that
|
58
|
+
// bundle Raphael with it. Also, if Raphael gets an update, then that patch will be lost. On version 2.1.2 of Raphael,
|
59
|
+
// the patch is on line 1542 and 1545 and it is:
|
60
|
+
// p[ps].sleep = 1;
|
61
|
+
};
|
62
|
+
|
63
|
+
/**
|
64
|
+
* Set whether we are formatting this for the screen, or as a preview for creating a PDF version.
|
65
|
+
* @param {bool} isPrint
|
66
|
+
*/
|
67
|
+
ABCJS.write.Renderer.prototype.setPrintMode = function (isPrint) {
|
68
|
+
this.isPrint = isPrint;
|
69
|
+
};
|
70
|
+
|
71
|
+
/**
|
72
|
+
* Set the size of the canvas.
|
73
|
+
* @param {object} maxwidth
|
74
|
+
* @param {object} scale
|
75
|
+
*/
|
76
|
+
ABCJS.write.Renderer.prototype.setPaperSize = function (maxwidth, scale) {
|
77
|
+
var w = (maxwidth+this.padding.right)*scale;
|
78
|
+
var h = (this.y+this.padding.bottom)*scale;
|
79
|
+
if (this.isPrint)
|
80
|
+
h = Math.max(h, 1056); // 11in x 72pt/in x 1.33px/pt
|
81
|
+
// TODO-PER: We are letting the page get as long as it needs now, but eventually that should go to a second page.
|
82
|
+
if (this.doRegression)
|
83
|
+
this.regressionLines.push("PAPER SIZE: ("+w+","+h+")");
|
84
|
+
|
85
|
+
this.paper.setSize(w/scale,h/scale);
|
86
|
+
// Correct for IE problem in calculating height
|
87
|
+
var isIE=/*@cc_on!@*/false;//IE detector
|
88
|
+
if (isIE) {
|
89
|
+
this.paper.canvas.parentNode.style.width=w+"px";
|
90
|
+
this.paper.canvas.parentNode.style.height=""+h+"px";
|
91
|
+
} else
|
92
|
+
this.paper.canvas.parentNode.setAttribute("style","width:"+w+"px");
|
93
|
+
if (scale !== 1) {
|
94
|
+
this.paper.canvas.style.transform = "scale("+scale+","+scale+")";
|
95
|
+
this.paper.canvas.style['-ms-tranform'] = "scale("+scale+","+scale+")";
|
96
|
+
this.paper.canvas.style['-webkit-tranform'] = "scale("+scale+","+scale+")";
|
97
|
+
this.paper.canvas.style['transform-origin'] = "0 0";
|
98
|
+
this.paper.canvas.style['-ms-transform-origin-x'] = "0";
|
99
|
+
this.paper.canvas.style['-ms-transform-origin-y'] = "0";
|
100
|
+
this.paper.canvas.style['-webkit-transform-origin-x'] = "0";
|
101
|
+
this.paper.canvas.style['-webkit-transform-origin-y'] = "0";
|
102
|
+
} else {
|
103
|
+
this.paper.canvas.style.transform = "";
|
104
|
+
this.paper.canvas.style['-ms-tranform'] = "";
|
105
|
+
this.paper.canvas.style['-webkit-tranform'] = "";
|
106
|
+
}
|
107
|
+
this.paper.canvas.parentNode.style.overflow="hidden";
|
108
|
+
this.paper.canvas.parentNode.style.height=""+h+"px";
|
109
|
+
};
|
110
|
+
|
111
|
+
/**
|
112
|
+
* Set the padding
|
113
|
+
* @param {object} params
|
114
|
+
*/
|
115
|
+
ABCJS.write.Renderer.prototype.setPaddingOverride = function(params) {
|
116
|
+
this.paddingOverride = { top: params.paddingtop, bottom: params.paddingbottom,
|
117
|
+
right: params.paddingright, left: params.paddingleft };
|
118
|
+
};
|
119
|
+
|
120
|
+
/**
|
121
|
+
* Set the padding
|
122
|
+
* @param {object} params
|
123
|
+
*/
|
124
|
+
ABCJS.write.Renderer.prototype.setPadding = function(abctune) {
|
125
|
+
// If the padding is set in the tune, then use that.
|
126
|
+
// Otherwise, if the padding is set in the override, use that.
|
127
|
+
// Otherwise, use the defaults (there are a different set of defaults for screen and print.)
|
128
|
+
function setPaddingVariable(self, paddingKey, formattingKey, printDefault, screenDefault) {
|
129
|
+
if (abctune.formatting[formattingKey] !== undefined)
|
130
|
+
self.padding[paddingKey] = abctune.formatting[formattingKey];
|
131
|
+
else if (self.paddingOverride[paddingKey] !== undefined)
|
132
|
+
self.padding[paddingKey] = self.paddingOverride[paddingKey];
|
133
|
+
else if (abctune.media === 'print')
|
134
|
+
self.padding[paddingKey] = printDefault;
|
135
|
+
else
|
136
|
+
self.padding[paddingKey] = screenDefault;
|
137
|
+
}
|
138
|
+
// 1cm x 0.393701in/cm x 72pt/in x 1.33px/pt = 38px
|
139
|
+
// 1.8cm x 0.393701in/cm x 72pt/in x 1.33px/pt = 68px
|
140
|
+
setPaddingVariable(this, 'top', 'topmargin', 38, 15);
|
141
|
+
setPaddingVariable(this, 'bottom', 'bottommargin', 38, 15);
|
142
|
+
setPaddingVariable(this, 'left', 'leftmargin', 68, 15);
|
143
|
+
setPaddingVariable(this, 'right', 'rightmargin', 68, 15);
|
144
|
+
};
|
145
|
+
|
146
|
+
/**
|
147
|
+
* Some of the items on the page are not scaled, so adjust them in the opposite direction of scaling to cancel out the scaling.
|
148
|
+
* @param {float} scale
|
149
|
+
*/
|
150
|
+
ABCJS.write.Renderer.prototype.adjustNonScaledItems = function (scale) {
|
151
|
+
this.padding.top /= scale;
|
152
|
+
this.padding.bottom /= scale;
|
153
|
+
this.padding.left /= scale;
|
154
|
+
this.padding.right /= scale;
|
155
|
+
this.abctune.formatting.headerfont.size /= scale;
|
156
|
+
this.abctune.formatting.footerfont.size /= scale;
|
157
|
+
};
|
158
|
+
|
159
|
+
/**
|
160
|
+
* Set the the values for all the configurable vertical space options.
|
161
|
+
*/
|
162
|
+
ABCJS.write.Renderer.prototype.initVerticalSpace = function() {
|
163
|
+
// conversion: 37.7953 = conversion factor for cm to px.
|
164
|
+
// All of the following values are in px.
|
165
|
+
this.spacing = {
|
166
|
+
composer: 7.56, // Set the vertical space above the composer.
|
167
|
+
graceBefore: 8.67, // Define the space before, inside and after the grace notes.
|
168
|
+
graceInside: 10.67,
|
169
|
+
graceAfter: 16,
|
170
|
+
info: 0, // Set the vertical space above the infoline.
|
171
|
+
lineSkipFactor: 1.1, // Set the factor for spacing between lines of text. (multiply this by the font size)
|
172
|
+
music: 7.56, // Set the vertical space above the first staff.
|
173
|
+
paragraphSkipFactor: 0.4, // Set the factor for spacing between text paragraphs. (multiply this by the font size)
|
174
|
+
parts: 11.33, // Set the vertical space above a new part.
|
175
|
+
slurHeight: 1.0, // Set the slur height factor.
|
176
|
+
staffSeparation: 61.33, // Do not put a staff system closer than <unit> from the previous system.
|
177
|
+
stemHeight: 26.67+10, // Set the stem height.
|
178
|
+
subtitle: 3.78, // Set the vertical space above the subtitle.
|
179
|
+
systemStaffSeparation: 48, // Do not place the staves closer than <unit> inside a system. * This values applies to all staves when in the tune header. Otherwise, it applies to the next staff
|
180
|
+
text: 18.9, // Set the vertical space above the history.
|
181
|
+
title: 7.56, // Set the vertical space above the title.
|
182
|
+
top: 30.24, //Set the vertical space above the tunes and on the top of the continuation pages.
|
183
|
+
vocal: 30.67, // Set the vertical space above the lyrics under the staves.
|
184
|
+
words: 0 // Set the vertical space above the lyrics at the end of the tune.
|
185
|
+
};
|
186
|
+
/*
|
187
|
+
TODO-PER: Handle the x-coordinate spacing items, too.
|
188
|
+
maxshrink <float>Default: 0.65
|
189
|
+
Set how much to compress horizontally when music line breaks
|
190
|
+
are automatic.
|
191
|
+
<float> must be between 0 (natural spacing)
|
192
|
+
and 1 (max shrinking).
|
193
|
+
|
194
|
+
// This next value is used to compute the natural spacing of
|
195
|
+
// the notes. The base spacing of the crotchet is always
|
196
|
+
// 40 pts. When the duration of a note type is twice the
|
197
|
+
// duration of an other note type, its spacing is multiplied
|
198
|
+
// by this factor.
|
199
|
+
// The default value causes the note spacing to be multiplied
|
200
|
+
// by 2 when its duration is multiplied by 4, i.e. the
|
201
|
+
// space of the semibreve is 80 pts and the space of the
|
202
|
+
// semiquaver is 20 pts.
|
203
|
+
// Setting this value to 1 sets all note spacing to 40 pts.
|
204
|
+
noteSpacingFactor: 1.414, // Set the note spacing factor to <float> (range 1..2).
|
205
|
+
|
206
|
+
scale <float> Default: 0.75 Set the page scale factor. Note that the header and footer are not scaled.
|
207
|
+
|
208
|
+
stretchlast <float>Default: 0.8
|
209
|
+
Stretch the last music line of a tune when it exceeds
|
210
|
+
the <float> fraction of the page width.
|
211
|
+
<float> range is 0.0 to 1.0.
|
212
|
+
*/
|
213
|
+
};
|
214
|
+
|
215
|
+
ABCJS.write.Renderer.prototype.setVerticalSpace = function(formatting) {
|
216
|
+
// conversion from pts to px 4/3
|
217
|
+
if (formatting.staffsep !== undefined)
|
218
|
+
this.spacing.staffSeparation = formatting.staffsep *4/3;
|
219
|
+
if (formatting.composerspace !== undefined)
|
220
|
+
this.spacing.composer = formatting.composerspace *4/3;
|
221
|
+
if (formatting.partsspace !== undefined)
|
222
|
+
this.spacing.parts = formatting.partsspace *4/3;
|
223
|
+
if (formatting.textspace !== undefined)
|
224
|
+
this.spacing.text = formatting.textspace *4/3;
|
225
|
+
if (formatting.musicspace !== undefined)
|
226
|
+
this.spacing.music = formatting.musicspace *4/3;
|
227
|
+
if (formatting.titlespace !== undefined)
|
228
|
+
this.spacing.title = formatting.titlespace *4/3;
|
229
|
+
if (formatting.sysstaffsep !== undefined)
|
230
|
+
this.spacing.systemStaffSeparation = formatting.sysstaffsep *4/3;
|
231
|
+
if (formatting.subtitlespace !== undefined)
|
232
|
+
this.spacing.subtitle = formatting.subtitlespace *4/3;
|
233
|
+
if (formatting.topspace !== undefined)
|
234
|
+
this.spacing.top = formatting.topspace *4/3;
|
235
|
+
if (formatting.vocalspace !== undefined)
|
236
|
+
this.spacing.vocal = formatting.vocalspace *4/3;
|
237
|
+
if (formatting.wordsspace !== undefined)
|
238
|
+
this.spacing.words = formatting.wordsspace *4/3;
|
239
|
+
};
|
240
|
+
|
241
|
+
/**
|
242
|
+
* Leave space at the top of the paper
|
243
|
+
* @param {object} abctune
|
244
|
+
*/
|
245
|
+
ABCJS.write.Renderer.prototype.topMargin = function(abctune) {
|
246
|
+
this.moveY(this.padding.top);
|
247
|
+
};
|
248
|
+
|
249
|
+
/**
|
250
|
+
* Leave space before printing the music
|
251
|
+
*/
|
252
|
+
ABCJS.write.Renderer.prototype.addMusicPadding = function() {
|
253
|
+
this.moveY(this.spacing.music);
|
254
|
+
};
|
255
|
+
|
256
|
+
/**
|
257
|
+
* Leave space before printing a staff system
|
258
|
+
*/
|
259
|
+
ABCJS.write.Renderer.prototype.addStaffPadding = function(lastStaffGroup, thisStaffGroup) {
|
260
|
+
var lastStaff = lastStaffGroup.staffs[lastStaffGroup.staffs.length-1];
|
261
|
+
var lastBottomLine = -(lastStaff.bottom - 2); // The 2 is because the scale goes to 2 below the last line.
|
262
|
+
var nextTopLine = thisStaffGroup.staffs[0].top - 10; // Because 10 represents the top line.
|
263
|
+
var naturalSeparation = nextTopLine + lastBottomLine; // This is how far apart they'd be without extra spacing
|
264
|
+
var separationInPixels = naturalSeparation * ABCJS.write.spacing.STEP;
|
265
|
+
if (separationInPixels < this.spacing.staffSeparation)
|
266
|
+
this.moveY(this.spacing.staffSeparation-separationInPixels);
|
267
|
+
};
|
268
|
+
|
269
|
+
/**
|
270
|
+
* Text that goes above the score
|
271
|
+
* @param {number} width
|
272
|
+
* @param {object} abctune
|
273
|
+
*/
|
274
|
+
ABCJS.write.Renderer.prototype.engraveTopText = function(width, abctune) {
|
275
|
+
if (abctune.metaText.header && this.isPrint) {
|
276
|
+
// Note: whether there is a header or not doesn't change any other positioning, so this doesn't change the Y-coordinate.
|
277
|
+
// This text goes above the margin, so we'll temporarily move up.
|
278
|
+
var headerTextHeight = this.getTextSize("XXXX", "headerfont", 'header meta-top').height;
|
279
|
+
this.y -=headerTextHeight;
|
280
|
+
this.outputTextIf(this.padding.left, abctune.metaText.header.left, 'headerfont', 'header meta-top', 0, null, 'start');
|
281
|
+
this.outputTextIf(this.padding.left + width / 2, abctune.metaText.header.center, 'headerfont', 'header meta-top', 0, null, 'middle');
|
282
|
+
this.outputTextIf(this.padding.left + width, abctune.metaText.header.right, 'headerfont', 'header meta-top', 0, null, 'end');
|
283
|
+
this.y += headerTextHeight;
|
284
|
+
}
|
285
|
+
if (this.isPrint)
|
286
|
+
this.moveY(this.spacing.top);
|
287
|
+
this.outputTextIf(this.padding.left + width / 2, abctune.metaText.title, 'titlefont', 'title meta-top', this.spacing.title, 0, 'middle');
|
288
|
+
if (abctune.lines[0])
|
289
|
+
this.outputTextIf(this.padding.left + width / 2, abctune.lines[0].subtitle, 'subtitlefont', 'text meta-top', this.spacing.subtitle, 0, 'middle');
|
290
|
+
|
291
|
+
if (abctune.metaText.rhythm || abctune.metaText.origin || abctune.metaText.composer) {
|
292
|
+
this.moveY(this.spacing.composer);
|
293
|
+
var rSpace = this.outputTextIf(this.padding.left, abctune.metaText.rhythm, 'infofont', 'meta-top', 0, null, "start");
|
294
|
+
|
295
|
+
var composerLine = "";
|
296
|
+
if (abctune.metaText.composer) composerLine += abctune.metaText.composer;
|
297
|
+
if (abctune.metaText.origin) composerLine += ' (' + abctune.metaText.origin + ')';
|
298
|
+
if (composerLine.length > 0) {
|
299
|
+
var space = this.outputTextIf(this.padding.left + width, composerLine, 'composerfont', 'meta-top', 0, null, "end");
|
300
|
+
this.moveY(space[1]);
|
301
|
+
} else {
|
302
|
+
this.moveY(rSpace[1]);
|
303
|
+
}
|
304
|
+
// 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.
|
305
|
+
this.moveY(-6);
|
306
|
+
//} else if (this.isPrint) {
|
307
|
+
// // abcm2ps adds this space whether there is anything to write or not.
|
308
|
+
// this.moveY(this.spacing.composer);
|
309
|
+
// var space2 = this.getTextSize("M", 'composerfont', 'meta-top');
|
310
|
+
// this.moveY(space2.height);
|
311
|
+
}
|
312
|
+
|
313
|
+
this.outputTextIf(this.padding.left + width, abctune.metaText.author, 'composerfont', 'meta-top', 0, 0, "end");
|
314
|
+
//this.skipSpaceY();
|
315
|
+
|
316
|
+
this.outputTextIf(this.padding.left, abctune.metaText.partOrder, 'partsfont', 'meta-bottom', 0, 0, "start");
|
317
|
+
};
|
318
|
+
|
319
|
+
/**
|
320
|
+
* Text that goes below the score
|
321
|
+
* @param {number} width
|
322
|
+
* @param {object} abctune
|
323
|
+
*/
|
324
|
+
ABCJS.write.Renderer.prototype.engraveExtraText = function(width, abctune) {
|
325
|
+
this.lineNumber = null;
|
326
|
+
this.measureNumber = null;
|
327
|
+
|
328
|
+
var extraText;
|
329
|
+
if (abctune.metaText.unalignedWords) {
|
330
|
+
extraText = "";
|
331
|
+
for (var j = 0; j < abctune.metaText.unalignedWords.length; j++) {
|
332
|
+
if (typeof abctune.metaText.unalignedWords[j] === 'string')
|
333
|
+
extraText += abctune.metaText.unalignedWords[j] + "\n";
|
334
|
+
else {
|
335
|
+
for (var k = 0; k < abctune.metaText.unalignedWords[j].length; k++) {
|
336
|
+
extraText += " FONT " + abctune.metaText.unalignedWords[j][k].text;
|
337
|
+
}
|
338
|
+
extraText += "\n";
|
339
|
+
}
|
340
|
+
}
|
341
|
+
this.outputTextIf(this.padding.left + ABCJS.write.spacing.INDENT, extraText, 'wordsfont', 'meta-bottom', this.spacing.words, 2, "start");
|
342
|
+
}
|
343
|
+
|
344
|
+
extraText = "";
|
345
|
+
if (abctune.metaText.book) extraText += "Book: " + abctune.metaText.book + "\n";
|
346
|
+
if (abctune.metaText.source) extraText += "Source: " + abctune.metaText.source + "\n";
|
347
|
+
if (abctune.metaText.discography) extraText += "Discography: " + abctune.metaText.discography + "\n";
|
348
|
+
if (abctune.metaText.notes) extraText += "Notes: " + abctune.metaText.notes + "\n";
|
349
|
+
if (abctune.metaText.transcription) extraText += "Transcription: " + abctune.metaText.transcription + "\n";
|
350
|
+
if (abctune.metaText.history) extraText += "History: " + abctune.metaText.history + "\n";
|
351
|
+
if (abctune.metaText['abc-copyright']) extraText += "Copyright: " + abctune.metaText['abc-copyright'] + "\n";
|
352
|
+
if (abctune.metaText['abc-creator']) extraText += "Creator: " + abctune.metaText['abc-creator'] + "\n";
|
353
|
+
if (abctune.metaText['abc-edited-by']) extraText += "Edited By: " + abctune.metaText['abc-edited-by'] + "\n";
|
354
|
+
this.outputTextIf(this.padding.left, extraText, 'historyfont', 'meta-bottom', this.spacing.info, 0, "start");
|
355
|
+
|
356
|
+
if (abctune.metaText.footer && this.isPrint) {
|
357
|
+
// Note: whether there is a footer or not doesn't change any other positioning, so this doesn't change the Y-coordinate.
|
358
|
+
this.outputTextIf(this.padding.left, abctune.metaText.footer.left, 'footerfont', 'header meta-bottom', 0, null, 'start');
|
359
|
+
this.outputTextIf(this.padding.left + width / 2, abctune.metaText.footer.center, 'footerfont', 'header meta-bottom', 0, null, 'middle');
|
360
|
+
this.outputTextIf(this.padding.left + width, abctune.metaText.footer.right, 'footerfont', 'header meta-bottom', 0, null, 'end');
|
361
|
+
}
|
362
|
+
};
|
363
|
+
|
364
|
+
/**
|
365
|
+
* Output text defined with %%text.
|
366
|
+
* @param {array or string} text
|
367
|
+
*/
|
368
|
+
ABCJS.write.Renderer.prototype.outputFreeText = function (text) {
|
369
|
+
if (typeof text === 'string')
|
370
|
+
this.outputTextIf(this.padding.left, text, 'textfont', 'defined-text', 0, 1, "start");
|
371
|
+
else {
|
372
|
+
var str = "";
|
373
|
+
for (var i = 0; i < text.length; i++) {
|
374
|
+
str += " FONT " + text[i].text;
|
375
|
+
}
|
376
|
+
this.outputTextIf(this.padding.left, str, 'textfont', 'defined-text', 0, 1, "start");
|
377
|
+
}
|
378
|
+
};
|
379
|
+
|
380
|
+
/**
|
381
|
+
* Output an extra subtitle that is defined later in the tune.
|
382
|
+
*/
|
383
|
+
ABCJS.write.Renderer.prototype.outputSubtitle = function (width, subtitle) {
|
384
|
+
this.outputTextIf(this.padding.left + width / 2, subtitle, 'subtitlefont', 'text meta-top', this.spacing.subtitle, 0, 'middle');
|
385
|
+
};
|
386
|
+
|
387
|
+
/**
|
388
|
+
* Begin a group of glyphs that will always be moved, scaled and highlighted together
|
389
|
+
*/
|
390
|
+
ABCJS.write.Renderer.prototype.beginGroup = function () {
|
391
|
+
this.path = [];
|
392
|
+
this.lastM = [0,0];
|
393
|
+
this.ingroup = true;
|
394
|
+
};
|
395
|
+
|
396
|
+
/**
|
397
|
+
* Add a path to the current group
|
398
|
+
* @param {Array} path
|
399
|
+
* @private
|
400
|
+
*/
|
401
|
+
ABCJS.write.Renderer.prototype.addPath = function (path) {
|
402
|
+
path = path || [];
|
403
|
+
if (path.length===0) return;
|
404
|
+
path[0][0]="m";
|
405
|
+
path[0][1]-=this.lastM[0];
|
406
|
+
path[0][2]-=this.lastM[1];
|
407
|
+
this.lastM[0]+=path[0][1];
|
408
|
+
this.lastM[1]+=path[0][2];
|
409
|
+
this.path.push(path[0]);
|
410
|
+
for (var i=1,ii=path.length;i<ii;i++) {
|
411
|
+
if (path[i][0]==="m") {
|
412
|
+
this.lastM[0]+=path[i][1];
|
413
|
+
this.lastM[1]+=path[i][2];
|
414
|
+
}
|
415
|
+
this.path.push(path[i]);
|
416
|
+
}
|
417
|
+
};
|
418
|
+
|
419
|
+
/**
|
420
|
+
* End a group of glyphs that will always be moved, scaled and highlighted together
|
421
|
+
*/
|
422
|
+
ABCJS.write.Renderer.prototype.endGroup = function (klass) {
|
423
|
+
this.ingroup = false;
|
424
|
+
if (this.path.length===0) return null;
|
425
|
+
var ret = this.paper.path().attr({path:this.path, stroke:"none", fill:"#000000", 'class': this.addClasses(klass)});
|
426
|
+
this.path = [];
|
427
|
+
if (this.doRegression) this.addToRegression(ret);
|
428
|
+
|
429
|
+
return ret;
|
430
|
+
};
|
431
|
+
|
432
|
+
/**
|
433
|
+
* gets scaled
|
434
|
+
* @param {number} x1 start x
|
435
|
+
* @param {number} x2 end x
|
436
|
+
* @param {number} pitch pitch the stave line is drawn at
|
437
|
+
*/
|
438
|
+
ABCJS.write.Renderer.prototype.printStaveLine = function (x1,x2, pitch, klass) {
|
439
|
+
var extraClass = "staff";
|
440
|
+
if (klass !== undefined)
|
441
|
+
extraClass += " " + klass;
|
442
|
+
var isIE=/*@cc_on!@*/false;//IE detector
|
443
|
+
var dy = 0.35;
|
444
|
+
var fill = "#000000";
|
445
|
+
if (isIE) {
|
446
|
+
dy = 1;
|
447
|
+
fill = "#666666";
|
448
|
+
}
|
449
|
+
var y = this.calcY(pitch);
|
450
|
+
var pathString = ABCJS.write.sprintf("M %f %f L %f %f L %f %f L %f %f z", x1, y-dy, x2, y-dy,
|
451
|
+
x2, y+dy, x1, y+dy);
|
452
|
+
var ret = this.paper.path().attr({path:pathString, stroke:"none", fill:fill, 'class': this.addClasses(extraClass)}).toBack();
|
453
|
+
if (this.doRegression) this.addToRegression(ret);
|
454
|
+
|
455
|
+
return ret;
|
456
|
+
};
|
457
|
+
|
458
|
+
/**
|
459
|
+
* gets scaled if not in a group
|
460
|
+
* @param {number} x1 x coordinate of the stem
|
461
|
+
* @param {number} dx stem width
|
462
|
+
* @param {number} y1 y coordinate of the stem bottom
|
463
|
+
* @param {number} y2 y coordinate of the stem top
|
464
|
+
*/
|
465
|
+
ABCJS.write.Renderer.prototype.printStem = function (x, dx, y1, y2) {
|
466
|
+
if (dx<0) { // correct path "handedness" for intersection with other elements
|
467
|
+
var tmp = y2;
|
468
|
+
y2 = y1;
|
469
|
+
y1 = tmp;
|
470
|
+
}
|
471
|
+
var isIE=/*@cc_on!@*/false;//IE detector
|
472
|
+
var fill = "#000000";
|
473
|
+
if (isIE && dx<1) {
|
474
|
+
dx = 1;
|
475
|
+
fill = "#666666";
|
476
|
+
}
|
477
|
+
if (~~x === x) x+=0.05; // raphael does weird rounding (for VML)
|
478
|
+
var pathArray = [["M",x,y1],["L", x, y2],["L", x+dx, y2],["L",x+dx,y1],["z"]];
|
479
|
+
if (!isIE && this.ingroup) {
|
480
|
+
this.addPath(pathArray);
|
481
|
+
} else {
|
482
|
+
var ret = this.paper.path().attr({path:pathArray, stroke:"none", fill:fill, 'class': this.addClasses('stem')}).toBack();
|
483
|
+
if (this.doRegression) this.addToRegression(ret);
|
484
|
+
|
485
|
+
return ret;
|
486
|
+
}
|
487
|
+
};
|
488
|
+
|
489
|
+
function kernSymbols(lastSymbol, thisSymbol, lastSymbolWidth) {
|
490
|
+
// This is just some adjustments to make it look better.
|
491
|
+
var width = lastSymbolWidth;
|
492
|
+
if (lastSymbol === 'f' && thisSymbol === 'f')
|
493
|
+
width = width*2/3;
|
494
|
+
if (lastSymbol === 'p' && thisSymbol === 'p')
|
495
|
+
width = width*5/6;
|
496
|
+
if (lastSymbol === 'f' && thisSymbol === 'z')
|
497
|
+
width = width*5/8;
|
498
|
+
return width;
|
499
|
+
}
|
500
|
+
|
501
|
+
/**
|
502
|
+
* assumes this.y is set appropriately
|
503
|
+
* if symbol is a multichar string without a . (as in scripts.staccato) 1 symbol per char is assumed
|
504
|
+
* not scaled if not in printgroup
|
505
|
+
*/
|
506
|
+
ABCJS.write.Renderer.prototype.printSymbol = function(x, offset, symbol, scalex, scaley, klass) {
|
507
|
+
var el;
|
508
|
+
var ycorr;
|
509
|
+
if (!symbol) return null;
|
510
|
+
if (symbol.length>0 && symbol.indexOf(".")<0) {
|
511
|
+
var elemset = this.paper.set();
|
512
|
+
var dx =0;
|
513
|
+
for (var i=0; i<symbol.length; i++) {
|
514
|
+
var s = symbol.charAt(i);
|
515
|
+
ycorr = ABCJS.write.glyphs.getYCorr(s);
|
516
|
+
el = ABCJS.write.glyphs.printSymbol(x+dx, this.calcY(offset+ycorr), s, this.paper, klass);
|
517
|
+
if (el) {
|
518
|
+
if (this.doRegression) this.addToRegression(el);
|
519
|
+
elemset.push(el);
|
520
|
+
if (i < symbol.length-1)
|
521
|
+
dx+= kernSymbols(s, symbol.charAt(i+1), ABCJS.write.glyphs.getSymbolWidth(s));
|
522
|
+
} else {
|
523
|
+
this.renderText(x, this.y, "no symbol:" +symbol, "debugfont", 'debug-msg', 'start');
|
524
|
+
}
|
525
|
+
}
|
526
|
+
return elemset;
|
527
|
+
} else {
|
528
|
+
ycorr = ABCJS.write.glyphs.getYCorr(symbol);
|
529
|
+
if (this.ingroup) {
|
530
|
+
this.addPath(ABCJS.write.glyphs.getPathForSymbol(x, this.calcY(offset+ycorr), symbol, scalex, scaley));
|
531
|
+
} else {
|
532
|
+
el = ABCJS.write.glyphs.printSymbol(x, this.calcY(offset+ycorr), symbol, this.paper, klass);
|
533
|
+
if (el) {
|
534
|
+
if (this.doRegression) this.addToRegression(el);
|
535
|
+
return el;
|
536
|
+
} else
|
537
|
+
this.renderText(x, this.y, "no symbol:" +symbol, "debugfont", 'debug-msg', 'start');
|
538
|
+
}
|
539
|
+
return null;
|
540
|
+
}
|
541
|
+
};
|
542
|
+
|
543
|
+
|
544
|
+
ABCJS.write.Renderer.prototype.printPath = function (attrs) {
|
545
|
+
var ret = this.paper.path().attr(attrs);
|
546
|
+
if (this.doRegression) this.addToRegression(ret);
|
547
|
+
return ret;
|
548
|
+
};
|
549
|
+
|
550
|
+
ABCJS.write.Renderer.prototype.drawArc = function(x1, x2, pitch1, pitch2, above) {
|
551
|
+
|
552
|
+
|
553
|
+
x1 = x1 + 6;
|
554
|
+
x2 = x2 + 4;
|
555
|
+
pitch1 = pitch1 + ((above)?1.5:-1.5);
|
556
|
+
pitch2 = pitch2 + ((above)?1.5:-1.5);
|
557
|
+
var y1 = this.calcY(pitch1);
|
558
|
+
var y2 = this.calcY(pitch2);
|
559
|
+
|
560
|
+
//unit direction vector
|
561
|
+
var dx = x2-x1;
|
562
|
+
var dy = y2-y1;
|
563
|
+
var norm= Math.sqrt(dx*dx+dy*dy);
|
564
|
+
var ux = dx/norm;
|
565
|
+
var uy = dy/norm;
|
566
|
+
|
567
|
+
var flatten = norm/3.5;
|
568
|
+
var curve = ((above)?-1:1)*Math.min(25, Math.max(4, flatten));
|
569
|
+
|
570
|
+
var controlx1 = x1+flatten*ux-curve*uy;
|
571
|
+
var controly1 = y1+flatten*uy+curve*ux;
|
572
|
+
var controlx2 = x2-flatten*ux-curve*uy;
|
573
|
+
var controly2 = y2-flatten*uy+curve*ux;
|
574
|
+
var thickness = 2;
|
575
|
+
var pathString = ABCJS.write.sprintf("M %f %f C %f %f %f %f %f %f C %f %f %f %f %f %f z", x1, y1,
|
576
|
+
controlx1, controly1, controlx2, controly2, x2, y2,
|
577
|
+
controlx2-thickness*uy, controly2+thickness*ux, controlx1-thickness*uy, controly1+thickness*ux, x1, y1);
|
578
|
+
var ret = this.paper.path().attr({path:pathString, stroke:"none", fill:"#000000", 'class': this.addClasses('slur')});
|
579
|
+
if (this.doRegression) this.addToRegression(ret);
|
580
|
+
|
581
|
+
return ret;
|
582
|
+
};
|
583
|
+
/**
|
584
|
+
* Calculates the y for a given pitch value (relative to the stave the renderer is currently printing)
|
585
|
+
* @param {number} ofs pitch value (bottom C on a G clef = 0, D=1, etc.)
|
586
|
+
*/
|
587
|
+
ABCJS.write.Renderer.prototype.calcY = function(ofs) {
|
588
|
+
return this.y - ofs*ABCJS.write.spacing.STEP;
|
589
|
+
};
|
590
|
+
|
591
|
+
/**
|
592
|
+
* Print @param {number} numLines. If there is 1 line it is the B line. Otherwise the bottom line is the E line.
|
593
|
+
*/
|
594
|
+
ABCJS.write.Renderer.prototype.printStave = function (startx, endx, numLines) {
|
595
|
+
var klass = "top-line";
|
596
|
+
// If there is one line, it is the B line. Otherwise, the bottom line is the E line.
|
597
|
+
if (numLines === 1) {
|
598
|
+
this.printStaveLine(startx,endx,6, klass);
|
599
|
+
return;
|
600
|
+
}
|
601
|
+
for (var i = numLines-1; i >= 0; i--) {
|
602
|
+
this.printStaveLine(startx,endx,(i+1)*2, klass);
|
603
|
+
klass = undefined;
|
604
|
+
}
|
605
|
+
};
|
606
|
+
|
607
|
+
/**
|
608
|
+
*
|
609
|
+
* @private
|
610
|
+
*/
|
611
|
+
ABCJS.write.Renderer.prototype.addClasses = function (c) {
|
612
|
+
var ret = [];
|
613
|
+
if (c.length > 0) ret.push(c);
|
614
|
+
if (this.lineNumber !== null) ret.push("l"+this.lineNumber);
|
615
|
+
if (this.measureNumber !== null) ret.push("m"+this.measureNumber);
|
616
|
+
return ret.join(' ');
|
617
|
+
};
|
618
|
+
|
619
|
+
ABCJS.write.Renderer.prototype.getFontAndAttr = function(type, klass) {
|
620
|
+
var font = this.abctune.formatting[type];
|
621
|
+
// Raphael deliberately changes the font units to pixels for some reason, so we need to change points to pixels here.
|
622
|
+
if (font)
|
623
|
+
font = { face: font.face, size: font.size*4/3, decoration: font.decoration, style: font.style, weight: font.weight };
|
624
|
+
else
|
625
|
+
font = { face: "Arial", size: 12*4/3, decoration: "underline", style: "normal", weight: "normal" };
|
626
|
+
|
627
|
+
var attr = {"font-size": font.size, 'font-style': font.style,
|
628
|
+
"font-family": font.face, 'font-weight': font.weight, 'text-decoration': font.decoration,
|
629
|
+
'class': this.addClasses(klass) };
|
630
|
+
attr.font = ""; // There is a spurious font definition that is put on all text elements. This overwrites it.
|
631
|
+
return { font: font, attr: attr };
|
632
|
+
};
|
633
|
+
|
634
|
+
ABCJS.write.Renderer.prototype.getTextSize = function(text, type, klass) {
|
635
|
+
var hash = this.getFontAndAttr(type, klass);
|
636
|
+
var el = this.paper.text(0,0, text).attr(hash.attr);
|
637
|
+
var size = el.getBBox();
|
638
|
+
el.remove();
|
639
|
+
return size;
|
640
|
+
};
|
641
|
+
|
642
|
+
ABCJS.write.Renderer.prototype.renderText = function(x, y, text, type, klass, anchor, centerVertically) {
|
643
|
+
var hash = this.getFontAndAttr(type, klass);
|
644
|
+
if (anchor)
|
645
|
+
hash.attr["text-anchor"] = anchor;
|
646
|
+
text = text.replace(/\n\n/g, "\n \n");
|
647
|
+
var el = this.paper.text(x, y, text).attr(hash.attr);
|
648
|
+
if (!centerVertically) {
|
649
|
+
// The text will be placed centered in vertical alignment, so we need to move the box down so that
|
650
|
+
// the top of the text is where we've requested.
|
651
|
+
var size = el.getBBox();
|
652
|
+
el.attr({ "y": y + size.height / 2 });
|
653
|
+
if (hash.font.box) {
|
654
|
+
this.paper.rect(size.x - 1, size.y - 1, size.width + 2, size.height + 2).attr({"stroke": "#cccccc"});
|
655
|
+
}
|
656
|
+
}
|
657
|
+
if (type === 'debugfont') {
|
658
|
+
console.log("Debug msg: " + text);
|
659
|
+
el.attr({ stroke: "#ff0000"});
|
660
|
+
}
|
661
|
+
if (this.doRegression) this.addToRegression(el);
|
662
|
+
return el;
|
663
|
+
};
|
664
|
+
|
665
|
+
ABCJS.write.Renderer.prototype.moveY = function (em, numLines) {
|
666
|
+
if (numLines === undefined) numLines = 1;
|
667
|
+
this.y += em*numLines;
|
668
|
+
};
|
669
|
+
|
670
|
+
ABCJS.write.Renderer.prototype.skipSpaceY = function () {
|
671
|
+
this.y += this.space;
|
672
|
+
};
|
673
|
+
|
674
|
+
// Call with 'kind' being the font type to use,
|
675
|
+
// if marginBottom === null then don't increment the Y after printing, otherwise that is the extra number of em's to leave below the line.
|
676
|
+
// and alignment being "start", "middle", or "end".
|
677
|
+
ABCJS.write.Renderer.prototype.outputTextIf = function(x, str, kind, klass, marginTop, marginBottom, alignment) {
|
678
|
+
if (str) {
|
679
|
+
if (marginTop)
|
680
|
+
this.moveY(marginTop);
|
681
|
+
var el = this.renderText(x, this.y, str, kind, klass, alignment);
|
682
|
+
if (marginBottom !== null) {
|
683
|
+
var numLines = str.split("\n").length;
|
684
|
+
this.moveY(el.getBBox().height/numLines, (numLines + marginBottom));
|
685
|
+
}
|
686
|
+
return [el.getBBox().width, el.getBBox().height];
|
687
|
+
}
|
688
|
+
return [0,0];
|
689
|
+
};
|
690
|
+
|
691
|
+
// For debugging, it is sometimes useful to know where you are vertically.
|
692
|
+
ABCJS.write.Renderer.prototype.printHorizontalLine = function (width, vertical, comment) {
|
693
|
+
var dy = 0.35;
|
694
|
+
var fill = "rgba(0,0,255,.4)";
|
695
|
+
var y = this.y;
|
696
|
+
if (vertical) y = vertical;
|
697
|
+
y = Math.round(y);
|
698
|
+
this.paper.text(10, y, ""+Math.round(y)).attr({"text-anchor": "start", "font-size":"18px", fill: fill, stroke: fill });
|
699
|
+
var x1 = 50;
|
700
|
+
var x2 = width;
|
701
|
+
var pathString = ABCJS.write.sprintf("M %f %f L %f %f L %f %f L %f %f z", x1, y-dy, x1+x2, y-dy,
|
702
|
+
x2, y+dy, x1, y+dy);
|
703
|
+
this.paper.path().attr({path:pathString, stroke:"none", fill:fill, 'class': this.addClasses('staff')}).toBack();
|
704
|
+
for (var i = 1; i < width/100; i++) {
|
705
|
+
pathString = ABCJS.write.sprintf("M %f %f L %f %f L %f %f L %f %f z", i*100-dy, y-5, i*100-dy, y+5,
|
706
|
+
i*100+dy, y-5, i*100+dy, y+5);
|
707
|
+
this.paper.path().attr({path:pathString, stroke:"none", fill:fill, 'class': this.addClasses('staff')}).toBack();
|
708
|
+
}
|
709
|
+
if (comment)
|
710
|
+
this.paper.text(width+70, y, comment).attr({"text-anchor": "start", "font-size":"18px", fill: fill, stroke: fill });
|
711
|
+
};
|
712
|
+
|
713
|
+
ABCJS.write.Renderer.prototype.printShadedBox = function (x, y, width, height, color, comment) {
|
714
|
+
var box = this.paper.rect(x, y, width, height).attr({fill: color, stroke: color });
|
715
|
+
if (comment)
|
716
|
+
this.paper.text(0, y+7, comment).attr({"text-anchor": "start", "font-size":"14px", fill: "rgba(0,0,255,.4)", stroke: "rgba(0,0,255,.4)" });
|
717
|
+
return box;
|
718
|
+
};
|
719
|
+
|
720
|
+
ABCJS.write.Renderer.prototype.printVerticalLine = function (x, y1, y2) {
|
721
|
+
var dy = 0.35;
|
722
|
+
var fill = "#00aaaa";
|
723
|
+
var pathString = ABCJS.write.sprintf("M %f %f L %f %f L %f %f L %f %f z", x - dy, y1, x - dy, y2,
|
724
|
+
x + dy, y1, x + dy, y2);
|
725
|
+
this.paper.path().attr({path: pathString, stroke: "none", fill: fill, 'class': this.addClasses('staff')}).toBack();
|
726
|
+
pathString = ABCJS.write.sprintf("M %f %f L %f %f L %f %f L %f %f z", x - 20, y1, x - 20, y1+3,
|
727
|
+
x, y1, x, y1+3);
|
728
|
+
this.paper.path().attr({path: pathString, stroke: "none", fill: fill, 'class': this.addClasses('staff')}).toBack();
|
729
|
+
pathString = ABCJS.write.sprintf("M %f %f L %f %f L %f %f L %f %f z", x + 20, y2, x + 20, y2+3,
|
730
|
+
x, y2, x, y2+3);
|
731
|
+
this.paper.path().attr({path: pathString, stroke: "none", fill: fill, 'class': this.addClasses('staff')}).toBack();
|
732
|
+
|
733
|
+
};
|
734
|
+
|
735
|
+
/**
|
736
|
+
* @private
|
737
|
+
*/
|
738
|
+
ABCJS.write.Renderer.prototype.addToRegression = function (el) {
|
739
|
+
var box = el.getBBox();
|
740
|
+
//var str = "("+box.x+","+box.y+")["+box.width+","+box.height+"] "
|
741
|
+
var str = el.type + ' ' + box.toString() + ' ';
|
742
|
+
var attrs = [];
|
743
|
+
for (var key in el.attrs) {
|
744
|
+
if (el.attrs.hasOwnProperty(key)) {
|
745
|
+
if (key === 'class')
|
746
|
+
str = el.attrs[key] + " " + str;
|
747
|
+
else
|
748
|
+
attrs.push(key+": "+el.attrs[key]);
|
749
|
+
}
|
750
|
+
}
|
751
|
+
attrs.sort();
|
752
|
+
str += "{ " +attrs.join(" ") + " }";
|
753
|
+
this.regressionLines.push(str);
|
754
|
+
};
|