def time_ligatures(glyphdata): """Builds recommended ligatures in Time Signatures range. Target name and numerator/denominator is determined by ligature name (underscore and ctrl character). """ for child in glyphdata: if helpers.check_excluded(child): continue components = [] shifts = [] parents = child.split('_') complete = True for i, parent in enumerate(parents): complete = helpers.check_complete(parent) if not complete: break helpers.decompose(parent) parent_index = f.FindGlyph(parent) parent_glyph = f.glyphs[parent_index] dx, dy = 0, 0 if parent in data.ctrl_char: continue # Set vertical shift values for denominator and numerator. dy = SPACE if parents[i - 1] == 'uniE09F' else SPACE * 3 if len(parents) <= 4: continue # Set horizontal shifts and spacing for ligatures # with double digit numerator. shifts.append(parent_glyph.width) if i == 3: dx = shifts[0] elif i == 5: glyph_width = sum(shifts[:2]) glyph_center = parent_glyph.width / 2 dx = glyph_width / 2 - glyph_center components.append(Component(parent_index, Point(dx, dy))) if not complete: helpers.print_incomplete(child) else: new_glyph = Glyph() for item in components: new_glyph.components.append(item) metrics = parent_glyph.GetMetrics() if len(parents) > 4: metrics = Point(glyph_width, 0) helpers.handle_replaced(child) helpers.append_glyph(new_glyph, child, metrics)
def flags(glyphdata): """Builds composite glyphs in Flags range. Additionally builds straight flags, as well as short flag and small flags (for small staff) stylistic sets. """ for child, parents in glyphdata.iteritems(): if helpers.check_excluded(child): continue components = [] complete = True for i, parent in enumerate(parents): complete = helpers.check_complete(parent) if not complete: continue helpers.decompose(parent) spacing = helpers.configvalue('Flags', 'internal flag spacing') suffix = helpers.configvalue('Set Suffixes', 'straight flags') if helpers.configvalue('Include', 'straight flags'): if parent.endswith(suffix): spacing = helpers.configvalue('Flags', 'straight flag spacing') shifts = range(-spacing, spacing * len(parents), spacing) parent_index = f.FindGlyph(parent) parent_glyph = f.glyphs[parent_index] dx, dy = 0, 0 for n, dy in enumerate(shifts): if 'uniE251' in parent or parent == 'uniE241' + '.' + suffix: dy = -dy if i == n: components.append(Component(parent_index, Point(dx, dy))) if not complete: helpers.print_incomplete(child) else: new_glyph = Glyph() for item in components: new_glyph.components.append(item) metrics = parent_glyph.GetMetrics() helpers.handle_replaced(child) helpers.append_glyph(new_glyph, child, metrics)
def stems(glyphdata): """Builds composites in Stems range. Dedicated technique components are found in Tremolos and various instrument-specific ranges. """ for child, parent in glyphdata.iteritems(): if helpers.check_excluded(child): continue complete = helpers.check_complete(parent) if not complete: if (config.getboolean('Global', 'draw missing') and parent == 'uniE210'): makers.stems(parent) complete = True elif child: helpers.print_incomplete(child) else: helpers.print_incomplete(parent) break else: helpers.decompose(parent) stem_index = f.FindGlyph(glyphdata[None]) parent_index = f.FindGlyph(parent) parent_glyph = f.glyphs[parent_index] new_glyph = Glyph() dx, dy = 0, 0 # append stem component. new_glyph.components.append(Component(stem_index, Point(dx, dy))) # Set parameters for symbols and append. dx = helpers.configvalue('Stems', 'stem thickness') / 2 dy = SPACE * 2 # Centre double sharp on left sidebearing. if parent == 'uniE263': dx -= parent_glyph.width / 2 new_glyph.components.append(Component(parent_index, Point(dx, dy))) stem_glyph = f.glyphs[stem_index] metrics = stem_glyph.GetMetrics() if child: helpers.handle_replaced(child) helpers.append_glyph(new_glyph, child, metrics)
def mirror_time(glyphdata): """Builds composites in Turned and Reversed time signatures ranges. Requires unencoded timeSigVerticalStroke component to retain cutTimeCommon as composite. """ for parent, child in glyphdata.iteritems(): if helpers.check_excluded(child): continue stroke = helpers.configvalue('Time Signatures', 'cut time stroke') complete = helpers.check_complete(parent) if complete and parent == 'uniE08B': complete = helpers.check_complete(stroke) helpers.decompose(parent) if not complete: helpers.print_incomplete(child) else: new_glyph = Glyph() parent_index = f.FindGlyph(parent) parent_glyph = f.glyphs[parent_index] # Turn. dx, dy, sx, sy = parent_glyph.width, parent_glyph.height, -1, -1 if glyphdata == data.reversed_time: # Reverse. dx, dy, sx, sy = parent_glyph.width, 0, -1, 1 new_glyph.components.append( Component(parent_index, Point(dx, dy), Point(sx, sy))) # Append component if cut time is component. if parent == 'uniE08B' and parent_glyph.components: stroke_index = f.FindGlyph(stroke) stroke_glyph = f.glyphs[stroke_index] for c in parent_glyph.components: if c.index == stroke_index: dx = (parent_glyph.width + stroke_glyph.width) * 0.5 new_glyph.components.append( Component(c.index, Point(dx, dy), Point(sx, sy))) metrics = parent_glyph.GetMetrics() helpers.handle_replaced(child) helpers.append_glyph(new_glyph, child, metrics)
def staves(glyphdata): """builds composites in Staves range. Determines baseline from number of components required. """ for parent, children in glyphdata.iteritems(): complete = helpers.check_complete(parent) if not complete and config.getboolean('Global', 'draw missing'): makers.staves(parent, children) complete = True helpers.decompose(parent) if not children: continue for child in children: if helpers.check_excluded(child) or not complete: helpers.print_incomplete(child) continue # Determine base y values for initial components # in glyphs with odd vs. even number of lines. num_of_lines = children.index(child) + 2 baseline = SPACE / 2 if num_of_lines % 2 == 0 else 0 glyph_height = SPACE * num_of_lines / 2 # Generate shift values for subsequent components. shifts = range(baseline, glyph_height, SPACE) new_glyph = Glyph() parent_index = f.FindGlyph(parent) parent_glyph = f.glyphs[parent_index] # Append components with + and - shift values to new glyph. dx, dy = 0, 0 for dy in shifts: new_glyph.components.append( Component(parent_index, Point(dx, dy))) if dy > 0: new_glyph.components.append( Component(parent_index, Point(dx, -dy))) metrics = parent_glyph.GetMetrics() helpers.handle_replaced(child) helpers.append_glyph(new_glyph, child, metrics)
def accordion_reg(glyphdata): """Builds registration composites in Accordion range. Placement schemes are defined by 'codes' derived from descriptions in SMuFL documentation. Reference values are given in comments below. """ # for parent_data, child_data in glyphdata: for parent, value in glyphdata[0].iteritems(): complete = helpers.check_complete(parent) if not complete: if not config.getboolean('Global', 'draw missing'): continue if parent == 'uniE8CA': makers.coupler_dot(parent) else: makers.ranks(parent) complete = True helpers.decompose(parent) for child, values in glyphdata[1].iteritems(): if helpers.check_excluded(child): continue components = [] parent = values[0] placement = values[1:] if not complete: helpers.print_incomplete(child) continue for i, value in enumerate(placement): # Calculates offsets for dot placement from reference values. # Reference values provided in comments. parent_index = f.FindGlyph(parent) parent_glyph = f.glyphs[parent_index] parent_bbox = parent_glyph.GetBoundingRect() dot_index = f.FindGlyph('uniE8CA') dot_glyph = f.glyphs[dot_index] dot_bbox = dot_glyph.GetBoundingRect() x, y = parent_bbox.width / 2, parent_bbox.height / 2 if value == 'stop4': y = parent_bbox.height / 1.219 # 'stop4' = top of round ranks 3 (780/640) elif value in {'upper8', 'master'}: x = parent_bbox.width / 1.3 # 'upper8'/'master' = mid right of round ranks 2/3/4 (780/600) elif value == 'lower8': x = parent_bbox.width / 4.333 # 'lower8' = mid left of round ranks 3 (780/180) elif value == 'stop16': y = parent_bbox.height / 5.571 # 'stop16' = bottom of round ranks 3 (780/140) elif value == 'soprano': y = parent_bbox.height / 1.1624 # 'soprano' = top of ranks 4 (780/671) elif value == 'alto': y = parent_bbox.height / 1.612 # 'alto' = upper mid of ranks 4 (780/484) elif value == 'tenor': y = parent_bbox.height / 2.635 # 'tenor' = lower mid of ranks 4 (780/296) elif value == 'bass': y = parent_bbox.height / 7.156 # 'bass' = bottom of ranks 4 (780/109) elif value == 'stop8b': y = parent_bbox.height / 1.352 # 'stop8b' = top of ranks 2 (780/577) elif value == 'stop16b': y = parent_bbox.height / 3.842 # 'stop16b' = bottom of ranks 2 (780/203) elif value in {'stop8c', 'left8stop', 'right8stop'}: y = parent_bbox.height / 5.555 # 'stop8c' = bottom of square ranks 3 (750/135) elif value == 'stop2': y = parent_bbox.height / 1.22 # 'stop2' = top of square ranks 3 (750/615) elif value == 'left8stop': x = parent_bbox.width / 3.079 # 'left8stop' = bottom left half of square ranks 3 (625/203) # (x = parent_bbox.width / 3 with 3% compensation <--> for # 'left8stop' and 'right8stop') elif value == 'right8stop': x = parent_bbox.width / 1.481 # 'right8stop' = bottom right half of square ranks 3 (625/422) # Adjust to dot center base and overshoot of round ranks glyphs. x -= dot_bbox.width / 2 y -= dot_bbox.height / 2 if parent != 'uniE8C9': y += helpers.configvalue('Accordion', 'round ranks overshoot') components.append(Component(dot_index, Point(x, y))) if i == 0: components.append(Component(parent_index, Point(0, 0))) new_glyph = Glyph() for item in components: new_glyph.components.append(item) metrics = parent_glyph.GetMetrics() if child: helpers.handle_replaced(child) helpers.append_glyph(new_glyph, child, metrics)
def dynamics(glyphdata): """Builds composites in Dynamics range. Uses sidebearings and any kerning pairs to space components. A global spacing setting for entire range is also available in config. """ for child, parents in glyphdata.iteritems(): if helpers.check_excluded(child): continue complete = True parent_widths = [] components = [] for i, parent in enumerate(parents): complete = helpers.check_complete(parent) if not complete: if not config.getboolean('Global', 'draw missing'): helpers.print_incomplete(parent) break elif parent in {'uniE53E', 'uniE541'}: makers.dynamics(parent) complete = True else: helpers.print_incomplete(child) break helpers.decompose(parent) parent_index = f.FindGlyph(parent) parent_glyph = f.glyphs[parent_index] width = parent_glyph.width dx = dy = 0 # Define shifts for letters based on width, spacing and kerning. if 'uniE53E' not in parents: spacing = helpers.configvalue('Dynamics', 'component spacing') left = parents[i - 1] right = parents[i] # Exclude kerning for leftmost component kerning = 0 if i == 0 else helpers.get_kerning(left, right) parent_widths.append(width + spacing + kerning) dx = sum(parent_widths[:-1]) + kerning # Exclude spacing for rightmost component width = sum(parent_widths) - spacing components.append(Component(parent_index, Point(dx, dy))) elif child == 'uniE53F': dx, sx, sy = width, -1, 1 components.append( Component(parent_index, Point(dx, dy), Point(sx, sy))) elif i == 0: components.append(Component(parent_index, Point(dx, dy))) else: spacing = helpers.configvalue('Dynamics', 'hairpin spacing') width = width * 2 + spacing dx, sx, sy = width, -1, 1 components.append( Component(parent_index, Point(dx, dy), Point(sx, sy))) if child and complete: new_glyph = Glyph() for item in components: new_glyph.components.append(item) metrics = Point(width, 0) helpers.handle_replaced(child) helpers.append_glyph(new_glyph, child, metrics)
def octaves(glyphdata): """Builds composite glyphs in Octaves and Octaves supplement ranges. Requires additional (unencoded) letters to build 'loco' and 'bassa' glyphs. """ for child, parents in glyphdata.iteritems(): if helpers.check_excluded(child): continue complete = True parent_widths = [] components = [] for i, parent in enumerate(parents): # Define names of unencoded letters according to spec. if parent == 'octaveC': parent = helpers.configvalue('Octaves', 'c') elif parent == 'octaveL': parent = helpers.configvalue('Octaves', 'l') elif parent == 'octaveO': parent = helpers.configvalue('Octaves', 'o') elif parent == 'octaveS': parent = helpers.configvalue('Octaves', 's') complete = helpers.check_complete(parent) if not complete: break helpers.decompose(parent) # Define shift, spacing and kerning for fraction glyphs acc. to spec. spacing = helpers.configvalue('Octaves', 'component spacing') parent_index = f.FindGlyph(parent) parent_glyph = f.glyphs[parent_index] # Define horizontal shifts. dx = dy = 0 parent_widths.append(parent_glyph.width + spacing) dx = sum(parent_widths[:-1]) new_width = sum(parent_widths) # Define vertical shift for superscript. number_bbox = helpers.get_bbox('uniE510') letter_bbox = helpers.get_bbox('uniEC91') super_height = (number_bbox.ur.y - letter_bbox.ur.y + number_bbox.ll.y + helpers.configvalue( 'Octaves', 'superscript height adjustment')) super_kern = helpers.configvalue('Octaves', 'superscript kern') if child in { 'uniE511', 'uniE515', 'uniE518', 'uniEC92', 'uniEC94', 'uniEC96', 'uniEC98' }: if parent in {'uniEC91', 'uniEC93', 'uniEC95', 'uniEC97'}: dx += super_kern dy = super_height new_width += super_kern components.append(Component(parent_index, Point(dx, dy))) if complete: new_glyph = Glyph() for item in components: new_glyph.components.append(item) metrics = Point(new_width, 0) helpers.handle_replaced(child) helpers.append_glyph(new_glyph, child, metrics) else: helpers.print_incomplete(child)
def tremolos(glyphdata): """Builds slash and separation dot composites in Tremolos range. Determines baseline of slash composites based on number of components required, and builds from there. Special spcing parameters are used for tremoloDivisiDots6 to split dots into to two rows of three. Drawing of parents is not yet implemented in [makers]. """ for parent, children in glyphdata.iteritems(): complete = helpers.check_complete(parent) helpers.decompose(parent) if not children: continue for child in children: if helpers.check_excluded(child): continue if not complete: helpers.print_incomplete(child) continue parent_index = f.FindGlyph(parent) parent_glyph = f.glyphs[parent_index] bbox = parent_glyph.GetBoundingRect() dot_spacing = helpers.configvalue('Tremolos', 'divisi dot spacing') num_of_comps = children.index(child) + 2 # Define base values for divisi dots. if parent == 'uniE4A2': baseline = 0 separation = bbox.height + dot_spacing glyph_span = separation * num_of_comps # Set up tremoloDivisiDots6 for double two rows of 3 dots. if child == 'uniE231': num_of_comps += -2 # Define base y values for initial comps with # odd/even number of trem slashes. else: baseline = bbox.height / 2 if num_of_comps % 2 == 0 else 0 spacing = helpers.configvalue('Tremolos', 'tremolo slash spacing') separation = bbox.height + spacing glyph_span = separation * num_of_comps / 2 if parent == 'uniE225': spacing = helpers.configvalue('Tremolos', 'fingered tremolo spacing') separation = bbox.height + spacing shifts = range(baseline, glyph_span, separation) new_glyph = Glyph() for shift in shifts: # Append trem slash comps with + and - shift values. if parent != 'uniE4A2': dx, dy = 0, shift new_glyph.components.append( Component(parent_index, Point(dx, dy))) if shift > 0: new_glyph.components.append( Component(parent_index, Point(dx, -dy))) metrics = parent_glyph.GetMetrics() # Append divisi dot components with 2x3 for 'uniE231'. else: dx, dy = shift, 0 if child != 'uniE231': new_glyph.components.append( Component(parent_index, Point(dx, dy))) else: new_glyph.components.append( Component(parent_index, Point(dx, dy))) dy = dot_spacing + bbox.height new_glyph.components.append( Component(parent_index, Point(dx, dy))) metrics = Point(glyph_span - dot_spacing, 0) helpers.handle_replaced(child) helpers.append_glyph(new_glyph, child, metrics)
def beamed_notes(glyphdata): """Builds composites in Beamed groups of notes range. Scales number in Tuplets range to 70%. Mirrors tuplet bracket. """ for child, parents in glyphdata.iteritems(): if helpers.check_excluded(child): continue components = [] complete = True for i, parent in enumerate(parents): if not parent: continue complete = helpers.check_complete(parent) if not complete: if not config.getboolean('Global', 'draw missing'): break elif parent in { 'uniE204', 'uniE205', 'uniE1E7', 'uniE1F7', 'uniE1FE' }: # Draw/append stem according to spec. if parent in {'uniE204', 'uniE205'}: makers.stems(parent) elif parent == 'uniE1E7': makers.augmentation_dot(parent) elif parent == 'uniE1F7': makers.note_beam(parent) elif parent == 'uniE1FE': makers.tuplet_bracket(parent) complete = True if not complete: continue helpers.decompose(parent) # Initialize components at origin and 100% scale. parent_index = f.FindGlyph(parent) parent_glyph = f.glyphs[parent_index] parent_bbox = parent_glyph.GetBoundingRect() dx, dy, sx, sy = 0, 0, 1, 1 # Scale tuplet nums to 72% and move in line # with parent bracket and move tall version up. long_stem_length = helpers.configvalue('Stems', 'long stem length') short_stem_length = helpers.configvalue('Stems', 'short stem length') tuplet_height = helpers.configvalue('Beams', 'tuplet height') hook_length = helpers.configvalue('Beams', 'tuplet bracket hook length') diff = long_stem_length - short_stem_length if child in {'uniE1FF', 'uniE202'}: bbox_center = parent_bbox.height / 2 sx = sy = 0.72 dy = tuplet_height + hook_length - (bbox_center * 0.72) # Reverse tuplet brackets horizontally. elif child in {'uniE200', 'uniE203'}: dx, sx, sy = parent_glyph.width, -1, 1 # Move bracket and number for long stem # up according to separation. if child in {'uniE201', 'uniE202', 'uniE203'}: dy += diff # Define parameters for beams. if parent == 'uniE1F7': beam_index = f.FindGlyph(parent) beam_glyph = f.glyphs[beam_index] beam_thickness = helpers.configvalue('Beams', 'beam thickness') beam_spacing = helpers.configvalue('Beams', 'beam spacing') separation = beam_thickness + beam_spacing # Set vertical shift for long short stem # and different number of beams. if 'uniE205' in parents or child in { 'uniE1F8', 'uniE1FA', 'uniE1FB' }: if parents.count('uniE1F7') == 1: dy = separation elif parents.count('uniE1F7') == 3: dy = -separation elif parents.count('uniE1F7') == 2: dy = -separation # Generate shifts and append to list. number_of_beams = parents.count('uniE1F7') shifts = range(dy, dy + separation * number_of_beams, separation) for n, dy in enumerate(shifts): if i - 2 == n: components.append(Component(beam_index, Point(dx, dy))) # Define parameters for beamed notes and append to list. elif ('uniE1F7' in parents and 'uniE204' in parents or 'uniE1F7' in parents and 'uniE205' in parents): diff = beam_glyph.width - parent_glyph.width dx = diff components.append(Component(parent_index, Point(dx, dy))) # Append the rest. else: components.append( Component(parent_index, Point(dx, dy), Point(sx, sy))) if not complete: helpers.print_incomplete(child) else: new_glyph = Glyph() for item in components: new_glyph.components.append(item) helpers.handle_replaced(child) metrics = parent_glyph.GetMetrics() if 'uniE883' in parents: metrics = Point(parent_bbox.width * 0.72, 0) helpers.append_glyph(new_glyph, child, metrics)
def indv_notes(glyphdata): """Builds composites in Individual notes range. Duplicates functionality of flags() which is not ideal, but difficult to avoid when parameters are different. """ for child, parents in glyphdata.iteritems(): if helpers.check_excluded(child): continue components = [] complete = True for i, parent in enumerate(parents): long_stem_length = helpers.configvalue('Stems', 'long stem length') complete = helpers.check_complete(parent) if not complete: if not config.getboolean('Global', 'draw missing'): break if parent not in {'uniE210', 'uniE1E7'}: continue # Draw/append stem according to spec. elif parent == 'uniE210': makers.stems(parent) # Draw/append augmentation dot according to spec. else: makers.augmentation_dot(parent) complete = True if not complete: continue helpers.decompose(parent) # Define parameters for notehead and append to list. if parent in { 'uniE0A0', 'uniE0A1', 'uniE0A2', 'uniE0A3', 'uniE0A4' }: note_index = f.FindGlyph(parent) note_glyph = f.glyphs[note_index] metrics = Point(note_glyph.width, 0) dx, dy = 0, 0 components.append(Component(note_index, Point(dx, dy))) # Define parameters for stem. elif parent == 'uniE210': note_bbox = note_glyph.GetBoundingRect() stem_index = f.FindGlyph('uniE210') stem_glyph = f.glyphs[stem_index] stem_bbox = stem_glyph.GetBoundingRect() dx, dy = note_glyph.width - stem_glyph.width, 0 # Handle downstem notes and append to list. if child in { 'uniE1D4', 'uniE1D6', 'uniE1D8', 'uniE1DA', 'uniE1DC', 'uniE1DE', 'uniE1E0', 'uniE1E2', 'uniE1E4', 'uniE1E6' }: dx, dy = stem_glyph.width, -long_stem_length components.append(Component(stem_index, Point(dx, dy))) # Define parameters for flags. elif parent in { 'uniE240', 'uniE241', 'uniE250', 'uniE242', 'uniE243', 'uniE251' }: flag_spacing = helpers.configvalue('Flags', 'internal flag spacing') flag_index = f.FindGlyph(parent) flag_glyph = f.glyphs[flag_index] glyph_width = (note_glyph.width + flag_glyph.width - stem_glyph.width * 2) metrics = Point(glyph_width, 0) dx, dy = (note_glyph.width - stem_glyph.width * 2, long_stem_length - flag_spacing * 2) shifts = range(dy, dy + flag_spacing * len(parents), flag_spacing) # Generate vertical shifts and append to list. for n, dy in enumerate(shifts): if parent in {'uniE241', 'uniE243', 'uniE251'}: dx, dy = 0, -dy metrics = Point(note_glyph.width, 0) if i == n: components.append(Component(flag_index, Point(dx, dy))) if not complete: helpers.print_incomplete(child) elif child: new_glyph = Glyph() for item in components: new_glyph.components.append(item) helpers.handle_replaced(child) helpers.append_glyph(new_glyph, child, metrics)
def fraction_time(glyphdata): """Builds Time signature fraction composites. Scales regular numerals to 50%. Fraction slash is kept at 100%, according to Bravura scaling factor. Option to build from dedicated numerals should perhaps be implemented. """ for child, parents in glyphdata.iteritems(): if helpers.check_excluded(child): continue parent_widths = [] new_glyph = Glyph() for i, parent in enumerate(parents): complete = helpers.check_complete(parent) if not complete: break helpers.decompose(parent) # Define scale, shift, spacing and # kerning for fraction glyphs acc. to spec. sidebearings = helpers.configvalue('Time Signatures', 'fraction sidebearings') spacing = helpers.configvalue('Time Signatures', 'fraction spacing') one_kern = helpers.configvalue('Time Signatures', 'fraction one kern') four_kern = helpers.configvalue('Time Signatures', 'fraction four kern') parent_index = f.FindGlyph(parent) parent_glyph = f.glyphs[parent_index] parent_width = parent_glyph.width sx = sy = 1 # 1 = 100% (x, y) dx = dy = 0 num_factor = 0.5 # num_factor reflects current scaling factor # of slash vs. numerals in Bravura. if i == 1: dx = parent_widths[i - 1] - parent_width / 2 + spacing else: parent_width = parent_glyph.width * num_factor + sidebearings sx, sy = sx * num_factor, sy * num_factor if i == 0: dx, dy = sidebearings, SPACE / 2 if parent == 'uniE081': parent_width += one_kern else: dx, dy = parent_widths[0] + spacing * 2, -SPACE / 2 if parent == 'uniE084': dx += four_kern parent_width += four_kern parent_widths.append(parent_width) new_glyph.components.append( Component(parent_index, Point(dx, dy), Point(sx, sy))) if not complete: helpers.print_incomplete(child) else: new_width = parent_widths[0] + parent_widths[2] + spacing * 2 metrics = Point(new_width, 0) helpers.handle_replaced(child) helpers.append_glyph(new_glyph, child, metrics)