diff --git a/Project.toml b/Project.toml index 8dfc70a..304cc19 100644 --- a/Project.toml +++ b/Project.toml @@ -1,7 +1,7 @@ name = "MathTeXEngine" uuid = "0a4f8689-d25c-4efe-a92b-7142dfc1aa53" authors = ["Benoît Richard "] -version = "0.6.0" +version = "0.6.1" [deps] AbstractTrees = "1520ce14-60c1-5f80-bbc7-55ef81b5835c" diff --git a/assets/fonts/TeXGyreHerosMakie/LICENSES.md b/assets/fonts/TeXGyreHerosMakie/LICENSES.md new file mode 100644 index 0000000..3d6ddc5 --- /dev/null +++ b/assets/fonts/TeXGyreHerosMakie/LICENSES.md @@ -0,0 +1,30 @@ +# TeX Gyre Heros + +% This is version 1.0, dated 22 June 2009, of the GUST Font License. +% (GUST is the Polish TeX Users Group, http://www.gust.org.pl) +% +% For the most recent version of this license see +% http://www.gust.org.pl/fonts/licenses/GUST-FONT-LICENSE.txt +% or +% http://tug.org/fonts/licenses/GUST-FONT-LICENSE.txt +% +% This work may be distributed and/or modified under the conditions +% of the LaTeX Project Public License, either version 1.3c of this +% license or (at your option) any later version. +% +% Please also observe the following clause: +% 1) it is requested, but not legally required, that derived works be +% distributed only after changing the names of the fonts comprising this +% work and given in an accompanying "manifest", and that the +% files comprising the Work, as listed in the manifest, also be given +% new names. Any exceptions to this request are also given in the +% manifest. +% +% We recommend the manifest be given in a separate file named +% MANIFEST-.txt, where is some unique identification +% of the font family. If a separate "readme" file accompanies the Work, +% we recommend a name of the form README-.txt. +% +% The latest version of the LaTeX Project Public License is in +% http://www.latex-project.org/lppl.txt and version 1.3c or later +% is part of all distributions of LaTeX version 2006/05/20 or later. \ No newline at end of file diff --git a/assets/fonts/TeXGyreHerosMakie/TeXGyreHerosMakie-Bold.otf b/assets/fonts/TeXGyreHerosMakie/TeXGyreHerosMakie-Bold.otf new file mode 100644 index 0000000..563cb05 Binary files /dev/null and b/assets/fonts/TeXGyreHerosMakie/TeXGyreHerosMakie-Bold.otf differ diff --git a/assets/fonts/TeXGyreHerosMakie/TeXGyreHerosMakie-BoldItalic.otf b/assets/fonts/TeXGyreHerosMakie/TeXGyreHerosMakie-BoldItalic.otf new file mode 100644 index 0000000..ba32117 Binary files /dev/null and b/assets/fonts/TeXGyreHerosMakie/TeXGyreHerosMakie-BoldItalic.otf differ diff --git a/assets/fonts/TeXGyreHerosMakie/TeXGyreHerosMakie-Italic.otf b/assets/fonts/TeXGyreHerosMakie/TeXGyreHerosMakie-Italic.otf new file mode 100644 index 0000000..5f5b0d7 Binary files /dev/null and b/assets/fonts/TeXGyreHerosMakie/TeXGyreHerosMakie-Italic.otf differ diff --git a/assets/fonts/TeXGyreHerosMakie/TeXGyreHerosMakie-Regular.otf b/assets/fonts/TeXGyreHerosMakie/TeXGyreHerosMakie-Regular.otf new file mode 100644 index 0000000..7d81655 Binary files /dev/null and b/assets/fonts/TeXGyreHerosMakie/TeXGyreHerosMakie-Regular.otf differ diff --git a/prototype/prototype.jl b/prototype/prototype.jl index 0cabe8e..8331eda 100644 --- a/prototype/prototype.jl +++ b/prototype/prototype.jl @@ -20,7 +20,7 @@ function draw_texelement!(ax, texchar::TeXChar, position, scale ; size=64) # TODO This doesn't make sense anymore text!(ax, string(Char(texchar.represented_char)), font=texchar.font, position=Point2f(x, y), - textsize=size*scale, + fontsize=size*scale, space=:data, markerspace=:data, align=(:left, :baseline), @@ -134,11 +134,12 @@ end function makie_tex!( ax, latex::LaTeXString ; - debug=false, - size=64, - position=[0, 0]) + debug = false, + size = 64, + position = [0, 0], + fontfamily = FontFamily()) - for (elem, pos, scale) in generate_tex_elements(latex) + for (elem, pos, scale) in generate_tex_elements(latex, fontfamily) draw_texelement!(ax, elem, pos .+ position, scale ; size=size) if debug draw_texelement_helpers!(ax, elem, pos .+ position, scale ; size=size) @@ -148,20 +149,21 @@ end begin # Quick test fig = Figure(size=(1800, 1000)) - fig[1, 1] = Label(fig, "LaTeX in Makie.jl", tellwidth=false, textsize=64) + fig[1, 1] = Label(fig, "LaTeX in Makie.jl", tellwidth=false, fontsize=64) ax = Axis(fig[2, 1]) hidedecorations!(ax) ax.aspect = DataAspect() tex = L"\nabla 3\degree \partial L^3 \sum \lim_{L →\infty} - \varphi \phi \varpi \pi \varepsilon \epsilon + \frac{\varphi \phi \varpi}{\pi \varepsilon \epsilon} ℝ^\sqrt{A + j + 2 + 3} |x^2|^3 = \sum_{k = 1}^N \vec{v}_{(a' + \bar{a})_k} + \sqrt{T} x! \quad \mathrm{when} \quad \left[ \sqrt{\frac{\Omega-2}{a \langle c^\dagger \rangle b}} \right]^3_3 < \int_{0}^{2π} |\sin(\mu x)| dx" - tex = L"\mathcal{A} \mathbb{R} \longrightarrow xyz \text{x y z} \mathrm{x y z}" + # tex = L"\mathcal{A} \mathbb{R} \longrightarrow xyz \text{x y z} \mathrm{x y z}" + # tex = L"$p_z$ ($10^5$ a. u.)" - makie_tex!(ax, tex, debug=true, size=64) - fig[3, 1] = Label(fig, tex, tellwidth=false, tellheight=false, textsize=40) + makie_tex!(ax, tex, size=64, fontfamily = FontFamily("TeXGyreHeros")) + fig[3, 1] = Label(fig, tex, tellwidth=false, tellheight=false, fontsize=40) fig end diff --git a/src/MathTeXEngine.jl b/src/MathTeXEngine.jl index a3b184c..588abed 100644 --- a/src/MathTeXEngine.jl +++ b/src/MathTeXEngine.jl @@ -22,7 +22,7 @@ import FreeTypeAbstraction: export TeXToken, tokenize export TeXExpr, texparse, TeXParseError, manual_texexpr export TeXElement, TeXChar, VLine, HLine, generate_tex_elements -export texfont +export texfont, FontFamily export glyph_index # Reexport from LaTeXStrings diff --git a/src/engine/fonts.jl b/src/engine/fonts.jl index cba050e..3ff13d5 100644 --- a/src/engine/fonts.jl +++ b/src/engine/fonts.jl @@ -25,21 +25,7 @@ end # Loading the font directly here lead to FreeTypeAbstraction to fail with error # code 35, because handles to fonts are C pointer that cannot be fully # serialized at compile time -const _new_computer_modern_fonts = Dict( - :regular => joinpath("NewComputerModern", "NewCMMath-Regular.otf"), - :italic => joinpath("NewComputerModern", "NewCM10-Italic.otf"), - :bold => joinpath("NewComputerModern", "NewCM10-Bold.otf"), - :bolditalic => joinpath("NewComputerModern", "NewCM10-BoldItalic.otf"), - :math => joinpath("NewComputerModern", "NewCMMath-Regular.otf") -) -const _computer_modern_fonts = Dict( - :regular => joinpath("ComputerModern", "cmr10.ttf"), - :italic => joinpath("ComputerModern", "cmmi10.ttf"), - :bold => joinpath("ComputerModern", "cmb10.ttf"), - :bolditalic => joinpath("ComputerModern", "cmmib10.ttf"), - :math => joinpath("ComputerModern", "cmmi10.ttf") -) const _default_font_mapping = Dict( :text => :regular, @@ -58,20 +44,27 @@ const _default_font_modifiers = Dict( ) """ - FontFamily([fonts, font_mapping, font_modifiers, slant_angle]) + FontFamily(fonts ; font_mapping, font_modifiers, special_chars, slant_angle, thickness) A set of font for LaTeX rendering. -# Fields +# Required fields + - `fonts` A with the path to 5 fonts (:regular, :italic, :bold, :bolditalic, + and :math). The same font can be used for multiple entries, and unrelated + fonts can be mixed. + +# Optional fields - `font_mapping` a dict mapping the different character types (`:digit`, `:function`, `:punctuation`, `:symbol`, `:variable`) to a font identifier. Default to `MathTeXEngine._default_font_mapping` - - `fonts` a dict mapping font identifier to a font path. Default to - `MathTeXEngine._default_fonts` which represents the NewComputerModern font. - `font_modifiers` a dict of dict, one entry per font command supported in the font set. Each entry is a dict that maps a font identifier to another. Default to `MathTeXEngine._default_font_modifiers`. - - `slant_angle` the angle by which the italic fonts are slanted, in degree + - `specail_chars` mapping for special characters that should not be + represented by their default unicode glyph + (for example necessary to access the big integral glyph). + - `slant_angle` the angle by which the italic fonts are slanted, in degree. + - `thickness` the thickness of the lines associated to the font. """ struct FontFamily fonts::Dict{Symbol, String} @@ -82,29 +75,64 @@ struct FontFamily thickness::Float64 end +function FontFamily(fonts::Dict ; + font_mapping = _default_font_mapping, + font_modifiers = _default_font_modifiers, + special_chars = Dict{Char, Tuple{String, Int}}(), + slant_angle = 13, + thickness = 0.0375) + + return FontFamily( + fonts, + font_mapping, + font_modifiers, + special_chars, + slant_angle, + thickness + ) +end + +""" + FontFamily(name::String = "NewComputerModern") + +One of the default set of font for LaTeX rendering. + +Currently available are +- NewComputerModern +- TeXGyreHeros + +These names can also be used in a LaTeXString directly, +with the command `\\fontfamily`, +e.g. L"\\fontfamily{TeXGyreHeros}x^2_3". +""" FontFamily() = FontFamily("NewComputerModern") -FontFamily(fontname) = default_font_families[fontname] +FontFamily(fontname::AbstractString) = default_font_families[fontname] # These two fonts internals are very different, despite their similar names # We only try to fully support NewComputerModern, the other is here as it may # sometime provide quickfix solution to bug const default_font_families = Dict( "NewComputerModern" => FontFamily( - _new_computer_modern_fonts, - _default_font_mapping, - _default_font_modifiers, - _symbol_to_new_computer_modern, - 13, - 0.0375), - "ComputerModern" => FontFamily( - _computer_modern_fonts, - _default_font_mapping, - _default_font_modifiers, - _symbol_to_computer_modern, - 15, - 0.0375) + Dict( + :regular => joinpath("NewComputerModern", "NewCMMath-Regular.otf"), + :italic => joinpath("NewComputerModern", "NewCM10-Italic.otf"), + :bold => joinpath("NewComputerModern", "NewCM10-Bold.otf"), + :bolditalic => joinpath("NewComputerModern", "NewCM10-BoldItalic.otf"), + :math => joinpath("NewComputerModern", "NewCMMath-Regular.otf") + ), + special_chars =_symbol_to_new_computer_modern), + "TeXGyreHeros" => FontFamily( + Dict( + :regular => joinpath("TeXGyreHerosMakie", "TeXGyreHerosMakie-Regular.otf"), + :italic => joinpath("TeXGyreHerosMakie", "TeXGyreHerosMakie-Italic.otf"), + :bold => joinpath("TeXGyreHerosMakie", "TeXGyreHerosMakie-Bold.otf"), + :bolditalic => joinpath("TeXGyreHerosMakie", "TeXGyreHerosMakie-BoldItalic.otf"), + :math => joinpath("TeXGyreHerosMakie", "TeXGyreHerosMakie-Regular.otf") + ) + ) ) + """ get_font([font_family=FontFamily()], fontstyle) diff --git a/src/engine/layout.jl b/src/engine/layout.jl index 0b77a84..bba9471 100644 --- a/src/engine/layout.jl +++ b/src/engine/layout.jl @@ -104,6 +104,8 @@ function tex_layout(expr, state) elseif head == :font modifier, content = args return tex_layout(content, add_font_modifier(state, modifier)) + elseif head == :fontfamily + return Space(0) elseif head == :frac numerator = tex_layout(args[1], state) denominator = tex_layout(args[2], state) @@ -200,6 +202,7 @@ function tex_layout(expr, state) for name in ["radical.v1", "radical.v2", "radical.v3", "radical.v4"] sqrt = TeXChar(name, state, :symbol ; represented = '√') + pad = inkheight(sqrt) if inkheight(sqrt) >= 1.05h pad = (inkheight(sqrt) - 1.05h) / 2 break @@ -318,6 +321,15 @@ The elments are of one of the following types """ function generate_tex_elements(str, font_family=FontFamily()) expr = texparse(str) + + for node in PreOrderDFS(expr) + if node isa TeXExpr && node.head == :fontfamily + # Reconstruct the argument as a single string + name = join([texchar.args[1] for texchar in node.args[1].args]) + font_family = FontFamily(name) + break + end + end layout = tex_layout(expr, font_family) return unravel(layout) end \ No newline at end of file diff --git a/src/parser/commands_registration.jl b/src/parser/commands_registration.jl index 5d92222..1cf551c 100644 --- a/src/parser/commands_registration.jl +++ b/src/parser/commands_registration.jl @@ -60,6 +60,7 @@ const command_definitions = Dict( raw"\overline" => (TeXExpr(:overline), 1), raw"\{" => (TeXExpr(:delimiter, '{'), 0), raw"\}" => (TeXExpr(:delimiter, '}'), 0), + raw"\fontfamily" => (TeXExpr(:fontfamily), 1) ) for func in underover_functions diff --git a/test/fonts.jl b/test/fonts.jl index b7699f2..372ba17 100644 --- a/test/fonts.jl +++ b/test/fonts.jl @@ -1,6 +1,6 @@ @testset "Fonts" begin @testset "Caching" begin - for font in values(_new_computer_modern_fonts) + for font in values(FontFamily().fonts) @test load_font(font) === load_font(font) end end diff --git a/test/runtests.jl b/test/runtests.jl index 03ec26d..17e3032 100644 --- a/test/runtests.jl +++ b/test/runtests.jl @@ -7,7 +7,7 @@ import MathTeXEngine: TeXParseError import MathTeXEngine: tex_layout, generate_tex_elements import MathTeXEngine: Space, TeXElement -import MathTeXEngine: _new_computer_modern_fonts, FontFamily, load_font +import MathTeXEngine: load_font import MathTeXEngine: inkheight, inkwidth include("texexpr.jl")