1

How does software like https://www.an2.net/zi/ and other stroke order dictionaries and practice tools (including authoritative sources like Taiwan's MoE) know how to generate the iterative images of correct strokes?

enter image description here

I would like to make some software that does the same but with my own preferred more customisable visual output. How can I get these kinds of images/fonts/data (whatever they are using) into my own program?

1 Answer 1

4

The data on how to draw characters (including stroke order) is freely available via Make Me a Hanzi. The file graphics.txt contains vector graphics files for each character, stroke by stroke.

For example, the entry for 休 is

{"character":"休","strokes":["M 246 517 Q 280 566 318 628 Q 343 677 368 701 Q 378 713 372 727 Q 368 740 338 763 Q 310 782 289 782 Q 270 779 279 756 Q 294 725 282 697 Q 222 543 63 344 Q 53 334 50 327 Q 46 315 61 318 Q 100 328 218 479 L 246 517 Z","M 218 479 Q 236 443 236 367 Q 235 351 235 331 Q 234 222 202 111 Q 192 66 226 15 Q 227 15 229 12 Q 247 -4 259 23 Q 274 63 273 110 Q 279 377 289 436 Q 296 463 287 474 Q 248 516 246 517 C 225 538 207 507 218 479 Z","M 613 510 Q 680 523 754 534 Q 820 547 829 555 Q 839 562 835 572 Q 828 585 798 593 Q 768 600 737 590 Q 676 571 613 556 L 570 546 Q 485 531 387 525 Q 350 521 376 502 Q 419 475 475 487 Q 509 493 546 499 L 613 510 Z","M 612 495 Q 612 502 613 510 L 613 556 Q 613 680 631 748 Q 646 773 620 792 Q 604 804 574 821 Q 550 837 529 822 Q 525 816 531 801 Q 565 752 566 701 Q 567 628 570 546 L 568 418 Q 565 193 556 140 Q 534 40 551 -4 Q 554 -11 559 -22 Q 566 -40 574 -43 Q 581 -50 588 -42 Q 615 -18 614 41 Q 613 87 612 442 L 612 495 Z","M 546 499 Q 495 334 305 147 Q 295 134 304 133 Q 325 127 405 194 Q 474 255 568 418 C 628 521 556 530 546 499 Z","M 612 442 Q 679 324 768 197 Q 786 175 817 172 Q 926 165 960 167 Q 970 168 972 173 Q 973 179 959 187 Q 826 250 778 298 Q 703 376 612 495 C 594 519 597 468 612 442 Z"],"medians":[[[291,766],[316,742],[327,716],[296,650],[194,482],[99,364],[59,327]],[[242,510],[250,473],[260,456],[260,369],[252,215],[237,110],[242,24]],[[379,515],[401,508],[450,506],[603,530],[775,568],[823,567]],[[541,816],[587,773],[595,751],[589,224],[578,47],[580,-28]],[[564,496],[556,488],[542,420],[467,299],[385,200],[307,140]],[[617,475],[630,444],[681,371],[744,287],[803,223],[853,203],[964,174]]]}

which you can edit in your favorite software in all sorts of ways. For example, I'm familiar with tikz in LaTeX, so I used the Make Me a Hanzi data to draw:

a drawing of 休 I made using tikz

This is the LaTeX code I used to draw the above:

\documentclass[crop]{standalone}

\usepackage{tikz}
\usetikzlibrary{svg.path}
\usetikzlibrary{calc}

\begin{document}
 
