返回文章列表文章索引阅读全文
STARBLOG / 阅读手记
C#

A small but elegant refactoring: Generate truly beautiful word clouds using C# in Avalonia

The process of refactoring the word cloud display feature in StarBlogPublisher. The author compared two popular C# word cloud libraries and ultimately selected the more powerful Sdcb.WordCloud. The article details how to use this library to obtain word frequency data and generate word cloud images, and presents a comparison of the effects before and after refactoring.

曦远 (DealiAxy)约 18 分钟
目录11 个章节

Preface

Didn't I previously develop StarBlogPublisher (a Markdown article publishing tool designed for StarBlog) using Avalonia?

At the time, it included a Word Cloud category display feature. Although the initial version of the word cloud was "usable," the result was extremely crude—essentially just a simple stacking of text, with none of the lively, dense, and staggered aesthetic that a word cloud should have.

Therefore, I decided to completely refactor this module and search anew for a suitable word cloud generation solution.

Selection

Within the Avalonia ecosystem, there is no readily available word cloud component.

However, that's not an issue. The C# ecosystem is relatively rich, offering almost everything you need, and word clouds are certainly no exception.

During my research phase, I found two fairly popular C# word cloud libraries:

Here's a brief comparison:

FeatureSdcb.WordCloudKnowledgePicker.WordCloud
Rendering EngineSkiaSharp (cross-platform)SkiaSharp (cross-platform)
Output FormatImage (PNG), SVG, JSONImage (Bitmap), SVG (requires custom drawing)
Customization LevelHigh (masks, fonts, multi-direction, JSON output, etc.)Medium (fonts, colors, layout customizable, but no mask support)
Mask Functionality✅ Natively supports mask images to generate word clouds in specific shapes❌ Does not support masks, generates standard rectangular word clouds
Recent MaintenanceActively updated (continuous updates in 2024)Active (commits in 2024)
Usage ComplexityMedium (more configuration, high flexibility)Medium (simpler, suitable for quick integration)

Common Points

  • Both use SkiaSharp, which means they can run on Windows, Linux, macOS and other platforms.
  • Both support flexible configuration of fonts, layouts, and colors, and are very fast.

Key Differences

  • Sdcb.WordCloud focuses more on visual effects (supports complex mask patterns), making it suitable for scenarios that pursue custom shapes and cool effects.
  • KnowledgePicker.WordCloud prioritizes performance and simplicity, making it ideal for standard rectangular word cloud generation without pursuing complex shapes.

Ultimately, I chose Sdcb.WordCloud for its stronger functionality and better compatibility.

Introduction to Sdcb.WordCloud

Sdcb.WordCloud is a cross-platform word cloud generation library based on SkiaSharp, featuring the following characteristics:

  • Cross-platform compatibility: Usable on Windows, Linux, and macOS.
  • Multiple output options: Supports generating images, SVG files, or JSON data.
  • Highly customizable: Customize fonts, colors, mask patterns, text layout arrangements, and more.
  • No dependency on System.Drawing: Easy to deploy in server environments as well.
  • Open-source friendly: Licensed under the MIT License, allowing developers to extend it freely.

Installation

dotnet add package Sdcb.WordCloud

Application in StarBlogPublisher: Practical Implementation

The refactored word cloud generation logic primarily consists of two steps:

Get Word Frequency Data

First, request the categorized word frequency data from the backend API and perform a simple expansion (to increase the word cloud density).

private async Task<List<WordScore>?> GetWordScores() {
  var response = await ApiService.Instance.Categories.GetWordCloud();
  if (response.Data == null) throw new Exception("Failed to retrieve word cloud data");

  var originalScores = response.Data
    .Select(e => new WordScore(Score: e.Value, Word: e.Name))
    .ToList();

  var extendedScores = new List<WordScore>();
  foreach (var score in originalScores) {
    for (int i = 0; i < 10; i++) {
      extendedScores.Add(score);
    }
  }

  return extendedScores;
}

A small tip here: 👉 Duplicating the original frequency for each word multiple times can effectively enhance the visual density and richness of the word cloud.

Generating a Word Cloud Image

