Blob Blame Raw


#include <ctype.h>
#include <stdlib.h>
#include <stdio.h>

#include <helianthus.h>


#include "svg-path.inc.h"
#include "svg-save.inc.h"

#define MSPACE "m 60 0"
#define MDOTSP "m 20 0"
#define MUP  "m 10 -25 "
#define MUP3 "m 30 -75 "
#define MDN  "m -10 25 "
#define MUPE "m 4 -10 "
#define MDNE "m -4 10 "

#define Co1 "q 6 -15 -4 -15 "
#define Co2 "q -10 0 -16 15 "
#define Co3 "q -6 15 4 15 "
#define Co4 "q 10 0 16 -15 "

#define CO1 "q 10 -25 -5 -25 "
#define CO2 "q -15 0 -25 25 "
#define CO3 "q -10 25 5 25 "
#define CO4 "q 15 0 25 -25 "

#define CO1B "q 15 0 5 25 "
#define CO2B "q 10 -25 25 -25 "
#define CO3B "q -15 0 -5 -25 "
#define CO4B "q -10 25 -25 25 "

#define CFO1 CO1 CO2 CO3 CO4
#define CFO2 CO2 CO3 CO4 CO1
#define CFO3 CO3 CO4 CO1 CO2
#define CFO4 CO4 CO1 CO2 CO3

#define CLU  "q 50 -50 30 0 "
#define CLD  "q -20 50 30 0 "
#define CLLD "q -10 25 0 25 q 15 0 40 -50 "

#define CKD1 "c 14 -35 44 -35 38 -20 "
#define CKD2 "q -18 45 32 -5 "

#define CJL0 "q -10 25 0 25 "
#define CJL  CJL0 "q 15 0 50 -50 "
#define CJR  CJL0 "c 15 0 40 -25 44 -35 " Co1 Co2 Co3 Co4 "l 6 -15 "

#define CGD0 CLD CDN "q -10 25 -20 25 "
#define CGD  CGD0 "c -20 0 20 -25 45 -50 "
#define CGDE CGD0 "c -20 0 19 -25 39 -35 "

#define CQU1 "c 25 -25 51 -40 61 -65 "
#define CQU2 "q 4 -10 -6 -10 q -10 0 -20 25 l -20 50 "
#define CQU  CQU1 CQU2

#define CQD1  CDN2
#define CQD2  "q -10 25 -20 25 q -10 0 -6 -10 "
#define CQD3  "c 10 -25 36 -40 61 -65 "
#define CQD3E "c 10 -25 35 -40 55 -50 "
#define CQD CQD1 CQD2 CQD3

#define CC1 "q 5 0 8 5 "
#define CC2 "q -3 -5 -8 -5 " CO2 CO3
#define CC  CC1 CC2
#define CXL CO1B CO4B "q -5 0 -8 -5 q 3 5 8 5 "

#define Cb0 "l -4 10 " Co3 Co4 Co1
#define Cb  Cb0 Co2 Co3

#define CUP "l 10 -25 "
#define CDN "l -10 25 "
#define CUP2 "l 20 -50 "
#define CDN2 "l -20 50 "

#define CEE "m -18 -65 l 2 -2 m 10 0 l -2 2 m 8 65 "
#define CYY "m -28 -65 q 3 5 17 -5 m 11 70 "

#define CDot   "m 2 0 q 0 2 -2 2 q -2 0 -2 -2 q 0 -2 2 -2 q 2 0 2 2 "
#define CComma CDot "q -1 5 -8 10 m 8 -10 "


#define C_O1 "q 20 -50 0 -50 "
#define C_O2 "q -20 0 -40 50 "
#define C_O3 "q -20 50 0 50 "
#define C_O4 "q 20 0 40 -50 "

#define C_O1B "q 20 0 0 50 "
#define C_O2B "q 20 -50 40 -50 "
#define C_O3B "q -20 0 0 -50 "
#define C_O4B "q -20 50 -40 50 "

#define C_XL1 "m 36 -90 q 10 -10 18 -10 "
#define C_XL2 C_O1B C_O4B
#define C_XL3 "q -7.5 0 -14 -10 q 3.5 10 14 10 "
#define C_XL  C_XL1 C_XL2 C_XL3

#define C_XR1 "q 7.5 0 14 10 "
#define C_XR2 "q -3.5 -10 -14 -10 "
#define C_XR3 C_O2 C_O3
#define C_XR  C_XR1 C_XR2 C_XR3

