Parcourir la source

запилил генератор файла-архива и генерацию xml из модели документа
получившийся документ успешно открылся в libreoffice

kpmy il y a 10 ans
Parent
commit
69382ff4fa

+ 1 - 0
.gitignore

@@ -22,3 +22,4 @@ _testmain.go
 *.exe
 *.test
 *.prof
+*.odf

+ 71 - 1
generators/gen.go

@@ -1,11 +1,81 @@
 package generators
 
 import (
+	"archive/zip"
+	"bytes"
+	"encoding/xml"
 	"io"
 	"odf/model"
 	"odf/xmlns"
+	"odf/xmlns/office"
+	"odf/xmlns/urn"
+	"ypk/assert"
+	"ypk/halt"
 )
 
-func Generate(m model.Model, out io.Writer, mimetype xmlns.Mime) {
+type Parts map[string]*bytes.Buffer
+
+type Entry struct {
+	MediaType string `xml:"manifest:media-type,attr"`
+	FullPath  string `xml:"manifest:full-path,attr"`
+}
+
+type Manifest struct {
+	XMLName xml.Name
+	NS      string  `xml:"xmlns:manifest,attr"`
+	Entries []Entry `xml:"manifest:file-entry"`
+}
 
+func (m *Manifest) init() {
+	m.XMLName.Local = "manifest:manifest"
+	m.NS = urn.Manifest
+}
+
+func docParts(m model.Model) (ret Parts) {
+	ret = make(map[string]*bytes.Buffer)
+	rd := m.NewReader()
+	rd.Pos(m.Root())
+	for !rd.Eol() {
+		l := rd.Read()
+		buf := new(bytes.Buffer)
+		buf.WriteString(xml.Header)
+		switch l.Name() {
+		case office.DocumentContent:
+			ret[xmlns.Content] = buf
+		case office.DocumentStyles:
+			ret[xmlns.Styles] = buf
+		case office.DocumentMeta:
+			ret[xmlns.Meta] = buf
+		default:
+			halt.As(100, l.Name())
+		}
+		enc := xml.NewEncoder(buf)
+		enc.Encode(l)
+	}
+	return
+}
+
+func Generate(m model.Model, out io.Writer, mimetype xmlns.Mime) {
+	z := zip.NewWriter(out)
+	mime := &zip.FileHeader{Name: xmlns.Mimetype, Method: zip.Store}
+	if w, err := z.CreateHeader(mime); err == nil {
+		bytes.NewBufferString(string(mimetype)).WriteTo(w)
+	}
+	manifest := &Manifest{}
+	manifest.init()
+	manifest.Entries = append(manifest.Entries, Entry{MediaType: string(mimetype), FullPath: "/"})
+	for k, v := range docParts(m) {
+		if w, err := z.Create(k); err == nil {
+			v.WriteTo(w)
+			manifest.Entries = append(manifest.Entries, Entry{MediaType: xmlns.MimeDefault, FullPath: k})
+		}
+	}
+	//place for attachements
+	if w, err := z.Create(xmlns.Manifest); err == nil {
+		w.Write([]byte(xml.Header))
+		enc := xml.NewEncoder(w)
+		err = enc.Encode(manifest)
+		assert.For(err == nil, 60, err)
+	}
+	z.Close()
 }

+ 1 - 0
mappers/fmt.go

@@ -30,6 +30,7 @@ func (f *Formatter) Init() {
 	assert.For(f.MimeType != "", 20)
 	wr := f.m.NewWriter()
 	wr.Pos(f.m.Root())
+	wr.Write(New(office.DocumentMeta))
 	f.attr.Init(f.m)
 	wr.WritePos(New(office.DocumentContent))
 	wr.Attr(office.Version, "1.0")

+ 10 - 0
model/stub/simple_attrs.go

@@ -1,5 +1,9 @@
 package stub
 
+import (
+	"encoding/xml"
+)
+
 type StringAttr struct {
 	Value string
 }
@@ -7,3 +11,9 @@ type StringAttr struct {
 func (a *StringAttr) String() string {
 	return a.Value
 }
+
+func (a *StringAttr) MarshalXMLAttr(name xml.Name) (xa xml.Attr, err error) {
+	xa.Name = name
+	xa.Value = a.Value
+	return
+}

+ 65 - 4
model/stub/simple_nodes.go

@@ -1,17 +1,78 @@
 package stub
 
 import (
+	"encoding/xml"
 	"odf/model"
+	"odf/xmlns"
+	"odf/xmlns/office"
+	"odf/xmlns/urn"
 	"ypk/assert"
 )
 
+type root struct {
+	inner *sn
+}
+
+func (r *root) Name() model.LeafName {
+	return r.inner.Name()
+}
+
+func (r *root) Attr(n model.AttrName, a ...model.Attribute) model.Attribute {
+	return r.inner.Attr(n, a...)
+}
+
+func (r *root) Child(i int) model.Leaf {
+	return r.inner.Child(i)
+}
+
+func (r *root) IndexOf(l model.Leaf) int {
+	return r.inner.IndexOf(l)
+}
+
+func (r *root) NofChild() int {
+	return r.inner.NofChild()
+}
+
+func (r *root) MarshalXML(e *xml.Encoder, start xml.StartElement) (err error) {
+	start.Name.Local = string(r.inner.Name())
+
+	start.Attr = append(start.Attr, xml.Attr{Name: xml.Name{Local: xmlns.NSanim}, Value: urn.Anim})
+	start.Attr = append(start.Attr, xml.Attr{Name: xml.Name{Local: xmlns.NSchart}, Value: urn.Chart})
+	start.Attr = append(start.Attr, xml.Attr{Name: xml.Name{Local: xmlns.NSconfig}, Value: urn.Config})
+	start.Attr = append(start.Attr, xml.Attr{Name: xml.Name{Local: xmlns.NSdc}, Value: urn.Dc})
+	start.Attr = append(start.Attr, xml.Attr{Name: xml.Name{Local: xmlns.NSdr3d}, Value: urn.Dr3d})
+	start.Attr = append(start.Attr, xml.Attr{Name: xml.Name{Local: xmlns.NSdraw}, Value: urn.Draw})
+	start.Attr = append(start.Attr, xml.Attr{Name: xml.Name{Local: xmlns.NSfo}, Value: urn.Fo})
+	start.Attr = append(start.Attr, xml.Attr{Name: xml.Name{Local: xmlns.NSform}, Value: urn.Form})
+	start.Attr = append(start.Attr, xml.Attr{Name: xml.Name{Local: xmlns.NSmath}, Value: urn.Math})
+	start.Attr = append(start.Attr, xml.Attr{Name: xml.Name{Local: xmlns.NSmeta}, Value: urn.Meta})
+	start.Attr = append(start.Attr, xml.Attr{Name: xml.Name{Local: xmlns.NSnumber}, Value: urn.Number})
+	start.Attr = append(start.Attr, xml.Attr{Name: xml.Name{Local: xmlns.NSoffice}, Value: urn.Office})
+	start.Attr = append(start.Attr, xml.Attr{Name: xml.Name{Local: xmlns.NSpresentation}, Value: urn.Presentation})
+	start.Attr = append(start.Attr, xml.Attr{Name: xml.Name{Local: xmlns.NSscript}, Value: urn.Script})
+	start.Attr = append(start.Attr, xml.Attr{Name: xml.Name{Local: xmlns.NSsmil}, Value: urn.Smil})
+	start.Attr = append(start.Attr, xml.Attr{Name: xml.Name{Local: xmlns.NSstyle}, Value: urn.Style})
+	start.Attr = append(start.Attr, xml.Attr{Name: xml.Name{Local: xmlns.NSsvg}, Value: urn.Svg})
+	start.Attr = append(start.Attr, xml.Attr{Name: xml.Name{Local: xmlns.NStable}, Value: urn.Table})
+	start.Attr = append(start.Attr, xml.Attr{Name: xml.Name{Local: xmlns.NStext}, Value: urn.Text})
+	start.Attr = append(start.Attr, xml.Attr{Name: xml.Name{Local: xmlns.NSxforms}, Value: urn.Xforms})
+	start.Attr = append(start.Attr, xml.Attr{Name: xml.Name{Local: xmlns.NSxlink}, Value: urn.Xlink})
+	err = e.EncodeElement(r.inner, start)
+	return err
+}
+
 func lf() func(x model.LeafName) model.Leaf {
-	return func(x model.LeafName) model.Leaf {
-		var ret *sn
+	return func(x model.LeafName) (ret model.Leaf) {
 		switch x {
+		case office.DocumentContent, office.DocumentMeta, office.DocumentStyles:
+			r := &root{}
+			r.inner = &sn{name: x}
+			r.inner.init()
+			ret = r
 		default:
-			ret = &sn{name: x}
-			ret.init()
+			r := &sn{name: x}
+			r.init()
+			ret = r
 		}
 		assert.For(ret != nil, 60)
 		return ret

+ 53 - 2
model/stub/simple_riders.go

@@ -8,6 +8,54 @@ import (
 	"ypk/halt"
 )
 
+type sr struct {
+	base *sm
+	pos  model.Leaf
+	eol  bool
+	this model.Leaf
+}
+
+func (r *sr) Base() model.Model {
+	return r.base
+}
+
+func (r *sr) InitFrom(old model.Reader) {
+	panic(126)
+}
+
+func (r *sr) Pos(p ...model.Leaf) model.Leaf {
+	if len(p) == 1 {
+		r.pos = p[0]
+		if n, ok := r.pos.(model.Node); ok {
+			r.eol = n.NofChild() == 0
+		} else {
+			r.eol = true
+		}
+	}
+	return r.pos
+}
+
+func (r *sr) Read() model.Leaf {
+	assert.For(r.pos != nil && !r.eol, 20)
+	n, ok := r.pos.(model.Node)
+	assert.For(ok, 21)
+	idx := 0
+	if r.this != nil {
+		idx = n.IndexOf(r.this)
+		idx++
+	}
+	if idx < n.NofChild() {
+		r.this = n.Child(idx)
+	} else {
+		r.eol = true
+	}
+	return r.this
+}
+
+func (r *sr) Eol() bool {
+	return r.eol
+}
+
 type sw struct {
 	base *sm
 	pos  model.Leaf
@@ -32,9 +80,12 @@ func (w *sw) Write(l model.Leaf) {
 	assert.For(l != nil, 20)
 	assert.For(w.pos != nil, 21)
 	if _n, ok := w.pos.(model.Node); ok {
-		if n, da := _n.(*sn); da {
+		switch n := _n.(type) {
+		case *sn:
 			n.children = append(n.children, l)
-		} else {
+		case *root:
+			n.inner.children = append(n.inner.children, l)
+		default:
 			halt.As(100, reflect.TypeOf(n))
 		}
 	}

+ 26 - 2
model/stub/simple_tree.go

@@ -5,6 +5,8 @@ package stub
 */
 
 import (
+	"encoding/xml"
+	"fmt"
 	"odf/model"
 	"ypk/assert"
 )
@@ -52,12 +54,34 @@ func (n *sn) init() {
 	n.children = make([]model.Leaf, 0)
 }
 
+func (n *sn) MarshalXML(e *xml.Encoder, start xml.StartElement) (err error) {
+	fmt.Println(n.name)
+	start.Name.Local = string(n.name)
+	for k, v := range n.attr {
+		a, err := v.(xml.MarshalerAttr).MarshalXMLAttr(xml.Name{Local: string(k)})
+		assert.For(err == nil, 30, err)
+		start.Attr = append(start.Attr, a)
+	}
+	e.EncodeToken(start)
+	for _, v := range n.children {
+		err = e.EncodeElement(v, xml.StartElement{Name: xml.Name{Local: string(v.Name())}})
+		assert.For(err == nil, 30, err)
+	}
+	err = e.EncodeToken(start.End())
+	assert.For(err == nil, 30, err)
+	return err
+}
+
 type sm struct {
 	root *sn
 }
 
-func (m *sm) NewReader(...model.Reader) model.Reader {
-	return nil
+func (m *sm) NewReader(old ...model.Reader) model.Reader {
+	r := &sr{base: m, eol: true}
+	if len(old) == 1 {
+		r.InitFrom(old[0])
+	}
+	return r
 }
 
 func (m *sm) NewWriter(old ...model.Writer) model.Writer {

+ 3 - 0
model/tree.go

@@ -25,6 +25,9 @@ type Model interface {
 type Reader interface {
 	InitFrom(Reader)
 	Base() Model
+	Read() Leaf
+	Eol() bool
+	Pos(...Leaf) Leaf
 }
 
 type Writer interface {

+ 1 - 1
odf_test.go

@@ -30,7 +30,7 @@ func TestMappers(t *testing.T) {
 }
 
 func TestGenerators(t *testing.T) {
-	output, _ := os.OpenFile("test0.odt", os.O_CREATE|os.O_WRONLY, 0666)
+	output, _ := os.OpenFile("test0.odf", os.O_CREATE|os.O_WRONLY, 0666)
 	m := model.ModelFactory()
 	fm := &mappers.Formatter{}
 	fm.ConnectTo(m)

+ 1 - 1
xmlns/office/office.go

@@ -9,7 +9,7 @@ const (
 	AutomaticStyles model.LeafName = "office:automatic-styles"
 	MasterStyles    model.LeafName = "office:master-styles"
 	FontFaceDecls   model.LeafName = "office:font-face-decls"
-
+	DocumentMeta    model.LeafName = "office:document-meta"
 	DocumentContent model.LeafName = "office:document-content"
 	Body            model.LeafName = "office:body"
 	Text            model.LeafName = "office:text"

+ 26 - 0
xmlns/urn/u.go

@@ -0,0 +1,26 @@
+package urn
+
+const (
+	Office       = "urn:oasis:names:tc:opendocument:xmlns:office:1.0"
+	Meta         = "urn:oasis:names:tc:opendocument:xmlns:meta:1.0"
+	Config       = "urn:oasis:names:tc:opendocument:xmlns:config:1.0"
+	Text         = "urn:oasis:names:tc:opendocument:xmlns:text:1.0"
+	Table        = "urn:oasis:names:tc:opendocument:xmlns:table:1.0"
+	Draw         = "urn:oasis:names:tc:opendocument:xmlns:drawing:1.0"
+	Presentation = "urn:oasis:names:tc:opendocument:xmlns:presentation:1.0"
+	Dr3d         = "urn:oasis:names:tc:opendocument:xmlns:dr3d:1.0"
+	Chart        = "urn:oasis:names:tc:opendocument:xmlns:chart:1.0"
+	Form         = "urn:oasis:names:tc:opendocument:xmlns:form:1.0"
+	Script       = "urn:oasis:names:tc:opendocument:xmlns:script:1.0"
+	Style        = "urn:oasis:names:tc:opendocument:xmlns:style:1.0"
+	Number       = "urn:oasis:names:tc:opendocument:xmlns:datastyle:1.0"
+	Anim         = "urn:oasis:names:tc:opendocument:xmlns:animation:1.0"
+	Dc           = "http://purl.org/dc/elements/1.1/"
+	Xlink        = "http://www.w3.org/1999/xlink"
+	Math         = "http://www.w3.org/1998/Math/MathML"
+	Xforms       = "http://www.w3.org/2002/xforms"
+	Fo           = "urn:oasis:names:tc:opendocument:xmlns:xsl-fo-compatible:1.0"
+	Svg          = "urn:oasis:names:tc:opendocument:xmlns:svg-compatible:1.0"
+	Smil         = "urn:oasis:names:tc:opendocument:xmlns:smil-compatible:1.0"
+	Manifest     = "urn:oasis:names:tc:opendocument:xmlns:manifest:1.0"
+)

+ 35 - 1
xmlns/x.go

@@ -4,6 +4,39 @@ import (
 	"odf/model"
 )
 
+const (
+	Mimetype = "mimetype"
+	Manifest = "META-INF/manifest.xml"
+	Content  = "content.xml"
+	Styles   = "styles.xml"
+	Meta     = "meta.xml"
+)
+
+const (
+	NSoffice       = "xmlns:office"
+	NSmeta         = "xmlns:meta"
+	NSconfig       = "xmlns:config"
+	NStext         = "xmlns:text"
+	NStable        = "xmlns:table"
+	NSdraw         = "xmlns:draw"
+	NSpresentation = "xmlns:presentation"
+	NSdr3d         = "xmlns:dr3d"
+	NSchart        = "xmlns:chart"
+	NSform         = "xmlns:form"
+	NSscript       = "xmlns:script"
+	NSstyle        = "xmlns:style"
+	NSnumber       = "xmlns:number"
+	NSanim         = "xmlns:anim"
+	NSdc           = "xmlns:dc"
+	NSxlink        = "xmlns:xlink"
+	NSmath         = "xmlns:math"
+	NSxforms       = "xmlns:xforms"
+	NSfo           = "xmlns:fo"
+	NSsvg          = "xmlns:svg"
+	NSsmil         = "xmlns:smil"
+	NSmanifest     = "xmlns:manifest"
+)
+
 type AttrType int
 
 const (
@@ -14,7 +47,8 @@ const (
 type Mime string
 
 const (
-	MimeText Mime = "application/vnd.oasis.opendocument.text"
+	MimeDefault      = "text/xml"
+	MimeText    Mime = "application/vnd.oasis.opendocument.text"
 )
 
 var Typed map[model.AttrName]AttrType