1 /*
2  * Copyright 2009 Chris Young <chris@unsatisfactorysoftware.co.uk>
3  *
4  * This file is part of NetSurf, http://www.netsurf-browser.org/
5  *
6  * NetSurf is free software; you can redistribute it and/or modify
7  * it under the terms of the GNU General Public License as published by
8  * the Free Software Foundation; version 2 of the License.
9  *
10  * NetSurf is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13  * GNU General Public License for more details.
14  *
15  * You should have received a copy of the GNU General Public License
16  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
17  */
18 
19 #ifdef WITH_NS_SVG
20 
21 #include <stdio.h>
22 #include <stdlib.h>
23 #include <svgtiny.h>
24 #include <proto/exec.h>
25 #include <string.h>
26 #include <proto/dos.h>
27 
28 #include "netsurf/inttypes.h"
29 #ifndef AMIGA_DR2D_STANDALONE
30 #include "utils/nsurl.h"
31 #include "netsurf/content.h"
32 #include "amiga/os3support.h"
33 #include "amiga/iff_dr2d.h"
34 #else
35 #include "os3support.h"
36 #include "iff_dr2d.h"
37 #endif
38 
39 static struct ColorRegister cm[1000];
40 static ULONG numcols;
41 
findcolour(ULONG newcol)42 static ULONG findcolour(ULONG newcol)
43 {
44 	ULONG i;
45 	ULONG colr = 0xFFFFFFFF;
46 	UBYTE red,grn,blu;
47 
48 	red = svgtiny_RED(newcol);
49 	grn = svgtiny_GREEN(newcol);
50 	blu = svgtiny_BLUE(newcol);
51 
52 	for(i=0;i<numcols;i++)
53 	{
54 		if((cm[i].red == red) && (cm[i].green == grn) && (cm[i].blue == blu))
55 			colr = i;
56 	}
57 
58 	return colr;
59 }
60 
addcolour(ULONG newcol)61 static void addcolour(ULONG newcol)
62 {
63 	ULONG colr = findcolour(newcol);
64 
65 	if(colr == 0xFFFFFFFF)
66 	{
67 		cm[numcols].red = svgtiny_RED(newcol);
68 		cm[numcols].green = svgtiny_GREEN(newcol);
69 		cm[numcols].blue = svgtiny_BLUE(newcol);
70 
71 		numcols++;
72 	}
73 }
74 
ami_svg_to_dr2d(struct IFFHandle * iffh,const char * buffer,uint32_t size,const char * url)75 bool ami_svg_to_dr2d(struct IFFHandle *iffh, const char *buffer,
76 		uint32_t size, const char *url)
77 {
78 	struct svgtiny_diagram *diagram;
79 	svgtiny_code code;
80 	BOOL fons_written = FALSE;
81 	struct fons_struct *fons;
82 	struct stxt_struct *stxt;
83 	struct attr_struct *attr;
84 
85 	/* create svgtiny object */
86 	diagram = svgtiny_create();
87 	if (!diagram) {
88 		fprintf(stderr, "svgtiny_create failed\n");
89 		return 1;
90 	}
91 
92 	/* parse */
93 	code = svgtiny_parse(diagram, buffer, size, url, 1000, 1000);
94 	if (code != svgtiny_OK) {
95 		fprintf(stderr, "svgtiny_parse failed: ");
96 		switch (code) {
97 		case svgtiny_OUT_OF_MEMORY:
98 			fprintf(stderr, "svgtiny_OUT_OF_MEMORY");
99 			break;
100 		case svgtiny_LIBDOM_ERROR:
101 			fprintf(stderr, "svgtiny_LIBDOM_ERROR");
102 			break;
103 		case svgtiny_NOT_SVG:
104 			fprintf(stderr, "svgtiny_NOT_SVG");
105 			break;
106 		case svgtiny_SVG_ERROR:
107 			fprintf(stderr, "svgtiny_SVG_ERROR: line %i: %s",
108 					diagram->error_line,
109 					diagram->error_message);
110 			break;
111 		default:
112 			fprintf(stderr, "unknown svgtiny_code %i", code);
113 			break;
114 		}
115 		fprintf(stderr, "\n");
116 	}
117 
118 	if(!(PushChunk(iffh,ID_DR2D,ID_FORM,IFFSIZE_UNKNOWN)))
119 	{
120 		if(!(PushChunk(iffh,0,ID_NAME,IFFSIZE_UNKNOWN)))
121 		{
122 			WriteChunkBytes(iffh,url,strlen(url));
123 			PopChunk(iffh);
124 		}
125 
126 		if(!(PushChunk(iffh,0,ID_ANNO,19)))
127 		{
128 			WriteChunkBytes(iffh,"Created by NetSurf\0",19);
129 			PopChunk(iffh);
130 		}
131 
132 		if(!(PushChunk(iffh,0,ID_DRHD,16)))
133 		{
134 			struct drhd_struct drhd;
135 			drhd.XLeft = (float) 0.0;
136 			drhd.YTop = (float) 0.0;
137 			drhd.XRight = (float) diagram->width;
138 			drhd.YBot = (float) diagram->height;
139 
140 			WriteChunkBytes(iffh,&drhd,16);
141 			PopChunk(iffh);
142 		}
143 
144 		if(!(PushChunk(iffh,0,ID_DASH,IFFSIZE_UNKNOWN)))
145 		{
146 			struct dash_struct dash;
147 			dash.DashID = 1;
148 			dash.NumDashes = 0;
149 
150 			WriteChunkBytes(iffh,&dash,sizeof(struct dash_struct));
151 			PopChunk(iffh);
152 		}
153 
154 		if(!(PushChunk(iffh,0,ID_CMAP,IFFSIZE_UNKNOWN)))
155 		{
156 			for (unsigned int i = 0; i != diagram->shape_count; i++) {
157 				if(diagram->shape[i].fill != svgtiny_TRANSPARENT)
158 				{
159 					addcolour(diagram->shape[i].fill);
160 				}
161 
162 				if(diagram->shape[i].stroke != svgtiny_TRANSPARENT)
163 				{
164 					addcolour(diagram->shape[i].stroke);
165 				}
166 			}
167 
168 			WriteChunkBytes(iffh,cm,3*numcols);
169 			PopChunk(iffh);
170 		}
171 
172 	for (unsigned int i = 0; i != diagram->shape_count; i++) {
173 		attr = calloc(1, sizeof(struct attr_struct));
174 		if (diagram->shape[i].fill == svgtiny_TRANSPARENT)
175 			attr->FillType = FT_NONE;
176 		else
177 		{
178 			attr->FillType = FT_COLOR;
179 			attr->FillValue = findcolour(diagram->shape[i].fill);
180 		}
181 		if (diagram->shape[i].stroke == svgtiny_TRANSPARENT)
182 			attr->DashPattern = 0;
183 		else
184 		{
185 			attr->DashPattern = 1;
186 			attr->EdgeValue = findcolour(diagram->shape[i].stroke);
187 		}
188 		attr->EdgeThick = (float) diagram->shape[i].stroke_width;
189 
190 		if(!(PushChunk(iffh,0,ID_ATTR,IFFSIZE_UNKNOWN)))
191 		{
192 			WriteChunkBytes(iffh,attr,14);
193 			PopChunk(iffh);
194 		}
195 		free(attr);
196 
197 		if (diagram->shape[i].path) {
198 			union {
199 				float PolyPoints;
200 				ULONG val;
201 			} poly[(diagram->shape[i].path_length)*2];
202 
203 			USHORT NumPoints;
204 			long type;
205 			float curx,cury;
206 
207 			curx = 0.0;
208 			cury = 0.0;
209 			NumPoints = 0;
210 			type = ID_OPLY;
211 
212 			for (unsigned int j = 0;
213 					j != diagram->shape[i].path_length; ) {
214 				switch ((int) diagram->shape[i].path[j]) {
215 					case svgtiny_PATH_MOVE:
216 						if(j != 0)
217 						{
218 							poly[NumPoints*2].val = INDICATOR;
219 							poly[(NumPoints*2)+1].val = IND_MOVETO;
220 							NumPoints++;
221 						}
222 						poly[(NumPoints*2)].PolyPoints = diagram->shape[i].path[j + 1];
223 						poly[(NumPoints*2)+1].PolyPoints = diagram->shape[i].path[j + 2];
224 						NumPoints++;
225 						curx = (float) diagram->shape[i].path[j + 1];
226 						cury = (float) diagram->shape[i].path[j + 2];
227 
228 						j += 3;
229 					break;
230 					case svgtiny_PATH_CLOSE:
231 						type = ID_CPLY;
232 						j += 1;
233 					break;
234 					case svgtiny_PATH_LINE:
235 						poly[(NumPoints*2)].PolyPoints = (float) diagram->shape[i].path[j + 1];
236 						poly[(NumPoints*2)+1].PolyPoints = (float) diagram->shape[i].path[j + 2];
237 						NumPoints++;
238 						curx = (float) diagram->shape[i].path[j + 1];
239 						cury = (float) diagram->shape[i].path[j + 2];
240 						j += 3;
241 					break;
242 					case svgtiny_PATH_BEZIER:
243 						poly[NumPoints*2].val = INDICATOR;
244 						poly[(NumPoints*2)+1].val = IND_CURVE;
245 						NumPoints++;
246 						poly[(NumPoints*2)].PolyPoints = curx;
247 						poly[(NumPoints*2)+1].PolyPoints = cury;
248 						NumPoints++;
249 						poly[(NumPoints*2)].PolyPoints = (float) diagram->shape[i].path[j + 1];
250 						poly[(NumPoints*2)+1].PolyPoints = (float) diagram->shape[i].path[j + 2];
251 						NumPoints++;
252 						poly[(NumPoints*2)].PolyPoints = (float) diagram->shape[i].path[j + 3];
253 						poly[(NumPoints*2)+1].PolyPoints = (float) diagram->shape[i].path[j + 4];
254 						NumPoints++;
255 						poly[(NumPoints*2)].PolyPoints = (float) diagram->shape[i].path[j + 5];
256 						poly[(NumPoints*2)+1].PolyPoints = (float) diagram->shape[i].path[j + 6];
257 						curx = poly[(NumPoints*2)].PolyPoints;
258 						cury = poly[(NumPoints*2)+1].PolyPoints;
259 						NumPoints++;
260 						j += 7;
261 					break;
262 					default:
263 						printf("error\n");
264 						j += 1;
265 					}
266 				}
267 				if(!(PushChunk(iffh,0,type,IFFSIZE_UNKNOWN)))
268 				{
269 					WriteChunkBytes(iffh,&NumPoints,sizeof(USHORT));
270 					WriteChunkBytes(iffh,poly,NumPoints*2*4);
271 					PopChunk(iffh);
272 				}
273 			} else if (diagram->shape[i].text) {
274 				stxt = calloc(1, sizeof(struct stxt_struct));
275 				stxt->BaseX = diagram->shape[i].text_x;
276 				stxt->BaseY = diagram->shape[i].text_y;
277 				stxt->NumChars = strlen(diagram->shape[i].text);
278 				if(!fons_written)
279 				{
280 					fons = calloc(1, sizeof(struct fons_struct));
281 					if(!(PushChunk(iffh, 0, ID_FONS, IFFSIZE_UNKNOWN)))
282 					{
283 						WriteChunkBytes(iffh, fons, sizeof(struct fons_struct));
284 						WriteChunkBytes(iffh, "Topaz\0", 6);
285 						PopChunk(iffh);
286 					}
287 					free(fons);
288 					fons_written = TRUE;
289 				}
290 
291 				if(!(PushChunk(iffh, 0, ID_STXT, IFFSIZE_UNKNOWN)))
292 				{
293 					WriteChunkBytes(iffh, stxt, 26);
294 					WriteChunkBytes(iffh, diagram->shape[i].text, strlen(diagram->shape[i].text));
295 					PopChunk(iffh);
296 				}
297 				free(stxt);
298 			}
299 		}
300 
301 		PopChunk(iffh);
302 	}
303 
304 	svgtiny_free(diagram);
305 
306 	return 0;
307 }
308 
309 #ifndef AMIGA_DR2D_STANDALONE
ami_save_svg(struct hlcache_handle * c,char * filename)310 bool ami_save_svg(struct hlcache_handle *c,char *filename)
311 {
312 	struct IFFHandle *iffh;
313 	const uint8_t *source_data;
314 	size_t source_size;
315 
316 	if (!ami_download_check_overwrite(filename, NULL, 0)) return false;
317 
318 	if ((iffh = AllocIFF())) {
319 		if ((iffh->iff_Stream = Open(filename,MODE_NEWFILE))) {
320 			InitIFFasDOS(iffh);
321 		}
322 		else return false;
323 	}
324 
325 	if ((OpenIFF(iffh,IFFF_WRITE))) return false;
326 
327 	source_data = content_get_source_data(c, &source_size);
328 	if (source_data != NULL) {
329 		ami_svg_to_dr2d(iffh,
330 				(const char *)source_data,
331 				source_size,
332 				nsurl_access(hlcache_handle_get_url(c)));
333 	}
334 
335 	if(iffh) CloseIFF(iffh);
336 	if(iffh->iff_Stream) Close((BPTR)iffh->iff_Stream);
337 	if(iffh) FreeIFF(iffh);
338 
339 	return true;
340 }
341 #else
342 /*
343  * This code can be compiled as a standalone program for testing etc.
344  * Use something like the following line:
345  * gcc -o svg2dr2d iff_dr2d.c -lauto -lsvgtiny -lpthread -lsvgtiny
346  * -ldom -lwapcaplet -lexpat -lparserutils
347  * -DWITH_NS_SVG -DAMIGA_DR2D_STANDALONE -D__USE_INLINE__ -D__NOLIBBASE__
348  */
349 
350 const char __attribute__((used)) ver[] = "\0$VER: svg2dr2d 1.2 (05.01.2015)\0";
351 
main(int argc,char ** argv)352 int main(int argc, char **argv)
353 {
354 	BPTR fh = 0;
355 	char *buffer;
356 	struct IFFHandle *iffh = NULL;
357 	int64_t size;
358 	LONG rarray[] = {0,0};
359 	struct RDArgs *args;
360 	STRPTR template = "SVG=INPUT/A,DR2D=OUTPUT/A";
361 	enum
362 	{
363 		A_SVG,
364 		A_DR2D
365 	};
366 
367 #ifndef __amigaos4__
368 	DOSBase = OpenLibrary("dos.library", 37);
369 	if(!DOSBase) return RETURN_FAIL;
370 
371 	IFFParseBase = OpenLibrary("iffparse.library", 37);
372 	if(!IFFParseBase) return RETURN_FAIL;
373 #endif
374 
375 	args = ReadArgs(template,rarray,NULL);
376 
377 	if(!args)
378 	{
379 		printf("Required argument missing\n");
380 		return 20;
381 	}
382 
383 	if(fh = Open((char *)rarray[A_SVG],MODE_OLDFILE))
384 	{
385 		size = GetFileSize(fh);
386 
387 		buffer = malloc((uint32_t)size);
388 
389 		Read(fh,buffer,(uint32_t)size);
390 		Close(fh);
391 	}
392 	else
393 	{
394 		printf("Unable to open file\n");
395 		return 20;
396 	}
397 
398 	if(iffh = AllocIFF())
399 	{
400 		if(iffh->iff_Stream = Open((char *)rarray[A_DR2D],MODE_NEWFILE))
401 		{
402 			InitIFFasDOS(iffh);
403 		}
404 		else return 20;
405 	}
406 
407 	if((OpenIFF(iffh,IFFF_WRITE))) return 20;
408 
409 	ami_svg_to_dr2d(iffh,buffer,size,(char *)rarray[A_SVG]);
410 
411 	free(buffer);
412 	if(iffh) CloseIFF(iffh);
413 	if(iffh->iff_Stream) Close((BPTR)iffh->iff_Stream);
414 	if(iffh) FreeIFF(iffh);
415 	FreeArgs(args);
416 
417 #ifndef __amigaos4__
418 	if(DOSBase) CloseLibrary(DOSBase);
419 	if(IFFParseBase) CloseLibrary(IFFParseBase);
420 #endif
421 }
422 
423 #endif // AMIGA_DR2D_STANDALONE
424 #endif // WITH_NS_SVG
425 
426