2017-08-28 20:56:18 -05:00
|
|
|
// Copyright 2017 Baliance. All rights reserved.
|
|
|
|
//
|
|
|
|
// Use of this source code is governed by the terms of the Affero GNU General
|
|
|
|
// Public License version 3.0 as published by the Free Software Foundation and
|
|
|
|
// appearing in the file LICENSE included in the packaging of this file. A
|
|
|
|
// commercial license can be purchased by contacting sales@baliance.com.
|
|
|
|
|
|
|
|
package common
|
|
|
|
|
|
|
|
import (
|
|
|
|
"fmt"
|
|
|
|
"strings"
|
|
|
|
|
2019-05-04 11:18:06 +03:00
|
|
|
"github.com/unidoc/unioffice"
|
|
|
|
"github.com/unidoc/unioffice/schema/soo/pkg/relationships"
|
2017-08-28 20:56:18 -05:00
|
|
|
)
|
|
|
|
|
|
|
|
// Relationships represents a .rels file.
|
|
|
|
type Relationships struct {
|
|
|
|
x *relationships.Relationships
|
|
|
|
}
|
|
|
|
|
|
|
|
// NewRelationships creates a new relationship wrapper.
|
|
|
|
func NewRelationships() Relationships {
|
|
|
|
return Relationships{x: relationships.NewRelationships()}
|
|
|
|
}
|
|
|
|
|
|
|
|
// X returns the underlying raw XML data.
|
|
|
|
func (r Relationships) X() *relationships.Relationships {
|
|
|
|
return r.x
|
|
|
|
}
|
|
|
|
|
2017-09-05 09:28:40 -05:00
|
|
|
// IsEmpty returns true if there are no relationships.
|
|
|
|
func (r Relationships) IsEmpty() bool {
|
2018-01-24 18:59:09 -06:00
|
|
|
return r.x == nil || len(r.x.Relationship) == 0
|
2017-09-05 09:28:40 -05:00
|
|
|
}
|
|
|
|
|
2017-08-28 20:56:18 -05:00
|
|
|
// Clear removes any existing relationships.
|
|
|
|
func (r Relationships) Clear() {
|
|
|
|
r.x.Relationship = nil
|
|
|
|
}
|
|
|
|
|
|
|
|
// FindRIDForN returns the relationship ID for the i'th relationship of type t.
|
|
|
|
func (r Relationships) FindRIDForN(i int, t string) string {
|
|
|
|
for _, rel := range r.x.CT_Relationships.Relationship {
|
|
|
|
if rel.TypeAttr == t {
|
|
|
|
if i == 0 {
|
|
|
|
return rel.IdAttr
|
|
|
|
}
|
|
|
|
i--
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return ""
|
|
|
|
}
|
|
|
|
|
2017-09-03 12:01:55 -05:00
|
|
|
// AddAutoRelationship adds a relationship with an automatically generated
|
|
|
|
// filename based off of the type. It should be preferred over AddRelationship
|
|
|
|
// to ensure consistent filenames are maintained.
|
2017-09-28 17:05:38 -05:00
|
|
|
func (r Relationships) AddAutoRelationship(dt gooxml.DocType, src string, idx int, ctype string) Relationship {
|
|
|
|
return r.AddRelationship(gooxml.RelativeFilename(dt, src, ctype, idx), ctype)
|
2017-09-03 12:01:55 -05:00
|
|
|
}
|
|
|
|
|
2017-08-28 20:56:18 -05:00
|
|
|
// AddRelationship adds a relationship.
|
|
|
|
func (r Relationships) AddRelationship(target, ctype string) Relationship {
|
|
|
|
if !strings.HasPrefix(ctype, "http://") {
|
2017-09-29 20:52:19 -05:00
|
|
|
gooxml.Log("relationship type %s should start with 'http://'", ctype)
|
2017-08-28 20:56:18 -05:00
|
|
|
}
|
|
|
|
rel := relationships.NewRelationship()
|
2018-09-14 14:14:06 -05:00
|
|
|
nextID := len(r.x.Relationship) + 1
|
2018-09-17 17:46:51 -05:00
|
|
|
used := map[string]struct{}{}
|
|
|
|
|
|
|
|
// identify IDs in use
|
2018-09-14 14:14:06 -05:00
|
|
|
for _, exRel := range r.x.Relationship {
|
2018-09-17 17:46:51 -05:00
|
|
|
used[exRel.IdAttr] = struct{}{}
|
|
|
|
}
|
|
|
|
// find the next ID that is unused
|
|
|
|
for _, ok := used[fmt.Sprintf("rId%d", nextID)]; ok; _, ok = used[fmt.Sprintf("rId%d", nextID)] {
|
|
|
|
nextID++
|
|
|
|
|
2018-09-14 14:14:06 -05:00
|
|
|
}
|
|
|
|
rel.IdAttr = fmt.Sprintf("rId%d", nextID)
|
2017-08-28 20:56:18 -05:00
|
|
|
rel.TargetAttr = target
|
|
|
|
rel.TypeAttr = ctype
|
|
|
|
r.x.Relationship = append(r.x.Relationship, rel)
|
|
|
|
return Relationship{rel}
|
|
|
|
}
|
|
|
|
|
2018-10-16 21:50:48 -05:00
|
|
|
// Remove removes an existing relationship.
|
|
|
|
func (r Relationships) Remove(rel Relationship) bool {
|
|
|
|
for i, ir := range r.x.Relationship {
|
|
|
|
if ir == rel.x {
|
|
|
|
copy(r.x.Relationship[i:], r.x.Relationship[i+1:])
|
|
|
|
r.x.Relationship = r.x.Relationship[0 : len(r.x.Relationship)-1]
|
|
|
|
return true
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return false
|
|
|
|
}
|
|
|
|
|
2017-09-06 22:13:08 -04:00
|
|
|
// Hyperlink is just an appropriately configured relationship.
|
|
|
|
type Hyperlink Relationship
|
|
|
|
|
|
|
|
// AddHyperlink adds an external hyperlink relationship.
|
|
|
|
func (r Relationships) AddHyperlink(target string) Hyperlink {
|
|
|
|
rel := r.AddRelationship(target, gooxml.HyperLinkType)
|
|
|
|
rel.x.TargetModeAttr = relationships.ST_TargetModeExternal
|
|
|
|
return Hyperlink(rel)
|
|
|
|
}
|
|
|
|
|
2017-08-28 20:56:18 -05:00
|
|
|
// Relationships returns a slice of all of the relationships.
|
|
|
|
func (r Relationships) Relationships() []Relationship {
|
|
|
|
ret := []Relationship{}
|
|
|
|
for _, x := range r.x.Relationship {
|
|
|
|
ret = append(ret, Relationship{x})
|
|
|
|
}
|
|
|
|
return ret
|
|
|
|
}
|