commit e061b14eecec721cdc4a06225e4ea01d36cc2aa1 Author: Marcus Noble Date: Tue Dec 8 19:18:28 2020 +0000 Initial commit diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 0000000..506bfdf --- /dev/null +++ b/Dockerfile @@ -0,0 +1,10 @@ +FROM jess/inkscape + +RUN apt-get update && apt-get install -y python-lxml python-numpy + +WORKDIR /app +RUN apt-get update && apt-get install -y golang +ADD main.go . +RUN go build -o main main.go + +ENTRYPOINT ["/app/main"] diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..2955198 --- /dev/null +++ b/LICENSE @@ -0,0 +1,19 @@ +MIT License Copyright (c) 2020 - present Marcus Noble + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is furnished +to do so, subject to the following conditions: + +The above copyright notice and this permission notice (including the next +paragraph) shall be included in all copies or substantial portions of the +Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS +FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS +OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF +OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..65df7cc --- /dev/null +++ b/Makefile @@ -0,0 +1,61 @@ +.DEFAULT_GOAL := default + +IMAGE ?= docker.cluster.fun/averagemarcus/svg-to-dxf:latest + +.PHONY: test # Run all tests, linting and format checks +test: lint check-format run-tests + +.PHONY: lint # Perform lint checks against code +lint: + @echo "⚠️ 'lint' unimplemented" + +.PHONY: check-format # Checks code formatting and returns a non-zero exit code if formatting errors found +check-format: + @echo "⚠️ 'check-format' unimplemented" + +.PHONY: format # Performs automatic format fixes on all code +format: + @echo "⚠️ 'format' unimplemented" + +.PHONY: run-tests # Runs all tests +run-tests: + @echo "⚠️ 'run-tests' unimplemented" + +.PHONY: fetch-deps # Fetch all project dependencies +fetch-deps: + @echo "⚠️ 'fetch-deps' unimplemented" + +.PHONY: build # Build the project +build: lint check-format fetch-deps + @echo "⚠️ 'build' unimplemented" + +.PHONY: docker-build # Build the docker image +docker-build: + @docker build build --tag $(IMAGE) . + +.PHONY: docker-publish # Push the docker image to the remote registry +docker-publish: + @docker push $(IMAGE) + +.PHONY: run # Run the application +run: + @echo "⚠️ 'run' unimplemented" + +.PHONY: ci # Perform CI specific tasks to perform on a pull request +ci: + @echo "⚠️ 'ci' unimplemented" + +.PHONY: release # Release the latest version of the application +release: + @echo "⚠️ 'release' unimplemented" + +.PHONY: help # Show this list of commands +help: + @echo "svg-to-dxf" + @echo "Usage: make [target]" + @echo "" + @echo "target description" | expand -t20 + @echo "-----------------------------------" + @grep '^.PHONY: .* #' Makefile | sed 's/\.PHONY: \(.*\) # \(.*\)/\1 \2/' | expand -t20 + +default: test build diff --git a/README.md b/README.md new file mode 100644 index 0000000..7b16edb --- /dev/null +++ b/README.md @@ -0,0 +1,37 @@ +# svg-to-dxf + +Convert .svg files to .dxf + +## Features + +Runs a webserver that takes in an `?url=` query string, fetches the SVG from that URL and then returns it as a .dxf + +## Usage + +```sh +docker run -it --rm -p 8080:8080 docker.cluster.fun/averagemarcus/svg-to-dxf +``` + +## Building from source + +With Docker: + +```sh +make docker-build +``` + +## Resources + +* [inkscape](https://inkscape.org/) + +## Contributing + +If you find a bug or have an idea for a new feature please raise an issue to discuss it. + +Pull requests are welcomed but please try and follow similar code style as the rest of the project and ensure all tests and code checkers are passing. + +Thank you 💛 + +## License + +See [LICENSE](LICENSE) diff --git a/main.go b/main.go new file mode 100644 index 0000000..2ac8abf --- /dev/null +++ b/main.go @@ -0,0 +1,92 @@ +package main + +import ( + "errors" + "fmt" + "io" + "math/rand" + "net/http" + "os" + "os/exec" +) + +var port = os.Getenv("PORT") + +func main() { + if port == "" { + port = "8080" + } + + http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) { + svgURL := r.URL.Query().Get("url") + if svgURL == "" { + return + } + fmt.Println("Fetching " + svgURL) + + filename := "/app/" + randomString(7) + ".svg" + + fmt.Println("Filename: " + filename) + if err := downloadFile(svgURL, filename); err != nil { + fmt.Fprintf(w, err.Error()) + return + } + fmt.Println("Downloaded SVG") + + dxf, err := convertToDXF(filename) + if err != nil { + fmt.Fprintf(w, err.Error()) + return + } + fmt.Println("Converted to DXF") + + w.Header().Add("Content-Type", "application/octet-stream") + w.Header().Add("Content-Length", fmt.Sprint(len(dxf))) + w.Header().Add("Content-Disposition", "inline; filename=out.dxf") + fmt.Fprintf(w, string(dxf)) + + os.Remove(filename) + }) + + fmt.Println(http.ListenAndServe(":"+port, nil)) +} + +func downloadFile(URL, fileName string) error { + response, err := http.Get(URL) + if err != nil { + return err + } + defer response.Body.Close() + + if response.StatusCode != 200 { + return errors.New("Received non 200 response code") + } + + file, err := os.Create(fileName) + if err != nil { + return err + } + defer file.Close() + + _, err = io.Copy(file, response.Body) + if err != nil { + return err + } + + return nil +} + +func convertToDXF(filename string) ([]byte, error) { + cmd := exec.Command("python", "/usr/share/inkscape/extensions/dxf_outlines.py", filename) + return cmd.Output() +} + +func randomString(n int) string { + var letters = []rune("abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789") + + s := make([]rune, n) + for i := range s { + s[i] = letters[rand.Intn(len(letters))] + } + return string(s) +}