package plotter import ( "codeisalie/gono/cfg" "encoding/xml" "fmt" "io/ioutil" "math" "os" "regexp" "strconv" "strings" ) // XMLSvg ... type XMLSvg struct { XMLName xml.Name `xml:"svg"` Width string `xml:"width,attr"` Height string `xml:"height,attr"` ViewBox string `xml:"viewBox,attr"` Paths []XMLPath `xml:"path"` Circles []XMLCircle `xml:"circle"` Texts []XMLFlowRoot `xml:"flowRoot"` } // XMLPath ... type XMLPath struct { XMLName xml.Name `xml:"path"` Style string `xml:"style,attr"` Data string `xml:"d,attr"` } // XMLCircle ... type XMLCircle struct { XMLName xml.Name `xml:"circle"` CenterX float64 `xml:"cx,attr"` CenterY float64 `xml:"cy,attr"` Radius float64 `xml:"r,attr"` } // XMLFlowRoot ... type XMLFlowRoot struct { XMLName xml.Name `xml:"flowRoot"` Rec XMLFlowRec `xml:"flowRegion>rect"` Text string `xml:"flowPara"` Transform string `xml:"transform,attr"` } type XMLFlowRec struct { XMLName xml.Name `xml:"rect"` X float64 `xml:"x,attr"` Y float64 `xml:"y,attr"` Width float64 `xml:"width,attr"` Height float64 `xml:"height,attr"` } // Svg ... type Svg struct { OriginX float64 OriginY float64 Width float64 Height float64 } // Point ... type Point struct { X float64 Y float64 } // OnePath ... type OnePath struct { // StartAt Point Center Point Type int List []*Point } // SvgParser struct type SvgParser struct { Root XMLSvg Svg Svg Font FontParser cfg cfg.Config Paths []OnePath } const ( ModeAbsolute int = 100 + iota ModeRelative TypePath int = 200 + iota TypeCircle TypeText ) func rads(deg float64) float64 { return deg * math.Pi / 180.0 } // Parse on SVG file func (s *SvgParser) Parse(svgPath string, cfg cfg.Config) { // Open our xmlFile xmlFile, err := os.Open(svgPath) // if we os.Open returns an error then handle it if err != nil { fmt.Println(err) } fmt.Println("Successfully Opened " + svgPath) // defer the closing of our xmlFile so that we can parse it later on defer xmlFile.Close() // read our opened xmlFile as a byte array. byteValue, _ := ioutil.ReadAll(xmlFile) // we unmarshal our byteArray which contains our // xmlFiles content into 'users' which we defined above xml.Unmarshal(byteValue, &s.Root) s.Font = FontParser{} s.Font.Parse("font-default.xml") s.cfg = cfg s.Svg = Svg{} reViewboxSrc := regexp.MustCompile("(.*) (.*) (.*) (.*)") reViewboxRes := reViewboxSrc.FindAllStringSubmatch(s.Root.ViewBox, -1) s.Svg.OriginX, _ = strconv.ParseFloat(reViewboxRes[0][1], 64) s.Svg.OriginY, _ = strconv.ParseFloat(reViewboxRes[0][2], 64) s.Svg.Width, _ = strconv.ParseFloat(reViewboxRes[0][3], 64) s.Svg.Height, _ = strconv.ParseFloat(reViewboxRes[0][4], 64) // fmt.Println(s.Root.Texts) s.ProcessPaths() s.ProcessCircles() s.ProcessTexts() for _, path := range s.Paths { for _, point := range path.List { point.X = point.X*s.cfg.DrawingArea.Width/s.Svg.Width + s.cfg.DrawingArea.TopLeft.X point.Y = -point.Y*s.cfg.DrawingArea.Height/s.Svg.Height + s.cfg.DrawingArea.BottomRight.Y path.Center.X += point.X path.Center.Y += point.Y } path.Center.X /= float64(len(path.List)) path.Center.Y /= float64(len(path.List)) } } func (s *SvgParser) ProcessTexts() { reTranslate := regexp.MustCompile(`translate\((.*),(.*)\)`) for _, text := range s.Root.Texts { msg := text.Text zoomH := text.Rec.Height / s.Font.Root.Height zoomW := zoomH * s.Font.Root.Width / s.Font.Root.Width origin := Point{ X: text.Rec.X, Y: s.Svg.Height - (text.Rec.Y - s.Font.Root.Height), } posX := 0.0 posY := 0.0 values := reTranslate.FindAllStringSubmatch(text.Transform, -1) if values != nil { tPoint := Point{} tPoint.X, _ = strconv.ParseFloat(values[0][1], 64) tPoint.Y, _ = strconv.ParseFloat(values[0][2], 64) origin.X += tPoint.X origin.Y += tPoint.Y } // fmt.Println("ORIGIN", origin) for _, char := range msg { seq, ok := s.Font.ByLetter[char] if !ok { fmt.Println("Unknown char", char) seq = s.Font.ByLetter[' '] } for _, path := range seq { fmt.Println(path) onePath := OnePath{ Type: TypeText, } for _, pt := range path.List { point := Point{ X: pt.X*zoomW + posX + origin.X, Y: (s.Font.Root.Height-pt.Y)*zoomH + posY + origin.Y, } // fmt.Println("POINT", point) onePath.List = append(onePath.List, &point) } s.Paths = append(s.Paths, onePath) } posX += (s.Font.Root.Width + s.Font.Root.Space) * zoomW } } } func (s *SvgParser) ProcessCircles() { for _, circle := range s.Root.Circles { var angle float64 onePath := OnePath{} onePath.Type = TypeCircle segments := s.cfg.CircleResolution for angle = 360 / segments; angle <= 360; angle += 360 / segments { point := Point{} point.X = circle.CenterX + circle.Radius*math.Cos(rads(angle)) point.Y = s.Svg.Height - circle.CenterY + circle.Radius*math.Sin(rads(angle)) // s.Scale(&point) onePath.List = append(onePath.List, &point) } pointEnd := Point{ X: onePath.List[0].X, Y: onePath.List[0].Y, } onePath.List = append(onePath.List, &pointEnd) s.Paths = append(s.Paths, onePath) } } func (s *SvgParser) ProcessPaths() { rePosSrc := regexp.MustCompile("(.*),(.*)") for _, path := range s.Root.Paths { var mode int var size int onePath := OnePath{} onePath.Type = TypePath segs := strings.Split(path.Data, " ") for _, seg := range segs { switch { case seg == "M": // fmt.Println("Absolute") mode = ModeAbsolute case seg == "m": // fmt.Println("Relative") mode = ModeRelative case seg == "z": // fmt.Println("Zero") point := Point{ X: onePath.List[0].X, Y: onePath.List[0].Y, } onePath.List = append(onePath.List, &point) default: point := Point{} seqRes := rePosSrc.FindAllStringSubmatch(seg, -1) if seqRes == nil { fmt.Printf("Unknown seg: %v\n", seg) continue } point.X, _ = strconv.ParseFloat(seqRes[0][1], 64) point.Y, _ = strconv.ParseFloat(seqRes[0][2], 64) size = len(onePath.List) if mode == ModeRelative && size > 0 { prevPoint := onePath.List[size-1] // fmt.Println(prevPoint, point) point.X = point.X + prevPoint.X point.Y = prevPoint.Y - point.Y } else { point.Y = s.Svg.Height - point.Y } onePath.List = append(onePath.List, &point) } } s.Paths = append(s.Paths, onePath) } } func (s *SvgParser) ScalePath(path *OnePath) { // var point *Point // for idx := range path.List { // pX := (*path).List[idx] // pointCell := path.List[idx] // fmt.Printf(">%v %p\n", idx, point) // path.List[idx].X = path.List[idx].X*s.cfg.DrawingArea.Width/s.Svg.Width + s.cfg.DrawingArea.TopLeft.X // path.List[idx].Y = -path.List[idx].Y*s.cfg.DrawingArea.Height/s.Svg.Height + s.cfg.DrawingArea.BottomRight.Y // } } func (s *SvgParser) Scale(point *Point) { (*point).X = point.X*s.cfg.DrawingArea.Width/s.Svg.Width + s.cfg.DrawingArea.TopLeft.X (*point).Y = -point.Y*s.cfg.DrawingArea.Height/s.Svg.Height + s.cfg.DrawingArea.BottomRight.Y } func (path *OnePath) process(cfg cfg.Config) { }