Created
March 9, 2018 06:36
-
-
Save mindon/f6ba1924aa9c20c023024c6201892a4a to your computer and use it in GitHub Desktop.
Convert simple ai Illustrator file to svg format
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| // CONVERT simple .ai (adobe illustrator) file to svg format | |
| // http://www.fileformat.info/format/ai/egff.htm | |
| // | |
| package main | |
| import ( | |
| "fmt" | |
| "io/ioutil" | |
| "math" | |
| "os" | |
| "regexp" | |
| "strconv" | |
| "strings" | |
| ) | |
| func main() { | |
| fpath := "demo.ai" | |
| if len(os.Args) > 1 { | |
| fpath = os.Args[1] | |
| } | |
| body, err := ioutil.ReadFile(fpath) | |
| if err != nil { | |
| panic(err) | |
| } | |
| lines := strings.Split(string(body), "\r\n") | |
| beginLine := "%%EndSetup" | |
| beginSize := len(beginLine) | |
| endSignal := "%%" | |
| boundingBox := "%%BoundingBox: " | |
| isBegin := false | |
| state := AIState{} | |
| viewSize := "" | |
| translate := "" | |
| result := "" | |
| taskNames := []string{"move", "lineto", "curveto", "cmyk_color", "gray_color", "end_path"} | |
| for _, line := range lines { | |
| if !isBegin || len(line) == 0 { | |
| if len(line) == beginSize && line == beginLine { | |
| isBegin = true | |
| } else if strings.HasPrefix(line, boundingBox) { | |
| d := strings.Split(line[len(boundingBox):], " ") | |
| if len(d) >= 4 { | |
| x0, _ := strconv.ParseFloat(d[0], 64) | |
| y0, _ := strconv.ParseFloat(d[1], 64) | |
| x1, _ := strconv.ParseFloat(d[2], 64) | |
| y1, _ := strconv.ParseFloat(d[3], 64) | |
| translate = fmt.Sprintf(` transform="scale(1, -1) translate(%.3f,%.3f)"`, -x0, -y1) | |
| viewSize = fmt.Sprintf(` width="%d" height="%d"`, int(x1-x0), int(y1-y0)) | |
| } | |
| } | |
| continue | |
| } | |
| if strings.HasPrefix(line, endSignal) { | |
| break | |
| } | |
| for _, name := range taskNames { | |
| task, ok := tasks[name] | |
| if !ok { | |
| continue | |
| } | |
| m := task.FindStringSubmatch(line) | |
| if len(m) == 0 { | |
| continue | |
| } | |
| if name == "move" { | |
| state.Path += fmt.Sprintf(" M %s,%s", m[1], m[2]) | |
| x, _ := strconv.ParseFloat(m[1], 64) | |
| y, _ := strconv.ParseFloat(m[2], 64) | |
| state.Cpx = x | |
| state.Cpy = y | |
| } else if name == "lineto" { | |
| state.Path += fmt.Sprintf(" L %s,%s", m[1], m[2]) | |
| x, _ := strconv.ParseFloat(m[1], 64) | |
| y, _ := strconv.ParseFloat(m[2], 64) | |
| state.Cpx = x | |
| state.Cpy = y | |
| } else if name == "curveto" { | |
| d := []float64{} | |
| for _, s := range m[1:] { | |
| v, _ := strconv.ParseFloat(s, 64) | |
| d = append(d, v) | |
| } | |
| state.Path += fmt.Sprintf(" C %f,%f %f,%f %f,%f ", d[0], d[1], d[2], d[3], d[4], d[5]) | |
| state.Cpx = d[len(d)-2] | |
| state.Cpx = d[len(d)-1] | |
| } else if name == "cmyk_color" { | |
| _c, _ := strconv.ParseFloat(m[1], 64) | |
| _y, _ := strconv.ParseFloat(m[2], 64) | |
| _m, _ := strconv.ParseFloat(m[3], 64) | |
| _k, _ := strconv.ParseFloat(m[4], 64) | |
| cssHex := CMYK2HEX(_c, _y, _m, _k) | |
| if m[5] == "K" { | |
| state.StrokeColor = cssHex | |
| } else { | |
| state.FillColor = cssHex | |
| } | |
| } else if name == "gray_color" { | |
| v, _ := strconv.ParseFloat(m[1], 64) | |
| cssHex := CMYK2HEX(0, 0, 0, 1-v) | |
| if m[2] == "G" { | |
| state.StrokeColor = cssHex | |
| } else { | |
| state.FillColor = cssHex | |
| } | |
| } else if name == "end_path" { | |
| _closed := false | |
| _stroke := "" | |
| _fill := "" | |
| key := m[1] | |
| if len(state.StrokeColor) == 0 { | |
| state.StrokeColor = "#000000" | |
| } | |
| if len(state.FillColor) == 0 { | |
| state.FillColor = "#ff0000" | |
| } | |
| if key == "S" || key == "B" { | |
| _stroke = fmt.Sprintf("stroke:%s", state.StrokeColor) | |
| } else if key == "s" || key == "b" { | |
| _stroke = fmt.Sprintf("stroke:%s", state.StrokeColor) | |
| _closed = true | |
| } | |
| if key == "F" || key == "B" { | |
| _fill = fmt.Sprintf("fill:%s", state.FillColor) | |
| } else if key == "f" || key == "b" { | |
| _fill = fmt.Sprintf("fill:%s", state.StrokeColor) | |
| _closed = true | |
| } | |
| if _closed { | |
| state.Path += " z " | |
| } | |
| result += fmt.Sprintf(`<g style="%s;%s"><path d="%s" /></g>`+"\n", _stroke, _fill, state.Path) | |
| state.Path = "" | |
| } else { | |
| fmt.Println("[IGNORE]", line) | |
| } | |
| break | |
| } | |
| } | |
| result = `<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink"` + viewSize + ">\n <g" + translate + ">\n" + result + "\n </g>\n" + `</svg>` | |
| output := strings.Replace(fpath, ".ai", ".svg", -1) | |
| ioutil.WriteFile(output, []byte(result), os.ModeAppend) | |
| fmt.Printf(`"%s" is converted as "%s"`+"\n", fpath, output) | |
| } | |
| func CMYK2HEX(c, m, y, k float64) string { | |
| r := int(255.0 * math.Max(1-(k+c), 0)) | |
| g := int(255.0 * math.Max(1-(k+m), 0)) | |
| b := int(255.0 * math.Max(1-(k+y), 0)) | |
| return fmt.Sprintf("%02x%02x%02x", r, g, b) | |
| } | |
| type AIState struct { | |
| FillColor string `json:"fill_color,omitempty"` | |
| StrokeColor string `json:"stroke_color,omitempty"` | |
| Path string `json:"path,omitempty"` | |
| Cpx float64 `json:"cpx,omitempty"` | |
| Cpy float64 `json:"cpy,omitempty"` | |
| } | |
| var tasks = map[string]*regexp.Regexp{ | |
| "move": regexp.MustCompile(`\s*(-?[\d\.]+) +(-?[\d\.]+) [mM]\s*$`), | |
| "lineto": regexp.MustCompile(`^\s*(-?[\d\.]+) +(-?[\d\.]+) [lL]\s*$`), | |
| "curveto": regexp.MustCompile(`^\s*(-?[\d\.]+) +(-?[\d\.]+) +(-?[\d\.]+) +(-?[\d\.]+) +(-?[\d\.]+) +(-?[\d\.]+) [cC]\s*$`), | |
| "cmyk_color": regexp.MustCompile(`^\s*(-?[\d\.]+) +(-?[\d\.]+) +(-?[\d\.]+) +(-?[\d\.]+) +([kK])\s*$`), | |
| "gray_color": regexp.MustCompile(`^\s*([\d\.]+) +([gG])\s*$`), | |
| "end_path": regexp.MustCompile(`^\s*([nNBbfFsS]+)\s*$`), | |
| } |
Author
@codingdudecom it would be helpful if you provide a demo .ai file
sure, here you go: https://file.io/4Zg1cqeQCRSm
Author
sure, here you go: https://file.io/4Zg1cqeQCRSm
the .ai file you provided should be a pdf file indeed, this article provides some details on this
https://www.datalogics.com/blog/pdf-tips/illustrator-and-pdf-compatibility/
It's generated if saving with the "PDF compatible mode" option.
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Hi, thanks for sharing this. Do you know if this works on newer versions of Illustrator? I'm getting an empty SVG, only the viewbox info seems to be corrrect.
The AI file header contains the text PDF1.5, does this mean that they've changed the format?