返回文章列表文章索引阅读全文
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 许可协议。转载请注明出处!

相关文章

C#学习(一)委托的概念和使用

前言 C#学习系列是根据我以前的笔记整理出来复习,顺便发一下文章做个记录。 先引用一个介绍: C# 中的委托(Delegate)类似于 C 或 C++ 中函数的指针。委托(Delegate) 是存有对某个方法的引用的一种引用类型变量。引用可在运行时被改变。 委托(Delegate)特别用于实现事件和回调方法。所有的委托(Delegate)都派生自 System.Delegate 类。 根据使用其他

2021-12-18 · C# · 约 1 分钟

讨论

共 0 条

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

加载评论中…

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