Spaces:
Sleeping
Sleeping
import {svgToElements} from "./svgParser"; | |
import tokenizeElements, {StaffAttributes, HashTable} from "./tokenizeElements"; | |
import organizeTokens from "./organizeTokens"; | |
import LogRecorder from "../logRecorder"; | |
import StaffToken from "./staffToken"; | |
import SheetDocument from "./sheetDocument"; | |
import * as StaffNotation from "./staffNotation"; | |
import TextSource from "../textSource"; | |
import * as domUtils from "../domUtils"; | |
import LilyDocument from "../lilyParser/lilyDocument"; | |
import {LilyDocumentAttribute, LilyDocumentAttributeReadOnly} from "../lilyParser/lilyDocument"; | |
import {docLocationSet} from "../lilyParser"; | |
import {Scheme, SchemePair} from "../lilyParser/lilyTerms"; | |
import {SheetPage, SheetSystem, SheetStaff, SheetMeasure} from "./sheetDocument"; | |
import * as glyph from "./glyph"; | |
interface SvgPageParserOptions { | |
lilyDocument?: LilyDocument; | |
logger?: LogRecorder; | |
attributes?: StaffAttributes; | |
tieLocations?: Set<string>; | |
briefChordLocations?: Set<string>; | |
lyricLocations?: Set<string>; | |
DOMParser?: any; | |
}; | |
const parseSvgPage = (dom, source: string | TextSource, { | |
lilyDocument, | |
logger = new LogRecorder(), | |
attributes, | |
tieLocations, | |
briefChordLocations, | |
lyricLocations, | |
...options | |
}: SvgPageParserOptions = {}) => { | |
if (!(source instanceof TextSource)) | |
source = new TextSource(source); | |
const elem = svgToElements(dom, {logger, ...options}); | |
logger.append("parseSvgPage.elem", elem); | |
if (!elem) | |
return {structure: null, hashTable: {}}; | |
if (!attributes && lilyDocument) | |
attributes = lilyDocument.globalAttributes({readonly: true}) as LilyDocumentAttributeReadOnly; | |
const {tokens, hashTable} = tokenizeElements(elem.children, attributes, logger); | |
const [x, y, width, height] = elem.viewBox.match(/[\d-.]+/g).map(Number); | |
const viewBox = {x, y, width, height}; | |
const lyricLines = lyricLocations && new Set([...lyricLocations].map(loc => Number(loc.split(":")[0]))); | |
// mark tie & brief chord symbol on tokens | |
tokens.forEach((token, index) => { | |
token.index = index; | |
if (token.sourcePosition) { | |
const {line, start} = token.sourcePosition; | |
const loc = `${line}:${start}`; | |
if (briefChordLocations && briefChordLocations.has(loc)) | |
token.addSymbol("CHORD_TEXT"); | |
if (lyricLocations && lyricLines.has(line)) | |
token.addSymbol("LYRIC_TEXT"); | |
if (tieLocations && tieLocations.has(loc)) | |
token.tied = true; | |
} | |
}); | |
return { | |
structure: organizeTokens(tokens, source, {logger, viewBox, width: elem.width, height: elem.height}), | |
hashTable, | |
}; | |
}; | |
const createSheetDocumentFromSvgs = (svgs: string[], ly: string, lilyDocument: LilyDocument, {logger, DOMParser}: {logger?: LogRecorder, DOMParser?: any} = {}): { | |
doc: SheetDocument, | |
hashTable: {[key: string]: object}, | |
} => { | |
const attributes = lilyDocument.globalAttributes({readonly: true}) as LilyDocumentAttributeReadOnly; | |
const source = new TextSource(ly); | |
const tieLocations = docLocationSet(lilyDocument.getTiedNoteLocations2()); | |
//logger.append("tieLocations:", Object.keys(tieLocations)); | |
const briefChordLocations = docLocationSet(lilyDocument.getBriefChordLocations()); | |
const lyricLocations = docLocationSet(lilyDocument.getLyricLocations()); | |
const pages = svgs.map(svg => parseSvgPage(svg, source, {DOMParser, logger, attributes, tieLocations, briefChordLocations, lyricLocations})); | |
const doc = new SheetDocument({ | |
pages: pages.map(page => page.structure), | |
}); | |
const hashTable = pages.reduce((sum, page) => ({...sum, ...page.hashTable}), {}); | |
postProcessSheetDocument(doc, lilyDocument); | |
return { | |
doc, | |
hashTable, | |
}; | |
}; | |
const postProcessSheetDocument = (sheet: SheetDocument, lilyDocument: LilyDocument) => { | |
// align token id | |
const interpreter = lilyDocument.interpret(); | |
const lilyNotation = interpreter.getNotation(); | |
if (lilyNotation) | |
sheet.alignTokensWithNotation(lilyNotation); | |
const attributes = lilyDocument.globalAttributes({readonly: true}) as LilyDocumentAttributeReadOnly; | |
const schemeOption = scm => scm.findAll(SchemePair).reduce((table, item) => (table[item.left] = item.right, table), {}); | |
const verticalCrop = attributes["LotusOption.verticalCrop"] as Scheme; | |
if (verticalCrop && verticalCrop.exp){ | |
const options = schemeOption(verticalCrop); | |
//console.debug("options:", options); | |
sheet.fitPageViewbox({verticalCropOnly: true, ...options}); | |
} | |
const fitPageViewbox = attributes["LotusOption.fitPageViewbox"] as Scheme; | |
if (fitPageViewbox && fitPageViewbox.exp) | |
sheet.fitPageViewbox(schemeOption(fitPageViewbox)); | |
}; | |
const turnRawSvgWithSheetDocument = (svgText: string, page: SheetPage, {DOMParser, XMLSerializer}): string => { | |
const dom = new DOMParser().parseFromString(svgText, "text/xml"); | |
const svg: any = dom.childNodes[0]; | |
svg.setAttribute("width", page.width); | |
svg.setAttribute("height", page.height); | |
svg.setAttribute("viewBox", `${page.viewBox.x} ${page.viewBox.y} ${page.viewBox.width} ${page.viewBox.height}`); | |
const ids = page.systems.reduce((ids, system) => { | |
system.staves.forEach(staff => staff.measures.forEach(measure => | |
ids.push(...measure.tokens.filter(token => token.is("NOTEHEAD")).map(token => token.href.replace(/:\d+$/, ""))))); | |
return ids; | |
}, []); | |
domUtils.traverse(dom, node => { | |
switch (node.tagName) { | |
case "a": | |
const capture = node.getAttribute("xlink:href").match(/\d+:\d+:\d+$/); | |
const id = capture && capture[0].replace(/:\d+$/, ""); | |
if (id && ids.includes(id)) | |
node.setAttribute("style", "color:transparent;"); | |
break; | |
case "text": | |
// remove lilypond engraving signature | |
if (/www\.lilypond\.org/.test(node.textContent)) | |
node.parentNode.removeChild(node); | |
break; | |
} | |
}); | |
return new XMLSerializer().serializeToString(dom); | |
}; | |
export { | |
svgToElements, | |
tokenizeElements, | |
organizeTokens, | |
parseSvgPage, | |
postProcessSheetDocument, | |
turnRawSvgWithSheetDocument, | |
createSheetDocumentFromSvgs, | |
StaffToken, | |
SheetDocument, | |
SheetPage, | |
SheetSystem, | |
SheetStaff, | |
SheetMeasure, | |
StaffNotation, | |
StaffAttributes, | |
HashTable, | |
LilyDocumentAttribute, | |
LilyDocumentAttributeReadOnly, | |
glyph, | |
}; | |