@harryjwang/simplewordcloud
Version:
A simple word cloud generator supporting English and Chinese text
213 lines • 8.57 kB
JavaScript
"use strict";
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
var desc = Object.getOwnPropertyDescriptor(m, k);
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
desc = { enumerable: true, get: function() { return m[k]; } };
}
Object.defineProperty(o, k2, desc);
}) : (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
o[k2] = m[k];
}));
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
Object.defineProperty(o, "default", { enumerable: true, value: v });
}) : function(o, v) {
o["default"] = v;
});
var __importStar = (this && this.__importStar) || (function () {
var ownKeys = function(o) {
ownKeys = Object.getOwnPropertyNames || function (o) {
var ar = [];
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
return ar;
};
return ownKeys(o);
};
return function (mod) {
if (mod && mod.__esModule) return mod;
var result = {};
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
__setModuleDefault(result, mod);
return result;
};
})();
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.WordCloud = void 0;
const d3 = __importStar(require("d3"));
const d3_cloud_1 = __importDefault(require("d3-cloud"));
const tokenizers_1 = require("./tokenizers");
// Check if we're in a browser environment
const isBrowser = typeof window !== 'undefined' && typeof document !== 'undefined';
/**
* Default options for the word cloud
*/
const DEFAULT_OPTIONS = {
width: 800,
height: 600,
fontFamily: 'Arial, sans-serif',
maxWords: 100,
colors: [...d3.schemeCategory10],
padding: 5,
minFontSize: 10,
maxFontSize: 60,
rotationAngles: [0, 90],
rotationProbability: 0.3
};
/**
* Word cloud generator for English and Chinese text
*/
class WordCloud {
/**
* Create a new word cloud generator
* @param options Configuration options for the word cloud
*/
constructor(options = {}) {
this.options = Object.assign(Object.assign({}, DEFAULT_OPTIONS), options);
}
/**
* Generate a word cloud from text
* @param text The input text
* @param language The language of the text ('english' or 'chinese')
* @returns An SVG element containing the word cloud (in browser) or SVG string (in Node.js)
*/
generate(text, language) {
// Get the appropriate tokenizer for the language
const tokenizer = (0, tokenizers_1.getTokenizer)(language);
// Tokenize the text and get word frequencies
const wordCounts = tokenizer.tokenize(text);
// Convert to array and sort by frequency
const words = Array.from(wordCounts.entries())
.sort((a, b) => b[1] - a[1])
.slice(0, this.options.maxWords)
.map(([text, size]) => ({
text,
size: this.scaleFontSize(size, wordCounts),
rotate: this.getRotation()
}));
if (isBrowser) {
// Browser environment - create DOM elements
// Create SVG element
const svg = document.createElementNS('http://www.w3.org/2000/svg', 'svg');
svg.setAttribute('width', this.options.width.toString());
svg.setAttribute('height', this.options.height.toString());
svg.setAttribute('class', 'wordcloud');
// Generate the layout using d3-cloud
const layout = (0, d3_cloud_1.default)()
.size([this.options.width, this.options.height])
.words(words)
.padding(this.options.padding)
.rotate((d) => d.rotate)
.font(this.options.fontFamily)
.fontSize((d) => d.size)
.on('end', (words) => this.draw(words, svg));
layout.start();
return svg;
}
else {
// Node.js environment - return SVG string
return this.generateSVGString(words);
}
}
/**
* Generate a word cloud and return it as an SVG string
* @param text The input text
* @param language The language of the text ('english' or 'chinese')
* @returns A string containing the SVG markup
*/
generateSVG(text, language) {
const result = this.generate(text, language);
if (typeof result === 'string') {
return result; // Already a string in Node.js environment
}
else {
return result.outerHTML; // Convert SVG element to string in browser
}
}
/**
* Generate SVG string directly (for Node.js environment)
* @param words The words to include in the cloud
* @returns SVG markup as a string
*/
generateSVGString(words) {
// Run the layout synchronously
const layout = (0, d3_cloud_1.default)()
.size([this.options.width, this.options.height])
.words(words)
.padding(this.options.padding)
.rotate((d) => d.rotate)
.font(this.options.fontFamily)
.fontSize((d) => d.size);
// Force the layout to run synchronously
layout.start();
const processedWords = layout.words();
// Generate SVG string manually
const colorScale = d3.scaleOrdinal(this.options.colors);
let svgString = `<svg width="${this.options.width}" height="${this.options.height}" class="wordcloud">`;
svgString += `<g transform="translate(${this.options.width / 2},${this.options.height / 2})">`;
processedWords.forEach((d, i) => {
const color = colorScale(i.toString());
const text = d.text || '';
svgString += `<text text-anchor="middle" transform="translate(${d.x},${d.y}) rotate(${d.rotate})" font-size="${d.size}px" font-family="${this.options.fontFamily}" fill="${color}">${text}</text>`;
});
svgString += '</g></svg>';
return svgString;
}
/**
* Scale the font size based on word frequency
* @param count The word count
* @param wordCounts Map of all word counts
* @returns The scaled font size
*/
scaleFontSize(count, wordCounts) {
const counts = Array.from(wordCounts.values());
const minCount = Math.min(...counts);
const maxCount = Math.max(...counts);
// Linear scaling between min and max font size
if (minCount === maxCount) {
return this.options.maxFontSize;
}
return this.options.minFontSize +
(count - minCount) / (maxCount - minCount) *
(this.options.maxFontSize - this.options.minFontSize);
}
/**
* Get a random rotation angle for a word
* @returns The rotation angle in degrees
*/
getRotation() {
if (Math.random() > this.options.rotationProbability) {
return 0;
}
const angles = this.options.rotationAngles;
return angles[Math.floor(Math.random() * angles.length)];
}
/**
* Draw the word cloud on the SVG element (browser only)
* @param words The words to draw
* @param svg The SVG element to draw on
*/
draw(words, svg) {
if (!isBrowser)
return; // Safety check
const colorScale = d3.scaleOrdinal(this.options.colors);
const g = document.createElementNS('http://www.w3.org/2000/svg', 'g');
g.setAttribute('transform', `translate(${this.options.width / 2},${this.options.height / 2})`);
words.forEach((d, i) => {
const text = document.createElementNS('http://www.w3.org/2000/svg', 'text');
text.setAttribute('text-anchor', 'middle');
text.setAttribute('transform', `translate(${d.x},${d.y}) rotate(${d.rotate})`);
text.setAttribute('font-size', `${d.size}px`);
text.setAttribute('font-family', this.options.fontFamily);
text.setAttribute('fill', colorScale(i.toString()));
text.textContent = d.text || '';
g.appendChild(text);
});
svg.appendChild(g);
}
}
exports.WordCloud = WordCloud;
//# sourceMappingURL=wordcloud.js.map