From d45299c45dbbaba6bd6adb91332127c217afbabf Mon Sep 17 00:00:00 2001 From: Julius Krumbiegel Date: Sat, 8 Oct 2022 19:16:36 +0200 Subject: [PATCH 01/40] add first implementation of rich text via tree of nodes --- src/basic_recipes/text.jl | 134 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 134 insertions(+) diff --git a/src/basic_recipes/text.jl b/src/basic_recipes/text.jl index 0644ca2c56c..37fddcd9aeb 100644 --- a/src/basic_recipes/text.jl +++ b/src/basic_recipes/text.jl @@ -257,3 +257,137 @@ function texelems_and_glyph_collection(str::LaTeXString, fontscale_px, halign, v end iswhitespace(l::LaTeXString) = iswhitespace(replace(l.s, '$' => "")) + + + + + + +struct RTFNode4 <: AbstractString + type::Symbol + children::Vector{Union{RTFNode4,String}} + attributes::Dict{Symbol, Any} + function RTFNode4(type, children...; kwargs...) + cs = Union{RTFNode4,String}[children...] + typeof(cs) + new(type, cs, Dict(kwargs)) + end +end + +## +function Makie._get_glyphcollection_and_linesegments(rtf::RTFNode4, index, ts, f, al, rot, jus, lh, col, scol, swi, www) + gc = Makie.layout_text(rtf, ts, f, al, rot, jus, lh) + gc, Point2f[], Float32[], Makie.RGBAf[], Int[] +end + +struct GlyphState2 + x::Float32 + baseline::Float32 + size::Vec2f + font::Makie.FreeTypeAbstraction.FTFont + color::RGBAf +end + +struct GlyphInfo2 + glyph::Int + font::Makie.FreeTypeAbstraction.FTFont + origin::Point2f + extent::Makie.GlyphExtent + size::Vec2f + rotation::Makie.Quaternion + color::RGBAf + strokecolor::RGBAf + strokewidth::Float32 +end + +function Makie.GlyphCollection(v::Vector{GlyphInfo2}) + Makie.GlyphCollection( + [i.glyph for i in v], + [i.font for i in v], + [Point3f(i.origin..., 0) for i in v], + [i.extent for i in v], + [i.size for i in v], + [i.rotation for i in v], + [i.color for i in v], + [i.strokecolor for i in v], + [i.strokewidth for i in v], + ) +end + + +function Makie.layout_text(rtf::RTFNode4, ts, f, al, rot, jus, lh) + + stack = [GlyphState2(0, 0, Vec2f(ts), f, RGBAf(0, 0, 0, 1))] + + lines = [GlyphInfo2[]] + + process_rtf_node!(stack, lines, rtf) + + Makie.GlyphCollection(reduce(vcat, lines)) +end + +function process_rtf_node!(stack, lines, rtf::RTFNode4) + push!(stack, new_glyphstate(stack[end], rtf, Val(rtf.type))) + for c in rtf.children + process_rtf_node!(stack, lines, c) + end + gs = pop!(stack) + gs_top = stack[end] + # x needs to continue even if going a level up + stack[end] = GlyphState2(gs.x, gs_top.baseline, gs_top.size, gs_top.font, gs_top.color) + return +end + +function process_rtf_node!(stack, lines, s::String) + gs = stack[end] + y = gs.baseline + x = gs.x + for char in s + gi = Makie.FreeTypeAbstraction.glyph_index(gs.font, char) + gext = Makie.GlyphExtent(gs.font, char) + ori = Point2f(x, y) + push!(lines[end], GlyphInfo2( + gi, + gs.font, + ori, + gext, + gs.size, + Makie.to_rotation(0), + gs.color, + RGBAf(0, 0, 0, 0), + 0f0, + )) + x = x + gext.hadvance * gs.size[1] + end + stack[end] = GlyphState2(x, y, gs.size, gs.font, gs.color) + return +end + +function new_glyphstate(gs::GlyphState2, rtf::RTFNode4, val::Val) + gs +end + +_get_color(attributes, default)::RGBAf = haskey(attributes, :color) ? Makie.to_color(attributes[:color]) : default + +function new_glyphstate(gs::GlyphState2, rtf::RTFNode4, val::Val{:sup}) + att = rtf.attributes + GlyphState2( + gs.x, + gs.baseline + 0.4 * gs.size[2], + gs.size * 0.6, + gs.font, + _get_color(att, gs.color), + ) +end + +function new_glyphstate(gs::GlyphState2, rtf::RTFNode4, val::Val{:sub}) + GlyphState2( + gs.x, + gs.baseline - 0.1 * gs.size[2], + gs.size * 0.6, + gs.font, + gs.color, + ) +end + +Makie.iswhitespace(::RTFNode4) = false \ No newline at end of file From f3694b1618e70f4dd0489ce3bab518304d948667 Mon Sep 17 00:00:00 2001 From: Julius Krumbiegel Date: Sun, 9 Oct 2022 16:18:27 +0200 Subject: [PATCH 02/40] add lineheight, alignment, justification --- Project.toml | 3 +- src/Makie.jl | 1 + src/basic_recipes/text.jl | 116 +++++++++++++++++++++++++++++++++----- 3 files changed, 104 insertions(+), 16 deletions(-) diff --git a/Project.toml b/Project.toml index a2d79a26043..b4039ba4188 100644 --- a/Project.toml +++ b/Project.toml @@ -42,6 +42,7 @@ Printf = "de0858da-6303-5e67-8744-51eddeeeb8d7" Random = "9a3f8284-a2c9-5f02-9a11-845980a1fd5c" RelocatableFolders = "05181044-ff0b-4ac5-8273-598c1e38db00" Serialization = "9e88b42a-f829-5b0c-bbe9-9e923198166b" +Setfield = "efcf1570-3423-57d1-acb7-fd33fddbac46" Showoff = "992d4aef-0814-514b-bc4d-f2e9a6c4116f" SignedDistanceFields = "73760f76-fbc4-59ce-8f25-708e95d2df96" SnoopPrecompile = "66db9d55-30c0-4569-8b51-7e840670fc0c" @@ -77,8 +78,8 @@ KernelDensity = "0.5, 0.6" LaTeXStrings = "1.2" MakieCore = "=0.4.0" Match = "1.1" -MiniQhull = "0.4" MathTeXEngine = "0.5" +MiniQhull = "0.4" Observables = "0.5.1" OffsetArrays = "1" Packing = "0.4" diff --git a/src/Makie.jl b/src/Makie.jl index cc8b03be7a5..a8526c54f37 100644 --- a/src/Makie.jl +++ b/src/Makie.jl @@ -50,6 +50,7 @@ import FileIO import SparseArrays import TriplotBase import MiniQhull +import Setfield using IntervalSets: IntervalSets, (..), OpenInterval, ClosedInterval, AbstractInterval, Interval, endpoints using FixedPointNumbers: N0f8 diff --git a/src/basic_recipes/text.jl b/src/basic_recipes/text.jl index 205b97dc96a..2497227d053 100644 --- a/src/basic_recipes/text.jl +++ b/src/basic_recipes/text.jl @@ -324,9 +324,90 @@ function Makie.layout_text(rtf::RTFNode4, ts, f, al, rot, jus, lh) process_rtf_node!(stack, lines, rtf) + apply_lineheight!(lines, lh) + apply_alignment_and_justification!(lines, jus, al) + Makie.GlyphCollection(reduce(vcat, lines)) end +function apply_lineheight!(lines, lh) + for (i, line) in enumerate(lines) + for j in eachindex(line) + l = line[j] + l = Setfield.@set l.origin[2] -= (i-1) * 20 # TODO: Lineheight + line[j] = l + end + end + return +end + +function apply_alignment_and_justification!(lines, ju, al) + max_xs = map(lines) do line + maximum(line, init = 0f0) do ginfo + ginfo.origin[1] + ginfo.extent.hadvance * ginfo.size[1] + end + end + max_x = maximum(max_xs) + + top_y = maximum(lines[1]) do ginfo + ginfo.origin[2] + ginfo.extent.ascender * ginfo.size[2] + end + bottom_y = minimum(lines[end]) do ginfo + ginfo.origin[2] + ginfo.extent.descender * ginfo.size[2] + end + + al_offset_x = if al[1] == :center + max_x / 2 + else + 0 + end + + al_offset_y = if al[2] == :center + 0.5 * (top_y - bottom_y) + elseif al[2] == :bottom + bottom_y + elseif al[2] == :top + top_y + else + 0f0 + end + + fju = float_justification(ju, al) + + for (i, line) in enumerate(lines) + ju_offset = fju * (max_x - max_xs[i]) + for j in eachindex(line) + l = line[j] + l = Setfield.@set l.origin -= Point2f(al_offset_x - ju_offset, al_offset_y) + line[j] = l + end + end + return +end + +function float_justification(ju, al)::Float32 + halign = al[1] + float_justification = if ju === automatic + if halign == :left || halign == 0 + 0.0f0 + elseif halign == :right || halign == 1 + 1.0f0 + elseif halign == :center || halign == 0.5 + 0.5f0 + else + 0.5f0 + end + elseif ju == :left + 0.0f0 + elseif ju == :right + 1.0f0 + elseif ju == :center + 0.5f0 + else + Float32(ju) + end +end + function process_rtf_node!(stack, lines, rtf::RTFNode4) push!(stack, new_glyphstate(stack[end], rtf, Val(rtf.type))) for c in rtf.children @@ -344,21 +425,26 @@ function process_rtf_node!(stack, lines, s::String) y = gs.baseline x = gs.x for char in s - gi = Makie.FreeTypeAbstraction.glyph_index(gs.font, char) - gext = Makie.GlyphExtent(gs.font, char) - ori = Point2f(x, y) - push!(lines[end], GlyphInfo2( - gi, - gs.font, - ori, - gext, - gs.size, - Makie.to_rotation(0), - gs.color, - RGBAf(0, 0, 0, 0), - 0f0, - )) - x = x + gext.hadvance * gs.size[1] + if char === '\n' + x = 0 + push!(lines, GlyphInfo2[]) + else + gi = Makie.FreeTypeAbstraction.glyph_index(gs.font, char) + gext = Makie.GlyphExtent(gs.font, char) + ori = Point2f(x, y) + push!(lines[end], GlyphInfo2( + gi, + gs.font, + ori, + gext, + gs.size, + Makie.to_rotation(0), + gs.color, + RGBAf(0, 0, 0, 0), + 0f0, + )) + x = x + gext.hadvance * gs.size[1] + end end stack[end] = GlyphState2(x, y, gs.size, gs.font, gs.color) return From 68a6325569e4361ebdc72ec43ccbd634ce47c269 Mon Sep 17 00:00:00 2001 From: Julius Krumbiegel Date: Tue, 11 Oct 2022 10:16:55 +0200 Subject: [PATCH 03/40] rename RTFNode to RT --- src/basic_recipes/text.jl | 44 +++++++++++++++++++++------------------ 1 file changed, 24 insertions(+), 20 deletions(-) diff --git a/src/basic_recipes/text.jl b/src/basic_recipes/text.jl index 2497227d053..1cece02777a 100644 --- a/src/basic_recipes/text.jl +++ b/src/basic_recipes/text.jl @@ -264,20 +264,24 @@ iswhitespace(l::LaTeXString) = iswhitespace(replace(l.s, '$' => "")) -struct RTFNode4 <: AbstractString +struct RT <: AbstractString type::Symbol - children::Vector{Union{RTFNode4,String}} + children::Vector{Union{RT,String}} attributes::Dict{Symbol, Any} - function RTFNode4(type, children...; kwargs...) - cs = Union{RTFNode4,String}[children...] + function RT(type::Symbol, children...; kwargs...) + cs = Union{RT,String}[children...] typeof(cs) new(type, cs, Dict(kwargs)) end end +RT(args...; kwargs...) = RT(:span, args...; kwargs...) + +export RT + ## -function Makie._get_glyphcollection_and_linesegments(rtf::RTFNode4, index, ts, f, al, rot, jus, lh, col, scol, swi, www) - gc = Makie.layout_text(rtf, ts, f, al, rot, jus, lh) +function Makie._get_glyphcollection_and_linesegments(rt::RT, index, ts, f, al, rot, jus, lh, col, scol, swi, www) + gc = Makie.layout_text(rt, ts, f, al, rot, jus, lh) gc, Point2f[], Float32[], Makie.RGBAf[], Int[] end @@ -316,13 +320,13 @@ function Makie.GlyphCollection(v::Vector{GlyphInfo2}) end -function Makie.layout_text(rtf::RTFNode4, ts, f, al, rot, jus, lh) +function Makie.layout_text(rt::RT, ts, f, al, rot, jus, lh) stack = [GlyphState2(0, 0, Vec2f(ts), f, RGBAf(0, 0, 0, 1))] lines = [GlyphInfo2[]] - process_rtf_node!(stack, lines, rtf) + process_rt_node!(stack, lines, rt) apply_lineheight!(lines, lh) apply_alignment_and_justification!(lines, jus, al) @@ -408,10 +412,10 @@ function float_justification(ju, al)::Float32 end end -function process_rtf_node!(stack, lines, rtf::RTFNode4) - push!(stack, new_glyphstate(stack[end], rtf, Val(rtf.type))) - for c in rtf.children - process_rtf_node!(stack, lines, c) +function process_rt_node!(stack, lines, rt::RT) + push!(stack, new_glyphstate(stack[end], rt, Val(rt.type))) + for c in rt.children + process_rt_node!(stack, lines, c) end gs = pop!(stack) gs_top = stack[end] @@ -420,7 +424,7 @@ function process_rtf_node!(stack, lines, rtf::RTFNode4) return end -function process_rtf_node!(stack, lines, s::String) +function process_rt_node!(stack, lines, s::String) gs = stack[end] y = gs.baseline x = gs.x @@ -450,31 +454,31 @@ function process_rtf_node!(stack, lines, s::String) return end -function new_glyphstate(gs::GlyphState2, rtf::RTFNode4, val::Val) +function new_glyphstate(gs::GlyphState2, rt::RT, val::Val) gs end _get_color(attributes, default)::RGBAf = haskey(attributes, :color) ? Makie.to_color(attributes[:color]) : default -function new_glyphstate(gs::GlyphState2, rtf::RTFNode4, val::Val{:sup}) - att = rtf.attributes +function new_glyphstate(gs::GlyphState2, rt::RT, val::Val{:sup}) + att = rt.attributes GlyphState2( gs.x, gs.baseline + 0.4 * gs.size[2], - gs.size * 0.6, + gs.size * 0.66, gs.font, _get_color(att, gs.color), ) end -function new_glyphstate(gs::GlyphState2, rtf::RTFNode4, val::Val{:sub}) +function new_glyphstate(gs::GlyphState2, rt::RT, val::Val{:sub}) GlyphState2( gs.x, gs.baseline - 0.1 * gs.size[2], - gs.size * 0.6, + gs.size * 0.66, gs.font, gs.color, ) end -Makie.iswhitespace(::RTFNode4) = false \ No newline at end of file +Makie.iswhitespace(::RT) = false \ No newline at end of file From 5af625b2083ae1b6dd5b3d09fb98dbe73feb4b50 Mon Sep 17 00:00:00 2001 From: Julius Krumbiegel Date: Tue, 11 Oct 2022 10:33:50 +0200 Subject: [PATCH 04/40] add :span as default, feed in color --- src/basic_recipes/text.jl | 20 ++++++++++++++++---- 1 file changed, 16 insertions(+), 4 deletions(-) diff --git a/src/basic_recipes/text.jl b/src/basic_recipes/text.jl index 1cece02777a..89ccb519a0d 100644 --- a/src/basic_recipes/text.jl +++ b/src/basic_recipes/text.jl @@ -281,7 +281,7 @@ export RT ## function Makie._get_glyphcollection_and_linesegments(rt::RT, index, ts, f, al, rot, jus, lh, col, scol, swi, www) - gc = Makie.layout_text(rt, ts, f, al, rot, jus, lh) + gc = Makie.layout_text(rt, ts, f, al, rot, jus, lh, col) gc, Point2f[], Float32[], Makie.RGBAf[], Int[] end @@ -320,9 +320,9 @@ function Makie.GlyphCollection(v::Vector{GlyphInfo2}) end -function Makie.layout_text(rt::RT, ts, f, al, rot, jus, lh) +function Makie.layout_text(rt::RT, ts, f, al, rot, jus, lh, col) - stack = [GlyphState2(0, 0, Vec2f(ts), f, RGBAf(0, 0, 0, 1))] + stack = [GlyphState2(0, 0, Vec2f(ts), f, Makie.to_color(col))] lines = [GlyphInfo2[]] @@ -471,13 +471,25 @@ function new_glyphstate(gs::GlyphState2, rt::RT, val::Val{:sup}) ) end +function new_glyphstate(gs::GlyphState2, rt::RT, val::Val{:span}) + att = rt.attributes + GlyphState2( + gs.x, + gs.baseline, + gs.size, + gs.font, + _get_color(att, gs.color), + ) +end + function new_glyphstate(gs::GlyphState2, rt::RT, val::Val{:sub}) + att = rt.attributes GlyphState2( gs.x, gs.baseline - 0.1 * gs.size[2], gs.size * 0.66, gs.font, - gs.color, + _get_color(att, gs.color), ) end From 55cac851dacc699c7ec5404b06c6d1f53369badd Mon Sep 17 00:00:00 2001 From: Julius Krumbiegel Date: Tue, 11 Oct 2022 11:53:09 +0200 Subject: [PATCH 05/40] use span, superscript, subscript --- src/basic_recipes/text.jl | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/src/basic_recipes/text.jl b/src/basic_recipes/text.jl index 89ccb519a0d..9eb6e9e529c 100644 --- a/src/basic_recipes/text.jl +++ b/src/basic_recipes/text.jl @@ -275,9 +275,11 @@ struct RT <: AbstractString end end -RT(args...; kwargs...) = RT(:span, args...; kwargs...) +span(args...; kwargs...) = RT(:span, args...; kwargs...) +subscript(args...; kwargs...) = RT(:sub, args...; kwargs...) +superscript(args...; kwargs...) = RT(:sup, args...; kwargs...) -export RT +export span, subscript, superscript ## function Makie._get_glyphcollection_and_linesegments(rt::RT, index, ts, f, al, rot, jus, lh, col, scol, swi, www) @@ -486,7 +488,7 @@ function new_glyphstate(gs::GlyphState2, rt::RT, val::Val{:sub}) att = rt.attributes GlyphState2( gs.x, - gs.baseline - 0.1 * gs.size[2], + gs.baseline - 0.15 * gs.size[2], gs.size * 0.66, gs.font, _get_color(att, gs.color), From 2d87ebdef32f8b9ae888b5370b7fe6d38ad7b8e9 Mon Sep 17 00:00:00 2001 From: Julius Krumbiegel Date: Tue, 11 Oct 2022 12:13:26 +0200 Subject: [PATCH 06/40] allow stacking super and subscript --- src/basic_recipes/text.jl | 28 ++++++++++++++++++++++++++-- 1 file changed, 26 insertions(+), 2 deletions(-) diff --git a/src/basic_recipes/text.jl b/src/basic_recipes/text.jl index 9eb6e9e529c..eb093cce28b 100644 --- a/src/basic_recipes/text.jl +++ b/src/basic_recipes/text.jl @@ -415,9 +415,33 @@ function float_justification(ju, al)::Float32 end function process_rt_node!(stack, lines, rt::RT) + _type(x) = nothing + _type(r::RT) = r.type + push!(stack, new_glyphstate(stack[end], rt, Val(rt.type))) - for c in rt.children - process_rt_node!(stack, lines, c) + sup_x = 0f0 + for (i, c) in enumerate(rt.children) + if _type(c) == :sup + sup_x = stack[end].x + end + # This special implementation allows to stack super and subscripts. + # In the naive implementation, x can only grow with each character, + # however, to stack super and subscript, we need to track back to the + # previous x value and afterwards continue with the maximum of super + # and subscript. + if i > 1 && _type(c) === :sub && _type(rt.children[i-1]) == :sup + gs = stack[end] + sup_x_end = gs.x + gs_modified = Setfield.@set gs.x = sup_x + stack[end] = gs_modified + process_rt_node!(stack, lines, c) + gs = stack[end] + max_x = max(sup_x_end, gs.x) + gs_max_x = Setfield.@set gs.x = max_x + stack[end] = gs_max_x + else + process_rt_node!(stack, lines, c) + end end gs = pop!(stack) gs_top = stack[end] From 566c2669a76d03e89d9cdda004c4f6b3cbffa130 Mon Sep 17 00:00:00 2001 From: Julius Krumbiegel Date: Tue, 11 Oct 2022 12:55:11 +0200 Subject: [PATCH 07/40] add fontset type --- src/theming.jl | 25 +++++++++++++++++++++++++ 1 file changed, 25 insertions(+) diff --git a/src/theming.jl b/src/theming.jl index 27fc6bec109..ddff004c7af 100644 --- a/src/theming.jl +++ b/src/theming.jl @@ -53,6 +53,31 @@ const default_palettes = Attributes( side = [:left, :right] ) +struct FontSet5{N <: NamedTuple} + fonts::N + function FontSet5(; kwargs...) + d = Dict([key => Makie.to_font(value) for (key, value) in kwargs]) + if !haskey(d, :regular) + d[:regular] = Makie.to_font("TeX Gyre Heros Makie") + end + if !haskey(d, :bold) + d[:bold] = Makie.to_font("TeX Gyre Heros Makie Bold") + end + if !haskey(d, :italic) + d[:italic] = Makie.to_font("TeX Gyre Heros Makie Italic") + end + nt = NamedTuple(d) + return new{typeof(nt)}(nt) + end +end + +function Base.show(io::IO, f::FontSet5) + println(io, "FontSet") + for (key, value) in pairs(f.fonts) + println(io, " ", key, ": ", value) + end +end + const minimal_default = Attributes( palette = default_palettes, font = "TeX Gyre Heros Makie", From ab0b0c62efdae8f675d89995a4b92077a38480a2 Mon Sep 17 00:00:00 2001 From: Julius Krumbiegel Date: Tue, 11 Oct 2022 20:14:14 +0200 Subject: [PATCH 08/40] add fontset and first docs entry --- MakieCore/src/basic_plots.jl | 1 + docs/examples/plotting_functions/text.md | 34 ++++++++++ src/basic_recipes/text.jl | 79 +++++++++++++----------- src/conversions.jl | 6 +- src/layouting/layouting.jl | 2 +- src/theming.jl | 15 ++++- 6 files changed, 96 insertions(+), 41 deletions(-) diff --git a/MakieCore/src/basic_plots.jl b/MakieCore/src/basic_plots.jl index 45e32185162..1f29754bc84 100644 --- a/MakieCore/src/basic_plots.jl +++ b/MakieCore/src/basic_plots.jl @@ -517,6 +517,7 @@ Plots one or multiple texts passed via the `text` keyword. default_theme(scene)..., color = theme(scene, :textcolor), font = theme(scene, :font), + fontset = theme(scene, :fontset), strokecolor = (:black, 0.0), strokewidth = 0, align = (:left, :bottom), diff --git a/docs/examples/plotting_functions/text.md b/docs/examples/plotting_functions/text.md index b90a6b17e8c..d1fe3662d9a 100644 --- a/docs/examples/plotting_functions/text.md +++ b/docs/examples/plotting_functions/text.md @@ -199,3 +199,37 @@ Legend(f[1, 2], ax) f ``` \end{examplefigure} + +## Formatted text + +With formatted text, you can build text using different colors or fonts, as well as subscripts and superscripts. +You can build formatted text using the functions `formatted`, `superscript` and `subscript`. + +\begin{examplefigure}{svg = true} +```julia +using CairoMakie +CairoMakie.activate!() # hide +Makie.inline!(true) # hide + +f = Figure(fontsize = 30) +Label( + f[1, 1], + formatted( + "H", subscript("2"), "O is the formula for ", + formatted("water", color = :blue, font = :italic) + ) +) + +str = "A beautiful rainbow" +rainbow = cgrad(:rainbow, length(str), categorical = true) + +Label( + f[2, 1], + formatted( + [formatted("$c", color = rainbow[i]) for (i, c) in enumerate(str)]... + ) +) + +f +``` +\end{examplefigure} diff --git a/src/basic_recipes/text.jl b/src/basic_recipes/text.jl index eb093cce28b..37af0a963d3 100644 --- a/src/basic_recipes/text.jl +++ b/src/basic_recipes/text.jl @@ -7,12 +7,12 @@ function plot!(plot::Text) linecolors = Observable(RGBAf[]) lineindices = Ref(Int[]) - onany(plot.text, plot.textsize, plot.font, plot.align, + onany(plot.text, plot.textsize, plot.font, plot.fontset, plot.align, plot.rotation, plot.justification, plot.lineheight, plot.color, plot.strokecolor, plot.strokewidth, plot.word_wrap_width) do str, - ts, f, al, rot, jus, lh, col, scol, swi, www + ts, f, fs, al, rot, jus, lh, col, scol, swi, www ts = to_textsize(ts) - f = to_font(f) + f = to_font(fs, f) rot = to_rotation(rot) col = to_color(col) scol = to_color(scol) @@ -36,12 +36,12 @@ function plot!(plot::Text) # as per string. broadcast_foreach( func, - str, 1:attr_broadcast_length(str), ts, f, al, rot, jus, lh, col, scol, swi, www + str, 1:attr_broadcast_length(str), ts, f, fs, al, rot, jus, lh, col, scol, swi, www ) else # Otherwise Vector arguments are interpreted by layout_text/ # glyph_collection as per character. - func(str, 1, ts, f, al, rot, jus, lh, col, scol, swi, www) + func(str, 1, ts, f, fs, al, rot, jus, lh, col, scol, swi, www) end glyphcollections[] = gcs linewidths[] = lwidths @@ -76,11 +76,11 @@ function plot!(plot::Text) plot end -function _get_glyphcollection_and_linesegments(str::AbstractString, index, ts, f, al, rot, jus, lh, col, scol, swi, www) - gc = layout_text(string(str), ts, f, al, rot, jus, lh, col, scol, swi, www) +function _get_glyphcollection_and_linesegments(str::AbstractString, index, ts, f, fs, al, rot, jus, lh, col, scol, swi, www) + gc = layout_text(string(str), ts, f, fs, al, rot, jus, lh, col, scol, swi, www) gc, Point2f[], Float32[], RGBAf[], Int[] end -function _get_glyphcollection_and_linesegments(latexstring::LaTeXString, index, ts, f, al, rot, jus, lh, col, scol, swi, www) +function _get_glyphcollection_and_linesegments(latexstring::LaTeXString, index, ts, f, fs, al, rot, jus, lh, col, scol, swi, www) tex_elements, glyphcollections, offset = texelems_and_glyph_collection(latexstring, ts, al[1], al[2], rot, col, scol, swi, www) @@ -264,26 +264,26 @@ iswhitespace(l::LaTeXString) = iswhitespace(replace(l.s, '$' => "")) -struct RT <: AbstractString +struct FormattedText <: AbstractString type::Symbol - children::Vector{Union{RT,String}} + children::Vector{Union{FormattedText,String}} attributes::Dict{Symbol, Any} - function RT(type::Symbol, children...; kwargs...) - cs = Union{RT,String}[children...] + function FormattedText(type::Symbol, children...; kwargs...) + cs = Union{FormattedText,String}[children...] typeof(cs) new(type, cs, Dict(kwargs)) end end -span(args...; kwargs...) = RT(:span, args...; kwargs...) -subscript(args...; kwargs...) = RT(:sub, args...; kwargs...) -superscript(args...; kwargs...) = RT(:sup, args...; kwargs...) +formatted(args...; kwargs...) = FormattedText(:span, args...; kwargs...) +subscript(args...; kwargs...) = FormattedText(:sub, args...; kwargs...) +superscript(args...; kwargs...) = FormattedText(:sup, args...; kwargs...) -export span, subscript, superscript +export formatted, subscript, superscript ## -function Makie._get_glyphcollection_and_linesegments(rt::RT, index, ts, f, al, rot, jus, lh, col, scol, swi, www) - gc = Makie.layout_text(rt, ts, f, al, rot, jus, lh, col) +function Makie._get_glyphcollection_and_linesegments(rt::FormattedText, index, ts, f, fset, al, rot, jus, lh, col, scol, swi, www) + gc = Makie.layout_text(rt, ts, f, fset, al, rot, jus, lh, col) gc, Point2f[], Float32[], Makie.RGBAf[], Int[] end @@ -322,13 +322,15 @@ function Makie.GlyphCollection(v::Vector{GlyphInfo2}) end -function Makie.layout_text(rt::RT, ts, f, al, rot, jus, lh, col) +function Makie.layout_text(rt::FormattedText, ts, f, fset, al, rot, jus, lh, col) - stack = [GlyphState2(0, 0, Vec2f(ts), f, Makie.to_color(col))] + _f = to_font(fset, f) + + stack = [GlyphState2(0, 0, Vec2f(ts), _f, Makie.to_color(col))] lines = [GlyphInfo2[]] - process_rt_node!(stack, lines, rt) + process_rt_node!(stack, lines, rt, fset) apply_lineheight!(lines, lh) apply_alignment_and_justification!(lines, jus, al) @@ -364,8 +366,12 @@ function apply_alignment_and_justification!(lines, ju, al) al_offset_x = if al[1] == :center max_x / 2 + elseif al[1] == :left + 0f0 + elseif al[1] == :right + max_x else - 0 + 0f0 end al_offset_y = if al[2] == :center @@ -414,11 +420,11 @@ function float_justification(ju, al)::Float32 end end -function process_rt_node!(stack, lines, rt::RT) +function process_rt_node!(stack, lines, rt::FormattedText, fontset) _type(x) = nothing - _type(r::RT) = r.type + _type(r::FormattedText) = r.type - push!(stack, new_glyphstate(stack[end], rt, Val(rt.type))) + push!(stack, new_glyphstate(stack[end], rt, Val(rt.type), fontset)) sup_x = 0f0 for (i, c) in enumerate(rt.children) if _type(c) == :sup @@ -434,13 +440,13 @@ function process_rt_node!(stack, lines, rt::RT) sup_x_end = gs.x gs_modified = Setfield.@set gs.x = sup_x stack[end] = gs_modified - process_rt_node!(stack, lines, c) + process_rt_node!(stack, lines, c, fontset) gs = stack[end] max_x = max(sup_x_end, gs.x) gs_max_x = Setfield.@set gs.x = max_x stack[end] = gs_max_x else - process_rt_node!(stack, lines, c) + process_rt_node!(stack, lines, c, fontset) end end gs = pop!(stack) @@ -450,7 +456,7 @@ function process_rt_node!(stack, lines, rt::RT) return end -function process_rt_node!(stack, lines, s::String) +function process_rt_node!(stack, lines, s::String, _) gs = stack[end] y = gs.baseline x = gs.x @@ -480,43 +486,44 @@ function process_rt_node!(stack, lines, s::String) return end -function new_glyphstate(gs::GlyphState2, rt::RT, val::Val) +function new_glyphstate(gs::GlyphState2, rt::FormattedText, val::Val, fontset) gs end _get_color(attributes, default)::RGBAf = haskey(attributes, :color) ? Makie.to_color(attributes[:color]) : default +_get_font(attributes, default::NativeFont, fontset)::NativeFont = haskey(attributes, :font) ? Makie.to_font(fontset, attributes[:font]) : default -function new_glyphstate(gs::GlyphState2, rt::RT, val::Val{:sup}) +function new_glyphstate(gs::GlyphState2, rt::FormattedText, val::Val{:sup}, fontset) att = rt.attributes GlyphState2( gs.x, gs.baseline + 0.4 * gs.size[2], gs.size * 0.66, - gs.font, + _get_font(att, gs.font, fontset), _get_color(att, gs.color), ) end -function new_glyphstate(gs::GlyphState2, rt::RT, val::Val{:span}) +function new_glyphstate(gs::GlyphState2, rt::FormattedText, val::Val{:span}, fontset) att = rt.attributes GlyphState2( gs.x, gs.baseline, gs.size, - gs.font, + _get_font(att, gs.font, fontset), _get_color(att, gs.color), ) end -function new_glyphstate(gs::GlyphState2, rt::RT, val::Val{:sub}) +function new_glyphstate(gs::GlyphState2, rt::FormattedText, val::Val{:sub}, fontset) att = rt.attributes GlyphState2( gs.x, gs.baseline - 0.15 * gs.size[2], gs.size * 0.66, - gs.font, + _get_font(att, gs.font, fontset), _get_color(att, gs.color), ) end -Makie.iswhitespace(::RT) = false \ No newline at end of file +Makie.iswhitespace(::FormattedText) = false diff --git a/src/conversions.jl b/src/conversions.jl index 4d748c1e7d1..0a892018fba 100644 --- a/src/conversions.jl +++ b/src/conversions.jl @@ -920,7 +920,7 @@ const FONT_CACHE = Dict{String, NativeFont}() a string naming a font, e.g. helvetica """ -function to_font(x::Union{Symbol, String}) +function to_font(x::String) str = string(x) get!(FONT_CACHE, str) do str == "default" && return to_font("TeX Gyre Heros Makie") @@ -952,6 +952,10 @@ to_font(x::Vector{String}) = to_font.(x) to_font(x::NativeFont) = x to_font(x::Vector{NativeFont}) = x +to_font(fs::FontSet6, s::Symbol) = fs[s] +to_font(fs::FontSet6, x) = to_font(x) + + """ rotation accepts: to_rotation(b, quaternion) diff --git a/src/layouting/layouting.jl b/src/layouting/layouting.jl index f81241bb817..f0f17fb4fd6 100644 --- a/src/layouting/layouting.jl +++ b/src/layouting/layouting.jl @@ -40,7 +40,7 @@ Compute a GlyphCollection for a `string` given textsize, font, align, rotation, """ function layout_text( string::AbstractString, textsize::Union{AbstractVector, Number}, - font, align, rotation, justification, lineheight, color, + font, fontset, align, rotation, justification, lineheight, color, strokecolor, strokewidth, word_wrap_width ) diff --git a/src/theming.jl b/src/theming.jl index ddff004c7af..ce5049e5133 100644 --- a/src/theming.jl +++ b/src/theming.jl @@ -53,9 +53,9 @@ const default_palettes = Attributes( side = [:left, :right] ) -struct FontSet5{N <: NamedTuple} +struct FontSet6{N <: NamedTuple} fonts::N - function FontSet5(; kwargs...) + function FontSet6(; kwargs...) d = Dict([key => Makie.to_font(value) for (key, value) in kwargs]) if !haskey(d, :regular) d[:regular] = Makie.to_font("TeX Gyre Heros Makie") @@ -66,21 +66,30 @@ struct FontSet5{N <: NamedTuple} if !haskey(d, :italic) d[:italic] = Makie.to_font("TeX Gyre Heros Makie Italic") end + if !haskey(d, :bold_italic) + d[:bold_italic] = Makie.to_font("TeX Gyre Heros Makie Bold Italic") + end nt = NamedTuple(d) return new{typeof(nt)}(nt) end end -function Base.show(io::IO, f::FontSet5) +function Base.show(io::IO, f::FontSet6) println(io, "FontSet") for (key, value) in pairs(f.fonts) println(io, " ", key, ": ", value) end end +function Base.getindex(fs::FontSet6, s::Symbol) + haskey(fs.fonts, s) && return fs.fonts[s] + error("FontSet does not have a key $(repr(s)). Available keys are $(keys(fs.fonts))") +end + const minimal_default = Attributes( palette = default_palettes, font = "TeX Gyre Heros Makie", + fontset = FontSet6(), textcolor = :black, padding = Vec3f(0.05), figure_padding = 16, From 38cb03fa017932a721b75e0e97bd56c8f72423de Mon Sep 17 00:00:00 2001 From: Julius Krumbiegel Date: Tue, 11 Oct 2022 20:20:23 +0200 Subject: [PATCH 09/40] move inner constructor out --- src/theming.jl | 32 ++++++++++++++++---------------- 1 file changed, 16 insertions(+), 16 deletions(-) diff --git a/src/theming.jl b/src/theming.jl index ce5049e5133..03cb80e9ab1 100644 --- a/src/theming.jl +++ b/src/theming.jl @@ -55,23 +55,23 @@ const default_palettes = Attributes( struct FontSet6{N <: NamedTuple} fonts::N - function FontSet6(; kwargs...) - d = Dict([key => Makie.to_font(value) for (key, value) in kwargs]) - if !haskey(d, :regular) - d[:regular] = Makie.to_font("TeX Gyre Heros Makie") - end - if !haskey(d, :bold) - d[:bold] = Makie.to_font("TeX Gyre Heros Makie Bold") - end - if !haskey(d, :italic) - d[:italic] = Makie.to_font("TeX Gyre Heros Makie Italic") - end - if !haskey(d, :bold_italic) - d[:bold_italic] = Makie.to_font("TeX Gyre Heros Makie Bold Italic") - end - nt = NamedTuple(d) - return new{typeof(nt)}(nt) +end + +function FontSet6(; kwargs...) + d = Dict([key => Makie.to_font(value) for (key, value) in kwargs]) + if !haskey(d, :regular) + d[:regular] = Makie.to_font("TeX Gyre Heros Makie") + end + if !haskey(d, :bold) + d[:bold] = Makie.to_font("TeX Gyre Heros Makie Bold") + end + if !haskey(d, :italic) + d[:italic] = Makie.to_font("TeX Gyre Heros Makie Italic") + end + if !haskey(d, :bold_italic) + d[:bold_italic] = Makie.to_font("TeX Gyre Heros Makie Bold Italic") end + return FontSet6(NamedTuple(d)) end function Base.show(io::IO, f::FontSet6) From ba82c7816699fb560df6ea4a6bd7a9a8537d29f9 Mon Sep 17 00:00:00 2001 From: Julius Krumbiegel Date: Tue, 11 Oct 2022 20:26:12 +0200 Subject: [PATCH 10/40] remove Makie. qualifier --- src/theming.jl | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/theming.jl b/src/theming.jl index 03cb80e9ab1..d4d541e4346 100644 --- a/src/theming.jl +++ b/src/theming.jl @@ -58,18 +58,18 @@ struct FontSet6{N <: NamedTuple} end function FontSet6(; kwargs...) - d = Dict([key => Makie.to_font(value) for (key, value) in kwargs]) + d = Dict([key => to_font(value) for (key, value) in kwargs]) if !haskey(d, :regular) - d[:regular] = Makie.to_font("TeX Gyre Heros Makie") + d[:regular] = to_font("TeX Gyre Heros Makie") end if !haskey(d, :bold) - d[:bold] = Makie.to_font("TeX Gyre Heros Makie Bold") + d[:bold] = to_font("TeX Gyre Heros Makie Bold") end if !haskey(d, :italic) - d[:italic] = Makie.to_font("TeX Gyre Heros Makie Italic") + d[:italic] = to_font("TeX Gyre Heros Makie Italic") end if !haskey(d, :bold_italic) - d[:bold_italic] = Makie.to_font("TeX Gyre Heros Makie Bold Italic") + d[:bold_italic] = to_font("TeX Gyre Heros Makie Bold Italic") end return FontSet6(NamedTuple(d)) end From b8bb3d4e666a888f7eb6f4f510143ec2ffa6ad13 Mon Sep 17 00:00:00 2001 From: Julius Krumbiegel Date: Sun, 23 Oct 2022 14:44:28 +0200 Subject: [PATCH 11/40] switch to Attributes for `fonts` --- MakieCore/src/basic_plots.jl | 2 +- src/Makie.jl | 14 ++++++------- src/basic_recipes/text.jl | 26 +++++++++++------------ src/conversions.jl | 14 +++++++++++-- src/layouting/boundingbox.jl | 3 ++- src/layouting/layouting.jl | 2 +- src/theming.jl | 40 ++++++------------------------------ 7 files changed, 42 insertions(+), 59 deletions(-) diff --git a/MakieCore/src/basic_plots.jl b/MakieCore/src/basic_plots.jl index 0a764dcf70a..60171c6b0eb 100644 --- a/MakieCore/src/basic_plots.jl +++ b/MakieCore/src/basic_plots.jl @@ -517,7 +517,7 @@ Plots one or multiple texts passed via the `text` keyword. default_theme(scene)..., color = theme(scene, :textcolor), font = theme(scene, :font), - fontset = theme(scene, :fontset), + fonts = theme(scene, :fonts), strokecolor = (:black, 0.0), strokewidth = 0, align = (:left, :bottom), diff --git a/src/Makie.jl b/src/Makie.jl index 3fb97ce80f5..5f988f5da0d 100644 --- a/src/Makie.jl +++ b/src/Makie.jl @@ -89,6 +89,9 @@ const RGBAf = RGBA{Float32} const RGBf = RGB{Float32} const NativeFont = FreeTypeAbstraction.FTFont +const ASSETS_DIR = RelocatableFolders.@path joinpath(@__DIR__, "..", "assets") +assetpath(files...) = normpath(joinpath(ASSETS_DIR, files...)) + include("documentation/docstringextension.jl") include("utilities/quaternions.jl") include("bezier.jl") @@ -102,16 +105,16 @@ include("utilities/utilities.jl") # need Makie.AbstractPattern # Basic scene/plot/recipe interfaces + types include("scenes.jl") +include("interfaces.jl") +include("conversions.jl") +include("units.jl") +include("shorthands.jl") include("theming.jl") include("themes/theme_ggplot2.jl") include("themes/theme_black.jl") include("themes/theme_minimal.jl") include("themes/theme_light.jl") include("themes/theme_dark.jl") -include("interfaces.jl") -include("units.jl") -include("conversions.jl") -include("shorthands.jl") # camera types + functions include("camera/projection_math.jl") @@ -274,9 +277,6 @@ export cgrad, available_gradients, showgradients export Pattern -const ASSETS_DIR = RelocatableFolders.@path joinpath(@__DIR__, "..", "assets") -assetpath(files...) = normpath(joinpath(ASSETS_DIR, files...)) - export assetpath # default icon for Makie function icon() diff --git a/src/basic_recipes/text.jl b/src/basic_recipes/text.jl index 37af0a963d3..8d13ef7e606 100644 --- a/src/basic_recipes/text.jl +++ b/src/basic_recipes/text.jl @@ -7,7 +7,7 @@ function plot!(plot::Text) linecolors = Observable(RGBAf[]) lineindices = Ref(Int[]) - onany(plot.text, plot.textsize, plot.font, plot.fontset, plot.align, + onany(plot.text, plot.textsize, plot.font, plot.fonts, plot.align, plot.rotation, plot.justification, plot.lineheight, plot.color, plot.strokecolor, plot.strokewidth, plot.word_wrap_width) do str, ts, f, fs, al, rot, jus, lh, col, scol, swi, www @@ -420,11 +420,11 @@ function float_justification(ju, al)::Float32 end end -function process_rt_node!(stack, lines, rt::FormattedText, fontset) +function process_rt_node!(stack, lines, rt::FormattedText, fonts) _type(x) = nothing _type(r::FormattedText) = r.type - push!(stack, new_glyphstate(stack[end], rt, Val(rt.type), fontset)) + push!(stack, new_glyphstate(stack[end], rt, Val(rt.type), fonts)) sup_x = 0f0 for (i, c) in enumerate(rt.children) if _type(c) == :sup @@ -440,13 +440,13 @@ function process_rt_node!(stack, lines, rt::FormattedText, fontset) sup_x_end = gs.x gs_modified = Setfield.@set gs.x = sup_x stack[end] = gs_modified - process_rt_node!(stack, lines, c, fontset) + process_rt_node!(stack, lines, c, fonts) gs = stack[end] max_x = max(sup_x_end, gs.x) gs_max_x = Setfield.@set gs.x = max_x stack[end] = gs_max_x else - process_rt_node!(stack, lines, c, fontset) + process_rt_node!(stack, lines, c, fonts) end end gs = pop!(stack) @@ -486,42 +486,42 @@ function process_rt_node!(stack, lines, s::String, _) return end -function new_glyphstate(gs::GlyphState2, rt::FormattedText, val::Val, fontset) +function new_glyphstate(gs::GlyphState2, rt::FormattedText, val::Val, fonts) gs end _get_color(attributes, default)::RGBAf = haskey(attributes, :color) ? Makie.to_color(attributes[:color]) : default -_get_font(attributes, default::NativeFont, fontset)::NativeFont = haskey(attributes, :font) ? Makie.to_font(fontset, attributes[:font]) : default +_get_font(attributes, default::NativeFont, fonts)::NativeFont = haskey(attributes, :font) ? Makie.to_font(fonts, attributes[:font]) : default -function new_glyphstate(gs::GlyphState2, rt::FormattedText, val::Val{:sup}, fontset) +function new_glyphstate(gs::GlyphState2, rt::FormattedText, val::Val{:sup}, fonts) att = rt.attributes GlyphState2( gs.x, gs.baseline + 0.4 * gs.size[2], gs.size * 0.66, - _get_font(att, gs.font, fontset), + _get_font(att, gs.font, fonts), _get_color(att, gs.color), ) end -function new_glyphstate(gs::GlyphState2, rt::FormattedText, val::Val{:span}, fontset) +function new_glyphstate(gs::GlyphState2, rt::FormattedText, val::Val{:span}, fonts) att = rt.attributes GlyphState2( gs.x, gs.baseline, gs.size, - _get_font(att, gs.font, fontset), + _get_font(att, gs.font, fonts), _get_color(att, gs.color), ) end -function new_glyphstate(gs::GlyphState2, rt::FormattedText, val::Val{:sub}, fontset) +function new_glyphstate(gs::GlyphState2, rt::FormattedText, val::Val{:sub}, fonts) att = rt.attributes GlyphState2( gs.x, gs.baseline - 0.15 * gs.size[2], gs.size * 0.66, - _get_font(att, gs.font, fontset), + _get_font(att, gs.font, fonts), _get_color(att, gs.color), ) end diff --git a/src/conversions.jl b/src/conversions.jl index 0a892018fba..7e72e7bd474 100644 --- a/src/conversions.jl +++ b/src/conversions.jl @@ -952,8 +952,18 @@ to_font(x::Vector{String}) = to_font.(x) to_font(x::NativeFont) = x to_font(x::Vector{NativeFont}) = x -to_font(fs::FontSet6, s::Symbol) = fs[s] -to_font(fs::FontSet6, x) = to_font(x) +function to_font(fonts::Attributes, s::Symbol) + if haskey(fonts, s) + f = fonts[s][] + if f isa Symbol + error("The value for font $(repr(s)) was Symbol $(repr(f)), which is not allowed. The value for a font in the fonts collection cannot be another Symbol and must be resolvable via `to_font(x)`.") + end + return to_font(fonts[s][]) + end + error("The symbol $(repr(s)) is not present in the fonts collection: $fonts.") +end + +to_font(fonts::Attributes, x) = to_font(x) """ diff --git a/src/layouting/boundingbox.jl b/src/layouting/boundingbox.jl index 0faee6ef67e..18757dcb7a5 100644 --- a/src/layouting/boundingbox.jl +++ b/src/layouting/boundingbox.jl @@ -131,8 +131,9 @@ _is_latex_string(other) = false function text_bb(str, font, size) rot = Quaternionf(0,0,0,1) + fonts = nothing # TODO: remove the arg if possible layout = layout_text( - str, size, font, Vec2f(0), rot, 0.5, 1.0, + str, size, font, fonts, Vec2f(0), rot, 0.5, 1.0, RGBAf(0, 0, 0, 0), RGBAf(0, 0, 0, 0), 0f0, 0f0) return boundingbox(layout, Point3f(0), rot) end diff --git a/src/layouting/layouting.jl b/src/layouting/layouting.jl index f0f17fb4fd6..a6767a59845 100644 --- a/src/layouting/layouting.jl +++ b/src/layouting/layouting.jl @@ -40,7 +40,7 @@ Compute a GlyphCollection for a `string` given textsize, font, align, rotation, """ function layout_text( string::AbstractString, textsize::Union{AbstractVector, Number}, - font, fontset, align, rotation, justification, lineheight, color, + font, fonts, align, rotation, justification, lineheight, color, strokecolor, strokewidth, word_wrap_width ) diff --git a/src/theming.jl b/src/theming.jl index 828e93434bf..1839a15919c 100644 --- a/src/theming.jl +++ b/src/theming.jl @@ -53,43 +53,15 @@ const default_palettes = Attributes( side = [:left, :right] ) -struct FontSet6{N <: NamedTuple} - fonts::N -end - -function FontSet6(; kwargs...) - d = Dict([key => to_font(value) for (key, value) in kwargs]) - if !haskey(d, :regular) - d[:regular] = to_font("TeX Gyre Heros Makie") - end - if !haskey(d, :bold) - d[:bold] = to_font("TeX Gyre Heros Makie Bold") - end - if !haskey(d, :italic) - d[:italic] = to_font("TeX Gyre Heros Makie Italic") - end - if !haskey(d, :bold_italic) - d[:bold_italic] = to_font("TeX Gyre Heros Makie Bold Italic") - end - return FontSet6(NamedTuple(d)) -end - -function Base.show(io::IO, f::FontSet6) - println(io, "FontSet") - for (key, value) in pairs(f.fonts) - println(io, " ", key, ": ", value) - end -end - -function Base.getindex(fs::FontSet6, s::Symbol) - haskey(fs.fonts, s) && return fs.fonts[s] - error("FontSet does not have a key $(repr(s)). Available keys are $(keys(fs.fonts))") -end - const minimal_default = Attributes( palette = default_palettes, font = "TeX Gyre Heros Makie", - fontset = FontSet6(), + fonts = Attributes( + :regular => "TeX Gyre Heros Makie", + :bold => "TeX Gyre Heros Makie Bold", + :italic => "TeX Gyre Heros Makie Italic", + :bold_italic => "TeX Gyre Heros Makie Bold Italic", + ), textcolor = :black, padding = Vec3f(0.05), figure_padding = 16, From 554b562b0574a85b21e356533ddd43339e3ebbd7 Mon Sep 17 00:00:00 2001 From: Julius Krumbiegel Date: Sun, 23 Oct 2022 14:55:43 +0200 Subject: [PATCH 12/40] improve error message --- src/conversions.jl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/conversions.jl b/src/conversions.jl index 7e72e7bd474..f8a98699f1f 100644 --- a/src/conversions.jl +++ b/src/conversions.jl @@ -960,7 +960,7 @@ function to_font(fonts::Attributes, s::Symbol) end return to_font(fonts[s][]) end - error("The symbol $(repr(s)) is not present in the fonts collection: $fonts.") + error("The symbol $(repr(s)) is not present in the fonts collection:\n$fonts.") end to_font(fonts::Attributes, x) = to_font(x) From 5b93ca539f06925b3369c548a624927808e521cf Mon Sep 17 00:00:00 2001 From: Julius Krumbiegel Date: Sun, 23 Oct 2022 14:55:56 +0200 Subject: [PATCH 13/40] remove all font field typings for Blocks --- src/makielayout/types.jl | 40 ++++++++++++++++++++-------------------- 1 file changed, 20 insertions(+), 20 deletions(-) diff --git a/src/makielayout/types.jl b/src/makielayout/types.jl index dffc322e18a..f852e5ff14b 100644 --- a/src/makielayout/types.jl +++ b/src/makielayout/types.jl @@ -198,7 +198,7 @@ end "The axis title string." title = "" "The font family of the title." - titlefont::Makie.FreeTypeAbstraction.FTFont = "TeX Gyre Heros Makie Bold" + titlefont = :bold "The title's font size." titlesize::Float64 = @inherit(:fontsize, 16f0) "The gap between axis and title." @@ -214,7 +214,7 @@ end "The axis subtitle string." subtitle = "" "The font family of the subtitle." - subtitlefont::Makie.FreeTypeAbstraction.FTFont = @inherit(:font, "TeX Gyre Heros Makie") + subtitlefont = :regular "The subtitle's font size." subtitlesize::Float64 = @inherit(:fontsize, 16f0) "The gap between subtitle and title." @@ -226,9 +226,9 @@ end "The axis subtitle line height multiplier." subtitlelineheight::Float64 = 1 "The font family of the xlabel." - xlabelfont::Makie.FreeTypeAbstraction.FTFont = @inherit(:font, "TeX Gyre Heros Makie") + xlabelfont = :regular "The font family of the ylabel." - ylabelfont::Makie.FreeTypeAbstraction.FTFont = @inherit(:font, "TeX Gyre Heros Makie") + ylabelfont = :regular "The color of the xlabel." xlabelcolor::RGBAf = @inherit(:textcolor, :black) "The color of the ylabel." @@ -246,9 +246,9 @@ end "The padding between the ylabel and the ticks or axis." ylabelpadding::Float64 = 5f0 # xlabels usually have some more visual padding because of ascenders, which are larger than the hadvance gaps of ylabels "The font family of the xticklabels." - xticklabelfont::Makie.FreeTypeAbstraction.FTFont = @inherit(:font, "TeX Gyre Heros Makie") + xticklabelfont = :regular "The font family of the yticklabels." - yticklabelfont::Makie.FreeTypeAbstraction.FTFont = @inherit(:font, "TeX Gyre Heros Makie") + yticklabelfont = :regular "The color of xticklabels." xticklabelcolor::RGBAf = @inherit(:textcolor, :black) "The color of yticklabels." @@ -481,7 +481,7 @@ end "The label color." labelcolor = @inherit(:textcolor, :black) "The label font family." - labelfont = @inherit(:font, "TeX Gyre Heros Makie") + labelfont = :regular "The label font size." labelsize = @inherit(:fontsize, 16f0) "Controls if the label is visible." @@ -489,7 +489,7 @@ end "The gap between the label and the ticks." labelpadding = 5f0 "The font family of the tick labels." - ticklabelfont = @inherit(:font, "TeX Gyre Heros Makie") + ticklabelfont = :regular "The font size of the tick labels." ticklabelsize = @inherit(:fontsize, 16f0) "Controls if the tick labels are visible." @@ -598,7 +598,7 @@ end "The font size of the text." textsize::Float32 = @inherit(:fontsize, 16f0) "The font family of the text." - font::Makie.FreeTypeAbstraction.FTFont = @inherit(:font, "TeX Gyre Heros Makie") + font = :regular "The justification of the text (:left, :right, :center)." justification = :center "The lineheight multiplier for the text." @@ -772,7 +772,7 @@ end "The text of the button label." label = "Button" "The font family of the button label." - font = @inherit(:font, "TeX Gyre Heros Makie") + font = :regular "The width setting of the button." width = Auto() "The height setting of the button." @@ -936,7 +936,7 @@ const EntryGroup = Tuple{Optional{<:AbstractString}, Vector{LegendEntry}} "Controls if the parent layout can adjust to this element's height" tellheight = automatic "The font family of the legend group titles." - titlefont = "TeX Gyre Heros Makie Bold" + titlefont = :bold "The font size of the legend group titles." titlesize = @inherit(:fontsize, 16f0) "The horizontal alignment of the legend group titles." @@ -952,7 +952,7 @@ const EntryGroup = Tuple{Optional{<:AbstractString}, Vector{LegendEntry}} "The font size of the entry labels." labelsize = @inherit(:fontsize, 16f0) "The font family of the entry labels." - labelfont = @inherit(:font, "TeX Gyre Heros Makie") + labelfont = :regular "The color of the entry labels." labelcolor = @inherit(:textcolor, :black) "The horizontal alignment of the entry labels." @@ -1091,7 +1091,7 @@ end "Text color for the placeholder." textcolor_placeholder = RGBf(0.5, 0.5, 0.5) "Font family." - font = @inherit(:font, "TeX Gyre Heros Makie") + font = :regular "Color of the box." boxcolor = :transparent "Color of the box when focused." @@ -1200,11 +1200,11 @@ end "The z label size" zlabelsize = @inherit(:fontsize, 16f0) "The x label font" - xlabelfont = @inherit(:font, "TeX Gyre Heros Makie") + xlabelfont = :regular "The y label font" - ylabelfont = @inherit(:font, "TeX Gyre Heros Makie") + ylabelfont = :regular "The z label font" - zlabelfont = @inherit(:font, "TeX Gyre Heros Makie") + zlabelfont = :regular "The x label rotation" xlabelrotation = Makie.automatic "The y label rotation" @@ -1242,11 +1242,11 @@ end "The z ticklabel pad" zticklabelpad = 10 "The x ticklabel font" - xticklabelfont = @inherit(:font, "TeX Gyre Heros Makie") + xticklabelfont = :regular "The y ticklabel font" - yticklabelfont = @inherit(:font, "TeX Gyre Heros Makie") + yticklabelfont = :regular "The z ticklabel font" - zticklabelfont = @inherit(:font, "TeX Gyre Heros Makie") + zticklabelfont = :regular "The x grid color" xgridcolor = RGBAf(0, 0, 0, 0.12) "The y grid color" @@ -1324,7 +1324,7 @@ end "The axis title string." title = "" "The font family of the title." - titlefont = "TeX Gyre Heros Makie Bold" + titlefont = :bold "The title's font size." titlesize = @inherit(:fontsize, 16f0) "The gap between axis and title." From ec78baa42919bc1aff4e3c927c54fb14e3b558c7 Mon Sep 17 00:00:00 2001 From: Julius Krumbiegel Date: Sun, 23 Oct 2022 19:18:20 +0200 Subject: [PATCH 14/40] change to RichText / rich --- docs/examples/plotting_functions/text.md | 18 +++++-------- src/basic_recipes/text.jl | 34 ++++++++++++------------ 2 files changed, 24 insertions(+), 28 deletions(-) diff --git a/docs/examples/plotting_functions/text.md b/docs/examples/plotting_functions/text.md index da31466f804..a3470c93735 100644 --- a/docs/examples/plotting_functions/text.md +++ b/docs/examples/plotting_functions/text.md @@ -200,10 +200,10 @@ f ``` \end{examplefigure} -## Formatted text +## Rich text -With formatted text, you can build text using different colors or fonts, as well as subscripts and superscripts. -You can build formatted text using the functions `formatted`, `superscript` and `subscript`. +With rich text, you can build text using different colors or fonts, as well as subscripts and superscripts. +You can build rich text using the functions `rich`, `superscript` and `subscript`. \begin{examplefigure}{svg = true} ```julia @@ -214,21 +214,17 @@ Makie.inline!(true) # hide f = Figure(fontsize = 30) Label( f[1, 1], - formatted( + rich( "H", subscript("2"), "O is the formula for ", - formatted("water", color = :blue, font = :italic) + rich("water", color = :blue, font = :italic) ) ) str = "A beautiful rainbow" rainbow = cgrad(:rainbow, length(str), categorical = true) +rainbow_chars = [rich("$c", color = rainbow[i]) for (i, c) in enumerate(str)] -Label( - f[2, 1], - formatted( - [formatted("$c", color = rainbow[i]) for (i, c) in enumerate(str)]... - ) -) +Label(f[2, 1], rich(rainbow_chars...)) f ``` diff --git a/src/basic_recipes/text.jl b/src/basic_recipes/text.jl index 8d13ef7e606..d99a44767f2 100644 --- a/src/basic_recipes/text.jl +++ b/src/basic_recipes/text.jl @@ -264,25 +264,25 @@ iswhitespace(l::LaTeXString) = iswhitespace(replace(l.s, '$' => "")) -struct FormattedText <: AbstractString +struct RichText <: AbstractString type::Symbol - children::Vector{Union{FormattedText,String}} + children::Vector{Union{RichText,String}} attributes::Dict{Symbol, Any} - function FormattedText(type::Symbol, children...; kwargs...) - cs = Union{FormattedText,String}[children...] + function RichText(type::Symbol, children...; kwargs...) + cs = Union{RichText,String}[children...] typeof(cs) new(type, cs, Dict(kwargs)) end end -formatted(args...; kwargs...) = FormattedText(:span, args...; kwargs...) -subscript(args...; kwargs...) = FormattedText(:sub, args...; kwargs...) -superscript(args...; kwargs...) = FormattedText(:sup, args...; kwargs...) +rich(args...; kwargs...) = RichText(:span, args...; kwargs...) +subscript(args...; kwargs...) = RichText(:sub, args...; kwargs...) +superscript(args...; kwargs...) = RichText(:sup, args...; kwargs...) -export formatted, subscript, superscript +export rich, subscript, superscript ## -function Makie._get_glyphcollection_and_linesegments(rt::FormattedText, index, ts, f, fset, al, rot, jus, lh, col, scol, swi, www) +function Makie._get_glyphcollection_and_linesegments(rt::RichText, index, ts, f, fset, al, rot, jus, lh, col, scol, swi, www) gc = Makie.layout_text(rt, ts, f, fset, al, rot, jus, lh, col) gc, Point2f[], Float32[], Makie.RGBAf[], Int[] end @@ -322,7 +322,7 @@ function Makie.GlyphCollection(v::Vector{GlyphInfo2}) end -function Makie.layout_text(rt::FormattedText, ts, f, fset, al, rot, jus, lh, col) +function Makie.layout_text(rt::RichText, ts, f, fset, al, rot, jus, lh, col) _f = to_font(fset, f) @@ -420,9 +420,9 @@ function float_justification(ju, al)::Float32 end end -function process_rt_node!(stack, lines, rt::FormattedText, fonts) +function process_rt_node!(stack, lines, rt::RichText, fonts) _type(x) = nothing - _type(r::FormattedText) = r.type + _type(r::RichText) = r.type push!(stack, new_glyphstate(stack[end], rt, Val(rt.type), fonts)) sup_x = 0f0 @@ -486,14 +486,14 @@ function process_rt_node!(stack, lines, s::String, _) return end -function new_glyphstate(gs::GlyphState2, rt::FormattedText, val::Val, fonts) +function new_glyphstate(gs::GlyphState2, rt::RichText, val::Val, fonts) gs end _get_color(attributes, default)::RGBAf = haskey(attributes, :color) ? Makie.to_color(attributes[:color]) : default _get_font(attributes, default::NativeFont, fonts)::NativeFont = haskey(attributes, :font) ? Makie.to_font(fonts, attributes[:font]) : default -function new_glyphstate(gs::GlyphState2, rt::FormattedText, val::Val{:sup}, fonts) +function new_glyphstate(gs::GlyphState2, rt::RichText, val::Val{:sup}, fonts) att = rt.attributes GlyphState2( gs.x, @@ -504,7 +504,7 @@ function new_glyphstate(gs::GlyphState2, rt::FormattedText, val::Val{:sup}, font ) end -function new_glyphstate(gs::GlyphState2, rt::FormattedText, val::Val{:span}, fonts) +function new_glyphstate(gs::GlyphState2, rt::RichText, val::Val{:span}, fonts) att = rt.attributes GlyphState2( gs.x, @@ -515,7 +515,7 @@ function new_glyphstate(gs::GlyphState2, rt::FormattedText, val::Val{:span}, fon ) end -function new_glyphstate(gs::GlyphState2, rt::FormattedText, val::Val{:sub}, fonts) +function new_glyphstate(gs::GlyphState2, rt::RichText, val::Val{:sub}, fonts) att = rt.attributes GlyphState2( gs.x, @@ -526,4 +526,4 @@ function new_glyphstate(gs::GlyphState2, rt::FormattedText, val::Val{:sub}, font ) end -Makie.iswhitespace(::FormattedText) = false +Makie.iswhitespace(::RichText) = false From 8af97a3bf7d3b45f9acc41d00772087ab8350a6c Mon Sep 17 00:00:00 2001 From: Julius Krumbiegel Date: Mon, 24 Oct 2022 09:40:22 +0200 Subject: [PATCH 15/40] improve docs --- docs/examples/plotting_functions/text.md | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/docs/examples/plotting_functions/text.md b/docs/examples/plotting_functions/text.md index a3470c93735..db6231698b9 100644 --- a/docs/examples/plotting_functions/text.md +++ b/docs/examples/plotting_functions/text.md @@ -202,8 +202,12 @@ f ## Rich text -With rich text, you can build text using different colors or fonts, as well as subscripts and superscripts. -You can build rich text using the functions `rich`, `superscript` and `subscript`. +With rich text, you can conveniently plot text whose parts have different colors or fonts, and you can position sections as subscripts and superscripts. +You can create such rich text objects using the functions `rich`, `superscript` and `subscript`, all of which create `RichText` objects. + +Each of these functions takes a variable number of arguments, each of which can be a `String` or `RichText`. +Each can also take keyword arguments such as `color` or `font`, to set these attributes for the given part. +The top-level settings for font, color, etc. are taken from the `text` attributes as usual. \begin{examplefigure}{svg = true} ```julia @@ -216,7 +220,7 @@ Label( f[1, 1], rich( "H", subscript("2"), "O is the formula for ", - rich("water", color = :blue, font = :italic) + rich("water", color = :cornflowerblue, font = :italic) ) ) @@ -224,7 +228,7 @@ str = "A beautiful rainbow" rainbow = cgrad(:rainbow, length(str), categorical = true) rainbow_chars = [rich("$c", color = rainbow[i]) for (i, c) in enumerate(str)] -Label(f[2, 1], rich(rainbow_chars...)) +Label(f[2, 1], rich(rainbow_chars...), font = :bold) f ``` From 1c96794af63e869e178f0e10d6cf726ecb278cd9 Mon Sep 17 00:00:00 2001 From: Julius Krumbiegel Date: Mon, 24 Oct 2022 10:02:45 +0200 Subject: [PATCH 16/40] add fonts section --- docs/documentation/fonts.md | 57 +++++++++++++++++++++++++++++++++++++ 1 file changed, 57 insertions(+) create mode 100644 docs/documentation/fonts.md diff --git a/docs/documentation/fonts.md b/docs/documentation/fonts.md new file mode 100644 index 00000000000..9f4efc27458 --- /dev/null +++ b/docs/documentation/fonts.md @@ -0,0 +1,57 @@ +# Fonts + +Makie uses the `FreeType.jl` package for font support, therefore, most fonts that this package can load should be supported by Makie as well. +Fonts can be selected in multiple different ways: + +## String + +If you pass a `String` as a font, this can either be resolved as a file name for a font file, or as the (partial) name of the font itself (font family plus style). +Font name matching is case insensitive and accepts partial matches. + +```julia +font_path = "/some/path/to/a/font_file.ttf" +font_name = "TeX Gyre Heros Makie" +``` + +If you want to find out what exact font your string was resolved as, you can execute `Makie.to_font(the_string)`: + +```julia:fonts1 +using Makie +Makie.to_font("Blackchancery") +``` + +## Symbol + +A `Symbol` will be resolved by looking it up in the `text`'s `fonts` attribute. +The default theme has the following fonts set: + +```julia:fonts2 +using Makie +Makie.current_default_theme()[:fonts] +``` + +Therefore, you can pick a font from this set by setting, for example, `font = :bold_italic`. +The advantage of this is that you can set your fonts not by hardcoding specific ones at every place where you use `text`, but by setting the fonts at the top level. + +You can modify or add keys in the font set using `set_theme!`, `with_theme`, `update_theme!`, or by passing them to the `Figure` constructor. +Here's an example: + +\begin{examplefigure}{svg = true} +```julia +using CairoMakie +CairoMakie.activate!() # hide +Makie.inline!(true) # hide + +f = Figure(fontsize = 30, fonts = (; regular = "Times", weird = "Blackchancery")) +Axis(f[1, 1], title = "A title", xlabel = "An x label", xlabelfont = :weird) + +f +``` + +## Emoji and color fonts + +Currently, Makie does not have the ability to draw emoji or other color fonts. +This is due to the implementation of text drawing in GLMakie and WGLMakie, which relies on signed distance fields that can only be used to render monochrome glyphs, but not arbitrary bitmaps. +If you want to use emoji as scatter markers, consider using images (you will need to find suitable images separately, you cannot easily extract emoji from fonts with Makie). + + From 18f45c6c148a7930c6c833d41f95446735419524 Mon Sep 17 00:00:00 2001 From: Julius Krumbiegel Date: Mon, 24 Oct 2022 11:14:37 +0200 Subject: [PATCH 17/40] fix glmakie precompile --- src/basic_recipes/text.jl | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/basic_recipes/text.jl b/src/basic_recipes/text.jl index d99a44767f2..fb05eaae7f6 100644 --- a/src/basic_recipes/text.jl +++ b/src/basic_recipes/text.jl @@ -70,7 +70,10 @@ function plot!(plot::Text) pop!(attrs, :align) pop!(attrs, :color) - text!(plot, glyphcollections; attrs..., position = positions) + t = text!(plot, glyphcollections; attrs..., position = positions) + # remove attributes that the backends will choke on + pop!(t.attributes, :font) + pop!(t.attributes, :fonts) linesegments!(plot, linesegs_shifted; linewidth = linewidths, color = linecolors, space = :pixel) plot From 40a3e4200e76325c9410e6fa9c2aa141b29d76e9 Mon Sep 17 00:00:00 2001 From: Julius Krumbiegel Date: Mon, 24 Oct 2022 11:51:53 +0200 Subject: [PATCH 18/40] fix block --- docs/documentation/fonts.md | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/docs/documentation/fonts.md b/docs/documentation/fonts.md index 9f4efc27458..164aa76c68a 100644 --- a/docs/documentation/fonts.md +++ b/docs/documentation/fonts.md @@ -9,8 +9,8 @@ If you pass a `String` as a font, this can either be resolved as a file name for Font name matching is case insensitive and accepts partial matches. ```julia -font_path = "/some/path/to/a/font_file.ttf" -font_name = "TeX Gyre Heros Makie" +font_by_path = "/some/path/to/a/font_file.ttf" +font_by_name = "TeX Gyre Heros Makie" ``` If you want to find out what exact font your string was resolved as, you can execute `Makie.to_font(the_string)`: @@ -47,6 +47,7 @@ Axis(f[1, 1], title = "A title", xlabel = "An x label", xlabelfont = :weird) f ``` +\end{examplefigure} ## Emoji and color fonts From 78eb825127ef362d2bcb66b4fe8f01da4f7707a3 Mon Sep 17 00:00:00 2001 From: Julius Krumbiegel Date: Mon, 24 Oct 2022 12:59:05 +0200 Subject: [PATCH 19/40] show outputs --- docs/documentation/fonts.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/docs/documentation/fonts.md b/docs/documentation/fonts.md index 164aa76c68a..0ecf23c2674 100644 --- a/docs/documentation/fonts.md +++ b/docs/documentation/fonts.md @@ -19,6 +19,7 @@ If you want to find out what exact font your string was resolved as, you can exe using Makie Makie.to_font("Blackchancery") ``` +\show{fonts1} ## Symbol @@ -29,6 +30,7 @@ The default theme has the following fonts set: using Makie Makie.current_default_theme()[:fonts] ``` +\show{fonts2} Therefore, you can pick a font from this set by setting, for example, `font = :bold_italic`. The advantage of this is that you can set your fonts not by hardcoding specific ones at every place where you use `text`, but by setting the fonts at the top level. From cfa6b30aed1b3658a93edec65847058b8e79091c Mon Sep 17 00:00:00 2001 From: Julius Krumbiegel Date: Mon, 24 Oct 2022 12:59:31 +0200 Subject: [PATCH 20/40] replace explicit font mentions --- docs/examples/blocks/axis.md | 4 ++-- docs/tutorials/layout-tutorial.md | 12 ++++++------ 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/docs/examples/blocks/axis.md b/docs/examples/blocks/axis.md index fbb2acc1b95..bc9ac3765ca 100644 --- a/docs/examples/blocks/axis.md +++ b/docs/examples/blocks/axis.md @@ -189,7 +189,7 @@ Axis( f[2, 1], title = "Third Title", titlecolor = :gray50, - titlefont = "TeX Gyre Heros Bold Italic Makie", + titlefont = :bold_italic, titlealign = :right, titlesize = 25, ) @@ -200,7 +200,7 @@ Axis( titlealign = :left, subtitlegap = 2, titlegap = 5, - subtitlefont = "TeX Gyre Heros Italic Makie", + subtitlefont = :italic, subtitlelineheight = 0.9, titlelineheight = 0.9, ) diff --git a/docs/tutorials/layout-tutorial.md b/docs/tutorials/layout-tutorial.md index 0f3814921d0..5d70a590c80 100644 --- a/docs/tutorials/layout-tutorial.md +++ b/docs/tutorials/layout-tutorial.md @@ -55,7 +55,7 @@ colgap!(ga, 10) rowgap!(ga, 10) Label(ga[1, 1:2, Top()], "Stimulus ratings", valign = :bottom, - font = "TeX Gyre Heros Bold", + font = :bold, padding = (0, 0, 5, 0)) xs = LinRange(0.5, 6, 50) @@ -111,7 +111,7 @@ axs[3, 1].xlabel = "Day 1" axs[3, 2].xlabel = "Day 2" Label(gd[1, :, Top()], "EEG traces", valign = :bottom, - font = "TeX Gyre Heros Bold", + font = :bold, padding = (0, 0, 5, 0)) rowgap!(gd, 10) @@ -133,7 +133,7 @@ colsize!(gd, 2, Auto(n_day_2)) for (label, layout) in zip(["A", "B", "C", "D"], [ga, gb, gc, gd]) Label(layout[1, 1, TopLeft()], label, textsize = 26, - font = "TeX Gyre Heros Bold", + font = :bold, padding = (0, 5, 5, 0), halign = :right) end @@ -283,7 +283,7 @@ We can make a title by placing a label across the top two elements. \begin{examplefigure}{px_per_unit = 1.5} ```julia Label(ga[1, 1:2, Top()], "Stimulus ratings", valign = :bottom, - font = "TeX Gyre Heros Bold", + font = :bold, padding = (0, 0, 5, 0)) f @@ -413,7 +413,7 @@ We can make a little title for the six axes by placing a `Label` in the top prot \begin{examplefigure}{px_per_unit = 1.5} ```julia Label(gd[1, :, Top()], "EEG traces", valign = :bottom, - font = "TeX Gyre Heros Bold", + font = :bold, padding = (0, 0, 5, 0)) f @@ -485,7 +485,7 @@ That will leave all other alignments intact, because we're not creating any new for (label, layout) in zip(["A", "B", "C", "D"], [ga, gb, gc, gd]) Label(layout[1, 1, TopLeft()], label, textsize = 26, - font = "TeX Gyre Heros Bold", + font = :bold, padding = (0, 5, 5, 0), halign = :right) end From 6e24f0b0f2d5fdc4b910254c50b5451274c41273 Mon Sep 17 00:00:00 2001 From: Julius Krumbiegel Date: Mon, 24 Oct 2022 14:58:06 +0200 Subject: [PATCH 21/40] change default font to :regular --- MakieCore/src/basic_plots.jl | 2 +- src/basic_recipes/axis.jl | 8 ++++---- src/basic_recipes/buffers.jl | 6 +++++- src/theming.jl | 2 +- 4 files changed, 11 insertions(+), 7 deletions(-) diff --git a/MakieCore/src/basic_plots.jl b/MakieCore/src/basic_plots.jl index 60171c6b0eb..f87c618d3ca 100644 --- a/MakieCore/src/basic_plots.jl +++ b/MakieCore/src/basic_plots.jl @@ -488,7 +488,7 @@ Plots one or multiple texts passed via the `text` keyword. - `text` specifies one piece of text or a vector of texts to show, where the number has to match the number of positions given. Makie supports `String` which is used for all normal text and `LaTeXString` which layouts mathematical expressions using `MathTeXEngine.jl`. - `align::Tuple{Union{Symbol, Real}, Union{Symbol, Real}} = (:left, :bottom)` sets the alignment of the string w.r.t. `position`. Uses `:left, :center, :right, :top, :bottom, :baseline` or fractions. -- `font::Union{String, Vector{String}} = "TeX Gyre Heros Makie"` sets the font for the string or each character. +- `font::Union{String, Vector{String}} = :regular` sets the font for the string or each character. - `justification::Union{Real, Symbol} = automatic` sets the alignment of text w.r.t its bounding box. Can be `:left, :center, :right` or a fraction. Will default to the horizontal alignment in `align`. - `rotation::Union{Real, Quaternion}` rotates text around the given position. - `textsize::Union{Real, Vec2f}` sets the size of each character. diff --git a/src/basic_recipes/axis.jl b/src/basic_recipes/axis.jl index 373513a9216..ed403a546c8 100644 --- a/src/basic_recipes/axis.jl +++ b/src/basic_recipes/axis.jl @@ -66,7 +66,7 @@ $(ATTRIBUTES) scale = Vec3f(1), padding = 0.1, inspectable = false, - + fonts = theme(scene, :fonts), names = Attributes( axisnames = ("x", "y", "z"), textcolor = (:black, :black, :black), @@ -222,7 +222,7 @@ to3tuple(x::Tuple{Any, Any}) = (x[1], x[2], x[2]) to3tuple(x::Tuple{Any, Any, Any}) = x to3tuple(x) = ntuple(i-> x, Val(3)) -function draw_axis3d(textbuffer, linebuffer, scale, limits, ranges_labels, args...) +function draw_axis3d(textbuffer, linebuffer, scale, limits, ranges_labels, fonts, args...) # make sure we extend all args to 3D ranges, ticklabels = ranges_labels args3d = to3tuple.(args) @@ -284,7 +284,7 @@ function draw_axis3d(textbuffer, linebuffer, scale, limits, ranges_labels, args. end end if !isempty(axisnames[i]) - font = to_font(tfont[i]) + font = to_font(fonts, tfont[i]) tick_widths = maximum(ticklabels[i]) do label widths(text_bb(label, font, ttextsize[i]))[1] end / scale[j] @@ -337,7 +337,7 @@ function plot!(scene::SceneLike, ::Type{<: Axis3D}, attributes::Attributes, args map_once( draw_axis3d, Observable(textbuffer), Observable(linebuffer), scale(scene), - axis[1], axis.ticks.ranges_labels, args... + axis[1], axis.ticks.ranges_labels, Observable(axis.fonts), args... ) push!(scene, axis) return axis diff --git a/src/basic_recipes/buffers.jl b/src/basic_recipes/buffers.jl index c3d2801c453..15797c859fc 100644 --- a/src/basic_recipes/buffers.jl +++ b/src/basic_recipes/buffers.jl @@ -100,7 +100,11 @@ function append!(tb::Annotations, text_positions::Vector{Tuple{String, Point{N, isempty(tb[key][]) && error("please provide default for $key") return last(tb[key][]) end - val_vec = same_length_array(text_positions, val, Key{key}()) + val_vec = if key === :font + same_length_array(text_positions, to_font(tb.fonts, val)) + else + same_length_array(text_positions, val, Key{key}()) + end append!(tb[key][], val_vec) end return diff --git a/src/theming.jl b/src/theming.jl index 1839a15919c..644b3b6eb44 100644 --- a/src/theming.jl +++ b/src/theming.jl @@ -55,7 +55,7 @@ const default_palettes = Attributes( const minimal_default = Attributes( palette = default_palettes, - font = "TeX Gyre Heros Makie", + font = :regular, fonts = Attributes( :regular => "TeX Gyre Heros Makie", :bold => "TeX Gyre Heros Makie Bold", From 582ca031247da10937869150bb9afae522ae6cb4 Mon Sep 17 00:00:00 2001 From: Julius Krumbiegel Date: Mon, 24 Oct 2022 23:05:07 +0200 Subject: [PATCH 22/40] use rich text for logtick superscripts --- src/basic_recipes/text.jl | 9 ++++----- src/makielayout/lineaxis.jl | 2 +- 2 files changed, 5 insertions(+), 6 deletions(-) diff --git a/src/basic_recipes/text.jl b/src/basic_recipes/text.jl index fb05eaae7f6..40bd98700d1 100644 --- a/src/basic_recipes/text.jl +++ b/src/basic_recipes/text.jl @@ -262,11 +262,6 @@ end iswhitespace(l::LaTeXString) = iswhitespace(replace(l.s, '$' => "")) - - - - - struct RichText <: AbstractString type::Symbol children::Vector{Union{RichText,String}} @@ -278,6 +273,10 @@ struct RichText <: AbstractString end end +function Base.:(==)(r1::RichText, r2::RichText) + r1.type == r2.type && r1.children == r2.children && r1.attributes == r2.attributes +end + rich(args...; kwargs...) = RichText(:span, args...; kwargs...) subscript(args...; kwargs...) = RichText(:sub, args...; kwargs...) superscript(args...; kwargs...) = RichText(:sup, args...; kwargs...) diff --git a/src/makielayout/lineaxis.jl b/src/makielayout/lineaxis.jl index 3b5bd5dc154..c3e5b9efa75 100644 --- a/src/makielayout/lineaxis.jl +++ b/src/makielayout/lineaxis.jl @@ -587,7 +587,7 @@ function get_ticks(l::LogTicks, scale::Union{typeof(log10), typeof(log2), typeof xs -> Showoff.showoff(xs, :plain), ticks_scaled ) - labels = _logbase(scale) .* Makie.UnicodeFun.to_superscript.(labels_scaled) + labels = rich.(_logbase(scale), superscript.(labels_scaled)) (ticks, labels) end From 65fa6bd730e2848c113e176ba5d016d5737cedc2 Mon Sep 17 00:00:00 2001 From: Julius Krumbiegel Date: Tue, 25 Oct 2022 10:07:23 +0200 Subject: [PATCH 23/40] use minus glyph instead of hyphen for ticks --- src/makielayout/lineaxis.jl | 17 +++++++++++++---- 1 file changed, 13 insertions(+), 4 deletions(-) diff --git a/src/makielayout/lineaxis.jl b/src/makielayout/lineaxis.jl index c3e5b9efa75..e82c942967e 100644 --- a/src/makielayout/lineaxis.jl +++ b/src/makielayout/lineaxis.jl @@ -575,6 +575,11 @@ function get_ticks(::Automatic, scale::Union{typeof(log10), typeof(log2), typeof get_ticks(LogTicks(WilkinsonTicks(5, k_min = 3)), scale, any_formatter, vmin, vmax) end +# the hyphen which is usually used to store negative number strings +# is shorter than the dedicated minus in most fonts, the minus glyph +# looks more balanced with numbers, especially in superscripts or subscripts +const MINUS_SIGN = "−" + # log ticks just use the normal pipeline but with log'd limits, then transform the labels function get_ticks(l::LogTicks, scale::Union{typeof(log10), typeof(log2), typeof(log)}, ::Automatic, vmin, vmax) ticks_scaled = get_tickvalues(l.linear_ticks, identity, scale(vmin), scale(vmax)) @@ -587,7 +592,7 @@ function get_ticks(l::LogTicks, scale::Union{typeof(log10), typeof(log2), typeof xs -> Showoff.showoff(xs, :plain), ticks_scaled ) - labels = rich.(_logbase(scale), superscript.(labels_scaled)) + labels = rich.(_logbase(scale), superscript.(replace.(labels_scaled, "-" => MINUS_SIGN))) (ticks, labels) end @@ -650,9 +655,9 @@ end """ get_ticklabels(::Automatic, values) -Gets tick labels by applying `Showoff.showoff` to `values`. +Gets tick labels by applying `showoff_minus` to `values`. """ -get_ticklabels(::Automatic, values) = Showoff.showoff(values) +get_ticklabels(::Automatic, values) = showoff_minus(values) """ get_ticklabels(formatfunction::Function, values) @@ -674,9 +679,13 @@ function get_ticks(m::MultiplesTicks, any_scale, ::Automatic, vmin, vmax) dvmax = vmax / m.multiple multiples = Makie.get_tickvalues(LinearTicks(m.n_ideal), dvmin, dvmax) - multiples .* m.multiple, Showoff.showoff(multiples) .* m.suffix + multiples .* m.multiple, showoff_minus(multiples) .* m.suffix end +# Replaces hyphens in negative numbers with the unicode MINUS_SIGN +function showoff_minus(x::AbstractVector) + replace.(Showoff.showoff(x), r"-(?=\d)" => MINUS_SIGN) +end function get_minor_tickvalues(i::IntervalsBetween, scale, tickvalues, vmin, vmax) vals = Float64[] From da1e49d75eceaa2b5a4d5163f778ad83e3c7393d Mon Sep 17 00:00:00 2001 From: Julius Krumbiegel Date: Tue, 25 Oct 2022 10:13:02 +0200 Subject: [PATCH 24/40] change to dejavu because times doesn't exist --- docs/documentation/fonts.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/documentation/fonts.md b/docs/documentation/fonts.md index 0ecf23c2674..4951a4e651d 100644 --- a/docs/documentation/fonts.md +++ b/docs/documentation/fonts.md @@ -44,7 +44,7 @@ using CairoMakie CairoMakie.activate!() # hide Makie.inline!(true) # hide -f = Figure(fontsize = 30, fonts = (; regular = "Times", weird = "Blackchancery")) +f = Figure(fontsize = 30, fonts = (; regular = "Dejavu", weird = "Blackchancery")) Axis(f[1, 1], title = "A title", xlabel = "An x label", xlabelfont = :weird) f From e14a80ee7af04e11efe01799c3b718678f514f61 Mon Sep 17 00:00:00 2001 From: Julius Krumbiegel Date: Tue, 25 Oct 2022 12:23:15 +0200 Subject: [PATCH 25/40] fix vertical center align --- src/basic_recipes/text.jl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/basic_recipes/text.jl b/src/basic_recipes/text.jl index 40bd98700d1..e57c14042dc 100644 --- a/src/basic_recipes/text.jl +++ b/src/basic_recipes/text.jl @@ -377,7 +377,7 @@ function apply_alignment_and_justification!(lines, ju, al) end al_offset_y = if al[2] == :center - 0.5 * (top_y - bottom_y) + 0.5 * (top_y + bottom_y) elseif al[2] == :bottom bottom_y elseif al[2] == :top From a36939daa5813b305bbdfab46cc492652d0ddd7c Mon Sep 17 00:00:00 2001 From: Julius Krumbiegel Date: Tue, 25 Oct 2022 12:23:41 +0200 Subject: [PATCH 26/40] smaller fontsize --- docs/documentation/fonts.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/documentation/fonts.md b/docs/documentation/fonts.md index 4951a4e651d..8bdd18c6d6f 100644 --- a/docs/documentation/fonts.md +++ b/docs/documentation/fonts.md @@ -44,7 +44,7 @@ using CairoMakie CairoMakie.activate!() # hide Makie.inline!(true) # hide -f = Figure(fontsize = 30, fonts = (; regular = "Dejavu", weird = "Blackchancery")) +f = Figure(fontsize = 24, fonts = (; regular = "Dejavu", weird = "Blackchancery")) Axis(f[1, 1], title = "A title", xlabel = "An x label", xlabelfont = :weird) f From bbbe8cd6db1a51290e772ad9ea219ddc023b6065 Mon Sep 17 00:00:00 2001 From: Julius Krumbiegel Date: Fri, 28 Oct 2022 09:57:08 +0200 Subject: [PATCH 27/40] remove minus replacement again --- src/makielayout/lineaxis.jl | 18 ++++-------------- 1 file changed, 4 insertions(+), 14 deletions(-) diff --git a/src/makielayout/lineaxis.jl b/src/makielayout/lineaxis.jl index e82c942967e..4876502a78a 100644 --- a/src/makielayout/lineaxis.jl +++ b/src/makielayout/lineaxis.jl @@ -575,11 +575,6 @@ function get_ticks(::Automatic, scale::Union{typeof(log10), typeof(log2), typeof get_ticks(LogTicks(WilkinsonTicks(5, k_min = 3)), scale, any_formatter, vmin, vmax) end -# the hyphen which is usually used to store negative number strings -# is shorter than the dedicated minus in most fonts, the minus glyph -# looks more balanced with numbers, especially in superscripts or subscripts -const MINUS_SIGN = "−" - # log ticks just use the normal pipeline but with log'd limits, then transform the labels function get_ticks(l::LogTicks, scale::Union{typeof(log10), typeof(log2), typeof(log)}, ::Automatic, vmin, vmax) ticks_scaled = get_tickvalues(l.linear_ticks, identity, scale(vmin), scale(vmax)) @@ -592,7 +587,7 @@ function get_ticks(l::LogTicks, scale::Union{typeof(log10), typeof(log2), typeof xs -> Showoff.showoff(xs, :plain), ticks_scaled ) - labels = rich.(_logbase(scale), superscript.(replace.(labels_scaled, "-" => MINUS_SIGN))) + labels = rich.(_logbase(scale), superscript.(labels_scaled)) (ticks, labels) end @@ -655,9 +650,9 @@ end """ get_ticklabels(::Automatic, values) -Gets tick labels by applying `showoff_minus` to `values`. +Gets tick labels by applying `showoff` to `values`. """ -get_ticklabels(::Automatic, values) = showoff_minus(values) +get_ticklabels(::Automatic, values) = Showoff.showoff(values) """ get_ticklabels(formatfunction::Function, values) @@ -679,12 +674,7 @@ function get_ticks(m::MultiplesTicks, any_scale, ::Automatic, vmin, vmax) dvmax = vmax / m.multiple multiples = Makie.get_tickvalues(LinearTicks(m.n_ideal), dvmin, dvmax) - multiples .* m.multiple, showoff_minus(multiples) .* m.suffix -end - -# Replaces hyphens in negative numbers with the unicode MINUS_SIGN -function showoff_minus(x::AbstractVector) - replace.(Showoff.showoff(x), r"-(?=\d)" => MINUS_SIGN) + multiples .* m.multiple, Showoff.showoff(multiples) .* m.suffix end function get_minor_tickvalues(i::IntervalsBetween, scale, tickvalues, vmin, vmax) From 7daa2bd38a57a313fe779a41b179f649b4960685 Mon Sep 17 00:00:00 2001 From: Julius Krumbiegel Date: Fri, 28 Oct 2022 10:06:08 +0200 Subject: [PATCH 28/40] add show method for RichText --- src/basic_recipes/text.jl | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/src/basic_recipes/text.jl b/src/basic_recipes/text.jl index e57c14042dc..de77053d479 100644 --- a/src/basic_recipes/text.jl +++ b/src/basic_recipes/text.jl @@ -273,6 +273,18 @@ struct RichText <: AbstractString end end +function get_text(r::RichText) + fn(io, x::RichText) = foreach(x -> fn(io, x), x.children) + fn(io, s::String) = print(io, s) + sprint() do io + fn(io, r) + end +end + +function Base.show(io::IO, ::MIME"text/plain", r::RichText) + print(io, "RichText: \"$(get_text(r))\"") +end + function Base.:(==)(r1::RichText, r2::RichText) r1.type == r2.type && r1.children == r2.children && r1.attributes == r2.attributes end @@ -528,4 +540,4 @@ function new_glyphstate(gs::GlyphState2, rt::RichText, val::Val{:sub}, fonts) ) end -Makie.iswhitespace(::RichText) = false +Makie.iswhitespace(r::RichText) = iswhitespace(get_text(r)) From 112117b213d0f9ccde9ecc0b2706299c212a62b0 Mon Sep 17 00:00:00 2001 From: Julius Krumbiegel Date: Fri, 28 Oct 2022 10:11:00 +0200 Subject: [PATCH 29/40] use String instead --- src/basic_recipes/text.jl | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/basic_recipes/text.jl b/src/basic_recipes/text.jl index de77053d479..d7cdaeb5825 100644 --- a/src/basic_recipes/text.jl +++ b/src/basic_recipes/text.jl @@ -273,7 +273,7 @@ struct RichText <: AbstractString end end -function get_text(r::RichText) +function Base.String(r::RichText) fn(io, x::RichText) = foreach(x -> fn(io, x), x.children) fn(io, s::String) = print(io, s) sprint() do io @@ -282,7 +282,7 @@ function get_text(r::RichText) end function Base.show(io::IO, ::MIME"text/plain", r::RichText) - print(io, "RichText: \"$(get_text(r))\"") + print(io, "RichText: \"$(String(r))\"") end function Base.:(==)(r1::RichText, r2::RichText) @@ -540,4 +540,4 @@ function new_glyphstate(gs::GlyphState2, rt::RichText, val::Val{:sub}, fonts) ) end -Makie.iswhitespace(r::RichText) = iswhitespace(get_text(r)) +Makie.iswhitespace(r::RichText) = iswhitespace(String(r)) From 14f6fd22682ae652f4739b09a5dc10c69dc0f2f7 Mon Sep 17 00:00:00 2001 From: Julius Krumbiegel Date: Fri, 28 Oct 2022 10:18:01 +0200 Subject: [PATCH 30/40] add ability to set fontsize --- src/basic_recipes/text.jl | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/basic_recipes/text.jl b/src/basic_recipes/text.jl index d7cdaeb5825..ee421446121 100644 --- a/src/basic_recipes/text.jl +++ b/src/basic_recipes/text.jl @@ -506,13 +506,13 @@ end _get_color(attributes, default)::RGBAf = haskey(attributes, :color) ? Makie.to_color(attributes[:color]) : default _get_font(attributes, default::NativeFont, fonts)::NativeFont = haskey(attributes, :font) ? Makie.to_font(fonts, attributes[:font]) : default - +_get_fontsize(attributes, default)::Vec2f = haskey(attributes, :fontsize) ? Vec2f(Makie.to_textsize(attributes[:fontsize])) : default function new_glyphstate(gs::GlyphState2, rt::RichText, val::Val{:sup}, fonts) att = rt.attributes GlyphState2( gs.x, gs.baseline + 0.4 * gs.size[2], - gs.size * 0.66, + _get_fontsize(att, gs.size * 0.66), _get_font(att, gs.font, fonts), _get_color(att, gs.color), ) @@ -523,7 +523,7 @@ function new_glyphstate(gs::GlyphState2, rt::RichText, val::Val{:span}, fonts) GlyphState2( gs.x, gs.baseline, - gs.size, + _get_fontsize(att, gs.size), _get_font(att, gs.font, fonts), _get_color(att, gs.color), ) @@ -534,7 +534,7 @@ function new_glyphstate(gs::GlyphState2, rt::RichText, val::Val{:sub}, fonts) GlyphState2( gs.x, gs.baseline - 0.15 * gs.size[2], - gs.size * 0.66, + _get_fontsize(att, gs.size * 0.66), _get_font(att, gs.font, fonts), _get_color(att, gs.color), ) From a92ea49cfbcc7caf86a419643b5796da855824c2 Mon Sep 17 00:00:00 2001 From: Julius Krumbiegel Date: Fri, 28 Oct 2022 10:32:21 +0200 Subject: [PATCH 31/40] add xshift and yshift --- src/basic_recipes/text.jl | 30 +++++++++++++++++++++--------- 1 file changed, 21 insertions(+), 9 deletions(-) diff --git a/src/basic_recipes/text.jl b/src/basic_recipes/text.jl index ee421446121..a716d5f3452 100644 --- a/src/basic_recipes/text.jl +++ b/src/basic_recipes/text.jl @@ -507,12 +507,18 @@ end _get_color(attributes, default)::RGBAf = haskey(attributes, :color) ? Makie.to_color(attributes[:color]) : default _get_font(attributes, default::NativeFont, fonts)::NativeFont = haskey(attributes, :font) ? Makie.to_font(fonts, attributes[:font]) : default _get_fontsize(attributes, default)::Vec2f = haskey(attributes, :fontsize) ? Vec2f(Makie.to_textsize(attributes[:fontsize])) : default +_get_xshift(attributes, default)::Float32 = haskey(attributes, :xshift) ? Float32(attributes[:xshift]) : default +_get_yshift(attributes, default)::Float32 = haskey(attributes, :yshift) ? Float32(attributes[:yshift]) : default + function new_glyphstate(gs::GlyphState2, rt::RichText, val::Val{:sup}, fonts) att = rt.attributes + fontsize = _get_fontsize(att, gs.size * 0.66) + xshift = _get_xshift(att, 0f0) * fontsize[1] + yshift = _get_yshift(att, 0f0) * fontsize[2] GlyphState2( - gs.x, - gs.baseline + 0.4 * gs.size[2], - _get_fontsize(att, gs.size * 0.66), + gs.x + xshift, + gs.baseline + 0.4 * gs.size[2] + yshift, + fontsize, _get_font(att, gs.font, fonts), _get_color(att, gs.color), ) @@ -520,10 +526,13 @@ end function new_glyphstate(gs::GlyphState2, rt::RichText, val::Val{:span}, fonts) att = rt.attributes + fontsize = _get_fontsize(att, gs.size) + xshift = _get_xshift(att, 0f0) * fontsize[1] + yshift = _get_yshift(att, 0f0) * fontsize[2] GlyphState2( - gs.x, - gs.baseline, - _get_fontsize(att, gs.size), + gs.x + xshift, + gs.baseline + yshift, + fontsize, _get_font(att, gs.font, fonts), _get_color(att, gs.color), ) @@ -531,10 +540,13 @@ end function new_glyphstate(gs::GlyphState2, rt::RichText, val::Val{:sub}, fonts) att = rt.attributes + fontsize = _get_fontsize(att, gs.size * 0.66) + xshift = _get_xshift(att, 0f0) * fontsize[1] + yshift = _get_yshift(att, 0f0) * fontsize[2] GlyphState2( - gs.x, - gs.baseline - 0.15 * gs.size[2], - _get_fontsize(att, gs.size * 0.66), + gs.x + xshift, + gs.baseline - 0.15 * gs.size[2] + yshift, + fontsize, _get_font(att, gs.font, fonts), _get_color(att, gs.color), ) From ab4ea3b80a420488e5bd47d4d56e999221564ced Mon Sep 17 00:00:00 2001 From: Julius Krumbiegel Date: Fri, 28 Oct 2022 10:43:50 +0200 Subject: [PATCH 32/40] use offset instead --- src/basic_recipes/text.jl | 24 ++++++++++-------------- src/makielayout/lineaxis.jl | 2 +- 2 files changed, 11 insertions(+), 15 deletions(-) diff --git a/src/basic_recipes/text.jl b/src/basic_recipes/text.jl index a716d5f3452..defc79c50a4 100644 --- a/src/basic_recipes/text.jl +++ b/src/basic_recipes/text.jl @@ -507,17 +507,15 @@ end _get_color(attributes, default)::RGBAf = haskey(attributes, :color) ? Makie.to_color(attributes[:color]) : default _get_font(attributes, default::NativeFont, fonts)::NativeFont = haskey(attributes, :font) ? Makie.to_font(fonts, attributes[:font]) : default _get_fontsize(attributes, default)::Vec2f = haskey(attributes, :fontsize) ? Vec2f(Makie.to_textsize(attributes[:fontsize])) : default -_get_xshift(attributes, default)::Float32 = haskey(attributes, :xshift) ? Float32(attributes[:xshift]) : default -_get_yshift(attributes, default)::Float32 = haskey(attributes, :yshift) ? Float32(attributes[:yshift]) : default +_get_offset(attributes, default)::Vec2f = haskey(attributes, :offset) ? Vec2f(attributes[:offset]) : default function new_glyphstate(gs::GlyphState2, rt::RichText, val::Val{:sup}, fonts) att = rt.attributes fontsize = _get_fontsize(att, gs.size * 0.66) - xshift = _get_xshift(att, 0f0) * fontsize[1] - yshift = _get_yshift(att, 0f0) * fontsize[2] + offset = _get_offset(att, Vec2f(0)) .* fontsize GlyphState2( - gs.x + xshift, - gs.baseline + 0.4 * gs.size[2] + yshift, + gs.x + offset[1], + gs.baseline + 0.4 * gs.size[2] + offset[2], fontsize, _get_font(att, gs.font, fonts), _get_color(att, gs.color), @@ -527,11 +525,10 @@ end function new_glyphstate(gs::GlyphState2, rt::RichText, val::Val{:span}, fonts) att = rt.attributes fontsize = _get_fontsize(att, gs.size) - xshift = _get_xshift(att, 0f0) * fontsize[1] - yshift = _get_yshift(att, 0f0) * fontsize[2] + offset = _get_offset(att, Vec2f(0)) .* fontsize GlyphState2( - gs.x + xshift, - gs.baseline + yshift, + gs.x + offset[1], + gs.baseline + offset[2], fontsize, _get_font(att, gs.font, fonts), _get_color(att, gs.color), @@ -541,11 +538,10 @@ end function new_glyphstate(gs::GlyphState2, rt::RichText, val::Val{:sub}, fonts) att = rt.attributes fontsize = _get_fontsize(att, gs.size * 0.66) - xshift = _get_xshift(att, 0f0) * fontsize[1] - yshift = _get_yshift(att, 0f0) * fontsize[2] + offset = _get_offset(att, Vec2f(0)) .* fontsize GlyphState2( - gs.x + xshift, - gs.baseline - 0.15 * gs.size[2] + yshift, + gs.x + offset[1], + gs.baseline - 0.15 * gs.size[2] + offset[2], fontsize, _get_font(att, gs.font, fonts), _get_color(att, gs.color), diff --git a/src/makielayout/lineaxis.jl b/src/makielayout/lineaxis.jl index 4876502a78a..9a15bfd3d43 100644 --- a/src/makielayout/lineaxis.jl +++ b/src/makielayout/lineaxis.jl @@ -587,7 +587,7 @@ function get_ticks(l::LogTicks, scale::Union{typeof(log10), typeof(log2), typeof xs -> Showoff.showoff(xs, :plain), ticks_scaled ) - labels = rich.(_logbase(scale), superscript.(labels_scaled)) + labels = rich.(_logbase(scale), superscript.(labels_scaled, offset = Vec2f(0.1f0, 0f0))) (ticks, labels) end From 9d822788033e60c9c796f4d58ec8b0988ddf4fcf Mon Sep 17 00:00:00 2001 From: Julius Krumbiegel Date: Fri, 28 Oct 2022 12:40:29 +0200 Subject: [PATCH 33/40] add offset example --- docs/examples/plotting_functions/text.md | 41 +++++++++++++++++++++++- 1 file changed, 40 insertions(+), 1 deletion(-) diff --git a/docs/examples/plotting_functions/text.md b/docs/examples/plotting_functions/text.md index db6231698b9..a3d395715ad 100644 --- a/docs/examples/plotting_functions/text.md +++ b/docs/examples/plotting_functions/text.md @@ -226,10 +226,49 @@ Label( str = "A beautiful rainbow" rainbow = cgrad(:rainbow, length(str), categorical = true) -rainbow_chars = [rich("$c", color = rainbow[i]) for (i, c) in enumerate(str)] +fontsizes = 30 .+ 10 .* sin.(range(0, 3pi, length = length(str))) + +rainbow_chars = map(enumerate(str)) do (i, c) + rich("$c", color = rainbow[i], fontsize = fontsizes[i]) +end Label(f[2, 1], rich(rainbow_chars...), font = :bold) f ``` \end{examplefigure} + +### Tweaking offsets + +Sometimes, when using regular and italic fonts next to each other, the gaps between glyphs are too narrow or too wide. +You can use the `offset` value for rich text to shift glyphs by an amount proportional to the fontsize. + + +\begin{examplefigure}{svg = true} +```julia +using CairoMakie +CairoMakie.activate!() # hide +Makie.inline!(true) # hide + +f = Figure(fontsize = 30) +Label( + f[1, 1], + rich( + "ITALIC", + superscript("Regular without x offset", font = :regular), + font = :italic + ) +) + +Label( + f[2, 1], + rich( + "ITALIC", + superscript("Regular with x offset", font = :regular, offset = (0.15, 0)), + font = :italic + ) +) + +f +``` +\end{examplefigure} \ No newline at end of file From 4339e3e3d66deacb066c0f70e89a3601dc963d9f Mon Sep 17 00:00:00 2001 From: Julius Krumbiegel Date: Fri, 28 Oct 2022 13:03:00 +0200 Subject: [PATCH 34/40] add test, fix rotation, add section about sub/superscripts --- ReferenceTests/src/tests/examples2d.jl | 16 +++++++ docs/examples/plotting_functions/text.md | 54 ++++++++++++++++++++++++ src/basic_recipes/text.jl | 9 +++- 3 files changed, 78 insertions(+), 1 deletion(-) diff --git a/ReferenceTests/src/tests/examples2d.jl b/ReferenceTests/src/tests/examples2d.jl index 25540ff5527..49353750766 100644 --- a/ReferenceTests/src/tests/examples2d.jl +++ b/ReferenceTests/src/tests/examples2d.jl @@ -761,3 +761,19 @@ end ax.yticks = ([3, 6, 9], [L"x" , L"y" , L"z"]) f end + +@reference_test "Rich text" +begin + f = Figure(fontsize = 30) + ax = Axis(f[1, 1], + limits = (1, 100, 0.001, 1), + xscale = log10, + yscale = log2, + title = rich("A ", rich("title", color = :red, font = :bold_italic)), + xlabel = rich("X", subscript("label", fontsize = 25)), + ylabel = rich("Y", superscript("label")), + ) + Label(f[1, 2], rich("Hi", rich("Hi", offset = (0.2, 0.2), color = :blue)), tellheight = false) + Label(f[1, 3], rich("X", superscript("super"), subscript("sub")), tellheight = false) + f +end \ No newline at end of file diff --git a/docs/examples/plotting_functions/text.md b/docs/examples/plotting_functions/text.md index a3d395715ad..693807304bb 100644 --- a/docs/examples/plotting_functions/text.md +++ b/docs/examples/plotting_functions/text.md @@ -269,6 +269,60 @@ Label( ) ) +f +``` +\end{examplefigure} + + +### Superscript and subscript + +There's a special behavior for a subscript following a superscript. +The subscript will be placed below the superscript, not to the right. +This way, even expressions with both sub- and superscript are possible, like they +sometimes appear in chemistry, for example. + +If you don't want this, you can place an empty string inbetween, for example. + +In the example below, the third `Label` also has adjusted baselines for the super/subscripts, as some conventions require a larger distance between the two than Makie's default setting. + +```julia +using CairoMakie +CairoMakie.activate!() # hide +Makie.inline!(true) # hide + +f = Figure(fontsize = 30) + +# normal +Label( + f[1, 1], + rich( + "C", + superscript("1"), + subscript("2"), + ) +) + +# with empty string +Label( + f[2, 1], + rich( + "C", + superscript("1"), + "", + subscript("2"), + ) +) + +# with adjusted baselines +Label( + f[3, 1], + rich( + "C", + superscript("1", offset = (0, 0.2)), + subscript("2", offset = (0, -0.2)), + ) +) + f ``` \end{examplefigure} \ No newline at end of file diff --git a/src/basic_recipes/text.jl b/src/basic_recipes/text.jl index defc79c50a4..5237d9b9642 100644 --- a/src/basic_recipes/text.jl +++ b/src/basic_recipes/text.jl @@ -281,6 +281,8 @@ function Base.String(r::RichText) end end +Base.ncodeunits(r::RichText) = ncodeunits(String(r)) # needed for isempty + function Base.show(io::IO, ::MIME"text/plain", r::RichText) print(io, "RichText: \"$(String(r))\"") end @@ -349,7 +351,12 @@ function Makie.layout_text(rt::RichText, ts, f, fset, al, rot, jus, lh, col) apply_lineheight!(lines, lh) apply_alignment_and_justification!(lines, jus, al) - Makie.GlyphCollection(reduce(vcat, lines)) + gc = Makie.GlyphCollection(reduce(vcat, lines)) + quat = Makie.to_rotation(rot)::Quaternionf + gc.origins .= Ref(quat) .* gc.origins + @assert gc.rotations.sv isa Vector # should always be a vector because that's how the glyphcollection is created + gc.rotations.sv .= Ref(quat) .* gc.rotations.sv + gc end function apply_lineheight!(lines, lh) From 3c9a1cf6227fe90ee5011fd8bc26b4a8881ec185 Mon Sep 17 00:00:00 2001 From: Julius Krumbiegel Date: Fri, 28 Oct 2022 13:56:14 +0200 Subject: [PATCH 35/40] fix test syntax --- ReferenceTests/src/tests/examples2d.jl | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/ReferenceTests/src/tests/examples2d.jl b/ReferenceTests/src/tests/examples2d.jl index 49353750766..9d9adef2633 100644 --- a/ReferenceTests/src/tests/examples2d.jl +++ b/ReferenceTests/src/tests/examples2d.jl @@ -762,8 +762,7 @@ end f end -@reference_test "Rich text" -begin +@reference_test "Rich text" begin f = Figure(fontsize = 30) ax = Axis(f[1, 1], limits = (1, 100, 0.001, 1), From 701765e8daca8a9378f8298c1c9499044d87e32a Mon Sep 17 00:00:00 2001 From: Julius Krumbiegel Date: Fri, 28 Oct 2022 13:57:39 +0200 Subject: [PATCH 36/40] fix example block syntax --- docs/examples/plotting_functions/text.md | 1 + 1 file changed, 1 insertion(+) diff --git a/docs/examples/plotting_functions/text.md b/docs/examples/plotting_functions/text.md index 693807304bb..fe75b57455d 100644 --- a/docs/examples/plotting_functions/text.md +++ b/docs/examples/plotting_functions/text.md @@ -285,6 +285,7 @@ If you don't want this, you can place an empty string inbetween, for example. In the example below, the third `Label` also has adjusted baselines for the super/subscripts, as some conventions require a larger distance between the two than Makie's default setting. +\begin{examplefigure}{svg = true} ```julia using CairoMakie CairoMakie.activate!() # hide From 94470871e35c4d8a40c6eb18522ed0122b93f751 Mon Sep 17 00:00:00 2001 From: ffreyer Date: Wed, 2 Nov 2022 12:25:03 +0100 Subject: [PATCH 37/40] cleanup type names and "Makie." --- src/basic_recipes/text.jl | 69 +++++++++++++++++++-------------------- 1 file changed, 34 insertions(+), 35 deletions(-) diff --git a/src/basic_recipes/text.jl b/src/basic_recipes/text.jl index 5237d9b9642..990ca418703 100644 --- a/src/basic_recipes/text.jl +++ b/src/basic_recipes/text.jl @@ -297,34 +297,33 @@ superscript(args...; kwargs...) = RichText(:sup, args...; kwargs...) export rich, subscript, superscript -## -function Makie._get_glyphcollection_and_linesegments(rt::RichText, index, ts, f, fset, al, rot, jus, lh, col, scol, swi, www) - gc = Makie.layout_text(rt, ts, f, fset, al, rot, jus, lh, col) - gc, Point2f[], Float32[], Makie.RGBAf[], Int[] +function _get_glyphcollection_and_linesegments(rt::RichText, index, ts, f, fset, al, rot, jus, lh, col, scol, swi, www) + gc = layout_text(rt, ts, f, fset, al, rot, jus, lh, col) + gc, Point2f[], Float32[], RGBAf[], Int[] end -struct GlyphState2 +struct GlyphState x::Float32 baseline::Float32 size::Vec2f - font::Makie.FreeTypeAbstraction.FTFont + font::FreeTypeAbstraction.FTFont color::RGBAf end -struct GlyphInfo2 +struct GlyphInfo glyph::Int - font::Makie.FreeTypeAbstraction.FTFont + font::FreeTypeAbstraction.FTFont origin::Point2f - extent::Makie.GlyphExtent + extent::GlyphExtent size::Vec2f - rotation::Makie.Quaternion + rotation::Quaternion color::RGBAf strokecolor::RGBAf strokewidth::Float32 end -function Makie.GlyphCollection(v::Vector{GlyphInfo2}) - Makie.GlyphCollection( +function GlyphCollection(v::Vector{GlyphInfo}) + GlyphCollection( [i.glyph for i in v], [i.font for i in v], [Point3f(i.origin..., 0) for i in v], @@ -338,21 +337,21 @@ function Makie.GlyphCollection(v::Vector{GlyphInfo2}) end -function Makie.layout_text(rt::RichText, ts, f, fset, al, rot, jus, lh, col) +function layout_text(rt::RichText, ts, f, fset, al, rot, jus, lh, col) _f = to_font(fset, f) - stack = [GlyphState2(0, 0, Vec2f(ts), _f, Makie.to_color(col))] + stack = [GlyphState(0, 0, Vec2f(ts), _f, to_color(col))] - lines = [GlyphInfo2[]] + lines = [GlyphInfo[]] process_rt_node!(stack, lines, rt, fset) apply_lineheight!(lines, lh) apply_alignment_and_justification!(lines, jus, al) - gc = Makie.GlyphCollection(reduce(vcat, lines)) - quat = Makie.to_rotation(rot)::Quaternionf + gc = GlyphCollection(reduce(vcat, lines)) + quat = to_rotation(rot)::Quaternionf gc.origins .= Ref(quat) .* gc.origins @assert gc.rotations.sv isa Vector # should always be a vector because that's how the glyphcollection is created gc.rotations.sv .= Ref(quat) .* gc.rotations.sv @@ -473,7 +472,7 @@ function process_rt_node!(stack, lines, rt::RichText, fonts) gs = pop!(stack) gs_top = stack[end] # x needs to continue even if going a level up - stack[end] = GlyphState2(gs.x, gs_top.baseline, gs_top.size, gs_top.font, gs_top.color) + stack[end] = GlyphState(gs.x, gs_top.baseline, gs_top.size, gs_top.font, gs_top.color) return end @@ -484,18 +483,18 @@ function process_rt_node!(stack, lines, s::String, _) for char in s if char === '\n' x = 0 - push!(lines, GlyphInfo2[]) + push!(lines, GlyphInfo[]) else - gi = Makie.FreeTypeAbstraction.glyph_index(gs.font, char) - gext = Makie.GlyphExtent(gs.font, char) + gi = FreeTypeAbstraction.glyph_index(gs.font, char) + gext = GlyphExtent(gs.font, char) ori = Point2f(x, y) - push!(lines[end], GlyphInfo2( + push!(lines[end], GlyphInfo( gi, gs.font, ori, gext, gs.size, - Makie.to_rotation(0), + to_rotation(0), gs.color, RGBAf(0, 0, 0, 0), 0f0, @@ -503,24 +502,24 @@ function process_rt_node!(stack, lines, s::String, _) x = x + gext.hadvance * gs.size[1] end end - stack[end] = GlyphState2(x, y, gs.size, gs.font, gs.color) + stack[end] = GlyphState(x, y, gs.size, gs.font, gs.color) return end -function new_glyphstate(gs::GlyphState2, rt::RichText, val::Val, fonts) +function new_glyphstate(gs::GlyphState, rt::RichText, val::Val, fonts) gs end -_get_color(attributes, default)::RGBAf = haskey(attributes, :color) ? Makie.to_color(attributes[:color]) : default -_get_font(attributes, default::NativeFont, fonts)::NativeFont = haskey(attributes, :font) ? Makie.to_font(fonts, attributes[:font]) : default -_get_fontsize(attributes, default)::Vec2f = haskey(attributes, :fontsize) ? Vec2f(Makie.to_textsize(attributes[:fontsize])) : default +_get_color(attributes, default)::RGBAf = haskey(attributes, :color) ? to_color(attributes[:color]) : default +_get_font(attributes, default::NativeFont, fonts)::NativeFont = haskey(attributes, :font) ? to_font(fonts, attributes[:font]) : default +_get_fontsize(attributes, default)::Vec2f = haskey(attributes, :fontsize) ? Vec2f(to_textsize(attributes[:fontsize])) : default _get_offset(attributes, default)::Vec2f = haskey(attributes, :offset) ? Vec2f(attributes[:offset]) : default -function new_glyphstate(gs::GlyphState2, rt::RichText, val::Val{:sup}, fonts) +function new_glyphstate(gs::GlyphState, rt::RichText, val::Val{:sup}, fonts) att = rt.attributes fontsize = _get_fontsize(att, gs.size * 0.66) offset = _get_offset(att, Vec2f(0)) .* fontsize - GlyphState2( + GlyphState( gs.x + offset[1], gs.baseline + 0.4 * gs.size[2] + offset[2], fontsize, @@ -529,11 +528,11 @@ function new_glyphstate(gs::GlyphState2, rt::RichText, val::Val{:sup}, fonts) ) end -function new_glyphstate(gs::GlyphState2, rt::RichText, val::Val{:span}, fonts) +function new_glyphstate(gs::GlyphState, rt::RichText, val::Val{:span}, fonts) att = rt.attributes fontsize = _get_fontsize(att, gs.size) offset = _get_offset(att, Vec2f(0)) .* fontsize - GlyphState2( + GlyphState( gs.x + offset[1], gs.baseline + offset[2], fontsize, @@ -542,11 +541,11 @@ function new_glyphstate(gs::GlyphState2, rt::RichText, val::Val{:span}, fonts) ) end -function new_glyphstate(gs::GlyphState2, rt::RichText, val::Val{:sub}, fonts) +function new_glyphstate(gs::GlyphState, rt::RichText, val::Val{:sub}, fonts) att = rt.attributes fontsize = _get_fontsize(att, gs.size * 0.66) offset = _get_offset(att, Vec2f(0)) .* fontsize - GlyphState2( + GlyphState( gs.x + offset[1], gs.baseline - 0.15 * gs.size[2] + offset[2], fontsize, @@ -555,4 +554,4 @@ function new_glyphstate(gs::GlyphState2, rt::RichText, val::Val{:sub}, fonts) ) end -Makie.iswhitespace(r::RichText) = iswhitespace(String(r)) +iswhitespace(r::RichText) = iswhitespace(String(r)) From f93d1574c680414ee4d7cefac43ca093804f6c05 Mon Sep 17 00:00:00 2001 From: ffreyer Date: Wed, 2 Nov 2022 15:13:55 +0100 Subject: [PATCH 38/40] capitalize string to make fontsize changes more obvious --- docs/examples/plotting_functions/text.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/examples/plotting_functions/text.md b/docs/examples/plotting_functions/text.md index fe75b57455d..8a135aa0982 100644 --- a/docs/examples/plotting_functions/text.md +++ b/docs/examples/plotting_functions/text.md @@ -224,7 +224,7 @@ Label( ) ) -str = "A beautiful rainbow" +str = "A BEAUTIFUL RAINBOW" rainbow = cgrad(:rainbow, length(str), categorical = true) fontsizes = 30 .+ 10 .* sin.(range(0, 3pi, length = length(str))) From 3f821043cda828eb43759d33ffb18f289690c808 Mon Sep 17 00:00:00 2001 From: Julius Krumbiegel Date: Wed, 23 Nov 2022 10:12:05 +0100 Subject: [PATCH 39/40] remove super-subscript stacking (for now) --- docs/examples/plotting_functions/text.md | 55 ------------------------ src/basic_recipes/text.jl | 23 +--------- 2 files changed, 1 insertion(+), 77 deletions(-) diff --git a/docs/examples/plotting_functions/text.md b/docs/examples/plotting_functions/text.md index 8a135aa0982..06c7e1c6492 100644 --- a/docs/examples/plotting_functions/text.md +++ b/docs/examples/plotting_functions/text.md @@ -272,58 +272,3 @@ Label( f ``` \end{examplefigure} - - -### Superscript and subscript - -There's a special behavior for a subscript following a superscript. -The subscript will be placed below the superscript, not to the right. -This way, even expressions with both sub- and superscript are possible, like they -sometimes appear in chemistry, for example. - -If you don't want this, you can place an empty string inbetween, for example. - -In the example below, the third `Label` also has adjusted baselines for the super/subscripts, as some conventions require a larger distance between the two than Makie's default setting. - -\begin{examplefigure}{svg = true} -```julia -using CairoMakie -CairoMakie.activate!() # hide -Makie.inline!(true) # hide - -f = Figure(fontsize = 30) - -# normal -Label( - f[1, 1], - rich( - "C", - superscript("1"), - subscript("2"), - ) -) - -# with empty string -Label( - f[2, 1], - rich( - "C", - superscript("1"), - "", - subscript("2"), - ) -) - -# with adjusted baselines -Label( - f[3, 1], - rich( - "C", - superscript("1", offset = (0, 0.2)), - subscript("2", offset = (0, -0.2)), - ) -) - -f -``` -\end{examplefigure} \ No newline at end of file diff --git a/src/basic_recipes/text.jl b/src/basic_recipes/text.jl index 990ca418703..f4cfd77e2e4 100644 --- a/src/basic_recipes/text.jl +++ b/src/basic_recipes/text.jl @@ -445,29 +445,8 @@ function process_rt_node!(stack, lines, rt::RichText, fonts) _type(r::RichText) = r.type push!(stack, new_glyphstate(stack[end], rt, Val(rt.type), fonts)) - sup_x = 0f0 for (i, c) in enumerate(rt.children) - if _type(c) == :sup - sup_x = stack[end].x - end - # This special implementation allows to stack super and subscripts. - # In the naive implementation, x can only grow with each character, - # however, to stack super and subscript, we need to track back to the - # previous x value and afterwards continue with the maximum of super - # and subscript. - if i > 1 && _type(c) === :sub && _type(rt.children[i-1]) == :sup - gs = stack[end] - sup_x_end = gs.x - gs_modified = Setfield.@set gs.x = sup_x - stack[end] = gs_modified - process_rt_node!(stack, lines, c, fonts) - gs = stack[end] - max_x = max(sup_x_end, gs.x) - gs_max_x = Setfield.@set gs.x = max_x - stack[end] = gs_max_x - else - process_rt_node!(stack, lines, c, fonts) - end + process_rt_node!(stack, lines, c, fonts) end gs = pop!(stack) gs_top = stack[end] From b6c035fd042a8109f156c88b299efe89efe8cb92 Mon Sep 17 00:00:00 2001 From: Julius Krumbiegel Date: Wed, 23 Nov 2022 14:51:54 +0100 Subject: [PATCH 40/40] add resolution to test image --- ReferenceTests/src/tests/examples2d.jl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ReferenceTests/src/tests/examples2d.jl b/ReferenceTests/src/tests/examples2d.jl index f5ef42b9eb7..02043ac12a9 100644 --- a/ReferenceTests/src/tests/examples2d.jl +++ b/ReferenceTests/src/tests/examples2d.jl @@ -763,7 +763,7 @@ end end @reference_test "Rich text" begin - f = Figure(fontsize = 30) + f = Figure(fontsize = 30, resolution = (800, 600)) ax = Axis(f[1, 1], limits = (1, 100, 0.001, 1), xscale = log10,