#define C_AL0 "m 4 -10 q 1 10 11 10 "
#define C_AL  C_AL0 "q 30 0 95 -100 "

#define C_LU  CLU
#define C_LD  CLD
#define C_LLD "q -10 25 0 25 q 15 0 70 -100 "

#define C_J1  "m 64 -85 l -24 60 "
#define C_J2  "l -4 10 q -6 15 -21 15 q -5 0 -11 -10 m 11 10 "
#define C_JJ2 "l -4 10 q -6 15 -21 15 q -5 0 -11 -10 q 1 10 11 10 "
#define C_J   C_J1 C_J2

#define C_T "m 28 -70 c -10 -10 -8 -30 12 -30 "

#define C_E "m 69 -90 " C_XR2 "q -15 0 -23 20 q -3 25 15 25 q -25 0 -35 25 q -12 30 13 30 "

#define C_3 C_XL1 "q 15 0 7 20 q -10 25 -25 25 q 15 0 5 25 q -12 30 -32 30 " C_XL3

#define C_B1  "m 60 -100 " CDN CDN2 CQD2 "q 56 -90 71 -90 "
#define C_B2  "q 15 0 7 20 q -10 25 -30 25 "
#define C_B3  "q 25 0 15 25 q -12 30 -32 30 q -5 0 -8 -5 q 3 5 8 5 "
#define C_B   C_B1 C_B2 C_B3

#define C_EE "m 31 -115 l 2 -2 m 10 0 l -2 2 m -41 115 "
#define C_YY "m -26.5 -15 q 3 5 17 -5 m 9.5 20 "


#define LLBO MUP "q 25 -25 35 -25 "


#define LBL { MUP CLU }
#define LBO { "m 35 -50 ", LLBO }
#define LBA { "m 40 -25 ", LLBO CO1B }
#define LBQ { MUP CQU }
#define LBE { MUPE "c 46 -23 56 -40 41 -40 " }
#define LBN { "m 20 -50 " CDN, MUP "l 25 -25 " CDN }
#define LBX { "m 18 -45 q 7 -5 12 -5 ", LLBO }
#define LBC { "m 38 -45 ", LLBO CC1 }
#define LBJ { MUP CJL }
#define LBr { MUP "l 25 -25 q 5 25 30 0 " }
#define LBR { MUP CJR }

#define LEL { CLD MDN, NULL, "c -14 35 4 25 24 15 " MDNE }
#define LELY { CLD MDN CYY, NULL, "c -14 35 4 25 24 15 " MDNE CYY }
#define LEO { "m 15 0 ", "q 15 0 40 -25 " MDN, "q 19 0 39 -10 " MDNE }
#define LEE { "q 19 0 39 -10 " MDNE, "q 15 0 40 -25 " MDN }
#define LEEE { "q 19 0 39 -10 " MDNE CEE, "q 15 0 40 -25 " MDN CEE }
#define LEQ { CQD2 CQD3 MDN, NULL, CQD2 CQD3E MDNE }
#define LEX { "q 15 0 40 -25 " MDN, NULL, "q 19 0 39 -10 " MDNE }
#define LEK { CKD2 MDN, NULL, "c -12 30 2 20 22 10 " MDNE }
#define LEG { CGD MDN, NULL, CGDE MDNE }


#define L_EO1 { "m 30 0 ", "q 15 0 40 -25 " MDN, "q 19 0 39 -10 " MDNE }
#define L_EO2 { "m 60 0 ", "q 15 0 40 -25 " MDN, "q 19 0 39 -10 " MDNE }
#define L_EO3 { "m 75 0 ", "q 15 0 40 -25 " MDN, "q 19 0 39 -10 " MDNE }

#define L_EA { "q 20 -50 -10 -50 c -20 0 -16 40 14 40 q 21 0 36 -15 " MDN, NULL, \
               "q 20 -50 -10 -50 c -20 0 -18 45 12 45 q 22 0 32 -5 " MDNE }



typedef struct {
  const char *base;
  const char *link0;
  const char *link1;
} Link;

typedef struct {
  const char *name;
  Link begin;
  const char *mid;
  Link end;
  int linkMode;
  const char *alias;
} Letter;


