Skip to content

Commit

Permalink
New release that parse freemind and xmind format
Browse files Browse the repository at this point in the history
  • Loading branch information
metal3d committed Jul 28, 2015
1 parent 1d403c4 commit 9bcded0
Show file tree
Hide file tree
Showing 5 changed files with 321 additions and 101 deletions.
12 changes: 11 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
@@ -1,9 +1,13 @@
# Vymad

Vymad is a markdown generator from "vym" file format. [Vym](http://www.insilmaril.de/vym/) (View Your Mind) is a very nice Mindmapping software for \*Nix environments. Writing a book, I was searching a way to easilly write content in Vym and to generate something that can be used by [Pandoc](http://pandoc.org/).
Vymad was originally a markdown generator from "vym" file format. It is now able to use Freemind and Xmind format.

[Vym](http://www.insilmaril.de/vym/) (View Your Mind) is a very nice Mindmapping software for \*Nix environments. Writing a book, I was searching a way to easilly write content in Vym and to generate something that can be used by [Pandoc](http://pandoc.org/).

I decided to build my own in Go and to share this little tool to make your life easier :)

**Note:** Freemind uses HTML to keep notes and I didn't find any solution to get plain text. You **must** have pandoc installed to let vymad tries to convert HTML to Markdown. Vym and Xmind are able to let notes to be "plain text" (especially Xmind which lets 2 blocks to get HTML and Plain text)

# Installation

## Built package
Expand Down Expand Up @@ -64,4 +68,10 @@ vymad myfile.vym | pandoc --toc --chapter -o book.pdf
```

# TODO

[] Add an option to tell vymad to get HTML instead of plain text to try to convert it to markdown
[] Find a way to fix Freemind HTML to markdown - be able to not force pandoc usage (eg. give a command used for convertion)
[] Add other Mindmap format if needed
[] Code rewrite to use interfaces and ease plugins developpements
[] Add option to run pandoc
97 changes: 97 additions & 0 deletions freemind/mm.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,97 @@
package freemind

import (
"bytes"
"encoding/xml"
"fmt"
"io/ioutil"
"os/exec"
"regexp"
"strings"
"text/template"
)

var PARTS = make([]string, 0)

type Map struct {
Node Node
}

type Node struct {
XMLName xml.Name `xml:"node"`
Text string `xml:"TEXT,attr"`
Content string `xml:"richcontent"`
Nodes []Node `xml:"node"`
}

func write(n *Node, level int) {

PARTS = append(PARTS, fmt.Sprintf("%s %s", strings.Repeat("#", level+1), n.Text))
if n.Content != "" {
cmd := exec.Command("/usr/bin/pandoc", "-f", "html", "-t", "markdown")

stdin, err := cmd.StdinPipe()
if err != nil {
panic(err)
}
stdout, err := cmd.StdoutPipe()
if err != nil {
panic(err)
}

if err := cmd.Start(); err != nil {
panic(err)
}

stdin.Write([]byte(n.Content))
stdin.Close()

if err != nil {
fmt.Println(err)
}
o, err := ioutil.ReadAll(stdout)
if err != nil {
panic(err)
}
PARTS = append(PARTS, string(o))
}

if len(n.Nodes) > 0 {
for _, n := range n.Nodes {
write(&n, level+1)
}
}

}

func Open(filename, tpl string) {

content, _ := ioutil.ReadFile(filename)

re := regexp.MustCompile(`<richcontent (.*)>`)

s := re.ReplaceAllString(string(content), "<richcontent><![CDATA[")
s = strings.Replace(s, "</richcontent>", "]]></richcontent>", -1)
content = []byte(s)

n := Map{}
if err := xml.Unmarshal(content, &n); err != nil {
fmt.Println(err)
}

title := n.Node.Text

for _, n := range n.Node.Nodes {
write(&n, 0)
}

t, _ := template.New("doc").Parse(tpl)
var b []byte
buff := bytes.NewBuffer(b)
t.Execute(buff, map[string]string{
"Title": title,
"Content": strings.Join(PARTS, "\n\n"),
})
fmt.Println(buff)

}
110 changes: 10 additions & 100 deletions main.go
Original file line number Diff line number Diff line change
@@ -1,16 +1,13 @@
package main

import (
"archive/zip"
"bytes"
"encoding/xml"
"flag"
"fmt"
"io"
"io/ioutil"
"os"
"strings"
"text/template"

"github.com/metal3d/vymad/freemind"
"github.com/metal3d/vymad/vym"
"github.com/metal3d/vymad/xmind"
)

// TPL is the main file content template.
Expand All @@ -23,83 +20,9 @@ const (
)

var (
// PARTS contains document lines.
PARTS = make([]string, 0)

VERSION = "master"
)

type VymMap struct {
MapCenter MapCenter `xml:"mapcenter"`
}

type MapCenter struct {
XMLName xml.Name `xml:"mapcenter"`
Heading string `xml:"heading"`
Branch []Branch `xml:"branch"`
}

type Branch struct {
XMLName xml.Name `xml:"branch"`
Heading string `xml:"heading"`
VymNote string `xml:"vymnote"`

Branches []Branch `xml:"branch"`
}

// build XML tree and return *VymMap.
func xmlBuildStruct(r io.ReadCloser) *VymMap {

defer r.Close()
c, err := ioutil.ReadAll(r)
if err != nil {
panic(err)
}
x := VymMap{}
xml.Unmarshal(c, &x)
return &x

}

// Parse branches.
func doParts(b *Branch, level int) {

heading := b.Heading
PARTS = append(PARTS, fmt.Sprintf("%s %s", strings.Repeat("#", level+1), heading))
PARTS = append(PARTS, b.VymNote)

// parse children branches
if len(b.Branches) > 0 {
for _, b := range b.Branches {
doParts(&b, level+1)
}
}

}

// parse the VymMap tree to build markdown.
func parse(v *VymMap) {

// parse each mapcenter branches
for _, b := range v.MapCenter.Branch {
doParts(&b, 0)
}

t, _ := template.New("doc").Parse(TPL)
var b []byte
buff := bytes.NewBuffer(b)

err := t.Execute(buff, map[string]string{
"Title": v.MapCenter.Heading,
"Content": strings.Join(PARTS, "\n\n"),
})
if err != nil {
panic(err)
}
fmt.Println(buff)

}

func main() {

v := flag.Bool("version", false, "print version")
Expand All @@ -117,31 +40,18 @@ func main() {
}

file := flag.Arg(0)
fmt.Println(file)
os.Exit(0)

if file == "" {
fmt.Println("You must provide a vym file")
os.Exit(1)
}

// try to open file
r, err := zip.OpenReader(os.Args[1])
if err != nil {
panic(err)
if file[len(file)-3:] == ".mm" { // Freemind
freemind.Open(file, TPL)
} else if file[len(file)-4:] == ".vym" { // Vym
vym.Open(file, TPL)
} else if file[len(file)-6:] == ".xmind" { //xmind
xmind.Open(file, TPL)
}
defer r.Close()

for _, f := range r.File {
if f.Name[len(f.Name)-4:] != ".xml" {
continue
}
rc, err := f.Open()
if err != nil {
panic(err)
}
x := xmlBuildStruct(rc)
parse(x)
break
}
}
109 changes: 109 additions & 0 deletions vym/vym.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,109 @@
package vym

import (
"archive/zip"
"bytes"
"encoding/xml"
"fmt"
"io"
"io/ioutil"
"strings"
"text/template"
)

// PARTS contains document lines.
var PARTS = make([]string, 0)

type VymMap struct {
MapCenter MapCenter `xml:"mapcenter"`
}

type MapCenter struct {
XMLName xml.Name `xml:"mapcenter"`
Heading string `xml:"heading"`
Branch []Branch `xml:"branch"`
}

type Branch struct {
XMLName xml.Name `xml:"branch"`
Heading string `xml:"heading"`
VymNote string `xml:"vymnote"`

Branches []Branch `xml:"branch"`
}

// build XML tree and return *VymMap.
func xmlBuildStruct(r io.ReadCloser) *VymMap {

defer r.Close()
c, err := ioutil.ReadAll(r)
if err != nil {
panic(err)
}
x := VymMap{}
xml.Unmarshal(c, &x)
return &x

}

// Parse branches.
func doParts(b *Branch, level int) {

heading := b.Heading
PARTS = append(PARTS, fmt.Sprintf("%s %s", strings.Repeat("#", level+1), heading))
PARTS = append(PARTS, b.VymNote)

// parse children branches
if len(b.Branches) > 0 {
for _, b := range b.Branches {
doParts(&b, level+1)
}
}

}

// parse the VymMap tree to build markdown.
func parseVymTree(v *VymMap, tpl string) {

// parse each mapcenter branches
for _, b := range v.MapCenter.Branch {
doParts(&b, 0)
}

t, _ := template.New("doc").Parse(tpl)
var b []byte
buff := bytes.NewBuffer(b)

err := t.Execute(buff, map[string]string{
"Title": v.MapCenter.Heading,
"Content": strings.Join(PARTS, "\n\n"),
})
if err != nil {
panic(err)
}
fmt.Println(buff)

}

func Open(filename string, tpl string) {
// try to open file
r, err := zip.OpenReader(filename)
if err != nil {
panic(err)
}
defer r.Close()

for _, f := range r.File {
if f.Name[len(f.Name)-4:] != ".xml" {
continue
}
rc, err := f.Open()
if err != nil {
panic(err)
}
x := xmlBuildStruct(rc)
parseVymTree(x, tpl)
break
}

}

0 comments on commit 9bcded0

Please sign in to comment.