\begin{tikzpicture}[scale=0.08]
\draw[fill=red!10!yellow,ultra thick] svg "M 246 517 Q 280 566 318 628 Q 343 677 368 701 Q 378 713 372 727 Q 368 740 338 763 Q 310 782 289 782 Q 270 779 279 756 Q 294 725 282 697 Q 222 543 63 344 Q 53 334 50 327 Q 46 315 61 318 Q 100 328 218 479 L 246 517 Z";
\draw[fill=red!25!yellow,thin] svg "M 218 479 Q 236 443 236 367 Q 235 351 235 331 Q 234 222 202 111 Q 192 66 226 15 Q 227 15 229 12 Q 247 -4 259 23 Q 274 63 273 110 Q 279 377 289 436 Q 296 463 287 474 Q 248 516 246 517 C 225 538 207 507 218 479 Z";
\draw[fill=red!40!yellow,dotted] svg "M 613 510 Q 680 523 754 534 Q 820 547 829 555 Q 839 562 835 572 Q 828 585 798 593 Q 768 600 737 590 Q 676 571 613 556 L 570 546 Q 485 531 387 525 Q 350 521 376 502 Q 419 475 475 487 Q 509 493 546 499 L 613 510 Z";
\draw[fill=red!55!yellow,densely dotted] svg "M 612 495 Q 612 502 613 510 L 613 556 Q 613 680 631 748 Q 646 773 620 792 Q 604 804 574 821 Q 550 837 529 822 Q 525 816 531 801 Q 565 752 566 701 Q 567 628 570 546 L 568 418 Q 565 193 556 140 Q 534 40 551 -4 Q 554 -11 559 -22 Q 566 -40 574 -43 Q 581 -50 588 -42 Q 615 -18 614 41 Q 613 87 612 442 L 612 495 Z";
\draw[fill=red!70!yellow,thick] svg "M 546 499 Q 495 334 305 147 Q 295 134 304 133 Q 325 127 405 194 Q 474 255 568 418 C 628 521 556 530 546 499 Z";
\draw[fill=red!85!yellow,very thick] svg "M 612 442 Q 679 324 768 197 Q 786 175 817 172 Q 926 165 960 167 Q 970 168 972 173 Q 973 179 959 187 Q 826 250 778 298 Q 703 376 612 495 C 594 519 597 468 612 442 Z";
\end{tikzpicture}
 
\end{document}

To turn this into a stroke-order graphic, we add surrounding boxes, and repeatedly draw the characters, deleting the relevant strokes:

How to draw 休, stroke by stroke

My LaTeX code for this is:

\documentclass[crop,margin=1mm]{standalone}

\usepackage{tikz}
\usetikzlibrary{svg.path}
\usetikzlibrary{calc}

\begin{document}

\begin{tikzpicture}[scale=0.1]
\draw[thick, dotted, black!40] svg  "M 0, 900 L 1024, -124";
\draw[thick, dotted, black!40] svg  "M 0 -124 L 1024 900";
\draw[thick, dotted, black!40] svg  "M 512 -124 L 512 900";
\draw[thick, dotted, black!40] svg  "M 0 388 L 1024 388";
\draw[ultra thick] svg  "M 0, 900 L 1024, 900 L 1024, -124 L 0 -124 Z";

\fill[black!50] svg "M 246 517 Q 280 566 318 628 Q 343 677 368 701 Q 378 713 372 727 Q 368 740 338 763 Q 310 782 289 782 Q 270 779 279 756 Q 294 725 282 697 Q 222 543 63 344 Q 53 334 50 327 Q 46 315 61 318 Q 100 328 218 479 L 246 517 Z";
% \fill[black!50] svg "M 218 479 Q 236 443 236 367 Q 235 351 235 331 Q 234 222 202 111 Q 192 66 226 15 Q 227 15 229 12 Q 247 -4 259 23 Q 274 63 273 110 Q 279 377 289 436 Q 296 463 287 474 Q 248 516 246 517 C 225 538 207 507 218 479 Z";
% \fill[black!50] svg "M 613 510 Q 680 523 754 534 Q 820 547 829 555 Q 839 562 835 572 Q 828 585 798 593 Q 768 600 737 590 Q 676 571 613 556 L 570 546 Q 485 531 387 525 Q 350 521 376 502 Q 419 475 475 487 Q 509 493 546 499 L 613 510 Z";
% \fill[black!50] svg "M 612 495 Q 612 502 613 510 L 613 556 Q 613 680 631 748 Q 646 773 620 792 Q 604 804 574 821 Q 550 837 529 822 Q 525 816 531 801 Q 565 752 566 701 Q 567 628 570 546 L 568 418 Q 565 193 556 140 Q 534 40 551 -4 Q 554 -11 559 -22 Q 566 -40 574 -43 Q 581 -50 588 -42 Q 615 -18 614 41 Q 613 87 612 442 L 612 495 Z";
% \fill[black!50] svg "M 546 499 Q 495 334 305 147 Q 295 134 304 133 Q 325 127 405 194 Q 474 255 568 418 C 628 521 556 530 546 499 Z";
% \fill[black!50] svg "M 612 442 Q 679 324 768 197 Q 786 175 817 172 Q 926 165 960 167 Q 970 168 972 173 Q 973 179 959 187 Q 826 250 778 298 Q 703 376 612 495 C 594 519 597 468 612 442 Z";
\end{tikzpicture}

