Files
gliffy2drawio/models.go
2026-01-06 19:05:48 +11:00

962 lines
31 KiB
Go

package gliffy2drawio
import (
"encoding/base64"
"encoding/json"
"fmt"
"math"
"regexp"
"sort"
"strconv"
"strings"
)
// Diagram is the root Gliffy document.
type Diagram struct {
ContentType string `json:"contentType"`
Version string `json:"version"`
Title string `json:"title"`
DefaultPage string `json:"defaultPage"`
Stage Stage `json:"stage"`
Pages []GliffyPage `json:"pages"`
Metadata Metadata `json:"metadata"`
EmbeddedResources EmbeddedResources `json:"embeddedResources"`
}
type Metadata struct {
Title string `json:"title"`
}
type Stage struct {
Background string `json:"background"`
Width float64 `json:"width"`
Height float64 `json:"height"`
AutoFit bool `json:"autoFit"`
GridOn bool `json:"gridOn"`
DrawingGuidesOn bool `json:"drawingGuidesOn"`
Objects []*GliffyObject `json:"objects"`
Layers []*GliffyLayer `json:"layers"`
TextStyles TextStyles `json:"textStyles"`
}
type GliffyPage struct {
ID string `json:"id"`
Title string `json:"title"`
Index int `json:"index"`
Scene Scene `json:"scene"`
}
type Scene struct {
Objects []*GliffyObject `json:"objects"`
Layers []*GliffyLayer `json:"layers"`
Background string `json:"background"`
Width float64 `json:"width"`
Height float64 `json:"height"`
AutoFit bool `json:"autoFit"`
GridOn bool `json:"gridOn"`
DrawingGuidesOn bool `json:"drawingGuidesOn"`
TextStyles TextStyles `json:"textStyles"`
}
type GliffyLayer struct {
GUID string `json:"guid"`
Order int `json:"order"`
Name string `json:"name"`
Active bool `json:"active"`
Locked bool `json:"locked"`
Visible bool `json:"visible"`
NodeIndex int `json:"nodeIndex"`
MxObject *MxCell `json:"-"`
}
type TextStyles struct {
Global GlobalTextStyles `json:"global"`
}
type GlobalTextStyles struct {
Size string `json:"size"`
Color string `json:"color"`
}
func (g GlobalTextStyles) SizeValue() string {
if g.Size == "" {
return ""
}
idx := strings.Index(g.Size, "px")
if idx == -1 {
return g.Size
}
return g.Size[:idx]
}
type GraphicType string
const (
GraphicTypeSVG GraphicType = "Svg"
GraphicTypeLine GraphicType = "Line"
GraphicTypeShape GraphicType = "Shape"
GraphicTypeText GraphicType = "Text"
GraphicTypeImage GraphicType = "Image"
GraphicTypeLink GraphicType = "Link"
GraphicTypeMindmap GraphicType = "Mindmap"
GraphicTypePopup GraphicType = "PopupNote"
GraphicTypeUnknown GraphicType = "Unwknown"
)
type Graphic struct {
Type GraphicType `json:"type"`
Text *GliffyText `json:"Text"`
Line *GliffyLine `json:"Line"`
Link *GliffyLink `json:"Link"`
Shape *GliffyShape `json:"Shape"`
Image *GliffyImage `json:"Image"`
Svg *GliffySvg `json:"Svg"`
Mindmap *GliffyMindmap `json:"Mindmap"`
PopupNote *GliffyPopupNote `json:"PopupNote"`
}
func (g *Graphic) GetType() GraphicType {
if g == nil {
return GraphicTypeUnknown
}
if g.Type == "" {
return GraphicTypeUnknown
}
return g.Type
}
type GliffyAbstractShape struct {
StrokeWidth float64 `json:"strokeWidth"`
StrokeColor string `json:"strokeColor"`
FillColor string `json:"fillColor"`
DashStyle string `json:"dashStyle"`
}
func (s GliffyAbstractShape) StrokeWidthValue() int {
return int(math.Round(s.StrokeWidth))
}
type GliffyLine struct {
GliffyAbstractShape
StartArrow *int `json:"startArrow"`
EndArrow *int `json:"endArrow"`
Interpolation string `json:"interpolationType"`
CornerRadius *int `json:"cornerRadius"`
ControlPath [][]float64 `json:"controlPath"`
}
type GliffyShape struct {
GliffyAbstractShape
TID string `json:"tid"`
Gradient bool `json:"gradient"`
DropShadow bool `json:"dropShadow"`
State int `json:"state"`
ShadowX float64 `json:"shadowX"`
ShadowY float64 `json:"shadowY"`
Opacity float64 `json:"opacity"`
}
func (s GliffyShape) NoFill() bool {
return s.TID != "" && strings.Contains(s.TID, "no_fill")
}
type GliffyImage struct {
GliffyShape
URL string `json:"url"`
}
func (g GliffyImage) CleanURL() string {
return strings.ReplaceAll(g.URL, ";base64", "")
}
type GliffySvg struct {
GliffyShape
EmbeddedResourceID *int `json:"embeddedResourceId"`
}
type GliffyMindmap struct {
GliffyShape
}
type GliffyPopupNote struct {
GliffyShape
Text string `json:"text"`
}
type GliffyLink struct {
Href string `json:"href"`
RenderIcon bool `json:"renderIcon"`
}
type EmbeddedResource struct {
ID int `json:"id"`
MimeType string `json:"mimeType"`
Data string `json:"data"`
}
func (r EmbeddedResource) Base64EncodedData() string {
return base64.StdEncoding.EncodeToString([]byte(r.Data))
}
type EmbeddedResources struct {
Resources []EmbeddedResource `json:"resources"`
resourceMap map[int]EmbeddedResource
}
func (e *EmbeddedResources) Get(id int) (EmbeddedResource, bool) {
if e == nil {
return EmbeddedResource{}, false
}
if e.resourceMap == nil {
e.resourceMap = make(map[int]EmbeddedResource, len(e.Resources))
for _, r := range e.Resources {
e.resourceMap[r.ID] = r
}
}
r, ok := e.resourceMap[id]
return r, ok
}
type Constraints struct {
Constraints []*Constraint `json:"constraints"`
StartConstraint *Constraint `json:"startConstraint"`
EndConstraint *Constraint `json:"endConstraint"`
}
type ConstraintType string
const (
ConstraintTypeStart ConstraintType = "StartPositionConstraint"
ConstraintTypeEnd ConstraintType = "EndPositionConstraint"
ConstraintTypeHeight ConstraintType = "HeightConstraint"
)
type Constraint struct {
Type ConstraintType `json:"type"`
StartPositionConstraint *ConstraintData `json:"StartPositionConstraint"`
EndPositionConstraint *ConstraintData `json:"EndPositionConstraint"`
}
type ConstraintData struct {
NodeID string `json:"nodeId"`
PX float64 `json:"px"`
PY float64 `json:"py"`
}
// GliffyText mirrors Gliffy's text object.
type GliffyText struct {
HTML string `json:"html"`
Valign string `json:"valign"`
Halign string `json:"-"`
VPosition string `json:"vposition"`
HPosition string `json:"hposition"`
PaddingLeft int `json:"paddingLeft"`
PaddingRight int `json:"paddingRight"`
PaddingBottom int `json:"paddingBottom"`
PaddingTop int `json:"paddingTop"`
LineTValue float64 `json:"lineTValue"`
LinePerpValue *float64 `json:"linePerpValue"`
CardinalityType string `json:"cardinalityType"`
Overflow string `json:"overflow"`
forceTopPaddingShift bool
}
func (t *GliffyText) UnmarshalJSON(data []byte) error {
type raw GliffyText
var r raw
if err := json.Unmarshal(data, &r); err != nil {
return err
}
*t = GliffyText(r)
if t.LineTValue == 0 {
t.LineTValue = 0.5
}
t.postDeserialize()
return nil
}
func (t *GliffyText) postDeserialize() {
t.Halign = t.extractHorizontalAlignment()
t.convertTextAlign()
t.replaceParagraphWithDiv()
}
func (t *GliffyText) GetStyle(x, y float64) string {
var sb strings.Builder
topPaddingShift := 7
switch t.VPosition {
case "above":
sb.WriteString("verticalLabelPosition=top;verticalAlign=bottom;")
case "below":
sb.WriteString("verticalLabelPosition=bottom;verticalAlign=top;")
case "none":
sb.WriteString("verticalAlign=" + t.Valign + ";")
if !t.forceTopPaddingShift && strings.EqualFold(t.Valign, "middle") {
topPaddingShift = 0
}
}
switch t.HPosition {
case "left":
sb.WriteString("labelPosition=left;align=right;")
case "right":
sb.WriteString("labelPosition=right;align=left;")
case "none":
if t.Halign != "" {
sb.WriteString("align=" + t.Halign + ";")
if strings.EqualFold(t.Halign, "right") {
x = 0
}
} else {
sb.WriteString("align=center;")
}
}
t.PaddingLeft = maxInt(0, t.PaddingLeft-2)
t.PaddingRight = maxInt(0, t.PaddingRight-2)
sb.WriteString("spacingLeft=" + floatToString(float64(t.PaddingLeft)+x) + ";")
sb.WriteString("spacingRight=" + intToString(t.PaddingRight) + ";")
if t.forceTopPaddingShift || !strings.EqualFold(t.Valign, "middle") {
sb.WriteString("spacingTop=" + floatToString(float64(t.PaddingTop-topPaddingShift)+y) + ";")
sb.WriteString("spacingBottom=" + intToString(t.PaddingBottom) + ";")
}
if t.Overflow == "none" {
sb.WriteString("whiteSpace=wrap;")
}
return sb.String()
}
func (t *GliffyText) SetHAlign(value string) {
t.Halign = value
}
func (t *GliffyText) SetVAlign(value string) {
t.Valign = value
}
func (t *GliffyText) SetForceTopPaddingShift(v bool) {
t.forceTopPaddingShift = v
}
var (
spanPattern = regexp.MustCompile(`<span style="(.*?)">`)
textAlign = regexp.MustCompile(`.*(text-align: ?(left|center|right);).*`)
textAlignToDraw = regexp.MustCompile(`style="text-align:\s?(left|center|right);"`)
lineHeight = regexp.MustCompile(`.*(line-height: .*px;).*`)
)
func (t *GliffyText) replaceParagraphWithDiv() {
m := spanPattern.FindAllStringSubmatchIndex(t.HTML, -1)
if len(m) == 0 {
t.HTML = strings.ReplaceAll(strings.ReplaceAll(strings.ReplaceAll(t.HTML, "<p ", "<div "), "<p>", "<div>"), "</p>", "</div>")
return
}
var mod strings.Builder
last := 0
for _, match := range m {
_, end := match[0], match[1]
styleStart, styleEnd := match[2], match[3]
span := t.HTML[last:end]
style := t.HTML[styleStart:styleEnd]
if style != "" {
if !lineHeight.MatchString(style) {
if strings.HasPrefix(strings.ToLower(t.HTML[end:]), "<span") {
span = span[:styleEnd-last] + " line-height: 0;" + span[styleEnd-last:]
} else {
span = span[:styleEnd-last] + " line-height: normal;" + span[styleEnd-last:]
}
}
}
mod.WriteString(span)
last = end
}
mod.WriteString(t.HTML[last:])
t.HTML = mod.String()
t.HTML = strings.ReplaceAll(strings.ReplaceAll(strings.ReplaceAll(t.HTML, "<p ", "<div "), "<p>", "<div>"), "</p>", "</div>")
}
func (t *GliffyText) extractHorizontalAlignment() string {
m := textAlign.FindStringSubmatch(t.HTML)
if len(m) == 0 {
return ""
}
return m[2]
}
func (t *GliffyText) convertTextAlign() {
t.HTML = textAlignToDraw.ReplaceAllString(t.HTML, `align="$1"`)
}
// GliffyObject represents an element on the canvas.
type GliffyObject struct {
X float64 `json:"x"`
Y float64 `json:"y"`
ID string `json:"id"`
Width float64 `json:"width"`
Height float64 `json:"height"`
Rotation float64 `json:"rotation"`
UID string `json:"uid"`
TID string `json:"tid"`
Order OrderValue `json:"order"`
LockShape bool `json:"lockshape"`
LayerID *string `json:"layerId"`
Graphic *Graphic `json:"graphic"`
Children []*GliffyObject `json:"children"`
Constraints *Constraints `json:"constraints"`
MxObject *MxCell `json:"-"`
Parent *GliffyObject `json:"-"`
}
// OrderValue supports both numeric and string orders found in Gliffy files.
type OrderValue string
func (o *OrderValue) UnmarshalJSON(data []byte) error {
if len(data) == 0 {
return nil
}
if data[0] == '"' {
var s string
if err := json.Unmarshal(data, &s); err != nil {
return err
}
*o = OrderValue(s)
return nil
}
var num json.Number
if err := json.Unmarshal(data, &num); err == nil {
*o = OrderValue(num.String())
return nil
}
*o = OrderValue(strings.Trim(string(data), "\""))
return nil
}
func (o OrderValue) String() string {
return string(o)
}
func (o *GliffyObject) UnmarshalJSON(data []byte) error {
type raw GliffyObject
var r raw
if err := json.Unmarshal(data, &r); err != nil {
return err
}
*o = GliffyObject(r)
o.postDeserialize()
return nil
}
func (o *GliffyObject) postDeserialize() {
if o.isGroup() && o.hasChildren() {
o.normalizeChildrenCoordinates()
o.adjustZOrder()
}
}
func (o *GliffyObject) hasChildren() bool {
return len(o.Children) > 0
}
func (o *GliffyObject) GraphicOrChildGraphic() *Graphic {
if o.Graphic != nil {
return o.Graphic
}
if o.isUml() || graphicalessShapes[o.UID] {
return o.firstChildGraphic()
}
return nil
}
func (o *GliffyObject) firstChildGraphic() *Graphic {
if len(o.Children) == 0 {
return nil
}
return o.Children[0].Graphic
}
func (o *GliffyObject) TextObject() *GliffyObject {
return o.textObjectWithOffset(0, 0)
}
func (o *GliffyObject) textObjectWithOffset(x, y float64) *GliffyObject {
if o.isText() {
return o
}
if len(o.Children) == 0 {
return nil
}
for _, child := range o.Children {
if g := child.GraphicOrChildGraphic(); g != nil && g.GetType() == GraphicTypeText {
child.X += x
child.Y += y
return child
}
if txt := child.textObjectWithOffset(child.X, child.Y); txt != nil {
return txt
}
}
return nil
}
func (o *GliffyObject) TextHTML() string {
if o.Graphic == nil || o.Graphic.Text == nil {
return ""
}
text := o.Graphic.Text
widthDiff := -3
if text.Overflow != "none" {
widthDiff = 6
}
return "<div style='width: " + floatToString(o.Width+float64(widthDiff)) + "px;height:auto;word-break: break-word;'>" + text.HTML + "</div>"
}
func (o *GliffyObject) isGroup() bool {
if !o.hasChildren() {
return false
}
if o.UID != "" && (groupShapes[o.UID] || strings.HasPrefix(o.UID, "com.gliffy.shape.table")) {
return true
}
if o.UID == "" && len(o.Children) > 0 && !o.Children[0].isText() {
return true
}
return false
}
func (o *GliffyObject) isSelection() bool {
return o.UID != "" && strings.Contains(o.UID, "default.selection")
}
func (o *GliffyObject) isMindmap() bool {
return o.UID != "" && mindmapShapes[o.UID]
}
func (o *GliffyObject) isLine() bool {
return o.Graphic != nil && o.Graphic.GetType() == GraphicTypeLine
}
func (o *GliffyObject) isLink() bool {
return o.Graphic != nil && o.Graphic.GetType() == GraphicTypeLink
}
func (o *GliffyObject) isUml() bool {
return strings.HasPrefix(o.UID, "com.gliffy.shape.uml.uml")
}
func (o *GliffyObject) isShape() bool {
if o.Graphic != nil {
t := o.Graphic.GetType()
return t == GraphicTypeShape || t == GraphicTypeMindmap
}
g := o.firstChildGraphic()
return g != nil && g.GetType() == GraphicTypeShape
}
func (o *GliffyObject) isSvg() bool {
return o.Graphic != nil && o.Graphic.GetType() == GraphicTypeSVG
}
func (o *GliffyObject) isSwimlane() bool {
return o.UID != "" && strings.Contains(o.UID, "com.gliffy.shape.swimlanes")
}
func (o *GliffyObject) isText() bool {
return o.Graphic != nil && o.Graphic.GetType() == GraphicTypeText
}
func (o *GliffyObject) isImage() bool {
return o.Graphic != nil && o.Graphic.GetType() == GraphicTypeImage
}
func (o *GliffyObject) isVennCircle() bool {
return strings.HasPrefix(o.UID, "com.gliffy.shape.venn")
}
func (o *GliffyObject) isDeeplyNestedLink() bool {
return o.UID != "" && deeplyNestedLinks[o.UID]
}
func (o *GliffyObject) GradientColor() string {
gradientColor := "#FFFFFF"
if o.Graphic != nil && o.Graphic.Shape != nil && o.UID != "" && !strings.HasPrefix(o.UID, "com.gliffy.shape.radial") {
hex := o.Graphic.Shape.FillColor
if len(hex) == 7 && strings.HasPrefix(hex, "#") {
clr, err := parseHex(hex[1:])
if err == nil {
r := math.Min(0xFF0000, float64(clr&0xFF0000)+0xAA0000)
g := math.Min(0x00FF00, float64(clr&0x00FF00)+0x00AA00)
b := math.Min(0x0000FF, float64(clr&0x0000FF)+0x0000AA)
gradientColor = "#" + strings.ToUpper(hexInt(int(r+g+b)))
}
}
}
return gradientColor
}
func (o *GliffyObject) GradientIgnored() bool {
return o.UID != "" && (strings.HasPrefix(o.UID, "com.gliffy.shape.venn.outline") || strings.HasPrefix(o.UID, "com.gliffy.shape.venn.flat"))
}
func (o *GliffyObject) IsSubRoutine() bool {
return o.UID == "com.gliffy.shape.flowchart.flowchart_v1.default.subroutine"
}
func (o *GliffyObject) IsUnrecognizedGraphicType() bool {
return o.Graphic != nil && o.Graphic.Type == ""
}
func (o *GliffyObject) GetConstraints() *Constraints {
return o.Constraints
}
func (o *GliffyObject) String() string {
if o.UID != "" {
return o.UID
}
return o.TID
}
func (o *GliffyObject) normalizeChildrenCoordinates() {
if !o.hasChildren() {
return
}
sort.Slice(o.Children, func(i, j int) bool { return o.Children[i].X < o.Children[j].X })
xMin := o.Children[0].X
if xMin < 0 {
o.Width += -xMin
o.X += xMin
for _, c := range o.Children {
c.X += -xMin
}
}
sort.Slice(o.Children, func(i, j int) bool { return o.Children[i].Y < o.Children[j].Y })
yMin := o.Children[0].Y
if yMin < 0 {
o.Height += -yMin
o.Y += yMin
for _, c := range o.Children {
c.Y += -yMin
}
}
}
func (o *GliffyObject) adjustZOrder() {
var maxOrder *int
for _, c := range o.Children {
if c.UID == "com.gliffy.shape.basic.basic_v1.default.rectangle" && c.X == 0 && c.Y == 0 && c.Width == o.Width && c.Height == o.Height {
if val, err := strconv.Atoi(c.Order.String()); err == nil {
if maxOrder == nil || val > *maxOrder {
maxOrder = &val
}
}
}
}
if maxOrder != nil {
o.Order = OrderValue(intToString(*maxOrder))
}
}
func (o *GliffyObject) adjustGeo(geo *MxGeometry) {
arr := shapeCoordFix[o.UID]
if arr == nil && o.Graphic != nil && o.Graphic.Shape != nil {
arr = shapeCoordFix[o.Graphic.Shape.TID]
}
if arr == nil {
return
}
x, y, w, h := geo.X, geo.Y, geo.Width, geo.Height
shifts := o.getAdjustShifts(arr, x, y, w, h)
geo.X = x + shifts.X
geo.Y = y + shifts.Y
geo.Width = w + shifts.Width
geo.Height = h + shifts.Height
}
func (o *GliffyObject) adjustTextPos(textObj *GliffyObject) {
arr := shapeCoordFix[o.UID]
if arr == nil || len(arr) != 4 {
return
}
shifts := o.getAdjustShifts(arr, o.X, o.Y, o.Width, o.Height)
textObj.X -= shifts.X
textObj.Y -= shifts.Y
}
func (o *GliffyObject) getAdjustShifts(arr []float64, x, y, w, h float64) *MxGeometry {
xShift := relativeShift(arr[0], w)
yShift := relativeShift(arr[1], h)
wShift := relativeShift(arr[2], w)
hShift := relativeShift(arr[3], h)
mod := &MxGeometry{X: x + xShift, Y: y + yShift, Width: w + wShift, Height: h + hShift}
if o.Rotation > 0 {
orig := &MxGeometry{X: x, Y: y, Width: w, Height: h}
rotateGeometry(orig, o.Rotation, 0, 0)
rotateGeometry(mod, o.Rotation, 0, 0)
xShift += mod.X - orig.X
yShift += mod.Y - orig.Y
}
mod.X = xShift
mod.Y = yShift
mod.Width = wShift
mod.Height = hShift
return mod
}
func (o *GliffyObject) AdjustedLink() string {
if len(o.Children) == 0 {
return ""
}
if o.isDeeplyNestedLink() {
var sb strings.Builder
o.collectDeepLink(&sb)
return sb.String()
}
for _, c := range o.Children {
if c.isLink() && c.Graphic != nil && c.Graphic.Link != nil {
return c.Graphic.Link.Href
}
}
return ""
}
func (o *GliffyObject) collectDeepLink(sb *strings.Builder) {
for _, c := range o.Children {
if c.isLink() && c.Graphic != nil && c.Graphic.Link != nil {
sb.WriteString(c.Graphic.Link.Href)
return
}
if len(c.Children) > 0 {
c.collectDeepLink(sb)
}
}
}
func (o *GliffyObject) IsUseFillColorForStroke() bool {
key := o.UID
if key == "" && o.Graphic != nil && o.Graphic.Shape != nil {
key = o.Graphic.Shape.TID
}
return fillColorIsStroke[key]
}
func (o *GliffyObject) ContainsTextBracket() bool {
return o.UID != "" && strings.Contains(o.UID, "com.gliffy.shape.uml.uml_v2.activity.frame")
}
func (o *GliffyObject) ForceConstraints() bool {
return forceConstraintsShapes[o.UID]
}
func (o *GliffyObject) UMLSequenceCombinedFragmentText() string {
switch o.UID {
case "com.gliffy.shape.uml.uml_v2.sequence.interaction_use":
return "ref"
case "com.gliffy.shape.uml.uml_v2.sequence.opt_combined_fragment":
return "opt"
case "com.gliffy.shape.uml.uml_v2.sequence.loop_combined_fragment":
return "loop"
case "com.gliffy.shape.uml.uml_v2.sequence.alt_combined_fragment":
return "alt"
default:
return ""
}
}
func relativeShift(v, size float64) float64 {
if math.Abs(v) < 1 {
return size * v
}
return v
}
func parseHex(s string) (int64, error) {
return strconv.ParseInt(s, 16, 64)
}
func hexInt(v int) string {
return fmt.Sprintf("%06X", v&0xFFFFFF)
}
// to avoid fmt in hot paths.
func floatToString(v float64) string {
return strings.TrimRight(strings.TrimRight(strconv.FormatFloat(v, 'f', 4, 64), "0"), ".")
}
func intToString(v int) string {
return strconv.Itoa(v)
}
func maxInt(a, b int) int {
if a > b {
return a
}
return b
}
// Static shape metadata replicated from Java implementation.
var graphicalessShapes = map[string]bool{
"com.gliffy.shape.uml.uml_v1.default.package": true,
"com.gliffy.shape.uml.uml_v1.default.class": true,
"com.gliffy.shape.uml.uml_v1.default.simple_class": true,
"com.gliffy.shape.uml.uml_v1.default.object_timeline": true,
"com.gliffy.shape.uml.uml_v1.default.lifeline": true,
"com.gliffy.shape.uml.uml_v1.default.use_case": true,
"com.gliffy.shape.uml.uml_v1.default.actor": true,
"com.gliffy.shape.uml.uml_v1.default.message": true,
"com.gliffy.shape.uml.uml_v1.default.activation": true,
"com.gliffy.shape.uml.uml_v1.default.dependency": true,
"com.gliffy.shape.uml.uml_v1.default.composition": true,
"com.gliffy.shape.uml.uml_v1.default.aggregation": true,
"com.gliffy.shape.uml.uml_v1.default.association": true,
"com.gliffy.shape.uml.uml_v2.class.package": true,
"com.gliffy.shape.uml.uml_v2.class.simple_class": true,
"com.gliffy.shape.uml.uml_v2.class.class": true,
"com.gliffy.shape.uml.uml_v2.class.class2": true,
"com.gliffy.shape.uml.uml_v2.class.interface": true,
"com.gliffy.shape.uml.uml_v2.class.enumeration": true,
"com.gliffy.shape.uml.uml_v2.sequence.lifeline": true,
"com.gliffy.shape.uml.uml_v2.sequence.boundary_lifeline": true,
"com.gliffy.shape.uml.uml_v2.sequence.control_lifeline": true,
"com.gliffy.shape.uml.uml_v2.sequence.entity_lifeline": true,
"com.gliffy.shape.uml.uml_v2.deployment.package": true,
"com.gliffy.shape.uml.uml_v2.component.package": true,
"com.gliffy.shape.uml.uml_v2.use_case.package": true,
"com.gliffy.shape.erd.erd_v1.default.entity_with_attributes": true,
"com.gliffy.shape.erd.erd_v1.default.entity_with_multiple_attributes": true,
"com.gliffy.shape.bpmn.bpmn_v1.data_artifacts.annotation": true,
"com.gliffy.shape.ui.ui_v3.navigation.navbar": true,
"com.gliffy.shape.ui.ui_v3.forms_controls.combo_box": true,
"com.gliffy.shape.ui.ui_v3.containers_content.tooltip_top": true,
"com.gliffy.shape.ui.ui_v3.containers_content.tooltip_bottom": true,
"com.gliffy.shape.ui.ui_v3.containers_content.tooltip_left": true,
"com.gliffy.shape.ui.ui_v3.containers_content.tooltip_right": true,
"com.gliffy.shape.ui.ui_v3.containers_content.popover_top": true,
"com.gliffy.shape.ui.ui_v3.containers_content.popover_bottom": true,
"com.gliffy.shape.ui.ui_v3.forms_controls.selector": true,
"com.gliffy.shape.ui.ui_v3.icon_symbols.annotate_left": true,
"com.gliffy.shape.ui.ui_v3.icon_symbols.annotate_right": true,
"com.gliffy.shape.ui.ui_v3.icon_symbols.annotate_top": true,
"com.gliffy.shape.ui.ui_v3.containers_content.speech_bubble_right": true,
"com.gliffy.shape.ui.ui_v3.containers_content.speech_bubble_left": true,
"com.gliffy.shape.sitemap.sitemap_v2.page": true,
"com.gliffy.shape.sitemap.sitemap_v2.home": true,
"com.gliffy.shape.sitemap.sitemap_v2.gliffy": true,
"com.gliffy.shape.sitemap.sitemap_v2.form": true,
"com.gliffy.shape.sitemap.sitemap_v2.shopping_cart": true,
"com.gliffy.shape.sitemap.sitemap_v2.text": true,
"com.gliffy.shape.sitemap.sitemap_v2.video": true,
"com.gliffy.shape.sitemap.sitemap_v2.upload": true,
"com.gliffy.shape.sitemap.sitemap_v2.slideshow": true,
"com.gliffy.shape.sitemap.sitemap_v2.sitemap": true,
"com.gliffy.shape.sitemap.sitemap_v2.settings": true,
"com.gliffy.shape.sitemap.sitemap_v2.search": true,
"com.gliffy.shape.sitemap.sitemap_v2.script": true,
"com.gliffy.shape.sitemap.sitemap_v2.print": true,
"com.gliffy.shape.sitemap.sitemap_v2.pricing": true,
"com.gliffy.shape.sitemap.sitemap_v2.photo": true,
"com.gliffy.shape.sitemap.sitemap_v2.map": true,
"com.gliffy.shape.sitemap.sitemap_v2.login": true,
"com.gliffy.shape.sitemap.sitemap_v2.game": true,
"com.gliffy.shape.sitemap.sitemap_v2.gallery": true,
"com.gliffy.shape.sitemap.sitemap_v2.download": true,
"com.gliffy.shape.sitemap.sitemap_v2.document": true,
"com.gliffy.shape.sitemap.sitemap_v2.chat": true,
"com.gliffy.shape.sitemap.sitemap_v2.calendar": true,
"com.gliffy.shape.sitemap.sitemap_v2.audio": true,
"com.gliffy.shape.sitemap.sitemap_v2.profile": true,
"com.gliffy.shape.sitemap.sitemap_v2.error": true,
"com.gliffy.shape.sitemap.sitemap_v2.success": true,
"com.gliffy.shape.sitemap.sitemap_v2.cloud": true,
}
var groupShapes = map[string]bool{
"com.gliffy.shape.basic.basic_v1.default.group": true,
"com.gliffy.shape.erd.erd_v1.default.entity_with_attributes": true,
"com.gliffy.shape.erd.erd_v1.default.entity_with_multiple_attributes": true,
"com.gliffy.shape.uml.uml_v2.sequence.interaction_use": true,
"com.gliffy.shape.uml.uml_v2.sequence.opt_combined_fragment": true,
"com.gliffy.shape.uml.uml_v2.sequence.loop_combined_fragment": true,
"com.gliffy.shape.uml.uml_v2.sequence.alt_combined_fragment": true,
"com.gliffy.shape.uml.uml_v2.class.object": true,
"com.gliffy.shape.uml.uml_v2.class.enumeration": true,
"com.gliffy.shape.uml.uml_v2.class.interface": true,
"com.gliffy.shape.uml.uml_v2.class.class2": true,
"com.gliffy.shape.uml.uml_v2.class.class": true,
"com.gliffy.shape.uml.uml_v2.class.data_type": true,
"com.gliffy.shape.uml.uml_v2.state_machine.composite_state": true,
"com.gliffy.shape.uml.uml_v2.state_machine.orthoganal_state": true,
"com.gliffy.shape.uml.uml_v2.class.package": true,
"com.gliffy.shape.uml.uml_v2.sequence.boundary_lifeline": true,
"com.gliffy.shape.uml.uml_v2.sequence.lifeline": true,
"com.gliffy.shape.uml.uml_v2.sequence.entity_lifeline": true,
"com.gliffy.shape.uml.uml_v2.sequence.control_lifeline": true,
"com.gliffy.shape.uml.uml_v1.default.object_timeline": true,
"com.gliffy.shape.uml.uml_v1.default.class": true,
"com.gliffy.shape.uml.uml_v1.default.object": true,
"com.gliffy.shape.iphone.iphone_ios7.containers_content.table": true,
"com.gliffy.shape.iphone.iphone_ios7.forms_controls.button_stack": true,
"com.gliffy.shape.iphone.iphone_ios7.forms_controls.alert_2options": true,
"com.gliffy.shape.iphone.iphone_ios7.forms_controls.alert": true,
"com.gliffy.shape.iphone.iphone_ios7.navigation.contextual_menu": true,
"com.gliffy.shape.iphone.iphone_ios7.navigation.nav_3tabs": true,
"com.gliffy.shape.iphone.iphone_ios7.containers_content.title_bar": true,
"com.gliffy.shape.iphone.iphone_ios7.navigation.tab_bar": true,
"com.gliffy.shape.iphone.iphone_ios7.forms_controls.search": true,
"com.gliffy.shape.android.android_v1.general.dialog": true,
"com.gliffy.shape.android.android_v1.general.list_1line": true,
"com.gliffy.shape.android.android_v1.general.list_2lines": true,
"com.gliffy.shape.android.android_v1.general.tabs01": true,
"com.gliffy.shape.android.android_v1.general.tabs02": true,
"com.gliffy.shape.network.network_v3.business.user_group": true,
"com.gliffy.shape.ui.ui_v3.navigation.navbar": true,
"com.gliffy.shape.ui.ui_v3.navigation.navbar_vertical": true,
"com.gliffy.shape.ui.ui_v3.forms_controls.dropdown": true,
"com.gliffy.shape.uml.uml_v2.sequence.recursive_message": true,
}
var mindmapShapes = map[string]bool{
"com.gliffy.shape.mindmap.mindmap_v1.default.main_topic": true,
"com.gliffy.shape.mindmap.mindmap_v1.default.subtopic": true,
"com.gliffy.shape.mindmap.mindmap_v1.default.child_node": true,
}
var fillColorIsStroke = map[string]bool{
"com.gliffy.stencil.rectangle.no_fill_line_bottom_2px_off": true,
}
var deeplyNestedLinks = map[string]bool{
"com.gliffy.shape.basic.basic_v1.default.chevron_box_right": true,
"com.gliffy.shape.basic.basic_v1.default.chevron_box_left": true,
"com.gliffy.shape.basic.basic_v1.default.chevron_tail_right": true,
"com.gliffy.shape.basic.basic_v1.default.chevron_tail_left": true,
}
var shapeCoordFix = map[string][]float64{
"com.gliffy.shape.flowchart.flowchart_v1.default.paper_tape": {0, -0.1, 0, 0.2},
"com.gliffy.shape.uml.uml_v1.default.node": {0, -10, 10, 10},
"com.gliffy.shape.uml.uml_v2.deployment.node": {0, -10, 10, 10},
"com.gliffy.shape.uml.uml_v2.deployment.device_node": {0, -10, 10, 10},
"com.gliffy.shape.uml.uml_v2.deployment.execution_environment_node": {0, -10, 10, 10},
"com.gliffy.shape.flowchart.flowchart_v1.default.data_storage": {0, 0, 0.115, 0},
"com.gliffy.shape.flowchart.flowchart_v1.default.database": {0, 0, 0, 0.15},
"com.gliffy.stencil.entity_lifeline.uml_v2": {10, 0, -20, 0},
"com.gliffy.stencil.boundary_lifeline.uml_v2": {35, 0, -70, 0},
"com.gliffy.stencil.control_lifeline.uml_v2": {10, 0, -20, 0},
"com.gliffy.shape.ui.ui_v3.containers_content.browser": {0, -40, 0, 40, 1},
}
var forceConstraintsShapes = map[string]bool{
"com.gliffy.shape.uml.uml_v2.class.association": true,
}