Advanced usage

The CDL XML Database

CDL contains 2 parts: The CDL XML database and CdlUtils Javascript library. The CDL XML database contains structured data about the strokes, radicals, and components of each Chinese character. The database contains over 82,000 CJK characters.

Below is the CDL XML snippet for the character "你". As you can see, CDL defines characters recursively, so "你" is made up of the sub-characters "亻" and "尔". The XML also defines "points" which is used by the CDL rendering engine to draw the character from the XML.

<cdl char="你" uni="4F60">
  <comp char="亻" uni="4EBB" points="0,0 32,128"/>
  <comp char="尔" uni="5C14" points="38,4 128,128"/>
</cdl>

Tracing the recursive relationship, we find the following CDL XML for the character "亻":

<cdl char="亻" uni="4EBB" points="0,0 56,128">
  <stroke type="p" points="128,0 0,64"/>
  <stroke type="s" points="86,46 86,128"/>
</cdl>

"亻" has no sub-characters, but contains 2 strokes. All CDL XML character definitions can eventually be followed down to individual strokes. The XML contains a shorthand for the type of stroke as well as a "points" list which is used to position the stroke properly when rendering.

For a full description of the CDL XML spec check out the CDL Specification PDF

You typically won't need to interact with CDL XML directly as the Javascript library handles that for you. For web projects and prototypes you shouldn't need to download the full CDL XML database because the CDL Javascript library is set up to pull CDL XML snippets using your API key.

Do not scrape the CDL XML database as that would be a violation of our Terms of Service!  If you need local access to the full CDL XML database, please contact licenses@wenlin.com to discuss a custom license.

Exploring character structure

The global CdlUtils object has a method, CdlUtils.fetchCdlChar(charOrUnicode, options), which returns a promise containing a javascript respresentation of the CDL structure of a given character. This method can be used as below:

CdlUtils.fetchCdlChar('你', {
  apiKey: 'your-api-key'
}).then(function(cdlChar) {
  console.log(cdlChar);
});

The object returned from this method is an instance of CdlUtils.CdlChar, and has several useful attributes and methods which can be used to understand the structor of a Chinese character. A few of these are illustrated below:

CdlUtils.fetchCdlChar('你', options).then(function(cdlChar) {
  console.log(cdlChar.char); // '你'
  console.log(cdlChar.unicode); // '4F60'
  console.log(cdlChar.getNumStrokes()); // 7
  console.log(cdlChar.toXml()); // the CDL XML string for this character
  console.log(cdlChar.components); // mixed array of components and/or strokes
});

The most import property of a CdlUtils.CdlChar is components. In CDL XML, a character is defined as a tree of components (sub-characters) and strokes. components, therefore, represents the inner components that make up the given CdlChar. This array can contain either other CdlUtils.CdlChar objects, or CdlUtils.CdlStroke objects, or a mix of both.

If you keep tracing the components of a CdlUtils.CdlChar, you'll eventually terminate in CdlUtils.CdlStroke objects. These represent the actual strokes that get drawn on the screen, and correspond with a <stroke> tag in XML. Correspondingly, CdlUtils.CdlChar objects correspond with <cdl> or <comp> tags in XML.

By tracing the components of a CdlChar, you can determine how a character is constructed and break it down into components and strokes. For more info on everything that's available in these classes check out the API docs on CdlUtils.CdlChar, CdlUtils.CdlStroke, and CdlUtils.Point.

Custom and modified characters

CdlUtils can also be used to create custom or modified characters and either render them to SVG or write them back out to CDL XML. The simplest way to do this is to take an existing CdlUtils.CdlChar and directly modify its attributes. For example, let's mess with some of the points used in the character "中" and then render it out to see what happens.

CdlUtils.fetchCdlChar('中', options).then(function(cdlChar) {
  var kou = cdlChar.components[0]; // the 口 component
  kou.components[1].points[0].x += 10;
  kou.components[1].points[0].y += 80;
  kou.points[1].x -= 30;

  // the vertical line stroke
  cdlChar.components[1].points[0].x += 20;
  cdlChar.components[1].points[0].y += 20;

  CdlUtils.getSvgPathStrings(cdlChar, { ... }).then(function(pathStrings) {
    // render these pathstrings
  });
});

Below we can see the before and after side by side:

before
after

We certainly won't be winning any digital caligraphy awards for our modified 中.

You may have noticed in the above example that we rendered out our modified character by passing it into CdlUtils.getSvgPathStrings. CdlUtils.getSvgPathStrings also works with CdlUtils.CdlChar objects.

You can do far more than just modifying points - you can modify the components array to add or remove components and strokes, or even create entirely new characters from scratch. If you'd like to view the XML for your custom characters you can call toXml() on your CdlUtils.CdlChar instances.

Loading CDL XML data

By default the CdlUtils Javascript library loads the XML for each character from our API, which should be fine for most websites and prototypes. If you would like to make an application for which you would download the full CDL XML database from us and host it from another location, please contact us (licenses@wenlin.com) to negotiate a custom license, going beyond our regular  Terms of Service. Do not scrape the CDL XML database as that would violate the Terms of Service!


