Skip to content

Commit 21831e2

Browse files
committed
Integrate Fontconfig for font listing
1 parent 851a730 commit 21831e2

File tree

4 files changed

+83
-64
lines changed

4 files changed

+83
-64
lines changed

Project.toml

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,13 @@
11
name = "WordCloud"
22
uuid = "6385f0a0-cb03-45b6-9089-4e0acc74b26b"
33
authors = ["guoyongzhi <guo-yong-zhi@outlook.com>"]
4-
version = "0.13.3"
4+
version = "0.13.4"
55

66
[deps]
77
ColorSchemes = "35d6a980-a343-548e-a6ea-1d62b119f2f4"
88
Colors = "5ae59095-9a9b-59fe-a467-6f913c188581"
99
FileIO = "5789e2e9-d7fb-5bc7-8068-2c6fae9b9549"
10+
Fontconfig = "186bb1d3-e1f7-5a2c-a377-96d770f13627"
1011
ImageTransformations = "02fcd773-0e25-5acc-982a-7f6622650795"
1112
LanguageIdentification = "35248bf2-58a5-46a0-9b1d-c0a73a50a105"
1213
Luxor = "ae8d54c2-7ccd-5906-9d76-62fc9837b5bc"
@@ -20,6 +21,7 @@ Stuffing = "4175e07e-e5b7-423e-8796-3ea7f6d48281"
2021
ColorSchemes = "3"
2122
Colors = "0.9, 0.10, 0.11, 0.12"
2223
FileIO = "1"
24+
Fontconfig = "0.4"
2325
ImageTransformations = "0.5, 0.6, 0.7, 0.8, 0.9, 0.10"
2426
LanguageIdentification = "1.0.1"
2527
Luxor = "3.6, 4"

WordCloudApp.jl

Lines changed: 11 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -379,26 +379,8 @@ begin
379379
end
380380

381381

382-
# ╔═╡ b09620ef-4495-4c83-ad1c-2d8b0ed70710
383-
begin
384-
google_fonts = ["Roboto", "Open Sans", "Lato", "Montserrat", "Noto Sans JP", "Roboto Condensed", "Oswald", "Source Sans Pro", "Slabo 27px", "Raleway", "PT Sans", "Poppins", "Roboto Slab", "Merriweather", "Noto Sans", "Ubuntu", "Roboto Mono", "Lora", "Playfair Display", "Nunito", "PT Serif", "Titillium Web", "PT Sans Narrow", "Arimo", "Noto Serif",
385-
"Rubik", "Fira Sans", "Work Sans", "Noto Sans KR", "Quicksand", "Dosis", "Inconsolata", "Oxygen", "Mukta", "Bitter", "Nanum Gothic", "Yanone Kaffeesatz", "Nunito Sans", "Lobster", "Cabin", "Fjalla One", "Indie Flower", "Anton", "Arvo", "Josefin Sans", "Karla", "Libre Baskerville", "Noto Sans TC", "Hind", "Crimson Text", "Hind Siliguri",
386-
"Inter", "Heebo", "Abel", "Libre Franklin", "Barlow", "Varela Round", "Pacifico", "Dancing Script", "Exo 2", "Source Code Pro", "Shadows Into Light", "Merriweather Sans", "Asap", "Bree Serif", "Archivo Narrow", "Play", "Ubuntu Condensed", "Questrial", "Abril Fatface", "Source Serif Pro", "Maven Pro", "Francois One", "Signika",
387-
"EB Garamond", "Comfortaa", "Exo", "Vollkorn", "Teko", "Catamaran", "Kanit", "Cairo", "Amatic SC", "IBM Plex Sans", "Cuprum", "Poiret One", "Rokkitt", "Bebas Neue", "Acme", "PT Sans Caption", "Righteous", "Noto Sans SC", "Alegreya Sans", "Alegreya", "Barlow Condensed", "Prompt", "Gloria Hallelujah", "Patua One", "Crete Round", "Permanent Marker"]
388-
empty!(WordCloud.AvailableFonts)
389-
append!(WordCloud.AvailableFonts, ["$f$w" for w in WordCloud.CandiWeights, f in google_fonts])
390-
function wordseg_cn(t)
391-
jieba = pyimport("jieba")
392-
pyconvert(Vector{String}, jieba.lcut(t))
393-
end
394-
WordCloud.settokenizer!("zho", wordseg_cn)
395-
WordCloud.settokenizer!("jpn", TinySegmenter.tokenize)
396-
nothing
397-
end
398-
399382
# ╔═╡ fa6b3269-357e-4bf9-8514-70aff9df427f
400383
begin
401-
google_fonts # used to adjust cell order
402384
function gen_cloud(words_weights)
403385
if outlinewidth isa Number && outlinewidth >= 0
404386
olw = outlinewidth
@@ -441,6 +423,17 @@ begin
441423
end
442424

443425

