svg-tree 0.5.1.2 → 0.6
raw patch · 9 files changed
+3987/−3787 lines, 9 filesPVP ok
version bump matches the API change (PVP)
API changes (from Hackage documentation)
+ Graphics.Svg.Types: ElementMeshGradient :: MeshGradient -> Element
+ Graphics.Svg.Types: GClose :: GradientPathCommand
+ Graphics.Svg.Types: GCurve :: !Origin -> !RPoint -> !RPoint -> !(Maybe RPoint) -> GradientPathCommand
+ Graphics.Svg.Types: GLine :: !Origin -> !(Maybe RPoint) -> GradientPathCommand
+ Graphics.Svg.Types: GradientBicubic :: MeshGradientType
+ Graphics.Svg.Types: GradientBilinear :: MeshGradientType
+ Graphics.Svg.Types: MeshGradient :: !DrawAttributes -> !Number -> !Number -> !MeshGradientType -> !CoordinateUnits -> ![Transformation] -> ![MeshGradientRow] -> MeshGradient
+ Graphics.Svg.Types: MeshGradientPatch :: ![GradientStop] -> MeshGradientPatch
+ Graphics.Svg.Types: MeshGradientRow :: ![MeshGradientPatch] -> MeshGradientRow
+ Graphics.Svg.Types: MeshGradientTree :: !MeshGradient -> Tree
+ Graphics.Svg.Types: [_gradientOpacity] :: GradientStop -> !(Maybe Float)
+ Graphics.Svg.Types: [_gradientPath] :: GradientStop -> !(Maybe GradientPathCommand)
+ Graphics.Svg.Types: [_meshGradientDrawAttributes] :: MeshGradient -> !DrawAttributes
+ Graphics.Svg.Types: [_meshGradientPatchStops] :: MeshGradientPatch -> ![GradientStop]
+ Graphics.Svg.Types: [_meshGradientRowPatches] :: MeshGradientRow -> ![MeshGradientPatch]
+ Graphics.Svg.Types: [_meshGradientRows] :: MeshGradient -> ![MeshGradientRow]
+ Graphics.Svg.Types: [_meshGradientTransform] :: MeshGradient -> ![Transformation]
+ Graphics.Svg.Types: [_meshGradientType] :: MeshGradient -> !MeshGradientType
+ Graphics.Svg.Types: [_meshGradientUnits] :: MeshGradient -> !CoordinateUnits
+ Graphics.Svg.Types: [_meshGradientX] :: MeshGradient -> !Number
+ Graphics.Svg.Types: [_meshGradientY] :: MeshGradient -> !Number
+ Graphics.Svg.Types: class HasMeshGradient c_aDiM where meshGradientDrawAttributes = (.) meshGradient meshGradientDrawAttributes meshGradientRows = (.) meshGradient meshGradientRows meshGradientTransform = (.) meshGradient meshGradientTransform meshGradientType = (.) meshGradient meshGradientType meshGradientUnits = (.) meshGradient meshGradientUnits meshGradientX = (.) meshGradient meshGradientX meshGradientY = (.) meshGradient meshGradientY
+ Graphics.Svg.Types: class HasMeshGradientPatch c_aDaO where meshGradientPatchStops = (.) meshGradientPatch meshGradientPatchStops
+ Graphics.Svg.Types: class HasMeshGradientRow c_aDdF where meshGradientRowPatches = (.) meshGradientRow meshGradientRowPatches
+ Graphics.Svg.Types: data GradientPathCommand
+ Graphics.Svg.Types: data MeshGradient
+ Graphics.Svg.Types: data MeshGradientPatch
+ Graphics.Svg.Types: data MeshGradientRow
+ Graphics.Svg.Types: data MeshGradientType
+ Graphics.Svg.Types: gradientOpacity :: HasGradientStop c_aD5r => Lens' c_aD5r (Maybe Float)
+ Graphics.Svg.Types: gradientPath :: HasGradientStop c_aD5r => Lens' c_aD5r (Maybe GradientPathCommand)
+ Graphics.Svg.Types: instance GHC.Classes.Eq Graphics.Svg.Types.GradientPathCommand
+ Graphics.Svg.Types: instance GHC.Classes.Eq Graphics.Svg.Types.MeshGradient
+ Graphics.Svg.Types: instance GHC.Classes.Eq Graphics.Svg.Types.MeshGradientPatch
+ Graphics.Svg.Types: instance GHC.Classes.Eq Graphics.Svg.Types.MeshGradientRow
+ Graphics.Svg.Types: instance GHC.Classes.Eq Graphics.Svg.Types.MeshGradientType
+ Graphics.Svg.Types: instance GHC.Show.Show Graphics.Svg.Types.GradientPathCommand
+ Graphics.Svg.Types: instance GHC.Show.Show Graphics.Svg.Types.MeshGradient
+ Graphics.Svg.Types: instance GHC.Show.Show Graphics.Svg.Types.MeshGradientPatch
+ Graphics.Svg.Types: instance GHC.Show.Show Graphics.Svg.Types.MeshGradientRow
+ Graphics.Svg.Types: instance GHC.Show.Show Graphics.Svg.Types.MeshGradientType
+ Graphics.Svg.Types: instance Graphics.Svg.Types.HasMeshGradient Graphics.Svg.Types.MeshGradient
+ Graphics.Svg.Types: instance Graphics.Svg.Types.HasMeshGradientPatch Graphics.Svg.Types.MeshGradientPatch
+ Graphics.Svg.Types: instance Graphics.Svg.Types.HasMeshGradientRow Graphics.Svg.Types.MeshGradientRow
+ Graphics.Svg.Types: instance Graphics.Svg.Types.WithDefaultSvg Graphics.Svg.Types.MeshGradient
+ Graphics.Svg.Types: instance Graphics.Svg.Types.WithDefaultSvg Graphics.Svg.Types.MeshGradientPatch
+ Graphics.Svg.Types: instance Graphics.Svg.Types.WithDefaultSvg Graphics.Svg.Types.MeshGradientRow
+ Graphics.Svg.Types: instance Graphics.Svg.Types.WithDrawAttributes Graphics.Svg.Types.MeshGradient
+ Graphics.Svg.Types: meshGradient :: HasMeshGradient c_aDiM => Lens' c_aDiM MeshGradient
+ Graphics.Svg.Types: meshGradientDrawAttributes :: HasMeshGradient c_aDiM => Lens' c_aDiM DrawAttributes
+ Graphics.Svg.Types: meshGradientPatch :: HasMeshGradientPatch c_aDaO => Lens' c_aDaO MeshGradientPatch
+ Graphics.Svg.Types: meshGradientPatchStops :: HasMeshGradientPatch c_aDaO => Lens' c_aDaO [GradientStop]
+ Graphics.Svg.Types: meshGradientRow :: HasMeshGradientRow c_aDdF => Lens' c_aDdF MeshGradientRow
+ Graphics.Svg.Types: meshGradientRowPatches :: HasMeshGradientRow c_aDdF => Lens' c_aDdF [MeshGradientPatch]
+ Graphics.Svg.Types: meshGradientRows :: HasMeshGradient c_aDiM => Lens' c_aDiM [MeshGradientRow]
+ Graphics.Svg.Types: meshGradientTransform :: HasMeshGradient c_aDiM => Lens' c_aDiM [Transformation]
+ Graphics.Svg.Types: meshGradientType :: HasMeshGradient c_aDiM => Lens' c_aDiM MeshGradientType
+ Graphics.Svg.Types: meshGradientUnits :: HasMeshGradient c_aDiM => Lens' c_aDiM CoordinateUnits
+ Graphics.Svg.Types: meshGradientX :: HasMeshGradient c_aDiM => Lens' c_aDiM Number
+ Graphics.Svg.Types: meshGradientY :: HasMeshGradient c_aDiM => Lens' c_aDiM Number
- Graphics.Svg.Types: GradientStop :: Float -> PixelRGBA8 -> GradientStop
+ Graphics.Svg.Types: GradientStop :: !Float -> !PixelRGBA8 -> !(Maybe GradientPathCommand) -> !(Maybe Float) -> GradientStop
- Graphics.Svg.Types: [_gradientColor] :: GradientStop -> PixelRGBA8
+ Graphics.Svg.Types: [_gradientColor] :: GradientStop -> !PixelRGBA8
- Graphics.Svg.Types: [_gradientOffset] :: GradientStop -> Float
+ Graphics.Svg.Types: [_gradientOffset] :: GradientStop -> !Float
- Graphics.Svg.Types: aspectRatioAlign :: HasPreserveAspectRatio c_azPm => Lens' c_azPm Alignment
+ Graphics.Svg.Types: aspectRatioAlign :: HasPreserveAspectRatio c_aA1Q => Lens' c_aA1Q Alignment
- Graphics.Svg.Types: aspectRatioDefer :: HasPreserveAspectRatio c_azPm => Lens' c_azPm Bool
+ Graphics.Svg.Types: aspectRatioDefer :: HasPreserveAspectRatio c_aA1Q => Lens' c_aA1Q Bool
- Graphics.Svg.Types: aspectRatioMeetSlice :: HasPreserveAspectRatio c_azPm => Lens' c_azPm (Maybe MeetSlice)
+ Graphics.Svg.Types: aspectRatioMeetSlice :: HasPreserveAspectRatio c_aA1Q => Lens' c_aA1Q (Maybe MeetSlice)
- Graphics.Svg.Types: attrClass :: HasDrawAttributes c_aAk7 => Lens' c_aAk7 [Text]
+ Graphics.Svg.Types: attrClass :: HasDrawAttributes c_aAxm => Lens' c_aAxm [Text]
- Graphics.Svg.Types: attrId :: HasDrawAttributes c_aAk7 => Lens' c_aAk7 (Maybe String)
+ Graphics.Svg.Types: attrId :: HasDrawAttributes c_aAxm => Lens' c_aAxm (Maybe String)
- Graphics.Svg.Types: circle :: HasCircle c_aBVg => Lens' c_aBVg Circle
+ Graphics.Svg.Types: circle :: HasCircle c_aCT4 => Lens' c_aCT4 Circle
- Graphics.Svg.Types: circleCenter :: HasCircle c_aBVg => Lens' c_aBVg Point
+ Graphics.Svg.Types: circleCenter :: HasCircle c_aCT4 => Lens' c_aCT4 Point
- Graphics.Svg.Types: circleDrawAttributes :: HasCircle c_aBVg => Lens' c_aBVg DrawAttributes
+ Graphics.Svg.Types: circleDrawAttributes :: HasCircle c_aCT4 => Lens' c_aCT4 DrawAttributes
- Graphics.Svg.Types: circleRadius :: HasCircle c_aBVg => Lens' c_aBVg Number
+ Graphics.Svg.Types: circleRadius :: HasCircle c_aCT4 => Lens' c_aCT4 Number
- Graphics.Svg.Types: class HasCircle c_aBVg where circleCenter = (.) circle circleCenter circleDrawAttributes = (.) circle circleDrawAttributes circleRadius = (.) circle circleRadius
+ Graphics.Svg.Types: class HasCircle c_aCT4 where circleCenter = (.) circle circleCenter circleDrawAttributes = (.) circle circleDrawAttributes circleRadius = (.) circle circleRadius
- Graphics.Svg.Types: class HasClipPath c_aE9q where clipPathContent = (.) clipPath clipPathContent clipPathDrawAttributes = (.) clipPath clipPathDrawAttributes clipPathUnits = (.) clipPath clipPathUnits
+ Graphics.Svg.Types: class HasClipPath c_aFoV where clipPathContent = (.) clipPath clipPathContent clipPathDrawAttributes = (.) clipPath clipPathDrawAttributes clipPathUnits = (.) clipPath clipPathUnits
- Graphics.Svg.Types: class HasDocument c_aEuT where definitions = (.) document definitions description = (.) document description documentLocation = (.) document documentLocation elements = (.) document elements height = (.) document height styleRules = (.) document styleRules viewBox = (.) document viewBox width = (.) document width
+ Graphics.Svg.Types: class HasDocument c_aFKR where definitions = (.) document definitions description = (.) document description documentLocation = (.) document documentLocation elements = (.) document elements height = (.) document height styleRules = (.) document styleRules viewBox = (.) document viewBox width = (.) document width
- Graphics.Svg.Types: class HasDrawAttributes c_aAk7 where attrClass = (.) drawAttributes attrClass attrId = (.) drawAttributes attrId clipPathRef = (.) drawAttributes clipPathRef clipRule = (.) drawAttributes clipRule fillColor = (.) drawAttributes fillColor fillOpacity = (.) drawAttributes fillOpacity fillRule = (.) drawAttributes fillRule fontFamily = (.) drawAttributes fontFamily fontSize = (.) drawAttributes fontSize fontStyle = (.) drawAttributes fontStyle groupOpacity = (.) drawAttributes groupOpacity markerEnd = (.) drawAttributes markerEnd markerMid = (.) drawAttributes markerMid markerStart = (.) drawAttributes markerStart maskRef = (.) drawAttributes maskRef strokeColor = (.) drawAttributes strokeColor strokeDashArray = (.) drawAttributes strokeDashArray strokeLineCap = (.) drawAttributes strokeLineCap strokeLineJoin = (.) drawAttributes strokeLineJoin strokeMiterLimit = (.) drawAttributes strokeMiterLimit strokeOffset = (.) drawAttributes strokeOffset strokeOpacity = (.) drawAttributes strokeOpacity strokeWidth = (.) drawAttributes strokeWidth textAnchor = (.) drawAttributes textAnchor transform = (.) drawAttributes transform
+ Graphics.Svg.Types: class HasDrawAttributes c_aAxm where attrClass = (.) drawAttributes attrClass attrId = (.) drawAttributes attrId clipPathRef = (.) drawAttributes clipPathRef clipRule = (.) drawAttributes clipRule fillColor = (.) drawAttributes fillColor fillOpacity = (.) drawAttributes fillOpacity fillRule = (.) drawAttributes fillRule fontFamily = (.) drawAttributes fontFamily fontSize = (.) drawAttributes fontSize fontStyle = (.) drawAttributes fontStyle groupOpacity = (.) drawAttributes groupOpacity markerEnd = (.) drawAttributes markerEnd markerMid = (.) drawAttributes markerMid markerStart = (.) drawAttributes markerStart maskRef = (.) drawAttributes maskRef strokeColor = (.) drawAttributes strokeColor strokeDashArray = (.) drawAttributes strokeDashArray strokeLineCap = (.) drawAttributes strokeLineCap strokeLineJoin = (.) drawAttributes strokeLineJoin strokeMiterLimit = (.) drawAttributes strokeMiterLimit strokeOffset = (.) drawAttributes strokeOffset strokeOpacity = (.) drawAttributes strokeOpacity strokeWidth = (.) drawAttributes strokeWidth textAnchor = (.) drawAttributes textAnchor transform = (.) drawAttributes transform
- Graphics.Svg.Types: class HasEllipse c_aC0Y where ellipseCenter = (.) ellipse ellipseCenter ellipseDrawAttributes = (.) ellipse ellipseDrawAttributes ellipseXRadius = (.) ellipse ellipseXRadius ellipseYRadius = (.) ellipse ellipseYRadius
+ Graphics.Svg.Types: class HasEllipse c_aCYQ where ellipseCenter = (.) ellipse ellipseCenter ellipseDrawAttributes = (.) ellipse ellipseDrawAttributes ellipseXRadius = (.) ellipse ellipseXRadius ellipseYRadius = (.) ellipse ellipseYRadius
- Graphics.Svg.Types: class HasGradientStop c_aDAu where gradientColor = (.) gradientStop gradientColor gradientOffset = (.) gradientStop gradientOffset
+ Graphics.Svg.Types: class HasGradientStop c_aD5r where gradientColor = (.) gradientStop gradientColor gradientOffset = (.) gradientStop gradientOffset gradientOpacity = (.) gradientStop gradientOpacity gradientPath = (.) gradientStop gradientPath
- Graphics.Svg.Types: class HasGroup c_aBHU a_aBaw | c_aBHU -> a_aBaw where groupAspectRatio = (.) group groupAspectRatio groupChildren = (.) group groupChildren groupDrawAttributes = (.) group groupDrawAttributes groupViewBox = (.) group groupViewBox
+ Graphics.Svg.Types: class HasGroup c_aCFY a_aBo5 | c_aCFY -> a_aBo5 where groupAspectRatio = (.) group groupAspectRatio groupChildren = (.) group groupChildren groupDrawAttributes = (.) group groupDrawAttributes groupViewBox = (.) group groupViewBox
- Graphics.Svg.Types: class HasImage c_aC8i where imageAspectRatio = (.) image imageAspectRatio imageCornerUpperLeft = (.) image imageCornerUpperLeft imageDrawAttributes = (.) image imageDrawAttributes imageHeight = (.) image imageHeight imageHref = (.) image imageHref imageWidth = (.) image imageWidth
+ Graphics.Svg.Types: class HasImage c_aDsW where imageAspectRatio = (.) image imageAspectRatio imageCornerUpperLeft = (.) image imageCornerUpperLeft imageDrawAttributes = (.) image imageDrawAttributes imageHeight = (.) image imageHeight imageHref = (.) image imageHref imageWidth = (.) image imageWidth
- Graphics.Svg.Types: class HasLine c_aAXj where lineDrawAttributes = (.) line lineDrawAttributes linePoint1 = (.) line linePoint1 linePoint2 = (.) line linePoint2
+ Graphics.Svg.Types: class HasLine c_aBaK where lineDrawAttributes = (.) line lineDrawAttributes linePoint1 = (.) line linePoint1 linePoint2 = (.) line linePoint2
- Graphics.Svg.Types: class HasLinearGradient c_aDFY where linearGradientSpread = (.) linearGradient linearGradientSpread linearGradientStart = (.) linearGradient linearGradientStart linearGradientStop = (.) linearGradient linearGradientStop linearGradientStops = (.) linearGradient linearGradientStops linearGradientTransform = (.) linearGradient linearGradientTransform linearGradientUnits = (.) linearGradient linearGradientUnits
+ Graphics.Svg.Types: class HasLinearGradient c_aEVh where linearGradientSpread = (.) linearGradient linearGradientSpread linearGradientStart = (.) linearGradient linearGradientStart linearGradientStop = (.) linearGradient linearGradientStop linearGradientStops = (.) linearGradient linearGradientStops linearGradientTransform = (.) linearGradient linearGradientTransform linearGradientUnits = (.) linearGradient linearGradientUnits
- Graphics.Svg.Types: class HasMarker c_aD61 where markerAspectRatio = (.) marker markerAspectRatio markerDrawAttributes = (.) marker markerDrawAttributes markerElements = (.) marker markerElements markerHeight = (.) marker markerHeight markerOrient = (.) marker markerOrient markerOverflow = (.) marker markerOverflow markerRefPoint = (.) marker markerRefPoint markerUnits = (.) marker markerUnits markerViewBox = (.) marker markerViewBox markerWidth = (.) marker markerWidth
+ Graphics.Svg.Types: class HasMarker c_aErK where markerAspectRatio = (.) marker markerAspectRatio markerDrawAttributes = (.) marker markerDrawAttributes markerElements = (.) marker markerElements markerHeight = (.) marker markerHeight markerOrient = (.) marker markerOrient markerOverflow = (.) marker markerOverflow markerRefPoint = (.) marker markerRefPoint markerUnits = (.) marker markerUnits markerViewBox = (.) marker markerViewBox markerWidth = (.) marker markerWidth
- Graphics.Svg.Types: class HasMask c_aE0u where maskContent = (.) mask maskContent maskContentUnits = (.) mask maskContentUnits maskDrawAttributes = (.) mask maskDrawAttributes maskHeight = (.) mask maskHeight maskPosition = (.) mask maskPosition maskUnits = (.) mask maskUnits maskWidth = (.) mask maskWidth
+ Graphics.Svg.Types: class HasMask c_aFfV where maskContent = (.) mask maskContent maskContentUnits = (.) mask maskContentUnits maskDrawAttributes = (.) mask maskDrawAttributes maskHeight = (.) mask maskHeight maskPosition = (.) mask maskPosition maskUnits = (.) mask maskUnits maskWidth = (.) mask maskWidth
- Graphics.Svg.Types: class HasPath c_aBad where pathDefinition = (.) path pathDefinition pathDrawAttributes = (.) path pathDrawAttributes
+ Graphics.Svg.Types: class HasPath c_aBnM where pathDefinition = (.) path pathDefinition pathDrawAttributes = (.) path pathDrawAttributes
- Graphics.Svg.Types: class HasPattern c_aEfL where patternAspectRatio = (.) pattern patternAspectRatio patternDrawAttributes = (.) pattern patternDrawAttributes patternElements = (.) pattern patternElements patternHeight = (.) pattern patternHeight patternPos = (.) pattern patternPos patternUnit = (.) pattern patternUnit patternViewBox = (.) pattern patternViewBox patternWidth = (.) pattern patternWidth
+ Graphics.Svg.Types: class HasPattern c_aFvk where patternAspectRatio = (.) pattern patternAspectRatio patternDrawAttributes = (.) pattern patternDrawAttributes patternElements = (.) pattern patternElements patternHeight = (.) pattern patternHeight patternPos = (.) pattern patternPos patternUnit = (.) pattern patternUnit patternViewBox = (.) pattern patternViewBox patternWidth = (.) pattern patternWidth
- Graphics.Svg.Types: class HasPolyLine c_aAOC where polyLineDrawAttributes = (.) polyLine polyLineDrawAttributes polyLinePoints = (.) polyLine polyLinePoints
+ Graphics.Svg.Types: class HasPolyLine c_aB1V where polyLineDrawAttributes = (.) polyLine polyLineDrawAttributes polyLinePoints = (.) polyLine polyLinePoints
- Graphics.Svg.Types: class HasPolygon c_aASM where polygonDrawAttributes = (.) polygon polygonDrawAttributes polygonPoints = (.) polygon polygonPoints
+ Graphics.Svg.Types: class HasPolygon c_aB69 where polygonDrawAttributes = (.) polygon polygonDrawAttributes polygonPoints = (.) polygon polygonPoints
- Graphics.Svg.Types: class HasPreserveAspectRatio c_azPm where aspectRatioAlign = (.) preserveAspectRatio aspectRatioAlign aspectRatioDefer = (.) preserveAspectRatio aspectRatioDefer aspectRatioMeetSlice = (.) preserveAspectRatio aspectRatioMeetSlice
+ Graphics.Svg.Types: class HasPreserveAspectRatio c_aA1Q where aspectRatioAlign = (.) preserveAspectRatio aspectRatioAlign aspectRatioDefer = (.) preserveAspectRatio aspectRatioDefer aspectRatioMeetSlice = (.) preserveAspectRatio aspectRatioMeetSlice
- Graphics.Svg.Types: class HasRadialGradient c_aDPs where radialGradientCenter = (.) radialGradient radialGradientCenter radialGradientFocusX = (.) radialGradient radialGradientFocusX radialGradientFocusY = (.) radialGradient radialGradientFocusY radialGradientRadius = (.) radialGradient radialGradientRadius radialGradientSpread = (.) radialGradient radialGradientSpread radialGradientStops = (.) radialGradient radialGradientStops radialGradientTransform = (.) radialGradient radialGradientTransform radialGradientUnits = (.) radialGradient radialGradientUnits
+ Graphics.Svg.Types: class HasRadialGradient c_aF4P where radialGradientCenter = (.) radialGradient radialGradientCenter radialGradientFocusX = (.) radialGradient radialGradientFocusX radialGradientFocusY = (.) radialGradient radialGradientFocusY radialGradientRadius = (.) radialGradient radialGradientRadius radialGradientSpread = (.) radialGradient radialGradientSpread radialGradientStops = (.) radialGradient radialGradientStops radialGradientTransform = (.) radialGradient radialGradientTransform radialGradientUnits = (.) radialGradient radialGradientUnits
- Graphics.Svg.Types: class HasRectangle c_aB3z where rectCornerRadius = (.) rectangle rectCornerRadius rectDrawAttributes = (.) rectangle rectDrawAttributes rectHeight = (.) rectangle rectHeight rectUpperLeftCorner = (.) rectangle rectUpperLeftCorner rectWidth = (.) rectangle rectWidth
+ Graphics.Svg.Types: class HasRectangle c_aBh4 where rectCornerRadius = (.) rectangle rectCornerRadius rectDrawAttributes = (.) rectangle rectDrawAttributes rectHeight = (.) rectangle rectHeight rectUpperLeftCorner = (.) rectangle rectUpperLeftCorner rectWidth = (.) rectangle rectWidth
- Graphics.Svg.Types: class HasText c_aCOF where textAdjust = (.) text textAdjust textRoot = (.) text textRoot
+ Graphics.Svg.Types: class HasText c_aE9F where textAdjust = (.) text textAdjust textRoot = (.) text textRoot
- Graphics.Svg.Types: class HasTextInfo c_aCqe where textInfoDX = (.) textInfo textInfoDX textInfoDY = (.) textInfo textInfoDY textInfoLength = (.) textInfo textInfoLength textInfoRotate = (.) textInfo textInfoRotate textInfoX = (.) textInfo textInfoX textInfoY = (.) textInfo textInfoY
+ Graphics.Svg.Types: class HasTextInfo c_aDKM where textInfoDX = (.) textInfo textInfoDX textInfoDY = (.) textInfo textInfoDY textInfoLength = (.) textInfo textInfoLength textInfoRotate = (.) textInfo textInfoRotate textInfoX = (.) textInfo textInfoX textInfoY = (.) textInfo textInfoY
- Graphics.Svg.Types: class HasTextPath c_aCHq where textPathData = (.) textPath textPathData textPathMethod = (.) textPath textPathMethod textPathName = (.) textPath textPathName textPathSpacing = (.) textPath textPathSpacing textPathStartOffset = (.) textPath textPathStartOffset
+ Graphics.Svg.Types: class HasTextPath c_aE2i where textPathData = (.) textPath textPathData textPathMethod = (.) textPath textPathMethod textPathName = (.) textPath textPathName textPathSpacing = (.) textPath textPathSpacing textPathStartOffset = (.) textPath textPathStartOffset
- Graphics.Svg.Types: class HasTextSpan c_aCzN where spanContent = (.) textSpan spanContent spanDrawAttributes = (.) textSpan spanDrawAttributes spanInfo = (.) textSpan spanInfo
+ Graphics.Svg.Types: class HasTextSpan c_aDUt where spanContent = (.) textSpan spanContent spanDrawAttributes = (.) textSpan spanDrawAttributes spanInfo = (.) textSpan spanInfo
- Graphics.Svg.Types: class HasUse c_aCgY where useBase = (.) use useBase useDrawAttributes = (.) use useDrawAttributes useHeight = (.) use useHeight useName = (.) use useName useWidth = (.) use useWidth
+ Graphics.Svg.Types: class HasUse c_aDBG where useBase = (.) use useBase useDrawAttributes = (.) use useDrawAttributes useHeight = (.) use useHeight useName = (.) use useName useWidth = (.) use useWidth
- Graphics.Svg.Types: clipPath :: HasClipPath c_aE9q => Lens' c_aE9q ClipPath
+ Graphics.Svg.Types: clipPath :: HasClipPath c_aFoV => Lens' c_aFoV ClipPath
- Graphics.Svg.Types: clipPathContent :: HasClipPath c_aE9q => Lens' c_aE9q [Tree]
+ Graphics.Svg.Types: clipPathContent :: HasClipPath c_aFoV => Lens' c_aFoV [Tree]
- Graphics.Svg.Types: clipPathDrawAttributes :: HasClipPath c_aE9q => Lens' c_aE9q DrawAttributes
+ Graphics.Svg.Types: clipPathDrawAttributes :: HasClipPath c_aFoV => Lens' c_aFoV DrawAttributes
- Graphics.Svg.Types: clipPathRef :: HasDrawAttributes c_aAk7 => Lens' c_aAk7 (Last ElementRef)
+ Graphics.Svg.Types: clipPathRef :: HasDrawAttributes c_aAxm => Lens' c_aAxm (Last ElementRef)
- Graphics.Svg.Types: clipPathUnits :: HasClipPath c_aE9q => Lens' c_aE9q CoordinateUnits
+ Graphics.Svg.Types: clipPathUnits :: HasClipPath c_aFoV => Lens' c_aFoV CoordinateUnits
- Graphics.Svg.Types: clipRule :: HasDrawAttributes c_aAk7 => Lens' c_aAk7 (Last FillRule)
+ Graphics.Svg.Types: clipRule :: HasDrawAttributes c_aAxm => Lens' c_aAxm (Last FillRule)
- Graphics.Svg.Types: definitions :: HasDocument c_aEuT => Lens' c_aEuT (Map String Element)
+ Graphics.Svg.Types: definitions :: HasDocument c_aFKR => Lens' c_aFKR (Map String Element)
- Graphics.Svg.Types: description :: HasDocument c_aEuT => Lens' c_aEuT String
+ Graphics.Svg.Types: description :: HasDocument c_aFKR => Lens' c_aFKR String
- Graphics.Svg.Types: document :: HasDocument c_aEuT => Lens' c_aEuT Document
+ Graphics.Svg.Types: document :: HasDocument c_aFKR => Lens' c_aFKR Document
- Graphics.Svg.Types: documentLocation :: HasDocument c_aEuT => Lens' c_aEuT FilePath
+ Graphics.Svg.Types: documentLocation :: HasDocument c_aFKR => Lens' c_aFKR FilePath
- Graphics.Svg.Types: drawAttributes :: HasDrawAttributes c_aAk7 => Lens' c_aAk7 DrawAttributes
+ Graphics.Svg.Types: drawAttributes :: HasDrawAttributes c_aAxm => Lens' c_aAxm DrawAttributes
- Graphics.Svg.Types: elements :: HasDocument c_aEuT => Lens' c_aEuT [Tree]
+ Graphics.Svg.Types: elements :: HasDocument c_aFKR => Lens' c_aFKR [Tree]
- Graphics.Svg.Types: ellipse :: HasEllipse c_aC0Y => Lens' c_aC0Y Ellipse
+ Graphics.Svg.Types: ellipse :: HasEllipse c_aCYQ => Lens' c_aCYQ Ellipse
- Graphics.Svg.Types: ellipseCenter :: HasEllipse c_aC0Y => Lens' c_aC0Y Point
+ Graphics.Svg.Types: ellipseCenter :: HasEllipse c_aCYQ => Lens' c_aCYQ Point
- Graphics.Svg.Types: ellipseDrawAttributes :: HasEllipse c_aC0Y => Lens' c_aC0Y DrawAttributes
+ Graphics.Svg.Types: ellipseDrawAttributes :: HasEllipse c_aCYQ => Lens' c_aCYQ DrawAttributes
- Graphics.Svg.Types: ellipseXRadius :: HasEllipse c_aC0Y => Lens' c_aC0Y Number
+ Graphics.Svg.Types: ellipseXRadius :: HasEllipse c_aCYQ => Lens' c_aCYQ Number
- Graphics.Svg.Types: ellipseYRadius :: HasEllipse c_aC0Y => Lens' c_aC0Y Number
+ Graphics.Svg.Types: ellipseYRadius :: HasEllipse c_aCYQ => Lens' c_aCYQ Number
- Graphics.Svg.Types: fillColor :: HasDrawAttributes c_aAk7 => Lens' c_aAk7 (Last Texture)
+ Graphics.Svg.Types: fillColor :: HasDrawAttributes c_aAxm => Lens' c_aAxm (Last Texture)
- Graphics.Svg.Types: fillOpacity :: HasDrawAttributes c_aAk7 => Lens' c_aAk7 (Maybe Float)
+ Graphics.Svg.Types: fillOpacity :: HasDrawAttributes c_aAxm => Lens' c_aAxm (Maybe Float)
- Graphics.Svg.Types: fillRule :: HasDrawAttributes c_aAk7 => Lens' c_aAk7 (Last FillRule)
+ Graphics.Svg.Types: fillRule :: HasDrawAttributes c_aAxm => Lens' c_aAxm (Last FillRule)
- Graphics.Svg.Types: fontFamily :: HasDrawAttributes c_aAk7 => Lens' c_aAk7 (Last [String])
+ Graphics.Svg.Types: fontFamily :: HasDrawAttributes c_aAxm => Lens' c_aAxm (Last [String])
- Graphics.Svg.Types: fontSize :: HasDrawAttributes c_aAk7 => Lens' c_aAk7 (Last Number)
+ Graphics.Svg.Types: fontSize :: HasDrawAttributes c_aAxm => Lens' c_aAxm (Last Number)
- Graphics.Svg.Types: fontStyle :: HasDrawAttributes c_aAk7 => Lens' c_aAk7 (Last FontStyle)
+ Graphics.Svg.Types: fontStyle :: HasDrawAttributes c_aAxm => Lens' c_aAxm (Last FontStyle)
- Graphics.Svg.Types: gradientColor :: HasGradientStop c_aDAu => Lens' c_aDAu PixelRGBA8
+ Graphics.Svg.Types: gradientColor :: HasGradientStop c_aD5r => Lens' c_aD5r PixelRGBA8
- Graphics.Svg.Types: gradientOffset :: HasGradientStop c_aDAu => Lens' c_aDAu Float
+ Graphics.Svg.Types: gradientOffset :: HasGradientStop c_aD5r => Lens' c_aD5r Float
- Graphics.Svg.Types: gradientStop :: HasGradientStop c_aDAu => Lens' c_aDAu GradientStop
+ Graphics.Svg.Types: gradientStop :: HasGradientStop c_aD5r => Lens' c_aD5r GradientStop
- Graphics.Svg.Types: group :: HasGroup c_aBHU a_aBaw => Lens' c_aBHU (Group a_aBaw)
+ Graphics.Svg.Types: group :: HasGroup c_aCFY a_aBo5 => Lens' c_aCFY (Group a_aBo5)
- Graphics.Svg.Types: groupAspectRatio :: HasGroup c_aBHU a_aBaw => Lens' c_aBHU PreserveAspectRatio
+ Graphics.Svg.Types: groupAspectRatio :: HasGroup c_aCFY a_aBo5 => Lens' c_aCFY PreserveAspectRatio
- Graphics.Svg.Types: groupChildren :: HasGroup c_aBHU a_aBaw => Lens' c_aBHU [a_aBaw]
+ Graphics.Svg.Types: groupChildren :: HasGroup c_aCFY a_aBo5 => Lens' c_aCFY [a_aBo5]
- Graphics.Svg.Types: groupDrawAttributes :: HasGroup c_aBHU a_aBaw => Lens' c_aBHU DrawAttributes
+ Graphics.Svg.Types: groupDrawAttributes :: HasGroup c_aCFY a_aBo5 => Lens' c_aCFY DrawAttributes
- Graphics.Svg.Types: groupOfSymbol :: forall a_aBIs a_aBPh. Iso (Symbol a_aBIs) (Symbol a_aBPh) (Group a_aBIs) (Group a_aBPh)
+ Graphics.Svg.Types: groupOfSymbol :: forall a_aCGw a_aCNh. Iso (Symbol a_aCGw) (Symbol a_aCNh) (Group a_aCGw) (Group a_aCNh)
- Graphics.Svg.Types: groupOpacity :: HasDrawAttributes c_aAk7 => Lens' c_aAk7 (Maybe Float)
+ Graphics.Svg.Types: groupOpacity :: HasDrawAttributes c_aAxm => Lens' c_aAxm (Maybe Float)
- Graphics.Svg.Types: groupViewBox :: HasGroup c_aBHU a_aBaw => Lens' c_aBHU (Maybe (Double, Double, Double, Double))
+ Graphics.Svg.Types: groupViewBox :: HasGroup c_aCFY a_aBo5 => Lens' c_aCFY (Maybe (Double, Double, Double, Double))
- Graphics.Svg.Types: height :: HasDocument c_aEuT => Lens' c_aEuT (Maybe Number)
+ Graphics.Svg.Types: height :: HasDocument c_aFKR => Lens' c_aFKR (Maybe Number)
- Graphics.Svg.Types: image :: HasImage c_aC8i => Lens' c_aC8i Image
+ Graphics.Svg.Types: image :: HasImage c_aDsW => Lens' c_aDsW Image
- Graphics.Svg.Types: imageAspectRatio :: HasImage c_aC8i => Lens' c_aC8i PreserveAspectRatio
+ Graphics.Svg.Types: imageAspectRatio :: HasImage c_aDsW => Lens' c_aDsW PreserveAspectRatio
- Graphics.Svg.Types: imageCornerUpperLeft :: HasImage c_aC8i => Lens' c_aC8i Point
+ Graphics.Svg.Types: imageCornerUpperLeft :: HasImage c_aDsW => Lens' c_aDsW Point
- Graphics.Svg.Types: imageDrawAttributes :: HasImage c_aC8i => Lens' c_aC8i DrawAttributes
+ Graphics.Svg.Types: imageDrawAttributes :: HasImage c_aDsW => Lens' c_aDsW DrawAttributes
- Graphics.Svg.Types: imageHeight :: HasImage c_aC8i => Lens' c_aC8i Number
+ Graphics.Svg.Types: imageHeight :: HasImage c_aDsW => Lens' c_aDsW Number
- Graphics.Svg.Types: imageHref :: HasImage c_aC8i => Lens' c_aC8i String
+ Graphics.Svg.Types: imageHref :: HasImage c_aDsW => Lens' c_aDsW String
- Graphics.Svg.Types: imageWidth :: HasImage c_aC8i => Lens' c_aC8i Number
+ Graphics.Svg.Types: imageWidth :: HasImage c_aDsW => Lens' c_aDsW Number
- Graphics.Svg.Types: line :: HasLine c_aAXj => Lens' c_aAXj Line
+ Graphics.Svg.Types: line :: HasLine c_aBaK => Lens' c_aBaK Line
- Graphics.Svg.Types: lineDrawAttributes :: HasLine c_aAXj => Lens' c_aAXj DrawAttributes
+ Graphics.Svg.Types: lineDrawAttributes :: HasLine c_aBaK => Lens' c_aBaK DrawAttributes
- Graphics.Svg.Types: linePoint1 :: HasLine c_aAXj => Lens' c_aAXj Point
+ Graphics.Svg.Types: linePoint1 :: HasLine c_aBaK => Lens' c_aBaK Point
- Graphics.Svg.Types: linePoint2 :: HasLine c_aAXj => Lens' c_aAXj Point
+ Graphics.Svg.Types: linePoint2 :: HasLine c_aBaK => Lens' c_aBaK Point
- Graphics.Svg.Types: linearGradient :: HasLinearGradient c_aDFY => Lens' c_aDFY LinearGradient
+ Graphics.Svg.Types: linearGradient :: HasLinearGradient c_aEVh => Lens' c_aEVh LinearGradient
- Graphics.Svg.Types: linearGradientSpread :: HasLinearGradient c_aDFY => Lens' c_aDFY Spread
+ Graphics.Svg.Types: linearGradientSpread :: HasLinearGradient c_aEVh => Lens' c_aEVh Spread
- Graphics.Svg.Types: linearGradientStart :: HasLinearGradient c_aDFY => Lens' c_aDFY Point
+ Graphics.Svg.Types: linearGradientStart :: HasLinearGradient c_aEVh => Lens' c_aEVh Point
- Graphics.Svg.Types: linearGradientStop :: HasLinearGradient c_aDFY => Lens' c_aDFY Point
+ Graphics.Svg.Types: linearGradientStop :: HasLinearGradient c_aEVh => Lens' c_aEVh Point
- Graphics.Svg.Types: linearGradientStops :: HasLinearGradient c_aDFY => Lens' c_aDFY [GradientStop]
+ Graphics.Svg.Types: linearGradientStops :: HasLinearGradient c_aEVh => Lens' c_aEVh [GradientStop]
- Graphics.Svg.Types: linearGradientTransform :: HasLinearGradient c_aDFY => Lens' c_aDFY [Transformation]
+ Graphics.Svg.Types: linearGradientTransform :: HasLinearGradient c_aEVh => Lens' c_aEVh [Transformation]
- Graphics.Svg.Types: linearGradientUnits :: HasLinearGradient c_aDFY => Lens' c_aDFY CoordinateUnits
+ Graphics.Svg.Types: linearGradientUnits :: HasLinearGradient c_aEVh => Lens' c_aEVh CoordinateUnits
- Graphics.Svg.Types: marker :: HasMarker c_aD61 => Lens' c_aD61 Marker
+ Graphics.Svg.Types: marker :: HasMarker c_aErK => Lens' c_aErK Marker
- Graphics.Svg.Types: markerAspectRatio :: HasMarker c_aD61 => Lens' c_aD61 PreserveAspectRatio
+ Graphics.Svg.Types: markerAspectRatio :: HasMarker c_aErK => Lens' c_aErK PreserveAspectRatio
- Graphics.Svg.Types: markerDrawAttributes :: HasMarker c_aD61 => Lens' c_aD61 DrawAttributes
+ Graphics.Svg.Types: markerDrawAttributes :: HasMarker c_aErK => Lens' c_aErK DrawAttributes
- Graphics.Svg.Types: markerElements :: HasMarker c_aD61 => Lens' c_aD61 [Tree]
+ Graphics.Svg.Types: markerElements :: HasMarker c_aErK => Lens' c_aErK [Tree]
- Graphics.Svg.Types: markerEnd :: HasDrawAttributes c_aAk7 => Lens' c_aAk7 (Last ElementRef)
+ Graphics.Svg.Types: markerEnd :: HasDrawAttributes c_aAxm => Lens' c_aAxm (Last ElementRef)
- Graphics.Svg.Types: markerHeight :: HasMarker c_aD61 => Lens' c_aD61 (Maybe Number)
+ Graphics.Svg.Types: markerHeight :: HasMarker c_aErK => Lens' c_aErK (Maybe Number)
- Graphics.Svg.Types: markerMid :: HasDrawAttributes c_aAk7 => Lens' c_aAk7 (Last ElementRef)
+ Graphics.Svg.Types: markerMid :: HasDrawAttributes c_aAxm => Lens' c_aAxm (Last ElementRef)
- Graphics.Svg.Types: markerOrient :: HasMarker c_aD61 => Lens' c_aD61 (Maybe MarkerOrientation)
+ Graphics.Svg.Types: markerOrient :: HasMarker c_aErK => Lens' c_aErK (Maybe MarkerOrientation)
- Graphics.Svg.Types: markerOverflow :: HasMarker c_aD61 => Lens' c_aD61 (Maybe Overflow)
+ Graphics.Svg.Types: markerOverflow :: HasMarker c_aErK => Lens' c_aErK (Maybe Overflow)
- Graphics.Svg.Types: markerRefPoint :: HasMarker c_aD61 => Lens' c_aD61 (Number, Number)
+ Graphics.Svg.Types: markerRefPoint :: HasMarker c_aErK => Lens' c_aErK (Number, Number)
- Graphics.Svg.Types: markerStart :: HasDrawAttributes c_aAk7 => Lens' c_aAk7 (Last ElementRef)
+ Graphics.Svg.Types: markerStart :: HasDrawAttributes c_aAxm => Lens' c_aAxm (Last ElementRef)
- Graphics.Svg.Types: markerUnits :: HasMarker c_aD61 => Lens' c_aD61 (Maybe MarkerUnit)
+ Graphics.Svg.Types: markerUnits :: HasMarker c_aErK => Lens' c_aErK (Maybe MarkerUnit)
- Graphics.Svg.Types: markerViewBox :: HasMarker c_aD61 => Lens' c_aD61 (Maybe (Double, Double, Double, Double))
+ Graphics.Svg.Types: markerViewBox :: HasMarker c_aErK => Lens' c_aErK (Maybe (Double, Double, Double, Double))
- Graphics.Svg.Types: markerWidth :: HasMarker c_aD61 => Lens' c_aD61 (Maybe Number)
+ Graphics.Svg.Types: markerWidth :: HasMarker c_aErK => Lens' c_aErK (Maybe Number)
- Graphics.Svg.Types: mask :: HasMask c_aE0u => Lens' c_aE0u Mask
+ Graphics.Svg.Types: mask :: HasMask c_aFfV => Lens' c_aFfV Mask
- Graphics.Svg.Types: maskContent :: HasMask c_aE0u => Lens' c_aE0u [Tree]
+ Graphics.Svg.Types: maskContent :: HasMask c_aFfV => Lens' c_aFfV [Tree]
- Graphics.Svg.Types: maskContentUnits :: HasMask c_aE0u => Lens' c_aE0u CoordinateUnits
+ Graphics.Svg.Types: maskContentUnits :: HasMask c_aFfV => Lens' c_aFfV CoordinateUnits
- Graphics.Svg.Types: maskDrawAttributes :: HasMask c_aE0u => Lens' c_aE0u DrawAttributes
+ Graphics.Svg.Types: maskDrawAttributes :: HasMask c_aFfV => Lens' c_aFfV DrawAttributes
- Graphics.Svg.Types: maskHeight :: HasMask c_aE0u => Lens' c_aE0u Number
+ Graphics.Svg.Types: maskHeight :: HasMask c_aFfV => Lens' c_aFfV Number
- Graphics.Svg.Types: maskPosition :: HasMask c_aE0u => Lens' c_aE0u Point
+ Graphics.Svg.Types: maskPosition :: HasMask c_aFfV => Lens' c_aFfV Point
- Graphics.Svg.Types: maskRef :: HasDrawAttributes c_aAk7 => Lens' c_aAk7 (Last ElementRef)
+ Graphics.Svg.Types: maskRef :: HasDrawAttributes c_aAxm => Lens' c_aAxm (Last ElementRef)
- Graphics.Svg.Types: maskUnits :: HasMask c_aE0u => Lens' c_aE0u CoordinateUnits
+ Graphics.Svg.Types: maskUnits :: HasMask c_aFfV => Lens' c_aFfV CoordinateUnits
- Graphics.Svg.Types: maskWidth :: HasMask c_aE0u => Lens' c_aE0u Number
+ Graphics.Svg.Types: maskWidth :: HasMask c_aFfV => Lens' c_aFfV Number
- Graphics.Svg.Types: path :: HasPath c_aBad => Lens' c_aBad Path
+ Graphics.Svg.Types: path :: HasPath c_aBnM => Lens' c_aBnM Path
- Graphics.Svg.Types: pathDefinition :: HasPath c_aBad => Lens' c_aBad [PathCommand]
+ Graphics.Svg.Types: pathDefinition :: HasPath c_aBnM => Lens' c_aBnM [PathCommand]
- Graphics.Svg.Types: pathDrawAttributes :: HasPath c_aBad => Lens' c_aBad DrawAttributes
+ Graphics.Svg.Types: pathDrawAttributes :: HasPath c_aBnM => Lens' c_aBnM DrawAttributes
- Graphics.Svg.Types: pattern :: HasPattern c_aEfL => Lens' c_aEfL Pattern
+ Graphics.Svg.Types: pattern :: HasPattern c_aFvk => Lens' c_aFvk Pattern
- Graphics.Svg.Types: patternAspectRatio :: HasPattern c_aEfL => Lens' c_aEfL PreserveAspectRatio
+ Graphics.Svg.Types: patternAspectRatio :: HasPattern c_aFvk => Lens' c_aFvk PreserveAspectRatio
- Graphics.Svg.Types: patternDrawAttributes :: HasPattern c_aEfL => Lens' c_aEfL DrawAttributes
+ Graphics.Svg.Types: patternDrawAttributes :: HasPattern c_aFvk => Lens' c_aFvk DrawAttributes
- Graphics.Svg.Types: patternElements :: HasPattern c_aEfL => Lens' c_aEfL [Tree]
+ Graphics.Svg.Types: patternElements :: HasPattern c_aFvk => Lens' c_aFvk [Tree]
- Graphics.Svg.Types: patternHeight :: HasPattern c_aEfL => Lens' c_aEfL Number
+ Graphics.Svg.Types: patternHeight :: HasPattern c_aFvk => Lens' c_aFvk Number
- Graphics.Svg.Types: patternPos :: HasPattern c_aEfL => Lens' c_aEfL Point
+ Graphics.Svg.Types: patternPos :: HasPattern c_aFvk => Lens' c_aFvk Point
- Graphics.Svg.Types: patternUnit :: HasPattern c_aEfL => Lens' c_aEfL CoordinateUnits
+ Graphics.Svg.Types: patternUnit :: HasPattern c_aFvk => Lens' c_aFvk CoordinateUnits
- Graphics.Svg.Types: patternViewBox :: HasPattern c_aEfL => Lens' c_aEfL (Maybe (Double, Double, Double, Double))
+ Graphics.Svg.Types: patternViewBox :: HasPattern c_aFvk => Lens' c_aFvk (Maybe (Double, Double, Double, Double))
- Graphics.Svg.Types: patternWidth :: HasPattern c_aEfL => Lens' c_aEfL Number
+ Graphics.Svg.Types: patternWidth :: HasPattern c_aFvk => Lens' c_aFvk Number
- Graphics.Svg.Types: polyLine :: HasPolyLine c_aAOC => Lens' c_aAOC PolyLine
+ Graphics.Svg.Types: polyLine :: HasPolyLine c_aB1V => Lens' c_aB1V PolyLine
- Graphics.Svg.Types: polyLineDrawAttributes :: HasPolyLine c_aAOC => Lens' c_aAOC DrawAttributes
+ Graphics.Svg.Types: polyLineDrawAttributes :: HasPolyLine c_aB1V => Lens' c_aB1V DrawAttributes
- Graphics.Svg.Types: polyLinePoints :: HasPolyLine c_aAOC => Lens' c_aAOC [RPoint]
+ Graphics.Svg.Types: polyLinePoints :: HasPolyLine c_aB1V => Lens' c_aB1V [RPoint]
- Graphics.Svg.Types: polygon :: HasPolygon c_aASM => Lens' c_aASM Polygon
+ Graphics.Svg.Types: polygon :: HasPolygon c_aB69 => Lens' c_aB69 Polygon
- Graphics.Svg.Types: polygonDrawAttributes :: HasPolygon c_aASM => Lens' c_aASM DrawAttributes
+ Graphics.Svg.Types: polygonDrawAttributes :: HasPolygon c_aB69 => Lens' c_aB69 DrawAttributes
- Graphics.Svg.Types: polygonPoints :: HasPolygon c_aASM => Lens' c_aASM [RPoint]
+ Graphics.Svg.Types: polygonPoints :: HasPolygon c_aB69 => Lens' c_aB69 [RPoint]
- Graphics.Svg.Types: preserveAspectRatio :: HasPreserveAspectRatio c_azPm => Lens' c_azPm PreserveAspectRatio
+ Graphics.Svg.Types: preserveAspectRatio :: HasPreserveAspectRatio c_aA1Q => Lens' c_aA1Q PreserveAspectRatio
- Graphics.Svg.Types: radialGradient :: HasRadialGradient c_aDPs => Lens' c_aDPs RadialGradient
+ Graphics.Svg.Types: radialGradient :: HasRadialGradient c_aF4P => Lens' c_aF4P RadialGradient
- Graphics.Svg.Types: radialGradientCenter :: HasRadialGradient c_aDPs => Lens' c_aDPs Point
+ Graphics.Svg.Types: radialGradientCenter :: HasRadialGradient c_aF4P => Lens' c_aF4P Point
- Graphics.Svg.Types: radialGradientFocusX :: HasRadialGradient c_aDPs => Lens' c_aDPs (Maybe Number)
+ Graphics.Svg.Types: radialGradientFocusX :: HasRadialGradient c_aF4P => Lens' c_aF4P (Maybe Number)
- Graphics.Svg.Types: radialGradientFocusY :: HasRadialGradient c_aDPs => Lens' c_aDPs (Maybe Number)
+ Graphics.Svg.Types: radialGradientFocusY :: HasRadialGradient c_aF4P => Lens' c_aF4P (Maybe Number)
- Graphics.Svg.Types: radialGradientRadius :: HasRadialGradient c_aDPs => Lens' c_aDPs Number
+ Graphics.Svg.Types: radialGradientRadius :: HasRadialGradient c_aF4P => Lens' c_aF4P Number
- Graphics.Svg.Types: radialGradientSpread :: HasRadialGradient c_aDPs => Lens' c_aDPs Spread
+ Graphics.Svg.Types: radialGradientSpread :: HasRadialGradient c_aF4P => Lens' c_aF4P Spread
- Graphics.Svg.Types: radialGradientStops :: HasRadialGradient c_aDPs => Lens' c_aDPs [GradientStop]
+ Graphics.Svg.Types: radialGradientStops :: HasRadialGradient c_aF4P => Lens' c_aF4P [GradientStop]
- Graphics.Svg.Types: radialGradientTransform :: HasRadialGradient c_aDPs => Lens' c_aDPs [Transformation]
+ Graphics.Svg.Types: radialGradientTransform :: HasRadialGradient c_aF4P => Lens' c_aF4P [Transformation]
- Graphics.Svg.Types: radialGradientUnits :: HasRadialGradient c_aDPs => Lens' c_aDPs CoordinateUnits
+ Graphics.Svg.Types: radialGradientUnits :: HasRadialGradient c_aF4P => Lens' c_aF4P CoordinateUnits
- Graphics.Svg.Types: rectCornerRadius :: HasRectangle c_aB3z => Lens' c_aB3z (Number, Number)
+ Graphics.Svg.Types: rectCornerRadius :: HasRectangle c_aBh4 => Lens' c_aBh4 (Number, Number)
- Graphics.Svg.Types: rectDrawAttributes :: HasRectangle c_aB3z => Lens' c_aB3z DrawAttributes
+ Graphics.Svg.Types: rectDrawAttributes :: HasRectangle c_aBh4 => Lens' c_aBh4 DrawAttributes
- Graphics.Svg.Types: rectHeight :: HasRectangle c_aB3z => Lens' c_aB3z Number
+ Graphics.Svg.Types: rectHeight :: HasRectangle c_aBh4 => Lens' c_aBh4 Number
- Graphics.Svg.Types: rectUpperLeftCorner :: HasRectangle c_aB3z => Lens' c_aB3z Point
+ Graphics.Svg.Types: rectUpperLeftCorner :: HasRectangle c_aBh4 => Lens' c_aBh4 Point
- Graphics.Svg.Types: rectWidth :: HasRectangle c_aB3z => Lens' c_aB3z Number
+ Graphics.Svg.Types: rectWidth :: HasRectangle c_aBh4 => Lens' c_aBh4 Number
- Graphics.Svg.Types: rectangle :: HasRectangle c_aB3z => Lens' c_aB3z Rectangle
+ Graphics.Svg.Types: rectangle :: HasRectangle c_aBh4 => Lens' c_aBh4 Rectangle
- Graphics.Svg.Types: spanContent :: HasTextSpan c_aCzN => Lens' c_aCzN [TextSpanContent]
+ Graphics.Svg.Types: spanContent :: HasTextSpan c_aDUt => Lens' c_aDUt [TextSpanContent]
- Graphics.Svg.Types: spanDrawAttributes :: HasTextSpan c_aCzN => Lens' c_aCzN DrawAttributes
+ Graphics.Svg.Types: spanDrawAttributes :: HasTextSpan c_aDUt => Lens' c_aDUt DrawAttributes
- Graphics.Svg.Types: spanInfo :: HasTextSpan c_aCzN => Lens' c_aCzN TextInfo
+ Graphics.Svg.Types: spanInfo :: HasTextSpan c_aDUt => Lens' c_aDUt TextInfo
- Graphics.Svg.Types: strokeColor :: HasDrawAttributes c_aAk7 => Lens' c_aAk7 (Last Texture)
+ Graphics.Svg.Types: strokeColor :: HasDrawAttributes c_aAxm => Lens' c_aAxm (Last Texture)
- Graphics.Svg.Types: strokeDashArray :: HasDrawAttributes c_aAk7 => Lens' c_aAk7 (Last [Number])
+ Graphics.Svg.Types: strokeDashArray :: HasDrawAttributes c_aAxm => Lens' c_aAxm (Last [Number])
- Graphics.Svg.Types: strokeLineCap :: HasDrawAttributes c_aAk7 => Lens' c_aAk7 (Last Cap)
+ Graphics.Svg.Types: strokeLineCap :: HasDrawAttributes c_aAxm => Lens' c_aAxm (Last Cap)
- Graphics.Svg.Types: strokeLineJoin :: HasDrawAttributes c_aAk7 => Lens' c_aAk7 (Last LineJoin)
+ Graphics.Svg.Types: strokeLineJoin :: HasDrawAttributes c_aAxm => Lens' c_aAxm (Last LineJoin)
- Graphics.Svg.Types: strokeMiterLimit :: HasDrawAttributes c_aAk7 => Lens' c_aAk7 (Last Double)
+ Graphics.Svg.Types: strokeMiterLimit :: HasDrawAttributes c_aAxm => Lens' c_aAxm (Last Double)
- Graphics.Svg.Types: strokeOffset :: HasDrawAttributes c_aAk7 => Lens' c_aAk7 (Last Number)
+ Graphics.Svg.Types: strokeOffset :: HasDrawAttributes c_aAxm => Lens' c_aAxm (Last Number)
- Graphics.Svg.Types: strokeOpacity :: HasDrawAttributes c_aAk7 => Lens' c_aAk7 (Maybe Float)
+ Graphics.Svg.Types: strokeOpacity :: HasDrawAttributes c_aAxm => Lens' c_aAxm (Maybe Float)
- Graphics.Svg.Types: strokeWidth :: HasDrawAttributes c_aAk7 => Lens' c_aAk7 (Last Number)
+ Graphics.Svg.Types: strokeWidth :: HasDrawAttributes c_aAxm => Lens' c_aAxm (Last Number)
- Graphics.Svg.Types: styleRules :: HasDocument c_aEuT => Lens' c_aEuT [CssRule]
+ Graphics.Svg.Types: styleRules :: HasDocument c_aFKR => Lens' c_aFKR [CssRule]
- Graphics.Svg.Types: text :: HasText c_aCOF => Lens' c_aCOF Text
+ Graphics.Svg.Types: text :: HasText c_aE9F => Lens' c_aE9F Text
- Graphics.Svg.Types: textAdjust :: HasText c_aCOF => Lens' c_aCOF TextAdjust
+ Graphics.Svg.Types: textAdjust :: HasText c_aE9F => Lens' c_aE9F TextAdjust
- Graphics.Svg.Types: textAnchor :: HasDrawAttributes c_aAk7 => Lens' c_aAk7 (Last TextAnchor)
+ Graphics.Svg.Types: textAnchor :: HasDrawAttributes c_aAxm => Lens' c_aAxm (Last TextAnchor)
- Graphics.Svg.Types: textInfo :: HasTextInfo c_aCqe => Lens' c_aCqe TextInfo
+ Graphics.Svg.Types: textInfo :: HasTextInfo c_aDKM => Lens' c_aDKM TextInfo
- Graphics.Svg.Types: textInfoDX :: HasTextInfo c_aCqe => Lens' c_aCqe [Number]
+ Graphics.Svg.Types: textInfoDX :: HasTextInfo c_aDKM => Lens' c_aDKM [Number]
- Graphics.Svg.Types: textInfoDY :: HasTextInfo c_aCqe => Lens' c_aCqe [Number]
+ Graphics.Svg.Types: textInfoDY :: HasTextInfo c_aDKM => Lens' c_aDKM [Number]
- Graphics.Svg.Types: textInfoLength :: HasTextInfo c_aCqe => Lens' c_aCqe (Maybe Number)
+ Graphics.Svg.Types: textInfoLength :: HasTextInfo c_aDKM => Lens' c_aDKM (Maybe Number)
- Graphics.Svg.Types: textInfoRotate :: HasTextInfo c_aCqe => Lens' c_aCqe [Double]
+ Graphics.Svg.Types: textInfoRotate :: HasTextInfo c_aDKM => Lens' c_aDKM [Double]
- Graphics.Svg.Types: textInfoX :: HasTextInfo c_aCqe => Lens' c_aCqe [Number]
+ Graphics.Svg.Types: textInfoX :: HasTextInfo c_aDKM => Lens' c_aDKM [Number]
- Graphics.Svg.Types: textInfoY :: HasTextInfo c_aCqe => Lens' c_aCqe [Number]
+ Graphics.Svg.Types: textInfoY :: HasTextInfo c_aDKM => Lens' c_aDKM [Number]
- Graphics.Svg.Types: textPath :: HasTextPath c_aCHq => Lens' c_aCHq TextPath
+ Graphics.Svg.Types: textPath :: HasTextPath c_aE2i => Lens' c_aE2i TextPath
- Graphics.Svg.Types: textPathData :: HasTextPath c_aCHq => Lens' c_aCHq [PathCommand]
+ Graphics.Svg.Types: textPathData :: HasTextPath c_aE2i => Lens' c_aE2i [PathCommand]
- Graphics.Svg.Types: textPathMethod :: HasTextPath c_aCHq => Lens' c_aCHq TextPathMethod
+ Graphics.Svg.Types: textPathMethod :: HasTextPath c_aE2i => Lens' c_aE2i TextPathMethod
- Graphics.Svg.Types: textPathName :: HasTextPath c_aCHq => Lens' c_aCHq String
+ Graphics.Svg.Types: textPathName :: HasTextPath c_aE2i => Lens' c_aE2i String
- Graphics.Svg.Types: textPathSpacing :: HasTextPath c_aCHq => Lens' c_aCHq TextPathSpacing
+ Graphics.Svg.Types: textPathSpacing :: HasTextPath c_aE2i => Lens' c_aE2i TextPathSpacing
- Graphics.Svg.Types: textPathStartOffset :: HasTextPath c_aCHq => Lens' c_aCHq Number
+ Graphics.Svg.Types: textPathStartOffset :: HasTextPath c_aE2i => Lens' c_aE2i Number
- Graphics.Svg.Types: textRoot :: HasText c_aCOF => Lens' c_aCOF TextSpan
+ Graphics.Svg.Types: textRoot :: HasText c_aE9F => Lens' c_aE9F TextSpan
- Graphics.Svg.Types: textSpan :: HasTextSpan c_aCzN => Lens' c_aCzN TextSpan
+ Graphics.Svg.Types: textSpan :: HasTextSpan c_aDUt => Lens' c_aDUt TextSpan
- Graphics.Svg.Types: transform :: HasDrawAttributes c_aAk7 => Lens' c_aAk7 (Maybe [Transformation])
+ Graphics.Svg.Types: transform :: HasDrawAttributes c_aAxm => Lens' c_aAxm (Maybe [Transformation])
- Graphics.Svg.Types: use :: HasUse c_aCgY => Lens' c_aCgY Use
+ Graphics.Svg.Types: use :: HasUse c_aDBG => Lens' c_aDBG Use
- Graphics.Svg.Types: useBase :: HasUse c_aCgY => Lens' c_aCgY Point
+ Graphics.Svg.Types: useBase :: HasUse c_aDBG => Lens' c_aDBG Point
- Graphics.Svg.Types: useDrawAttributes :: HasUse c_aCgY => Lens' c_aCgY DrawAttributes
+ Graphics.Svg.Types: useDrawAttributes :: HasUse c_aDBG => Lens' c_aDBG DrawAttributes
- Graphics.Svg.Types: useHeight :: HasUse c_aCgY => Lens' c_aCgY (Maybe Number)
+ Graphics.Svg.Types: useHeight :: HasUse c_aDBG => Lens' c_aDBG (Maybe Number)
- Graphics.Svg.Types: useName :: HasUse c_aCgY => Lens' c_aCgY String
+ Graphics.Svg.Types: useName :: HasUse c_aDBG => Lens' c_aDBG String
- Graphics.Svg.Types: useWidth :: HasUse c_aCgY => Lens' c_aCgY (Maybe Number)
+ Graphics.Svg.Types: useWidth :: HasUse c_aDBG => Lens' c_aDBG (Maybe Number)
- Graphics.Svg.Types: viewBox :: HasDocument c_aEuT => Lens' c_aEuT (Maybe (Double, Double, Double, Double))
+ Graphics.Svg.Types: viewBox :: HasDocument c_aFKR => Lens' c_aFKR (Maybe (Double, Double, Double, Double))
- Graphics.Svg.Types: width :: HasDocument c_aEuT => Lens' c_aEuT (Maybe Number)
+ Graphics.Svg.Types: width :: HasDocument c_aFKR => Lens' c_aFKR (Maybe Number)
Files
- changelog.md +64/−60
- src/Graphics/Svg.hs +112/−111
- src/Graphics/Svg/ColorParser.hs +111/−111
- src/Graphics/Svg/CssParser.hs +257/−257
- src/Graphics/Svg/CssTypes.hs +271/−271
- src/Graphics/Svg/PathParser.hs +260/−231
- src/Graphics/Svg/Types.hs +1624/−1521
- src/Graphics/Svg/XmlParser.hs +1227/−1164
- svg-tree.cabal +61/−61
changelog.md view
@@ -1,60 +1,64 @@--*-change-log-*- - -v0.5.1.2: September 2016 - * Fix path parsing with white space prefix - -v0.5.1.1: May 2016 - * Fix: GHC 8.0 compatibility - -v0.5.1: March 2016 - * Fix: serialization of multi criteria css selector. - -v0.5: March 2016: - * Adding: preserveAspectRatio attribute - * Fix: Application of CSS rules with indirect parent/child relation. - -v0.4.2: March 2016 - * Enhancement: avoiding serializatinon of empty class attribute - * Fix: incorrect deserialization of complex CSS - * Fix: Really fixing duplicate ID with serialization - -v0.4.1: February 2016 - * Fix: fixing duplicate ID with serialization - -v0.4: February 2016 - * Breaking change: viewbox types are no longer Int - but double, sneakingly passed in v0.3.2.2. This - version acknoweledge this change - -V0.3.2.2 February 2016 (Deprecated) - * Fix: Bad serialization of some None constructors. - -v0.3.2.1 October 2015 - * Fix: Don't add '#' for <img> serialization - -v0.3.2 August 2015 - * Fix: allow compilation with GHC 7.4 - -v0.3.1 May 2015 - * Fix: Bumping lens dependency and removing upper bound. - -v0.3 April 2015 - * Breaking change: Switching all the numeric types associated to geometry - to Double precision (thx to Kasbah) - -v0.2 April 2015 - * Fix: Differentiating opacity & fill-opacity, as they are - semantically deferent (BREAKING CHANGE!) - -v0.1.1 April 2015 - * Fix: Bumping lens bounds - -v0.1.0.2 March 2015 - * Fix: Bumping lens bounds - -v0.1.0.1 - * Fix: Lowering some lower bounds - -v0.1 - * Initial release - +-*-change-log-*-++v0.6: September 2016+ * Add SVG 2.0 gradient mesh+ * Add gradient opacity parsing++v0.5.1.2: September 2016+ * Fix path parsing with white space prefix++v0.5.1.1: May 2016+ * Fix: GHC 8.0 compatibility++v0.5.1: March 2016+ * Fix: serialization of multi criteria css selector.++v0.5: March 2016:+ * Adding: preserveAspectRatio attribute+ * Fix: Application of CSS rules with indirect parent/child relation.++v0.4.2: March 2016+ * Enhancement: avoiding serializatinon of empty class attribute+ * Fix: incorrect deserialization of complex CSS+ * Fix: Really fixing duplicate ID with serialization++v0.4.1: February 2016+ * Fix: fixing duplicate ID with serialization++v0.4: February 2016+ * Breaking change: viewbox types are no longer Int+ but double, sneakingly passed in v0.3.2.2. This+ version acknoweledge this change++V0.3.2.2 February 2016 (Deprecated)+ * Fix: Bad serialization of some None constructors.++v0.3.2.1 October 2015+ * Fix: Don't add '#' for <img> serialization++v0.3.2 August 2015+ * Fix: allow compilation with GHC 7.4++v0.3.1 May 2015+ * Fix: Bumping lens dependency and removing upper bound.++v0.3 April 2015+ * Breaking change: Switching all the numeric types associated to geometry+ to Double precision (thx to Kasbah)++v0.2 April 2015+ * Fix: Differentiating opacity & fill-opacity, as they are+ semantically deferent (BREAKING CHANGE!)++v0.1.1 April 2015+ * Fix: Bumping lens bounds++v0.1.0.2 March 2015+ * Fix: Bumping lens bounds++v0.1.0.1+ * Fix: Lowering some lower bounds++v0.1+ * Initial release+
src/Graphics/Svg.hs view
@@ -1,111 +1,112 @@-{-# LANGUAGE CPP #-} --- | Module providing basic input/output for the SVG document, --- for document building, please refer to Graphics.Svg.Types. -module Graphics.Svg ( -- * Saving/Loading functions - loadSvgFile - , parseSvgFile - , xmlOfDocument - , saveXmlFile - - -- * Manipulation functions - , cssApply - , cssRulesOfText - , applyCSSRules - , resolveUses - - -- * Type definitions - , module Graphics.Svg.Types - ) where - -#if !MIN_VERSION_base(4,8,0) -import Control.Applicative( (<$>) ) -#endif - -import Data.List( foldl' ) -import qualified Data.ByteString as B -import qualified Data.Map as M -import qualified Data.Text as T -import Text.XML.Light.Input( parseXMLDoc ) -import Text.XML.Light.Output( ppcTopElement, prettyConfigPP ) -import Control.Lens - -import Graphics.Svg.Types -import Graphics.Svg.CssTypes -import Graphics.Svg.CssParser( cssRulesOfText ) -import Graphics.Svg.XmlParser - -{-import Graphics.Svg.CssParser-} - --- | Try to load an svg file on disc and parse it as --- a SVG Document. -loadSvgFile :: FilePath -> IO (Maybe Document) -loadSvgFile filename = - parseSvgFile filename <$> B.readFile filename - --- | Parse an in-memory SVG file -parseSvgFile :: FilePath -- ^ Source path/URL of the document, used - -- to resolve relative links. - -> B.ByteString - -> Maybe Document -parseSvgFile filename fileContent = - parseXMLDoc fileContent >>= unparseDocument filename - --- | Save a svg Document on a file on disk. -saveXmlFile :: FilePath -> Document -> IO () -saveXmlFile filePath = - writeFile filePath . ppcTopElement prettyConfigPP . xmlOfDocument - -cssDeclApplyer :: DrawAttributes -> CssDeclaration - -> DrawAttributes -cssDeclApplyer value (CssDeclaration txt elems) = - case lookup txt cssUpdaters of - Nothing -> value - Just f -> f value elems - where - cssUpdaters = [(T.pack $ _attributeName n, u) | - (n, u) <- drawAttributesList] - --- | Rewrite a SVG Tree using some CSS rules. --- --- This action will propagate the definition of the --- css directly in each matched element. -cssApply :: [CssRule] -> Tree -> Tree -cssApply rules = zipTree go where - go [] = None - go ([]:_) = None - go context@((t:_):_) = t & drawAttr .~ attr' - where - matchingDeclarations = - findMatchingDeclarations rules context - attr = view drawAttr t - attr' = foldl' cssDeclApplyer attr matchingDeclarations - --- | For every 'use' tag, try to resolve the geometry associated --- with it and place it in the scene Tree. It is important to --- resolve the 'use' tag before applying the CSS rules, as some --- rules may apply some elements matching the children of "use". -resolveUses :: Document -> Document -resolveUses doc = - doc { _elements = mapTree fetchUses <$> _elements doc } - where - fetchUses (UseTree useInfo _) = UseTree useInfo $ search useInfo - fetchUses a = a - - search nfo = maybe Nothing geometryExtract found where - found = M.lookup (_useName nfo) $ _definitions doc - - geometryExtract c = case c of - ElementLinearGradient _ -> Nothing - ElementRadialGradient _ -> Nothing - ElementMask _ -> Nothing - ElementClipPath _ -> Nothing - ElementGeometry t -> Just t - ElementPattern _ -> Nothing - ElementMarker _ -> Nothing - --- | Rewrite the document by applying the CSS rules embedded --- inside it. -applyCSSRules :: Document -> Document -applyCSSRules doc = doc - { _elements = cssApply (_styleRules doc) <$> _elements doc } - +{-# LANGUAGE CPP #-}+-- | Module providing basic input/output for the SVG document,+-- for document building, please refer to Graphics.Svg.Types.+module Graphics.Svg ( -- * Saving/Loading functions+ loadSvgFile+ , parseSvgFile+ , xmlOfDocument+ , saveXmlFile++ -- * Manipulation functions+ , cssApply+ , cssRulesOfText+ , applyCSSRules+ , resolveUses++ -- * Type definitions+ , module Graphics.Svg.Types+ ) where++#if !MIN_VERSION_base(4,8,0)+import Control.Applicative( (<$>) )+#endif++import Data.List( foldl' )+import qualified Data.ByteString as B+import qualified Data.Map as M+import qualified Data.Text as T+import Text.XML.Light.Input( parseXMLDoc )+import Text.XML.Light.Output( ppcTopElement, prettyConfigPP )+import Control.Lens++import Graphics.Svg.Types+import Graphics.Svg.CssTypes+import Graphics.Svg.CssParser( cssRulesOfText )+import Graphics.Svg.XmlParser++{-import Graphics.Svg.CssParser-}++-- | Try to load an svg file on disc and parse it as+-- a SVG Document.+loadSvgFile :: FilePath -> IO (Maybe Document)+loadSvgFile filename =+ parseSvgFile filename <$> B.readFile filename++-- | Parse an in-memory SVG file+parseSvgFile :: FilePath -- ^ Source path/URL of the document, used+ -- to resolve relative links.+ -> B.ByteString+ -> Maybe Document+parseSvgFile filename fileContent =+ parseXMLDoc fileContent >>= unparseDocument filename++-- | Save a svg Document on a file on disk.+saveXmlFile :: FilePath -> Document -> IO ()+saveXmlFile filePath =+ writeFile filePath . ppcTopElement prettyConfigPP . xmlOfDocument++cssDeclApplyer :: DrawAttributes -> CssDeclaration+ -> DrawAttributes+cssDeclApplyer value (CssDeclaration txt elems) =+ case lookup txt cssUpdaters of+ Nothing -> value+ Just f -> f value elems+ where+ cssUpdaters = [(T.pack $ _attributeName n, u) |+ (n, u) <- drawAttributesList]++-- | Rewrite a SVG Tree using some CSS rules.+--+-- This action will propagate the definition of the+-- css directly in each matched element.+cssApply :: [CssRule] -> Tree -> Tree+cssApply rules = zipTree go where+ go [] = None+ go ([]:_) = None+ go context@((t:_):_) = t & drawAttr .~ attr'+ where+ matchingDeclarations =+ findMatchingDeclarations rules context+ attr = view drawAttr t+ attr' = foldl' cssDeclApplyer attr matchingDeclarations++-- | For every 'use' tag, try to resolve the geometry associated+-- with it and place it in the scene Tree. It is important to+-- resolve the 'use' tag before applying the CSS rules, as some+-- rules may apply some elements matching the children of "use".+resolveUses :: Document -> Document+resolveUses doc =+ doc { _elements = mapTree fetchUses <$> _elements doc }+ where+ fetchUses (UseTree useInfo _) = UseTree useInfo $ search useInfo+ fetchUses a = a++ search nfo = maybe Nothing geometryExtract found where+ found = M.lookup (_useName nfo) $ _definitions doc++ geometryExtract c = case c of+ ElementLinearGradient _ -> Nothing+ ElementRadialGradient _ -> Nothing+ ElementMeshGradient _ -> Nothing+ ElementMask _ -> Nothing+ ElementClipPath _ -> Nothing+ ElementGeometry t -> Just t+ ElementPattern _ -> Nothing+ ElementMarker _ -> Nothing++-- | Rewrite the document by applying the CSS rules embedded+-- inside it.+applyCSSRules :: Document -> Document+applyCSSRules doc = doc+ { _elements = cssApply (_styleRules doc) <$> _elements doc }+
src/Graphics/Svg/ColorParser.hs view
@@ -1,111 +1,111 @@-{-# LANGUAGE CPP #-} -{-# LANGUAGE OverloadedStrings #-} -module Graphics.Svg.ColorParser( colorParser - , colorSerializer - , textureParser - , textureSerializer - , urlRef - ) where - -#if !MIN_VERSION_base(4,8,0) -import Control.Applicative( (<*>), (<*), (*>), (<$>), (<$) ) -#endif - -import Data.Bits( (.|.), unsafeShiftL ) -import Control.Applicative( (<|>) ) -import Data.Attoparsec.Text - ( Parser - , string - , skipSpace - , satisfy - , inClass - , takeWhile1 - , option - , char - , digit - , letter - , many1 - , scientific - ) - -import Text.Printf( printf ) -import Data.Scientific( toRealFloat ) -import Codec.Picture( PixelRGBA8( .. ) ) -import Data.Word( Word8 ) -import Graphics.Svg.NamedColors -import Graphics.Svg.Types -import qualified Data.Map as M - -commaWsp :: Parser () -commaWsp = skipSpace *> option () (string "," *> return ()) - <* skipSpace - - -num :: Parser Double -num = realToFrac <$> (skipSpace *> plusMinus <* skipSpace) - where doubleNumber :: Parser Double - doubleNumber = toRealFloat <$> scientific - - plusMinus = negate <$ string "-" <*> doubleNumber - <|> string "+" *> doubleNumber - <|> doubleNumber - -colorSerializer :: PixelRGBA8 -> String -colorSerializer (PixelRGBA8 r g b _) = printf "#%02X%02X%02X" r g b - -colorParser :: Parser PixelRGBA8 -colorParser = rgbColor - <|> (string "#" *> (color <|> colorReduced)) - <|> namedColor - where - charRange c1 c2 = - (\c -> fromIntegral $ fromEnum c - fromEnum c1) <$> satisfy (\v -> c1 <= v && v <= c2) - black = PixelRGBA8 0 0 0 255 - - hexChar :: Parser Word8 - hexChar = charRange '0' '9' - <|> ((+ 10) <$> charRange 'a' 'f') - <|> ((+ 10) <$> charRange 'A' 'F') - - namedColor = do - str <- takeWhile1 (inClass "a-z") - return $ M.findWithDefault black str svgNamedColors - - percentToWord v = floor $ v * (255 / 100) - - numPercent = ((percentToWord <$> num) <* string "%") - <|> (floor <$> num) - - hexByte = (\h1 h2 -> h1 `unsafeShiftL` 4 .|. h2) - <$> hexChar <*> hexChar - - color = (\r g b -> PixelRGBA8 r g b 255) - <$> hexByte <*> hexByte <*> hexByte - rgbColor = (\r g b -> PixelRGBA8 r g b 255) - <$> (string "rgb(" *> numPercent) - <*> (commaWsp *> numPercent) - <*> (commaWsp *> numPercent <* skipSpace <* string ")") - - colorReduced = - (\r g b -> PixelRGBA8 (r * 17) (g * 17) (b * 17) 255) - <$> hexChar <*> hexChar <*> hexChar - - -textureSerializer :: Texture -> String -textureSerializer (ColorRef px) = colorSerializer px -textureSerializer (TextureRef str) = printf "url(#%s)" str -textureSerializer FillNone = "none" - -urlRef :: Parser String -urlRef = string "url(" *> skipSpace *> - char '#' *> many1 (letter <|> digit) - <* skipSpace <* char ')' <* skipSpace - - -textureParser :: Parser Texture -textureParser = - none <|> (TextureRef <$> urlRef) - <|> (ColorRef <$> colorParser) - where - none = FillNone <$ string "none" - +{-# LANGUAGE CPP #-}+{-# LANGUAGE OverloadedStrings #-}+module Graphics.Svg.ColorParser( colorParser+ , colorSerializer+ , textureParser+ , textureSerializer+ , urlRef+ ) where++#if !MIN_VERSION_base(4,8,0)+import Control.Applicative( (<*>), (<*), (*>), (<$>), (<$) )+#endif++import Data.Bits( (.|.), unsafeShiftL )+import Control.Applicative( (<|>) )+import Data.Attoparsec.Text+ ( Parser+ , string+ , skipSpace+ , satisfy+ , inClass+ , takeWhile1+ , option+ , char+ , digit+ , letter+ , many1+ , scientific+ )++import Text.Printf( printf )+import Data.Scientific( toRealFloat )+import Codec.Picture( PixelRGBA8( .. ) )+import Data.Word( Word8 )+import Graphics.Svg.NamedColors+import Graphics.Svg.Types+import qualified Data.Map as M++commaWsp :: Parser ()+commaWsp = skipSpace *> option () (string "," *> return ())+ <* skipSpace+++num :: Parser Double+num = realToFrac <$> (skipSpace *> plusMinus <* skipSpace)+ where doubleNumber :: Parser Double+ doubleNumber = toRealFloat <$> scientific++ plusMinus = negate <$ string "-" <*> doubleNumber+ <|> string "+" *> doubleNumber+ <|> doubleNumber++colorSerializer :: PixelRGBA8 -> String+colorSerializer (PixelRGBA8 r g b _) = printf "#%02X%02X%02X" r g b++colorParser :: Parser PixelRGBA8+colorParser = rgbColor+ <|> (string "#" *> (color <|> colorReduced))+ <|> namedColor+ where+ charRange c1 c2 =+ (\c -> fromIntegral $ fromEnum c - fromEnum c1) <$> satisfy (\v -> c1 <= v && v <= c2)+ black = PixelRGBA8 0 0 0 255++ hexChar :: Parser Word8+ hexChar = charRange '0' '9'+ <|> ((+ 10) <$> charRange 'a' 'f')+ <|> ((+ 10) <$> charRange 'A' 'F')++ namedColor = do+ str <- takeWhile1 (inClass "a-z")+ return $ M.findWithDefault black str svgNamedColors++ percentToWord v = floor $ v * (255 / 100)++ numPercent = ((percentToWord <$> num) <* string "%")+ <|> (floor <$> num)++ hexByte = (\h1 h2 -> h1 `unsafeShiftL` 4 .|. h2)+ <$> hexChar <*> hexChar++ color = (\r g b -> PixelRGBA8 r g b 255)+ <$> hexByte <*> hexByte <*> hexByte+ rgbColor = (\r g b -> PixelRGBA8 r g b 255)+ <$> (string "rgb(" *> numPercent)+ <*> (commaWsp *> numPercent)+ <*> (commaWsp *> numPercent <* skipSpace <* string ")")++ colorReduced =+ (\r g b -> PixelRGBA8 (r * 17) (g * 17) (b * 17) 255)+ <$> hexChar <*> hexChar <*> hexChar+++textureSerializer :: Texture -> String+textureSerializer (ColorRef px) = colorSerializer px+textureSerializer (TextureRef str) = printf "url(#%s)" str+textureSerializer FillNone = "none"++urlRef :: Parser String+urlRef = string "url(" *> skipSpace *>+ char '#' *> many1 (letter <|> digit)+ <* skipSpace <* char ')' <* skipSpace+++textureParser :: Parser Texture+textureParser =+ none <|> (TextureRef <$> urlRef)+ <|> (ColorRef <$> colorParser)+ where+ none = FillNone <$ string "none"+
src/Graphics/Svg/CssParser.hs view
@@ -1,257 +1,257 @@-{-# LANGUAGE CPP #-} -{-# LANGUAGE OverloadedStrings #-} -{-# LANGUAGE PatternGuards #-} -module Graphics.Svg.CssParser - ( CssElement( .. ) - , complexNumber - , declaration - , ruleSet - , styleString - , dashArray - , numberList - , num - , cssRulesOfText - ) - where - -#if !MIN_VERSION_base(4,8,0) -import Control.Applicative( (<*>), (<*), (*>) - , (<$>), (<$) - , pure - ) -#endif - -import Control.Applicative( (<|>) - , many - ) -import Data.Attoparsec.Text - ( Parser - , double - , string - , skipSpace - , letter - , char - , digit - {-, skip-} - , sepBy1 - , (<?>) - , skipMany - , notChar - , parseOnly - ) -import qualified Data.Attoparsec.Text as AT - -import Data.Attoparsec.Combinator - ( option - , sepBy - {-, sepBy1-} - , many1 - ) - -import Codec.Picture( PixelRGBA8( .. ) ) -import Graphics.Svg.Types -import Graphics.Svg.NamedColors( svgNamedColors ) -import Graphics.Svg.ColorParser( colorParser ) -import Graphics.Svg.CssTypes -import qualified Data.Text as T -import qualified Data.Map as M -{-import Graphics.Rasterific.Linear( V2( V2 ) )-} -{-import Graphics.Rasterific.Transformations-} - -num :: Parser Double -num = realToFrac <$> (skipSpace *> plusMinus <* skipSpace) - where doubleNumber = char '.' *> (scale <$> double) - <|> double - - scalingCoeff n = 10 ^ digitCount - where digitCount :: Int - digitCount = ceiling . logBase 10 $ abs n - - scale n = n / scalingCoeff n - - plusMinus = negate <$ string "-" <*> doubleNumber - <|> string "+" *> doubleNumber - <|> doubleNumber - - -ident :: Parser T.Text -ident = - (\f c -> f . T.cons c . T.pack) - <$> trailingSub - <*> nmstart <*> nmchar - where - trailingSub = option id $ T.cons '-' <$ char '-' - underscore = char '_' - nmstart = letter <|> underscore - nmchar = many (letter <|> digit <|> underscore <|> char '-') - -str :: Parser T.Text -str = char '"' *> AT.takeWhile (/= '"') <* char '"' <* skipSpace - <?> "str" - -between :: Char -> Char -> Parser a -> Parser a -between o e p = - (skipSpace *> - char o *> skipSpace *> p - <* skipSpace <* char e <* skipSpace) - <?> ("between " ++ [o, e]) - -bracket :: Parser a -> Parser a -bracket = between '[' ']' - - -comment :: Parser () -comment = string "/*" *> toStar *> skipSpace - where - toStar = skipMany (notChar '*') *> char '*' *> testEnd - testEnd = (() <$ char '/') <|> toStar - -cleanSpace :: Parser () -cleanSpace = skipSpace <* many comment - --- | combinator: '+' S* | '>' S* -combinator :: Parser CssSelector -combinator = parse <* cleanSpace where - parse = Nearby <$ char '+' - <|> DirectChildren <$ char '>' - <?> "combinator" - --- unary_operator : '-' | '+' ; - -commaWsp :: Parser Char -commaWsp = skipSpace *> option ',' (char ',') <* skipSpace - -ruleSet :: Parser CssRule -ruleSet = cleanSpace *> rule where - rule = CssRule - <$> selector `sepBy1` commaWsp - <*> (between '{' '}' styleString) - <?> "cssrule" - -styleString :: Parser [CssDeclaration] -styleString = ((cleanSpace *> declaration) `sepBy` semiWsp) <* mayWsp - <?> "styleString" - where semiWsp = skipSpace *> char ';' <* skipSpace - mayWsp = option ';' semiWsp - -selector :: Parser [CssSelector] -selector = (:) - <$> (AllOf <$> simpleSelector <* skipSpace <?> "firstpart:(") - <*> ((next <|> return []) <?> "secondpart") - <?> "selector" - where - combOpt :: Parser ([CssSelector] -> [CssSelector]) - - combOpt = cleanSpace *> option id ((:) <$> combinator) - next :: Parser [CssSelector] - next = id <$> combOpt <*> selector - -simpleSelector :: Parser [CssDescriptor] -simpleSelector = (:) <$> elementName <*> many whole - <|> (many1 whole <?> "inmany") - <?> "simple selector" - where - whole = pseudo <|> hash <|> classParser <|> attrib - <?> "whole" - pseudo = char ':' *> (OfPseudoClass <$> ident) - <?> "pseudo" - hash = char '#' *> (OfId <$> ident) - <?> "hash" - classParser = char '.' *> (OfClass <$> ident) - <?> "classParser" - - elementName = el <* skipSpace <?> "elementName" - where el = (OfName <$> ident) - <|> AnyElem <$ char '*' - - attrib = bracket - (WithAttrib <$> ident <*> (char '=' *> skipSpace *> (ident <|> str)) - <?> "attrib") - -declaration :: Parser CssDeclaration -declaration = - CssDeclaration <$> property - <*> (char ':' - *> cleanSpace - *> many1 expr - <* prio - ) - <?> "declaration" - where - property = (ident <* cleanSpace) <?> "property" - prio = option "" $ string "!important" - -operator :: Parser CssElement -operator = skipSpace *> op <* skipSpace - where - op = CssOpSlash <$ char '/' - <|> CssOpComa <$ char ',' - <?> "operator" - -expr :: Parser [CssElement] -expr = ((:) <$> term <*> (concat <$> many termOp)) - <?> "expr" - where - op = option (:[]) $ (\a b -> [a, b]) <$> operator - termOp = ($) <$> op <*> term - -dashArray :: Parser [Number] -dashArray = skipSpace *> (complexNumber `sepBy1` commaWsp) - -numberList :: Parser [Double] -numberList = skipSpace *> (num `sepBy1` commaWsp) - -complexNumber :: Parser Number -complexNumber = do - n <- num - (Percent (n / 100) <$ char '%') - <|> (Em n <$ string "em") - <|> (Mm n <$ string "mm") - <|> (Cm n <$ string "cm") - <|> (Point n <$ string "pt") - <|> (Pc n <$ string "pc") - <|> (Px n <$ string "px") - <|> (Inches n <$ string "in") - <|> pure (Num n) - -term :: Parser CssElement -term = checkRgb <$> function - <|> (CssNumber <$> complexNumber) - <|> (CssString <$> str) - <|> (checkNamedColor <$> ident) - <|> (CssColor <$> colorParser) - where - comma = skipSpace *> char ',' <* skipSpace - checkNamedColor n - | Just c <- M.lookup n svgNamedColors = CssColor c - | otherwise = CssIdent n - - ref = char '#' *> ident - - checkRgb (CssFunction "rgb" - [CssNumber r, CssNumber g, CssNumber b]) = - CssColor $ PixelRGBA8 (to r) (to g) (to b) 255 - where clamp = max 0 . min 255 - to (Num n) = floor $ clamp n - to (Px n) = floor $ clamp n - to (Percent p) = floor . clamp $ p * 255 - to (Em c) = floor $ clamp c - to (Pc n) = floor $ clamp n - to (Mm n) = floor $ clamp n - to (Cm n) = floor $ clamp n - to (Point n) = floor $ clamp n - to (Inches n) = floor $ clamp n - - checkRgb a = a - functionParam = (CssReference <$> ref) <|> term - - function = CssFunction - <$> ident <* char '(' - <*> (functionParam `sepBy` comma) <* char ')' <* skipSpace - --- | Parse CSS text into rules. -cssRulesOfText :: T.Text -> [CssRule] -cssRulesOfText txt = case parseOnly (many1 ruleSet) $ txt of - Left _ -> [] - Right rules -> rules - +{-# LANGUAGE CPP #-}+{-# LANGUAGE OverloadedStrings #-}+{-# LANGUAGE PatternGuards #-}+module Graphics.Svg.CssParser+ ( CssElement( .. )+ , complexNumber+ , declaration+ , ruleSet+ , styleString+ , dashArray+ , numberList+ , num+ , cssRulesOfText+ )+ where++#if !MIN_VERSION_base(4,8,0)+import Control.Applicative( (<*>), (<*), (*>)+ , (<$>), (<$)+ , pure+ )+#endif++import Control.Applicative( (<|>)+ , many+ )+import Data.Attoparsec.Text+ ( Parser+ , double+ , string+ , skipSpace+ , letter+ , char+ , digit+ {-, skip-}+ , sepBy1+ , (<?>)+ , skipMany+ , notChar+ , parseOnly+ )+import qualified Data.Attoparsec.Text as AT++import Data.Attoparsec.Combinator+ ( option+ , sepBy+ {-, sepBy1-}+ , many1+ )++import Codec.Picture( PixelRGBA8( .. ) )+import Graphics.Svg.Types+import Graphics.Svg.NamedColors( svgNamedColors )+import Graphics.Svg.ColorParser( colorParser )+import Graphics.Svg.CssTypes+import qualified Data.Text as T+import qualified Data.Map as M+{-import Graphics.Rasterific.Linear( V2( V2 ) )-}+{-import Graphics.Rasterific.Transformations-}++num :: Parser Double+num = realToFrac <$> (skipSpace *> plusMinus <* skipSpace)+ where doubleNumber = char '.' *> (scale <$> double)+ <|> double++ scalingCoeff n = 10 ^ digitCount+ where digitCount :: Int+ digitCount = ceiling . logBase 10 $ abs n++ scale n = n / scalingCoeff n++ plusMinus = negate <$ string "-" <*> doubleNumber+ <|> string "+" *> doubleNumber+ <|> doubleNumber+++ident :: Parser T.Text+ident =+ (\f c -> f . T.cons c . T.pack)+ <$> trailingSub+ <*> nmstart <*> nmchar+ where+ trailingSub = option id $ T.cons '-' <$ char '-'+ underscore = char '_'+ nmstart = letter <|> underscore+ nmchar = many (letter <|> digit <|> underscore <|> char '-')++str :: Parser T.Text+str = char '"' *> AT.takeWhile (/= '"') <* char '"' <* skipSpace+ <?> "str"++between :: Char -> Char -> Parser a -> Parser a+between o e p =+ (skipSpace *>+ char o *> skipSpace *> p+ <* skipSpace <* char e <* skipSpace)+ <?> ("between " ++ [o, e])++bracket :: Parser a -> Parser a+bracket = between '[' ']'+++comment :: Parser ()+comment = string "/*" *> toStar *> skipSpace+ where+ toStar = skipMany (notChar '*') *> char '*' *> testEnd+ testEnd = (() <$ char '/') <|> toStar++cleanSpace :: Parser ()+cleanSpace = skipSpace <* many comment++-- | combinator: '+' S* | '>' S*+combinator :: Parser CssSelector+combinator = parse <* cleanSpace where+ parse = Nearby <$ char '+'+ <|> DirectChildren <$ char '>'+ <?> "combinator"++-- unary_operator : '-' | '+' ;++commaWsp :: Parser Char+commaWsp = skipSpace *> option ',' (char ',') <* skipSpace++ruleSet :: Parser CssRule+ruleSet = cleanSpace *> rule where+ rule = CssRule+ <$> selector `sepBy1` commaWsp+ <*> (between '{' '}' styleString)+ <?> "cssrule"++styleString :: Parser [CssDeclaration]+styleString = ((cleanSpace *> declaration) `sepBy` semiWsp) <* mayWsp+ <?> "styleString"+ where semiWsp = skipSpace *> char ';' <* skipSpace+ mayWsp = option ';' semiWsp++selector :: Parser [CssSelector]+selector = (:)+ <$> (AllOf <$> simpleSelector <* skipSpace <?> "firstpart:(")+ <*> ((next <|> return []) <?> "secondpart")+ <?> "selector"+ where+ combOpt :: Parser ([CssSelector] -> [CssSelector])++ combOpt = cleanSpace *> option id ((:) <$> combinator)+ next :: Parser [CssSelector]+ next = id <$> combOpt <*> selector++simpleSelector :: Parser [CssDescriptor]+simpleSelector = (:) <$> elementName <*> many whole+ <|> (many1 whole <?> "inmany")+ <?> "simple selector"+ where+ whole = pseudo <|> hash <|> classParser <|> attrib+ <?> "whole"+ pseudo = char ':' *> (OfPseudoClass <$> ident)+ <?> "pseudo"+ hash = char '#' *> (OfId <$> ident)+ <?> "hash"+ classParser = char '.' *> (OfClass <$> ident)+ <?> "classParser"++ elementName = el <* skipSpace <?> "elementName"+ where el = (OfName <$> ident)+ <|> AnyElem <$ char '*'++ attrib = bracket+ (WithAttrib <$> ident <*> (char '=' *> skipSpace *> (ident <|> str))+ <?> "attrib")++declaration :: Parser CssDeclaration+declaration =+ CssDeclaration <$> property+ <*> (char ':'+ *> cleanSpace+ *> many1 expr+ <* prio+ )+ <?> "declaration"+ where+ property = (ident <* cleanSpace) <?> "property"+ prio = option "" $ string "!important"++operator :: Parser CssElement+operator = skipSpace *> op <* skipSpace+ where+ op = CssOpSlash <$ char '/'+ <|> CssOpComa <$ char ','+ <?> "operator"++expr :: Parser [CssElement]+expr = ((:) <$> term <*> (concat <$> many termOp))+ <?> "expr"+ where+ op = option (:[]) $ (\a b -> [a, b]) <$> operator+ termOp = ($) <$> op <*> term++dashArray :: Parser [Number]+dashArray = skipSpace *> (complexNumber `sepBy1` commaWsp)++numberList :: Parser [Double]+numberList = skipSpace *> (num `sepBy1` commaWsp)++complexNumber :: Parser Number+complexNumber = do+ n <- num+ (Percent (n / 100) <$ char '%')+ <|> (Em n <$ string "em")+ <|> (Mm n <$ string "mm")+ <|> (Cm n <$ string "cm")+ <|> (Point n <$ string "pt")+ <|> (Pc n <$ string "pc")+ <|> (Px n <$ string "px")+ <|> (Inches n <$ string "in")+ <|> pure (Num n)++term :: Parser CssElement+term = checkRgb <$> function+ <|> (CssNumber <$> complexNumber)+ <|> (CssString <$> str)+ <|> (checkNamedColor <$> ident)+ <|> (CssColor <$> colorParser)+ where+ comma = skipSpace *> char ',' <* skipSpace+ checkNamedColor n+ | Just c <- M.lookup n svgNamedColors = CssColor c+ | otherwise = CssIdent n++ ref = char '#' *> ident++ checkRgb (CssFunction "rgb"+ [CssNumber r, CssNumber g, CssNumber b]) =+ CssColor $ PixelRGBA8 (to r) (to g) (to b) 255+ where clamp = max 0 . min 255+ to (Num n) = floor $ clamp n+ to (Px n) = floor $ clamp n+ to (Percent p) = floor . clamp $ p * 255+ to (Em c) = floor $ clamp c+ to (Pc n) = floor $ clamp n+ to (Mm n) = floor $ clamp n+ to (Cm n) = floor $ clamp n+ to (Point n) = floor $ clamp n+ to (Inches n) = floor $ clamp n++ checkRgb a = a+ functionParam = (CssReference <$> ref) <|> term++ function = CssFunction+ <$> ident <* char '('+ <*> (functionParam `sepBy` comma) <* char ')' <* skipSpace++-- | Parse CSS text into rules.+cssRulesOfText :: T.Text -> [CssRule]+cssRulesOfText txt = case parseOnly (many1 ruleSet) $ txt of+ Left _ -> []+ Right rules -> rules+
src/Graphics/Svg/CssTypes.hs view
@@ -1,271 +1,271 @@-{-# LANGUAGE OverloadedStrings #-} -{-# LANGUAGE CPP #-} --- | Define the types used to describes CSS elements -module Graphics.Svg.CssTypes - ( CssSelector( .. ) - , CssSelectorRule - , CssRule( .. ) - , CssDescriptor( .. ) - , CssDeclaration( .. ) - , CssElement( .. ) - - , CssMatcheable( .. ) - , CssContext - , Dpi - , Number( .. ) - , serializeNumber - , findMatchingDeclarations - , toUserUnit - , mapNumber - , tserialize - ) where - -#if !MIN_VERSION_base(4,8,0) -import Data.Monoid( mconcat ) -#endif - -import Data.Monoid( (<>) ) -import Data.List( intersperse ) -import qualified Data.Text as T -import qualified Data.Text.Lazy.Builder as TB -import Text.Printf - -import Codec.Picture( PixelRGBA8( .. ) ) - --- | Alias describing a "dot per inch" information --- used for size calculation (see toUserUnit). -type Dpi = Int - --- | Helper typeclass for serialization to Text. -class TextBuildable a where - -- | Serialize an element to a text builder. - tserialize :: a -> TB.Builder - --- | Describe an element of a CSS selector. Multiple --- elements can be combined in a CssSelector type. -data CssDescriptor - = OfClass T.Text -- ^ .IDENT - | OfName T.Text -- ^ IDENT - | OfId T.Text -- ^ #IDENT - | OfPseudoClass T.Text -- ^ `:IDENT` (ignore function syntax) - | AnyElem -- ^ '*' - | WithAttrib T.Text T.Text -- ^ `` - deriving (Eq, Show) - -instance TextBuildable CssDescriptor where - tserialize d = case d of - OfClass c -> si '.' <> ft c - OfName n -> ft n - OfId i -> si '#' <> ft i - OfPseudoClass c -> si '#' <> ft c - AnyElem -> si '*' - WithAttrib a b -> mconcat [si '[', ft a, si '=', ft b, si ']'] - where - ft = TB.fromText - si = TB.singleton - --- | Define complex selector. -data CssSelector - = Nearby -- ^ Correspond to the `+` CSS selector. - | DirectChildren -- ^ Correspond to the `>` CSS selectro. - | AllOf [CssDescriptor] -- ^ Grouping construct, all the elements - -- of the list must be matched. - deriving (Eq, Show) - -instance TextBuildable CssSelector where - tserialize s = case s of - Nearby -> si '+' - DirectChildren -> si '>' - AllOf lst -> mconcat $ map tserialize lst - where - si = TB.singleton - --- | A CssSelectorRule is a list of all the elements --- that must be meet in a depth first search fashion. -type CssSelectorRule = [CssSelector] - --- | Represent a CSS selector and the different declarations --- to apply to the matched elemens. -data CssRule = CssRule - { -- | At the first level represent a list of elements - -- to be matched. If any match is made, you can apply - -- the declarations. At the second level - cssRuleSelector :: ![CssSelectorRule] - -- | Declarations to apply to the matched element. - , cssDeclarations :: ![CssDeclaration] - } - deriving (Eq, Show) - -instance TextBuildable CssRule where - tserialize (CssRule selectors decl) = - mconcat tselectors - <> ft " {\n" - <> mconcat (fmap tserializeDecl decl) - <> ft "}\n" - where - ft = TB.fromText - tserializeDecl d = ft " " <> tserialize d <> ft ";\n" - tselector = - mconcat . intersperse (ft " ") . fmap tserialize - tselectors = - intersperse (ft ",\n") $ fmap tselector selectors - --- | Interface for elements to be matched against --- some CssRule. -class CssMatcheable a where - -- | For an element, tell its optional ID attribute. - cssIdOf :: a -> Maybe T.Text - -- | For an element, return all of it's class attributes. - cssClassOf :: a -> [T.Text] - -- | Return the name of the tagname of the element - cssNameOf :: a -> T.Text - -- | Return a value of a given attribute if present - cssAttribOf :: a -> T.Text -> Maybe T.Text - --- | Represent a zipper in depth at the first list --- level, and the previous nodes at in the second --- list level. -type CssContext a = [[a]] - -isDescribedBy :: CssMatcheable a - => a -> [CssDescriptor] -> Bool -isDescribedBy e = all tryMatch - where - tryMatch (OfClass t) = t `elem` cssClassOf e - tryMatch (OfId i) = cssIdOf e == Just i - tryMatch (OfName n) = cssNameOf e == n - tryMatch (OfPseudoClass _) = False - tryMatch (WithAttrib a v) = cssAttribOf e a == Just v - tryMatch AnyElem = True - -isMatching :: CssMatcheable a - => CssContext a -> [CssSelector] -> Bool -isMatching = go where - go _ [] = True - go [] _ = False - go ((_ : near):upper) (Nearby : rest) = go (near:upper) rest - go ((e:_):upper) (DirectChildren:AllOf descr:rest) - | isDescribedBy e descr = go upper rest - go _ (DirectChildren:_) = False - go ((e:_):upper) selectors@(AllOf descr : rest) - | isDescribedBy e descr = go upper rest - | otherwise = go upper selectors - go (_:upper) selector = go upper selector - --- | Given CSS rules, find all the declaration to apply to the --- element in a given context. -findMatchingDeclarations :: CssMatcheable a - => [CssRule] -> CssContext a -> [CssDeclaration] -findMatchingDeclarations rules context = - concat [cssDeclarations rule - | rule <- rules - , selector <- cssRuleSelector rule - , isMatching context $ reverse selector ] - --- | Represent the content to apply to some --- CSS matched rules. -data CssDeclaration = CssDeclaration - { -- | Property name to change (like font-family or color). - _cssDeclarationProperty :: T.Text - -- | List of values - , _cssDecarationlValues :: [[CssElement]] - } - deriving (Eq, Show) - -instance TextBuildable CssDeclaration where - tserialize (CssDeclaration n elems) = - mconcat $ ft n : ft ": " : intersperse (si ' ') finalElems - where - finalElems = map tserialize (concat elems) - ft = TB.fromText - si = TB.singleton - - --- | Encode complex number possibly dependant to the current --- render size. -data Number - = Num Double -- ^ Simple coordinate in current user coordinate. - | Px Double -- ^ With suffix "px" - | Em Double -- ^ Number relative to the current font size. - | Percent Double -- ^ Number relative to the current viewport size. - | Pc Double - | Mm Double -- ^ Number in millimeters, relative to DPI. - | Cm Double -- ^ Number in centimeters, relative to DPI. - | Point Double -- ^ Number in points, relative to DPI. - | Inches Double -- ^ Number in inches, relative to DPI. - deriving (Eq, Show) - --- | Helper function to modify inner value of a number -mapNumber :: (Double -> Double) -> Number -> Number -mapNumber f nu = case nu of - Num n -> Num $ f n - Px n -> Px $ f n - Em n -> Em $ f n - Percent n -> Percent $ f n - Pc n -> Pc $ f n - Mm n -> Mm $ f n - Cm n -> Cm $ f n - Point n -> Point $ f n - Inches n -> Inches $ f n - --- | Encode the number to string which can be used in a --- CSS or a svg attributes. -serializeNumber :: Number -> String -serializeNumber n = case n of - Num c -> printf "%g" c - Px c -> printf "%gpx" c - Em cc -> printf "%gem" cc - Percent p -> printf "%d%%" (floor $ 100 * p :: Int) - Pc p -> printf "%gpc" p - Mm m -> printf "%gmm" m - Cm c -> printf "%gcm" c - Point p -> printf "%gpt" p - Inches i -> printf "%gin" i - -instance TextBuildable Number where - tserialize = TB.fromText . T.pack . serializeNumber - --- | Value of a CSS property. -data CssElement - = CssIdent !T.Text - | CssString !T.Text - | CssReference !T.Text - | CssNumber !Number - | CssColor !PixelRGBA8 - | CssFunction !T.Text ![CssElement] - | CssOpComa - | CssOpSlash - deriving (Eq, Show) - -instance TextBuildable CssElement where - tserialize e = case e of - CssIdent n -> ft n - CssString s -> si '"' <> ft s <> si '"' - CssReference r -> si '#' <> ft r - CssNumber n -> tserialize n - CssColor (PixelRGBA8 r g b _) -> - ft . T.pack $ printf "#%02X%02X%02X" r g b - CssFunction t els -> mconcat $ ft t : si '(' : args ++ [si ')'] - where args = intersperse (ft ", ") (map tserialize els) - CssOpComa -> si ',' - CssOpSlash -> si '/' - where - ft = TB.fromText - si = TB.singleton - --- | This function replace all device dependant units to user --- units given it's DPI configuration. --- Preserve percentage and "em" notation. -toUserUnit :: Dpi -> Number -> Number -toUserUnit dpi = go where - go nu = case nu of - Num _ -> nu - Px p -> go $ Num p - Em _ -> nu - Percent _ -> nu - Pc n -> go . Inches $ (12 * n) / 72 - Inches n -> Num $ n * fromIntegral dpi - Mm n -> go . Inches $ n / 25.4 - Cm n -> go . Inches $ n / 2.54 - Point n -> go . Inches $ n / 72 - +{-# LANGUAGE OverloadedStrings #-}+{-# LANGUAGE CPP #-}+-- | Define the types used to describes CSS elements+module Graphics.Svg.CssTypes+ ( CssSelector( .. )+ , CssSelectorRule+ , CssRule( .. )+ , CssDescriptor( .. )+ , CssDeclaration( .. )+ , CssElement( .. )++ , CssMatcheable( .. )+ , CssContext+ , Dpi+ , Number( .. )+ , serializeNumber+ , findMatchingDeclarations+ , toUserUnit+ , mapNumber+ , tserialize+ ) where++#if !MIN_VERSION_base(4,8,0)+import Data.Monoid( mconcat )+#endif++import Data.Monoid( (<>) )+import Data.List( intersperse )+import qualified Data.Text as T+import qualified Data.Text.Lazy.Builder as TB+import Text.Printf++import Codec.Picture( PixelRGBA8( .. ) )++-- | Alias describing a "dot per inch" information+-- used for size calculation (see toUserUnit).+type Dpi = Int++-- | Helper typeclass for serialization to Text.+class TextBuildable a where+ -- | Serialize an element to a text builder.+ tserialize :: a -> TB.Builder++-- | Describe an element of a CSS selector. Multiple+-- elements can be combined in a CssSelector type.+data CssDescriptor+ = OfClass T.Text -- ^ .IDENT+ | OfName T.Text -- ^ IDENT+ | OfId T.Text -- ^ #IDENT+ | OfPseudoClass T.Text -- ^ `:IDENT` (ignore function syntax)+ | AnyElem -- ^ '*'+ | WithAttrib T.Text T.Text -- ^ ``+ deriving (Eq, Show)++instance TextBuildable CssDescriptor where+ tserialize d = case d of+ OfClass c -> si '.' <> ft c+ OfName n -> ft n+ OfId i -> si '#' <> ft i+ OfPseudoClass c -> si '#' <> ft c+ AnyElem -> si '*'+ WithAttrib a b -> mconcat [si '[', ft a, si '=', ft b, si ']']+ where+ ft = TB.fromText+ si = TB.singleton++-- | Define complex selector.+data CssSelector+ = Nearby -- ^ Correspond to the `+` CSS selector.+ | DirectChildren -- ^ Correspond to the `>` CSS selectro.+ | AllOf [CssDescriptor] -- ^ Grouping construct, all the elements+ -- of the list must be matched.+ deriving (Eq, Show)++instance TextBuildable CssSelector where+ tserialize s = case s of+ Nearby -> si '+'+ DirectChildren -> si '>'+ AllOf lst -> mconcat $ map tserialize lst+ where+ si = TB.singleton++-- | A CssSelectorRule is a list of all the elements+-- that must be meet in a depth first search fashion.+type CssSelectorRule = [CssSelector]++-- | Represent a CSS selector and the different declarations+-- to apply to the matched elemens.+data CssRule = CssRule+ { -- | At the first level represent a list of elements+ -- to be matched. If any match is made, you can apply+ -- the declarations. At the second level+ cssRuleSelector :: ![CssSelectorRule]+ -- | Declarations to apply to the matched element.+ , cssDeclarations :: ![CssDeclaration]+ }+ deriving (Eq, Show)++instance TextBuildable CssRule where+ tserialize (CssRule selectors decl) =+ mconcat tselectors+ <> ft " {\n"+ <> mconcat (fmap tserializeDecl decl)+ <> ft "}\n"+ where+ ft = TB.fromText+ tserializeDecl d = ft " " <> tserialize d <> ft ";\n"+ tselector =+ mconcat . intersperse (ft " ") . fmap tserialize+ tselectors = + intersperse (ft ",\n") $ fmap tselector selectors++-- | Interface for elements to be matched against+-- some CssRule.+class CssMatcheable a where+ -- | For an element, tell its optional ID attribute.+ cssIdOf :: a -> Maybe T.Text+ -- | For an element, return all of it's class attributes.+ cssClassOf :: a -> [T.Text]+ -- | Return the name of the tagname of the element+ cssNameOf :: a -> T.Text+ -- | Return a value of a given attribute if present+ cssAttribOf :: a -> T.Text -> Maybe T.Text++-- | Represent a zipper in depth at the first list+-- level, and the previous nodes at in the second+-- list level.+type CssContext a = [[a]]++isDescribedBy :: CssMatcheable a+ => a -> [CssDescriptor] -> Bool+isDescribedBy e = all tryMatch+ where+ tryMatch (OfClass t) = t `elem` cssClassOf e+ tryMatch (OfId i) = cssIdOf e == Just i+ tryMatch (OfName n) = cssNameOf e == n+ tryMatch (OfPseudoClass _) = False+ tryMatch (WithAttrib a v) = cssAttribOf e a == Just v+ tryMatch AnyElem = True++isMatching :: CssMatcheable a+ => CssContext a -> [CssSelector] -> Bool+isMatching = go where+ go _ [] = True+ go [] _ = False+ go ((_ : near):upper) (Nearby : rest) = go (near:upper) rest+ go ((e:_):upper) (DirectChildren:AllOf descr:rest)+ | isDescribedBy e descr = go upper rest+ go _ (DirectChildren:_) = False+ go ((e:_):upper) selectors@(AllOf descr : rest)+ | isDescribedBy e descr = go upper rest+ | otherwise = go upper selectors+ go (_:upper) selector = go upper selector++-- | Given CSS rules, find all the declaration to apply to the+-- element in a given context.+findMatchingDeclarations :: CssMatcheable a+ => [CssRule] -> CssContext a -> [CssDeclaration]+findMatchingDeclarations rules context =+ concat [cssDeclarations rule+ | rule <- rules+ , selector <- cssRuleSelector rule+ , isMatching context $ reverse selector ]++-- | Represent the content to apply to some+-- CSS matched rules.+data CssDeclaration = CssDeclaration+ { -- | Property name to change (like font-family or color).+ _cssDeclarationProperty :: T.Text+ -- | List of values+ , _cssDecarationlValues :: [[CssElement]]+ }+ deriving (Eq, Show)++instance TextBuildable CssDeclaration where+ tserialize (CssDeclaration n elems) =+ mconcat $ ft n : ft ": " : intersperse (si ' ') finalElems+ where+ finalElems = map tserialize (concat elems)+ ft = TB.fromText+ si = TB.singleton+++-- | Encode complex number possibly dependant to the current+-- render size.+data Number+ = Num Double -- ^ Simple coordinate in current user coordinate.+ | Px Double -- ^ With suffix "px"+ | Em Double -- ^ Number relative to the current font size.+ | Percent Double -- ^ Number relative to the current viewport size.+ | Pc Double+ | Mm Double -- ^ Number in millimeters, relative to DPI.+ | Cm Double -- ^ Number in centimeters, relative to DPI.+ | Point Double -- ^ Number in points, relative to DPI.+ | Inches Double -- ^ Number in inches, relative to DPI.+ deriving (Eq, Show)++-- | Helper function to modify inner value of a number+mapNumber :: (Double -> Double) -> Number -> Number+mapNumber f nu = case nu of+ Num n -> Num $ f n+ Px n -> Px $ f n+ Em n -> Em $ f n+ Percent n -> Percent $ f n+ Pc n -> Pc $ f n+ Mm n -> Mm $ f n+ Cm n -> Cm $ f n+ Point n -> Point $ f n+ Inches n -> Inches $ f n++-- | Encode the number to string which can be used in a+-- CSS or a svg attributes.+serializeNumber :: Number -> String+serializeNumber n = case n of+ Num c -> printf "%g" c+ Px c -> printf "%gpx" c+ Em cc -> printf "%gem" cc+ Percent p -> printf "%d%%" (floor $ 100 * p :: Int)+ Pc p -> printf "%gpc" p+ Mm m -> printf "%gmm" m+ Cm c -> printf "%gcm" c+ Point p -> printf "%gpt" p+ Inches i -> printf "%gin" i++instance TextBuildable Number where+ tserialize = TB.fromText . T.pack . serializeNumber++-- | Value of a CSS property.+data CssElement+ = CssIdent !T.Text+ | CssString !T.Text+ | CssReference !T.Text+ | CssNumber !Number+ | CssColor !PixelRGBA8+ | CssFunction !T.Text ![CssElement]+ | CssOpComa+ | CssOpSlash+ deriving (Eq, Show)++instance TextBuildable CssElement where+ tserialize e = case e of+ CssIdent n -> ft n+ CssString s -> si '"' <> ft s <> si '"'+ CssReference r -> si '#' <> ft r+ CssNumber n -> tserialize n+ CssColor (PixelRGBA8 r g b _) ->+ ft . T.pack $ printf "#%02X%02X%02X" r g b+ CssFunction t els -> mconcat $ ft t : si '(' : args ++ [si ')']+ where args = intersperse (ft ", ") (map tserialize els)+ CssOpComa -> si ','+ CssOpSlash -> si '/'+ where+ ft = TB.fromText+ si = TB.singleton++-- | This function replace all device dependant units to user+-- units given it's DPI configuration.+-- Preserve percentage and "em" notation.+toUserUnit :: Dpi -> Number -> Number+toUserUnit dpi = go where+ go nu = case nu of+ Num _ -> nu+ Px p -> go $ Num p+ Em _ -> nu+ Percent _ -> nu+ Pc n -> go . Inches $ (12 * n) / 72+ Inches n -> Num $ n * fromIntegral dpi+ Mm n -> go . Inches $ n / 25.4+ Cm n -> go . Inches $ n / 2.54+ Point n -> go . Inches $ n / 72+
src/Graphics/Svg/PathParser.hs view
@@ -1,231 +1,260 @@-{-# LANGUAGE OverloadedStrings #-} -{-# LANGUAGE CPP #-} -module Graphics.Svg.PathParser( transformParser - , command - , pathParser - , viewBoxParser - , pointData - , serializePoints - , serializeCommand - , serializeCommands - , serializeViewBox - ) where - -#if !MIN_VERSION_base(4,8,0) -import Control.Applicative( (<*>), (<*), (*>), (<$>), (<$) ) -#endif - -import Control.Applicative( (<|>) ) -import Data.Scientific( toRealFloat ) -import Data.Attoparsec.Text - ( Parser - , scientific - , string - , skipSpace - , char - , many1 - ) -import Data.Attoparsec.Combinator( option - , sepBy - , sepBy1 ) - -import Linear hiding ( angle, point ) -import Graphics.Svg.Types -import qualified Data.Text as T -import Text.Printf( printf ) - -num :: Parser Double -num = realToFrac <$> (skipSpace *> plusMinus <* skipSpace) - where doubleNumber :: Parser Double - doubleNumber = toRealFloat <$> scientific - - plusMinus = negate <$ string "-" <*> doubleNumber - <|> string "+" *> doubleNumber - <|> doubleNumber - -viewBoxParser :: Parser (Double, Double, Double, Double) -viewBoxParser = (,,,) - <$> iParse <*> iParse <*> iParse <*> iParse - where - iParse = num <* skipSpace - -serializeViewBox :: (Double, Double, Double, Double) -> String -serializeViewBox (a, b, c, d) = printf "%g %g %g %g" a b c d - -commaWsp :: Parser () -commaWsp = skipSpace *> option () (string "," *> return ()) <* skipSpace - -point :: Parser RPoint -point = V2 <$> num <* commaWsp <*> num - -pointData :: Parser [RPoint] -pointData = point `sepBy` commaWsp - -pathParser :: Parser [PathCommand] -pathParser = skipSpace *> many1 command - -command :: Parser PathCommand -command = (MoveTo OriginAbsolute <$ string "M" <*> pointList) - <|> (MoveTo OriginRelative <$ string "m" <*> pointList) - <|> (LineTo OriginAbsolute <$ string "L" <*> pointList) - <|> (LineTo OriginRelative <$ string "l" <*> pointList) - <|> (HorizontalTo OriginAbsolute <$ string "H" <*> coordList) - <|> (HorizontalTo OriginRelative <$ string "h" <*> coordList) - <|> (VerticalTo OriginAbsolute <$ string "V" <*> coordList) - <|> (VerticalTo OriginRelative <$ string "v" <*> coordList) - <|> (CurveTo OriginAbsolute <$ string "C" <*> manyComma curveToArgs) - <|> (CurveTo OriginRelative <$ string "c" <*> manyComma curveToArgs) - <|> (SmoothCurveTo OriginAbsolute <$ string "S" <*> pointPairList) - <|> (SmoothCurveTo OriginRelative <$ string "s" <*> pointPairList) - <|> (QuadraticBezier OriginAbsolute <$ string "Q" <*> pointPairList) - <|> (QuadraticBezier OriginRelative <$ string "q" <*> pointPairList) - <|> (SmoothQuadraticBezierCurveTo OriginAbsolute <$ string "T" <*> pointList) - <|> (SmoothQuadraticBezierCurveTo OriginRelative <$ string "t" <*> pointList) - <|> (EllipticalArc OriginAbsolute <$ string "A" <*> manyComma ellipticalArgs) - <|> (EllipticalArc OriginRelative <$ string "a" <*> manyComma ellipticalArgs) - <|> (EndPath <$ string "Z" <* commaWsp) - <|> (EndPath <$ string "z" <* commaWsp) - where pointList = point `sepBy1` commaWsp - pointPair = (,) <$> point <* commaWsp <*> point - pointPairList = pointPair `sepBy1` commaWsp - coordList = num `sepBy1` commaWsp - curveToArgs = (,,) <$> (point <* commaWsp) - <*> (point <* commaWsp) - <*> point - manyComma a = a `sepBy1` commaWsp - - numComma = num <* commaWsp - ellipticalArgs = (,,,,,) <$> numComma - <*> numComma - <*> numComma - <*> (fmap (/= 0) numComma) - <*> (fmap (/= 0) numComma) - <*> point - -serializePoint :: RPoint -> String -serializePoint (V2 x y) = printf "%g,%g" x y - -serializePoints :: [RPoint] -> String -serializePoints = unwords . fmap serializePoint - -serializeCoords :: [Coord] -> String -serializeCoords = unwords . fmap (printf "%g") - -serializePointPair :: (RPoint, RPoint) -> String -serializePointPair (a, b) = serializePoint a ++ " " ++ serializePoint b - -serializePointPairs :: [(RPoint, RPoint)] -> String -serializePointPairs = unwords . fmap serializePointPair - -serializePointTriplet :: (RPoint, RPoint, RPoint) -> String -serializePointTriplet (a, b, c) = - serializePoint a ++ " " ++ serializePoint b ++ " " ++ serializePoint c - -serializePointTriplets :: [(RPoint, RPoint, RPoint)] -> String -serializePointTriplets = unwords . fmap serializePointTriplet - -serializeCommands :: [PathCommand] -> String -serializeCommands = unwords . fmap serializeCommand - -serializeCommand :: PathCommand -> String -serializeCommand p = case p of - MoveTo OriginAbsolute points -> "M" ++ serializePoints points - MoveTo OriginRelative points -> "m" ++ serializePoints points - LineTo OriginAbsolute points -> "L" ++ serializePoints points - LineTo OriginRelative points -> "l" ++ serializePoints points - - HorizontalTo OriginAbsolute coords -> "H" ++ serializeCoords coords - HorizontalTo OriginRelative coords -> "h" ++ serializeCoords coords - VerticalTo OriginAbsolute coords -> "V" ++ serializeCoords coords - VerticalTo OriginRelative coords -> "v" ++ serializeCoords coords - - CurveTo OriginAbsolute triplets -> "C" ++ serializePointTriplets triplets - CurveTo OriginRelative triplets -> "c" ++ serializePointTriplets triplets - SmoothCurveTo OriginAbsolute pointPairs -> "S" ++ serializePointPairs pointPairs - SmoothCurveTo OriginRelative pointPairs -> "s" ++ serializePointPairs pointPairs - QuadraticBezier OriginAbsolute pointPairs -> "Q" ++ serializePointPairs pointPairs - QuadraticBezier OriginRelative pointPairs -> "q" ++ serializePointPairs pointPairs - SmoothQuadraticBezierCurveTo OriginAbsolute points -> "T" ++ serializePoints points - SmoothQuadraticBezierCurveTo OriginRelative points -> "t" ++ serializePoints points - EllipticalArc OriginAbsolute args -> "A" ++ serializeArgs args - EllipticalArc OriginRelative args -> "a" ++ serializeArgs args - EndPath -> "Z" - where - serializeArg (a, b, c, d, e, V2 x y) = - printf "%g %g %g %d %d %g,%g" a b c (fromEnum d) (fromEnum e) x y - serializeArgs = unwords . fmap serializeArg - - - -transformParser :: Parser Transformation -transformParser = matrixParser - <|> translationParser - <|> scaleParser - <|> rotateParser - <|> skewYParser - <|> skewXParser - -functionParser :: T.Text -> Parser [Double] -functionParser funcName = - string funcName *> skipSpace - *> char '(' *> skipSpace - *> num `sepBy1` commaWsp - <* skipSpace <* char ')' <* skipSpace - -translationParser :: Parser Transformation -translationParser = do - args <- functionParser "translate" - return $ case args of - [x] -> Translate x 0 - [x, y] -> Translate x y - _ -> TransformUnknown - -skewXParser :: Parser Transformation -skewXParser = do - args <- functionParser "skewX" - return $ case args of - [x] -> SkewX x - _ -> TransformUnknown - -skewYParser :: Parser Transformation -skewYParser = do - args <- functionParser "skewY" - return $ case args of - [x] -> SkewY x - _ -> TransformUnknown - - -scaleParser :: Parser Transformation -scaleParser = do - args <- functionParser "scale" - return $ case args of - [x] -> Scale x Nothing - [x, y] -> Scale x (Just y) - _ -> TransformUnknown - -matrixParser :: Parser Transformation -matrixParser = do - args <- functionParser "matrix" - return $ case args of - [a, b, c, d, e, f] -> - TransformMatrix a b c d e f - _ -> TransformUnknown - -rotateParser :: Parser Transformation -rotateParser = do - args <- functionParser "rotate" - return $ case args of - [angle] -> Rotate angle Nothing - [angle, x, y] -> Rotate angle $ Just (x, y) - _ -> TransformUnknown -{- -rotate(<rotate-angle> [<cx> <cy>]), which specifies a rotation by <rotate-angle> degrees about a given point. - -If optional parameters <cx> and <cy> are not supplied, the rotation is about the origin of the current user coordinate system. The operation corresponds to the matrix [cos(a) sin(a) -sin(a) cos(a) 0 0]. - -If optional parameters <cx> and <cy> are supplied, the rotation is about the point (cx, cy). The operation represents the equivalent of the following specification: translate(<cx>, <cy>) rotate(<rotate-angle>) translate(-<cx>, -<cy>). - -skewX(<skew-angle>), which specifies a skew transformation along the x-axis. - -skewY(<skew-angle>), which specifies a skew transformation along the y-axis. - -} +{-# LANGUAGE OverloadedStrings #-}+{-# LANGUAGE CPP #-}+module Graphics.Svg.PathParser( transformParser+ , command+ , pathParser+ , viewBoxParser+ , pointData+ , gradientCommand+ , serializePoints+ , serializeCommand+ , serializeGradientCommand + , serializeCommands+ , serializeViewBox+ ) where++#if !MIN_VERSION_base(4,8,0)+import Control.Applicative( (<*>), (<*), (*>), (<$>), (<$) )+#endif++import Control.Applicative( (<|>) )+import Data.Scientific( toRealFloat )+import Data.Attoparsec.Text+ ( Parser+ , scientific+ , string+ , skipSpace+ , char+ , many1+ )+import Data.Attoparsec.Combinator( option+ , sepBy+ , sepBy1 )++import Linear hiding ( angle, point )+import Graphics.Svg.Types+import qualified Data.Text as T+import Text.Printf( printf )++num :: Parser Double+num = realToFrac <$> (skipSpace *> plusMinus <* skipSpace)+ where doubleNumber :: Parser Double+ doubleNumber = toRealFloat <$> scientific++ plusMinus = negate <$ string "-" <*> doubleNumber+ <|> string "+" *> doubleNumber+ <|> doubleNumber++viewBoxParser :: Parser (Double, Double, Double, Double)+viewBoxParser = (,,,)+ <$> iParse <*> iParse <*> iParse <*> iParse+ where+ iParse = num <* skipSpace++serializeViewBox :: (Double, Double, Double, Double) -> String+serializeViewBox (a, b, c, d) = printf "%g %g %g %g" a b c d++commaWsp :: Parser ()+commaWsp = skipSpace *> option () (string "," *> return ()) <* skipSpace++point :: Parser RPoint+point = V2 <$> num <* commaWsp <*> num++pointData :: Parser [RPoint]+pointData = point `sepBy` commaWsp++pathParser :: Parser [PathCommand]+pathParser = skipSpace *> many1 command++command :: Parser PathCommand+command = (MoveTo OriginAbsolute <$ string "M" <*> pointList)+ <|> (MoveTo OriginRelative <$ string "m" <*> pointList)+ <|> (LineTo OriginAbsolute <$ string "L" <*> pointList)+ <|> (LineTo OriginRelative <$ string "l" <*> pointList)+ <|> (HorizontalTo OriginAbsolute <$ string "H" <*> coordList)+ <|> (HorizontalTo OriginRelative <$ string "h" <*> coordList)+ <|> (VerticalTo OriginAbsolute <$ string "V" <*> coordList)+ <|> (VerticalTo OriginRelative <$ string "v" <*> coordList)+ <|> (CurveTo OriginAbsolute <$ string "C" <*> manyComma curveToArgs)+ <|> (CurveTo OriginRelative <$ string "c" <*> manyComma curveToArgs)+ <|> (SmoothCurveTo OriginAbsolute <$ string "S" <*> pointPairList)+ <|> (SmoothCurveTo OriginRelative <$ string "s" <*> pointPairList)+ <|> (QuadraticBezier OriginAbsolute <$ string "Q" <*> pointPairList)+ <|> (QuadraticBezier OriginRelative <$ string "q" <*> pointPairList)+ <|> (SmoothQuadraticBezierCurveTo OriginAbsolute <$ string "T" <*> pointList)+ <|> (SmoothQuadraticBezierCurveTo OriginRelative <$ string "t" <*> pointList)+ <|> (EllipticalArc OriginAbsolute <$ string "A" <*> manyComma ellipticalArgs)+ <|> (EllipticalArc OriginRelative <$ string "a" <*> manyComma ellipticalArgs)+ <|> (EndPath <$ string "Z" <* commaWsp)+ <|> (EndPath <$ string "z" <* commaWsp)+ where pointList = point `sepBy1` commaWsp+ pointPair = (,) <$> point <* commaWsp <*> point+ pointPairList = pointPair `sepBy1` commaWsp+ coordList = num `sepBy1` commaWsp+ curveToArgs = (,,) <$> (point <* commaWsp)+ <*> (point <* commaWsp)+ <*> point+ manyComma a = a `sepBy1` commaWsp++ numComma = num <* commaWsp+ ellipticalArgs = (,,,,,) <$> numComma+ <*> numComma+ <*> numComma+ <*> (fmap (/= 0) numComma)+ <*> (fmap (/= 0) numComma)+ <*> point++serializePoint :: RPoint -> String+serializePoint (V2 x y) = printf "%g,%g" x y++serializePoints :: [RPoint] -> String+serializePoints = unwords . fmap serializePoint++serializeCoords :: [Coord] -> String+serializeCoords = unwords . fmap (printf "%g")++serializePointPair :: (RPoint, RPoint) -> String+serializePointPair (a, b) = serializePoint a ++ " " ++ serializePoint b++serializePointPairs :: [(RPoint, RPoint)] -> String+serializePointPairs = unwords . fmap serializePointPair++serializePointTriplet :: (RPoint, RPoint, RPoint) -> String+serializePointTriplet (a, b, c) =+ serializePoint a ++ " " ++ serializePoint b ++ " " ++ serializePoint c++serializePointTriplets :: [(RPoint, RPoint, RPoint)] -> String+serializePointTriplets = unwords . fmap serializePointTriplet++serializeCommands :: [PathCommand] -> String+serializeCommands = unwords . fmap serializeCommand++serializeCommand :: PathCommand -> String+serializeCommand p = case p of+ MoveTo OriginAbsolute points -> "M" ++ serializePoints points+ MoveTo OriginRelative points -> "m" ++ serializePoints points+ LineTo OriginAbsolute points -> "L" ++ serializePoints points+ LineTo OriginRelative points -> "l" ++ serializePoints points++ HorizontalTo OriginAbsolute coords -> "H" ++ serializeCoords coords+ HorizontalTo OriginRelative coords -> "h" ++ serializeCoords coords+ VerticalTo OriginAbsolute coords -> "V" ++ serializeCoords coords+ VerticalTo OriginRelative coords -> "v" ++ serializeCoords coords++ CurveTo OriginAbsolute triplets -> "C" ++ serializePointTriplets triplets+ CurveTo OriginRelative triplets -> "c" ++ serializePointTriplets triplets+ SmoothCurveTo OriginAbsolute pointPairs -> "S" ++ serializePointPairs pointPairs+ SmoothCurveTo OriginRelative pointPairs -> "s" ++ serializePointPairs pointPairs+ QuadraticBezier OriginAbsolute pointPairs -> "Q" ++ serializePointPairs pointPairs+ QuadraticBezier OriginRelative pointPairs -> "q" ++ serializePointPairs pointPairs+ SmoothQuadraticBezierCurveTo OriginAbsolute points -> "T" ++ serializePoints points+ SmoothQuadraticBezierCurveTo OriginRelative points -> "t" ++ serializePoints points+ EllipticalArc OriginAbsolute args -> "A" ++ serializeArgs args+ EllipticalArc OriginRelative args -> "a" ++ serializeArgs args+ EndPath -> "Z"+ where+ serializeArg (a, b, c, d, e, V2 x y) =+ printf "%g %g %g %d %d %g,%g" a b c (fromEnum d) (fromEnum e) x y+ serializeArgs = unwords . fmap serializeArg++++transformParser :: Parser Transformation+transformParser = matrixParser+ <|> translationParser+ <|> scaleParser+ <|> rotateParser+ <|> skewYParser+ <|> skewXParser++functionParser :: T.Text -> Parser [Double]+functionParser funcName =+ string funcName *> skipSpace+ *> char '(' *> skipSpace+ *> num `sepBy1` commaWsp+ <* skipSpace <* char ')' <* skipSpace++translationParser :: Parser Transformation+translationParser = do+ args <- functionParser "translate"+ return $ case args of+ [x] -> Translate x 0+ [x, y] -> Translate x y+ _ -> TransformUnknown++skewXParser :: Parser Transformation+skewXParser = do+ args <- functionParser "skewX"+ return $ case args of+ [x] -> SkewX x+ _ -> TransformUnknown++skewYParser :: Parser Transformation+skewYParser = do+ args <- functionParser "skewY"+ return $ case args of+ [x] -> SkewY x+ _ -> TransformUnknown+++scaleParser :: Parser Transformation+scaleParser = do+ args <- functionParser "scale"+ return $ case args of+ [x] -> Scale x Nothing+ [x, y] -> Scale x (Just y)+ _ -> TransformUnknown++matrixParser :: Parser Transformation+matrixParser = do+ args <- functionParser "matrix"+ return $ case args of+ [a, b, c, d, e, f] ->+ TransformMatrix a b c d e f+ _ -> TransformUnknown++rotateParser :: Parser Transformation+rotateParser = do+ args <- functionParser "rotate"+ return $ case args of+ [angle] -> Rotate angle Nothing+ [angle, x, y] -> Rotate angle $ Just (x, y)+ _ -> TransformUnknown+{-+rotate(<rotate-angle> [<cx> <cy>]), which specifies a rotation by <rotate-angle> degrees about a given point.++If optional parameters <cx> and <cy> are not supplied, the rotation is about the origin of the current user coordinate system. The operation corresponds to the matrix [cos(a) sin(a) -sin(a) cos(a) 0 0].++If optional parameters <cx> and <cy> are supplied, the rotation is about the point (cx, cy). The operation represents the equivalent of the following specification: translate(<cx>, <cy>) rotate(<rotate-angle>) translate(-<cx>, -<cy>).++skewX(<skew-angle>), which specifies a skew transformation along the x-axis.++skewY(<skew-angle>), which specifies a skew transformation along the y-axis.+ -}+gradientCommand :: Parser GradientPathCommand+gradientCommand = + (GLine OriginAbsolute <$> (string "L" *> mayPoint))+ <|> (GLine OriginRelative <$> (string "l" *> mayPoint))+ <|> (string "C" *> curveToArgs OriginAbsolute)+ <|> (string "c" *> curveToArgs OriginRelative)+ <|> (GClose <$ string "Z")+ where + mayPoint = option Nothing $ Just <$> point+ curveToArgs o =+ GCurve o <$> (point <* commaWsp)+ <*> (point <* commaWsp)+ <*> mayPoint++serializeGradientCommand :: GradientPathCommand -> String+serializeGradientCommand p = case p of+ GLine OriginAbsolute points -> "L" ++ smp points+ GLine OriginRelative points -> "l" ++ smp points+ GClose -> "Z"++ GCurve OriginAbsolute a b c -> "C" ++ sp a ++ sp b ++ smp c+ GCurve OriginRelative a b c -> "c" ++ sp a ++ sp b ++ smp c+ where+ sp = serializePoint+ smp Nothing = ""+ smp (Just pp) = serializePoint pp+
src/Graphics/Svg/Types.hs view
@@ -1,1521 +1,1624 @@-{-# LANGUAGE TemplateHaskell #-} -{-# LANGUAGE OverloadedStrings #-} -{-# LANGUAGE RankNTypes #-} -{-# LANGUAGE MultiParamTypeClasses #-} -{-# LANGUAGE FunctionalDependencies #-} -{-# LANGUAGE FlexibleInstances #-} -{-# LANGUAGE CPP #-} --- | This module define all the types used in the definition --- of a svg scene. --- --- Most of the types are lensified. -module Graphics.Svg.Types - ( -- * Basic building types - Coord - , Origin( .. ) - , Point - , RPoint - , PathCommand( .. ) - , Transformation( .. ) - , ElementRef( .. ) - , CoordinateUnits( .. ) - - -- ** Building helpers - , toPoint - , serializeNumber - , serializeTransformation - , serializeTransformations - - -- * Drawing control types - , Cap( .. ) - , LineJoin( .. ) - , Tree( .. ) - , Number( .. ) - , Spread( .. ) - , Texture( .. ) - , Element( .. ) - , FillRule( .. ) - , FontStyle( .. ) - , Dpi - - , WithDefaultSvg( .. ) - - -- * Main type - , Document( .. ) - , HasDocument( .. ) - , documentSize - - -- * Drawing attributes - , DrawAttributes( .. ) - , HasDrawAttributes( .. ) - , WithDrawAttributes( .. ) - - -- * SVG drawing primitives - -- ** Rectangle - , Rectangle( .. ) - , HasRectangle( .. ) - - -- ** Line - , Line( .. ) - , HasLine( .. ) - - -- ** Polygon - , Polygon( .. ) - , HasPolygon( .. ) - - -- ** Polyline - , PolyLine( .. ) - , HasPolyLine( .. ) - - -- ** Path - , Path( .. ) - , HasPath( .. ) - - -- ** Circle - , Circle( .. ) - , HasCircle( .. ) - - -- ** Ellipse - , Ellipse( .. ) - , HasEllipse( .. ) - - -- ** Image - , Image( .. ) - , HasImage( .. ) - - -- ** Use - , Use( .. ) - , HasUse( .. ) - - -- * Grouping primitives - -- ** Group - , Group( .. ) - , HasGroup( .. ) - - -- ** Symbol - , Symbol( .. ) - , groupOfSymbol - - -- * Text related types - -- ** Text - , Text( .. ) - , HasText( .. ) - , TextAnchor( .. ) - , textAt - - -- ** Text path - , TextPath( .. ) - , HasTextPath( .. ) - - , TextPathSpacing( .. ) - , TextPathMethod( .. ) - - -- ** Text span. - , TextSpanContent( .. ) - - , TextSpan( .. ) - , HasTextSpan( .. ) - - , TextInfo( .. ) - , HasTextInfo( .. ) - - , TextAdjust( .. ) - - -- * Marker definition - , Marker( .. ) - , Overflow( .. ) - , MarkerOrientation( .. ) - , MarkerUnit( .. ) - , HasMarker( .. ) - - -- * Gradient definition - , GradientStop( .. ) - , HasGradientStop( .. ) - - -- ** Linear Gradient - , LinearGradient( .. ) - , HasLinearGradient( .. ) - - -- ** Radial Gradient - , RadialGradient( .. ) - , HasRadialGradient( .. ) - - -- * Pattern definition - , Pattern( .. ) - , HasPattern( .. ) - - -- * Mask definition - , Mask( .. ) - , HasMask( .. ) - - -- * Clip path definition - , ClipPath( .. ) - , HasClipPath( .. ) - - -- * Aspect Ratio description - , PreserveAspectRatio( .. ) - , Alignment( .. ) - , MeetSlice( .. ) - , HasPreserveAspectRatio( .. ) - - -- * MISC functions - , isPathArc - , isPathWithArc - , nameOfTree - , zipTree - , mapTree - , foldTree - , toUserUnit - , mapNumber - ) where - -#if !MIN_VERSION_base(4,8,0) -import Data.Monoid( Monoid( .. ) ) -import Data.Foldable( Foldable ) -#endif - -import Data.Function( on ) -import Data.List( inits ) -import qualified Data.Map as M -import Data.Monoid( Last( .. ), (<>) ) -import qualified Data.Foldable as F -import qualified Data.Text as T -import Codec.Picture( PixelRGBA8( .. ) ) -import Control.Lens( Lens' - , lens - , makeClassy - , makeLenses - , view - , (^.) - , (&) - , (.~) - ) -import Graphics.Svg.CssTypes -import Linear hiding ( angle ) - -import Text.Printf - --- | Basic coordiante type. -type Coord = Double - --- | Real Point, fully determined and not --- dependant of the rendering context. -type RPoint = V2 Coord - --- | Possibly context dependant point. -type Point = (Number, Number) - --- | Tell if a path command is absolute (in the current --- user coordiante) or relative to the previous poitn. -data Origin - = OriginAbsolute -- ^ Next point in absolute coordinate - | OriginRelative -- ^ Next point relative to the previous - deriving (Eq, Show) - --- | Path command definition. -data PathCommand - -- | 'M' or 'm' command - = MoveTo !Origin ![RPoint] - -- | Line to, 'L' or 'l' Svg path command. - | LineTo !Origin ![RPoint] - - -- | Equivalent to the 'H' or 'h' svg path command. - | HorizontalTo !Origin ![Coord] - -- | Equivalent to the 'V' or 'v' svg path command. - | VerticalTo !Origin ![Coord] - - -- | Cubic bezier, 'C' or 'c' command - | CurveTo !Origin ![(RPoint, RPoint, RPoint)] - -- | Smooth cubic bezier, equivalent to 'S' or 's' command - | SmoothCurveTo !Origin ![(RPoint, RPoint)] - -- | Quadratic bezier, 'Q' or 'q' command - | QuadraticBezier !Origin ![(RPoint, RPoint)] - -- | Quadratic bezier, 'T' or 't' command - | SmoothQuadraticBezierCurveTo !Origin ![RPoint] - -- | Eliptical arc, 'A' or 'a' command. - | EllipticalArc !Origin ![(Coord, Coord, Coord, Bool, Bool, RPoint)] - -- | Close the path, 'Z' or 'z' svg path command. - | EndPath - deriving (Eq, Show) - --- | Little helper function to build a point. -toPoint :: Number -> Number -> Point -toPoint = (,) - --- | Tell if the path command is an EllipticalArc. -isPathArc :: PathCommand -> Bool -isPathArc (EllipticalArc _ _) = True -isPathArc _ = False - --- | Tell if a full path contain an EllipticalArc. -isPathWithArc :: Foldable f => f PathCommand -> Bool -isPathWithArc = F.any isPathArc - --- | This type represent the align information of the --- preserveAspectRatio SVGattribute -data Alignment - = AlignNone -- ^ "none" value - | AlignxMinYMin -- "xMinYMin" value - | AlignxMidYMin -- ^ "xMidYMin" value - | AlignxMaxYMin -- ^ "xMaxYMin" value - | AlignxMinYMid -- ^ "xMinYMid" value - | AlignxMidYMid -- ^ "xMidYMid" value - | AlignxMaxYMid -- ^ "xMaxYMid" value - | AlignxMinYMax -- ^ "xMinYMax" value - | AlignxMidYMax -- ^ "xMidYMax" value - | AlignxMaxYMax -- ^ "xMaxYMax" value - deriving (Eq, Show) - --- | This type represent the "meet or slice" information --- of the preserveAspectRatio SVGattribute -data MeetSlice = Meet | Slice - deriving (Eq, Show) - --- | Describe the content of the preserveAspectRatio attribute. -data PreserveAspectRatio = PreserveAspectRatio - { _aspectRatioDefer :: !Bool - , _aspectRatioAlign :: !Alignment - , _aspectRatioMeetSlice :: !(Maybe MeetSlice) - } - deriving (Eq, Show) - --- | Lenses for the PreserveAspectRatio type -makeClassy ''PreserveAspectRatio - -instance WithDefaultSvg PreserveAspectRatio where - defaultSvg = PreserveAspectRatio - { _aspectRatioDefer = False - , _aspectRatioAlign = AlignxMidYMid - , _aspectRatioMeetSlice = Nothing - } - --- | Describe how the line should be terminated --- when stroking them. Describe the values of the --- `stroke-linecap` attribute. --- See `_strokeLineCap` -data Cap - = CapRound -- ^ End with a round (`round` value) - | CapButt -- ^ Define straight just at the end (`butt` value) - | CapSquare -- ^ Straight further of the ends (`square` value) - deriving (Eq, Show) - --- | Define the possible values of the `stroke-linejoin` --- attribute. --- see `_strokeLineJoin` -data LineJoin - = JoinMiter -- ^ `miter` value - | JoinBevel -- ^ `bevel` value - | JoinRound -- ^ `round` value - deriving (Eq, Show) - --- | Describe the different value which can be used --- in the `fill` or `stroke` attributes. -data Texture - = ColorRef PixelRGBA8 -- ^ Direct solid color (#rrggbb, #rgb) - | TextureRef String -- ^ Link to a complex texture (url(#name)) - | FillNone -- ^ Equivalent to the `none` value. - deriving (Eq, Show) - --- | Describe the possile filling algorithms. --- Map the values of the `fill-rule` attributes. -data FillRule - = FillEvenOdd -- ^ Correspond to the `evenodd` value. - | FillNonZero -- ^ Correspond to the `nonzero` value. - deriving (Eq, Show) - --- | Describe the content of the `transformation` attribute. --- see `_transform` and `transform`. -data Transformation - = -- | Directly encode the translation matrix. - TransformMatrix !Coord !Coord !Coord - !Coord !Coord !Coord - -- | Translation along a vector - | Translate !Double !Double - -- | Scaling on both axis or on X axis and Y axis. - | Scale !Double !(Maybe Double) - -- | Rotation around `(0, 0)` or around an optional - -- point. - | Rotate !Double !(Maybe (Double, Double)) - -- | Skew transformation along the X axis. - | SkewX !Double - -- | Skew transformation along the Y axis. - | SkewY !Double - -- | Unkown transformation, like identity. - | TransformUnknown - deriving (Eq, Show) - --- | Convert the Transformation to a string which can be --- directly used in a svg attributes. -serializeTransformation :: Transformation -> String -serializeTransformation t = case t of - TransformUnknown -> "" - TransformMatrix a b c d e f -> - printf "matrix(%g, %g, %g, %g, %g, %g)" a b c d e f - Translate x y -> printf "translate(%g, %g)" x y - Scale x Nothing -> printf "scale(%g)" x - Scale x (Just y) -> printf "scale(%g, %g)" x y - Rotate angle Nothing -> printf "rotate(%g)" angle - Rotate angle (Just (x, y))-> printf "rotate(%g, %g, %g)" angle x y - SkewX x -> printf "skewX(%g)" x - SkewY y -> printf "skewY(%g)" y - --- | Transform a list of transformations to a string for svg --- `transform` attributes. -serializeTransformations :: [Transformation] -> String -serializeTransformations = - unwords . fmap serializeTransformation - --- | Class helping find the drawing attributes for all --- the SVG attributes. -class WithDrawAttributes a where - -- | Lens which can be used to read/write primitives. - drawAttr :: Lens' a DrawAttributes - --- | Define an empty 'default' element for the SVG tree. --- It is used as base when parsing the element from XML. -class WithDefaultSvg a where - -- | The default element. - defaultSvg :: a - --- | Classify the font style, used to search a matching --- font in the FontCache. -data FontStyle - = FontStyleNormal - | FontStyleItalic - | FontStyleOblique - deriving (Eq, Show) - --- | Tell where to anchor the text, where the position --- given is realative to the text. -data TextAnchor - -- | The text with left aligned, or start at the postion - -- If the point is the '*' then the text will be printed - -- this way: - -- - -- > *THE_TEXT_TO_PRINT - -- - -- Equivalent to the `start` value. - = TextAnchorStart - -- | The text is middle aligned, so the text will be at - -- the left and right of the position: - -- - -- > THE_TEXT*TO_PRINT - -- - -- Equivalent to the `middle` value. - | TextAnchorMiddle - -- | The text is right aligned. - -- - -- > THE_TEXT_TO_PRINT* - -- - -- Equivalent to the `end` value. - | TextAnchorEnd - deriving (Eq, Show) - - --- | Correspond to the possible values of the --- the attributes which are either `none` or --- `url(#elem)` -data ElementRef - = RefNone -- ^ Value for `none` - | Ref String -- ^ Equivalent to `url()` attribute. - deriving (Eq, Show) - --- | This type define how to draw any primitives, --- which color to use, how to stroke the primitives --- and the potential transformations to use. --- --- All these attributes are propagated to the children. -data DrawAttributes = DrawAttributes - { -- | Attribute corresponding to the `stroke-width` - -- SVG attribute. - _strokeWidth :: !(Last Number) - -- | Correspond to the `stroke` attribute. - , _strokeColor :: !(Last Texture) - -- | Define the `stroke-opacity` attribute, the transparency - -- for the "border". - , _strokeOpacity :: !(Maybe Float) - -- | Correspond to the `stroke-linecap` SVG - -- attribute - , _strokeLineCap :: !(Last Cap) - -- | Correspond to the `stroke-linejoin` SVG - -- attribute - , _strokeLineJoin :: !(Last LineJoin) - -- | Define the distance of the miter join, correspond - -- to the `stroke-miterlimit` attritbue. - , _strokeMiterLimit :: !(Last Double) - -- | Define the filling color of the elements. Corresponding - -- to the `fill` attribute. - , _fillColor :: !(Last Texture) - -- | Define the `fill-opacity` attribute, the transparency - -- for the "content". - , _fillOpacity :: !(Maybe Float) - -- | Define the global or group opacity attribute. - , _groupOpacity :: !(Maybe Float) - -- | Content of the `transform` attribute - , _transform :: !(Maybe [Transformation]) - -- | Define the `fill-rule` used during the rendering. - , _fillRule :: !(Last FillRule) - -- | Define the `mask` attribute. - , _maskRef :: !(Last ElementRef) - -- | Define the `clip-path` attribute. - , _clipPathRef :: !(Last ElementRef) - -- | Define the `clip-rule` attribute. - , _clipRule :: !(Last FillRule) - -- | Map to the `class` attribute. Used for the CSS - -- rewriting. - , _attrClass :: ![T.Text] - -- | Map to the `id` attribute. Used for the CSS - -- rewriting. - , _attrId :: !(Maybe String) - -- | Define the start distance of the dashing pattern. - -- Correspond to the `stroke-dashoffset` attribute. - , _strokeOffset :: !(Last Number) - -- | Define the dashing pattern for the lines. Correspond - -- to the `stroke-dasharray` attribute. - , _strokeDashArray :: !(Last [Number]) - -- | Current size of the text, correspond to the - -- `font-size` SVG attribute. - , _fontSize :: !(Last Number) - -- | Define the possible fonts to be used for text rendering. - -- Map to the `font-family` attribute. - , _fontFamily :: !(Last [String]) - -- | Map to the `font-style` attribute. - , _fontStyle :: !(Last FontStyle) - -- | Define how to interpret the text position, correspond - -- to the `text-anchor` attribute. - , _textAnchor :: !(Last TextAnchor) - -- | Define the marker used for the start of the line. - -- Correspond to the `marker-start` attribute. - , _markerStart :: !(Last ElementRef) - -- | Define the marker used for every point of the - -- polyline/path Correspond to the `marker-mid` - -- attribute. - , _markerMid :: !(Last ElementRef) - -- | Define the marker used for the end of the line. - -- Correspond to the `marker-end` attribute. - , _markerEnd :: !(Last ElementRef) - } - deriving (Eq, Show) - --- | Lenses for the DrawAttributes type. -makeClassy ''DrawAttributes - --- | This primitive describe an unclosed suite of --- segments. Correspond to the `<polyline>` tag. -data PolyLine = PolyLine - { -- | drawing attributes of the polyline. - _polyLineDrawAttributes :: !DrawAttributes - - -- | Geometry definition of the polyline. - -- correspond to the `points` attribute - , _polyLinePoints :: ![RPoint] - } - deriving (Eq, Show) - --- | Lenses for the PolyLine type. -makeClassy ''PolyLine - -instance WithDefaultSvg PolyLine where - defaultSvg = PolyLine - { _polyLineDrawAttributes = mempty - , _polyLinePoints = [] - } - - -instance WithDrawAttributes PolyLine where - drawAttr = polyLineDrawAttributes - --- | Primitive decriving polygon composed --- of segements. Correspond to the `<polygon>` --- tag -data Polygon = Polygon - { -- | Drawing attributes for the polygon. - _polygonDrawAttributes :: !DrawAttributes - -- | Points of the polygon. Correspond to - -- the `points` attributes. - , _polygonPoints :: ![RPoint] - } - deriving (Eq, Show) - --- | Lenses for the Polygon type -makeClassy ''Polygon - -instance WithDrawAttributes Polygon where - drawAttr = polygonDrawAttributes - -instance WithDefaultSvg Polygon where - defaultSvg = Polygon - { _polygonDrawAttributes = mempty - , _polygonPoints = [] - } - --- | Define a simple line. Correspond to the --- `<line>` tag. -data Line = Line - { -- | Drawing attributes of line. - _lineDrawAttributes :: !DrawAttributes - -- | First point of the the line, correspond - -- to the `x1` and `y1` attributes. - , _linePoint1 :: !Point - -- | Second point of the the line, correspond - -- to the `x2` and `y2` attributes. - , _linePoint2 :: !Point - } - deriving (Eq, Show) - --- | Lenses for the Line type. -makeClassy ''Line - -instance WithDrawAttributes Line where - drawAttr = lineDrawAttributes - -instance WithDefaultSvg Line where - defaultSvg = Line - { _lineDrawAttributes = mempty - , _linePoint1 = zeroPoint - , _linePoint2 = zeroPoint - } - where zeroPoint = (Num 0, Num 0) - --- | Define a rectangle. Correspond to --- `<rectangle>` svg tag. -data Rectangle = Rectangle - { -- | Rectangle drawing attributes. - _rectDrawAttributes :: !DrawAttributes - -- | Upper left corner of the rectangle, correspond - -- to the attributes `x` and `y`. - , _rectUpperLeftCorner :: !Point - -- | Rectangle width, correspond, strangely, to - -- the `width` attribute. - , _rectWidth :: !Number - -- | Rectangle height, correspond, amazingly, to - -- the `height` attribute. - , _rectHeight :: !Number - -- | Define the rounded corner radius radius - -- of the rectangle. Correspond to the `rx` and - -- `ry` attributes. - , _rectCornerRadius :: !(Number, Number) - } - deriving (Eq, Show) - --- | Lenses for the Rectangle type. -makeClassy ''Rectangle - -instance WithDrawAttributes Rectangle where - drawAttr = rectDrawAttributes - -instance WithDefaultSvg Rectangle where - defaultSvg = Rectangle - { _rectDrawAttributes = mempty - , _rectUpperLeftCorner = (Num 0, Num 0) - , _rectWidth = Num 0 - , _rectHeight = Num 0 - , _rectCornerRadius = (Num 0, Num 0) - } - --- | Type mapping the `<path>` svg tag. -data Path = Path - { -- | Drawing attributes of the path. - _pathDrawAttributes :: !DrawAttributes - -- | Definition of the path, correspond to the - -- `d` attributes. - , _pathDefinition :: ![PathCommand] - } - deriving (Eq, Show) - --- | Lenses for the Path type -makeClassy ''Path - -instance WithDrawAttributes Path where - drawAttr = pathDrawAttributes - -instance WithDefaultSvg Path where - defaultSvg = Path - { _pathDrawAttributes = mempty - , _pathDefinition = [] - } - --- | Define a SVG group, corresponding `<g>` tag. -data Group a = Group - { -- | Group drawing attributes, propagated to all of its - -- children. - _groupDrawAttributes :: !DrawAttributes - -- | Content of the group, corresponding to all the tags - -- inside the `<g>` tag. - , _groupChildren :: ![a] - -- | Mapped to the attribute `viewBox` - , _groupViewBox :: !(Maybe (Double, Double, Double, Double)) - -- | used for symbols only - , _groupAspectRatio :: !PreserveAspectRatio - } - deriving (Eq, Show) - --- | Lenses associated to the Group type. -makeClassy ''Group - -instance WithDrawAttributes (Group a) where - drawAttr = groupDrawAttributes - -instance WithDefaultSvg (Group a) where - defaultSvg = Group - { _groupDrawAttributes = mempty - , _groupChildren = [] - , _groupViewBox = Nothing - , _groupAspectRatio = defaultSvg - } - --- | Define the `<symbol>` tag, equivalent to --- a named group. -newtype Symbol a = - Symbol { _groupOfSymbol :: Group a } - deriving (Eq, Show) - --- | Lenses associated with the Symbol type. -makeLenses ''Symbol - -instance WithDrawAttributes (Symbol a) where - drawAttr = groupOfSymbol . drawAttr - -instance WithDefaultSvg (Symbol a) where - defaultSvg = Symbol defaultSvg - --- | Define a `<circle>`. -data Circle = Circle - { -- | Drawing attributes of the circle. - _circleDrawAttributes :: !DrawAttributes - -- | Define the center of the circle, describe - -- the `cx` and `cy` attributes. - , _circleCenter :: !Point - -- | Radius of the circle, equivalent to the `r` - -- attribute. - , _circleRadius :: !Number - } - deriving (Eq, Show) - --- | Lenses for the Circle type. -makeClassy ''Circle - -instance WithDrawAttributes Circle where - drawAttr = circleDrawAttributes - -instance WithDefaultSvg Circle where - defaultSvg = Circle - { _circleDrawAttributes = mempty - , _circleCenter = (Num 0, Num 0) - , _circleRadius = Num 0 - } - --- | Define an `<ellipse>` -data Ellipse = Ellipse - { -- | Drawing attributes of the ellipse. - _ellipseDrawAttributes :: !DrawAttributes - -- | Center of the ellipse, map to the `cx` - -- and `cy` attributes. - , _ellipseCenter :: !Point - -- | Radius along the X axis, map the - -- `rx` attribute. - , _ellipseXRadius :: !Number - -- | Radius along the Y axis, map the - -- `ry` attribute. - , _ellipseYRadius :: !Number - } - deriving (Eq, Show) - --- | Lenses for the ellipse type. -makeClassy ''Ellipse - -instance WithDrawAttributes Ellipse where - drawAttr = ellipseDrawAttributes - -instance WithDefaultSvg Ellipse where - defaultSvg = Ellipse - { _ellipseDrawAttributes = mempty - , _ellipseCenter = (Num 0, Num 0) - , _ellipseXRadius = Num 0 - , _ellipseYRadius = Num 0 - } - --- | Define an `<image>` tag. -data Image = Image - { -- | Drawing attributes of the image - _imageDrawAttributes :: !DrawAttributes - -- | Position of the image referenced by its - -- upper left corner. - , _imageCornerUpperLeft :: !Point - -- | Image width - , _imageWidth :: !Number - -- | Image Height - , _imageHeight :: !Number - -- | Image href, pointing to the real image. - , _imageHref :: !String - -- | preserveAspectRatio attribute - , _imageAspectRatio :: !PreserveAspectRatio - } - deriving (Eq, Show) - --- | Lenses for the Image type. -makeClassy ''Image - -instance WithDrawAttributes Image where - drawAttr = imageDrawAttributes - -instance WithDefaultSvg Image where - defaultSvg = Image - { _imageDrawAttributes = mempty - , _imageCornerUpperLeft = (Num 0, Num 0) - , _imageWidth = Num 0 - , _imageHeight = Num 0 - , _imageHref = "" - , _imageAspectRatio = defaultSvg - } - --- | Define an `<use>` for a named content. --- Every named content can be reused in the --- document using this element. -data Use = Use - { -- | Position where to draw the "used" element. - -- Correspond to the `x` and `y` attributes. - _useBase :: Point - -- | Referenced name, correspond to `xlink:href` - -- attribute. - , _useName :: String - -- | Define the width of the region where - -- to place the element. Map to the `width` - -- attribute. - , _useWidth :: Maybe Number - -- | Define the height of the region where - -- to place the element. Map to the `height` - -- attribute. - , _useHeight :: Maybe Number - -- | Use draw attributes. - , _useDrawAttributes :: DrawAttributes - } - deriving (Eq, Show) - --- | Lenses for the Use type. -makeClassy ''Use - -instance WithDrawAttributes Use where - drawAttr = useDrawAttributes - -instance WithDefaultSvg Use where - defaultSvg = Use - { _useBase = (Num 0, Num 0) - , _useName = "" - , _useWidth = Nothing - , _useHeight = Nothing - , _useDrawAttributes = mempty - } - --- | Define position information associated to --- `<text>` or `<tspan>` svg tag. -data TextInfo = TextInfo - { _textInfoX :: ![Number] -- ^ `x` attribute. - , _textInfoY :: ![Number] -- ^ `y` attribute. - , _textInfoDX :: ![Number] -- ^ `dx` attribute. - , _textInfoDY :: ![Number] -- ^ `dy` attribute. - , _textInfoRotate :: ![Double] -- ^ `rotate` attribute. - , _textInfoLength :: !(Maybe Number) -- ^ `textLength` attribute. - } - deriving (Eq, Show) - -instance Monoid TextInfo where - mempty = TextInfo [] [] [] [] [] Nothing - mappend (TextInfo x1 y1 dx1 dy1 r1 l1) - (TextInfo x2 y2 dx2 dy2 r2 l2) = - TextInfo (x1 <> x2) (y1 <> y2) - (dx1 <> dx2) (dy1 <> dy2) - (r1 <> r2) - (getLast $ Last l1 <> Last l2) - --- | Lenses for the TextInfo type. -makeClassy ''TextInfo - -instance WithDefaultSvg TextInfo where - defaultSvg = mempty - --- | Define the content of a `<tspan>` tag. -data TextSpanContent - = SpanText !T.Text -- ^ Raw text - | SpanTextRef !String -- ^ Equivalent to a `<tref>` - | SpanSub !TextSpan -- ^ Define a `<tspan>` - deriving (Eq, Show) - --- | Define a `<tspan>` tag. -data TextSpan = TextSpan - { -- | Placing information for the text. - _spanInfo :: !TextInfo - -- | Drawing attributes for the text span. - , _spanDrawAttributes :: !DrawAttributes - -- | Content of the span. - , _spanContent :: ![TextSpanContent] - } - deriving (Eq, Show) - --- | Lenses for the TextSpan type. -makeClassy ''TextSpan - -instance WithDefaultSvg TextSpan where - defaultSvg = TextSpan - { _spanInfo = defaultSvg - , _spanDrawAttributes = mempty - , _spanContent = mempty - } - --- | Describe the content of the `method` attribute on --- text path. -data TextPathMethod - = TextPathAlign -- ^ Map to the `align` value. - | TextPathStretch -- ^ Map to the `stretch` value. - deriving (Eq, Show) - --- | Describe the content of the `spacing` text path --- attribute. -data TextPathSpacing - = TextPathSpacingExact -- ^ Map to the `exact` value. - | TextPathSpacingAuto -- ^ Map to the `auto` value. - deriving (Eq, Show) - --- | Describe the `<textpath>` SVG tag. -data TextPath = TextPath - { -- | Define the beginning offset on the path, - -- the `startOffset` attribute. - _textPathStartOffset :: !Number - -- | Define the `xlink:href` attribute. - , _textPathName :: !String - -- | Correspond to the `method` attribute. - , _textPathMethod :: !TextPathMethod - -- | Correspond to the `spacing` attribute. - , _textPathSpacing :: !TextPathSpacing - -- | Real content of the path. - , _textPathData :: ![PathCommand] - } - deriving (Eq, Show) - --- | Lenses for the TextPath type. -makeClassy ''TextPath - -instance WithDefaultSvg TextPath where - defaultSvg = TextPath - { _textPathStartOffset = Num 0 - , _textPathName = mempty - , _textPathMethod = TextPathAlign - , _textPathSpacing = TextPathSpacingExact - , _textPathData = [] - } - --- | Define the possible values of the `lengthAdjust` --- attribute. -data TextAdjust - = TextAdjustSpacing -- ^ Value `spacing` - | TextAdjustSpacingAndGlyphs -- ^ Value `spacingAndGlyphs` - deriving (Eq, Show) - --- | Define the global `<tag>` SVG tag. -data Text = Text - { -- | Define the `lengthAdjust` attribute. - _textAdjust :: !TextAdjust - -- | Root of the text content. - , _textRoot :: !TextSpan - } - deriving (Eq, Show) - --- | Lenses for the Text type. -makeClassy ''Text - --- | Little helper to create a SVG text at a given --- baseline position. -textAt :: Point -> T.Text -> Text -textAt (x, y) txt = Text TextAdjustSpacing tspan where - tspan = defaultSvg - { _spanContent = [SpanText txt] - , _spanInfo = defaultSvg - { _textInfoX = [x] - , _textInfoY = [y] - } - } - -instance WithDrawAttributes Text where - drawAttr = textRoot . spanDrawAttributes - -instance WithDefaultSvg Text where - defaultSvg = Text - { _textRoot = defaultSvg - , _textAdjust = TextAdjustSpacing - } - --- | Main type for the scene description, reorient to --- specific type describing each tag. -data Tree - = None - | UseTree { useInformation :: !Use - , useSubTree :: !(Maybe Tree) } - | GroupTree !(Group Tree) - | SymbolTree !(Symbol Tree) - | PathTree !Path - | CircleTree !Circle - | PolyLineTree !PolyLine - | PolygonTree !Polygon - | EllipseTree !Ellipse - | LineTree !Line - | RectangleTree !Rectangle - | TextTree !(Maybe TextPath) !Text - | ImageTree !Image - deriving (Eq, Show) - --- | Define the orientation, associated to the --- `orient` attribute on the Marker -data MarkerOrientation - = OrientationAuto -- ^ Auto value - | OrientationAngle Coord -- ^ Specific angle. - deriving (Eq, Show) - --- | Define the content of the `markerUnits` attribute --- on the Marker. -data MarkerUnit - = MarkerUnitStrokeWidth -- ^ Value `strokeWidth` - | MarkerUnitUserSpaceOnUse -- ^ Value `userSpaceOnUse` - deriving (Eq, Show) - --- | Define the content of the `markerUnits` attribute --- on the Marker. -data Overflow - = OverflowVisible -- ^ Value `visible` - | OverflowHidden -- ^ Value `hidden` - deriving (Eq, Show) - --- | Define the `<marker>` tag. -data Marker = Marker - { -- | Draw attributes of the marker. - _markerDrawAttributes :: DrawAttributes - -- | Define the reference point of the marker. - -- correspond to the `refX` and `refY` attributes. - , _markerRefPoint :: !(Number, Number) - -- | Define the width of the marker. Correspond to - -- the `markerWidth` attribute. - , _markerWidth :: !(Maybe Number) - -- | Define the height of the marker. Correspond to - -- the `markerHeight` attribute. - , _markerHeight :: !(Maybe Number) - -- | Correspond to the `orient` attribute. - , _markerOrient :: !(Maybe MarkerOrientation) - -- | Map the `markerUnits` attribute. - , _markerUnits :: !(Maybe MarkerUnit) - -- | Optional viewbox - , _markerViewBox :: !(Maybe (Double, Double, Double, Double)) - -- | Elements defining the marker. - , _markerOverflow :: !(Maybe Overflow) - -- | preserveAspectRatio attribute - , _markerAspectRatio :: !PreserveAspectRatio - -- | Elements defining the marker. - , _markerElements :: [Tree] - } - deriving (Eq, Show) - --- | Lenses for the Marker type. -makeClassy ''Marker - -instance WithDrawAttributes Marker where - drawAttr = markerDrawAttributes - -instance WithDefaultSvg Marker where - defaultSvg = Marker - { _markerDrawAttributes = mempty - , _markerRefPoint = (Num 0, Num 0) - , _markerWidth = Just (Num 3) - , _markerHeight = Just (Num 3) - , _markerOrient = Nothing -- MarkerOrientation - , _markerUnits = Nothing -- MarkerUnitStrokeWidth - , _markerViewBox = Nothing - , _markerOverflow = Nothing - , _markerElements = mempty - , _markerAspectRatio = defaultSvg - } - --- | Insert element in the first sublist in the list of list. -appNode :: [[a]] -> a -> [[a]] -appNode [] e = [[e]] -appNode (curr:above) e = (e:curr) : above - --- | Map a tree while propagating context information. --- The function passed in parameter receive a list --- representing the the path used to go arrive to the --- current node. -zipTree :: ([[Tree]] -> Tree) -> Tree -> Tree -zipTree f = dig [] where - dig prev e@None = f $ appNode prev e - dig prev e@(UseTree _ Nothing) = f $ appNode prev e - dig prev e@(UseTree nfo (Just u)) = - f . appNode prev . UseTree nfo . Just $ dig ([] : appNode prev e) u - dig prev e@(GroupTree g) = - f . appNode prev . GroupTree $ zipGroup (appNode prev e) g - dig prev e@(SymbolTree g) = - f . appNode prev . SymbolTree . Symbol . - zipGroup (appNode prev e) $ _groupOfSymbol g - dig prev e@(PathTree _) = f $ appNode prev e - dig prev e@(CircleTree _) = f $ appNode prev e - dig prev e@(PolyLineTree _) = f $ appNode prev e - dig prev e@(PolygonTree _) = f $ appNode prev e - dig prev e@(EllipseTree _) = f $ appNode prev e - dig prev e@(LineTree _) = f $ appNode prev e - dig prev e@(RectangleTree _) = f $ appNode prev e - dig prev e@(TextTree _ _) = f $ appNode prev e - dig prev e@(ImageTree _) = f $ appNode prev e - - zipGroup prev g = g { _groupChildren = updatedChildren } - where - groupChild = _groupChildren g - updatedChildren = - [dig (c:prev) child - | (child, c) <- zip groupChild $ inits groupChild] - --- | Fold all nodes of a SVG tree. -foldTree :: (a -> Tree -> a) -> a -> Tree -> a -foldTree f = go where - go acc e = case e of - None -> f acc e - UseTree _ _ -> f acc e - PathTree _ -> f acc e - CircleTree _ -> f acc e - PolyLineTree _ -> f acc e - PolygonTree _ -> f acc e - EllipseTree _ -> f acc e - LineTree _ -> f acc e - RectangleTree _ -> f acc e - TextTree _ _ -> f acc e - ImageTree _ -> f acc e - GroupTree g -> - let subAcc = F.foldl' go acc $ _groupChildren g in - f subAcc e - SymbolTree s -> - let subAcc = - F.foldl' go acc . _groupChildren $ _groupOfSymbol s in - f subAcc e - --- | Helper function mapping every tree element. -mapTree :: (Tree -> Tree) -> Tree -> Tree -mapTree f = go where - go e@None = f e - go e@(UseTree _ _) = f e - go (GroupTree g) = f . GroupTree $ mapGroup g - go (SymbolTree g) = - f . SymbolTree . Symbol . mapGroup $ _groupOfSymbol g - go e@(PathTree _) = f e - go e@(CircleTree _) = f e - go e@(PolyLineTree _) = f e - go e@(PolygonTree _) = f e - go e@(EllipseTree _) = f e - go e@(LineTree _) = f e - go e@(RectangleTree _) = f e - go e@(TextTree _ _) = f e - go e@(ImageTree _) = f e - - mapGroup g = - g { _groupChildren = map go $ _groupChildren g } - --- | For every element of a svg tree, associate --- it's SVG tag name. -nameOfTree :: Tree -> T.Text -nameOfTree v = - case v of - None -> "" - UseTree _ _ -> "use" - GroupTree _ -> "g" - SymbolTree _ -> "symbol" - PathTree _ -> "path" - CircleTree _ -> "circle" - PolyLineTree _ -> "polyline" - PolygonTree _ -> "polygon" - EllipseTree _ -> "ellipse" - LineTree _ -> "line" - RectangleTree _ -> "rectangle" - TextTree _ _ -> "text" - ImageTree _ -> "image" - -drawAttrOfTree :: Tree -> DrawAttributes -drawAttrOfTree v = case v of - None -> mempty - UseTree e _ -> e ^. drawAttr - GroupTree e -> e ^. drawAttr - SymbolTree e -> e ^. drawAttr - PathTree e -> e ^. drawAttr - CircleTree e -> e ^. drawAttr - PolyLineTree e -> e ^. drawAttr - PolygonTree e -> e ^. drawAttr - EllipseTree e -> e ^. drawAttr - LineTree e -> e ^. drawAttr - RectangleTree e -> e ^. drawAttr - TextTree _ e -> e ^. drawAttr - ImageTree e -> e ^. drawAttr - -setDrawAttrOfTree :: Tree -> DrawAttributes -> Tree -setDrawAttrOfTree v attr = case v of - None -> None - UseTree e m -> UseTree (e & drawAttr .~ attr) m - GroupTree e -> GroupTree $ e & drawAttr .~ attr - SymbolTree e -> SymbolTree $ e & drawAttr .~ attr - PathTree e -> PathTree $ e & drawAttr .~ attr - CircleTree e -> CircleTree $ e & drawAttr .~ attr - PolyLineTree e -> PolyLineTree $ e & drawAttr .~ attr - PolygonTree e -> PolygonTree $ e & drawAttr .~ attr - EllipseTree e -> EllipseTree $ e & drawAttr .~ attr - LineTree e -> LineTree $ e & drawAttr .~ attr - RectangleTree e -> RectangleTree $ e & drawAttr .~ attr - TextTree a e -> TextTree a $ e & drawAttr .~ attr - ImageTree e -> ImageTree $ e & drawAttr .~ attr - -instance WithDrawAttributes Tree where - drawAttr = lens drawAttrOfTree setDrawAttrOfTree - -instance WithDefaultSvg Tree where - defaultSvg = None - --- | Define the possible values of various *units attributes --- used in the definition of the gradients and masks. -data CoordinateUnits - = CoordUserSpace -- ^ `userSpaceOnUse` value - | CoordBoundingBox -- ^ `objectBoundingBox` value - deriving (Eq, Show) - --- | Define the possible values for the `spreadMethod` --- values used for the gradient definitions. -data Spread - = SpreadRepeat -- ^ `reapeat` value - | SpreadPad -- ^ `pad` value - | SpreadReflect -- ^ `reflect value` - deriving (Eq, Show) - --- | Define a color stop for the gradients. Represent --- the `<stop>` SVG tag. -data GradientStop = GradientStop - { -- | Gradient offset between 0 and 1, correspond - -- to the `offset` attribute. - _gradientOffset :: Float - -- | Color of the gradient stop. Correspond - -- to the `stop-color` attribute. - , _gradientColor :: PixelRGBA8 - } - deriving (Eq, Show) - --- | Lenses for the GradientStop type. -makeClassy ''GradientStop - -instance WithDefaultSvg GradientStop where - defaultSvg = GradientStop - { _gradientOffset = 0.0 - , _gradientColor = PixelRGBA8 0 0 0 255 - } - --- | Define a `<linearGradient>` tag. -data LinearGradient = LinearGradient - { -- | Define coordinate system of the gradient, - -- associated to the `gradientUnits` attribute. - _linearGradientUnits :: CoordinateUnits - -- | Point defining the beginning of the line gradient. - -- Associated to the `x1` and `y1` attribute. - , _linearGradientStart :: Point - -- | Point defining the end of the line gradient. - -- Associated to the `x2` and `y2` attribute. - , _linearGradientStop :: Point - -- | Define how to handle the values outside - -- the gradient start and stop. Associated to the - -- `spreadMethod` attribute. - , _linearGradientSpread :: Spread - -- | Define the transformation to apply to the - -- gradient points. Associated to the `gradientTransform` - -- attribute. - , _linearGradientTransform :: [Transformation] - -- | List of color stops of the linear gradient. - , _linearGradientStops :: [GradientStop] - } - deriving (Eq, Show) - --- | Lenses for the LinearGradient type. -makeClassy ''LinearGradient - -instance WithDefaultSvg LinearGradient where - defaultSvg = LinearGradient - { _linearGradientUnits = CoordBoundingBox - , _linearGradientStart = (Percent 0, Percent 0) - , _linearGradientStop = (Percent 1, Percent 0) - , _linearGradientSpread = SpreadPad - , _linearGradientTransform = [] - , _linearGradientStops = [] - } - --- | Define a `<radialGradient>` tag. -data RadialGradient = RadialGradient - { -- | Define coordinate system of the gradient, - -- associated to the `gradientUnits` attribute. - _radialGradientUnits :: CoordinateUnits - -- | Center of the radial gradient. Associated to - -- the `cx` and `cy` attributes. - , _radialGradientCenter :: Point - -- | Radius of the radial gradient. Associated to - -- the `r` attribute. - , _radialGradientRadius :: Number - -- | X coordinate of the focus point of the radial - -- gradient. Associated to the `fx` attribute. - , _radialGradientFocusX :: Maybe Number - -- | Y coordinate of the focus point of the radial - -- gradient. Associated to the `fy` attribute. - , _radialGradientFocusY :: Maybe Number - -- | Define how to handle the values outside - -- the gradient start and stop. Associated to the - -- `spreadMethod` attribute. - , _radialGradientSpread :: Spread - -- | Define the transformation to apply to the - -- gradient points. Associated to the `gradientTransform` - -- attribute. - , _radialGradientTransform :: [Transformation] - -- | List of color stops of the radial gradient. - , _radialGradientStops :: [GradientStop] - } - deriving (Eq, Show) - --- | Lenses for the RadialGradient type. -makeClassy ''RadialGradient - -instance WithDefaultSvg RadialGradient where - defaultSvg = RadialGradient - { _radialGradientUnits = CoordBoundingBox - , _radialGradientCenter = (Percent 0.5, Percent 0.5) - , _radialGradientRadius = Percent 0.5 - , _radialGradientFocusX = Nothing - , _radialGradientFocusY = Nothing - , _radialGradientSpread = SpreadPad - , _radialGradientTransform = [] - , _radialGradientStops = [] - } - --- | Define a SVG `<mask>` tag. -data Mask = Mask - { -- | Drawing attributes of the Mask - _maskDrawAttributes :: DrawAttributes - -- | Correspond to the `maskContentUnits` attributes. - , _maskContentUnits :: CoordinateUnits - -- | Mapping to the `maskUnits` attribute. - , _maskUnits :: CoordinateUnits - -- | Map to the `x` and `y` attributes. - , _maskPosition :: Point - -- | Map to the `width` attribute - , _maskWidth :: Number - -- | Map to the `height` attribute. - , _maskHeight :: Number - -- | Children of the `<mask>` tag. - , _maskContent :: [Tree] - } - deriving (Eq, Show) - --- | Lenses for the Mask type. -makeClassy ''Mask - -instance WithDrawAttributes Mask where - drawAttr = maskDrawAttributes - -instance WithDefaultSvg Mask where - defaultSvg = Mask - { _maskDrawAttributes = mempty - , _maskContentUnits = CoordUserSpace - , _maskUnits = CoordBoundingBox - , _maskPosition = (Percent (-0.1), Percent (-0.1)) - , _maskWidth = Percent 1.2 - , _maskHeight = Percent 1.2 - , _maskContent = [] - } - --- | Define a `<clipPath>` tag. -data ClipPath = ClipPath - { _clipPathDrawAttributes :: DrawAttributes - -- | Maps to the `clipPathUnits` attribute - , _clipPathUnits :: CoordinateUnits - -- | Maps to the content of the tree - , _clipPathContent :: [Tree] - } - deriving (Eq, Show) - --- | Lenses for the ClipPath type. -makeClassy ''ClipPath - -instance WithDrawAttributes ClipPath where - drawAttr = clipPathDrawAttributes - -instance WithDefaultSvg ClipPath where - defaultSvg = ClipPath - { _clipPathDrawAttributes = mempty - , _clipPathUnits = CoordUserSpace - , _clipPathContent = mempty - } - --- | Define a `<pattern>` tag. -data Pattern = Pattern - { -- | Pattern drawing attributes. - _patternDrawAttributes :: !DrawAttributes - -- | Possible `viewBox`. - , _patternViewBox :: !(Maybe (Double, Double, Double, Double)) - -- | Width of the pattern tile, mapped to the - -- `width` attribute - , _patternWidth :: !Number - -- | Height of the pattern tile, mapped to the - -- `height` attribute - , _patternHeight :: !Number - -- | Pattern tile base, mapped to the `x` and - -- `y` attributes. - , _patternPos :: !Point - -- | Elements used in the pattern. - , _patternElements :: ![Tree] - -- | Define the cordinate system to use for - -- the pattern. Mapped to the `patternUnits` - -- attribute. - , _patternUnit :: !CoordinateUnits - -- | Value of the "preserveAspectRatio" attribute - , _patternAspectRatio :: !PreserveAspectRatio - } - deriving Show - --- | Lenses for the Patter type. -makeClassy ''Pattern - -instance WithDrawAttributes Pattern where - drawAttr = patternDrawAttributes - -instance WithDefaultSvg Pattern where - defaultSvg = Pattern - { _patternViewBox = Nothing - , _patternWidth = Num 0 - , _patternHeight = Num 0 - , _patternPos = (Num 0, Num 0) - , _patternElements = [] - , _patternUnit = CoordBoundingBox - , _patternDrawAttributes = mempty - , _patternAspectRatio = defaultSvg - } - --- | Sum types helping keeping track of all the namable --- elemens in a SVG document. -data Element - = ElementLinearGradient LinearGradient - | ElementRadialGradient RadialGradient - | ElementGeometry Tree - | ElementPattern Pattern - | ElementMarker Marker - | ElementMask Mask - | ElementClipPath ClipPath - deriving Show - --- | Represent a full svg document with style, --- geometry and named elements. -data Document = Document - { _viewBox :: Maybe (Double, Double, Double, Double) - , _width :: Maybe Number - , _height :: Maybe Number - , _elements :: [Tree] - , _definitions :: M.Map String Element - , _description :: String - , _styleRules :: [CssRule] - , _documentLocation :: FilePath - } - deriving Show - --- | Lenses associated to a SVG document. -makeClassy ''Document - --- | Calculate the document size in function of the --- different available attributes in the document. -documentSize :: Dpi -> Document -> (Int, Int) -documentSize _ Document { _viewBox = Just (x1, y1, x2, y2) - , _width = Just (Percent pw) - , _height = Just (Percent ph) - } = - (floor $ dx * pw, floor $ dy * ph) - where - dx = abs $ x2 - x1 - dy = abs $ y2 - y1 -documentSize _ Document { _width = Just (Num w) - , _height = Just (Num h) } = (floor w, floor h) -documentSize dpi doc@(Document { _width = Just w - , _height = Just h }) = - documentSize dpi $ doc - { _width = Just $ toUserUnit dpi w - , _height = Just $ toUserUnit dpi h } -documentSize _ Document { _viewBox = Just (x1, y1, x2, y2) } = - (floor . abs $ x2 - x1, floor . abs $ y2 - y1) -documentSize _ _ = (1, 1) - -mayMerge :: Monoid a => Maybe a -> Maybe a -> Maybe a -mayMerge (Just a) (Just b) = Just $ mappend a b -mayMerge _ b@(Just _) = b -mayMerge a Nothing = a - -instance Monoid DrawAttributes where - mempty = DrawAttributes - { _strokeWidth = Last Nothing - , _strokeColor = Last Nothing - , _strokeOpacity = Nothing - , _strokeLineCap = Last Nothing - , _strokeLineJoin = Last Nothing - , _strokeMiterLimit = Last Nothing - , _fillColor = Last Nothing - , _groupOpacity = Nothing - , _fillOpacity = Nothing - , _fontSize = Last Nothing - , _fontFamily = Last Nothing - , _fontStyle = Last Nothing - , _transform = Nothing - , _fillRule = Last Nothing - , _attrClass = mempty - , _attrId = Nothing - , _strokeOffset = Last Nothing - , _strokeDashArray = Last Nothing - , _textAnchor = Last Nothing - , _maskRef = Last Nothing - , _clipPathRef = Last Nothing - , _clipRule = Last Nothing - - , _markerStart = Last Nothing - , _markerMid = Last Nothing - , _markerEnd = Last Nothing - } - - mappend a b = DrawAttributes - { _strokeWidth = (mappend `on` _strokeWidth) a b - , _strokeColor = (mappend `on` _strokeColor) a b - , _strokeLineCap = (mappend `on` _strokeLineCap) a b - , _strokeOpacity = (opacityMappend `on` _strokeOpacity) a b - , _strokeLineJoin = (mappend `on` _strokeLineJoin) a b - , _strokeMiterLimit = (mappend `on` _strokeMiterLimit) a b - , _fillColor = (mappend `on` _fillColor) a b - , _fillOpacity = (opacityMappend `on` _fillOpacity) a b - , _fontSize = (mappend `on` _fontSize) a b - , _transform = (mayMerge `on` _transform) a b - , _fillRule = (mappend `on` _fillRule) a b - , _attrClass = _attrClass b - , _attrId = _attrId b - , _groupOpacity = _groupOpacity b - , _strokeOffset = (mappend `on` _strokeOffset) a b - , _strokeDashArray = (mappend `on` _strokeDashArray) a b - , _fontFamily = (mappend `on` _fontFamily) a b - , _fontStyle = (mappend `on` _fontStyle) a b - , _textAnchor = (mappend `on` _textAnchor) a b - , _maskRef = (mappend `on` _maskRef) a b - , _clipPathRef = (mappend `on` _clipPathRef) a b - , _clipRule = (mappend `on` _clipRule) a b - , _markerStart = (mappend `on` _markerStart) a b - , _markerMid = (mappend `on` _markerMid) a b - , _markerEnd = (mappend `on` _markerEnd) a b - } - where - opacityMappend Nothing Nothing = Nothing - opacityMappend (Just v) Nothing = Just v - opacityMappend Nothing (Just v) = Just v - opacityMappend (Just v) (Just v2) = Just $ v * v2 - -instance WithDefaultSvg DrawAttributes where - defaultSvg = mempty - -instance CssMatcheable Tree where - cssAttribOf _ _ = Nothing - cssClassOf = view (drawAttr . attrClass) - cssIdOf = fmap T.pack . view (drawAttr . attrId) - cssNameOf = nameOfTree - +{-# LANGUAGE TemplateHaskell #-}+{-# LANGUAGE OverloadedStrings #-}+{-# LANGUAGE RankNTypes #-}+{-# LANGUAGE MultiParamTypeClasses #-}+{-# LANGUAGE FunctionalDependencies #-}+{-# LANGUAGE FlexibleInstances #-}+{-# LANGUAGE CPP #-}+-- | This module define all the types used in the definition+-- of a svg scene.+--+-- Most of the types are lensified.+module Graphics.Svg.Types+ ( -- * Basic building types+ Coord+ , Origin( .. )+ , Point+ , RPoint+ , PathCommand( .. )+ , Transformation( .. )+ , ElementRef( .. )+ , CoordinateUnits( .. )++ -- ** Building helpers+ , toPoint+ , serializeNumber+ , serializeTransformation+ , serializeTransformations++ -- * Drawing control types+ , Cap( .. )+ , LineJoin( .. )+ , Tree( .. )+ , Number( .. )+ , Spread( .. )+ , Texture( .. )+ , Element( .. )+ , FillRule( .. )+ , FontStyle( .. )+ , Dpi++ , WithDefaultSvg( .. )++ -- * Main type+ , Document( .. )+ , HasDocument( .. )+ , documentSize++ -- * Drawing attributes+ , DrawAttributes( .. )+ , HasDrawAttributes( .. )+ , WithDrawAttributes( .. )++ -- * SVG drawing primitives+ -- ** Rectangle+ , Rectangle( .. )+ , HasRectangle( .. )++ -- ** Line+ , Line( .. )+ , HasLine( .. )++ -- ** Polygon+ , Polygon( .. )+ , HasPolygon( .. )++ -- ** Polyline+ , PolyLine( .. )+ , HasPolyLine( .. )++ -- ** Path+ , Path( .. )+ , HasPath( .. )++ -- ** Circle+ , Circle( .. )+ , HasCircle( .. )++ -- ** Ellipse+ , Ellipse( .. )+ , HasEllipse( .. )++ -- ** Mesh (gradient mesh)+ , GradientPathCommand( .. )+ , MeshGradientType( .. )++ , MeshGradient( .. )+ , HasMeshGradient( .. )++ , MeshGradientRow( .. )+ , HasMeshGradientRow( .. )++ , MeshGradientPatch( .. )+ , HasMeshGradientPatch( .. )++ -- ** Image+ , Image( .. )+ , HasImage( .. )++ -- ** Use+ , Use( .. )+ , HasUse( .. )++ -- * Grouping primitives+ -- ** Group+ , Group( .. )+ , HasGroup( .. )++ -- ** Symbol+ , Symbol( .. )+ , groupOfSymbol++ -- * Text related types+ -- ** Text+ , Text( .. )+ , HasText( .. )+ , TextAnchor( .. )+ , textAt++ -- ** Text path+ , TextPath( .. )+ , HasTextPath( .. )++ , TextPathSpacing( .. )+ , TextPathMethod( .. )++ -- ** Text span.+ , TextSpanContent( .. )++ , TextSpan( .. )+ , HasTextSpan( .. )++ , TextInfo( .. )+ , HasTextInfo( .. )++ , TextAdjust( .. )++ -- * Marker definition+ , Marker( .. )+ , Overflow( .. )+ , MarkerOrientation( .. )+ , MarkerUnit( .. )+ , HasMarker( .. )++ -- * Gradient definition+ , GradientStop( .. )+ , HasGradientStop( .. )++ -- ** Linear Gradient+ , LinearGradient( .. )+ , HasLinearGradient( .. )++ -- ** Radial Gradient+ , RadialGradient( .. )+ , HasRadialGradient( .. )++ -- * Pattern definition+ , Pattern( .. )+ , HasPattern( .. )++ -- * Mask definition+ , Mask( .. )+ , HasMask( .. )++ -- * Clip path definition+ , ClipPath( .. )+ , HasClipPath( .. )++ -- * Aspect Ratio description+ , PreserveAspectRatio( .. )+ , Alignment( .. )+ , MeetSlice( .. )+ , HasPreserveAspectRatio( .. )++ -- * MISC functions+ , isPathArc+ , isPathWithArc+ , nameOfTree+ , zipTree+ , mapTree+ , foldTree+ , toUserUnit+ , mapNumber+ ) where++#if !MIN_VERSION_base(4,8,0)+import Data.Monoid( Monoid( .. ) )+import Data.Foldable( Foldable )+#endif++import Data.Function( on )+import Data.List( inits )+import qualified Data.Map as M+import Data.Monoid( Last( .. ), (<>) )+import qualified Data.Foldable as F+import qualified Data.Text as T+import Codec.Picture( PixelRGBA8( .. ) )+import Control.Lens( Lens'+ , lens+ , makeClassy+ , makeLenses+ , view+ , (^.)+ , (&)+ , (.~)+ )+import Graphics.Svg.CssTypes+import Linear hiding ( angle )++import Text.Printf++-- | Basic coordiante type.+type Coord = Double++-- | Real Point, fully determined and not+-- dependant of the rendering context.+type RPoint = V2 Coord++-- | Possibly context dependant point.+type Point = (Number, Number)++-- | Tell if a path command is absolute (in the current+-- user coordiante) or relative to the previous poitn.+data Origin+ = OriginAbsolute -- ^ Next point in absolute coordinate+ | OriginRelative -- ^ Next point relative to the previous+ deriving (Eq, Show)++data MeshGradientType+ = GradientBilinear+ | GradientBicubic+ deriving (Eq, Show)++-- | Path command definition.+data PathCommand+ -- | 'M' or 'm' command+ = MoveTo !Origin ![RPoint]+ -- | Line to, 'L' or 'l' Svg path command.+ | LineTo !Origin ![RPoint]++ -- | Equivalent to the 'H' or 'h' svg path command.+ | HorizontalTo !Origin ![Coord]+ -- | Equivalent to the 'V' or 'v' svg path command.+ | VerticalTo !Origin ![Coord]++ -- | Cubic bezier, 'C' or 'c' command+ | CurveTo !Origin ![(RPoint, RPoint, RPoint)]+ -- | Smooth cubic bezier, equivalent to 'S' or 's' command+ | SmoothCurveTo !Origin ![(RPoint, RPoint)]+ -- | Quadratic bezier, 'Q' or 'q' command+ | QuadraticBezier !Origin ![(RPoint, RPoint)]+ -- | Quadratic bezier, 'T' or 't' command+ | SmoothQuadraticBezierCurveTo !Origin ![RPoint]+ -- | Eliptical arc, 'A' or 'a' command.+ | EllipticalArc !Origin ![(Coord, Coord, Coord, Bool, Bool, RPoint)]+ -- | Close the path, 'Z' or 'z' svg path command.+ | EndPath+ deriving (Eq, Show)++-- | Description of path used in meshgradient tag+data GradientPathCommand+ -- | Line to, 'L' or 'l' Svg path command.+ = GLine !Origin !(Maybe RPoint)+ -- | Cubic bezier, 'C' or 'c' command+ | GCurve !Origin !RPoint !RPoint !(Maybe RPoint)+ -- | 'Z' command+ | GClose+ deriving (Eq, Show)++-- | Little helper function to build a point.+toPoint :: Number -> Number -> Point+toPoint = (,)++-- | Tell if the path command is an EllipticalArc.+isPathArc :: PathCommand -> Bool+isPathArc (EllipticalArc _ _) = True+isPathArc _ = False++-- | Tell if a full path contain an EllipticalArc.+isPathWithArc :: Foldable f => f PathCommand -> Bool+isPathWithArc = F.any isPathArc++-- | Define the possible values of various *units attributes+-- used in the definition of the gradients and masks.+data CoordinateUnits+ = CoordUserSpace -- ^ `userSpaceOnUse` value+ | CoordBoundingBox -- ^ `objectBoundingBox` value+ deriving (Eq, Show)++-- | This type represent the align information of the+-- preserveAspectRatio SVGattribute+data Alignment+ = AlignNone -- ^ "none" value+ | AlignxMinYMin -- "xMinYMin" value+ | AlignxMidYMin -- ^ "xMidYMin" value+ | AlignxMaxYMin -- ^ "xMaxYMin" value+ | AlignxMinYMid -- ^ "xMinYMid" value+ | AlignxMidYMid -- ^ "xMidYMid" value+ | AlignxMaxYMid -- ^ "xMaxYMid" value+ | AlignxMinYMax -- ^ "xMinYMax" value+ | AlignxMidYMax -- ^ "xMidYMax" value+ | AlignxMaxYMax -- ^ "xMaxYMax" value+ deriving (Eq, Show)++-- | This type represent the "meet or slice" information+-- of the preserveAspectRatio SVGattribute+data MeetSlice = Meet | Slice+ deriving (Eq, Show)++-- | Describe the content of the preserveAspectRatio attribute.+data PreserveAspectRatio = PreserveAspectRatio+ { _aspectRatioDefer :: !Bool+ , _aspectRatioAlign :: !Alignment+ , _aspectRatioMeetSlice :: !(Maybe MeetSlice)+ }+ deriving (Eq, Show)++-- | Lenses for the PreserveAspectRatio type+makeClassy ''PreserveAspectRatio ++instance WithDefaultSvg PreserveAspectRatio where+ defaultSvg = PreserveAspectRatio + { _aspectRatioDefer = False+ , _aspectRatioAlign = AlignxMidYMid+ , _aspectRatioMeetSlice = Nothing+ }++-- | Describe how the line should be terminated+-- when stroking them. Describe the values of the+-- `stroke-linecap` attribute.+-- See `_strokeLineCap`+data Cap+ = CapRound -- ^ End with a round (`round` value)+ | CapButt -- ^ Define straight just at the end (`butt` value)+ | CapSquare -- ^ Straight further of the ends (`square` value)+ deriving (Eq, Show)++-- | Define the possible values of the `stroke-linejoin`+-- attribute.+-- see `_strokeLineJoin`+data LineJoin+ = JoinMiter -- ^ `miter` value+ | JoinBevel -- ^ `bevel` value+ | JoinRound -- ^ `round` value+ deriving (Eq, Show)++-- | Describe the different value which can be used+-- in the `fill` or `stroke` attributes.+data Texture+ = ColorRef PixelRGBA8 -- ^ Direct solid color (#rrggbb, #rgb)+ | TextureRef String -- ^ Link to a complex texture (url(#name))+ | FillNone -- ^ Equivalent to the `none` value.+ deriving (Eq, Show)++-- | Describe the possile filling algorithms.+-- Map the values of the `fill-rule` attributes.+data FillRule+ = FillEvenOdd -- ^ Correspond to the `evenodd` value.+ | FillNonZero -- ^ Correspond to the `nonzero` value.+ deriving (Eq, Show)++-- | Describe the content of the `transformation` attribute.+-- see `_transform` and `transform`.+data Transformation+ = -- | Directly encode the translation matrix.+ TransformMatrix !Coord !Coord !Coord+ !Coord !Coord !Coord+ -- | Translation along a vector+ | Translate !Double !Double+ -- | Scaling on both axis or on X axis and Y axis.+ | Scale !Double !(Maybe Double)+ -- | Rotation around `(0, 0)` or around an optional+ -- point.+ | Rotate !Double !(Maybe (Double, Double))+ -- | Skew transformation along the X axis.+ | SkewX !Double+ -- | Skew transformation along the Y axis.+ | SkewY !Double+ -- | Unkown transformation, like identity.+ | TransformUnknown+ deriving (Eq, Show)++-- | Convert the Transformation to a string which can be+-- directly used in a svg attributes.+serializeTransformation :: Transformation -> String+serializeTransformation t = case t of+ TransformUnknown -> ""+ TransformMatrix a b c d e f ->+ printf "matrix(%g, %g, %g, %g, %g, %g)" a b c d e f+ Translate x y -> printf "translate(%g, %g)" x y+ Scale x Nothing -> printf "scale(%g)" x+ Scale x (Just y) -> printf "scale(%g, %g)" x y+ Rotate angle Nothing -> printf "rotate(%g)" angle+ Rotate angle (Just (x, y))-> printf "rotate(%g, %g, %g)" angle x y+ SkewX x -> printf "skewX(%g)" x+ SkewY y -> printf "skewY(%g)" y++-- | Transform a list of transformations to a string for svg+-- `transform` attributes.+serializeTransformations :: [Transformation] -> String+serializeTransformations =+ unwords . fmap serializeTransformation++-- | Class helping find the drawing attributes for all+-- the SVG attributes.+class WithDrawAttributes a where+ -- | Lens which can be used to read/write primitives.+ drawAttr :: Lens' a DrawAttributes++-- | Define an empty 'default' element for the SVG tree.+-- It is used as base when parsing the element from XML.+class WithDefaultSvg a where+ -- | The default element.+ defaultSvg :: a++-- | Classify the font style, used to search a matching+-- font in the FontCache.+data FontStyle+ = FontStyleNormal+ | FontStyleItalic+ | FontStyleOblique+ deriving (Eq, Show)++-- | Tell where to anchor the text, where the position+-- given is realative to the text.+data TextAnchor+ -- | The text with left aligned, or start at the postion+ -- If the point is the '*' then the text will be printed+ -- this way:+ --+ -- > *THE_TEXT_TO_PRINT+ --+ -- Equivalent to the `start` value.+ = TextAnchorStart+ -- | The text is middle aligned, so the text will be at+ -- the left and right of the position:+ --+ -- > THE_TEXT*TO_PRINT+ --+ -- Equivalent to the `middle` value.+ | TextAnchorMiddle+ -- | The text is right aligned.+ --+ -- > THE_TEXT_TO_PRINT*+ --+ -- Equivalent to the `end` value.+ | TextAnchorEnd+ deriving (Eq, Show)+++-- | Correspond to the possible values of the+-- the attributes which are either `none` or+-- `url(#elem)`+data ElementRef+ = RefNone -- ^ Value for `none`+ | Ref String -- ^ Equivalent to `url()` attribute.+ deriving (Eq, Show)++-- | This type define how to draw any primitives,+-- which color to use, how to stroke the primitives+-- and the potential transformations to use.+--+-- All these attributes are propagated to the children.+data DrawAttributes = DrawAttributes+ { -- | Attribute corresponding to the `stroke-width`+ -- SVG attribute.+ _strokeWidth :: !(Last Number)+ -- | Correspond to the `stroke` attribute.+ , _strokeColor :: !(Last Texture)+ -- | Define the `stroke-opacity` attribute, the transparency+ -- for the "border".+ , _strokeOpacity :: !(Maybe Float)+ -- | Correspond to the `stroke-linecap` SVG+ -- attribute+ , _strokeLineCap :: !(Last Cap)+ -- | Correspond to the `stroke-linejoin` SVG+ -- attribute+ , _strokeLineJoin :: !(Last LineJoin)+ -- | Define the distance of the miter join, correspond+ -- to the `stroke-miterlimit` attritbue.+ , _strokeMiterLimit :: !(Last Double)+ -- | Define the filling color of the elements. Corresponding+ -- to the `fill` attribute.+ , _fillColor :: !(Last Texture)+ -- | Define the `fill-opacity` attribute, the transparency+ -- for the "content".+ , _fillOpacity :: !(Maybe Float)+ -- | Define the global or group opacity attribute.+ , _groupOpacity :: !(Maybe Float)+ -- | Content of the `transform` attribute+ , _transform :: !(Maybe [Transformation])+ -- | Define the `fill-rule` used during the rendering.+ , _fillRule :: !(Last FillRule)+ -- | Define the `mask` attribute.+ , _maskRef :: !(Last ElementRef)+ -- | Define the `clip-path` attribute.+ , _clipPathRef :: !(Last ElementRef)+ -- | Define the `clip-rule` attribute.+ , _clipRule :: !(Last FillRule)+ -- | Map to the `class` attribute. Used for the CSS+ -- rewriting.+ , _attrClass :: ![T.Text]+ -- | Map to the `id` attribute. Used for the CSS+ -- rewriting.+ , _attrId :: !(Maybe String)+ -- | Define the start distance of the dashing pattern.+ -- Correspond to the `stroke-dashoffset` attribute.+ , _strokeOffset :: !(Last Number)+ -- | Define the dashing pattern for the lines. Correspond+ -- to the `stroke-dasharray` attribute.+ , _strokeDashArray :: !(Last [Number])+ -- | Current size of the text, correspond to the+ -- `font-size` SVG attribute.+ , _fontSize :: !(Last Number)+ -- | Define the possible fonts to be used for text rendering.+ -- Map to the `font-family` attribute.+ , _fontFamily :: !(Last [String])+ -- | Map to the `font-style` attribute.+ , _fontStyle :: !(Last FontStyle)+ -- | Define how to interpret the text position, correspond+ -- to the `text-anchor` attribute.+ , _textAnchor :: !(Last TextAnchor)+ -- | Define the marker used for the start of the line.+ -- Correspond to the `marker-start` attribute.+ , _markerStart :: !(Last ElementRef)+ -- | Define the marker used for every point of the+ -- polyline/path Correspond to the `marker-mid`+ -- attribute.+ , _markerMid :: !(Last ElementRef)+ -- | Define the marker used for the end of the line.+ -- Correspond to the `marker-end` attribute.+ , _markerEnd :: !(Last ElementRef)+ }+ deriving (Eq, Show)++-- | Lenses for the DrawAttributes type.+makeClassy ''DrawAttributes++-- | This primitive describe an unclosed suite of+-- segments. Correspond to the `<polyline>` tag.+data PolyLine = PolyLine+ { -- | drawing attributes of the polyline.+ _polyLineDrawAttributes :: !DrawAttributes++ -- | Geometry definition of the polyline.+ -- correspond to the `points` attribute+ , _polyLinePoints :: ![RPoint]+ }+ deriving (Eq, Show)++-- | Lenses for the PolyLine type.+makeClassy ''PolyLine++instance WithDefaultSvg PolyLine where+ defaultSvg = PolyLine+ { _polyLineDrawAttributes = mempty+ , _polyLinePoints = []+ }+++instance WithDrawAttributes PolyLine where+ drawAttr = polyLineDrawAttributes++-- | Primitive decriving polygon composed+-- of segements. Correspond to the `<polygon>`+-- tag+data Polygon = Polygon+ { -- | Drawing attributes for the polygon.+ _polygonDrawAttributes :: !DrawAttributes+ -- | Points of the polygon. Correspond to+ -- the `points` attributes.+ , _polygonPoints :: ![RPoint]+ }+ deriving (Eq, Show)++-- | Lenses for the Polygon type+makeClassy ''Polygon++instance WithDrawAttributes Polygon where+ drawAttr = polygonDrawAttributes++instance WithDefaultSvg Polygon where+ defaultSvg = Polygon+ { _polygonDrawAttributes = mempty+ , _polygonPoints = []+ }++-- | Define a simple line. Correspond to the+-- `<line>` tag.+data Line = Line+ { -- | Drawing attributes of line.+ _lineDrawAttributes :: !DrawAttributes+ -- | First point of the the line, correspond+ -- to the `x1` and `y1` attributes.+ , _linePoint1 :: !Point+ -- | Second point of the the line, correspond+ -- to the `x2` and `y2` attributes.+ , _linePoint2 :: !Point+ }+ deriving (Eq, Show)++-- | Lenses for the Line type.+makeClassy ''Line++instance WithDrawAttributes Line where+ drawAttr = lineDrawAttributes++instance WithDefaultSvg Line where+ defaultSvg = Line+ { _lineDrawAttributes = mempty+ , _linePoint1 = zeroPoint+ , _linePoint2 = zeroPoint+ }+ where zeroPoint = (Num 0, Num 0)++-- | Define a rectangle. Correspond to+-- `<rectangle>` svg tag.+data Rectangle = Rectangle+ { -- | Rectangle drawing attributes.+ _rectDrawAttributes :: !DrawAttributes+ -- | Upper left corner of the rectangle, correspond+ -- to the attributes `x` and `y`.+ , _rectUpperLeftCorner :: !Point+ -- | Rectangle width, correspond, strangely, to+ -- the `width` attribute.+ , _rectWidth :: !Number+ -- | Rectangle height, correspond, amazingly, to+ -- the `height` attribute.+ , _rectHeight :: !Number+ -- | Define the rounded corner radius radius+ -- of the rectangle. Correspond to the `rx` and+ -- `ry` attributes.+ , _rectCornerRadius :: !(Number, Number)+ }+ deriving (Eq, Show)++-- | Lenses for the Rectangle type.+makeClassy ''Rectangle++instance WithDrawAttributes Rectangle where+ drawAttr = rectDrawAttributes++instance WithDefaultSvg Rectangle where+ defaultSvg = Rectangle+ { _rectDrawAttributes = mempty+ , _rectUpperLeftCorner = (Num 0, Num 0)+ , _rectWidth = Num 0+ , _rectHeight = Num 0+ , _rectCornerRadius = (Num 0, Num 0)+ }++-- | Type mapping the `<path>` svg tag.+data Path = Path+ { -- | Drawing attributes of the path.+ _pathDrawAttributes :: !DrawAttributes+ -- | Definition of the path, correspond to the+ -- `d` attributes.+ , _pathDefinition :: ![PathCommand]+ }+ deriving (Eq, Show)++-- | Lenses for the Path type+makeClassy ''Path++instance WithDrawAttributes Path where+ drawAttr = pathDrawAttributes++instance WithDefaultSvg Path where+ defaultSvg = Path+ { _pathDrawAttributes = mempty+ , _pathDefinition = []+ }++-- | Define a SVG group, corresponding `<g>` tag.+data Group a = Group+ { -- | Group drawing attributes, propagated to all of its+ -- children.+ _groupDrawAttributes :: !DrawAttributes+ -- | Content of the group, corresponding to all the tags+ -- inside the `<g>` tag.+ , _groupChildren :: ![a]+ -- | Mapped to the attribute `viewBox`+ , _groupViewBox :: !(Maybe (Double, Double, Double, Double))+ -- | used for symbols only+ , _groupAspectRatio :: !PreserveAspectRatio+ }+ deriving (Eq, Show)++-- | Lenses associated to the Group type.+makeClassy ''Group++instance WithDrawAttributes (Group a) where+ drawAttr = groupDrawAttributes++instance WithDefaultSvg (Group a) where+ defaultSvg = Group+ { _groupDrawAttributes = mempty+ , _groupChildren = []+ , _groupViewBox = Nothing+ , _groupAspectRatio = defaultSvg+ }++-- | Define the `<symbol>` tag, equivalent to+-- a named group.+newtype Symbol a =+ Symbol { _groupOfSymbol :: Group a }+ deriving (Eq, Show)++-- | Lenses associated with the Symbol type.+makeLenses ''Symbol++instance WithDrawAttributes (Symbol a) where+ drawAttr = groupOfSymbol . drawAttr++instance WithDefaultSvg (Symbol a) where+ defaultSvg = Symbol defaultSvg++-- | Define a `<circle>`.+data Circle = Circle+ { -- | Drawing attributes of the circle.+ _circleDrawAttributes :: !DrawAttributes+ -- | Define the center of the circle, describe+ -- the `cx` and `cy` attributes.+ , _circleCenter :: !Point+ -- | Radius of the circle, equivalent to the `r`+ -- attribute.+ , _circleRadius :: !Number+ }+ deriving (Eq, Show)++-- | Lenses for the Circle type.+makeClassy ''Circle++instance WithDrawAttributes Circle where+ drawAttr = circleDrawAttributes++instance WithDefaultSvg Circle where+ defaultSvg = Circle+ { _circleDrawAttributes = mempty+ , _circleCenter = (Num 0, Num 0)+ , _circleRadius = Num 0+ }++-- | Define an `<ellipse>`+data Ellipse = Ellipse+ { -- | Drawing attributes of the ellipse.+ _ellipseDrawAttributes :: !DrawAttributes+ -- | Center of the ellipse, map to the `cx`+ -- and `cy` attributes.+ , _ellipseCenter :: !Point+ -- | Radius along the X axis, map the+ -- `rx` attribute.+ , _ellipseXRadius :: !Number+ -- | Radius along the Y axis, map the+ -- `ry` attribute.+ , _ellipseYRadius :: !Number+ }+ deriving (Eq, Show)++-- | Lenses for the ellipse type.+makeClassy ''Ellipse++instance WithDrawAttributes Ellipse where+ drawAttr = ellipseDrawAttributes++instance WithDefaultSvg Ellipse where+ defaultSvg = Ellipse+ { _ellipseDrawAttributes = mempty+ , _ellipseCenter = (Num 0, Num 0)+ , _ellipseXRadius = Num 0+ , _ellipseYRadius = Num 0+ }++-- | Define a color stop for the gradients. Represent+-- the `<stop>` SVG tag.+data GradientStop = GradientStop+ { -- | Gradient offset between 0 and 1, correspond+ -- to the `offset` attribute.+ _gradientOffset :: !Float+ -- | Color of the gradient stop. Correspond+ -- to the `stop-color` attribute.+ , _gradientColor :: !PixelRGBA8+ -- | Path command used in mesh patch+ , _gradientPath :: !(Maybe GradientPathCommand)+ -- | Stop color opacity+ , _gradientOpacity :: !(Maybe Float)+ }+ deriving (Eq, Show)++-- | Lenses for the GradientStop type.+makeClassy ''GradientStop++instance WithDefaultSvg GradientStop where+ defaultSvg = GradientStop+ { _gradientOffset = 0.0+ , _gradientColor = PixelRGBA8 0 0 0 255+ , _gradientPath = Nothing+ , _gradientOpacity = Nothing+ }+++-- | Define `<meshpatch>` SVG tag+data MeshGradientPatch = MeshGradientPatch+ { -- | List of stop, from 2 to 4 in a patch+ _meshGradientPatchStops :: ![GradientStop]+ }+ deriving (Eq, Show)++makeClassy ''MeshGradientPatch++instance WithDefaultSvg MeshGradientPatch where+ defaultSvg = MeshGradientPatch []++-- | Define a `<meshrow>` tag.+data MeshGradientRow = MeshGradientRow+ { -- | List of patch in a row+ _meshGradientRowPatches :: ![MeshGradientPatch]+ }+ deriving (Eq, Show)++makeClassy ''MeshGradientRow++instance WithDefaultSvg MeshGradientRow where+ defaultSvg = MeshGradientRow []+++-- | Define a `<meshgradient>` tag.+data MeshGradient = MeshGradient+ { _meshGradientDrawAttributes :: !DrawAttributes+ -- | Original x coordinate of the mesh gradient+ , _meshGradientX :: !Number+ -- | Original y coordinate of the mesh gradient+ , _meshGradientY :: !Number+ -- | Type of color interpolation to use+ , _meshGradientType :: !MeshGradientType+ -- | Coordiante system to use+ , _meshGradientUnits :: !CoordinateUnits+ -- | Optional transform+ , _meshGradientTransform :: ![Transformation]+ -- | List of patch rows in the the mesh.+ , _meshGradientRows :: ![MeshGradientRow]+ }+ deriving (Eq, Show)++makeClassy ''MeshGradient++instance WithDrawAttributes MeshGradient where+ drawAttr = meshGradientDrawAttributes++instance WithDefaultSvg MeshGradient where+ defaultSvg = MeshGradient+ { _meshGradientDrawAttributes = mempty+ , _meshGradientX = Percent 0+ , _meshGradientY = Percent 0+ , _meshGradientType = GradientBilinear+ , _meshGradientUnits = CoordBoundingBox+ , _meshGradientTransform = mempty+ , _meshGradientRows = mempty+ }+++-- | Define an `<image>` tag.+data Image = Image+ { -- | Drawing attributes of the image+ _imageDrawAttributes :: !DrawAttributes+ -- | Position of the image referenced by its+ -- upper left corner.+ , _imageCornerUpperLeft :: !Point+ -- | Image width+ , _imageWidth :: !Number+ -- | Image Height+ , _imageHeight :: !Number+ -- | Image href, pointing to the real image.+ , _imageHref :: !String+ -- | preserveAspectRatio attribute+ , _imageAspectRatio :: !PreserveAspectRatio+ }+ deriving (Eq, Show)++-- | Lenses for the Image type.+makeClassy ''Image++instance WithDrawAttributes Image where+ drawAttr = imageDrawAttributes++instance WithDefaultSvg Image where+ defaultSvg = Image+ { _imageDrawAttributes = mempty+ , _imageCornerUpperLeft = (Num 0, Num 0)+ , _imageWidth = Num 0+ , _imageHeight = Num 0+ , _imageHref = ""+ , _imageAspectRatio = defaultSvg+ }++-- | Define an `<use>` for a named content.+-- Every named content can be reused in the+-- document using this element.+data Use = Use+ { -- | Position where to draw the "used" element.+ -- Correspond to the `x` and `y` attributes.+ _useBase :: Point+ -- | Referenced name, correspond to `xlink:href`+ -- attribute.+ , _useName :: String+ -- | Define the width of the region where+ -- to place the element. Map to the `width`+ -- attribute.+ , _useWidth :: Maybe Number+ -- | Define the height of the region where+ -- to place the element. Map to the `height`+ -- attribute.+ , _useHeight :: Maybe Number+ -- | Use draw attributes.+ , _useDrawAttributes :: DrawAttributes+ }+ deriving (Eq, Show)++-- | Lenses for the Use type.+makeClassy ''Use++instance WithDrawAttributes Use where+ drawAttr = useDrawAttributes++instance WithDefaultSvg Use where+ defaultSvg = Use+ { _useBase = (Num 0, Num 0)+ , _useName = ""+ , _useWidth = Nothing+ , _useHeight = Nothing+ , _useDrawAttributes = mempty+ }++-- | Define position information associated to+-- `<text>` or `<tspan>` svg tag.+data TextInfo = TextInfo+ { _textInfoX :: ![Number] -- ^ `x` attribute.+ , _textInfoY :: ![Number] -- ^ `y` attribute.+ , _textInfoDX :: ![Number] -- ^ `dx` attribute.+ , _textInfoDY :: ![Number] -- ^ `dy` attribute.+ , _textInfoRotate :: ![Double] -- ^ `rotate` attribute.+ , _textInfoLength :: !(Maybe Number) -- ^ `textLength` attribute.+ }+ deriving (Eq, Show)++instance Monoid TextInfo where+ mempty = TextInfo [] [] [] [] [] Nothing+ mappend (TextInfo x1 y1 dx1 dy1 r1 l1)+ (TextInfo x2 y2 dx2 dy2 r2 l2) =+ TextInfo (x1 <> x2) (y1 <> y2)+ (dx1 <> dx2) (dy1 <> dy2)+ (r1 <> r2)+ (getLast $ Last l1 <> Last l2)++-- | Lenses for the TextInfo type.+makeClassy ''TextInfo++instance WithDefaultSvg TextInfo where+ defaultSvg = mempty++-- | Define the content of a `<tspan>` tag.+data TextSpanContent+ = SpanText !T.Text -- ^ Raw text+ | SpanTextRef !String -- ^ Equivalent to a `<tref>`+ | SpanSub !TextSpan -- ^ Define a `<tspan>`+ deriving (Eq, Show)++-- | Define a `<tspan>` tag.+data TextSpan = TextSpan+ { -- | Placing information for the text.+ _spanInfo :: !TextInfo+ -- | Drawing attributes for the text span.+ , _spanDrawAttributes :: !DrawAttributes+ -- | Content of the span.+ , _spanContent :: ![TextSpanContent]+ }+ deriving (Eq, Show)++-- | Lenses for the TextSpan type.+makeClassy ''TextSpan++instance WithDefaultSvg TextSpan where+ defaultSvg = TextSpan+ { _spanInfo = defaultSvg+ , _spanDrawAttributes = mempty+ , _spanContent = mempty+ }++-- | Describe the content of the `method` attribute on+-- text path.+data TextPathMethod+ = TextPathAlign -- ^ Map to the `align` value.+ | TextPathStretch -- ^ Map to the `stretch` value.+ deriving (Eq, Show)++-- | Describe the content of the `spacing` text path+-- attribute.+data TextPathSpacing+ = TextPathSpacingExact -- ^ Map to the `exact` value.+ | TextPathSpacingAuto -- ^ Map to the `auto` value.+ deriving (Eq, Show)++-- | Describe the `<textpath>` SVG tag.+data TextPath = TextPath+ { -- | Define the beginning offset on the path,+ -- the `startOffset` attribute.+ _textPathStartOffset :: !Number+ -- | Define the `xlink:href` attribute.+ , _textPathName :: !String+ -- | Correspond to the `method` attribute.+ , _textPathMethod :: !TextPathMethod+ -- | Correspond to the `spacing` attribute.+ , _textPathSpacing :: !TextPathSpacing+ -- | Real content of the path.+ , _textPathData :: ![PathCommand]+ }+ deriving (Eq, Show)++-- | Lenses for the TextPath type.+makeClassy ''TextPath++instance WithDefaultSvg TextPath where+ defaultSvg = TextPath+ { _textPathStartOffset = Num 0+ , _textPathName = mempty+ , _textPathMethod = TextPathAlign+ , _textPathSpacing = TextPathSpacingExact+ , _textPathData = []+ }++-- | Define the possible values of the `lengthAdjust`+-- attribute.+data TextAdjust+ = TextAdjustSpacing -- ^ Value `spacing`+ | TextAdjustSpacingAndGlyphs -- ^ Value `spacingAndGlyphs`+ deriving (Eq, Show)++-- | Define the global `<tag>` SVG tag.+data Text = Text+ { -- | Define the `lengthAdjust` attribute.+ _textAdjust :: !TextAdjust+ -- | Root of the text content.+ , _textRoot :: !TextSpan+ }+ deriving (Eq, Show)++-- | Lenses for the Text type.+makeClassy ''Text++-- | Little helper to create a SVG text at a given+-- baseline position.+textAt :: Point -> T.Text -> Text+textAt (x, y) txt = Text TextAdjustSpacing tspan where+ tspan = defaultSvg+ { _spanContent = [SpanText txt]+ , _spanInfo = defaultSvg+ { _textInfoX = [x]+ , _textInfoY = [y]+ }+ }++instance WithDrawAttributes Text where+ drawAttr = textRoot . spanDrawAttributes++instance WithDefaultSvg Text where+ defaultSvg = Text+ { _textRoot = defaultSvg+ , _textAdjust = TextAdjustSpacing+ }++-- | Main type for the scene description, reorient to+-- specific type describing each tag.+data Tree+ = None+ | UseTree { useInformation :: !Use+ , useSubTree :: !(Maybe Tree) }+ | GroupTree !(Group Tree)+ | SymbolTree !(Symbol Tree)+ | PathTree !Path+ | CircleTree !Circle+ | PolyLineTree !PolyLine+ | PolygonTree !Polygon+ | EllipseTree !Ellipse+ | LineTree !Line+ | RectangleTree !Rectangle+ | TextTree !(Maybe TextPath) !Text+ | ImageTree !Image+ | MeshGradientTree !MeshGradient+ deriving (Eq, Show)++-- | Define the orientation, associated to the+-- `orient` attribute on the Marker+data MarkerOrientation+ = OrientationAuto -- ^ Auto value+ | OrientationAngle Coord -- ^ Specific angle.+ deriving (Eq, Show)++-- | Define the content of the `markerUnits` attribute+-- on the Marker.+data MarkerUnit+ = MarkerUnitStrokeWidth -- ^ Value `strokeWidth`+ | MarkerUnitUserSpaceOnUse -- ^ Value `userSpaceOnUse`+ deriving (Eq, Show)++-- | Define the content of the `markerUnits` attribute+-- on the Marker.+data Overflow+ = OverflowVisible -- ^ Value `visible`+ | OverflowHidden -- ^ Value `hidden`+ deriving (Eq, Show)++-- | Define the `<marker>` tag.+data Marker = Marker+ { -- | Draw attributes of the marker.+ _markerDrawAttributes :: DrawAttributes+ -- | Define the reference point of the marker.+ -- correspond to the `refX` and `refY` attributes.+ , _markerRefPoint :: !(Number, Number)+ -- | Define the width of the marker. Correspond to+ -- the `markerWidth` attribute.+ , _markerWidth :: !(Maybe Number)+ -- | Define the height of the marker. Correspond to+ -- the `markerHeight` attribute.+ , _markerHeight :: !(Maybe Number)+ -- | Correspond to the `orient` attribute.+ , _markerOrient :: !(Maybe MarkerOrientation)+ -- | Map the `markerUnits` attribute.+ , _markerUnits :: !(Maybe MarkerUnit)+ -- | Optional viewbox+ , _markerViewBox :: !(Maybe (Double, Double, Double, Double))+ -- | Elements defining the marker.+ , _markerOverflow :: !(Maybe Overflow)+ -- | preserveAspectRatio attribute+ , _markerAspectRatio :: !PreserveAspectRatio+ -- | Elements defining the marker.+ , _markerElements :: [Tree]+ }+ deriving (Eq, Show)++-- | Lenses for the Marker type.+makeClassy ''Marker++instance WithDrawAttributes Marker where+ drawAttr = markerDrawAttributes++instance WithDefaultSvg Marker where+ defaultSvg = Marker+ { _markerDrawAttributes = mempty+ , _markerRefPoint = (Num 0, Num 0)+ , _markerWidth = Just (Num 3)+ , _markerHeight = Just (Num 3)+ , _markerOrient = Nothing -- MarkerOrientation+ , _markerUnits = Nothing -- MarkerUnitStrokeWidth+ , _markerViewBox = Nothing+ , _markerOverflow = Nothing+ , _markerElements = mempty+ , _markerAspectRatio = defaultSvg+ }++-- | Insert element in the first sublist in the list of list.+appNode :: [[a]] -> a -> [[a]]+appNode [] e = [[e]]+appNode (curr:above) e = (e:curr) : above++-- | Map a tree while propagating context information.+-- The function passed in parameter receive a list+-- representing the the path used to go arrive to the+-- current node.+zipTree :: ([[Tree]] -> Tree) -> Tree -> Tree+zipTree f = dig [] where+ dig prev e@None = f $ appNode prev e+ dig prev e@(UseTree _ Nothing) = f $ appNode prev e+ dig prev e@(UseTree nfo (Just u)) =+ f . appNode prev . UseTree nfo . Just $ dig ([] : appNode prev e) u+ dig prev e@(GroupTree g) =+ f . appNode prev . GroupTree $ zipGroup (appNode prev e) g+ dig prev e@(SymbolTree g) =+ f . appNode prev . SymbolTree . Symbol .+ zipGroup (appNode prev e) $ _groupOfSymbol g+ dig prev e@(PathTree _) = f $ appNode prev e+ dig prev e@(CircleTree _) = f $ appNode prev e+ dig prev e@(PolyLineTree _) = f $ appNode prev e+ dig prev e@(PolygonTree _) = f $ appNode prev e+ dig prev e@(EllipseTree _) = f $ appNode prev e+ dig prev e@(LineTree _) = f $ appNode prev e+ dig prev e@(RectangleTree _) = f $ appNode prev e+ dig prev e@(TextTree _ _) = f $ appNode prev e+ dig prev e@(ImageTree _) = f $ appNode prev e+ dig prev e@(MeshGradientTree _) = f $ appNode prev e++ zipGroup prev g = g { _groupChildren = updatedChildren }+ where+ groupChild = _groupChildren g+ updatedChildren =+ [dig (c:prev) child+ | (child, c) <- zip groupChild $ inits groupChild]++-- | Fold all nodes of a SVG tree.+foldTree :: (a -> Tree -> a) -> a -> Tree -> a+foldTree f = go where+ go acc e = case e of+ None -> f acc e+ UseTree _ _ -> f acc e+ PathTree _ -> f acc e+ CircleTree _ -> f acc e+ PolyLineTree _ -> f acc e+ PolygonTree _ -> f acc e+ EllipseTree _ -> f acc e+ LineTree _ -> f acc e+ RectangleTree _ -> f acc e+ TextTree _ _ -> f acc e+ ImageTree _ -> f acc e+ MeshGradientTree _ -> f acc e+ GroupTree g ->+ let subAcc = F.foldl' go acc $ _groupChildren g in+ f subAcc e+ SymbolTree s ->+ let subAcc =+ F.foldl' go acc . _groupChildren $ _groupOfSymbol s in+ f subAcc e++-- | Helper function mapping every tree element.+mapTree :: (Tree -> Tree) -> Tree -> Tree+mapTree f = go where+ go e@None = f e+ go e@(UseTree _ _) = f e+ go (GroupTree g) = f . GroupTree $ mapGroup g+ go (SymbolTree g) =+ f . SymbolTree . Symbol . mapGroup $ _groupOfSymbol g+ go e@(PathTree _) = f e+ go e@(CircleTree _) = f e+ go e@(PolyLineTree _) = f e+ go e@(PolygonTree _) = f e+ go e@(EllipseTree _) = f e+ go e@(LineTree _) = f e+ go e@(RectangleTree _) = f e+ go e@(TextTree _ _) = f e+ go e@(ImageTree _) = f e+ go e@(MeshGradientTree _) = f e++ mapGroup g =+ g { _groupChildren = map go $ _groupChildren g }++-- | For every element of a svg tree, associate+-- it's SVG tag name.+nameOfTree :: Tree -> T.Text+nameOfTree v =+ case v of+ None -> ""+ UseTree _ _ -> "use"+ GroupTree _ -> "g"+ SymbolTree _ -> "symbol"+ PathTree _ -> "path"+ CircleTree _ -> "circle"+ PolyLineTree _ -> "polyline"+ PolygonTree _ -> "polygon"+ EllipseTree _ -> "ellipse"+ LineTree _ -> "line"+ RectangleTree _ -> "rectangle"+ TextTree _ _ -> "text"+ ImageTree _ -> "image"+ MeshGradientTree _ -> "meshgradient"++drawAttrOfTree :: Tree -> DrawAttributes+drawAttrOfTree v = case v of+ None -> mempty+ UseTree e _ -> e ^. drawAttr+ GroupTree e -> e ^. drawAttr+ SymbolTree e -> e ^. drawAttr+ PathTree e -> e ^. drawAttr+ CircleTree e -> e ^. drawAttr+ PolyLineTree e -> e ^. drawAttr+ PolygonTree e -> e ^. drawAttr+ EllipseTree e -> e ^. drawAttr+ LineTree e -> e ^. drawAttr+ RectangleTree e -> e ^. drawAttr+ TextTree _ e -> e ^. drawAttr+ ImageTree e -> e ^. drawAttr+ MeshGradientTree e -> e ^. drawAttr++setDrawAttrOfTree :: Tree -> DrawAttributes -> Tree+setDrawAttrOfTree v attr = case v of+ None -> None+ UseTree e m -> UseTree (e & drawAttr .~ attr) m+ GroupTree e -> GroupTree $ e & drawAttr .~ attr+ SymbolTree e -> SymbolTree $ e & drawAttr .~ attr+ PathTree e -> PathTree $ e & drawAttr .~ attr+ CircleTree e -> CircleTree $ e & drawAttr .~ attr+ PolyLineTree e -> PolyLineTree $ e & drawAttr .~ attr+ PolygonTree e -> PolygonTree $ e & drawAttr .~ attr+ EllipseTree e -> EllipseTree $ e & drawAttr .~ attr+ LineTree e -> LineTree $ e & drawAttr .~ attr+ RectangleTree e -> RectangleTree $ e & drawAttr .~ attr+ TextTree a e -> TextTree a $ e & drawAttr .~ attr+ ImageTree e -> ImageTree $ e & drawAttr .~ attr+ MeshGradientTree e -> MeshGradientTree $ e & drawAttr .~ attr++instance WithDrawAttributes Tree where+ drawAttr = lens drawAttrOfTree setDrawAttrOfTree++instance WithDefaultSvg Tree where+ defaultSvg = None++-- | Define the possible values for the `spreadMethod`+-- values used for the gradient definitions.+data Spread+ = SpreadRepeat -- ^ `reapeat` value+ | SpreadPad -- ^ `pad` value+ | SpreadReflect -- ^ `reflect value`+ deriving (Eq, Show)++-- | Define a `<linearGradient>` tag.+data LinearGradient = LinearGradient+ { -- | Define coordinate system of the gradient,+ -- associated to the `gradientUnits` attribute.+ _linearGradientUnits :: CoordinateUnits+ -- | Point defining the beginning of the line gradient.+ -- Associated to the `x1` and `y1` attribute.+ , _linearGradientStart :: Point+ -- | Point defining the end of the line gradient.+ -- Associated to the `x2` and `y2` attribute.+ , _linearGradientStop :: Point+ -- | Define how to handle the values outside+ -- the gradient start and stop. Associated to the+ -- `spreadMethod` attribute.+ , _linearGradientSpread :: Spread+ -- | Define the transformation to apply to the+ -- gradient points. Associated to the `gradientTransform`+ -- attribute.+ , _linearGradientTransform :: [Transformation]+ -- | List of color stops of the linear gradient.+ , _linearGradientStops :: [GradientStop]+ }+ deriving (Eq, Show)++-- | Lenses for the LinearGradient type.+makeClassy ''LinearGradient++instance WithDefaultSvg LinearGradient where+ defaultSvg = LinearGradient+ { _linearGradientUnits = CoordBoundingBox+ , _linearGradientStart = (Percent 0, Percent 0)+ , _linearGradientStop = (Percent 1, Percent 0)+ , _linearGradientSpread = SpreadPad+ , _linearGradientTransform = []+ , _linearGradientStops = []+ }++-- | Define a `<radialGradient>` tag.+data RadialGradient = RadialGradient+ { -- | Define coordinate system of the gradient,+ -- associated to the `gradientUnits` attribute.+ _radialGradientUnits :: CoordinateUnits+ -- | Center of the radial gradient. Associated to+ -- the `cx` and `cy` attributes.+ , _radialGradientCenter :: Point+ -- | Radius of the radial gradient. Associated to+ -- the `r` attribute.+ , _radialGradientRadius :: Number+ -- | X coordinate of the focus point of the radial+ -- gradient. Associated to the `fx` attribute.+ , _radialGradientFocusX :: Maybe Number+ -- | Y coordinate of the focus point of the radial+ -- gradient. Associated to the `fy` attribute.+ , _radialGradientFocusY :: Maybe Number+ -- | Define how to handle the values outside+ -- the gradient start and stop. Associated to the+ -- `spreadMethod` attribute.+ , _radialGradientSpread :: Spread+ -- | Define the transformation to apply to the+ -- gradient points. Associated to the `gradientTransform`+ -- attribute.+ , _radialGradientTransform :: [Transformation]+ -- | List of color stops of the radial gradient.+ , _radialGradientStops :: [GradientStop]+ }+ deriving (Eq, Show)++-- | Lenses for the RadialGradient type.+makeClassy ''RadialGradient++instance WithDefaultSvg RadialGradient where+ defaultSvg = RadialGradient+ { _radialGradientUnits = CoordBoundingBox+ , _radialGradientCenter = (Percent 0.5, Percent 0.5)+ , _radialGradientRadius = Percent 0.5+ , _radialGradientFocusX = Nothing+ , _radialGradientFocusY = Nothing+ , _radialGradientSpread = SpreadPad+ , _radialGradientTransform = []+ , _radialGradientStops = []+ }++-- | Define a SVG `<mask>` tag.+data Mask = Mask+ { -- | Drawing attributes of the Mask+ _maskDrawAttributes :: DrawAttributes+ -- | Correspond to the `maskContentUnits` attributes.+ , _maskContentUnits :: CoordinateUnits+ -- | Mapping to the `maskUnits` attribute.+ , _maskUnits :: CoordinateUnits+ -- | Map to the `x` and `y` attributes.+ , _maskPosition :: Point+ -- | Map to the `width` attribute+ , _maskWidth :: Number+ -- | Map to the `height` attribute.+ , _maskHeight :: Number+ -- | Children of the `<mask>` tag.+ , _maskContent :: [Tree]+ }+ deriving (Eq, Show)++-- | Lenses for the Mask type.+makeClassy ''Mask++instance WithDrawAttributes Mask where+ drawAttr = maskDrawAttributes++instance WithDefaultSvg Mask where+ defaultSvg = Mask+ { _maskDrawAttributes = mempty+ , _maskContentUnits = CoordUserSpace+ , _maskUnits = CoordBoundingBox+ , _maskPosition = (Percent (-0.1), Percent (-0.1))+ , _maskWidth = Percent 1.2+ , _maskHeight = Percent 1.2+ , _maskContent = []+ }++-- | Define a `<clipPath>` tag.+data ClipPath = ClipPath+ { _clipPathDrawAttributes :: DrawAttributes+ -- | Maps to the `clipPathUnits` attribute+ , _clipPathUnits :: CoordinateUnits+ -- | Maps to the content of the tree+ , _clipPathContent :: [Tree]+ }+ deriving (Eq, Show)++-- | Lenses for the ClipPath type.+makeClassy ''ClipPath++instance WithDrawAttributes ClipPath where+ drawAttr = clipPathDrawAttributes++instance WithDefaultSvg ClipPath where+ defaultSvg = ClipPath+ { _clipPathDrawAttributes = mempty+ , _clipPathUnits = CoordUserSpace+ , _clipPathContent = mempty+ }++-- | Define a `<pattern>` tag.+data Pattern = Pattern+ { -- | Pattern drawing attributes.+ _patternDrawAttributes :: !DrawAttributes+ -- | Possible `viewBox`.+ , _patternViewBox :: !(Maybe (Double, Double, Double, Double))+ -- | Width of the pattern tile, mapped to the+ -- `width` attribute+ , _patternWidth :: !Number+ -- | Height of the pattern tile, mapped to the+ -- `height` attribute+ , _patternHeight :: !Number+ -- | Pattern tile base, mapped to the `x` and+ -- `y` attributes.+ , _patternPos :: !Point+ -- | Elements used in the pattern.+ , _patternElements :: ![Tree]+ -- | Define the cordinate system to use for+ -- the pattern. Mapped to the `patternUnits`+ -- attribute.+ , _patternUnit :: !CoordinateUnits+ -- | Value of the "preserveAspectRatio" attribute+ , _patternAspectRatio :: !PreserveAspectRatio + }+ deriving Show++-- | Lenses for the Patter type.+makeClassy ''Pattern++instance WithDrawAttributes Pattern where+ drawAttr = patternDrawAttributes++instance WithDefaultSvg Pattern where+ defaultSvg = Pattern+ { _patternViewBox = Nothing+ , _patternWidth = Num 0+ , _patternHeight = Num 0+ , _patternPos = (Num 0, Num 0)+ , _patternElements = []+ , _patternUnit = CoordBoundingBox+ , _patternDrawAttributes = mempty+ , _patternAspectRatio = defaultSvg+ }++-- | Sum types helping keeping track of all the namable+-- elemens in a SVG document.+data Element+ = ElementLinearGradient LinearGradient+ | ElementRadialGradient RadialGradient+ | ElementMeshGradient MeshGradient+ | ElementGeometry Tree+ | ElementPattern Pattern+ | ElementMarker Marker+ | ElementMask Mask+ | ElementClipPath ClipPath+ deriving Show++-- | Represent a full svg document with style,+-- geometry and named elements.+data Document = Document+ { _viewBox :: Maybe (Double, Double, Double, Double)+ , _width :: Maybe Number+ , _height :: Maybe Number+ , _elements :: [Tree]+ , _definitions :: M.Map String Element+ , _description :: String+ , _styleRules :: [CssRule]+ , _documentLocation :: FilePath+ }+ deriving Show++-- | Lenses associated to a SVG document.+makeClassy ''Document++-- | Calculate the document size in function of the+-- different available attributes in the document.+documentSize :: Dpi -> Document -> (Int, Int)+documentSize _ Document { _viewBox = Just (x1, y1, x2, y2)+ , _width = Just (Percent pw)+ , _height = Just (Percent ph)+ } =+ (floor $ dx * pw, floor $ dy * ph)+ where+ dx = abs $ x2 - x1+ dy = abs $ y2 - y1+documentSize _ Document { _width = Just (Num w)+ , _height = Just (Num h) } = (floor w, floor h)+documentSize dpi doc@(Document { _width = Just w+ , _height = Just h }) =+ documentSize dpi $ doc+ { _width = Just $ toUserUnit dpi w+ , _height = Just $ toUserUnit dpi h }+documentSize _ Document { _viewBox = Just (x1, y1, x2, y2) } =+ (floor . abs $ x2 - x1, floor . abs $ y2 - y1)+documentSize _ _ = (1, 1)++mayMerge :: Monoid a => Maybe a -> Maybe a -> Maybe a+mayMerge (Just a) (Just b) = Just $ mappend a b+mayMerge _ b@(Just _) = b+mayMerge a Nothing = a++instance Monoid DrawAttributes where+ mempty = DrawAttributes+ { _strokeWidth = Last Nothing+ , _strokeColor = Last Nothing+ , _strokeOpacity = Nothing+ , _strokeLineCap = Last Nothing+ , _strokeLineJoin = Last Nothing+ , _strokeMiterLimit = Last Nothing+ , _fillColor = Last Nothing+ , _groupOpacity = Nothing+ , _fillOpacity = Nothing+ , _fontSize = Last Nothing+ , _fontFamily = Last Nothing+ , _fontStyle = Last Nothing+ , _transform = Nothing+ , _fillRule = Last Nothing+ , _attrClass = mempty+ , _attrId = Nothing+ , _strokeOffset = Last Nothing+ , _strokeDashArray = Last Nothing+ , _textAnchor = Last Nothing+ , _maskRef = Last Nothing+ , _clipPathRef = Last Nothing+ , _clipRule = Last Nothing++ , _markerStart = Last Nothing+ , _markerMid = Last Nothing+ , _markerEnd = Last Nothing+ }++ mappend a b = DrawAttributes+ { _strokeWidth = (mappend `on` _strokeWidth) a b+ , _strokeColor = (mappend `on` _strokeColor) a b+ , _strokeLineCap = (mappend `on` _strokeLineCap) a b+ , _strokeOpacity = (opacityMappend `on` _strokeOpacity) a b+ , _strokeLineJoin = (mappend `on` _strokeLineJoin) a b+ , _strokeMiterLimit = (mappend `on` _strokeMiterLimit) a b+ , _fillColor = (mappend `on` _fillColor) a b+ , _fillOpacity = (opacityMappend `on` _fillOpacity) a b+ , _fontSize = (mappend `on` _fontSize) a b+ , _transform = (mayMerge `on` _transform) a b+ , _fillRule = (mappend `on` _fillRule) a b+ , _attrClass = _attrClass b+ , _attrId = _attrId b+ , _groupOpacity = _groupOpacity b+ , _strokeOffset = (mappend `on` _strokeOffset) a b+ , _strokeDashArray = (mappend `on` _strokeDashArray) a b+ , _fontFamily = (mappend `on` _fontFamily) a b+ , _fontStyle = (mappend `on` _fontStyle) a b+ , _textAnchor = (mappend `on` _textAnchor) a b+ , _maskRef = (mappend `on` _maskRef) a b+ , _clipPathRef = (mappend `on` _clipPathRef) a b+ , _clipRule = (mappend `on` _clipRule) a b+ , _markerStart = (mappend `on` _markerStart) a b+ , _markerMid = (mappend `on` _markerMid) a b+ , _markerEnd = (mappend `on` _markerEnd) a b+ }+ where+ opacityMappend Nothing Nothing = Nothing+ opacityMappend (Just v) Nothing = Just v+ opacityMappend Nothing (Just v) = Just v+ opacityMappend (Just v) (Just v2) = Just $ v * v2++instance WithDefaultSvg DrawAttributes where+ defaultSvg = mempty++instance CssMatcheable Tree where+ cssAttribOf _ _ = Nothing+ cssClassOf = view (drawAttr . attrClass)+ cssIdOf = fmap T.pack . view (drawAttr . attrId)+ cssNameOf = nameOfTree+
src/Graphics/Svg/XmlParser.hs view
@@ -1,1164 +1,1227 @@-{-# LANGUAGE CPP #-} -{-# LANGUAGE ViewPatterns #-} -{-# LANGUAGE RankNTypes #-} -{-# LANGUAGE OverloadedStrings #-} -{-# LANGUAGE FlexibleInstances #-} -{-# LANGUAGE FlexibleContexts #-} -module Graphics.Svg.XmlParser( xmlOfDocument - , unparseDocument - - , SvgAttributeLens( .. ) - , drawAttributesList - ) where - - -#if !MIN_VERSION_base(4,6,0) -import Text.Read( reads ) -#else -import Text.Read( readMaybe ) -#endif - -#if !MIN_VERSION_base(4,8,0) -import Control.Applicative( pure, (<$>), (<$), (<*>) ) -import Data.Foldable( foldMap ) -import Data.Monoid( mempty ) -#endif - -import Control.Applicative( (<|>), many ) - -import Control.Lens hiding( transform, children, elements, element ) -import Control.Monad.State.Strict( State, runState, modify, gets ) -import Data.Maybe( fromMaybe, catMaybes ) -import Data.Monoid( Last( Last ), getLast, (<>) ) -import Data.List( foldl', intercalate ) -import Text.XML.Light.Proc( findAttrBy, elChildren, strContent ) -import qualified Text.XML.Light as X -import qualified Data.Text as T -import qualified Data.Text.Lazy as TL -import qualified Data.Text.Lazy.Builder as TB -import qualified Data.Map as M -import Data.Attoparsec.Text( Parser, string, parseOnly, many1 ) -import Codec.Picture( PixelRGBA8( .. ) ) -import Graphics.Svg.Types -import Graphics.Svg.PathParser -import Graphics.Svg.ColorParser -import Graphics.Svg.CssTypes( CssDeclaration( .. ) - , CssElement( .. ) - , CssRule - , tserialize - ) -import Graphics.Svg.CssParser( complexNumber - , num - , ruleSet - , dashArray - , styleString - , numberList ) - -import Text.Printf( printf ) - -{-import Debug.Trace-} - -#if !MIN_VERSION_base(4,6,0) -readMaybe :: Read a => String -> Maybe a -readMaybe str = case reads str of - [] -> Nothing - (x, _):_ -> Just x -#endif - -nodeName :: X.Element -> String -nodeName = X.qName . X.elName - -attributeFinder :: String -> X.Element -> Maybe String -attributeFinder str = - findAttrBy (\a -> X.qName a == str) - --- | Helper class to help simplify parsing code --- for various attributes. -class ParseableAttribute a where - aparse :: String -> Maybe a - aserialize :: a -> Maybe String - -instance ParseableAttribute v => ParseableAttribute (Maybe v) where - aparse = fmap Just . aparse - aserialize = (>>= aserialize) - -instance ParseableAttribute v => ParseableAttribute (Last v) where - aparse = fmap Last . aparse - aserialize = aserialize . getLast - -instance ParseableAttribute String where - aparse = Just - aserialize = Just - -instance ParseableAttribute Number where - aparse = parseMayStartDot complexNumber - aserialize = Just . serializeNumber - -instance ParseableAttribute [Number] where - aparse = parse dashArray - aserialize = Just . serializeDashArray - -instance ParseableAttribute PixelRGBA8 where - aparse = parse colorParser - aserialize = Just . colorSerializer - -instance ParseableAttribute [PathCommand] where - aparse = parse pathParser - aserialize = Just . serializeCommands - -instance ParseableAttribute [RPoint] where - aparse = parse pointData - aserialize = Just . serializePoints - -instance ParseableAttribute Double where - aparse = parseMayStartDot num - aserialize v = Just $ printf "%g" v - -instance ParseableAttribute Texture where - aparse = parse textureParser - aserialize = Just . textureSerializer - -instance ParseableAttribute [Transformation] where - aparse = parse $ many transformParser - aserialize = Just . serializeTransformations - -instance ParseableAttribute Alignment where - aparse s = Just $ case s of - "none" -> AlignNone - "xMinYMin" -> AlignxMinYMin - "xMidYMin" -> AlignxMidYMin - "xMaxYMin" -> AlignxMaxYMin - "xMinYMid" -> AlignxMinYMid - "xMidYMid" -> AlignxMidYMid - "xMaxYMid" -> AlignxMaxYMid - "xMinYMax" -> AlignxMinYMax - "xMidYMax" -> AlignxMidYMax - "xMaxYMax" -> AlignxMaxYMax - _ -> _aspectRatioAlign defaultSvg - - aserialize v = Just $ case v of - AlignNone -> "none" - AlignxMinYMin -> "xMinYMin" - AlignxMidYMin -> "xMidYMin" - AlignxMaxYMin -> "xMaxYMin" - AlignxMinYMid -> "xMinYMid" - AlignxMidYMid -> "xMidYMid" - AlignxMaxYMid -> "xMaxYMid" - AlignxMinYMax -> "xMinYMax" - AlignxMidYMax -> "xMidYMax" - AlignxMaxYMax -> "xMaxYMax" - -instance ParseableAttribute MeetSlice where - aparse s = case s of - "meet" -> Just Meet - "slice" -> Just Slice - _ -> Nothing - - aserialize v = Just $ case v of - Meet -> "meet" - Slice -> "slice" - -instance ParseableAttribute PreserveAspectRatio where - aserialize v = Just $ defer <> align <> meetSlice where - defer = if _aspectRatioDefer v then "defer " else "" - align = fromMaybe "" . aserialize $ _aspectRatioAlign v - meetSlice = fromMaybe "" $ aserialize =<< _aspectRatioMeetSlice v - - aparse s = case words s of - [] -> Nothing - [align] -> Just $ defaultSvg { _aspectRatioAlign = alignOf align } - ["defer", align] -> - Just $ defaultSvg - { _aspectRatioDefer = True - , _aspectRatioAlign = alignOf align - } - [align, meet] -> - Just $ defaultSvg - { _aspectRatioMeetSlice = aparse meet - , _aspectRatioAlign = alignOf align - } - ["defer", align, meet] -> - Just $ PreserveAspectRatio - { _aspectRatioDefer = True - , _aspectRatioAlign = alignOf align - , _aspectRatioMeetSlice = aparse meet - } - _ -> Nothing - where - alignOf = fromMaybe (_aspectRatioAlign defaultSvg) . aparse - -instance ParseableAttribute Cap where - aparse s = case s of - "butt" -> Just CapButt - "round" -> Just CapRound - "square" -> Just CapSquare - _ -> Nothing - - aserialize c = Just $ case c of - CapButt -> "butt" - CapRound -> "round" - CapSquare -> "square" - -instance ParseableAttribute TextAnchor where - aparse s = case s of - "middle" -> Just TextAnchorMiddle - "start" -> Just TextAnchorStart - "end" -> Just TextAnchorEnd - _ -> Nothing - - aserialize t = Just $ case t of - TextAnchorMiddle -> "middle" - TextAnchorStart -> "start" - TextAnchorEnd -> "end" - -instance ParseableAttribute ElementRef where - aparse s = case parseOnly pa $ T.pack s of - Left _ -> Nothing - Right v -> Just v - where - pa = (RefNone <$ string "none") - <|> (Ref <$> urlRef) - - aserialize c = Just $ case c of - Ref r -> "url(#" <> r <> ")" - RefNone -> "none" - -instance ParseableAttribute LineJoin where - aparse s = case s of - "miter" -> Just JoinMiter - "round" -> Just JoinRound - "bevel" -> Just JoinBevel - _ -> Nothing - - aserialize j = Just $ case j of - JoinMiter -> "miter" - JoinRound -> "round" - JoinBevel -> "bevel" - -instance ParseableAttribute CoordinateUnits where - aparse s = case s of - "userSpaceOnUse" -> Just CoordUserSpace - "objectBoundingBox" -> Just CoordBoundingBox - _ -> Just CoordBoundingBox - - aserialize uni = Just $ case uni of - CoordUserSpace -> "userSpaceOnUse" - CoordBoundingBox -> "objectBoundingBox" - -instance ParseableAttribute Spread where - aparse s = case s of - "pad" -> Just SpreadPad - "reflect" -> Just SpreadReflect - "repeat" -> Just SpreadRepeat - _ -> Nothing - - aserialize s = Just $ case s of - SpreadPad -> "pad" - SpreadReflect -> "reflect" - SpreadRepeat -> "repeat" - -instance ParseableAttribute FillRule where - aparse s = case s of - "nonzero" -> Just FillNonZero - "evenodd" -> Just FillEvenOdd - _ -> Nothing - - aserialize f = Just $ case f of - FillNonZero -> "nonzero" - FillEvenOdd -> "evenodd" - -instance ParseableAttribute TextAdjust where - aparse s = Just $ case s of - "spacing" -> TextAdjustSpacing - "spacingAndGlyphs" -> TextAdjustSpacingAndGlyphs - _ -> TextAdjustSpacing - - aserialize a = Just $ case a of - TextAdjustSpacing -> "spacing" - TextAdjustSpacingAndGlyphs -> "spacingAndGlyphs" - -instance ParseableAttribute MarkerUnit where - aparse s = case s of - "strokeWidth" -> Just MarkerUnitStrokeWidth - "userSpaceOnUse" -> Just MarkerUnitUserSpaceOnUse - _ -> Nothing - - aserialize u = Just $ case u of - MarkerUnitStrokeWidth -> "strokeWidth" - MarkerUnitUserSpaceOnUse -> "userSpaceOnUse" - -instance ParseableAttribute Overflow where - aparse s = case s of - "visible" -> Just OverflowVisible - "hidden" -> Just OverflowHidden - _ -> Nothing - - aserialize u = Just $ case u of - OverflowVisible -> "visible" - OverflowHidden -> "hidden" - -instance ParseableAttribute MarkerOrientation where - aparse s = case (s, readMaybe s) of - ("auto", _) -> Just OrientationAuto - (_, Just f) -> Just $ OrientationAngle f - _ -> Nothing - - aserialize s = Just $ case s of - OrientationAuto -> "auto" - OrientationAngle f -> show f - -instance ParseableAttribute (Double, Double, Double, Double) where - aparse = parse viewBoxParser - aserialize = Just . serializeViewBox - -instance ParseableAttribute TextPathMethod where - aparse s = case s of - "align" -> Just TextPathAlign - "stretch" -> Just TextPathStretch - _ -> Nothing - aserialize m = Just $ case m of - TextPathAlign -> "align" - TextPathStretch -> "stretch" - -instance ParseableAttribute TextPathSpacing where - aparse s = case s of - "auto" -> Just TextPathSpacingAuto - "exact" -> Just TextPathSpacingExact - _ -> Nothing - - aserialize s = Just $ case s of - TextPathSpacingAuto -> "auto" - TextPathSpacingExact -> "exact" - -parse :: Parser a -> String -> Maybe a -parse p str = case parseOnly p (T.pack str) of - Left _ -> Nothing - Right r -> Just r - -parseMayStartDot :: Parser a -> String -> Maybe a -parseMayStartDot p l@('.':_) = parse p ('0':l) -parseMayStartDot p l = parse p l - -xmlUpdate :: (XMLUpdatable a) => a -> X.Element -> a -xmlUpdate initial el = foldl' grab initial attributes - where - grab value updater = - case attributeFinder (_attributeName updater) el of - Nothing -> value - Just v -> _attributeUpdater updater value v - -xmlUnparse :: (XMLUpdatable a) => X.Element -> a -xmlUnparse = xmlUpdate defaultSvg - -xmlUnparseWithDrawAttr - :: (XMLUpdatable a, WithDrawAttributes a) - => X.Element -> a -xmlUnparseWithDrawAttr e = - xmlUnparse e & drawAttr .~ xmlUnparse e - -data SvgAttributeLens t = SvgAttributeLens - { _attributeName :: String - , _attributeUpdater :: t -> String -> t - , _attributeSerializer :: t -> Maybe String - } - -class (WithDefaultSvg treeNode) => XMLUpdatable treeNode where - xmlTagName :: treeNode -> String - attributes :: [SvgAttributeLens treeNode] - - serializeTreeNode :: treeNode -> Maybe X.Element - -setChildren :: X.Element -> [X.Content] -> X.Element -setChildren xNode children = xNode { X.elContent = children } - -updateWithAccessor :: XMLUpdatable b => (a -> [b]) -> a -> Maybe X.Element -> Maybe X.Element -updateWithAccessor _ _ Nothing = Nothing -updateWithAccessor accessor node (Just xNode) = - Just . setChildren xNode . fmap X.Elem . catMaybes $ serializeTreeNode <$> accessor node - -genericSerializeNode :: (XMLUpdatable treeNode) => treeNode -> Maybe X.Element -genericSerializeNode node = - Just . X.unode (xmlTagName node) $ concatMap generateAttribute attributes - where - generateAttribute attr = case _attributeSerializer attr node of - Nothing -> [] - Just str -> return X.Attr - { X.attrKey = xName $ _attributeName attr - , X.attrVal = str - } - where - xName "href" = - X.QName { X.qName = "href" - , X.qURI = Nothing - , X.qPrefix = Just "xlink" } - xName h = X.unqual h - - -mergeAttributes :: X.Element -> X.Element -> X.Element -mergeAttributes thisXml otherXml = - thisXml { X.elAttribs = X.elAttribs otherXml ++ X.elAttribs thisXml } - -genericSerializeWithDrawAttr :: (XMLUpdatable treeNode, WithDrawAttributes treeNode) - => treeNode -> Maybe X.Element -genericSerializeWithDrawAttr node = mergeAttributes <$> thisXml <*> drawAttrNode where - thisXml = genericSerializeNode node - drawAttrNode = genericSerializeNode $ node ^. drawAttr - -type CssUpdater = - DrawAttributes -> [[CssElement]] -> DrawAttributes - -opacitySetter :: String -> Lens' a (Maybe Float) -> SvgAttributeLens a -opacitySetter attribute elLens = - SvgAttributeLens attribute updater serializer - where - serializer a = printf "%g" <$> a ^. elLens - updater el str = case parseMayStartDot num str of - Nothing -> el - Just v -> el & elLens .~ Just (realToFrac v) - -type Serializer e = e -> Maybe String - -parserSetter :: String -> Lens' a e -> (String -> Maybe e) -> Serializer e - -> SvgAttributeLens a -parserSetter attribute elLens parser serialize = - SvgAttributeLens attribute updater serializer - where - updater el str = case parser str of - Nothing -> el - Just v -> el & elLens .~ v - - serializer a = serialize $ a ^. elLens - -parseIn :: (Eq a, WithDefaultSvg s, ParseableAttribute a) - => String -> Lens' s a -> SvgAttributeLens s -parseIn attribute elLens = - SvgAttributeLens attribute updater serializer - where - updater el str = case aparse str of - Nothing -> el - Just v -> el & elLens .~ v - - serializer a - | v /= defaultVal = aserialize v - | otherwise = Nothing - where - v = a ^. elLens - defaultVal = defaultSvg ^. elLens - -parserLastSetter :: String -> Lens' a (Last e) -> (String -> Maybe e) -> Serializer e - -> SvgAttributeLens a -parserLastSetter attribute elLens parser serialize = - SvgAttributeLens attribute updater serializer - where - updater el str = case parser str of - Nothing -> el - Just v -> el & elLens .~ Last (Just v) - - serializer a = getLast (a ^. elLens) >>= serialize - -classSetter :: SvgAttributeLens DrawAttributes -classSetter = SvgAttributeLens "class" updater serializer - where - updater el str = - el & attrClass .~ (T.split (== ' ') $ T.pack str) - - serializer a = case a ^. attrClass of - [] -> Nothing - lst -> Just . T.unpack $ T.intercalate " " lst - -cssUniqueNumber :: ASetter DrawAttributes DrawAttributes - a (Last Number) - -> CssUpdater -cssUniqueNumber setter attr ((CssNumber n:_):_) = - attr & setter .~ Last (Just n) -cssUniqueNumber _ attr _ = attr - -cssUniqueFloat :: (Fractional n) - => ASetter DrawAttributes DrawAttributes a (Maybe n) - -> CssUpdater -cssUniqueFloat setter attr ((CssNumber (Num n):_):_) = - attr & setter .~ Just (realToFrac n) -cssUniqueFloat _ attr _ = attr - -cssUniqueMayFloat :: ASetter DrawAttributes DrawAttributes a (Last Double) - -> CssUpdater -cssUniqueMayFloat setter attr ((CssNumber (Num n):_):_) = - attr & setter .~ Last (Just n) -cssUniqueMayFloat _ attr _ = attr - -cssIdentAttr :: ParseableAttribute a => Lens' DrawAttributes a -> CssUpdater -cssIdentAttr setter attr ((CssIdent i:_):_) = case aparse $ T.unpack i of - Nothing -> attr - Just v -> attr & setter .~ v -cssIdentAttr _ attr _ = attr - -fontFamilyParser :: CssUpdater -fontFamilyParser attr (lst:_) = attr & fontFamily .~ fontNames - where - fontNames = Last . Just $ T.unpack <$> extractString lst - - extractString [] = [] - extractString (CssIdent n:rest) = n : extractString rest - extractString (CssString n:rest) = n : extractString rest - extractString (_:rest) = extractString rest -fontFamilyParser attr _ = attr - - -cssUniqueTexture :: ASetter DrawAttributes DrawAttributes - a (Last Texture) - -> CssUpdater -cssUniqueTexture setter attr ((CssIdent "none":_):_) = - attr & setter .~ Last (Just FillNone) -cssUniqueTexture setter attr ((CssColor c:_):_) = - attr & setter .~ Last (Just $ ColorRef c) -cssUniqueTexture setter attr ((CssFunction "url" [CssReference c]:_):_) = - attr & setter .~ Last (Just . TextureRef $ T.unpack c) -cssUniqueTexture _ attr _ = attr - -cssElementRefSetter :: Lens' DrawAttributes (Last ElementRef) - -> CssUpdater -cssElementRefSetter setter attr ((CssFunction "url" [CssReference c]:_):_) = - attr & setter .~ Last (Just . Ref $ T.unpack c) -cssElementRefSetter setter attr ((CssIdent "none":_):_) = - attr & setter .~ Last (Just RefNone) -cssElementRefSetter _ attr _ = attr - -cssMayStringSetter :: ASetter DrawAttributes DrawAttributes a (Maybe String) - -> CssUpdater -cssMayStringSetter setter attr ((CssIdent i:_):_) = - attr & setter .~ Just (T.unpack i) -cssMayStringSetter setter attr ((CssString i:_):_) = - attr & setter .~ Just (T.unpack i) -cssMayStringSetter _ attr _ = attr - -cssNullSetter :: CssUpdater -cssNullSetter attr _ = attr - -cssDashArray :: ASetter DrawAttributes DrawAttributes a (Last [Number]) - -> CssUpdater -cssDashArray setter attr (lst:_) = - case [n | CssNumber n <- lst ] of - [] -> attr - v -> attr & setter .~ Last (Just v) -cssDashArray _ attr _ = attr - - -drawAttributesList :: [(SvgAttributeLens DrawAttributes, CssUpdater)] -drawAttributesList = - [("stroke-width" `parseIn` strokeWidth, cssUniqueNumber strokeWidth) - ,("stroke" `parseIn` strokeColor, cssUniqueTexture strokeColor) - ,("fill" `parseIn` fillColor, cssUniqueTexture fillColor) - ,("stroke-linecap" `parseIn` strokeLineCap, cssIdentAttr strokeLineCap) - ,("stroke-linejoin" `parseIn` strokeLineJoin, cssIdentAttr strokeLineJoin) - ,("stroke-miterlimit" `parseIn` strokeMiterLimit, - cssUniqueMayFloat strokeMiterLimit) - - ,("transform" `parseIn` transform, const) - ,(opacitySetter "opacity" groupOpacity, cssUniqueFloat groupOpacity) - ,(opacitySetter "fill-opacity" fillOpacity, cssUniqueFloat fillOpacity) - ,(opacitySetter "stroke-opacity" strokeOpacity, cssUniqueFloat strokeOpacity) - ,("font-size" `parseIn` fontSize, cssUniqueNumber fontSize) - ,(parserLastSetter "font-family" fontFamily (Just . commaSeparate) - (Just . intercalate ", "), fontFamilyParser) - - ,("fill-rule" `parseIn` fillRule, cssIdentAttr fillRule) - ,("clip-rule" `parseIn` clipRule, cssIdentAttr clipRule) - ,("mask" `parseIn` maskRef, cssElementRefSetter maskRef) - ,(classSetter, cssNullSetter) -- can't set class in CSS - ,("id" `parseIn` attrId, cssMayStringSetter attrId) - ,("stroke-dashoffset" `parseIn` strokeOffset, - cssUniqueNumber strokeOffset) - ,("stroke-dasharray" `parseIn` strokeDashArray, cssDashArray strokeDashArray) - ,("text-anchor" `parseIn` textAnchor, cssIdentAttr textAnchor) - ,("clip-path" `parseIn` clipPathRef, cssElementRefSetter clipPathRef) - ,("marker-end" `parseIn` markerEnd, cssElementRefSetter markerEnd) - ,("marker-start" `parseIn` markerStart, cssElementRefSetter markerStart) - ,("marker-mid" `parseIn` markerMid, cssElementRefSetter markerMid) - ] - where - commaSeparate = - fmap (T.unpack . T.strip) . T.split (',' ==) . T.pack - -serializeDashArray :: [Number] -> String -serializeDashArray = - intercalate ", " . fmap serializeNumber - -instance XMLUpdatable DrawAttributes where - xmlTagName _ = "DRAWATTRIBUTES" - attributes = styleAttribute : fmap fst drawAttributesList - serializeTreeNode = genericSerializeNode - -styleAttribute :: SvgAttributeLens DrawAttributes -styleAttribute = SvgAttributeLens - { _attributeName = "style" - , _attributeUpdater = updater - , _attributeSerializer = const Nothing - } - where - updater attrs style = case parse styleString style of - Nothing -> attrs - Just decls -> foldl' applyer attrs decls - - cssUpdaters = [(T.pack $ _attributeName n, u) | (n, u) <- drawAttributesList] - applyer value (CssDeclaration txt elems) = - case lookup txt cssUpdaters of - Nothing -> value - Just f -> f value elems - -instance XMLUpdatable Rectangle where - xmlTagName _ = "rect" - serializeTreeNode = genericSerializeWithDrawAttr - attributes = - ["width" `parseIn` rectWidth - ,"height" `parseIn` rectHeight - ,"x" `parseIn` (rectUpperLeftCorner._1) - ,"y" `parseIn` (rectUpperLeftCorner._2) - ,"rx" `parseIn` (rectCornerRadius._1) - ,"ry" `parseIn` (rectCornerRadius._2) - ] - -instance XMLUpdatable Image where - xmlTagName _ = "image" - serializeTreeNode = genericSerializeWithDrawAttr - attributes = - ["width" `parseIn` imageWidth - ,"height" `parseIn` imageHeight - ,"x" `parseIn` (imageCornerUpperLeft._1) - ,"y" `parseIn` (imageCornerUpperLeft._2) - ,parserSetter "href" imageHref (Just . dropSharp) Just - ,"preserveAspectRatio" `parseIn` imageAspectRatio - ] - -instance XMLUpdatable Line where - xmlTagName _ = "line" - serializeTreeNode = genericSerializeWithDrawAttr - attributes = - ["x1" `parseIn` (linePoint1._1) - ,"y1" `parseIn` (linePoint1._2) - ,"x2" `parseIn` (linePoint2._1) - ,"y2" `parseIn` (linePoint2._2) - ] - -instance XMLUpdatable Ellipse where - xmlTagName _ = "ellipse" - serializeTreeNode = genericSerializeWithDrawAttr - attributes = - ["cx" `parseIn` (ellipseCenter._1) - ,"cy" `parseIn` (ellipseCenter._2) - ,"rx" `parseIn` ellipseXRadius - ,"ry" `parseIn` ellipseYRadius - ] - -instance XMLUpdatable Circle where - xmlTagName _ = "circle" - serializeTreeNode = genericSerializeWithDrawAttr - attributes = - ["cx" `parseIn` (circleCenter._1) - ,"cy" `parseIn` (circleCenter._2) - ,"r" `parseIn` circleRadius - ] - -instance XMLUpdatable Mask where - xmlTagName _ = "mask" - serializeTreeNode node = - updateWithAccessor _maskContent node $ - genericSerializeWithDrawAttr node - - attributes = - ["x" `parseIn` (maskPosition._1) - ,"y" `parseIn` (maskPosition._2) - ,"width" `parseIn` maskWidth - ,"height" `parseIn` maskHeight - ,"maskContentUnits" `parseIn` maskContentUnits - ,"maskUnits" `parseIn` maskUnits - ] - -instance XMLUpdatable ClipPath where - xmlTagName _ = "clipPath" - serializeTreeNode node = - updateWithAccessor _clipPathContent node $ - genericSerializeWithDrawAttr node - attributes = - ["clipPathUnits" `parseIn` clipPathUnits] - -instance XMLUpdatable Polygon where - xmlTagName _ = "polygon" - serializeTreeNode = genericSerializeWithDrawAttr - attributes = ["points" `parseIn` polygonPoints] - -instance XMLUpdatable PolyLine where - xmlTagName _ = "polyline" - serializeTreeNode = genericSerializeWithDrawAttr - attributes = ["points" `parseIn` polyLinePoints] - -instance XMLUpdatable Path where - xmlTagName _ = "path" - serializeTreeNode = genericSerializeWithDrawAttr - attributes = ["d" `parseIn` pathDefinition] - -instance XMLUpdatable LinearGradient where - xmlTagName _ = "linearGradient" - serializeTreeNode node = - updateWithAccessor _linearGradientStops node $ genericSerializeNode node - - attributes = - ["gradientTransform" `parseIn` linearGradientTransform - ,"gradientUnits" `parseIn` linearGradientUnits - ,"spreadMethod" `parseIn` linearGradientSpread - ,"x1" `parseIn` (linearGradientStart._1) - ,"y1" `parseIn` (linearGradientStart._2) - ,"x2" `parseIn` (linearGradientStop._1) - ,"y2" `parseIn` (linearGradientStop._2) - ] - -instance XMLUpdatable Tree where - xmlTagName _ = "TREE" - attributes = [] - serializeTreeNode e = case e of - None -> Nothing - UseTree u _ -> serializeTreeNode u - GroupTree g -> serializeTreeNode g - SymbolTree s -> serializeTreeNode s - PathTree p -> serializeTreeNode p - CircleTree c -> serializeTreeNode c - PolyLineTree p -> serializeTreeNode p - PolygonTree p -> serializeTreeNode p - EllipseTree el -> serializeTreeNode el - LineTree l -> serializeTreeNode l - RectangleTree r -> serializeTreeNode r - TextTree Nothing t -> serializeTreeNode t - ImageTree i -> serializeTreeNode i - TextTree (Just p) t -> do - textNode <- serializeTreeNode t - pathNode <- serializeTreeNode p - let sub = [X.Elem . setChildren pathNode $ X.elContent textNode] - return $ setChildren textNode sub - - -isNotNone :: Tree -> Bool -isNotNone None = False -isNotNone _ = True - -instance XMLUpdatable (Group Tree) where - xmlTagName _ = "g" - serializeTreeNode node = - updateWithAccessor (filter isNotNone . _groupChildren) node $ - genericSerializeWithDrawAttr node - attributes = [] - -instance XMLUpdatable (Symbol Tree) where - xmlTagName _ = "symbol" - serializeTreeNode node = - updateWithAccessor (filter isNotNone . _groupChildren . _groupOfSymbol) node $ - genericSerializeWithDrawAttr node - attributes = - ["viewBox" `parseIn` (groupOfSymbol . groupViewBox) - ,"preserveAspectRatio" `parseIn` (groupOfSymbol . groupAspectRatio) - ] - - -instance XMLUpdatable RadialGradient where - xmlTagName _ = "radialGradient" - serializeTreeNode node = - updateWithAccessor _radialGradientStops node $ genericSerializeNode node - attributes = - ["gradientTransform" `parseIn` radialGradientTransform - ,"gradientUnits" `parseIn` radialGradientUnits - ,"spreadMethod" `parseIn` radialGradientSpread - ,"cx" `parseIn` (radialGradientCenter._1) - ,"cy" `parseIn` (radialGradientCenter._2) - ,"r" `parseIn` radialGradientRadius - ,"fx" `parseIn` radialGradientFocusX - ,"fy" `parseIn` radialGradientFocusY - ] - -instance XMLUpdatable Use where - xmlTagName _ = "use" - serializeTreeNode = genericSerializeWithDrawAttr - attributes = - ["x" `parseIn` (useBase._1) - ,"y" `parseIn` (useBase._2) - ,"width" `parseIn` useWidth - ,"height" `parseIn` useHeight - ,parserSetter "href" useName (Just . dropSharp) (Just . ('#':)) - ] - -dropSharp :: String -> String -dropSharp ('#':rest) = rest -dropSharp a = a - -instance XMLUpdatable TextInfo where - xmlTagName _ = "tspan" - serializeTreeNode = genericSerializeNode - attributes = - [parserSetter "x" textInfoX (parse dashArray) dashNotEmpty - ,parserSetter "y" textInfoY (parse dashArray) dashNotEmpty - ,parserSetter "dx" textInfoDX (parse dashArray) dashNotEmpty - ,parserSetter "dy" textInfoDY (parse dashArray) dashNotEmpty - ,parserSetter "rotate" textInfoRotate - (parse numberList) - rotateNotEmpty - ,"textLength" `parseIn` textInfoLength - ] - where - dashNotEmpty [] = Nothing - dashNotEmpty lst = Just $ serializeDashArray lst - - rotateNotEmpty [] = Nothing - rotateNotEmpty lst = - Just . unwords $ printf "%g" <$> lst - - -instance XMLUpdatable TextPath where - xmlTagName _ = "textPath" - serializeTreeNode = genericSerializeNode - attributes = - ["startOffset" `parseIn` textPathStartOffset - ,"method" `parseIn` textPathMethod - ,"spacing" `parseIn` textPathSpacing - ,parserSetter "href" textPathName (Just . dropSharp) (Just . ('#':)) - ] - -instance XMLUpdatable Text where - xmlTagName _ = "text" - serializeTreeNode = serializeText - attributes = ["lengthAdjust" `parseIn` textAdjust] - - -instance XMLUpdatable Pattern where - xmlTagName _ = "pattern" - serializeTreeNode node = - updateWithAccessor _patternElements node $ genericSerializeWithDrawAttr node - attributes = - ["viewBox" `parseIn` patternViewBox - ,"patternUnits" `parseIn` patternUnit - ,"width" `parseIn` patternWidth - ,"height" `parseIn` patternHeight - ,"x" `parseIn` (patternPos._1) - ,"y" `parseIn` (patternPos._2) - ,"preserveAspectRatio" `parseIn` patternAspectRatio - ] - -instance XMLUpdatable Marker where - xmlTagName _ = "marker" - serializeTreeNode node = - updateWithAccessor _markerElements node $ genericSerializeWithDrawAttr node - attributes = - ["refX" `parseIn` (markerRefPoint._1) - ,"refY" `parseIn` (markerRefPoint._2) - ,"markerWidth" `parseIn` markerWidth - ,"markerHeight" `parseIn` markerHeight - ,"patternUnits" `parseIn` markerUnits - ,"orient" `parseIn` markerOrient - ,"viewBox" `parseIn` markerViewBox - ,"overflow" `parseIn` markerOverflow - ,"preserveAspectRatio" `parseIn` markerAspectRatio - ] - -serializeText :: Text -> Maybe X.Element -serializeText topText = namedNode where - namedNode = fmap (\x -> x { X.elName = X.unqual "text" }) topNode - topNode = serializeSpan $ _textRoot topText - - serializeSpan tspan = case (info, drawInfo) of - (Nothing, Nothing) -> Nothing - (Just a, Nothing) -> Just $ setChildren a subContent - (Nothing, Just b) -> Just $ setChildren b subContent - (Just a, Just b) -> - Just $ setChildren (mergeAttributes a b) subContent - where - info = genericSerializeNode $ _spanInfo tspan - drawInfo = genericSerializeNode $ _spanDrawAttributes tspan - subContent = catMaybes $ serializeContent <$> _spanContent tspan - - serializeContent (SpanText t) = Just . X.Text $ X.blank_cdata { X.cdData = T.unpack t } - serializeContent (SpanTextRef _t) = Just . X.Text $ X.blank_cdata { X.cdData = "" } - serializeContent (SpanSub sub) = X.Elem <$> serializeSpan sub - -unparseText :: [X.Content] -> ([TextSpanContent], Maybe TextPath) -unparseText = extractResult . go True - where - extractResult (a, b, _) = (a, b) - - go startStrip [] = ([], Nothing, startStrip) - go startStrip (X.CRef _:rest) = go startStrip rest - go startStrip (X.Elem e@(nodeName -> "tspan"):rest) = - (SpanSub spans : trest, mpath, retStrip) - where - (trest, mpath, retStrip) = go restStrip rest - (sub, _, restStrip) = go startStrip $ X.elContent e - spans = TextSpan (xmlUnparse e) (xmlUnparse e) sub - - go startStrip (X.Elem e@(nodeName -> "tref"):rest) = - case attributeFinder "href" e of - Nothing -> go startStrip rest - Just v -> (SpanTextRef v : trest, mpath, stripRet) - where (trest, mpath, stripRet) = go startStrip rest - - go startStrip (X.Elem e@(nodeName -> "textPath"):rest) = - case attributeFinder "href" e of - Nothing -> go startStrip rest - Just v -> (tsub ++ trest, pure p, retStrp) - where - p = (xmlUnparse e) { _textPathName = dropSharp v } - (trest, _, retStrp) = go restStrip rest - (tsub, _, restStrip) = go startStrip $ X.elContent e - - go startStrip (X.Elem _:rest) = go startStrip rest - go startStrip (X.Text t:rest) - | T.length cleanText == 0 = go startStrip rest - | otherwise = - (SpanText cleanText : trest, mpath, stripRet) - where - (trest, mpath, stripRet) = go subShouldStrip rest - - subShouldStrip = T.pack " " `T.isSuffixOf` cleanText - - space = T.singleton ' ' - singulariseSpaces tt - | space `T.isPrefixOf` tt = space - | otherwise = tt - - stripStart | startStrip = T.stripStart - | otherwise = id - - cleanText = stripStart - . T.concat - . fmap singulariseSpaces - . T.groupBy (\a b -> (a /= ' ' && b /= ' ') || a == b) - . T.filter (\c -> c /= '\n' && c /= '\r') - . T.map (\c -> if c == '\t' then ' ' else c) - . T.pack - $ X.cdData t - -gradientOffsetSetter :: SvgAttributeLens GradientStop -gradientOffsetSetter = SvgAttributeLens "offset" setter serialize - where - serialize a = Just $ printf "%d%%" percentage - where percentage = floor . (100 *) $ a ^. gradientOffset :: Int - - setter el str = el & gradientOffset .~ val - where - val = realToFrac $ case parseMayStartDot complexNumber str of - Nothing -> 0 - Just (Num n) -> n - Just (Px n) -> n - Just (Percent n) -> n - Just (Em n) -> n - Just (Pc n) -> n - Just (Mm n) -> n - Just (Cm n) -> n - Just (Point n) -> n - Just (Inches n) -> n - -instance XMLUpdatable GradientStop where - xmlTagName _ = "stop" - serializeTreeNode = genericSerializeNode - attributes = - [gradientOffsetSetter - ,"stop-color" `parseIn` gradientColor - ] - - -data Symbols = Symbols - { symbols :: !(M.Map String Element) - , cssStyle :: [CssRule] - } - -emptyState :: Symbols -emptyState = Symbols mempty mempty - -parseGradientStops :: X.Element -> [GradientStop] -parseGradientStops = concatMap unStop . elChildren - where - unStop e@(nodeName -> "stop") = [xmlUnparse e] - unStop _ = [] - -withId :: X.Element -> (X.Element -> Element) - -> State Symbols Tree -withId el f = case attributeFinder "id" el of - Nothing -> return None - Just elemId -> do - modify $ \s -> - s { symbols = M.insert elemId (f el) $ symbols s } - return None - -unparseDefs :: X.Element -> State Symbols Tree -unparseDefs e@(nodeName -> "pattern") = do - subElements <- mapM unparse $ elChildren e - withId e . const . ElementPattern $ pat { _patternElements = subElements} - where - pat = xmlUnparse e -unparseDefs e@(nodeName -> "marker") = do - subElements <- mapM unparse $ elChildren e - withId e . const . ElementMarker $ mark {_markerElements = subElements } - where - mark = xmlUnparseWithDrawAttr e -unparseDefs e@(nodeName -> "mask") = do - children <- mapM unparse $ elChildren e - let realChildren = filter isNotNone children - parsedMask = xmlUnparseWithDrawAttr e - withId e . const . ElementMask $ parsedMask { _maskContent = realChildren } - -unparseDefs e@(nodeName -> "clipPath") = do - children <- mapM unparse $ elChildren e - let realChildren = filter isNotNone children - parsedClip = xmlUnparseWithDrawAttr e - withId e . const . ElementClipPath $ parsedClip { _clipPathContent = realChildren } - -unparseDefs e@(nodeName -> "linearGradient") = - withId e $ ElementLinearGradient . unparser - where - unparser ee = - xmlUnparse ee & linearGradientStops .~ parseGradientStops ee -unparseDefs e@(nodeName -> "radialGradient") = - withId e $ ElementRadialGradient . unparser - where - unparser ee = - xmlUnparse ee & radialGradientStops .~ parseGradientStops ee -unparseDefs e = do - el <- unparse e - withId e (const $ ElementGeometry el) - -unparse :: X.Element -> State Symbols Tree -unparse e@(nodeName -> "style") = do - case parseOnly (many1 ruleSet) . T.pack $ strContent e of - Left _ -> return () - Right rules -> - modify $ \s -> s { cssStyle = cssStyle s ++ rules } - return None -unparse e@(nodeName -> "defs") = do - mapM_ unparseDefs $ elChildren e - return None -unparse e@(nodeName -> "symbol") = do - symbolChildren <- mapM unparse $ elChildren e - let realChildren = filter isNotNone symbolChildren - pure . SymbolTree . Symbol $ groupNode & groupChildren .~ realChildren - where - groupNode :: Group Tree - groupNode = _groupOfSymbol $ xmlUnparseWithDrawAttr e - -unparse e@(nodeName -> "g") = do - children <- mapM unparse $ elChildren e - let realChildren = filter isNotNone children - - groupNode :: Group Tree - groupNode = xmlUnparseWithDrawAttr e - - pure $ GroupTree $ groupNode & groupChildren .~ realChildren - -unparse e@(nodeName -> "text") = do - pathWithGeometry <- pathGeomtryOf tPath - pure . TextTree pathWithGeometry $ xmlUnparse e & textRoot .~ root - where - (textContent, tPath) = unparseText $ X.elContent e - - pathGeomtryOf Nothing = pure Nothing - pathGeomtryOf (Just pathInfo) = do - pathElem <- gets $ M.lookup (_textPathName pathInfo) . symbols - case pathElem of - Nothing -> pure Nothing - Just (ElementLinearGradient _) -> pure Nothing - Just (ElementRadialGradient _) -> pure Nothing - Just (ElementPattern _) -> pure Nothing - Just (ElementMask _) -> pure Nothing - Just (ElementClipPath _) -> pure Nothing - Just (ElementMarker _) -> pure Nothing - Just (ElementGeometry (PathTree p)) -> - pure . Just $ pathInfo { _textPathData = _pathDefinition p } - Just (ElementGeometry _) -> pure Nothing - - root = TextSpan - { _spanInfo = xmlUnparse e - , _spanDrawAttributes = xmlUnparse e - , _spanContent = textContent - } - -unparse e = pure $ case nodeName e of - "image" -> ImageTree parsed - "ellipse" -> EllipseTree parsed - "rect" -> RectangleTree parsed - "polyline" -> PolyLineTree parsed - "polygon" -> PolygonTree parsed - "circle"-> CircleTree parsed - "line" -> LineTree parsed - "path" -> PathTree parsed - "use" -> UseTree parsed Nothing - _ -> None - where - parsed :: (XMLUpdatable a, WithDrawAttributes a) => a - parsed = xmlUnparseWithDrawAttr e - -unparseDocument :: FilePath -> X.Element -> Maybe Document -unparseDocument rootLocation e@(nodeName -> "svg") = Just Document - { _viewBox = - attributeFinder "viewBox" e >>= parse viewBoxParser - , _elements = parsedElements - , _width = lengthFind "width" - , _height = lengthFind "height" - , _definitions = symbols named - , _description = "" - , _styleRules = cssStyle named - , _documentLocation = rootLocation - } - where - (parsedElements, named) = - runState (mapM unparse $ elChildren e) emptyState - lengthFind n = - attributeFinder n e >>= parse complexNumber -unparseDocument _ _ = Nothing - --- | Transform a SVG document to a XML node. -xmlOfDocument :: Document -> X.Element -xmlOfDocument doc = - X.node (X.unqual "svg") (attrs, descTag ++ styleTag ++ defsTag ++ children) - where - attr name = X.Attr (X.unqual name) - children = catMaybes [serializeTreeNode el | el <- _elements doc] - - defsTag | null defs = [] - | otherwise = [X.node (X.unqual "defs") defs] - - defs = catMaybes [elementRender k e | (k, e) <- M.assocs $ _definitions doc] - - elementRender k e = case e of - ElementGeometry t -> serialize t - ElementMarker m -> serialize m - ElementMask m -> serialize m - ElementClipPath c -> serialize c - ElementPattern p -> serialize p - ElementLinearGradient lg -> addId $ serializeTreeNode lg - ElementRadialGradient rg -> addId $ serializeTreeNode rg - where - addId = fmap (X.add_attr $ attr "id" k) - - serialize :: (WithDrawAttributes e, XMLUpdatable e) => e -> Maybe X.Element - serialize el = case el^.drawAttr.attrId of - Nothing -> addId $ serializeTreeNode el - Just _id -> - let newNode = el & drawAttr.attrId .~ Just k in - serializeTreeNode newNode - - docViewBox = case _viewBox doc of - Nothing -> [] - Just b -> [attr "viewBox" $ serializeViewBox b] - - descTag = case _description doc of - "" -> [] - txt -> [X.node (X.unqual "desc") txt] - - styleTag = case _styleRules doc of - [] -> [] - rules -> [X.node (X.unqual "style") - ([attr "type" "text/css"], txt)] - where txt = TL.unpack . TB.toLazyText $ foldMap tserialize rules - - attrs = - docViewBox ++ - [attr "xmlns" "http://www.w3.org/2000/svg" - ,attr "xmlns:xlink" "http://www.w3.org/1999/xlink" - ,attr "version" "1.1"] ++ - catMaybes [attr "width" . serializeNumber <$> _width doc - ,attr "height" . serializeNumber <$> _height doc - ] - +{-# LANGUAGE CPP #-}+{-# LANGUAGE ViewPatterns #-}+{-# LANGUAGE RankNTypes #-}+{-# LANGUAGE OverloadedStrings #-}+{-# LANGUAGE FlexibleInstances #-}+{-# LANGUAGE FlexibleContexts #-}+module Graphics.Svg.XmlParser( xmlOfDocument+ , unparseDocument++ , SvgAttributeLens( .. )+ , drawAttributesList+ ) where+++#if !MIN_VERSION_base(4,6,0)+import Text.Read( reads )+#else+import Text.Read( readMaybe )+#endif++#if !MIN_VERSION_base(4,8,0)+import Control.Applicative( pure, (<$>), (<$), (<*>) )+import Data.Foldable( foldMap )+import Data.Monoid( mempty )+#endif++import Control.Applicative( (<|>), many )++import Control.Lens hiding( transform, children, elements, element )+import Control.Monad.State.Strict( State, runState, modify, gets )+import Data.Maybe( fromMaybe, catMaybes )+import Data.Monoid( Last( Last ), getLast, (<>) )+import Data.List( foldl', intercalate )+import Text.XML.Light.Proc( findAttrBy, elChildren, strContent )+import qualified Text.XML.Light as X+import qualified Data.Text as T+import qualified Data.Text.Lazy as TL+import qualified Data.Text.Lazy.Builder as TB+import qualified Data.Map as M+import Data.Attoparsec.Text( Parser, string, parseOnly, many1 )+import Codec.Picture( PixelRGBA8( .. ) )+import Graphics.Svg.Types+import Graphics.Svg.PathParser+import Graphics.Svg.ColorParser+import Graphics.Svg.CssTypes( CssDeclaration( .. )+ , CssElement( .. )+ , CssRule+ , tserialize+ )+import Graphics.Svg.CssParser( complexNumber+ , num+ , ruleSet+ , dashArray+ , styleString+ , numberList )++import Text.Printf( printf )++{-import Debug.Trace-}++#if !MIN_VERSION_base(4,6,0)+readMaybe :: Read a => String -> Maybe a+readMaybe str = case reads str of+ [] -> Nothing+ (x, _):_ -> Just x+#endif++nodeName :: X.Element -> String+nodeName = X.qName . X.elName++attributeFinder :: String -> X.Element -> Maybe String+attributeFinder str =+ findAttrBy (\a -> X.qName a == str)++-- | Helper class to help simplify parsing code+-- for various attributes.+class ParseableAttribute a where+ aparse :: String -> Maybe a+ aserialize :: a -> Maybe String++instance ParseableAttribute v => ParseableAttribute (Maybe v) where+ aparse = fmap Just . aparse+ aserialize = (>>= aserialize)++instance ParseableAttribute v => ParseableAttribute (Last v) where+ aparse = fmap Last . aparse+ aserialize = aserialize . getLast++instance ParseableAttribute String where+ aparse = Just+ aserialize = Just++instance ParseableAttribute Number where+ aparse = parseMayStartDot complexNumber+ aserialize = Just . serializeNumber++instance ParseableAttribute [Number] where+ aparse = parse dashArray+ aserialize = Just . serializeDashArray++instance ParseableAttribute PixelRGBA8 where+ aparse = parse colorParser+ aserialize = Just . colorSerializer++instance ParseableAttribute [PathCommand] where+ aparse = parse pathParser+ aserialize = Just . serializeCommands++instance ParseableAttribute GradientPathCommand where+ aparse = parse gradientCommand+ aserialize = Just . serializeGradientCommand++instance ParseableAttribute [RPoint] where+ aparse = parse pointData+ aserialize = Just . serializePoints++instance ParseableAttribute Double where+ aparse = parseMayStartDot num+ aserialize v = Just $ printf "%g" v++instance ParseableAttribute Texture where+ aparse = parse textureParser+ aserialize = Just . textureSerializer++instance ParseableAttribute [Transformation] where+ aparse = parse $ many transformParser+ aserialize = Just . serializeTransformations++instance ParseableAttribute Alignment where+ aparse s = Just $ case s of+ "none" -> AlignNone + "xMinYMin" -> AlignxMinYMin+ "xMidYMin" -> AlignxMidYMin+ "xMaxYMin" -> AlignxMaxYMin+ "xMinYMid" -> AlignxMinYMid+ "xMidYMid" -> AlignxMidYMid+ "xMaxYMid" -> AlignxMaxYMid+ "xMinYMax" -> AlignxMinYMax+ "xMidYMax" -> AlignxMidYMax+ "xMaxYMax" -> AlignxMaxYMax+ _ -> _aspectRatioAlign defaultSvg++ aserialize v = Just $ case v of+ AlignNone -> "none" + AlignxMinYMin -> "xMinYMin"+ AlignxMidYMin -> "xMidYMin"+ AlignxMaxYMin -> "xMaxYMin"+ AlignxMinYMid -> "xMinYMid"+ AlignxMidYMid -> "xMidYMid"+ AlignxMaxYMid -> "xMaxYMid"+ AlignxMinYMax -> "xMinYMax"+ AlignxMidYMax -> "xMidYMax"+ AlignxMaxYMax -> "xMaxYMax"++instance ParseableAttribute MeshGradientType where+ aparse s = Just $ case s of+ "bilinear" -> GradientBilinear+ "bicubic" -> GradientBicubic+ _ -> GradientBilinear+ + aserialize v = Just $ case v of+ GradientBilinear -> "bilinear"+ GradientBicubic -> "bicubic"++instance ParseableAttribute MeetSlice where+ aparse s = case s of+ "meet" -> Just Meet+ "slice" -> Just Slice+ _ -> Nothing++ aserialize v = Just $ case v of+ Meet -> "meet"+ Slice -> "slice"++instance ParseableAttribute PreserveAspectRatio where+ aserialize v = Just $ defer <> align <> meetSlice where+ defer = if _aspectRatioDefer v then "defer " else ""+ align = fromMaybe "" . aserialize $ _aspectRatioAlign v+ meetSlice = fromMaybe "" $ aserialize =<< _aspectRatioMeetSlice v++ aparse s = case words s of+ [] -> Nothing+ [align] -> Just $ defaultSvg { _aspectRatioAlign = alignOf align }+ ["defer", align] ->+ Just $ defaultSvg+ { _aspectRatioDefer = True+ , _aspectRatioAlign = alignOf align+ }+ [align, meet] ->+ Just $ defaultSvg+ { _aspectRatioMeetSlice = aparse meet+ , _aspectRatioAlign = alignOf align+ }+ ["defer", align, meet] ->+ Just $ PreserveAspectRatio + { _aspectRatioDefer = True+ , _aspectRatioAlign = alignOf align+ , _aspectRatioMeetSlice = aparse meet+ }+ _ -> Nothing+ where+ alignOf = fromMaybe (_aspectRatioAlign defaultSvg) . aparse++instance ParseableAttribute Cap where+ aparse s = case s of+ "butt" -> Just CapButt+ "round" -> Just CapRound+ "square" -> Just CapSquare+ _ -> Nothing++ aserialize c = Just $ case c of+ CapButt -> "butt"+ CapRound -> "round"+ CapSquare -> "square"++instance ParseableAttribute TextAnchor where+ aparse s = case s of+ "middle" -> Just TextAnchorMiddle+ "start" -> Just TextAnchorStart+ "end" -> Just TextAnchorEnd+ _ -> Nothing++ aserialize t = Just $ case t of+ TextAnchorMiddle -> "middle"+ TextAnchorStart -> "start"+ TextAnchorEnd -> "end"++instance ParseableAttribute ElementRef where+ aparse s = case parseOnly pa $ T.pack s of+ Left _ -> Nothing+ Right v -> Just v+ where+ pa = (RefNone <$ string "none")+ <|> (Ref <$> urlRef)++ aserialize c = Just $ case c of+ Ref r -> "url(#" <> r <> ")"+ RefNone -> "none"++instance ParseableAttribute LineJoin where+ aparse s = case s of+ "miter" -> Just JoinMiter+ "round" -> Just JoinRound+ "bevel" -> Just JoinBevel+ _ -> Nothing++ aserialize j = Just $ case j of+ JoinMiter -> "miter"+ JoinRound -> "round"+ JoinBevel -> "bevel"++instance ParseableAttribute CoordinateUnits where+ aparse s = case s of+ "userSpaceOnUse" -> Just CoordUserSpace+ "objectBoundingBox" -> Just CoordBoundingBox+ _ -> Just CoordBoundingBox++ aserialize uni = Just $ case uni of+ CoordUserSpace -> "userSpaceOnUse"+ CoordBoundingBox -> "objectBoundingBox"++instance ParseableAttribute Spread where+ aparse s = case s of+ "pad" -> Just SpreadPad+ "reflect" -> Just SpreadReflect+ "repeat" -> Just SpreadRepeat+ _ -> Nothing++ aserialize s = Just $ case s of+ SpreadPad -> "pad"+ SpreadReflect -> "reflect"+ SpreadRepeat -> "repeat"++instance ParseableAttribute FillRule where+ aparse s = case s of+ "nonzero" -> Just FillNonZero+ "evenodd" -> Just FillEvenOdd+ _ -> Nothing++ aserialize f = Just $ case f of+ FillNonZero -> "nonzero"+ FillEvenOdd -> "evenodd"++instance ParseableAttribute TextAdjust where+ aparse s = Just $ case s of+ "spacing" -> TextAdjustSpacing+ "spacingAndGlyphs" -> TextAdjustSpacingAndGlyphs+ _ -> TextAdjustSpacing++ aserialize a = Just $ case a of+ TextAdjustSpacing -> "spacing"+ TextAdjustSpacingAndGlyphs -> "spacingAndGlyphs"++instance ParseableAttribute MarkerUnit where+ aparse s = case s of+ "strokeWidth" -> Just MarkerUnitStrokeWidth+ "userSpaceOnUse" -> Just MarkerUnitUserSpaceOnUse+ _ -> Nothing++ aserialize u = Just $ case u of+ MarkerUnitStrokeWidth -> "strokeWidth"+ MarkerUnitUserSpaceOnUse -> "userSpaceOnUse"++instance ParseableAttribute Overflow where+ aparse s = case s of+ "visible" -> Just OverflowVisible+ "hidden" -> Just OverflowHidden+ _ -> Nothing++ aserialize u = Just $ case u of+ OverflowVisible -> "visible"+ OverflowHidden -> "hidden"++instance ParseableAttribute MarkerOrientation where+ aparse s = case (s, readMaybe s) of+ ("auto", _) -> Just OrientationAuto+ (_, Just f) -> Just $ OrientationAngle f+ _ -> Nothing++ aserialize s = Just $ case s of+ OrientationAuto -> "auto"+ OrientationAngle f -> show f++instance ParseableAttribute (Double, Double, Double, Double) where+ aparse = parse viewBoxParser+ aserialize = Just . serializeViewBox++instance ParseableAttribute TextPathMethod where+ aparse s = case s of+ "align" -> Just TextPathAlign+ "stretch" -> Just TextPathStretch+ _ -> Nothing+ aserialize m = Just $ case m of+ TextPathAlign -> "align"+ TextPathStretch -> "stretch"++instance ParseableAttribute TextPathSpacing where+ aparse s = case s of+ "auto" -> Just TextPathSpacingAuto+ "exact" -> Just TextPathSpacingExact+ _ -> Nothing++ aserialize s = Just $ case s of+ TextPathSpacingAuto -> "auto"+ TextPathSpacingExact -> "exact"++parse :: Parser a -> String -> Maybe a+parse p str = case parseOnly p (T.pack str) of+ Left _ -> Nothing+ Right r -> Just r++parseMayStartDot :: Parser a -> String -> Maybe a+parseMayStartDot p l@('.':_) = parse p ('0':l)+parseMayStartDot p l = parse p l++xmlUpdate :: (XMLUpdatable a) => a -> X.Element -> a+xmlUpdate initial el = foldl' grab initial attributes+ where+ grab value updater =+ case attributeFinder (_attributeName updater) el of+ Nothing -> value+ Just v -> _attributeUpdater updater value v++xmlUnparse :: (XMLUpdatable a) => X.Element -> a+xmlUnparse = xmlUpdate defaultSvg++xmlUnparseWithDrawAttr+ :: (XMLUpdatable a, WithDrawAttributes a)+ => X.Element -> a+xmlUnparseWithDrawAttr e =+ xmlUnparse e & drawAttr .~ xmlUnparse e++data SvgAttributeLens t = SvgAttributeLens+ { _attributeName :: String+ , _attributeUpdater :: t -> String -> t+ , _attributeSerializer :: t -> Maybe String+ }++class (WithDefaultSvg treeNode) => XMLUpdatable treeNode where+ xmlTagName :: treeNode -> String+ attributes :: [SvgAttributeLens treeNode]++ serializeTreeNode :: treeNode -> Maybe X.Element++setChildren :: X.Element -> [X.Content] -> X.Element+setChildren xNode children = xNode { X.elContent = children }++updateWithAccessor :: XMLUpdatable b => (a -> [b]) -> a -> Maybe X.Element -> Maybe X.Element+updateWithAccessor _ _ Nothing = Nothing+updateWithAccessor accessor node (Just xNode) =+ Just . setChildren xNode . fmap X.Elem . catMaybes $ serializeTreeNode <$> accessor node++genericSerializeNode :: (XMLUpdatable treeNode) => treeNode -> Maybe X.Element+genericSerializeNode node =+ Just . X.unode (xmlTagName node) $ concatMap generateAttribute attributes+ where+ generateAttribute attr = case _attributeSerializer attr node of+ Nothing -> []+ Just str -> return X.Attr+ { X.attrKey = xName $ _attributeName attr+ , X.attrVal = str+ }+ where+ xName "href" =+ X.QName { X.qName = "href"+ , X.qURI = Nothing+ , X.qPrefix = Just "xlink" }+ xName h = X.unqual h+++mergeAttributes :: X.Element -> X.Element -> X.Element+mergeAttributes thisXml otherXml =+ thisXml { X.elAttribs = X.elAttribs otherXml ++ X.elAttribs thisXml }++genericSerializeWithDrawAttr :: (XMLUpdatable treeNode, WithDrawAttributes treeNode)+ => treeNode -> Maybe X.Element+genericSerializeWithDrawAttr node = mergeAttributes <$> thisXml <*> drawAttrNode where+ thisXml = genericSerializeNode node+ drawAttrNode = genericSerializeNode $ node ^. drawAttr++type CssUpdater =+ DrawAttributes -> [[CssElement]] -> DrawAttributes++opacitySetter :: String -> Lens' a (Maybe Float) -> SvgAttributeLens a+opacitySetter attribute elLens =+ SvgAttributeLens attribute updater serializer+ where+ serializer a = printf "%g" <$> a ^. elLens+ updater el str = case parseMayStartDot num str of+ Nothing -> el+ Just v -> el & elLens .~ Just (realToFrac v)++type Serializer e = e -> Maybe String++parserSetter :: String -> Lens' a e -> (String -> Maybe e) -> Serializer e+ -> SvgAttributeLens a+parserSetter attribute elLens parser serialize =+ SvgAttributeLens attribute updater serializer+ where+ updater el str = case parser str of+ Nothing -> el+ Just v -> el & elLens .~ v++ serializer a = serialize $ a ^. elLens++parseIn :: (Eq a, WithDefaultSvg s, ParseableAttribute a)+ => String -> Lens' s a -> SvgAttributeLens s+parseIn attribute elLens =+ SvgAttributeLens attribute updater serializer+ where+ updater el str = case aparse str of+ Nothing -> el+ Just v -> el & elLens .~ v++ serializer a+ | v /= defaultVal = aserialize v+ | otherwise = Nothing+ where+ v = a ^. elLens+ defaultVal = defaultSvg ^. elLens++parserLastSetter :: String -> Lens' a (Last e) -> (String -> Maybe e) -> Serializer e+ -> SvgAttributeLens a+parserLastSetter attribute elLens parser serialize =+ SvgAttributeLens attribute updater serializer+ where+ updater el str = case parser str of+ Nothing -> el+ Just v -> el & elLens .~ Last (Just v)++ serializer a = getLast (a ^. elLens) >>= serialize++classSetter :: SvgAttributeLens DrawAttributes+classSetter = SvgAttributeLens "class" updater serializer+ where+ updater el str =+ el & attrClass .~ (T.split (== ' ') $ T.pack str)++ serializer a = case a ^. attrClass of+ [] -> Nothing+ lst -> Just . T.unpack $ T.intercalate " " lst++cssUniqueNumber :: ASetter DrawAttributes DrawAttributes+ a (Last Number)+ -> CssUpdater+cssUniqueNumber setter attr ((CssNumber n:_):_) =+ attr & setter .~ Last (Just n)+cssUniqueNumber _ attr _ = attr++cssUniqueFloat :: (Fractional n)+ => ASetter DrawAttributes DrawAttributes a (Maybe n)+ -> CssUpdater+cssUniqueFloat setter attr ((CssNumber (Num n):_):_) =+ attr & setter .~ Just (realToFrac n)+cssUniqueFloat _ attr _ = attr++cssUniqueMayFloat :: ASetter DrawAttributes DrawAttributes a (Last Double)+ -> CssUpdater+cssUniqueMayFloat setter attr ((CssNumber (Num n):_):_) =+ attr & setter .~ Last (Just n)+cssUniqueMayFloat _ attr _ = attr++cssIdentAttr :: ParseableAttribute a => Lens' DrawAttributes a -> CssUpdater+cssIdentAttr setter attr ((CssIdent i:_):_) = case aparse $ T.unpack i of+ Nothing -> attr+ Just v -> attr & setter .~ v+cssIdentAttr _ attr _ = attr++fontFamilyParser :: CssUpdater+fontFamilyParser attr (lst:_) = attr & fontFamily .~ fontNames+ where+ fontNames = Last . Just $ T.unpack <$> extractString lst++ extractString [] = []+ extractString (CssIdent n:rest) = n : extractString rest+ extractString (CssString n:rest) = n : extractString rest+ extractString (_:rest) = extractString rest+fontFamilyParser attr _ = attr+++cssUniqueTexture :: ASetter DrawAttributes DrawAttributes+ a (Last Texture)+ -> CssUpdater+cssUniqueTexture setter attr ((CssIdent "none":_):_) =+ attr & setter .~ Last (Just FillNone)+cssUniqueTexture setter attr ((CssColor c:_):_) =+ attr & setter .~ Last (Just $ ColorRef c)+cssUniqueTexture setter attr ((CssFunction "url" [CssReference c]:_):_) =+ attr & setter .~ Last (Just . TextureRef $ T.unpack c)+cssUniqueTexture _ attr _ = attr++cssElementRefSetter :: Lens' DrawAttributes (Last ElementRef)+ -> CssUpdater+cssElementRefSetter setter attr ((CssFunction "url" [CssReference c]:_):_) =+ attr & setter .~ Last (Just . Ref $ T.unpack c)+cssElementRefSetter setter attr ((CssIdent "none":_):_) =+ attr & setter .~ Last (Just RefNone)+cssElementRefSetter _ attr _ = attr++cssMayStringSetter :: ASetter DrawAttributes DrawAttributes a (Maybe String)+ -> CssUpdater+cssMayStringSetter setter attr ((CssIdent i:_):_) =+ attr & setter .~ Just (T.unpack i)+cssMayStringSetter setter attr ((CssString i:_):_) =+ attr & setter .~ Just (T.unpack i)+cssMayStringSetter _ attr _ = attr++cssNullSetter :: CssUpdater+cssNullSetter attr _ = attr++cssDashArray :: ASetter DrawAttributes DrawAttributes a (Last [Number])+ -> CssUpdater+cssDashArray setter attr (lst:_) =+ case [n | CssNumber n <- lst ] of+ [] -> attr+ v -> attr & setter .~ Last (Just v)+cssDashArray _ attr _ = attr+++drawAttributesList :: [(SvgAttributeLens DrawAttributes, CssUpdater)]+drawAttributesList =+ [("stroke-width" `parseIn` strokeWidth, cssUniqueNumber strokeWidth)+ ,("stroke" `parseIn` strokeColor, cssUniqueTexture strokeColor)+ ,("fill" `parseIn` fillColor, cssUniqueTexture fillColor)+ ,("stroke-linecap" `parseIn` strokeLineCap, cssIdentAttr strokeLineCap)+ ,("stroke-linejoin" `parseIn` strokeLineJoin, cssIdentAttr strokeLineJoin)+ ,("stroke-miterlimit" `parseIn` strokeMiterLimit,+ cssUniqueMayFloat strokeMiterLimit)++ ,("transform" `parseIn` transform, const)+ ,(opacitySetter "opacity" groupOpacity, cssUniqueFloat groupOpacity)+ ,(opacitySetter "fill-opacity" fillOpacity, cssUniqueFloat fillOpacity)+ ,(opacitySetter "stroke-opacity" strokeOpacity, cssUniqueFloat strokeOpacity)+ ,("font-size" `parseIn` fontSize, cssUniqueNumber fontSize)+ ,(parserLastSetter "font-family" fontFamily (Just . commaSeparate)+ (Just . intercalate ", "), fontFamilyParser)++ ,("fill-rule" `parseIn` fillRule, cssIdentAttr fillRule)+ ,("clip-rule" `parseIn` clipRule, cssIdentAttr clipRule)+ ,("mask" `parseIn` maskRef, cssElementRefSetter maskRef)+ ,(classSetter, cssNullSetter) -- can't set class in CSS+ ,("id" `parseIn` attrId, cssMayStringSetter attrId)+ ,("stroke-dashoffset" `parseIn` strokeOffset,+ cssUniqueNumber strokeOffset)+ ,("stroke-dasharray" `parseIn` strokeDashArray, cssDashArray strokeDashArray)+ ,("text-anchor" `parseIn` textAnchor, cssIdentAttr textAnchor)+ ,("clip-path" `parseIn` clipPathRef, cssElementRefSetter clipPathRef)+ ,("marker-end" `parseIn` markerEnd, cssElementRefSetter markerEnd)+ ,("marker-start" `parseIn` markerStart, cssElementRefSetter markerStart)+ ,("marker-mid" `parseIn` markerMid, cssElementRefSetter markerMid)+ ]+ where+ commaSeparate =+ fmap (T.unpack . T.strip) . T.split (',' ==) . T.pack++serializeDashArray :: [Number] -> String+serializeDashArray =+ intercalate ", " . fmap serializeNumber++instance XMLUpdatable DrawAttributes where+ xmlTagName _ = "DRAWATTRIBUTES"+ attributes = styleAttribute : fmap fst drawAttributesList+ serializeTreeNode = genericSerializeNode++styleAttribute :: SvgAttributeLens DrawAttributes+styleAttribute = SvgAttributeLens+ { _attributeName = "style"+ , _attributeUpdater = updater+ , _attributeSerializer = const Nothing+ }+ where+ updater attrs style = case parse styleString style of+ Nothing -> attrs+ Just decls -> foldl' applyer attrs decls++ cssUpdaters = [(T.pack $ _attributeName n, u) | (n, u) <- drawAttributesList]+ applyer value (CssDeclaration txt elems) =+ case lookup txt cssUpdaters of+ Nothing -> value+ Just f -> f value elems++instance XMLUpdatable Rectangle where+ xmlTagName _ = "rect"+ serializeTreeNode = genericSerializeWithDrawAttr+ attributes =+ ["width" `parseIn` rectWidth+ ,"height" `parseIn` rectHeight+ ,"x" `parseIn` (rectUpperLeftCorner._1)+ ,"y" `parseIn` (rectUpperLeftCorner._2)+ ,"rx" `parseIn` (rectCornerRadius._1)+ ,"ry" `parseIn` (rectCornerRadius._2)+ ]++instance XMLUpdatable Image where+ xmlTagName _ = "image"+ serializeTreeNode = genericSerializeWithDrawAttr+ attributes =+ ["width" `parseIn` imageWidth+ ,"height" `parseIn` imageHeight+ ,"x" `parseIn` (imageCornerUpperLeft._1)+ ,"y" `parseIn` (imageCornerUpperLeft._2)+ ,parserSetter "href" imageHref (Just . dropSharp) Just+ ,"preserveAspectRatio" `parseIn` imageAspectRatio+ ]++instance XMLUpdatable Line where+ xmlTagName _ = "line"+ serializeTreeNode = genericSerializeWithDrawAttr+ attributes =+ ["x1" `parseIn` (linePoint1._1)+ ,"y1" `parseIn` (linePoint1._2)+ ,"x2" `parseIn` (linePoint2._1)+ ,"y2" `parseIn` (linePoint2._2)+ ]++instance XMLUpdatable Ellipse where+ xmlTagName _ = "ellipse"+ serializeTreeNode = genericSerializeWithDrawAttr+ attributes =+ ["cx" `parseIn` (ellipseCenter._1)+ ,"cy" `parseIn` (ellipseCenter._2)+ ,"rx" `parseIn` ellipseXRadius+ ,"ry" `parseIn` ellipseYRadius+ ]++instance XMLUpdatable Circle where+ xmlTagName _ = "circle"+ serializeTreeNode = genericSerializeWithDrawAttr+ attributes =+ ["cx" `parseIn` (circleCenter._1)+ ,"cy" `parseIn` (circleCenter._2)+ ,"r" `parseIn` circleRadius+ ]++instance XMLUpdatable Mask where+ xmlTagName _ = "mask"+ serializeTreeNode node =+ updateWithAccessor _maskContent node $+ genericSerializeWithDrawAttr node++ attributes =+ ["x" `parseIn` (maskPosition._1)+ ,"y" `parseIn` (maskPosition._2)+ ,"width" `parseIn` maskWidth+ ,"height" `parseIn` maskHeight+ ,"maskContentUnits" `parseIn` maskContentUnits+ ,"maskUnits" `parseIn` maskUnits+ ]++instance XMLUpdatable ClipPath where+ xmlTagName _ = "clipPath"+ serializeTreeNode node =+ updateWithAccessor _clipPathContent node $+ genericSerializeWithDrawAttr node+ attributes =+ ["clipPathUnits" `parseIn` clipPathUnits]++instance XMLUpdatable Polygon where+ xmlTagName _ = "polygon"+ serializeTreeNode = genericSerializeWithDrawAttr+ attributes = ["points" `parseIn` polygonPoints]++instance XMLUpdatable PolyLine where+ xmlTagName _ = "polyline"+ serializeTreeNode = genericSerializeWithDrawAttr+ attributes = ["points" `parseIn` polyLinePoints]++instance XMLUpdatable Path where+ xmlTagName _ = "path"+ serializeTreeNode = genericSerializeWithDrawAttr+ attributes = ["d" `parseIn` pathDefinition]++instance XMLUpdatable MeshGradientPatch where+ xmlTagName _ = "meshpatch"+ attributes = []+ serializeTreeNode node =+ updateWithAccessor _meshGradientPatchStops node $ genericSerializeNode node++instance XMLUpdatable MeshGradientRow where+ xmlTagName _ = "meshrow"+ serializeTreeNode node =+ updateWithAccessor _meshGradientRowPatches node $ genericSerializeNode node+ attributes = []++instance XMLUpdatable MeshGradient where+ xmlTagName _ = "meshgradient"+ serializeTreeNode node =+ updateWithAccessor _meshGradientRows node $ genericSerializeWithDrawAttr node+ attributes =+ ["x" `parseIn` meshGradientX+ ,"y" `parseIn` meshGradientY+ ,"type" `parseIn` meshGradientType+ ,"gradientUnits" `parseIn` meshGradientUnits+ ,"gradientTransform" `parseIn` meshGradientTransform+ ]+++instance XMLUpdatable LinearGradient where+ xmlTagName _ = "linearGradient"+ serializeTreeNode node =+ updateWithAccessor _linearGradientStops node $ genericSerializeNode node++ attributes =+ ["gradientTransform" `parseIn` linearGradientTransform+ ,"gradientUnits" `parseIn` linearGradientUnits+ ,"spreadMethod" `parseIn` linearGradientSpread+ ,"x1" `parseIn` (linearGradientStart._1)+ ,"y1" `parseIn` (linearGradientStart._2)+ ,"x2" `parseIn` (linearGradientStop._1)+ ,"y2" `parseIn` (linearGradientStop._2)+ ]++instance XMLUpdatable Tree where+ xmlTagName _ = "TREE"+ attributes = []+ serializeTreeNode e = case e of+ None -> Nothing+ UseTree u _ -> serializeTreeNode u+ GroupTree g -> serializeTreeNode g+ SymbolTree s -> serializeTreeNode s+ PathTree p -> serializeTreeNode p+ CircleTree c -> serializeTreeNode c+ PolyLineTree p -> serializeTreeNode p+ PolygonTree p -> serializeTreeNode p+ EllipseTree el -> serializeTreeNode el+ LineTree l -> serializeTreeNode l+ RectangleTree r -> serializeTreeNode r+ TextTree Nothing t -> serializeTreeNode t+ ImageTree i -> serializeTreeNode i+ MeshGradientTree m -> serializeTreeNode m+ TextTree (Just p) t -> do+ textNode <- serializeTreeNode t+ pathNode <- serializeTreeNode p+ let sub = [X.Elem . setChildren pathNode $ X.elContent textNode]+ return $ setChildren textNode sub+++isNotNone :: Tree -> Bool+isNotNone None = False+isNotNone _ = True++instance XMLUpdatable (Group Tree) where+ xmlTagName _ = "g"+ serializeTreeNode node =+ updateWithAccessor (filter isNotNone . _groupChildren) node $+ genericSerializeWithDrawAttr node+ attributes = []++instance XMLUpdatable (Symbol Tree) where+ xmlTagName _ = "symbol"+ serializeTreeNode node =+ updateWithAccessor (filter isNotNone . _groupChildren . _groupOfSymbol) node $+ genericSerializeWithDrawAttr node+ attributes =+ ["viewBox" `parseIn` (groupOfSymbol . groupViewBox)+ ,"preserveAspectRatio" `parseIn` (groupOfSymbol . groupAspectRatio)+ ]+++instance XMLUpdatable RadialGradient where+ xmlTagName _ = "radialGradient"+ serializeTreeNode node =+ updateWithAccessor _radialGradientStops node $ genericSerializeNode node+ attributes =+ ["gradientTransform" `parseIn` radialGradientTransform+ ,"gradientUnits" `parseIn` radialGradientUnits+ ,"spreadMethod" `parseIn` radialGradientSpread+ ,"cx" `parseIn` (radialGradientCenter._1)+ ,"cy" `parseIn` (radialGradientCenter._2)+ ,"r" `parseIn` radialGradientRadius+ ,"fx" `parseIn` radialGradientFocusX+ ,"fy" `parseIn` radialGradientFocusY+ ]++instance XMLUpdatable Use where+ xmlTagName _ = "use"+ serializeTreeNode = genericSerializeWithDrawAttr+ attributes =+ ["x" `parseIn` (useBase._1)+ ,"y" `parseIn` (useBase._2)+ ,"width" `parseIn` useWidth+ ,"height" `parseIn` useHeight+ ,parserSetter "href" useName (Just . dropSharp) (Just . ('#':))+ ]++dropSharp :: String -> String+dropSharp ('#':rest) = rest+dropSharp a = a++instance XMLUpdatable TextInfo where+ xmlTagName _ = "tspan"+ serializeTreeNode = genericSerializeNode+ attributes =+ [parserSetter "x" textInfoX (parse dashArray) dashNotEmpty+ ,parserSetter "y" textInfoY (parse dashArray) dashNotEmpty+ ,parserSetter "dx" textInfoDX (parse dashArray) dashNotEmpty+ ,parserSetter "dy" textInfoDY (parse dashArray) dashNotEmpty+ ,parserSetter "rotate" textInfoRotate+ (parse numberList)+ rotateNotEmpty+ ,"textLength" `parseIn` textInfoLength+ ]+ where+ dashNotEmpty [] = Nothing+ dashNotEmpty lst = Just $ serializeDashArray lst++ rotateNotEmpty [] = Nothing+ rotateNotEmpty lst =+ Just . unwords $ printf "%g" <$> lst+++instance XMLUpdatable TextPath where+ xmlTagName _ = "textPath"+ serializeTreeNode = genericSerializeNode+ attributes =+ ["startOffset" `parseIn` textPathStartOffset+ ,"method" `parseIn` textPathMethod+ ,"spacing" `parseIn` textPathSpacing+ ,parserSetter "href" textPathName (Just . dropSharp) (Just . ('#':))+ ]++instance XMLUpdatable Text where+ xmlTagName _ = "text"+ serializeTreeNode = serializeText+ attributes = ["lengthAdjust" `parseIn` textAdjust]+++instance XMLUpdatable Pattern where+ xmlTagName _ = "pattern"+ serializeTreeNode node =+ updateWithAccessor _patternElements node $ genericSerializeWithDrawAttr node+ attributes =+ ["viewBox" `parseIn` patternViewBox+ ,"patternUnits" `parseIn` patternUnit+ ,"width" `parseIn` patternWidth+ ,"height" `parseIn` patternHeight+ ,"x" `parseIn` (patternPos._1)+ ,"y" `parseIn` (patternPos._2)+ ,"preserveAspectRatio" `parseIn` patternAspectRatio+ ]++instance XMLUpdatable Marker where+ xmlTagName _ = "marker"+ serializeTreeNode node =+ updateWithAccessor _markerElements node $ genericSerializeWithDrawAttr node+ attributes =+ ["refX" `parseIn` (markerRefPoint._1)+ ,"refY" `parseIn` (markerRefPoint._2)+ ,"markerWidth" `parseIn` markerWidth+ ,"markerHeight" `parseIn` markerHeight+ ,"patternUnits" `parseIn` markerUnits+ ,"orient" `parseIn` markerOrient+ ,"viewBox" `parseIn` markerViewBox+ ,"overflow" `parseIn` markerOverflow+ ,"preserveAspectRatio" `parseIn` markerAspectRatio+ ]++serializeText :: Text -> Maybe X.Element+serializeText topText = namedNode where+ namedNode = fmap (\x -> x { X.elName = X.unqual "text" }) topNode+ topNode = serializeSpan $ _textRoot topText++ serializeSpan tspan = case (info, drawInfo) of+ (Nothing, Nothing) -> Nothing+ (Just a, Nothing) -> Just $ setChildren a subContent+ (Nothing, Just b) -> Just $ setChildren b subContent+ (Just a, Just b) -> + Just $ setChildren (mergeAttributes a b) subContent+ where+ info = genericSerializeNode $ _spanInfo tspan+ drawInfo = genericSerializeNode $ _spanDrawAttributes tspan+ subContent = catMaybes $ serializeContent <$> _spanContent tspan++ serializeContent (SpanText t) = Just . X.Text $ X.blank_cdata { X.cdData = T.unpack t }+ serializeContent (SpanTextRef _t) = Just . X.Text $ X.blank_cdata { X.cdData = "" }+ serializeContent (SpanSub sub) = X.Elem <$> serializeSpan sub++unparseText :: [X.Content] -> ([TextSpanContent], Maybe TextPath)+unparseText = extractResult . go True+ where+ extractResult (a, b, _) = (a, b)++ go startStrip [] = ([], Nothing, startStrip)+ go startStrip (X.CRef _:rest) = go startStrip rest+ go startStrip (X.Elem e@(nodeName -> "tspan"):rest) =+ (SpanSub spans : trest, mpath, retStrip)+ where+ (trest, mpath, retStrip) = go restStrip rest+ (sub, _, restStrip) = go startStrip $ X.elContent e+ spans = TextSpan (xmlUnparse e) (xmlUnparse e) sub++ go startStrip (X.Elem e@(nodeName -> "tref"):rest) =+ case attributeFinder "href" e of+ Nothing -> go startStrip rest+ Just v -> (SpanTextRef v : trest, mpath, stripRet)+ where (trest, mpath, stripRet) = go startStrip rest++ go startStrip (X.Elem e@(nodeName -> "textPath"):rest) =+ case attributeFinder "href" e of+ Nothing -> go startStrip rest+ Just v -> (tsub ++ trest, pure p, retStrp)+ where+ p = (xmlUnparse e) { _textPathName = dropSharp v }+ (trest, _, retStrp) = go restStrip rest+ (tsub, _, restStrip) = go startStrip $ X.elContent e++ go startStrip (X.Elem _:rest) = go startStrip rest+ go startStrip (X.Text t:rest)+ | T.length cleanText == 0 = go startStrip rest+ | otherwise =+ (SpanText cleanText : trest, mpath, stripRet)+ where+ (trest, mpath, stripRet) = go subShouldStrip rest++ subShouldStrip = T.pack " " `T.isSuffixOf` cleanText++ space = T.singleton ' '+ singulariseSpaces tt+ | space `T.isPrefixOf` tt = space+ | otherwise = tt++ stripStart | startStrip = T.stripStart+ | otherwise = id++ cleanText = stripStart+ . T.concat+ . fmap singulariseSpaces+ . T.groupBy (\a b -> (a /= ' ' && b /= ' ') || a == b)+ . T.filter (\c -> c /= '\n' && c /= '\r')+ . T.map (\c -> if c == '\t' then ' ' else c)+ . T.pack+ $ X.cdData t++gradientOffsetSetter :: SvgAttributeLens GradientStop+gradientOffsetSetter = SvgAttributeLens "offset" setter serialize+ where+ serialize a = Just $ printf "%d%%" percentage+ where percentage = floor . (100 *) $ a ^. gradientOffset :: Int++ setter el str = el & gradientOffset .~ val+ where+ val = realToFrac $ case parseMayStartDot complexNumber str of+ Nothing -> 0+ Just (Num n) -> n+ Just (Px n) -> n+ Just (Percent n) -> n+ Just (Em n) -> n+ Just (Pc n) -> n+ Just (Mm n) -> n+ Just (Cm n) -> n+ Just (Point n) -> n+ Just (Inches n) -> n++instance XMLUpdatable GradientStop where+ xmlTagName _ = "stop"+ serializeTreeNode = genericSerializeNode+ attributes =+ [gradientOffsetSetter+ ,opacitySetter "stop-opacity" gradientOpacity+ ,"stop-color" `parseIn` gradientColor+ ,"path" `parseIn` gradientPath+ ]+++data Symbols = Symbols+ { symbols :: !(M.Map String Element)+ , cssStyle :: [CssRule]+ }++emptyState :: Symbols+emptyState = Symbols mempty mempty++parseGradientStops :: X.Element -> [GradientStop]+parseGradientStops = concatMap unStop . elChildren+ where+ unStop e@(nodeName -> "stop") = [xmlUnparse e]+ unStop _ = []++parseMeshGradientPatches :: X.Element -> [MeshGradientPatch]+parseMeshGradientPatches = foldMap unparsePatch . elChildren where+ unparsePatch e@(nodeName -> "meshpatch") = [MeshGradientPatch $ parseGradientStops e]+ unparsePatch _ = []++parseMeshGradientRows :: X.Element -> [MeshGradientRow]+parseMeshGradientRows = foldMap unRows . elChildren where+ unRows e@(nodeName -> "meshrow") = [MeshGradientRow $ parseMeshGradientPatches e]+ unRows _ = []++withId :: X.Element -> (X.Element -> Element)+ -> State Symbols Tree+withId el f = case attributeFinder "id" el of+ Nothing -> return None+ Just elemId -> do+ modify $ \s ->+ s { symbols = M.insert elemId (f el) $ symbols s }+ return None++unparseDefs :: X.Element -> State Symbols Tree+unparseDefs e@(nodeName -> "pattern") = do+ subElements <- mapM unparse $ elChildren e+ withId e . const . ElementPattern $ pat { _patternElements = subElements}+ where+ pat = xmlUnparse e+unparseDefs e@(nodeName -> "marker") = do+ subElements <- mapM unparse $ elChildren e+ withId e . const . ElementMarker $ mark {_markerElements = subElements }+ where+ mark = xmlUnparseWithDrawAttr e+unparseDefs e@(nodeName -> "mask") = do+ children <- mapM unparse $ elChildren e+ let realChildren = filter isNotNone children+ parsedMask = xmlUnparseWithDrawAttr e+ withId e . const . ElementMask $ parsedMask { _maskContent = realChildren }++unparseDefs e@(nodeName -> "clipPath") = do+ children <- mapM unparse $ elChildren e+ let realChildren = filter isNotNone children+ parsedClip = xmlUnparseWithDrawAttr e+ withId e . const . ElementClipPath $ parsedClip { _clipPathContent = realChildren }++unparseDefs e@(nodeName -> "linearGradient") =+ withId e $ ElementLinearGradient . unparser+ where+ unparser ee =+ xmlUnparse ee & linearGradientStops .~ parseGradientStops ee++unparseDefs e@(nodeName -> "meshgradient") =+ withId e $ ElementMeshGradient . unparser+ where+ unparser ee =+ xmlUnparseWithDrawAttr ee & meshGradientRows .~ parseMeshGradientRows ee++unparseDefs e@(nodeName -> "radialGradient") =+ withId e $ ElementRadialGradient . unparser+ where+ unparser ee =+ xmlUnparse ee & radialGradientStops .~ parseGradientStops ee+unparseDefs e = do+ el <- unparse e+ withId e (const $ ElementGeometry el)++unparse :: X.Element -> State Symbols Tree+unparse e@(nodeName -> "style") = do+ case parseOnly (many1 ruleSet) . T.pack $ strContent e of+ Left _ -> return ()+ Right rules ->+ modify $ \s -> s { cssStyle = cssStyle s ++ rules }+ return None+unparse e@(nodeName -> "defs") = do+ mapM_ unparseDefs $ elChildren e+ return None+unparse e@(nodeName -> "symbol") = do+ symbolChildren <- mapM unparse $ elChildren e+ let realChildren = filter isNotNone symbolChildren+ pure . SymbolTree . Symbol $ groupNode & groupChildren .~ realChildren+ where+ groupNode :: Group Tree+ groupNode = _groupOfSymbol $ xmlUnparseWithDrawAttr e++unparse e@(nodeName -> "g") = do+ children <- mapM unparse $ elChildren e+ let realChildren = filter isNotNone children++ groupNode :: Group Tree+ groupNode = xmlUnparseWithDrawAttr e++ pure $ GroupTree $ groupNode & groupChildren .~ realChildren++unparse e@(nodeName -> "text") = do+ pathWithGeometry <- pathGeomtryOf tPath+ pure . TextTree pathWithGeometry $ xmlUnparse e & textRoot .~ root+ where+ (textContent, tPath) = unparseText $ X.elContent e++ pathGeomtryOf Nothing = pure Nothing+ pathGeomtryOf (Just pathInfo) = do+ pathElem <- gets $ M.lookup (_textPathName pathInfo) . symbols+ case pathElem of+ Nothing -> pure Nothing+ Just (ElementLinearGradient _) -> pure Nothing+ Just (ElementRadialGradient _) -> pure Nothing+ Just (ElementMeshGradient _) -> pure Nothing+ Just (ElementPattern _) -> pure Nothing+ Just (ElementMask _) -> pure Nothing+ Just (ElementClipPath _) -> pure Nothing+ Just (ElementMarker _) -> pure Nothing+ Just (ElementGeometry (PathTree p)) ->+ pure . Just $ pathInfo { _textPathData = _pathDefinition p }+ Just (ElementGeometry _) -> pure Nothing++ root = TextSpan+ { _spanInfo = xmlUnparse e+ , _spanDrawAttributes = xmlUnparse e+ , _spanContent = textContent+ }++unparse e = pure $ case nodeName e of+ "image" -> ImageTree parsed+ "ellipse" -> EllipseTree parsed+ "rect" -> RectangleTree parsed+ "polyline" -> PolyLineTree parsed+ "polygon" -> PolygonTree parsed+ "circle"-> CircleTree parsed+ "line" -> LineTree parsed+ "path" -> PathTree parsed+ "meshgradient" ->+ MeshGradientTree $ parsed & meshGradientRows .~ parseMeshGradientRows e+ "use" -> UseTree parsed Nothing+ _ -> None+ where+ parsed :: (XMLUpdatable a, WithDrawAttributes a) => a+ parsed = xmlUnparseWithDrawAttr e++unparseDocument :: FilePath -> X.Element -> Maybe Document+unparseDocument rootLocation e@(nodeName -> "svg") = Just Document+ { _viewBox =+ attributeFinder "viewBox" e >>= parse viewBoxParser+ , _elements = parsedElements+ , _width = lengthFind "width"+ , _height = lengthFind "height"+ , _definitions = symbols named+ , _description = ""+ , _styleRules = cssStyle named+ , _documentLocation = rootLocation+ }+ where+ (parsedElements, named) =+ runState (mapM unparse $ elChildren e) emptyState+ lengthFind n =+ attributeFinder n e >>= parse complexNumber+unparseDocument _ _ = Nothing++-- | Transform a SVG document to a XML node.+xmlOfDocument :: Document -> X.Element+xmlOfDocument doc =+ X.node (X.unqual "svg") (attrs, descTag ++ styleTag ++ defsTag ++ children)+ where+ attr name = X.Attr (X.unqual name)+ children = catMaybes [serializeTreeNode el | el <- _elements doc]++ defsTag | null defs = []+ | otherwise = [X.node (X.unqual "defs") defs]++ defs = catMaybes [elementRender k e | (k, e) <- M.assocs $ _definitions doc]++ elementRender k e = case e of+ ElementGeometry t -> serialize t+ ElementMarker m -> serialize m+ ElementMask m -> serialize m+ ElementClipPath c -> serialize c+ ElementPattern p -> serialize p+ ElementLinearGradient lg -> addId $ serializeTreeNode lg+ ElementRadialGradient rg -> addId $ serializeTreeNode rg+ ElementMeshGradient mg -> addId $ serializeTreeNode mg+ where+ addId = fmap (X.add_attr $ attr "id" k)++ serialize :: (WithDrawAttributes e, XMLUpdatable e) => e -> Maybe X.Element+ serialize el = case el^.drawAttr.attrId of+ Nothing -> addId $ serializeTreeNode el+ Just _id ->+ let newNode = el & drawAttr.attrId .~ Just k in+ serializeTreeNode newNode++ docViewBox = case _viewBox doc of+ Nothing -> []+ Just b -> [attr "viewBox" $ serializeViewBox b]++ descTag = case _description doc of+ "" -> []+ txt -> [X.node (X.unqual "desc") txt]++ styleTag = case _styleRules doc of+ [] -> []+ rules -> [X.node (X.unqual "style")+ ([attr "type" "text/css"], txt)]+ where txt = TL.unpack . TB.toLazyText $ foldMap tserialize rules++ attrs =+ docViewBox +++ [attr "xmlns" "http://www.w3.org/2000/svg"+ ,attr "xmlns:xlink" "http://www.w3.org/1999/xlink"+ ,attr "version" "1.1"] +++ catMaybes [attr "width" . serializeNumber <$> _width doc+ ,attr "height" . serializeNumber <$> _height doc+ ]+
svg-tree.cabal view
@@ -1,61 +1,61 @@-name: svg-tree -version: 0.5.1.2 -synopsis: SVG file loader and serializer -description: - svg-tree provides types representing a SVG document, - and allows to load and save it. - . - The types definition are aimed at rendering, - so they are rather comple. For simpler SVG document building, - look after `lucid-svg`. - . - To render an svg document you can use the `rasterific-svg` package - - -license: BSD3 -author: Vincent Berthoux -maintainer: Vincent Berthoux --- copyright: -category: Graphics, Svg -build-type: Simple -cabal-version: >=1.10 - -extra-source-files: changelog.md - -Source-Repository head - Type: git - Location: git://github.com/Twinside/svg-tree.git - -Source-Repository this - Type: git - Location: git://github.com/Twinside/svg-tree.git - Tag: v0.5.1.2 - -library - hs-source-dirs: src - Ghc-options: -O3 -Wall - default-language: Haskell2010 - exposed-modules: Graphics.Svg - , Graphics.Svg.CssTypes - , Graphics.Svg.Types - - other-modules: Graphics.Svg.NamedColors - , Graphics.Svg.XmlParser - , Graphics.Svg.PathParser - , Graphics.Svg.CssParser - , Graphics.Svg.ColorParser - - build-depends: base >= 4.5 && < 5 - , JuicyPixels >= 3.2 - , attoparsec >= 0.12 - , scientific >= 0.3 - , containers >= 0.4 - , xml >= 1.3 - , bytestring >= 0.10 - , linear >= 1.20 - , vector >= 0.10 - , text >= 1.1 - , transformers >= 0.3 && < 0.6 - , mtl >= 2.1 && < 2.3 - , lens >= 4.6 && < 5 - +name: svg-tree+version: 0.6+synopsis: SVG file loader and serializer+description:+ svg-tree provides types representing a SVG document,+ and allows to load and save it.+ .+ The types definition are aimed at rendering,+ so they are rather comple. For simpler SVG document building,+ look after `lucid-svg`.+ .+ To render an svg document you can use the `rasterific-svg` package+++license: BSD3+author: Vincent Berthoux+maintainer: Vincent Berthoux+-- copyright:+category: Graphics, Svg+build-type: Simple+cabal-version: >=1.10++extra-source-files: changelog.md++Source-Repository head+ Type: git+ Location: git://github.com/Twinside/svg-tree.git++Source-Repository this+ Type: git+ Location: git://github.com/Twinside/svg-tree.git+ Tag: v0.6++library+ hs-source-dirs: src+ Ghc-options: -O3 -Wall+ default-language: Haskell2010+ exposed-modules: Graphics.Svg+ , Graphics.Svg.CssTypes+ , Graphics.Svg.Types++ other-modules: Graphics.Svg.NamedColors+ , Graphics.Svg.XmlParser+ , Graphics.Svg.PathParser+ , Graphics.Svg.CssParser+ , Graphics.Svg.ColorParser++ build-depends: base >= 4.5 && < 5+ , JuicyPixels >= 3.2+ , attoparsec >= 0.12+ , scientific >= 0.3+ , containers >= 0.4+ , xml >= 1.3+ , bytestring >= 0.10+ , linear >= 1.20+ , vector >= 0.10+ , text >= 1.1+ , transformers >= 0.3 && < 0.6+ , mtl >= 2.1 && < 2.3+ , lens >= 4.6 && < 5+