forked from unidoc/unioffice
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathrelationships.go
152 lines (127 loc) · 4.37 KB
/
relationships.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
// Copyright 2017 FoxyUtils ehf. 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 on https://unidoc.io.
package common
import (
"fmt"
"strings"
"github.com/unidoc/unioffice"
"github.com/unidoc/unioffice/schema/soo/pkg/relationships"
)
// 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()}
}
// NewRelationshipsCopy creates a new relationships wrapper as a copy of passed in instance.
func NewRelationshipsCopy(rels Relationships) Relationships {
copiedBody := *rels.x
return Relationships{x: &copiedBody}
}
// X returns the underlying raw XML data.
func (r Relationships) X() *relationships.Relationships {
return r.x
}
// IsEmpty returns true if there are no relationships.
func (r Relationships) IsEmpty() bool {
return r.x == nil || len(r.x.Relationship) == 0
}
// 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 ""
}
// 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.
func (r Relationships) AddAutoRelationship(dt unioffice.DocType, src string, idx int, ctype string) Relationship {
return r.AddRelationship(unioffice.RelativeFilename(dt, src, ctype, idx), ctype)
}
// AddRelationship adds a relationship.
func (r Relationships) AddRelationship(target, ctype string) Relationship {
if !strings.HasPrefix(ctype, "http://") {
unioffice.Log("relationship type %s should start with 'http://'", ctype)
}
rel := relationships.NewRelationship()
nextID := len(r.x.Relationship) + 1
used := map[string]struct{}{}
// identify IDs in use
for _, exRel := range r.x.Relationship {
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++
}
rel.IdAttr = fmt.Sprintf("rId%d", nextID)
rel.TargetAttr = target
rel.TypeAttr = ctype
r.x.Relationship = append(r.x.Relationship, rel)
return Relationship{rel}
}
// 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
}
// CopyRelationship copies the relationship.
func (r Relationships) CopyRelationship(idAttr string) (Relationship, bool) {
for i := range r.x.Relationship {
if r.x.Relationship[i].IdAttr == idAttr {
copied := *r.x.Relationship[i]
nextID := len(r.x.Relationship) + 1
used := map[string]struct{}{}
// identify IDs in use
for _, exRel := range r.x.Relationship {
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++
}
copied.IdAttr = fmt.Sprintf("rId%d", nextID)
r.x.Relationship = append(r.x.Relationship, &copied)
return Relationship{&copied}, true
}
}
return Relationship{}, false
}
// 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, unioffice.HyperLinkType)
rel.x.TargetModeAttr = relationships.ST_TargetModeExternal
return Hyperlink(rel)
}
// 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
}