The CDL XML database is far too big to load on most webpages, so CdlUtils needs to be provided with some way to get the CDL XML for any given character and variant. To accomplish this, a function must be passed as the dataLoader option which takes in the unicode of a character and a variant number and returns either a CDL XML string or a promise containing a CDL XML string. If you use a custom data loader then there's no need to pass in an apiKey option. A few examples of this are provided below:

Hardcoding the CDL XML for a character

If you know in advance the CDL XML for the character to be rendered, you can hardcode that into JS. For example, below the character "你" is loaded using hardcoded XML:
var CDL_DB = {
  "4F60-0": "<cdl char='你' uni='4F60'><comp char='亻' uni='4EBB' points='0,0 32,128' /><comp char='尔' uni='5C14' points='38,4 128,128' /></cdl>",
  "4EBB-0": "<cdl char='亻' uni='4EBB' points='0,0 56,128'><stroke type='p' points='128,0 0,64' /><stroke type='s' points='86,46 86,128' /></cdl>",
  "5C14-0": "<cdl char='尔' uni='5C14' radical='Z3 K4 D1'><comp char='𠂊󰀂' uni='2008A' variant='2' points='0,0 128,58' /><comp char='小' uni='5C0F' points='4,46 128,128' /></cdl>",
  "5C0F-0": "<cdl char='小' uni='5C0F'><stroke type='sg' points='65,0 65,128 30,108' /><comp char='八󰀁' uni='516B' variant='1' points='0,29 128,92' /></cdl>",
  "516B-1": "<cdl char='八󰀁' uni='516B' variant='1' points='0,4 128,124' radical='K0'><stroke type='p' points='34,8 0,128' tail='long' /><stroke type='d' points='89,0 128,124' /></cdl>",
  "2008A-2": "<cdl char='𠂊󰀂' uni='2008A' variant='2' points='0,0 128,54' radical='K0'><stroke type='p' points='46,0 0,128' tail='long' /><stroke type='hg' points='34,62 128,62 108,128' head='cut' /></cdl>",
};

var dataLoader = function(unicode, variant) {
  return CDL_DB[unicode + '-' + variant];
}

CdlUtils.getSvgPathStrings('你', {
  dataLoader: dataLoader
}).then(function(pathStrings) {
  console.log(pathStrings);
});
Hardcoding CDL data like this requires tracing recursive relationships and as such it's not recommended to generate data like this by hand.

Loading the CDL XML via AJAX

The best way to load CDL XML for most websites is via ajax. For example, assuming you have an endpoint set up to load CDL XML at /cdl/[UNICODE].xml?variant=[VARIANT] and you're using jQuery, you could write the following:
var dataLoader = function(unicode, variant) {
  // jQuery's $.ajax already returns a promise
  return $.ajax({
    url: '/cdl/' + unicode + '.xml',
    data: {
      variant: variant
    }
  }).then(function(res, textStatus, xhr) {
    return xhr.responseText;
  });
}

CdlUtils.getSvgPathStrings('你', {
  dataLoader: dataLoader
}).then(function(pathStrings) {
  console.log(pathStrings);
});


Parsing the entire CDL XML DB

Our regular  Terms of Service  don't allow storing a copy of our entire CDL XML database on your own server, or distributing copies of it for mobile apps. If you have a custom signed license from us that does allow you to do so, and if you're running CdlUtils from node or in a mobile app, you may just want to load the entire CDL DB. There's a built-in loader which makes this simple, if you're legally and technically able to get the entire DB as a string. This loader is available at CdlUtils.xmlDbLoader and can be used like the following, supposing you have a function getCdlXmlDbAsAString (which is not part of the regular API):

// get the whole XML DB into a string
var entireCdlXmlDb = getCdlXmlDbAsAString();

CdlUtils.getSvgPathStrings('你', {
  dataLoader: CdlUtils.xmlDbLoader(entireCdlXmlDb)
}).then(function(pathStrings) {
  console.log(pathStrings);
});

This loader lazily parses the XML when the first character is requested, so it make take a few seconds on the first load as the CDL XML db is quite large. After that it should return results instantly.

Using CdlUtils in Node.js

CdlUtils uses the Browser's DOM APIs to parse XML, but these don't exist in Node. To use CdlUtils from node you must npm install xml2js and pass in xmlParser: require('xml2js') to options when using CdlUtils. An example is below. Like the preceding section, this advanced code example presupposes that you have a custom signed license from us that allows you to store a copy of our entire CDL XML database on your own machine and access it as a string.

CdlUtils.getSvgPathStrings('你', {
  dataLoader: CdlUtils.xmlDbLoader(entireCdlXmlDb),
  xmlParser: require('xml2js')
}).then(function(pathStrings) {
  console.log(pathStrings);
});

xml2js was left out of package.json dependencies so it doesn't get accidently included by webpack or babel when bundling node modules into JS for the browser. xml2js adds about 300kb to the output js file size which is irrelevant in node but a huge problem when loaded in the browser.