fix empty labels with class & sql_tables w/ premeasured texts

This commit is contained in:
Alexander Wang 2023-01-24 12:09:59 -08:00
parent 5abde05b86
commit 5059085f3c
No known key found for this signature in database
GPG key ID: D89FA31966BDBECE
9 changed files with 339 additions and 12 deletions

View file

@ -707,6 +707,9 @@ func (obj *Object) GetLabelSize(mtexts []*d2target.MText, ruler *textmeasure.Rul
}
if dims == nil {
if obj.Text().Text == "" {
return d2target.NewTextDimensions(0, 0), nil
}
if shapeType == d2target.ShapeImage {
dims = d2target.NewTextDimensions(0, 0)
} else {
@ -728,7 +731,7 @@ func (obj *Object) GetDefaultSize(mtexts []*d2target.MText, ruler *textmeasure.R
return d2target.NewTextDimensions(128, 128), nil
case d2target.ShapeClass:
maxWidth := labelDims.Width
maxWidth := go2.Max(12, labelDims.Width)
for _, f := range obj.Class.Fields {
fdims := GetTextDimensions(mtexts, ruler, f.Text(), go2.Pointer(d2fonts.SourceCodePro))
@ -764,7 +767,7 @@ func (obj *Object) GetDefaultSize(mtexts []*d2target.MText, ruler *textmeasure.R
rowHeight := GetTextDimensions(mtexts, ruler, anyRowText, go2.Pointer(d2fonts.SourceCodePro)).Height + 20
dims.Height = rowHeight * (len(obj.Class.Fields) + len(obj.Class.Methods) + 2)
} else {
dims.Height = labelDims.Height
dims.Height = go2.Max(12, labelDims.Height)
}
case d2target.ShapeSQLTable:
@ -804,10 +807,10 @@ func (obj *Object) GetDefaultSize(mtexts []*d2target.MText, ruler *textmeasure.R
}
// The rows get padded a little due to header font being larger than row font
dims.Height = labelDims.Height * (len(obj.SQLTable.Columns) + 1)
dims.Height = go2.Max(12, labelDims.Height*(len(obj.SQLTable.Columns)+1))
headerWidth := d2target.HeaderPadding + labelDims.Width + d2target.HeaderPadding
rowsWidth := d2target.NamePadding + maxNameWidth + d2target.TypePadding + maxTypeWidth + d2target.TypePadding + constraintWidth
dims.Width = go2.Max(headerWidth, rowsWidth)
dims.Width = go2.Max(12, go2.Max(headerWidth, rowsWidth))
}
return &dims, nil

View file

@ -37,6 +37,7 @@ func TestE2E(t *testing.T) {
t.Run("stable", testStable)
t.Run("regression", testRegression)
t.Run("todo", testTodo)
t.Run("measured", testMeasured)
}
func testSanity(t *testing.T) {
@ -73,6 +74,7 @@ a -> c
type testCase struct {
name string
script string
mtexts []*d2target.MText
assertions func(t *testing.T, diagram *d2target.Diagram)
skip bool
}
@ -118,12 +120,16 @@ func run(t *testing.T, tc testCase) {
ctx = log.WithTB(ctx, t, nil)
ctx = log.Leveled(ctx, slog.LevelDebug)
ruler, err := textmeasure.NewRuler()
if !tassert.Nil(t, err) {
return
}
var ruler *textmeasure.Ruler
var err error
if tc.mtexts == nil {
ruler, err = textmeasure.NewRuler()
if !tassert.Nil(t, err) {
return
}
serde(t, tc, ruler)
serde(t, tc, ruler)
}
layoutsTested := []string{"dagre", "elk"}
@ -132,12 +138,17 @@ func run(t *testing.T, tc testCase) {
if layoutName == "dagre" {
layout = d2dagrelayout.DefaultLayout
} else if layoutName == "elk" {
// If measured texts exists, we are specifically exercising text measurements, no need to run on both layouts
if tc.mtexts != nil {
continue
}
layout = d2elklayout.DefaultLayout
}
diagram, _, err := d2lib.Compile(ctx, tc.script, &d2lib.CompileOptions{
Ruler: ruler,
ThemeID: 0,
Layout: layout,
Ruler: ruler,
MeasuredTexts: tc.mtexts,
ThemeID: 0,
Layout: layout,
})
if !tassert.Nil(t, err) {
return

34
e2etests/measured_test.go Normal file
View file

@ -0,0 +1,34 @@
package e2etests
import (
_ "embed"
"testing"
"oss.terrastruct.com/d2/d2target"
)
// testMeasured exercises the code paths that provide pre-measured texts
func testMeasured(t *testing.T) {
tcs := []testCase{
{
name: "empty-shape",
mtexts: []*d2target.MText{},
script: `a: ""
`,
},
{
name: "empty-class",
mtexts: []*d2target.MText{},
script: `a: "" { shape: class }
`,
},
{
name: "empty-sql_table",
mtexts: []*d2target.MText{},
script: `a: "" { shape: sql_table }
`,
},
}
runa(t, tcs)
}

View file

@ -0,0 +1,49 @@
{
"name": "",
"fontFamily": "SourceSansPro",
"shapes": [
{
"id": "a",
"type": "class",
"pos": {
"x": 0,
"y": 0
},
"width": 112,
"height": 12,
"opacity": 1,
"strokeDash": 0,
"strokeWidth": 2,
"borderRadius": 0,
"fill": "#0A0F25",
"stroke": "#FFFFFF",
"shadow": false,
"3d": false,
"multiple": false,
"tooltip": "",
"link": "",
"icon": null,
"iconPosition": "",
"blend": false,
"fields": null,
"methods": null,
"columns": null,
"label": "",
"fontSize": 20,
"fontFamily": "DEFAULT",
"language": "",
"color": "#0A0F25",
"italic": false,
"bold": false,
"underline": false,
"labelWidth": 0,
"labelHeight": 0,
"zIndex": 0,
"level": 1,
"primaryAccentColor": "#0D32B2",
"secondaryAccentColor": "#4A6FF3",
"neutralAccentColor": "#676C7E"
}
],
"connections": []
}

View file

@ -0,0 +1,45 @@
<?xml version="1.0" encoding="utf-8"?>
<svg
id="d2-svg"
style="background: white;"
xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink"
width="316" height="216" viewBox="-102 -102 316 216"><style type="text/css">
<![CDATA[
.shape {
shape-rendering: geometricPrecision;
stroke-linejoin: round;
}
.connection {
stroke-linecap: round;
stroke-linejoin: round;
}
.blend {
mix-blend-mode: multiply;
opacity: 0.5;
}
]]>
</style><script type="application/javascript"><![CDATA[window.addEventListener("DOMContentLoaded", () => {
if (document.documentElement.getAttribute("id") !== "d2-svg") {
return;
}
const svgEl = document.documentElement;
let width = parseInt(svgEl.getAttribute("width"), 10);
let height = parseInt(svgEl.getAttribute("height"), 10);
let ratio;
if (width > height) {
if (width > window.innerWidth) {
ratio = window.innerWidth / width;
}
} else if (height > window.innerHeight) {
ratio = window.innerHeight / height;
}
if (ratio) {
svgEl.setAttribute("width", width * ratio - 16);
svgEl.setAttribute("height", height * ratio - 16);
}
});
]]></script><g id="a"><g class="shape" ><rect class="shape" x="0" y="0" width="112" height="12" style="fill:#FFFFFF;stroke:#0A0F25;stroke-width:2;"/><rect class="class_header" x="0.000000" y="0.000000" width="112.000000" height="12.000000" fill="#0A0F25" /><line x1="0.000000" y1="12.000000" x2="112.000000" y2="12.000000" style="stroke-width:1;stroke:#0A0F25" /></g></g><mask id="2115119125" maskUnits="userSpaceOnUse" x="-100" y="-100" width="316" height="216">
<rect x="-100" y="-100" width="316" height="216" fill="white"></rect>
</mask><style type="text/css"><![CDATA[]]></style></svg>

After

Width:  |  Height:  |  Size: 1.7 KiB

View file

@ -0,0 +1,46 @@
{
"name": "",
"fontFamily": "SourceSansPro",
"shapes": [
{
"id": "a",
"type": "",
"pos": {
"x": 0,
"y": 0
},
"width": 100,
"height": 100,
"opacity": 1,
"strokeDash": 0,
"strokeWidth": 2,
"borderRadius": 0,
"fill": "#F7F8FE",
"stroke": "#0D32B2",
"shadow": false,
"3d": false,
"multiple": false,
"tooltip": "",
"link": "",
"icon": null,
"iconPosition": "",
"blend": false,
"fields": null,
"methods": null,
"columns": null,
"label": "",
"fontSize": 16,
"fontFamily": "DEFAULT",
"language": "",
"color": "#0A0F25",
"italic": false,
"bold": true,
"underline": false,
"labelWidth": 0,
"labelHeight": 0,
"zIndex": 0,
"level": 1
}
],
"connections": []
}

View file

@ -0,0 +1,45 @@
<?xml version="1.0" encoding="utf-8"?>
<svg
id="d2-svg"
style="background: white;"
xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink"
width="304" height="304" viewBox="-102 -102 304 304"><style type="text/css">
<![CDATA[
.shape {
shape-rendering: geometricPrecision;
stroke-linejoin: round;
}
.connection {
stroke-linecap: round;
stroke-linejoin: round;
}
.blend {
mix-blend-mode: multiply;
opacity: 0.5;
}
]]>
</style><script type="application/javascript"><![CDATA[window.addEventListener("DOMContentLoaded", () => {
if (document.documentElement.getAttribute("id") !== "d2-svg") {
return;
}
const svgEl = document.documentElement;
let width = parseInt(svgEl.getAttribute("width"), 10);
let height = parseInt(svgEl.getAttribute("height"), 10);
let ratio;
if (width > height) {
if (width > window.innerWidth) {
ratio = window.innerWidth / width;
}
} else if (height > window.innerHeight) {
ratio = window.innerHeight / height;
}
if (ratio) {
svgEl.setAttribute("width", width * ratio - 16);
svgEl.setAttribute("height", height * ratio - 16);
}
});
]]></script><g id="a"><g class="shape" ><rect x="0" y="0" width="100" height="100" style="fill:#F7F8FE;stroke:#0D32B2;stroke-width:2;" /></g></g><mask id="1236167803" maskUnits="userSpaceOnUse" x="-100" y="-100" width="304" height="304">
<rect x="-100" y="-100" width="304" height="304" fill="white"></rect>
</mask><style type="text/css"><![CDATA[]]></style></svg>

After

Width:  |  Height:  |  Size: 1.5 KiB

View file

@ -0,0 +1,49 @@
{
"name": "",
"fontFamily": "SourceSansPro",
"shapes": [
{
"id": "a",
"type": "sql_table",
"pos": {
"x": 0,
"y": 0
},
"width": 50,
"height": 12,
"opacity": 1,
"strokeDash": 0,
"strokeWidth": 2,
"borderRadius": 0,
"fill": "#0A0F25",
"stroke": "#FFFFFF",
"shadow": false,
"3d": false,
"multiple": false,
"tooltip": "",
"link": "",
"icon": null,
"iconPosition": "",
"blend": false,
"fields": null,
"methods": null,
"columns": null,
"label": "",
"fontSize": 20,
"fontFamily": "DEFAULT",
"language": "",
"color": "#0A0F25",
"italic": false,
"bold": true,
"underline": false,
"labelWidth": 0,
"labelHeight": 0,
"zIndex": 0,
"level": 1,
"primaryAccentColor": "#0D32B2",
"secondaryAccentColor": "#4A6FF3",
"neutralAccentColor": "#676C7E"
}
],
"connections": []
}

View file

@ -0,0 +1,45 @@
<?xml version="1.0" encoding="utf-8"?>
<svg
id="d2-svg"
style="background: white;"
xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink"
width="254" height="216" viewBox="-102 -102 254 216"><style type="text/css">
<![CDATA[
.shape {
shape-rendering: geometricPrecision;
stroke-linejoin: round;
}
.connection {
stroke-linecap: round;
stroke-linejoin: round;
}
.blend {
mix-blend-mode: multiply;
opacity: 0.5;
}
]]>
</style><script type="application/javascript"><![CDATA[window.addEventListener("DOMContentLoaded", () => {
if (document.documentElement.getAttribute("id") !== "d2-svg") {
return;
}
const svgEl = document.documentElement;
let width = parseInt(svgEl.getAttribute("width"), 10);
let height = parseInt(svgEl.getAttribute("height"), 10);
let ratio;
if (width > height) {
if (width > window.innerWidth) {
ratio = window.innerWidth / width;
}
} else if (height > window.innerHeight) {
ratio = window.innerHeight / height;
}
if (ratio) {
svgEl.setAttribute("width", width * ratio - 16);
svgEl.setAttribute("height", height * ratio - 16);
}
});
]]></script><g id="a"><g class="shape" ><rect class="shape" x="0" y="0" width="50" height="12" style="fill:#FFFFFF;stroke:#0A0F25;stroke-width:2;"/><rect class="class_header" x="0.000000" y="0.000000" width="50.000000" height="12.000000" fill="#0A0F25" /></g></g><mask id="2389823220" maskUnits="userSpaceOnUse" x="-100" y="-100" width="254" height="216">
<rect x="-100" y="-100" width="254" height="216" fill="white"></rect>
</mask><style type="text/css"><![CDATA[]]></style></svg>

After

Width:  |  Height:  |  Size: 1.6 KiB