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 7.0KB

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