//----------------------------------------------------------------------------// // // // T e x t R o l e // // // //----------------------------------------------------------------------------// // <editor-fold defaultstate="collapsed" desc="hdr"> // // Copyright © Hervé Bitteur and others 2000-2013. All rights reserved. // // This software is released under the GNU General Public License. // // Goto http://kenai.com/projects/audiveris to report bugs or suggestions. // //----------------------------------------------------------------------------// // </editor-fold> package omr.text; import omr.constant.ConstantSet; import omr.glyph.facets.Glyph; import omr.score.entity.ScoreSystem; import omr.score.entity.Staff; import omr.score.entity.SystemNode.StaffPosition; import omr.score.entity.SystemPart; import omr.score.entity.Text; import omr.sheet.Scale; import omr.sheet.Sheet; import omr.sheet.SystemInfo; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import java.awt.Point; import java.awt.Rectangle; /** * Class {@code TextRole} describes the role of a piece of text * (typically a sentence). * * @author Hervé Bitteur */ public enum TextRole { /** No role known. */ UnknownRole, /** (Part of) lyrics. */ Lyrics, /** Title of the opus. */ Title, /** Playing instruction. */ Direction, /** Number for this opus. */ Number, /** Name for the part. */ Name, /** A creator (composer, etc...). */ Creator, /** Copyright notice. */ Rights, /** Chord mark. */ Chord; // /** Specific application parameters. */ private static final Constants constants = new Constants(); /** Usual logger utility. */ private static final Logger logger = LoggerFactory.getLogger( TextRole.class); // //-----------------// // getStringHolder // //-----------------// /** * Forge a string to be used in lieu of real text value. * * @param NbOfChars the number of characters desired * * @return a dummy string of NbOfChars chars */ public String getStringHolder (int NbOfChars) { if (NbOfChars > 1000) { logger.warn("Abnormal text length:{}", NbOfChars); return "<<" + Integer.toString(NbOfChars) + ">>"; } else { StringBuilder sb = new StringBuilder("["); while (sb.length() < (NbOfChars - 1)) { sb.append(toString()) .append("-"); } return sb.substring(0, Math.max(NbOfChars - 1, 0)) + "]"; } } //-----------// // guessRole // //-----------// /** * Try to infer the role of this textual item. * For the time being, this is a simple algorithm based on sentence location * within the page, augmented by valid chord name, etc. * * @param line the sentence * @param systemInfo the containing system * @return the role information inferred for the provided sentence glyph */ public static TextRoleInfo guessRole (TextLine line, SystemInfo systemInfo) { if (line == null) { return null; } if (line.isVip()) { logger.info("TextRoleInfo. guessRole for {}", line.getValue()); } int chordCount = 0; for (TextWord word : line.getWords()) { // At least one word/glyph with a role manually assigned Glyph glyph = word.getGlyph(); if (glyph != null) { if (glyph.getManualRole() != null) { return glyph.getManualRole(); } } // Word that could be a chord symbol? if (word.guessChordInfo() != null) { chordCount++; } } // Is line made entirely of potential chord symbols? boolean isAllChord = chordCount == line.getWords() .size(); Rectangle box = line.getBounds(); if (box == null) { return null; } // Is line mainly in italic? boolean isMainlyItalic = systemInfo.getTextBuilder() .isMainlyItalic(line); Sheet sheet = systemInfo.getSheet(); ScoreSystem system = systemInfo.getScoreSystem(); Scale scale = system.getScale(); Point left = new Point(box.x, box.y + (box.height / 2)); Point right = new Point( box.x + box.width, box.y + (box.height / 2)); // First system in page? boolean firstSystem = system.getId() == 1; // Last system in page? boolean lastSystem = sheet.getSystems() .size() == system.getId(); // Vertical position wrt (system) staves StaffPosition systemPosition = system.getStaffPosition(left); // Vertical position wrt (part) staves SystemPart part = system.getPartAbove(left); StaffPosition partPosition = part.getStaffPosition(left); // Vertical distance from staff? Staff staff = system.getStaffAt(left); int staffDy = Math.abs(staff.getTopLeft().y - box.y); boolean closeToStaff = staffDy <= scale.toPixels(constants.maxStaffDy); // Begins on left side of the part? boolean leftOfStaves = system.isLeftOfStaves(left); // At the center of page width? int maxCenterDx = scale.toPixels(constants.maxCenterDx); int pageCenter = sheet.getWidth() / 2; boolean pageCentered = Math.abs((box.x + (box.width / 2)) - pageCenter) <= maxCenterDx; // Right aligned with staves? int maxRightDx = scale.toPixels(constants.maxRightDx); boolean rightAligned = Math.abs( right.x - system.getTopLeft().x - system.getDimension().width) <= maxRightDx; // Short Sentence? int maxShortLength = scale.toPixels(constants.maxShortLength); boolean shortSentence = box.width <= maxShortLength; // Tiny Sentence? int maxTinyLength = scale.toPixels(constants.maxTinyLength); boolean tinySentence = box.width <= maxTinyLength; // High text? int minTitleHeight = scale.toPixels(constants.minTitleHeight); boolean highText = box.height >= minTitleHeight; logger.debug( "{} firstSystem={} lastSystem={} systemPosition={}" + " partPosition={} closeToStaff={} leftOfStaves={}" + " pageCentered={} rightAligned={} shortSentence={}" + " highText={10}", box, firstSystem, lastSystem, systemPosition, partPosition, closeToStaff, leftOfStaves, pageCentered, rightAligned, shortSentence, highText); // Decisions ... switch (systemPosition) { case ABOVE_STAVES: // Title, Number, Creator, Direction, Chord if (tinySentence) { if (isAllChord) { return new TextRoleInfo(TextRole.Chord); } else { return new TextRoleInfo(TextRole.UnknownRole); } } if (firstSystem) { if (leftOfStaves) { return new TextRoleInfo( TextRole.Creator, Text.CreatorText.CreatorType.lyricist); } else if (rightAligned) { return new TextRoleInfo( TextRole.Creator, Text.CreatorText.CreatorType.composer); } else if (closeToStaff) { if (isAllChord) { return new TextRoleInfo(TextRole.Chord); } else { return new TextRoleInfo(TextRole.Direction); } } else if (pageCentered) { // Title, Number if (highText) { return new TextRoleInfo(TextRole.Title); } else { return new TextRoleInfo(TextRole.Number); } } } else { if (isAllChord) { return new TextRoleInfo(TextRole.Chord); } else { return new TextRoleInfo(TextRole.Direction); } } break; case WITHIN_STAVES: // Name, Lyrics, Direction if (leftOfStaves) { return new TextRoleInfo(TextRole.Name); } else if ((partPosition == StaffPosition.BELOW_STAVES) && !isMainlyItalic) { return new TextRoleInfo(TextRole.Lyrics); } else { return new TextRoleInfo(TextRole.Direction); } case BELOW_STAVES: // Copyright, Lyrics for single-staff part if (tinySentence) { return new TextRoleInfo(TextRole.UnknownRole); } if (pageCentered && shortSentence && lastSystem) { return new TextRoleInfo(TextRole.Rights); } if (part.getStaves() .size() == 1) { if ((partPosition == StaffPosition.BELOW_STAVES) && !isMainlyItalic) { return new TextRoleInfo(TextRole.Lyrics); } } } // Default return new TextRoleInfo(TextRole.UnknownRole); } //-----------// // Constants // //-----------// private static final class Constants extends ConstantSet { Scale.Fraction maxRightDx = new Scale.Fraction( 2, "Maximum horizontal distance on the right end of the staff"); Scale.Fraction maxCenterDx = new Scale.Fraction( 30, "Maximum horizontal distance around center of page"); Scale.Fraction maxShortLength = new Scale.Fraction( 35, "Maximum length for a short sentence (no lyrics)"); Scale.Fraction maxTinyLength = new Scale.Fraction( 2, "Maximum length for a tiny sentence (no lyrics)"); Scale.Fraction maxStaffDy = new Scale.Fraction( 7, "Maximum distance above staff for a direction"); Scale.Fraction minTitleHeight = new Scale.Fraction( 3, "Minimum height for a title text"); } }