Once you have the word frequency data, use WordCloud.Create() to create a word cloud object. By customizing it with a mask pattern and font, you can generate the final word cloud image.

private async Task GenerateWordCloudImage() {
  var wordScores = await GetWordScores();
  if (wordScores == null || !wordScores.Any()) {
    ErrorMessage = "No available word cloud data.";
    return;
  }

  var wc = WordCloud.Create(new WordCloudOptions(900, 900, wordScores) {
    FontManager = new FontManager([
      SKTypeface.FromFamilyName("Times New Roman")
    ]),
    Mask = MaskOptions.CreateWithForegroundColor(
      SKBitmap.Decode(await new HttpClient().GetByteArrayAsync(
        "https://io.starworks.cc:88/cv-public/2024/alice_mask.png"
      )),
      SKColors.White
    )
  });

  using var skImage = wc.ToSKBitmap();
  using var data = skImage.Encode(SKEncodedImageFormat.Png, 100);
  using var stream = new MemoryStream(data.ToArray());
  WordCloudImage = new Bitmap(stream);
}

Two simple techniques are used here to enhance the experience:

  • 📄 Mask Image: By using a transparent image with a specific shape, the word cloud can form silhouettes like people or logos, greatly improving its visual appeal.
  • 🖋 Custom Font: Changing the font allows the overall style to better match the design of your website or application.

Effect Demonstration

Could that really count as a word cloud from before??

Before ModificationAfter Modification
image-20250426235822892image-20250426235918517

Summary

Through this refactoring, I've summarized a few key takeaways:

  • Choosing the right library is crucial—cross-platform support and high extensibility are the top priorities.
  • The aesthetics of a word cloud hinge on the combination of density, mask shape, and font style.
  • Prioritize asynchronous requests (异步请求) and local optimization (局部优化) whenever possible to prevent UI lag.

If you also need to integrate word cloud functionality into your C# project, give Sdcb.WordCloud a try—it's simple, efficient, and delivers great results.

_EOF_

A small but elegant refactoring: Generate truly beautiful word clouds using C# in Avalonia

本文作者:
程序设计实验室
版权声明:
本博客所有文章除特别声明外,均采用 BY-NC-SA 许可协议。转载请注明出处!

相关文章

并行化优化KD树算法:使用C#实现高效的最近邻搜索

本文介绍了如何使用并行计算技术优化 KD 树算法,并使用 C# 编程语言实现了高效的最近邻搜索。首先,我们简要介绍了 KD 树的原理和构建过程,然后详细讨论了如何利用并行计算库在多个 CPU 核心上并行构建 KD 树,从而加速搜索过程。通过实验验证,我们证明了并行化优化能够显著提高 KD 树的构建速度和搜索效率,为大规模数据集下的最近邻搜索问题提供了一种高效的解决方案。

2024-03-10 · C# · 约 1 分钟

SharpIco:用纯C#打造零依赖的.ico图标生成器,支持.NET9与AOT编译

SharpIco 是一款纯 C# 实现的跨平台图标生成工具,支持 AOT 编译,无需外部依赖。它能将 PNG 图片分解为多尺寸(16x16至512x512)合成 ICO 图标,并支持图标结构分析。工具提供命令行界面,可通过 dotnet tool 一键安装,并已发布至 NuGet 和 Scoop。项目旨在简化图标生成与验证,追求轻量、纯粹、易集成的使用体验。

2025-05-26 · C# · 约 1 分钟

C#移除字符串中的不可见Unicode字符

背景 最近发现某个数据采集的系统拿下来的数据,有些字段的JSON被莫名截断了,导致后续数据分析的时候解析JSON失败。 类似这样 {title: 你好 或者这样,多了个双引号啥的 {title:你好} 因为数据库是Oracle,起初以为是Oracle这老古董出问题了,结果一番折腾,把每条写入数据的SQL语句都拿出来,看起来里面的JSON格式都没问题。 这也太诡异了吧,看起来没毛病,但就为啥JSON

2023-02-02 · C# · 约 1 分钟

讨论

共 0 条

良言一句三冬暖,恶语伤人六月寒。

加载评论中…