libcm is a C development framework with an emphasis on audio signal processing applications.
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.

cmSvgWriter.c 6.7KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289
  1. #include "cmPrefix.h"
  2. #include "cmGlobal.h"
  3. #include "cmFloatTypes.h"
  4. #include "cmRpt.h"
  5. #include "cmErr.h"
  6. #include "cmCtx.h"
  7. #include "cmMem.h"
  8. #include "cmMallocDebug.h"
  9. #include "cmLinkedHeap.h"
  10. #include "cmTime.h"
  11. #include "cmText.h"
  12. #include "cmFile.h"
  13. #include "cmSvgWriter.h"
  14. enum
  15. {
  16. kRectSvgId,
  17. kLineSvgId,
  18. kTextSvgId
  19. };
  20. typedef struct cmSvgEle_str
  21. {
  22. unsigned id;
  23. double x0;
  24. double y0;
  25. double x1;
  26. double y1;
  27. cmChar_t* text;
  28. cmChar_t* cssClass;
  29. struct cmSvgEle_str* link;
  30. } cmSvgEle_t;
  31. typedef struct cmSvg_str
  32. {
  33. cmErr_t err;
  34. cmLHeapH_t lhH;
  35. cmSvgEle_t* elist;
  36. cmSvgEle_t* eol;
  37. } cmSvg_t;
  38. cmSvgH_t cmSvgNullHandle = cmSTATIC_NULL_HANDLE;
  39. cmSvg_t* _cmSvgHandleToPtr( cmSvgH_t h )
  40. {
  41. cmSvg_t* p = (cmSvg_t*)h.h;
  42. assert(p != NULL );
  43. return p;
  44. }
  45. cmSvgRC_t _cmSvgInsertEle( cmSvg_t* p, unsigned id, double x0, double y0, double x1, double y1, const cmChar_t* text, const cmChar_t* class )
  46. {
  47. cmSvgEle_t* e = cmLhAllocZ(p->lhH,cmSvgEle_t,1);
  48. e->id = id;
  49. e->x0 = x0;
  50. e->y0 = y0;
  51. e->x1 = x1;
  52. e->y1 = y1;
  53. e->text = text==NULL ? "" : cmLhAllocStr(p->lhH,text);
  54. e->cssClass = cmLhAllocStr(p->lhH,class);
  55. if( p->eol == NULL )
  56. p->elist = p->eol = e;
  57. else
  58. p->eol->link = e;
  59. p->eol = e;
  60. return kOkSvgRC;
  61. }
  62. cmSvgRC_t _cmSvgWriterFree( cmSvg_t* p )
  63. {
  64. cmLHeapDestroy(&p->lhH);
  65. cmMemFree(p);
  66. return kOkSvgRC;
  67. }
  68. cmSvgRC_t cmSvgWriterAlloc( cmCtx_t* ctx, cmSvgH_t* hp )
  69. {
  70. cmSvgRC_t rc;
  71. if((rc = cmSvgWriterFree(hp)) != kOkSvgRC )
  72. return rc;
  73. cmSvg_t* p = cmMemAllocZ(cmSvg_t,1);
  74. cmErrSetup(&p->err,&ctx->rpt,"SVG Writer");
  75. // create a local linked heap
  76. if( cmLHeapIsValid( p->lhH = cmLHeapCreate(8196,ctx)) == false )
  77. {
  78. rc = cmErrMsg(&p->err,kLHeapFailSvgRC,"Lheap create failed.");
  79. goto errLabel;
  80. }
  81. hp->h = p;
  82. errLabel:
  83. if( rc != kOkSvgRC )
  84. _cmSvgWriterFree(p);
  85. return rc;
  86. }
  87. cmSvgRC_t cmSvgWriterFree( cmSvgH_t* hp )
  88. {
  89. cmSvgRC_t rc = kOkSvgRC;
  90. if( hp==NULL || cmSvgWriterIsValid(*hp)==false )
  91. return kOkSvgRC;
  92. cmSvg_t* p = _cmSvgHandleToPtr(*hp);
  93. if((rc = _cmSvgWriterFree(p)) != kOkSvgRC )
  94. return rc;
  95. hp->h = NULL;
  96. return rc;
  97. }
  98. bool cmSvgWriterIsValid( cmSvgH_t h )
  99. { return h.h != NULL; }
  100. cmSvgRC_t cmSvgWriterRect( cmSvgH_t h, double x, double y, double ww, double hh, const cmChar_t* cssClassLabel )
  101. {
  102. cmSvg_t* p = _cmSvgHandleToPtr(h);
  103. return _cmSvgInsertEle( p, kRectSvgId, x, y, x+ww, y+hh, NULL, cssClassLabel==NULL?"rectclass":cssClassLabel );
  104. }
  105. cmSvgRC_t cmSvgWriterLine( cmSvgH_t h, double x0, double y0, double x1, double y1, const cmChar_t* cssClassLabel )
  106. {
  107. cmSvg_t* p = _cmSvgHandleToPtr(h);
  108. return _cmSvgInsertEle( p, kLineSvgId, x0, y0, x1, y1, NULL, cssClassLabel==NULL?"lineclass":cssClassLabel );
  109. }
  110. cmSvgRC_t cmSvgWriterText( cmSvgH_t h, double x, double y, const cmChar_t* text, const cmChar_t* cssClassLabel )
  111. {
  112. cmSvg_t* p = _cmSvgHandleToPtr(h);
  113. return _cmSvgInsertEle( p, kTextSvgId, x, y, 0, 0, text==NULL?"":text, cssClassLabel==NULL?"textclass":cssClassLabel );
  114. }
  115. void _cmSvgSize( cmSvg_t* p, double* widthRef, double* heightRef )
  116. {
  117. *widthRef = 0;
  118. *heightRef = 0;
  119. if( p->elist == NULL )
  120. return;
  121. cmSvgEle_t* e = p->elist;
  122. double min_x = cmMin(e->x0,e->x1);
  123. double max_x = cmMax(e->x0,e->x1);
  124. double min_y = cmMin(e->y0,e->y1);
  125. double max_y = cmMax(e->y0,e->y1);
  126. for(e=e->link; e!=NULL; e=e->link)
  127. {
  128. min_x = cmMin(cmMin(min_x,e->x0),e->x1);
  129. max_x = cmMax(cmMax(max_x,e->x0),e->x1);
  130. min_y = cmMin(cmMin(min_y,e->y0),e->y1);
  131. max_y = cmMax(cmMax(max_y,e->y0),e->y1);
  132. }
  133. *widthRef = max_x - min_x;
  134. *heightRef = max_y - min_y;
  135. }
  136. void _cmSvgWriterFlipY( cmSvg_t* p, unsigned height )
  137. {
  138. cmSvgEle_t* e = p->elist;
  139. for(; e!=NULL; e=e->link)
  140. {
  141. e->y0 = (-e->y0) + height;
  142. e->y1 = (-e->y1) + height;
  143. if( e->id == kRectSvgId )
  144. {
  145. double t = e->y1;
  146. e->y1 = e->y0;
  147. e->y0 = t;
  148. }
  149. }
  150. }
  151. cmSvgRC_t cmSvgWriterWrite( cmSvgH_t h, const cmChar_t* cssFn, const cmChar_t* outFn )
  152. {
  153. cmSvgRC_t rc = kOkSvgRC;
  154. cmSvg_t* p = _cmSvgHandleToPtr(h);
  155. double svgWidth = 0;
  156. double svgHeight = 0;
  157. cmSvgEle_t* e = p->elist;
  158. cmFileH_t fH = cmFileNullHandle;
  159. cmChar_t* s0 = NULL;
  160. cmChar_t* s1 = NULL;
  161. cmChar_t hdr[] =
  162. "<!DOCTYPE html>"
  163. "<html>"
  164. "<head>"
  165. "<meta charset=\"utf-8\">"
  166. "<link rel=\"stylesheet\" type=\"text/css\" href=\"%s\">"
  167. "<script type=\"text/javascript\" src=\"svg-pan-zoom.min.js\"></script>"
  168. "<script>"
  169. " var panZoom = null;"
  170. " function doOnLoad() { panZoom = svgPanZoom(document.querySelector('#mysvg'), { controlIconsEnabled:true } ) }"
  171. "</script>"
  172. "</head>"
  173. "<body onload=\"doOnLoad()\">"
  174. "<svg id=\"mysvg\" width=\"%f\" height=\"%f\">";
  175. _cmSvgSize(p, &svgWidth, &svgHeight );
  176. _cmSvgWriterFlipY( p, svgHeight );
  177. // print the file header
  178. if( (s0 = cmTsPrintfP(s0,hdr,cssFn,svgWidth,svgHeight)) == NULL )
  179. {
  180. rc = cmErrMsg(&p->err,kPrintFailSvgRC,"File prefix write failed.");
  181. goto errLabel;
  182. }
  183. for(; e!=NULL; e=e->link)
  184. {
  185. switch( e->id )
  186. {
  187. case kRectSvgId:
  188. if( (s1 = cmTsPrintfP(s1,"<rect x=\"%f\" y=\"%f\" width=\"%f\" height=\"%f\" class=\"%s\"/>\n",e->x0,e->y0,e->x1-e->x0,e->y1-e->y0,e->cssClass)) == NULL )
  189. rc = kPrintFailSvgRC;
  190. break;
  191. case kLineSvgId:
  192. if( (s1 = cmTsPrintfP(s1,"<line x1=\"%f\" y1=\"%f\" x2=\"%f\" y2=\"%f\" class=\"%s\"/>\n",e->x0,e->y0,e->x1,e->y1,e->cssClass)) == NULL )
  193. rc = kPrintFailSvgRC;
  194. break;
  195. case kTextSvgId:
  196. if( (s1 = cmTsPrintfP(s1,"<text x=\"%f\" y=\"%f\" class=\"%s\">%s</text>\n",e->x0,e->y0,e->cssClass,e->text)) == NULL )
  197. rc = kPrintFailSvgRC;
  198. break;
  199. default:
  200. { assert(0); }
  201. }
  202. if( rc != kOkSvgRC )
  203. {
  204. rc = cmErrMsg(&p->err,kPrintFailSvgRC,"Element write failed.");
  205. break;
  206. }
  207. s0 = cmTextAppendSS(s0,s1);
  208. }
  209. if( (s1 = cmTsPrintfP(s1,"</svg>\n</body>\n</html>\n")) == NULL )
  210. {
  211. rc = cmErrMsg(&p->err,kPrintFailSvgRC,"File suffix write failed.");
  212. goto errLabel;
  213. }
  214. if( cmFileOpen(&fH,outFn,kWriteFileFl,p->err.rpt) != kOkFileRC )
  215. {
  216. rc = cmErrMsg(&p->err,kFileFailSvgRC,"SVG file create failed for '%s'.",cmStringNullGuard(outFn));
  217. goto errLabel;
  218. }
  219. if( cmFilePrint(fH,s0 = cmTextAppendSS(s0,s1)) != kOkFileRC )
  220. {
  221. rc = cmErrMsg(&p->err,kFileFailSvgRC,"File write failed.");
  222. goto errLabel;
  223. }
  224. errLabel:
  225. cmFileClose(&fH);
  226. cmMemFree(s0);
  227. cmMemFree(s1);
  228. return rc;
  229. }