import Vex from "vexflow" ; import { VexFlowBackend } from "./VexFlowBackend" ; import { VexFlowConverter } from "./VexFlowConverter" ; import { FontStyles } from "../../../Common/Enums/FontStyles" ; import { Fonts } from "../../../Common/Enums/Fonts" ; import { RectangleF2D } from "../../../Common/DataObjects/RectangleF2D" ; import { PointF2D } from "../../../Common/DataObjects/PointF2D" ; import { BackendType } from "../../../OpenSheetMusicDisplay/OSMDOptions" ; import { EngravingRules } from "../EngravingRules" ; import log from "loglevel" ; export class SvgVexFlowBackend extends VexFlowBackend { private ctx : Vex . Flow . SVGContext ; public zoom : number ; constructor ( rules ) { super ( ) ; this . rules = rules ; } public getVexflowBackendType ( ) { return Vex . Flow . Renderer . Backends . SVG ; } public getOSMDBackendType ( ) { return BackendType . SVG ; } public getCanvasSize ( ) { return document . getElementById ( "osmdCanvasPage" + this . graphicalMusicPage . PageNumber ) ?. offsetHeight ; } public initialize ( container , zoom ) { this . zoom = zoom ; this . canvas = document . createElement ( "div" ) ; this . canvas . id = "osmdCanvasPage" + this . graphicalMusicPage . PageNumber ; this . inner = this . canvas ; this . inner . style . position = "relative" ; this . canvas . style . zIndex = "0" ; container . appendChild ( this . inner ) ; this . renderer = new Vex . Flow . Renderer ( this . canvas , this . getVexflowBackendType ( ) ) ; this . ctx = < Vex . Flow . SVGContext > this . renderer . getContext ( ) ; this . ctx . svg . id = "osmdSvgPage" + this . graphicalMusicPage . PageNumber ; } public getContext ( ) { return this . ctx ; } public getSvgElement ( ) { return this . ctx . svg ; } removeNode ( node ) { const svg = this . ctx ?. svg ; if ( ! svg ) { return false ; } try { svg . removeChild ( node ) ; } catch ( ex ) { log . error ( ex ) ; return false ; } return true ; } public clear ( ) { if ( ! this . ctx ) { return ; } const svg = this . ctx . svg ; while ( svg . lastChild ) { svg . removeChild ( svg . lastChild ) ; } if ( this . rules . PageBackgroundColor ) { this . ctx . svg . style [ "background-color" ] = this . rules . PageBackgroundColor ; } } public scale ( k ) { this . ctx . scale ( k , k ) ; } public translate ( x , y ) { } public renderText ( fontHeight , fontStyle , font , text , heightInPixel , screenPosition , color = undefined , fontFamily = undefined ) { this . ctx . save ( ) ; const node = this . ctx . openGroup ( "text" ) ; if ( color ) { this . ctx . attributes . fill = color ; this . ctx . attributes . stroke = color ; } let fontFamilyVexFlow = fontFamily ; if ( ! fontFamily || fontFamily === "default" ) { fontFamilyVexFlow = this . rules . DefaultFontFamily ; } this . ctx . setFont ( fontFamilyVexFlow , fontHeight , VexFlowConverter . fontStyle ( fontStyle ) ) ; this . ctx . attributes [ "font-size" ] = `${ fontHeight }px` ; this . ctx . state [ "font-size" ] = `${ fontHeight }px` ; let fontWeightVexflow = "normal" ; let fontStyleVexflow = "normal" ; switch ( fontStyle ) { case FontStyles . Bold : fontWeightVexflow = "bold" ; break ; case FontStyles . Italic : fontStyleVexflow = "italic" ; break ; case FontStyles . BoldItalic : fontWeightVexflow = "bold" ; fontStyleVexflow = "italic" ; break ; default : fontWeightVexflow = "normal" ; } this . ctx . attributes [ "font-weight" ] = fontWeightVexflow ; this . ctx . state [ "font-weight" ] = fontWeightVexflow ; this . ctx . attributes [ "font-style" ] = fontStyleVexflow ; this . ctx . state [ "font-style" ] = fontStyleVexflow ; this . ctx . fillText ( text , screenPosition . x , screenPosition . y + heightInPixel ) ; this . ctx . closeGroup ( ) ; this . ctx . restore ( ) ; return node ; } public renderRectangle ( rectangle , styleId , colorHex , alpha = 1 ) { this . ctx . save ( ) ; const node = this . ctx . openGroup ( "rect" ) ; if ( colorHex ) { this . ctx . attributes . fill = colorHex ; } else { this . ctx . attributes . fill = VexFlowConverter . style ( styleId ) ; } this . ctx . attributes [ "fill-opacity" ] = alpha ; this . ctx . fillRect ( rectangle . x , rectangle . y , rectangle . width , rectangle . height ) ; this . ctx . restore ( ) ; this . ctx . attributes [ "fill-opacity" ] = 1 ; this . ctx . closeGroup ( ) ; return node ; } public renderLine ( start , stop , color = "#FF0000FF" , lineWidth = 2 ) { this . ctx . save ( ) ; const node = this . ctx . openGroup ( "line" ) ; this . ctx . beginPath ( ) ; this . ctx . moveTo ( start . x , start . y ) ; this . ctx . lineTo ( stop . x , stop . y ) ; this . ctx . attributes . stroke = color ; this . ctx . lineWidth = lineWidth ; this . ctx . stroke ( ) ; this . ctx . closeGroup ( ) ; this . ctx . restore ( ) ; return node ; } public renderCurve ( points ) { const node = this . ctx . openGroup ( "curve" ) ; this . ctx . beginPath ( ) ; this . ctx . moveTo ( points [ 0 ] . x , points [ 0 ] . y ) ; this . ctx . bezierCurveTo ( points [ 1 ] . x , points [ 1 ] . y , points [ 2 ] . x , points [ 2 ] . y , points [ 3 ] . x , points [ 3 ] . y ) ; this . ctx . lineTo ( points [ 7 ] . x , points [ 7 ] . y ) ; this . ctx . bezierCurveTo ( points [ 6 ] . x , points [ 6 ] . y , points [ 5 ] . x , points [ 5 ] . y , points [ 4 ] . x , points [ 4 ] . y ) ; this . ctx . lineTo ( points [ 0 ] . x , points [ 0 ] . y ) ; this . ctx . closePath ( ) ; this . ctx . fill ( ) ; this . ctx . closeGroup ( ) ; return node ; } public export ( ) { const clone = ( this . ctx . svg . cloneNode ( true ) as SVGElement ) ; const svgDocType = document . implementation . createDocumentType ( "svg" , "-//W3C//DTD SVG 1.1//EN" , "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd" ) ; const svgDoc = document . implementation . createDocument ( "http://www.w3.org/2000/svg" , "svg" , svgDocType ) ; svgDoc . replaceChild ( clone , svgDoc . documentElement ) ; const svgData = ( new XMLSerializer ( ) ) . serializeToString ( svgDoc ) ; const a = document . createElement ( "a" ) ; a . href = "data:image/svg+xml; charset=utf8, " + encodeURIComponent ( svgData . replace ( /></g , ">\n\r<" ) ) ; a . download = "opensheetmusicdisplay_download.svg" ; a . innerHTML = window . location . href + "/download" ; document . body . appendChild ( a ) ; } }