Merge branch 'master' into shape-specific-inner-boxes
|
|
@ -228,6 +228,7 @@ let us know and we'll be happy to include it here!
|
||||||
- **Comparison site**: [https://github.com/terrastruct/text-to-diagram-site](https://github.com/terrastruct/text-to-diagram-site)
|
- **Comparison site**: [https://github.com/terrastruct/text-to-diagram-site](https://github.com/terrastruct/text-to-diagram-site)
|
||||||
- **Playground**: [https://github.com/terrastruct/d2-playground](https://github.com/terrastruct/d2-playground)
|
- **Playground**: [https://github.com/terrastruct/d2-playground](https://github.com/terrastruct/d2-playground)
|
||||||
- **Language docs**: [https://github.com/terrastruct/d2-docs](https://github.com/terrastruct/d2-docs)
|
- **Language docs**: [https://github.com/terrastruct/d2-docs](https://github.com/terrastruct/d2-docs)
|
||||||
|
- **Hosted icons**: [https://icons.terrastruct.com](https://icons.terrastruct.com)
|
||||||
|
|
||||||
## FAQ
|
## FAQ
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,9 +1,12 @@
|
||||||
#### Features 🚀
|
#### Features 🚀
|
||||||
|
|
||||||
- `double-circle` keyword implemented. [#565](https://github.com/terrastruct/d2/pull/565)
|
- `double-border` keyword implemented. [#565](https://github.com/terrastruct/d2/pull/565)
|
||||||
- The [Dockerfile](./docs/INSTALL.md#docker) now supports rendering PNGs [#594](https://github.com/terrastruct/d2/issues/594)
|
- The [Dockerfile](./docs/INSTALL.md#docker) now supports rendering PNGs [#594](https://github.com/terrastruct/d2/issues/594)
|
||||||
|
|
||||||
- There was a minor breaking change as part of this where the default working directory of the Dockerfile is now `/home/debian/src` instead of `/root/src` to allow UID remapping with [`fixuid`](https://github.com/boxboat/fixuid).
|
- There was a minor breaking change as part of this where the default working directory of the Dockerfile is now `/home/debian/src` instead of `/root/src` to allow UID remapping with [`fixuid`](https://github.com/boxboat/fixuid).
|
||||||
|
|
||||||
|
- `d2 fmt` accepts multiple files to be formatted [#718](https://github.com/terrastruct/d2/issues/718)
|
||||||
|
|
||||||
#### Improvements 🧹
|
#### Improvements 🧹
|
||||||
|
|
||||||
- Reduces default padding of shapes. [#702](https://github.com/terrastruct/d2/pull/702)
|
- Reduces default padding of shapes. [#702](https://github.com/terrastruct/d2/pull/702)
|
||||||
|
|
@ -14,5 +17,7 @@
|
||||||
|
|
||||||
#### Bugfixes ⛑️
|
#### Bugfixes ⛑️
|
||||||
|
|
||||||
|
- Fixes groups overlapping in sequence diagrams when they end in a self loop. [#728](https://github.com/terrastruct/d2/pull/728)
|
||||||
- Fixed dimensions of unlabeled squares or circles with only a set width or height. [#702](https://github.com/terrastruct/d2/pull/702)
|
- Fixed dimensions of unlabeled squares or circles with only a set width or height. [#702](https://github.com/terrastruct/d2/pull/702)
|
||||||
- Fixed scaling of actor shapes in sequence diagrams. [#702](https://github.com/terrastruct/d2/pull/702)
|
- Fixed scaling of actor shapes in sequence diagrams. [#702](https://github.com/terrastruct/d2/pull/702)
|
||||||
|
- Code snippets use bold and italic font styles as determined by highlighter [#710](https://github.com/terrastruct/d2/issues/710), [#741](https://github.com/terrastruct/d2/issues/741)
|
||||||
|
|
|
||||||
|
|
@ -13,7 +13,7 @@
|
||||||
.Nm d2
|
.Nm d2
|
||||||
.Ar layout Op Ar name
|
.Ar layout Op Ar name
|
||||||
.Nm d2
|
.Nm d2
|
||||||
.Ar fmt Ar file.d2
|
.Ar fmt Ar file.d2 ...
|
||||||
.Sh DESCRIPTION
|
.Sh DESCRIPTION
|
||||||
.Nm
|
.Nm
|
||||||
compiles and renders
|
compiles and renders
|
||||||
|
|
@ -83,10 +83,8 @@ Print version information and exit.
|
||||||
Lists available layout engine options with short help.
|
Lists available layout engine options with short help.
|
||||||
.It Ar layout Op Ar name
|
.It Ar layout Op Ar name
|
||||||
Display long help for a particular layout engine, including its configuration options.
|
Display long help for a particular layout engine, including its configuration options.
|
||||||
.It Ar fmt Ar file.d2
|
.It Ar fmt Ar file.d2 ...
|
||||||
Format
|
Format all passed files.
|
||||||
.Ar file.d2
|
|
||||||
.Ns .
|
|
||||||
.El
|
.El
|
||||||
.Sh SEE ALSO
|
.Sh SEE ALSO
|
||||||
.Xr d2plugin-tala 1
|
.Xr d2plugin-tala 1
|
||||||
|
|
|
||||||
|
|
@ -1074,6 +1074,12 @@ func appendTextDedup(texts []*d2target.MText, t *d2target.MText) []*d2target.MTe
|
||||||
}
|
}
|
||||||
|
|
||||||
func (g *Graph) SetDimensions(mtexts []*d2target.MText, ruler *textmeasure.Ruler, fontFamily *d2fonts.FontFamily) error {
|
func (g *Graph) SetDimensions(mtexts []*d2target.MText, ruler *textmeasure.Ruler, fontFamily *d2fonts.FontFamily) error {
|
||||||
|
if ruler != nil && fontFamily != nil {
|
||||||
|
if ok := ruler.HasFontFamilyLoaded(fontFamily); !ok {
|
||||||
|
return fmt.Errorf("ruler does not have entire font family %s loaded, is a style missing?", *fontFamily)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
for _, obj := range g.Objects {
|
for _, obj := range g.Objects {
|
||||||
obj.Box = &geo.Box{}
|
obj.Box = &geo.Box{}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -97,3 +97,12 @@ func (e *Edge) ContainedBy(obj *Object) bool {
|
||||||
}
|
}
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (e *Edge) GetGroup() *Object {
|
||||||
|
for _, ref := range e.References {
|
||||||
|
if ref.ScopeObj.IsSequenceDiagramGroup() {
|
||||||
|
return ref.ScopeObj
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -451,6 +451,8 @@ func (sd *sequenceDiagram) placeSpans() {
|
||||||
// routeMessages routes horizontal edges (messages) from Src to Dst lifeline (actor/span center)
|
// routeMessages routes horizontal edges (messages) from Src to Dst lifeline (actor/span center)
|
||||||
// in another step, routes are adjusted to spans borders when necessary
|
// in another step, routes are adjusted to spans borders when necessary
|
||||||
func (sd *sequenceDiagram) routeMessages() error {
|
func (sd *sequenceDiagram) routeMessages() error {
|
||||||
|
var prevIsLoop bool
|
||||||
|
var prevGroup *d2graph.Object
|
||||||
messageOffset := sd.maxActorHeight + sd.yStep
|
messageOffset := sd.maxActorHeight + sd.yStep
|
||||||
for _, message := range sd.messages {
|
for _, message := range sd.messages {
|
||||||
message.ZIndex = MESSAGE_Z_INDEX
|
message.ZIndex = MESSAGE_Z_INDEX
|
||||||
|
|
@ -460,6 +462,14 @@ func (sd *sequenceDiagram) routeMessages() error {
|
||||||
noteOffset += note.Height + sd.yStep
|
noteOffset += note.Height + sd.yStep
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// we need extra space if the previous message is a loop in a different group
|
||||||
|
group := message.GetGroup()
|
||||||
|
if prevIsLoop && prevGroup != group {
|
||||||
|
messageOffset += MIN_MESSAGE_DISTANCE
|
||||||
|
}
|
||||||
|
prevGroup = group
|
||||||
|
|
||||||
startY := messageOffset + noteOffset
|
startY := messageOffset + noteOffset
|
||||||
|
|
||||||
var startX, endX float64
|
var startX, endX float64
|
||||||
|
|
@ -496,11 +506,13 @@ func (sd *sequenceDiagram) routeMessages() error {
|
||||||
geo.NewPoint(midX, endY),
|
geo.NewPoint(midX, endY),
|
||||||
geo.NewPoint(endX, endY),
|
geo.NewPoint(endX, endY),
|
||||||
}
|
}
|
||||||
|
prevIsLoop = true
|
||||||
} else {
|
} else {
|
||||||
message.Route = []*geo.Point{
|
message.Route = []*geo.Point{
|
||||||
geo.NewPoint(startX, startY),
|
geo.NewPoint(startX, startY),
|
||||||
geo.NewPoint(endX, startY),
|
geo.NewPoint(endX, startY),
|
||||||
}
|
}
|
||||||
|
prevIsLoop = false
|
||||||
}
|
}
|
||||||
messageOffset += sd.yStep
|
messageOffset += sd.yStep
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -78,6 +78,12 @@ var sourceSansProItalicBase64 string
|
||||||
//go:embed encoded/SourceCodePro-Regular.txt
|
//go:embed encoded/SourceCodePro-Regular.txt
|
||||||
var sourceCodeProRegularBase64 string
|
var sourceCodeProRegularBase64 string
|
||||||
|
|
||||||
|
//go:embed encoded/SourceCodePro-Bold.txt
|
||||||
|
var sourceCodeProBoldBase64 string
|
||||||
|
|
||||||
|
//go:embed encoded/SourceCodePro-Italic.txt
|
||||||
|
var sourceCodeProItalicBase64 string
|
||||||
|
|
||||||
//go:embed encoded/ArchitectsDaughter-Regular.txt
|
//go:embed encoded/ArchitectsDaughter-Regular.txt
|
||||||
var architectsDaughterRegularBase64 string
|
var architectsDaughterRegularBase64 string
|
||||||
|
|
||||||
|
|
@ -108,6 +114,14 @@ func init() {
|
||||||
Family: SourceCodePro,
|
Family: SourceCodePro,
|
||||||
Style: FONT_STYLE_REGULAR,
|
Style: FONT_STYLE_REGULAR,
|
||||||
}: sourceCodeProRegularBase64,
|
}: sourceCodeProRegularBase64,
|
||||||
|
{
|
||||||
|
Family: SourceCodePro,
|
||||||
|
Style: FONT_STYLE_BOLD,
|
||||||
|
}: sourceCodeProBoldBase64,
|
||||||
|
{
|
||||||
|
Family: SourceCodePro,
|
||||||
|
Style: FONT_STYLE_ITALIC,
|
||||||
|
}: sourceCodeProItalicBase64,
|
||||||
{
|
{
|
||||||
Family: HandDrawn,
|
Family: HandDrawn,
|
||||||
Style: FONT_STYLE_REGULAR,
|
Style: FONT_STYLE_REGULAR,
|
||||||
|
|
@ -144,6 +158,22 @@ func init() {
|
||||||
Family: SourceCodePro,
|
Family: SourceCodePro,
|
||||||
Style: FONT_STYLE_REGULAR,
|
Style: FONT_STYLE_REGULAR,
|
||||||
}] = b
|
}] = b
|
||||||
|
b, err = fontFacesFS.ReadFile("ttf/SourceCodePro-Bold.ttf")
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
FontFaces[Font{
|
||||||
|
Family: SourceCodePro,
|
||||||
|
Style: FONT_STYLE_BOLD,
|
||||||
|
}] = b
|
||||||
|
b, err = fontFacesFS.ReadFile("ttf/SourceCodePro-Italic.ttf")
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
FontFaces[Font{
|
||||||
|
Family: SourceCodePro,
|
||||||
|
Style: FONT_STYLE_ITALIC,
|
||||||
|
}] = b
|
||||||
b, err = fontFacesFS.ReadFile("ttf/SourceSansPro-Bold.ttf")
|
b, err = fontFacesFS.ReadFile("ttf/SourceSansPro-Bold.ttf")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
panic(err)
|
panic(err)
|
||||||
|
|
|
||||||
1
d2renderers/d2fonts/encoded/SourceCodePro-Bold.txt
Normal file
1
d2renderers/d2fonts/encoded/SourceCodePro-Italic.txt
Normal file
BIN
d2renderers/d2fonts/ttf/SourceCodePro-Bold.ttf
Normal file
BIN
d2renderers/d2fonts/ttf/SourceCodePro-Italic.ttf
Normal file
|
|
@ -47,7 +47,8 @@ func styleAttr(styles map[chroma.TokenType]string, tt chroma.TokenType) string {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return styles[tt]
|
out := strings.Replace(styles[tt], `font-weight="bold"`, `class="text-mono-bold"`, -1)
|
||||||
|
return strings.Replace(out, `font-style="italic"`, `class="text-mono-italic"`, -1)
|
||||||
}
|
}
|
||||||
|
|
||||||
// <<< END
|
// <<< END
|
||||||
|
|
|
||||||
|
|
@ -1185,6 +1185,44 @@ func embedFonts(buf *bytes.Buffer, fontFamily *d2fonts.FontFamily) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
triggers = []string{
|
||||||
|
`class="text-mono-bold"`,
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, t := range triggers {
|
||||||
|
if strings.Contains(content, t) {
|
||||||
|
fmt.Fprintf(buf, `
|
||||||
|
.text-mono-bold {
|
||||||
|
font-family: "font-mono-bold";
|
||||||
|
}
|
||||||
|
@font-face {
|
||||||
|
font-family: font-mono-bold;
|
||||||
|
src: url("%s");
|
||||||
|
}`,
|
||||||
|
d2fonts.FontEncodings[d2fonts.SourceCodePro.Font(0, d2fonts.FONT_STYLE_BOLD)])
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
triggers = []string{
|
||||||
|
`class="text-mono-italic"`,
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, t := range triggers {
|
||||||
|
if strings.Contains(content, t) {
|
||||||
|
fmt.Fprintf(buf, `
|
||||||
|
.text-mono-italic {
|
||||||
|
font-family: "font-mono-italic";
|
||||||
|
}
|
||||||
|
@font-face {
|
||||||
|
font-family: font-mono-italic;
|
||||||
|
src: url("%s");
|
||||||
|
}`,
|
||||||
|
d2fonts.FontEncodings[d2fonts.SourceCodePro.Font(0, d2fonts.FONT_STYLE_ITALIC)])
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
buf.WriteString(`]]></style>`)
|
buf.WriteString(`]]></style>`)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -394,6 +394,56 @@ x -> a: {
|
||||||
label: You don't have to know how the computer works,\njust how to work the computer.
|
label: You don't have to know how the computer works,\njust how to work the computer.
|
||||||
style.opacity: 0.4
|
style.opacity: 0.4
|
||||||
}
|
}
|
||||||
|
`,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "sequence_diagram_self_edge_group_overlap",
|
||||||
|
script: `
|
||||||
|
shape: sequence_diagram
|
||||||
|
a: A
|
||||||
|
b: B
|
||||||
|
c: C
|
||||||
|
group 1: {
|
||||||
|
a -> a
|
||||||
|
}
|
||||||
|
group 2: {
|
||||||
|
a -> b
|
||||||
|
}
|
||||||
|
group 3: {
|
||||||
|
a -> a.a
|
||||||
|
}
|
||||||
|
group 4: {
|
||||||
|
a.a -> b
|
||||||
|
}
|
||||||
|
group 5: {
|
||||||
|
b -> b
|
||||||
|
b -> b
|
||||||
|
}
|
||||||
|
group 6: {
|
||||||
|
b -> a
|
||||||
|
}
|
||||||
|
group 7: {
|
||||||
|
a -> a
|
||||||
|
}
|
||||||
|
group 8: {
|
||||||
|
a -> a
|
||||||
|
}
|
||||||
|
a -> a
|
||||||
|
group 9: {
|
||||||
|
a -> a
|
||||||
|
}
|
||||||
|
a -> a
|
||||||
|
|
||||||
|
b -> c
|
||||||
|
group 10: {
|
||||||
|
c -> c
|
||||||
|
}
|
||||||
|
b -> c
|
||||||
|
group 11: {
|
||||||
|
c -> c
|
||||||
|
}
|
||||||
|
b -> c
|
||||||
|
|
||||||
`,
|
`,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
Before Width: | Height: | Size: 185 KiB After Width: | Height: | Size: 350 KiB |
|
Before Width: | Height: | Size: 185 KiB After Width: | Height: | Size: 350 KiB |
1490
e2etests/testdata/regression/sequence_diagram_self_edge_group_overlap/dagre/board.exp.json
generated
vendored
Normal file
62
e2etests/testdata/regression/sequence_diagram_self_edge_group_overlap/dagre/sketch.exp.svg
vendored
Normal file
|
After Width: | Height: | Size: 336 KiB |
1490
e2etests/testdata/regression/sequence_diagram_self_edge_group_overlap/elk/board.exp.json
generated
vendored
Normal file
62
e2etests/testdata/regression/sequence_diagram_self_edge_group_overlap/elk/sketch.exp.svg
vendored
Normal file
|
After Width: | Height: | Size: 336 KiB |
|
Before Width: | Height: | Size: 512 KiB After Width: | Height: | Size: 601 KiB |
|
Before Width: | Height: | Size: 512 KiB After Width: | Height: | Size: 601 KiB |
|
Before Width: | Height: | Size: 508 KiB After Width: | Height: | Size: 673 KiB |
|
Before Width: | Height: | Size: 508 KiB After Width: | Height: | Size: 673 KiB |
|
Before Width: | Height: | Size: 668 KiB After Width: | Height: | Size: 757 KiB |
|
Before Width: | Height: | Size: 668 KiB After Width: | Height: | Size: 757 KiB |
|
Before Width: | Height: | Size: 513 KiB After Width: | Height: | Size: 602 KiB |
|
Before Width: | Height: | Size: 513 KiB After Width: | Height: | Size: 602 KiB |
|
Before Width: | Height: | Size: 513 KiB After Width: | Height: | Size: 602 KiB |
|
Before Width: | Height: | Size: 513 KiB After Width: | Height: | Size: 602 KiB |
|
Before Width: | Height: | Size: 842 KiB After Width: | Height: | Size: 931 KiB |
|
Before Width: | Height: | Size: 842 KiB After Width: | Height: | Size: 931 KiB |
8
fmt.go
|
|
@ -17,12 +17,10 @@ func fmtCmd(ctx context.Context, ms *xmain.State) (err error) {
|
||||||
|
|
||||||
ms.Opts = xmain.NewOpts(ms.Env, ms.Log, ms.Opts.Flags.Args()[1:])
|
ms.Opts = xmain.NewOpts(ms.Env, ms.Log, ms.Opts.Flags.Args()[1:])
|
||||||
if len(ms.Opts.Args) == 0 {
|
if len(ms.Opts.Args) == 0 {
|
||||||
return xmain.UsageErrorf("fmt must be passed the file to be formatted")
|
return xmain.UsageErrorf("fmt must be passed at least one file to be formatted")
|
||||||
} else if len(ms.Opts.Args) > 1 {
|
|
||||||
return xmain.UsageErrorf("fmt accepts only one argument for the file to be formatted")
|
|
||||||
}
|
}
|
||||||
|
|
||||||
inputPath := ms.Opts.Args[0]
|
for _, inputPath := range ms.Opts.Args {
|
||||||
input, err := ms.ReadPath(inputPath)
|
input, err := ms.ReadPath(inputPath)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
|
|
@ -37,6 +35,6 @@ func fmtCmd(ctx context.Context, ms *xmain.State) (err error) {
|
||||||
if !bytes.Equal(output, input) {
|
if !bytes.Equal(output, input) {
|
||||||
return ms.WritePath(inputPath, output)
|
return ms.WritePath(inputPath, output)
|
||||||
}
|
}
|
||||||
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
|
||||||
4
help.go
|
|
@ -18,7 +18,7 @@ func help(ms *xmain.State) {
|
||||||
fmt.Fprintf(ms.Stdout, `Usage:
|
fmt.Fprintf(ms.Stdout, `Usage:
|
||||||
%[1]s [--watch=false] [--theme=0] file.d2 [file.svg | file.png]
|
%[1]s [--watch=false] [--theme=0] file.d2 [file.svg | file.png]
|
||||||
%[1]s layout [name]
|
%[1]s layout [name]
|
||||||
%[1]s fmt file.d2
|
%[1]s fmt file.d2 ...
|
||||||
|
|
||||||
%[1]s compiles and renders file.d2 to file.svg | file.png
|
%[1]s compiles and renders file.d2 to file.svg | file.png
|
||||||
It defaults to file.svg if an output path is not provided.
|
It defaults to file.svg if an output path is not provided.
|
||||||
|
|
@ -33,7 +33,7 @@ Flags:
|
||||||
Subcommands:
|
Subcommands:
|
||||||
%[1]s layout - Lists available layout engine options with short help
|
%[1]s layout - Lists available layout engine options with short help
|
||||||
%[1]s layout [name] - Display long help for a particular layout engine, including its configuration options
|
%[1]s layout [name] - Display long help for a particular layout engine, including its configuration options
|
||||||
%[1]s fmt file.d2 - Format file.d2
|
%[1]s fmt file.d2 ... - Format passed files
|
||||||
|
|
||||||
See more docs and the source code at https://oss.terrastruct.com/d2
|
See more docs and the source code at https://oss.terrastruct.com/d2
|
||||||
`, filepath.Base(ms.Name), ms.Opts.Defaults())
|
`, filepath.Base(ms.Name), ms.Opts.Defaults())
|
||||||
|
|
|
||||||
|
|
@ -15,6 +15,7 @@ import (
|
||||||
)
|
)
|
||||||
|
|
||||||
const TAB_SIZE = 4
|
const TAB_SIZE = 4
|
||||||
|
const SIZELESS_FONT_SIZE = 0
|
||||||
|
|
||||||
// ASCII is a set of all ASCII runes. These runes are codepoints from 32 to 127 inclusive.
|
// ASCII is a set of all ASCII runes. These runes are codepoints from 32 to 127 inclusive.
|
||||||
var ASCII []rune
|
var ASCII []rune
|
||||||
|
|
@ -135,9 +136,25 @@ func NewRuler() (*Ruler, error) {
|
||||||
return r, nil
|
return r, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (r *Ruler) HasFontFamilyLoaded(fontFamily *d2fonts.FontFamily) bool {
|
||||||
|
for _, fontStyle := range d2fonts.FontStyles {
|
||||||
|
font := d2fonts.Font{
|
||||||
|
Family: *fontFamily,
|
||||||
|
Style: fontStyle,
|
||||||
|
Size: SIZELESS_FONT_SIZE,
|
||||||
|
}
|
||||||
|
_, ok := r.ttfs[font]
|
||||||
|
if !ok {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
func (r *Ruler) addFontSize(font d2fonts.Font) {
|
func (r *Ruler) addFontSize(font d2fonts.Font) {
|
||||||
sizeless := font
|
sizeless := font
|
||||||
sizeless.Size = 0
|
sizeless.Size = SIZELESS_FONT_SIZE
|
||||||
face := truetype.NewFace(r.ttfs[sizeless], &truetype.Options{
|
face := truetype.NewFace(r.ttfs[sizeless], &truetype.Options{
|
||||||
Size: float64(font.Size),
|
Size: float64(font.Size),
|
||||||
})
|
})
|
||||||
|
|
|
||||||