length.go 2.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596
  1. package svg
  2. import "regexp"
  3. import "fmt"
  4. import "strconv"
  5. import "encoding/xml"
  6. type Unit string
  7. // Relative units are:
  8. // em: the 'font-size' of the relevant font
  9. // ex: the 'x-height' of the relevant font
  10. // px: pixels, relative to the viewing device
  11. // ebisvg uses exactly one ebiten pixel for px unit.
  12. // const UnitEx = Unit(ex)
  13. // As the CSS2 standard states "1px thus corresponds to about 0.28 mm"
  14. // This then means that there are 3.571428571428571 pixels per mm.
  15. // ebisvg uses this scale factor for the absolute SVG units.
  16. //
  17. const MM2PX = 3.571428571428571
  18. const IN2MM = 254
  19. const PT2IN = 1 / 72
  20. const PC2IN = 1 / 6
  21. const UnitNo = Unit("")
  22. const UnitPx = Unit("px")
  23. const UnitEm = Unit("em")
  24. const UnitEx = Unit("ex")
  25. const UnitIn = Unit("in")
  26. const UnitCm = Unit("cm")
  27. const UnitMm = Unit("mm")
  28. const UnitPt = Unit("pt")
  29. const UnitPc = Unit("pc")
  30. const UnitPe = Unit("%")
  31. type Length struct {
  32. Magnitude float32
  33. Unit
  34. }
  35. func (l Length) Pixels(parent ...float32) float32 {
  36. switch l.Unit {
  37. case UnitNo, UnitPx:
  38. return l.Magnitude
  39. case UnitEx, UnitEm, UnitPe:
  40. if len(parent) > 0 {
  41. return l.Magnitude * parent[0]
  42. } else {
  43. return l.Magnitude
  44. }
  45. case UnitPc:
  46. return l.Magnitude * PC2IN * IN2MM * MM2PX
  47. case UnitPt:
  48. return l.Magnitude * PT2IN * IN2MM * MM2PX
  49. case UnitMm:
  50. return l.Magnitude * MM2PX
  51. case UnitCm:
  52. return l.Magnitude * MM2PX * 100
  53. case UnitIn:
  54. return l.Magnitude * IN2MM * MM2PX
  55. default:
  56. return l.Magnitude
  57. }
  58. }
  59. var ParseLengthRe = regexp.MustCompile(`([0-9]*\.?[0-9]*)(em|ex|px|in|cm|mm|pt|pc|%)?`)
  60. func ParseLength(s string) (Length, error) {
  61. parts := ParseLengthRe.FindStringSubmatch(s)
  62. res := Length{}
  63. if len(parts) < 2 {
  64. return res, fmt.Errorf("empty length")
  65. }
  66. magnitude, err := strconv.ParseFloat(parts[1], 32)
  67. if err != nil {
  68. return res, err
  69. }
  70. res.Magnitude = float32(magnitude)
  71. if len(parts) > 2 {
  72. res.Unit = Unit(parts[2])
  73. }
  74. return res, nil
  75. }
  76. func (l *Length) UnmarshalXMLAttr(attr xml.Attr) error {
  77. length, err := ParseLength(attr.Value)
  78. if err != nil {
  79. return err
  80. }
  81. *l = length
  82. return nil
  83. }
  84. var _ xml.UnmarshalerAttr = &Length{}