426+
# ╔═╡ b09620ef-4495-4c83-ad1c-2d8b0ed70710
427+
begin
428+
function wordseg_cn(t)
429+
jieba = pyimport("jieba")
430+
pyconvert(Vector{String}, jieba.lcut(t))
431+
end
432+
WordCloud.settokenizer!("zho", wordseg_cn)
433+
WordCloud.settokenizer!("jpn", TinySegmenter.tokenize)
434+
nothing
435+
end
436+
444437
# ╔═╡ Cell order:
445438
# ╟─bda3fa85-04a3-4033-9890-a5b4f10e2a77
446439
# ╟─9191230b-b72a-4707-b7cf-1a51c9cdb217

src/artist.jl

Lines changed: 62 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -1,45 +1,66 @@
11
using Random
2-
SansSerifFonts = ["Trebuchet MS", "Heiti TC", "微軟正黑體", "Arial Unicode MS", "Droid Fallback Sans", "sans-serif", "Helvetica", "Verdana", "Hei",
3-
"Arial", "Tahoma", "Microsoft Yahei", "Comic Sans MS", "Impact", "Segoe Script", "STHeiti", "Apple LiGothic", "MingLiU", "Ubuntu", "Segoe UI",
4-
"DejaVu Sans", "DejaVu Sans Mono", "Noto Sans CJK", "Arial Black", "Gadget", "cursive", "Charcoal", "Lucida Sans Unicode", "Lucida Grande", "Geneva"]
5-
SerifFonts = ["Baskerville", "Times New Roman", "Times", "華康儷金黑 Std", "華康儷宋 Std", "DFLiKingHeiStd-W8", "DFLiSongStd-W5", "DejaVu Serif", "SimSun",
6-
"Hiragino Mincho Pro", "LiSong Pro", "新細明體", "serif", "Georgia", "STSong", "FangSong", "KaiTi", "STKaiti", "Courier", "Courier New", "monospace",
7-
"Palatino Linotype", "Book Antiqua", "Palatino", "Lucida Console", "Monaco"]
8-
CandiFonts = union(SansSerifFonts, SerifFonts)
9-
CandiWeights = ["", " Regular", " Normal", " Medium", " Bold", " Light"]
10-
function checkfonts(fonts::AbstractVector)
11-
fname = tempname()
12-
r = Bool[]
13-
open(fname, "w") do f
14-
redirect_stderr(f) do
15-
p = position(f)
16-
for font in fonts
17-
err = false
18-
try
19-
rendertext("a", 1 + rand(), font=font) # 相同字体相同字号仅warning一次,故首次执行最准
20-
catch
21-
err = true
22-
end
23-
# flush(f) # https://en.cppreference.com/w/cpp/io/c/fseek The standard C++ file streams guarantee both flushing and unshifting
24-
seekend(f)
25-
p2 = position(f)
26-
push!(r, (p2 == p) && !err)
27-
p = p2
2+
import Fontconfig: list, Pattern
3+
using StopWords
4+
5+
FontCandidates::Dict{String, Vector{String}} = Dict{String, Vector{String}}()
6+
WeightCandidates::Vector{String} = ["", " Regular", " Normal", " Medium", " Bold", " Light"]
7+
8+
function listfonts(lang="")
9+
if !isempty(lang)
10+
ps = list(Pattern(lang=lang))
11+
else
12+
ps = list(Pattern())
13+
end
14+
names = String[]
15+
for p in ps
16+
name = string(p)
17+
b = findfirst("\"", name)
18+
e = findfirst(":", name)
19+
if b !== nothing && e !== nothing
20+
b = nextind(name, first(b), 1)
21+
e = prevind(name, first(e), 1)
22+
if 0 < b < e < length(name)
23+
push!(names, name[b:e])
2824
end
2925
end
3026
end
31-
return r
27+
return names
3228
end
33-
checkfonts(f) = checkfonts([f]) |> only
34-
function filterfonts(;fonts=CandiFonts, weights=CandiWeights)
35-
candi = ["$f$w" for w in weights, f in fonts] |> vec
36-
candi[checkfonts(candi)]
29+
function reverse_dict(d)
30+
rd = Dict{String, Vector{String}}()
31+
for (k, v) in d
32+
get!(rd, v) do
33+
String[]
34+
end
35+
push!(rd[v], k)
36+
end
37+
return rd
3738
end
38-
if Sys.iswindows()
39-
AvailableFonts = [""]
40-
else
41-
AvailableFonts = filterfonts()
42-
push!(AvailableFonts, "")
39+
const id_part1 = reverse_dict(StopWords.part1_id)
40+
const mid_iid = reverse_dict(StopWords.iid_mid)
41+
function expandlangcode(c)
42+
c in StopWords.id_all || (c = get(StopWords.name_id, c, c))
43+
c in StopWords.id_all || (c = get(StopWords.name_id, titlecase(c), c))
44+
cs = []
45+
for c1 in Iterators.flatten((get(mid_iid, c, []), [c]))
46+
for c2 in Iterators.flatten((get(id_part1, c1, []), [c1]))
47+
push!(cs, c2)
48+
end
49+
end
50+
cs
51+
end
52+
function fontsof(lang)
53+
union((listfonts(l) for l in expandlangcode(lang))...)
54+
end
55+
function getfonts(lang)
56+
if haskey(FontCandidates, lang)
57+
return FontCandidates[lang]
58+
else
59+
fs = fontsof(lang)
60+
push!(fs, "")
61+
FontCandidates[lang] = fs
62+
return fs
63+
end
4364
end
4465