\begin{tikzpicture}[scale=0.1]
\draw[thick, dotted, black!40] svg  "M 0, 900 L 1024, -124";
\draw[thick, dotted, black!40] svg  "M 0 -124 L 1024 900";
\draw[thick, dotted, black!40] svg  "M 512 -124 L 512 900";
\draw[thick, dotted, black!40] svg  "M 0 388 L 1024 388";
\draw[ultra thick] svg  "M 0, 900 L 1024, 900 L 1024, -124 L 0 -124 Z";

\fill[black!50] svg "M 246 517 Q 280 566 318 628 Q 343 677 368 701 Q 378 713 372 727 Q 368 740 338 763 Q 310 782 289 782 Q 270 779 279 756 Q 294 725 282 697 Q 222 543 63 344 Q 53 334 50 327 Q 46 315 61 318 Q 100 328 218 479 L 246 517 Z";
\fill[black!50] svg "M 218 479 Q 236 443 236 367 Q 235 351 235 331 Q 234 222 202 111 Q 192 66 226 15 Q 227 15 229 12 Q 247 -4 259 23 Q 274 63 273 110 Q 279 377 289 436 Q 296 463 287 474 Q 248 516 246 517 C 225 538 207 507 218 479 Z";
% \fill[black!50] svg "M 613 510 Q 680 523 754 534 Q 820 547 829 555 Q 839 562 835 572 Q 828 585 798 593 Q 768 600 737 590 Q 676 571 613 556 L 570 546 Q 485 531 387 525 Q 350 521 376 502 Q 419 475 475 487 Q 509 493 546 499 L 613 510 Z";
% \fill[black!50] svg "M 612 495 Q 612 502 613 510 L 613 556 Q 613 680 631 748 Q 646 773 620 792 Q 604 804 574 821 Q 550 837 529 822 Q 525 816 531 801 Q 565 752 566 701 Q 567 628 570 546 L 568 418 Q 565 193 556 140 Q 534 40 551 -4 Q 554 -11 559 -22 Q 566 -40 574 -43 Q 581 -50 588 -42 Q 615 -18 614 41 Q 613 87 612 442 L 612 495 Z";
% \fill[black!50] svg "M 546 499 Q 495 334 305 147 Q 295 134 304 133 Q 325 127 405 194 Q 474 255 568 418 C 628 521 556 530 546 499 Z";
% \fill[black!50] svg "M 612 442 Q 679 324 768 197 Q 786 175 817 172 Q 926 165 960 167 Q 970 168 972 173 Q 973 179 959 187 Q 826 250 778 298 Q 703 376 612 495 C 594 519 597 468 612 442 Z";
\end{tikzpicture}

\begin{tikzpicture}[scale=0.1]
\draw[thick, dotted, black!40] svg  "M 0, 900 L 1024, -124";
\draw[thick, dotted, black!40] svg  "M 0 -124 L 1024 900";
\draw[thick, dotted, black!40] svg  "M 512 -124 L 512 900";
\draw[thick, dotted, black!40] svg  "M 0 388 L 1024 388";
\draw[ultra thick] svg  "M 0, 900 L 1024, 900 L 1024, -124 L 0 -124 Z";

\fill[black!50] svg "M 246 517 Q 280 566 318 628 Q 343 677 368 701 Q 378 713 372 727 Q 368 740 338 763 Q 310 782 289 782 Q 270 779 279 756 Q 294 725 282 697 Q 222 543 63 344 Q 53 334 50 327 Q 46 315 61 318 Q 100 328 218 479 L 246 517 Z";
\fill[black!50] svg "M 218 479 Q 236 443 236 367 Q 235 351 235 331 Q 234 222 202 111 Q 192 66 226 15 Q 227 15 229 12 Q 247 -4 259 23 Q 274 63 273 110 Q 279 377 289 436 Q 296 463 287 474 Q 248 516 246 517 C 225 538 207 507 218 479 Z";
\fill[black!50] svg "M 613 510 Q 680 523 754 534 Q 820 547 829 555 Q 839 562 835 572 Q 828 585 798 593 Q 768 600 737 590 Q 676 571 613 556 L 570 546 Q 485 531 387 525 Q 350 521 376 502 Q 419 475 475 487 Q 509 493 546 499 L 613 510 Z";
% \fill[black!50] svg "M 612 495 Q 612 502 613 510 L 613 556 Q 613 680 631 748 Q 646 773 620 792 Q 604 804 574 821 Q 550 837 529 822 Q 525 816 531 801 Q 565 752 566 701 Q 567 628 570 546 L 568 418 Q 565 193 556 140 Q 534 40 551 -4 Q 554 -11 559 -22 Q 566 -40 574 -43 Q 581 -50 588 -42 Q 615 -18 614 41 Q 613 87 612 442 L 612 495 Z";
% \fill[black!50] svg "M 546 499 Q 495 334 305 147 Q 295 134 304 133 Q 325 127 405 194 Q 474 255 568 418 C 628 521 556 530 546 499 Z";
% \fill[black!50] svg "M 612 442 Q 679 324 768 197 Q 786 175 817 172 Q 926 165 960 167 Q 970 168 972 173 Q 973 179 959 187 Q 826 250 778 298 Q 703 376 612 495 C 594 519 597 468 612 442 Z";
\end{tikzpicture}