Letter letters[] = {
  { "а", LBA, CFO1 CUP CDN, LEL },
  { "б", LBA, CFO1 CUP2 "q 10 -25 25 -25 l 15 0 m -85 100", LEO },
  { "в", LBQ, CFO3 CO3, LEO },
  { "г", LBL, "", LEL },
  { "д", LBA, CFO1 CUP CDN CQD1, LEQ },
  { "е", LBE, CO2 CO3, LEE, 1 },
  { "ё", LBE, CO2 CO3, LEEE, 1 },
  { "ж", LBX, CXL "q 15 0 55 -50 " CDN2 "q 40 -50 55 -50 " CC, LEX },
  { "з", LBX, CO1B "q -3 7.5 -19 15 q 18 -7.5 1 35 ", LEQ },
  { "и", LBN, CLLD CDN, LEL },
  { "й", LBN, CLLD CDN, LELY },
  { "к", LBN, CDN "q 48 -70 46 -40 m -46 40 " CKD1, LEK },
  { "л", LBJ, CDN, LEL },
  { "м", LBJ, CDN CLLD CDN, LEL },
  { "н", LBN, CDN CUP "q 30 0 40 -25" CDN, LEL },
  { "о", LBO, CFO2 CO2 CO3, LEO },
  { "п", LBN, CDN CUP CLU, LEL },
  { "р", LBN, "l -30 75 l 30 -75 " CLU, LEL },
  { "с", LBC, CC2, LEX },
  { "т", LBN, CDN CUP CLU CDN CUP CLU, LEL },
  { "у", LBN, CLLD CDN CQD1, LEQ },
  { "ф", LBA, CFO1 CUP CDN CFO3 "l -30 75 m 35 -50 ", LEO },
  { "х", LBX, CXL CO4 CO2B CC, LEX },
  { "ц", LBN, CLLD CDN, LEG },
  { "ч", LBr, CDN, LEL },
  { "ш", LBN, CLLD CDN CLLD CDN, LEL },
  { "щ", LBN, CLLD CDN CLLD CDN, LEG },
  { "ъ", LBr, CDN Cb, LEO },
  { "ы", LBN, Cb0 "q 20 0 28 -20 " CDN, LEL },
  { "ь", LBN, Cb, LEO },
  { "э", LBX, CXL "m 10 -25 l 15 0 m -25 25 ", LEO },
  { "ю", LBN, CDN CUP "c 30 0 40 -25 55 -25 " CFO2 CO2 CO3, LEO },
  { "я", LBR, CDN, LEL },


  //{ "А", {""}, C_AL "m -64 60 l 50 0 m 14 -60 " CDN2 CDN, LEL, 3 },
  { "А", {""}, C_AL CDN2 CDN2, L_EA, 3 },
  { "Б", {""}, "m 53 -85 l -24 60 " CQD2 "q 33 -45 48 -45 " C_B3 "m -30 0 " C_T "l 50 0 m -60 100 ", L_EO2, 3 },
  { "В", {""}, C_B, L_EO1, 3 },
  { "Г", {""}, C_J "m -10 0 " C_T "l 60 0 m -90 100 ", L_EO3, 3 },
  { "Д", {""}, C_J1 CO4B "c -15 0 -9 -15 1 -15 c 10 0 14 15 24 15 " C_O4 "q 20 -50 -15 -50 q -25 0 -37 30 m 12 70 ", L_EO1, 3 },
  { "Е", {""}, C_E, LEX, 3 },
  { "Ё", {""}, C_E C_EE, LEX, 3 },
  { "Ж", {""}, C_XL "q 30 0 85 -100 " CDN2 CDN2 "q 55 -100 85 -100 " C_XR, LEX, 3 },
  { "З", {""}, C_3, L_EO1, 3 },
  { "И", {""}, MUP3 C_LU CDN2 C_LLD CDN2 CDN, LEL, 3 },
  { "Й", {""}, MUP3 C_LU CDN2 C_LLD C_YY CDN2 CDN, LEL, 3 },
  { "К", {""}, "m 40 -75 l 25 -25 " CDN CDN2 CQD2 "c 4 -10 76 -90 86 -90 q 2.5 0 1 10 m -56 55 " CO2B CO1B "l -4 10 ", LEL, 3 },
  { "Л", {""}, C_AL CDN2 CDN, LEL, 3 },
  { "М", {""}, C_AL CDN2 CDN C_LLD CDN2 CDN, LEL, 3 },
  { "Н", {""}, "m 40 -75 l 25 -25 " CDN CDN2 CQD2 "c 8 -20 96 -60 102 -80 " CQU2, LEL, 3 },
  { "О", {""}, "m 20 0 " C_O4 C_O1 C_O2 C_O3, L_EO1, 3 },
  { "П", {""}, C_J "m -5 0 " C_T "l 60 0 m -90 100 " C_J1, LEL, 3 },
  { "Р", {""}, C_J "m 18 -70 c -5 -5 7 -30 32 -30 c 35 0 25 30 15 35 m -65 65 ", L_EO2, 3 },
  { "С", {""}, "m 74 -90 " C_XR2 C_XR3, LEX, 3 },
  { "Т", {""}, C_J "m -5 0 " C_T "l 80 0 m -110 100 " C_J1 CDN "m -10 0 " C_J1, LEL, 3 },
  { "У", {""}, MUP3 C_LU CLLD CDN2 CDN C_JJ2, LEO, 3 },
  { "Ф", {""}, "m 60 -75 " CFO1 CUP CDN CFO3 CDN2 C_J2, L_EO2, 3 },
  { "Х", {""}, C_XL C_O4 C_O2B C_XR, LEX, 3 },
  { "Ц", {""}, MUP3 C_LU CDN2 C_LLD CDN2 CDN, LEG, 3 },
  { "Ч", {""}, MUP3 C_LU CLLD CDN2 CDN, LEL, 3 },
  { "Ш", {""}, MUP3 C_LU CDN2 C_LLD CDN2 CDN C_LLD CDN2 CDN, LEL, 3 },
  { "Щ", {""}, MUP3 C_LU CDN2 C_LLD CDN2 CDN C_LLD CDN2 CDN, LEG, 3 },

  { "Э", {""}, C_XL "m 20 -50 l 20 0 m -40 50 ", L_EO1, 3 },
  { "Ю", {""}, "m 40 -75 l 25 -25 " CDN CDN2 CQD2 "c 8 -20 46 -35 56 -40 " C_O3 C_O4 C_O1 C_O2 C_O3, L_EO1, 3 },
  { "Я", {""}, C_AL0 "c 15 0 55 -50 65 -75 " CFO1 CUP CDN2 CDN, LEL, 3 },

  { "-", {""}, MDOTSP "m 10 -25 l 40 0 m -10 25 " MDOTSP, {""}, 2 },
  { ".", {""}, MDOTSP CDot MDOTSP, {""}, 2 },
  { ",", {""}, MDOTSP CComma MDOTSP, {""}, 2 },
  { "\"",{""}, "m 40 -100 " CComma "m 10 0 " CComma "m -40 100 " MDOTSP, {""}, 2 },
  { "»", {}, NULL, {}, 0, "\"" },
  { "“", {}, NULL, {}, 0, "\"" },
  { "«", {""}, MDOTSP CComma "m 10 0 " CComma MDOTSP, {""}, 2 },
  { "„", {}, NULL, {}, 0, "«" },

  { "!", {""}, MDOTSP "m 40 -100 l -34 85 m -6 15 " CDot MDOTSP, {""}, 2 },
  { "?", {""}, MDOTSP "m 15 -75 " CO2B CO1B "c -10 25 -29 35 -39 60 m -6 15 " CDot MDOTSP, {""}, 2 },
};

