github.com/boki/go-xmp@v1.0.1/xmp/marshal.go (about) 1 // Copyright (c) 2017-2018 Alexander Eichhorn 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"): you may 4 // not use this file except in compliance with the License. You may obtain 5 // a copy of the License at 6 // 7 // http://www.apache.org/licenses/LICENSE-2.0 8 // 9 // Unless required by applicable law or agreed to in writing, software 10 // distributed under the License is distributed on an "AS IS" BASIS, WITHOUT 11 // WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the 12 // License for the specific language governing permissions and limitations 13 // under the License. 14 15 package xmp 16 17 import ( 18 "bytes" 19 "encoding" 20 "encoding/base64" 21 "encoding/xml" 22 "errors" 23 "fmt" 24 "io" 25 "reflect" 26 "strconv" 27 ) 28 29 type Marshaler interface { 30 MarshalXMP(e *Encoder, n *Node, model Model) error 31 } 32 33 type MarshalerAttr interface { 34 MarshalXMPAttr(e *Encoder, name xml.Name, n *Node) (Attr, error) 35 } 36 37 const ( 38 Xpacket = 1 << iota 39 Xpadding 40 eMode = Xpacket | Xpadding 41 ) 42 43 type Encoder struct { 44 e *xml.Encoder 45 cw *countWriter 46 root *Node 47 version Version 48 nsTagMap map[string]string 49 intNsMap map[string]*Namespace 50 extNsMap map[string]*Namespace 51 flags int 52 } 53 54 var ErrOverflow = errors.New("xmp: document exceeds size limit") 55 56 type countWriter struct { 57 n int64 58 limit int64 59 w io.Writer 60 } 61 62 func min(a, b int64) int64 { 63 if a < b { 64 return a 65 } 66 return b 67 } 68 69 func newCountWriter(w io.Writer) *countWriter { 70 return &countWriter{w: w} 71 } 72 73 func (w *countWriter) Write(b []byte) (int, error) { 74 if w.limit > 0 { 75 if w.n >= w.limit { 76 return 0, ErrOverflow 77 } 78 bLen := int64(len(b)) 79 copied, err := io.CopyN(w.w, bytes.NewReader(b), min(w.limit-w.n, bLen)) 80 w.n += copied 81 if err != nil { 82 return int(copied), err 83 } 84 if copied < bLen { 85 return int(copied), ErrOverflow 86 } 87 return int(copied), nil 88 } else { 89 n, err := w.w.Write(b) 90 w.n += int64(n) 91 return n, err 92 } 93 } 94 95 func NewEncoder(w io.Writer) *Encoder { 96 cw := newCountWriter(w) 97 return &Encoder{ 98 e: xml.NewEncoder(cw), 99 cw: cw, 100 root: NewNode(xml.Name{}), 101 nsTagMap: make(map[string]string), 102 intNsMap: make(map[string]*Namespace), 103 extNsMap: make(map[string]*Namespace), 104 flags: Xpacket, 105 } 106 } 107 108 func (e *Encoder) SetMaxSize(size int64) { 109 e.cw.limit = size 110 } 111 112 func (e *Encoder) SetFlags(flags int) { 113 e.flags = flags & eMode 114 } 115 116 func (e *Encoder) Indent(prefix, indent string) { 117 e.e.Indent(prefix, indent) 118 } 119 120 func Marshal(d *Document) ([]byte, error) { 121 var b bytes.Buffer 122 if err := NewEncoder(&b).Encode(d); err != nil { 123 return nil, err 124 } 125 return b.Bytes(), nil 126 } 127 128 func MarshalIndent(d *Document, prefix, indent string) ([]byte, error) { 129 var b bytes.Buffer 130 enc := NewEncoder(&b) 131 enc.e.Indent(prefix, indent) 132 if err := enc.Encode(d); err != nil { 133 return nil, err 134 } 135 return b.Bytes(), nil 136 } 137 138 // encode to separate rdf:Description objects per namespace 139 func (e *Encoder) Encode(d *Document) error { 140 141 if d == nil { 142 return nil 143 } 144 145 // sync individual models to establish correct XMP entries 146 if err := d.syncToXMP(); err != nil { 147 return err 148 } 149 150 // reset nodes and map 151 e.root = NewNode(xml.Name{}) 152 defer e.root.Close() 153 e.nsTagMap = make(map[string]string) 154 e.intNsMap = d.intNsMap 155 e.extNsMap = d.extNsMap 156 157 // 1 build output node tree (model -> nodes+attr with one root node per 158 // XMP namespace) 159 for _, n := range d.nodes { 160 // 1.1 encode the model (Note: models typically use multiple XMP namespaces) 161 // so we generate wrapper nodes on the fly 162 if n.Model != nil { 163 if err := e.marshalValue(reflect.ValueOf(n.Model), nil, e.root, true); err != nil { 164 return err 165 } 166 } 167 168 // 1.2 merge external nodes (Note: all ext nodes collected under a 169 // document node belong to the same namespace) 170 ns := e.findNs(n.XMLName) 171 if ns == nil { 172 return fmt.Errorf("xmp: missing namespace for model node %s", n.XMLName.Local) 173 } 174 node := e.root.Nodes.FindNode(ns) 175 if node == nil { 176 node = NewNode(n.XMLName) 177 e.root.AddNode(node) 178 } 179 node.Nodes = append(node.Nodes, copyNodes(n.Nodes)...) 180 181 // 1.3 merge external attributes (Note: all ext attr collected under a 182 // document node belong to the same namespace) 183 node.Attr = append(node.Attr, n.Attr...) 184 } 185 186 // 2 collect root-node namespaces 187 for _, n := range e.root.Nodes { 188 l := make([]Attr, 0) 189 for _, ns := range n.Namespaces(d) { 190 l = append(l, ns.GetAttr()) 191 } 192 193 // add the about attr 194 about := aboutAttr 195 about.Value = d.about 196 l = append(l, about) 197 n.Attr = append(l, n.Attr...) 198 n.XMLName = rdfDescription 199 } 200 201 // 3 remove empty root nodes 202 nl := make(NodeList, 0) 203 for _, n := range e.root.Nodes { 204 if len(n.Attr) <= 2 && len(n.Nodes) == 0 { 205 n.Close() 206 continue 207 } 208 nl = append(nl, n) 209 } 210 e.root.Nodes = nl 211 212 // 4 output XML 213 214 // 4.1 write packet header 215 if e.flags&Xpacket > 0 { 216 if _, err := e.cw.Write(xmp_packet_header); err != nil { 217 return err 218 } 219 } 220 221 // 4.2 add top-level XMP namespace and toolkit as attributes 222 start := xml.StartElement{ 223 Name: xml.Name{Local: "x:xmpmeta"}, 224 Attr: make([]xml.Attr, 0), 225 } 226 start.Attr = append(start.Attr, xml.Attr{Name: xml.Name{Local: "xmlns:x"}, Value: "adobe:ns:meta/"}) 227 tk := d.toolkit 228 if tk == "" { 229 tk = XMP_TOOLKIT_VERSION 230 } 231 start.Attr = append(start.Attr, xml.Attr{Name: xml.Name{Local: "x:xmptk"}, Value: tk}) 232 if err := e.e.EncodeToken(start); err != nil { 233 return err 234 } 235 236 // 4.3 add document tree 237 e.root.XMLName = xml.Name{Local: "rdf:RDF"} 238 e.root.Attr = []Attr{nsRDF.GetAttr()} 239 if err := e.e.Encode(e.root); err != nil { 240 return err 241 } 242 243 if err := e.e.EncodeToken(start.End()); err != nil { 244 return err 245 } 246 if err := e.e.Flush(); err != nil { 247 return err 248 } 249 250 // 4.4 write footer including optional padding 251 if e.flags&Xpacket > 0 { 252 if e.flags&Xpadding > 0 && e.cw.limit > 0 { 253 pad := e.cw.limit - e.cw.n - 20 254 255 for i := int64(0); i < pad; i++ { 256 if i%80 == 0 { 257 if _, err := e.cw.Write([]byte("\n")); err != nil { 258 return err 259 } 260 } else { 261 if _, err := e.cw.Write([]byte(" ")); err != nil { 262 return err 263 } 264 } 265 } 266 } 267 268 if _, err := e.cw.Write(xmp_packet_footer); err != nil { 269 return err 270 } 271 } 272 273 return nil 274 } 275 276 func (e *Encoder) EncodeElement(v interface{}, node *Node) error { 277 return e.marshalValue(reflect.ValueOf(v), nil, node, false) 278 } 279 280 func (e *Encoder) marshalValue(val reflect.Value, finfo *fieldInfo, node *Node, withWrapper bool) error { 281 // name := "-" 282 // if finfo != nil { 283 // name = finfo.name 284 // } 285 // log.Debugf("xmp: marshalValue %v %v %s\n", val.Kind(), val.Type(), name) 286 287 if !val.IsValid() { 288 return nil 289 } 290 291 // check empty 292 if finfo != nil && finfo.flags&fEmpty == 0 && isEmptyValue(val) { 293 // log.Debugf("xmp: skipping empty value for %s\n", name) 294 return nil 295 } 296 297 // Drill into interfaces and pointers. 298 // This can turn into an infinite loop given a cyclic chain, 299 // but it matches the Go 1 behavior. 300 for val.Kind() == reflect.Interface || val.Kind() == reflect.Ptr { 301 if val.IsNil() { 302 // log.Debugf("xmp: skipping nil value for %s\n", name) 303 return nil 304 } 305 val = val.Elem() 306 } 307 308 kind := val.Kind() 309 typ := val.Type() 310 311 // call marshaler interface 312 if val.CanInterface() && (finfo != nil && finfo.flags&fMarshal > 0 || typ.Implements(marshalerType)) { 313 // log.Debugf("xmp: marshalValue calling MarshalXMP on %v for %s\n", val.Type(), name) 314 return val.Interface().(Marshaler).MarshalXMP(e, node, nil) 315 } 316 317 if val.CanAddr() { 318 pv := val.Addr() 319 if pv.CanInterface() && (finfo != nil && finfo.flags&fMarshal > 0 || pv.Type().Implements(marshalerType)) { 320 // log.Debugf("xmp: marshalValue calling MarshalXMP on %v for %s\n", val.Type(), name) 321 return pv.Interface().(Marshaler).MarshalXMP(e, node, nil) 322 } 323 } 324 325 // Check for text marshaler and marshal as node value 326 if val.CanInterface() && (finfo != nil && finfo.flags&fTextMarshal > 0 || typ.Implements(textMarshalerType)) { 327 // log.Debugf("xmp: marshalValue calling MarshalText on %v for %s\n", val.Type(), name) 328 b, err := val.Interface().(encoding.TextMarshaler).MarshalText() 329 if err != nil || b == nil { 330 return err 331 } 332 node.Value = string(b) 333 return nil 334 } 335 336 if val.CanAddr() { 337 pv := val.Addr() 338 if pv.CanInterface() && (finfo != nil && finfo.flags&fTextMarshal > 0 || pv.Type().Implements(textMarshalerType)) { 339 // log.Debugf("xmp: marshalValue calling MarshalText on %v for %s\n", val.Type(), name) 340 b, err := pv.Interface().(encoding.TextMarshaler).MarshalText() 341 if err != nil || b == nil { 342 return err 343 } 344 node.Value = string(b) 345 return nil 346 } 347 } 348 349 // XMP arrays require special treatment. Most arrays should have MarshalXML 350 // methods defined, but in case an extenstion developer forgets, we add this 351 // transparently, guessing the correct RDF array type from Go array/slice. 352 if (kind == reflect.Slice || kind == reflect.Array) && typ.Elem().Kind() != reflect.Uint8 { 353 atype := ArrayTypeOrdered 354 if kind == reflect.Array { 355 atype = ArrayTypeUnordered 356 } 357 // log.Debugf("xmp: marshalValue calling MarshalArray on %v for %s\n", typ, name) 358 return MarshalArray(e, node, atype, val.Interface()) 359 } 360 361 // simple values are just fine 362 if node != nil && kind != reflect.Struct { 363 // log.Debugf("xmp: marshalValue adding simple value to node %s type %v\n", node.XMLName.Local, typ) 364 s, b, err := marshalSimple(typ, val) 365 if err != nil { 366 return err 367 } 368 if b != nil { 369 s = string(b) 370 } 371 node.Value = s 372 return nil 373 } 374 375 // handle structs 376 tinfo, err := getTypeInfo(typ, "xmp") 377 if err != nil { 378 return err 379 } 380 381 // encode struct attributes 382 for _, finfo := range tinfo.fields { 383 if finfo.flags&fOmit > 0 { 384 continue 385 } 386 387 if finfo.flags&fAttr == 0 { 388 continue 389 } 390 391 // version must always match 392 if !e.version.Between(finfo.minVersion, finfo.maxVersion) { 393 // log.Debugf("xmp: marshalValue attr field %s version %v - %v does not match %v\n", finfo.name, finfo.minVersion, finfo.maxVersion, e.version) 394 continue 395 } 396 397 fv := finfo.value(val) 398 399 if (fv.Kind() == reflect.Interface || fv.Kind() == reflect.Ptr) && fv.IsNil() { 400 // log.Debugf("xmp: marshalValue attr field %s is nil\n", finfo.name) 401 continue 402 } 403 404 if finfo.flags&fEmpty == 0 && isEmptyValue(fv) { 405 // log.Debugf("xmp: marshalValue attr field %s is empty\n", finfo.name) 406 continue 407 } 408 409 // find or create output node for storing the attribute/node contents 410 var dest *Node 411 if withWrapper || node == nil { 412 ns := e.findNs(NewName(finfo.name)) 413 if ns == nil { 414 return fmt.Errorf("xmp: missing namespace for attr field %s", finfo.name) 415 } 416 dest = node.Nodes.FindNode(ns) 417 if dest == nil { 418 // log.Debugf("xmp: marshalValue creating new node for attr field %s type %s\n", finfo.name, ns.GetName()) 419 dest = NewNode(ns.XMLName("")) 420 node.AddNode(dest) 421 } 422 } else { 423 dest = node 424 } 425 426 name := NewName(finfo.name) 427 if err := e.marshalAttr(dest, name, fv); err != nil { 428 return err 429 } 430 } 431 432 // encode struct fields 433 var haveField bool 434 for _, finfo := range tinfo.fields { 435 if finfo.flags&fOmit > 0 { 436 continue 437 } 438 439 if finfo.flags&fElement == 0 { 440 // log.Debugf("xmp: marshalValue field %s is not an element\n", finfo.name) 441 continue 442 } 443 444 // version must always match 445 if !e.version.Between(finfo.minVersion, finfo.maxVersion) { 446 // log.Debugf("xmp: marshalValue node field %s version %v - %v does not match %v\n", finfo.name, finfo.minVersion, finfo.maxVersion, e.version) 447 continue 448 } 449 450 fv := finfo.value(val) 451 452 if (fv.Kind() == reflect.Interface || fv.Kind() == reflect.Ptr) && fv.IsNil() { 453 // log.Debugf("xmp: marshalValue node field %s is nil\n", finfo.name) 454 continue 455 } 456 457 if finfo.flags&fEmpty == 0 && isEmptyValue(fv) { 458 // log.Debugf("xmp: marshalValue node field %s is empty\n", finfo.name) 459 continue 460 } 461 462 // find or create output node for storing the attribute/node contents 463 var dest *Node 464 if withWrapper || node == nil { 465 ns := e.findNs(NewName(finfo.name)) 466 if ns == nil { 467 return fmt.Errorf("xmp: marshalValue missing namespace for %s", finfo.name) 468 } 469 dest = node.Nodes.FindNode(ns) 470 if dest == nil { 471 // log.Debugf("xmp: marshalValue creating new node for node field %s type %s\n", finfo.name, ns.GetName()) 472 dest = NewNode(ns.XMLName("")) 473 node.AddNode(dest) 474 } 475 } else { 476 dest = node 477 } 478 479 // create a new node for this resource 480 resNode := NewNode(NewName(finfo.name)) 481 dest.AddNode(resNode) 482 483 if err := e.marshalValue(fv, &finfo, resNode, false); err != nil { 484 return err 485 } 486 haveField = true 487 } 488 489 if haveField && node.XMLName != rdfDescription { 490 // for all kinds of structs add rdf:resource as node attribute 491 node.AddAttr(rdfResourceAttr) 492 } 493 494 return nil 495 } 496 497 func isEmptyValue(v reflect.Value) bool { 498 if v.CanInterface() && v.Type().Implements(zeroType) { 499 return v.Interface().(Zero).IsZero() 500 } 501 502 switch v.Kind() { 503 case reflect.Array, reflect.Map, reflect.Slice, reflect.String: 504 return v.Len() == 0 505 case reflect.Bool: 506 return !v.Bool() 507 case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: 508 return v.Int() == 0 509 case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr: 510 return v.Uint() == 0 511 case reflect.Float32, reflect.Float64: 512 return v.Float() == 0 513 case reflect.Interface, reflect.Ptr: 514 return v.IsNil() 515 } 516 return false 517 } 518 519 func (e *Encoder) marshalAttr(node *Node, name xml.Name, val reflect.Value) error { 520 if val.CanInterface() && val.Type().Implements(attrMarshalerType) { 521 // log.Debugf("xmp: marshalAttr calling MarshalXmpAttr on %v for %s\n", val.Type(), name.Local) 522 attr, err := val.Interface().(MarshalerAttr).MarshalXMPAttr(e, name, node) 523 if err != nil { 524 return err 525 } 526 if attr.Name.Local != "" { 527 node.AddAttr(attr) 528 } 529 return nil 530 } 531 532 if val.CanAddr() { 533 pv := val.Addr() 534 if pv.CanInterface() && pv.Type().Implements(attrMarshalerType) { 535 // log.Debugf("xmp: marshalAttr calling MarshalXmpAttr on %v for %s\n", val.Type(), name.Local) 536 attr, err := pv.Interface().(MarshalerAttr).MarshalXMPAttr(e, name, node) 537 if err != nil { 538 return err 539 } 540 if attr.Name.Local != "" { 541 node.AddAttr(attr) 542 } 543 return nil 544 } 545 } 546 547 if val.CanInterface() && val.Type().Implements(textMarshalerType) { 548 // log.Debugf("xmp: marshalAttr calling MarshalText on %v for %s\n", val.Type(), name.Local) 549 b, err := val.Interface().(encoding.TextMarshaler).MarshalText() 550 if err != nil || b == nil { 551 return err 552 } 553 node.AddAttr(Attr{Name: name, Value: string(b)}) 554 return nil 555 } 556 557 if val.CanAddr() { 558 pv := val.Addr() 559 if pv.CanInterface() && pv.Type().Implements(textMarshalerType) { 560 // log.Debugf("xmp: marshalAttr calling MarshalText on %v for %s\n", val.Type(), name.Local) 561 b, err := pv.Interface().(encoding.TextMarshaler).MarshalText() 562 if err != nil || b == nil { 563 return err 564 } 565 node.AddAttr(Attr{Name: name, Value: string(b)}) 566 return nil 567 } 568 } 569 570 // Dereference or skip nil pointer, interface values. 571 switch val.Kind() { 572 case reflect.Ptr, reflect.Interface: 573 if val.IsNil() { 574 // log.Debugf("xmp: marshalAttr field %s is nil\n", name.Local) 575 return nil 576 } 577 val = val.Elem() 578 } 579 580 s, b, err := marshalSimple(val.Type(), val) 581 if err != nil { 582 return err 583 } 584 if b != nil { 585 s = string(b) 586 } 587 node.AddAttr(Attr{Name: name, Value: s}) 588 return nil 589 } 590 591 func marshalSimple(typ reflect.Type, val reflect.Value) (string, []byte, error) { 592 switch val.Kind() { 593 case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: 594 return strconv.FormatInt(val.Int(), 10), nil, nil 595 case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr: 596 return strconv.FormatUint(val.Uint(), 10), nil, nil 597 case reflect.Float32, reflect.Float64: 598 return strconv.FormatFloat(val.Float(), 'g', -1, val.Type().Bits()), nil, nil 599 case reflect.String: 600 return val.String(), nil, nil 601 case reflect.Bool: 602 return strconv.FormatBool(val.Bool()), nil, nil 603 case reflect.Array: 604 if typ.Elem().Kind() != reflect.Uint8 { 605 break 606 } 607 // [...]byte 608 var bytes []byte 609 if val.CanAddr() { 610 bytes = val.Slice(0, val.Len()).Bytes() 611 } else { 612 bytes = make([]byte, val.Len()) 613 reflect.Copy(reflect.ValueOf(bytes), val) 614 } 615 return "", bytes, nil 616 case reflect.Slice: 617 if typ.Elem().Kind() != reflect.Uint8 { 618 break 619 } 620 // []byte 621 return "", val.Bytes(), nil 622 } 623 return "", nil, fmt.Errorf("xmp: no method for marshalling type %s (%v)\n", typ.String(), val.Kind()) 624 } 625 626 func (e Encoder) _findNsByURI(uri string) *Namespace { 627 if v, ok := e.intNsMap[uri]; ok { 628 return v 629 } 630 if v, ok := e.extNsMap[uri]; ok { 631 return v 632 } 633 return nil 634 } 635 636 func (e Encoder) _findNsByPrefix(pre string) *Namespace { 637 for _, v := range e.intNsMap { 638 if v.GetName() == pre { 639 return v 640 } 641 } 642 for _, v := range e.extNsMap { 643 if v.GetName() == pre { 644 return v 645 } 646 } 647 if ns, err := NsRegistry.GetNamespace(pre); err == nil { 648 return ns 649 } 650 return nil 651 } 652 653 func (e Encoder) findNs(n xml.Name) *Namespace { 654 var ns *Namespace 655 if n.Space != "" { 656 ns = e._findNsByURI(n.Space) 657 } 658 if ns == nil { 659 ns = e._findNsByPrefix(getPrefix(n.Local)) 660 } 661 return ns 662 } 663 664 func ToString(t interface{}) string { 665 val := reflect.Indirect(reflect.ValueOf(t)) 666 if !val.IsValid() { 667 return "" 668 } 669 if val.Type().Implements(stringerType) { 670 return t.(fmt.Stringer).String() 671 } 672 if s, err := ToRawString(val.Interface()); err == nil { 673 return s 674 } 675 return fmt.Sprintf("%v", val.Interface()) 676 } 677 678 func isBase64(s string) bool { 679 _, err := base64.StdEncoding.DecodeString(s) 680 return err == nil 681 } 682 683 func ToRawString(t interface{}) (string, error) { 684 val := reflect.Indirect(reflect.ValueOf(t)) 685 typ := val.Type() 686 switch val.Kind() { 687 case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: 688 return strconv.FormatInt(val.Int(), 10), nil 689 case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr: 690 return strconv.FormatUint(val.Uint(), 10), nil 691 case reflect.Float32, reflect.Float64: 692 return strconv.FormatFloat(val.Float(), 'g', -1, val.Type().Bits()), nil 693 case reflect.String: 694 return val.String(), nil 695 case reflect.Bool: 696 return strconv.FormatBool(val.Bool()), nil 697 case reflect.Array: 698 if typ.Elem().Kind() != reflect.Uint8 { 699 break 700 } 701 // [...]byte 702 var b []byte 703 if val.CanAddr() { 704 b = val.Slice(0, val.Len()).Bytes() 705 } else { 706 b = make([]byte, val.Len()) 707 reflect.Copy(reflect.ValueOf(b), val) 708 } 709 if !isBase64(string(b)) { 710 return base64.StdEncoding.EncodeToString(b), nil 711 } 712 return string(b), nil 713 case reflect.Slice: 714 if typ.Elem().Kind() != reflect.Uint8 { 715 break 716 } 717 // []byte 718 b := val.Bytes() 719 if !isBase64(string(b)) { 720 return base64.StdEncoding.EncodeToString(b), nil 721 } 722 return string(b), nil 723 } 724 return "", fmt.Errorf("xmp: no method for converting type %s (%v) to string", typ.String(), val.Kind()) 725 }