\begin{tikzpicture}[scale=0.1]
\draw[thick, dotted, black!40] svg  "M 0, 900 L 1024, -124";
\draw[thick, dotted, black!40] svg  "M 0 -124 L 1024 900";
\draw[thick, dotted, black!40] svg  "M 512 -124 L 512 900";
\draw[thick, dotted, black!40] svg  "M 0 388 L 1024 388";
\draw[ultra thick] svg  "M 0, 900 L 1024, 900 L 1024, -124 L 0 -124 Z";

\fill[black!50] svg "M 246 517 Q 280 566 318 628 Q 343 677 368 701 Q 378 713 372 727 Q 368 740 338 763 Q 310 782 289 782 Q 270 779 279 756 Q 294 725 282 697 Q 222 543 63 344 Q 53 334 50 327 Q 46 315 61 318 Q 100 328 218 479 L 246 517 Z";
\fill[black!50] svg "M 218 479 Q 236 443 236 367 Q 235 351 235 331 Q 234 222 202 111 Q 192 66 226 15 Q 227 15 229 12 Q 247 -4 259 23 Q 274 63 273 110 Q 279 377 289 436 Q 296 463 287 474 Q 248 516 246 517 C 225 538 207 507 218 479 Z";
\fill[black!50] svg "M 613 510 Q 680 523 754 534 Q 820 547 829 555 Q 839 562 835 572 Q 828 585 798 593 Q 768 600 737 590 Q 676 571 613 556 L 570 546 Q 485 531 387 525 Q 350 521 376 502 Q 419 475 475 487 Q 509 493 546 499 L 613 510 Z";
\fill[black!50] svg "M 612 495 Q 612 502 613 510 L 613 556 Q 613 680 631 748 Q 646 773 620 792 Q 604 804 574 821 Q 550 837 529 822 Q 525 816 531 801 Q 565 752 566 701 Q 567 628 570 546 L 568 418 Q 565 193 556 140 Q 534 40 551 -4 Q 554 -11 559 -22 Q 566 -40 574 -43 Q 581 -50 588 -42 Q 615 -18 614 41 Q 613 87 612 442 L 612 495 Z";
% \fill[black!50] svg "M 546 499 Q 495 334 305 147 Q 295 134 304 133 Q 325 127 405 194 Q 474 255 568 418 C 628 521 556 530 546 499 Z";
% \fill[black!50] svg "M 612 442 Q 679 324 768 197 Q 786 175 817 172 Q 926 165 960 167 Q 970 168 972 173 Q 973 179 959 187 Q 826 250 778 298 Q 703 376 612 495 C 594 519 597 468 612 442 Z";
\end{tikzpicture}

\begin{tikzpicture}[scale=0.1]
\draw[thick, dotted, black!40] svg  "M 0, 900 L 1024, -124";
\draw[thick, dotted, black!40] svg  "M 0 -124 L 1024 900";
\draw[thick, dotted, black!40] svg  "M 512 -124 L 512 900";
\draw[thick, dotted, black!40] svg  "M 0 388 L 1024 388";
\draw[ultra thick] svg  "M 0, 900 L 1024, 900 L 1024, -124 L 0 -124 Z";