char path[1024*1024];
double pathX0 = 60, pathRowStep = 175, pathWidth = 5;
char *pathEnd;
double pathX, pathY;
FILE *pathFile;
int pathMode;


void putPath(const char *str) {
  if (!str || !*str) return;
  if (!*path) {
    char buf[256] = {};
    sprintf(buf, "M %g %g ", pathX, pathY);
    char *c = buf;
    while(*c) *pathEnd++ = *c++;
  }
  spTrack(str, &pathX, &pathY);
  while(*str) *pathEnd++ = *str++;
  *pathEnd = 0;
}


void splitPath(int mode) {
  if (mode >= 2) {
    if (pathMode >= 2) {
      if (*path) svgAddPath(pathFile, path, pathWidth, TRUE);
      *(pathEnd = path) = 0;
    }
  } else
  if (mode >= 1) {
    pathX = pathX0;
    pathY += pathRowStep;
    if (pathMode >= 1) {
      if (*path) svgAddPath(pathFile, path, pathWidth, TRUE);
      *(pathEnd = path) = 0;
    } else
    if (*path) {
      char buf[256] = {};
      sprintf(buf, "M %g %g ", pathX, pathY);
      putPath(buf);
    }
  } else {
    if (*path) svgAddPath(pathFile, path, pathWidth, TRUE);
    //*(pathEnd = path) = 0;
  }
}