4566
Schemes_colorbrewer = filter(s -> occursin("colorbrewer", colorschemes[s].category), collect(keys(colorschemes)))
@@ -333,11 +354,13 @@ function randomlinecolor(colors)
333354
linecolor
334355
end
335356
randomoutline() = rand((0, 0, 0, rand(2:10)))
336-
function randomfonts()
357+
function randomfonts(lang="")
337358
if rand() < 0.8
338-
fonts = rand(AvailableFonts)
359+
fonts = rand(getfonts(lang))
360+
fonts = fonts * rand(WeightCandidates)
339361
else
340-
fonts = rand(AvailableFonts, 2 + floor(Int, 2randexp()))
362+
fonts = rand(getfonts(lang), 2 + floor(Int, 2randexp()))
363+
fonts = [f * rand(WeightCandidates) for f in fonts]
341364
rand() > 0.5 && (fonts = tuple(fonts...))
342365
end
343366
@show fonts

src/wc-class.jl

Lines changed: 7 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -73,12 +73,12 @@ wordcloud(wordsweights::Tuple; kargs...) = wordcloud(wordsweights...; kargs...)
7373
wordcloud(counter::AbstractDict; kargs...) = wordcloud(keys(counter) |> collect, values(counter) |> collect; kargs...)
7474
wordcloud(counter::AbstractVector{<:Union{Pair,Tuple,AbstractVector}}; kargs...) = wordcloud(first.(counter), [v[2] for v in counter]; kargs...)
7575
function wordcloud(text; language=:auto, stopwords=:auto, stopwords_extra=nothing, maxnum=500, kargs...)
76-
wordcloud(processtext(text, language=language, stopwords=stopwords, stopwords_extra=stopwords_extra, maxnum=maxnum); kargs...)
76+
wordcloud(processtext(text, language=language, stopwords=stopwords, stopwords_extra=stopwords_extra, maxnum=maxnum); language=language, kargs...)
7777
end
7878
wordcloud(words, weight::Number; kargs...) = wordcloud(words, repeat([weight], length(words)); kargs...)
7979
function wordcloud(words::AbstractVector{<:AbstractString}, weights::AbstractVector{<:Real};
8080
colors=:auto, angles=:auto,
81-
mask=:auto, fonts=:auto,
81+
mask=:auto, fonts=:auto, language=:auto,
8282
transparent=:auto, minfontsize=:auto, maxfontsize=:auto, spacing::Integer=2, density=0.5,
8383
state=layout!, style=:auto, centralword=:auto, reorder=:auto, level=:auto, kargs...)
8484
@assert length(words) == length(weights) > 0
@@ -90,8 +90,8 @@ function wordcloud(words::AbstractVector{<:AbstractString}, weights::AbstractVec
9090
params[:reorder] = reorder
9191
params[:level] = level
9292

93-
colors, angles, mask, svgmask, fonts, transparent = getstylescheme(words, weights; colors=colors, angles=angles,
94-
mask=mask, fonts=fonts, transparent=transparent, params=params, kargs...)
93+
colors, angles, mask, svgmask, fonts, transparent = getstylescheme(words, weights; colors=colors, angles=angles, mask=mask,
94+
fonts=fonts, language=language, transparent=transparent, params=params, kargs...)
9595
params[:colors] = Any[colors...]
9696
params[:angles] = angles
9797
params[:transparent] = transparent
@@ -138,7 +138,7 @@ end
138138
function getstylescheme(words, weights; colors=:auto, angles=:auto, mask=:auto,
139139
masksize=:auto, maskcolor=:default, keepmaskarea=:auto,
140140
backgroundcolor=:default, padding=:default,
141-
outline=:default, linecolor=:auto, fonts=:auto,
141+
outline=:default, linecolor=:auto, fonts=:auto, language=:auto,
142142
transparent=:auto, params=Dict{Symbol,Any}(), kargs...)
143143
merge!(params, kargs)
144144
colors in DEFAULTSYMBOLS && (colors = randomscheme(weights))
@@ -249,7 +249,8 @@ function getstylescheme(words, weights; colors=:auto, angles=:auto, mask=:auto,
249249
Render.recolor!(mask, maskcolor) # tobitmap后有杂色 https://github.com/JuliaGraphics/Luxor.jl/issues/160
250250
end
251251
end
252-
fonts in DEFAULTSYMBOLS && (fonts = randomfonts())
252+
lang = language in DEFAULTSYMBOLS ? "" : language
253+
fonts in DEFAULTSYMBOLS && (fonts = randomfonts(lang))
253254
fonts = Iterators.take(iter_expand(fonts), length(words)) |> collect
254255
colors, angles, mask, svgmask, fonts, transparent
255256
end

0 commit comments

Comments
 (0)