\fill[black!50] svg "M 246 517 Q 280 566 318 628 Q 343 677 368 701 Q 378 713 372 727 Q 368 740 338 763 Q 310 782 289 782 Q 270 779 279 756 Q 294 725 282 697 Q 222 543 63 344 Q 53 334 50 327 Q 46 315 61 318 Q 100 328 218 479 L 246 517 Z";
\fill[black!50] svg "M 218 479 Q 236 443 236 367 Q 235 351 235 331 Q 234 222 202 111 Q 192 66 226 15 Q 227 15 229 12 Q 247 -4 259 23 Q 274 63 273 110 Q 279 377 289 436 Q 296 463 287 474 Q 248 516 246 517 C 225 538 207 507 218 479 Z";
\fill[black!50] svg "M 613 510 Q 680 523 754 534 Q 820 547 829 555 Q 839 562 835 572 Q 828 585 798 593 Q 768 600 737 590 Q 676 571 613 556 L 570 546 Q 485 531 387 525 Q 350 521 376 502 Q 419 475 475 487 Q 509 493 546 499 L 613 510 Z";
\fill[black!50] svg "M 612 495 Q 612 502 613 510 L 613 556 Q 613 680 631 748 Q 646 773 620 792 Q 604 804 574 821 Q 550 837 529 822 Q 525 816 531 801 Q 565 752 566 701 Q 567 628 570 546 L 568 418 Q 565 193 556 140 Q 534 40 551 -4 Q 554 -11 559 -22 Q 566 -40 574 -43 Q 581 -50 588 -42 Q 615 -18 614 41 Q 613 87 612 442 L 612 495 Z";
\fill[black!50] svg "M 546 499 Q 495 334 305 147 Q 295 134 304 133 Q 325 127 405 194 Q 474 255 568 418 C 628 521 556 530 546 499 Z";
% \fill[black!50] svg "M 612 442 Q 679 324 768 197 Q 786 175 817 172 Q 926 165 960 167 Q 970 168 972 173 Q 973 179 959 187 Q 826 250 778 298 Q 703 376 612 495 C 594 519 597 468 612 442 Z";
\end{tikzpicture}

\begin{tikzpicture}[scale=0.1]
\draw[thick, dotted, black!40] svg  "M 0, 900 L 1024, -124";
\draw[thick, dotted, black!40] svg  "M 0 -124 L 1024 900";
\draw[thick, dotted, black!40] svg  "M 512 -124 L 512 900";
\draw[thick, dotted, black!40] svg  "M 0 388 L 1024 388";
\draw[ultra thick] svg  "M 0, 900 L 1024, 900 L 1024, -124 L 0 -124 Z";

\fill[black!50] svg "M 246 517 Q 280 566 318 628 Q 343 677 368 701 Q 378 713 372 727 Q 368 740 338 763 Q 310 782 289 782 Q 270 779 279 756 Q 294 725 282 697 Q 222 543 63 344 Q 53 334 50 327 Q 46 315 61 318 Q 100 328 218 479 L 246 517 Z";
\fill[black!50] svg "M 218 479 Q 236 443 236 367 Q 235 351 235 331 Q 234 222 202 111 Q 192 66 226 15 Q 227 15 229 12 Q 247 -4 259 23 Q 274 63 273 110 Q 279 377 289 436 Q 296 463 287 474 Q 248 516 246 517 C 225 538 207 507 218 479 Z";
\fill[black!50] svg "M 613 510 Q 680 523 754 534 Q 820 547 829 555 Q 839 562 835 572 Q 828 585 798 593 Q 768 600 737 590 Q 676 571 613 556 L 570 546 Q 485 531 387 525 Q 350 521 376 502 Q 419 475 475 487 Q 509 493 546 499 L 613 510 Z";
\fill[black!50] svg "M 612 495 Q 612 502 613 510 L 613 556 Q 613 680 631 748 Q 646 773 620 792 Q 604 804 574 821 Q 550 837 529 822 Q 525 816 531 801 Q 565 752 566 701 Q 567 628 570 546 L 568 418 Q 565 193 556 140 Q 534 40 551 -4 Q 554 -11 559 -22 Q 566 -40 574 -43 Q 581 -50 588 -42 Q 615 -18 614 41 Q 613 87 612 442 L 612 495 Z";
\fill[black!50] svg "M 546 499 Q 495 334 305 147 Q 295 134 304 133 Q 325 127 405 194 Q 474 255 568 418 C 628 521 556 530 546 499 Z";
\fill[black!50] svg "M 612 442 Q 679 324 768 197 Q 786 175 817 172 Q 926 165 960 167 Q 970 168 972 173 Q 973 179 959 187 Q 826 250 778 298 Q 703 376 612 495 C 594 519 597 468 612 442 Z";
\end{tikzpicture}

\end{document}

To draw this, we need to know the coordinates:

Each stroke is laid out on a 1024x1024 size coordinate system where:

  • The upper-left corner is at position (0, 900).
  • The lower-right corner is at position (1024, -124).
1
  • What a great resource! But it doesn't have graphics entries for characters like 㠭, 麤 which I've seen other sites render no problem
    – minseong
    Jan 7, 2023 at 12:07

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.