void textToPath(const char *text) {
  pathEnd = path;
  *pathEnd = 0;

  int cnt = sizeof(letters)/sizeof(*letters);
  Letter *prev = NULL;
  while(*text) {
    Letter *curr = NULL;
    do {
      for(int i = 0; i < cnt; ++i) {
        Letter *l = &letters[i];
        const char *t = curr ? curr->alias : text;
        const char *ln = l->name;
        while(*t && *ln && *t == *ln) ++t, ++ln;
        if (!*ln) {
          if (!curr) text = t;
          curr = l;
          break;
        }
      }
    } while(curr && curr->alias);


    if (prev) {
      const char *link = curr && curr->linkMode == 1 ? prev->end.link1 : prev->end.link0;
      putPath(curr && curr->linkMode != 2 && curr->linkMode != 3 && link ? link : prev->end.base);
    }

    splitPath(2);

    if (curr) {
      const char *link = curr->linkMode == 1 ? curr->begin.link1 : curr->begin.link0;
      putPath(prev && prev->linkMode != 2 && link ? link : curr->begin.base);
      putPath(curr->mid);
    } else
    if (*text == '\n') {
      splitPath(1);
      ++text;
    } else {
      putPath(MSPACE);
      ++text;
    }

    prev = curr;
  }
  if (prev) putPath(prev->end.base);
  splitPath(0);
}


int textFileToSVG(const char *textfile, const char *svgfile) {
  FILE *sf = fopen(textfile, "r");
  if (!sf) return FALSE;
  char buf[1024*1024] = {};
  char *str = buf;
  while(TRUE) {
    int c = fgetc(sf);
    if (c <= 0) break;
    *str++ = c;
  }
  fclose(sf);

  pathX = pathX0;
  pathY = pathRowStep;
  pathEnd = path;
  pathFile = svgBegin(svgfile, 300, 300, 10);
  if (!pathFile) return FALSE;

  textToPath(buf);
  svgEnd(pathFile);

  pathX = pathX0;
  pathY = pathRowStep;
  pathEnd = path;
  pathFile = NULL;

  return TRUE;
}


void init() {
  pathMode = 2;
  pathRowStep = 200;
  textFileToSVG("data/writing.txt", "data/output/writing.svg");

  pathRowStep = 175;
  pathMode = 0;
  pathX = pathX0;
  pathY = pathRowStep;
  pathEnd = path;
  pathFile = svgBegin("data/output/generated-writing.svg", 300, 300, 10);

  textToPath(
    "ааа ббб ввв ггг ддд еее ёёё\n"
    "жжж ззз иии ййй ккк ллл ммм\n"
    "ннн ооо ппп ррр ссс ттт ууу\n"
    "ффф ххх ццц ччч шшш щщщ ъъъ\n"
    "ыыы ььь эээ ююю яяя\n"
    "ае бе де ке хе це ье еа\n"
    "ААа ББб ВВв ГГг ДДд ЕЕе\n"
    "ЁЁё ЖЖж ЗЗз ИИи ЙЙй ККк\n"
    "ЛЛл ММм ННн ООо ППп РРр\n"
    "ССс ТТт УУу ФФф ХХх ЦЦц\n"
    "ЧЧч ШШш ЩЩщ ЪЪъ ЫЫы ЬЬь\n"
    "ЭЭэ ЮЮю ЯЯя\n"
    "Ае, Бе. Ге? Же! «Ие - Ке» \"Це\" „Ье“ т-т\n" );

  svgEnd(pathFile);
  pathFile = NULL;
}


void draw() {
  saveState();
  zoom(0.4);
  //translate(150, 150);

  stroke(COLOR_BLACK);
  strokeWidth(5);
  noFill();

  spMoveTo(0, 0);
  spParse(path);
  restoreState();
}


int main() {
  windowSetVariableFrameRate();
  windowSetResizable(TRUE);
  windowSetSize(1250, 800);
  windowSetInit(&init);
  windowSetDraw(&draw);
  windowRun();
  return 0;
}