a simple textfile based psf font editor suite
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 

268 lines
6.5 KiB

  1. /* psfc
  2. *
  3. * Compiles a text file into a psf file.
  4. * part of a simple textfile based psf font editor suite.
  5. *
  6. * Gunnar Zötl <gz@tset.de> 2016
  7. * Released under the terms of the MIT license. See file LICENSE for details.
  8. */
  9. #include <stdio.h>
  10. #include <stdlib.h>
  11. #include <string.h>
  12. #include <ctype.h>
  13. #include "psf.h"
  14. #include "psftools_version.h"
  15. #define LINEBUFSIZE 1024
  16. static int skipws(const char* buf, int pos)
  17. {
  18. while (buf[pos] && isspace(buf[pos])) {
  19. ++pos;
  20. }
  21. return pos;
  22. }
  23. static void lowercasify(char *line)
  24. {
  25. while (*line) {
  26. if (*line >= 'A' && *line <= 'Z') {
  27. *line = tolower(*line);
  28. }
  29. ++line;
  30. }
  31. }
  32. static int readnum(char *line, int *ppos)
  33. {
  34. int res = 0, pos = *ppos;
  35. while (isdigit(line[pos])) {
  36. res = res * 10 + (line[pos] - '0');
  37. ++pos;
  38. }
  39. *ppos = pos;
  40. return res;
  41. }
  42. static int readunichar(char *line, int *ppos, unsigned int lineno)
  43. {
  44. int res = 0, pos = *ppos;
  45. if (line[pos++] != 'u' || line[pos++] != '+') {
  46. fprintf(stderr, "psfc: invalid unicode spec in line %u\n", lineno);
  47. return -1;
  48. }
  49. while (isxdigit(line[pos])) {
  50. int dig = line[pos];
  51. if (dig >= '0' && dig <= '9') {
  52. dig -= '0';
  53. } else if (dig >= 'a' && dig <= 'f') {
  54. dig = 10 + (dig - 'a');
  55. }
  56. res = (res << 4) + dig;
  57. ++pos;
  58. }
  59. if (res > 0x10ffff || res < 0) {
  60. fprintf(stderr, "psfc: invalid unicode spec in line %u\n", lineno);
  61. return -1;
  62. }
  63. *ppos = pos;
  64. return res;
  65. }
  66. static int psfc_compile_char(struct psf_font *psf, char pixel, char *spec, FILE *in, unsigned int *plineno)
  67. {
  68. int pos = 0;
  69. unsigned int x, y, lineno = *plineno;
  70. if (spec[pos] != '@') {
  71. fprintf(stderr, "psfc: invalid char spec in line %u", lineno);
  72. return 0;
  73. }
  74. /* glyph number */
  75. pos = 1;
  76. int no = readnum(spec, &pos);
  77. pos = skipws(spec, pos);
  78. /* unicode tables */
  79. struct psf_glyph *glyph = psf_addglyph(psf, no);
  80. if (spec[pos] == ':') {
  81. pos = skipws(spec, pos + 1);
  82. while (spec[pos] && spec[pos] != '#') {
  83. int uc = PSF1_SEPARATOR;
  84. if (spec[pos] != ';') {
  85. uc = readunichar(spec, &pos, lineno);
  86. if (uc < 0) {
  87. return 0;
  88. }
  89. }
  90. psf_glyph_adducval(psf, glyph, uc);
  91. pos = skipws(spec, pos);
  92. }
  93. }
  94. if (spec[pos] != '\0' && spec[pos] != '#') {
  95. fprintf(stderr, "psfc: invalid char spec in line %u\n", lineno);
  96. return 0;
  97. }
  98. /* glyph data */
  99. char lnbuf[LINEBUFSIZE], *line;
  100. for (y = 0; y < psf_height(psf); ++y) {
  101. line = fgets(lnbuf, LINEBUFSIZE, in);
  102. if (!line) {
  103. fprintf(stderr, "psfc: unexpected end of file in line %u\n", lineno);
  104. return 0;
  105. }
  106. ++lineno;
  107. pos = 0;
  108. for (x = 0; x < psf_width(psf); ++x) {
  109. if (line[pos] != '\0') {
  110. psf_glyph_setpx(psf, glyph, x, y, line[pos] == pixel);
  111. ++pos;
  112. }
  113. }
  114. pos = skipws(line, pos);
  115. if (line[pos] != '\0') {
  116. fprintf(stderr, "psfc: invalid bitmap data in line %d\n", lineno);
  117. int i; for (i = 0; line[i]; ++i) { printf("%02x ", line[i]); }printf("Width: %d Height: %d Pos: %d\n", psf_width(psf), psf_height(psf), pos);
  118. return 0;
  119. }
  120. }
  121. *plineno = lineno;
  122. return 1;
  123. }
  124. static struct psf_font *psfc_compile(FILE *in)
  125. {
  126. char lnbuf[LINEBUFSIZE], *line;
  127. int pos;
  128. unsigned int version = 0, width = 0, height = 0, lineno = 0;
  129. char pixel = '\0';
  130. /* read header */
  131. while ((line = fgets(lnbuf, LINEBUFSIZE, in)) != 0) {
  132. ++lineno;
  133. pos = skipws(line, 0);
  134. if (!line[pos] || line[pos] == '#') { break; } /* skip comments and empty lines */
  135. lowercasify(line);
  136. if (strncmp(&line[pos], "@psf", 4) == 0) {
  137. if (version != 0) {
  138. fprintf(stderr, "psfc: duplicate @psf spec in line %u\n", lineno);
  139. return 0;
  140. }
  141. version = line[pos+4] - '1' + 1;
  142. pos = skipws(line, pos + 5);
  143. if ((version != 1 && version != 2) || (line[pos] && line[pos] != '#')) {
  144. fprintf(stderr, "psfc: invalid version spec in line %u\n", lineno);
  145. return 0;
  146. }
  147. } else if (strncmp(&line[pos], "width:", 6) == 0) {
  148. if (width != 0) {
  149. fprintf(stderr, "psfc: duplicate width spec in line %u\n", lineno);
  150. return 0;
  151. }
  152. pos = skipws(line, pos + 6);
  153. width = readnum(line, &pos);
  154. pos = skipws(line, pos);
  155. if (width == 0 || (line[pos] && line[pos] != '#')) {
  156. fprintf(stderr, "psfc: invalid width spec in line %u\n", lineno);
  157. return 0;
  158. }
  159. } else if (strncmp(&line[pos], "height:", 7) == 0) {
  160. if (height != 0) {
  161. fprintf(stderr, "psfc: duplicate height spec in line %u\n", lineno);
  162. return 0;
  163. }
  164. pos = skipws(line, pos + 7);
  165. height = readnum(line, &pos);
  166. pos = skipws(line, pos);
  167. if (height == 0 || (line[pos] && line[pos] != '#')) {
  168. fprintf(stderr, "psfc: invalid height spec in line %u\n", lineno);
  169. return 0;
  170. }
  171. } else if (strncmp(&line[pos], "pixel:", 6) == 0) {
  172. if (pixel != 0) {
  173. fprintf(stderr, "psfc: duplicate pixel spec in line %u\n", lineno);
  174. return 0;
  175. }
  176. int opos = pos;
  177. pixel = 0;
  178. pos = skipws(line, opos + 6);
  179. if (pos > opos + 6) {
  180. pixel = line[pos] ? line[pos] : line[pos-1];
  181. if (line[pos]) { ++pos; }
  182. pos = skipws(line, pos);
  183. }
  184. if (pixel == 0 || (line[pos] && line[pos] != '#')) {
  185. fprintf(stderr, "psfc: invalid pixel spec in line %u\n", lineno);
  186. return 0;
  187. }
  188. } else if (line[pos] == '@' && isdigit(line[pos + 1])) {
  189. break; /* first char starts here */
  190. } else {
  191. fprintf(stderr, "psfc: invalid header field in line %u\n", lineno);
  192. return 0;
  193. }
  194. }
  195. if (version == 1 && width == 0) {
  196. width = 8;
  197. }
  198. if (version == 0 || width == 0 || height == 0) {
  199. fprintf(stderr, "psfc: incomplete header\n");
  200. return 0;
  201. }
  202. if (!pixel) {
  203. pixel = '#';
  204. }
  205. struct psf_font *psf = psf_new(version, width, height);
  206. if (!psf) { return 0; }
  207. while (line) {
  208. lowercasify(line);
  209. if (!psfc_compile_char(psf, pixel, line, in, &lineno)) {
  210. psf_delete(psf);
  211. return 0;
  212. }
  213. do {
  214. line = fgets(lnbuf, LINEBUFSIZE, in);
  215. if (line) {
  216. pos = skipws(line, pos);
  217. ++lineno;
  218. }
  219. } while (line && (*line == 0 || *line == '#'));
  220. }
  221. return psf;
  222. }
  223. int main(int argc, char **argv)
  224. {
  225. if (argc < 1 || argc > 3 || (argv[1] && (!strcmp(argv[1], "-h") || !strcmp(argv[1], "--help")))) {
  226. fprintf(stderr, "%s [file.txt [file.psf]]\n", argv[0]);
  227. fprintf(stderr, "psftools version %s\n", PSFTOOLS_VERSION);
  228. exit(1);
  229. }
  230. const char* infile = argc >= 2 ? argv[1] : 0;
  231. const char* outfile = argc == 3 ? argv[2] : 0;
  232. FILE *in = (infile && strcmp(infile, "-") != 0) ? fopen(infile, "r") : stdin;
  233. if (!in) {
  234. perror("psfc: could not open input file");
  235. exit(1);
  236. }
  237. struct psf_font *psf = psfc_compile(in);
  238. if (in != stdin) { fclose(in); }
  239. if (!psf) { exit(1); }
  240. int ok = 0;
  241. if (outfile) {
  242. ok = psf_save(outfile, psf);
  243. } else {
  244. ok = psf_save_tofile(stdout, psf);
  245. }
  246. psf_delete(psf);
  247. exit(ok == 0);
  248. }