/src/graphicsmagick/coders/mvg.c
Line | Count | Source |
1 | | /* |
2 | | % Copyright (C) 2003-2025 GraphicsMagick Group |
3 | | % Copyright (C) 2002 ImageMagick Studio |
4 | | % |
5 | | % This program is covered by multiple licenses, which are described in |
6 | | % Copyright.txt. You should have received a copy of Copyright.txt with this |
7 | | % package; otherwise see http://www.graphicsmagick.org/www/Copyright.html. |
8 | | % |
9 | | %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% |
10 | | % % |
11 | | % % |
12 | | % % |
13 | | % M M V V GGGG % |
14 | | % MM MM V V G % |
15 | | % M M M V V G GG % |
16 | | % M M V V G G % |
17 | | % M M V GGG % |
18 | | % % |
19 | | % Read/Write Magick Vector Graphics Metafiles. % |
20 | | % % |
21 | | % % |
22 | | % Software Design % |
23 | | % John Cristy % |
24 | | % April 2000 % |
25 | | % % |
26 | | % % |
27 | | % % |
28 | | %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% |
29 | | % |
30 | | % |
31 | | */ |
32 | | |
33 | | /* |
34 | | Include declarations. |
35 | | */ |
36 | | #include "magick/studio.h" |
37 | | #include "magick/attribute.h" |
38 | | #include "magick/blob.h" |
39 | | #include "magick/log.h" |
40 | | #include "magick/magick.h" |
41 | | #include "magick/render.h" |
42 | | #include "magick/pixel_cache.h" |
43 | | #include "magick/utility.h" |
44 | | #include "magick/static.h" |
45 | | |
46 | | /* |
47 | | Forward declarations. |
48 | | */ |
49 | | static unsigned int |
50 | | WriteMVGImage(const ImageInfo *,Image *); |
51 | | |
52 | | /* |
53 | | %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% |
54 | | % % |
55 | | % % |
56 | | % % |
57 | | % I s M V G % |
58 | | % % |
59 | | % % |
60 | | % % |
61 | | %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% |
62 | | % |
63 | | % Method IsMVG returns True if the image format type, identified by the |
64 | | % magick string, is MVG. |
65 | | % |
66 | | % The format of the IsMVG method is: |
67 | | % |
68 | | % unsigned int IsMVG(const unsigned char *magick,const size_t length) |
69 | | % |
70 | | % A description of each parameter follows: |
71 | | % |
72 | | % o status: Method IsMVG returns True if the image format type is MVG. |
73 | | % |
74 | | % o magick: This string is generally the first few bytes of an image file |
75 | | % or blob. |
76 | | % |
77 | | % o length: Specifies the length of the magick string. |
78 | | % |
79 | | % |
80 | | */ |
81 | | static unsigned int IsMVG(const unsigned char *magick,const size_t length) |
82 | 0 | { |
83 | 0 | if (length < 7) |
84 | 0 | return(False); |
85 | 0 | if (LocaleNCompare((char *) magick,"viewbox",7) == 0) |
86 | 0 | return(True); |
87 | 0 | return(False); |
88 | 0 | } |
89 | | |
90 | | /* |
91 | | %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% |
92 | | % % |
93 | | % % |
94 | | % % |
95 | | % R e a d M V G I m a g e % |
96 | | % % |
97 | | % % |
98 | | % % |
99 | | %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% |
100 | | % |
101 | | % Method ReadMVGImage renders a MVG vector-graphics file into a raster |
102 | | % image. It allocates the memory necessary for the new Image structure |
103 | | % and returns a pointer to the new image. |
104 | | % |
105 | | % The format of the ReadMVGImage method is: |
106 | | % |
107 | | % Image *ReadMVGImage(const ImageInfo *image_info,ExceptionInfo *exception) |
108 | | % |
109 | | % A description of each parameter follows: |
110 | | % |
111 | | % o image: Method ReadMVGImage returns a pointer to the image after |
112 | | % creating it. A null image is returned if there is a memory shortage |
113 | | % or if the image cannot be read. |
114 | | % |
115 | | % o image_info: Specifies a pointer to a ImageInfo structure. |
116 | | % |
117 | | % o exception: return any errors or warnings in this structure. |
118 | | % |
119 | | % |
120 | | */ |
121 | | static Image *ReadMVGImage(const ImageInfo *image_info,ExceptionInfo *exception) |
122 | 31.4k | { |
123 | 696k | #define BoundingBox "viewbox" |
124 | | |
125 | 31.4k | DrawInfo |
126 | 31.4k | *draw_info; |
127 | | |
128 | 31.4k | Image |
129 | 31.4k | *image; |
130 | | |
131 | 31.4k | size_t |
132 | 31.4k | length; |
133 | | |
134 | 31.4k | magick_off_t |
135 | 31.4k | blob_size; |
136 | | |
137 | 31.4k | MagickPassFail |
138 | 31.4k | status; |
139 | | |
140 | | /* |
141 | | Open image. |
142 | | */ |
143 | 31.4k | assert(image_info != (const ImageInfo *) NULL); |
144 | 31.4k | assert(image_info->signature == MagickSignature); |
145 | 31.4k | assert(exception != (ExceptionInfo *) NULL); |
146 | 31.4k | assert(exception->signature == MagickSignature); |
147 | 31.4k | image=AllocateImage(image_info); |
148 | | /* |
149 | | FIXME: This implementation should be entirely re-written so that |
150 | | it does not require seekable_stream=True and is thus able to read |
151 | | directly from a pipe or compressed stream. |
152 | | */ |
153 | 31.4k | status=OpenBlob(image_info,image,ReadBinaryBlobMode,exception); |
154 | 31.4k | if (status == MagickFail) |
155 | 31.4k | ThrowReaderException(FileOpenError,UnableToOpenFile,image); |
156 | 31.4k | if ((image->columns == 0) || (image->rows == 0)) |
157 | 26.1k | { |
158 | 26.1k | char |
159 | 26.1k | primitive[MaxTextExtent]; |
160 | | |
161 | 26.1k | register char |
162 | 26.1k | *p; |
163 | | |
164 | 26.1k | SegmentInfo |
165 | 26.1k | bounds; |
166 | | |
167 | | /* |
168 | | Determine size of image canvas. |
169 | | */ |
170 | 26.1k | (void) memset(&bounds,0,sizeof(bounds)); |
171 | 356k | while (ReadBlobString(image,primitive) != (char *) NULL) |
172 | 348k | { |
173 | 360k | for (p=primitive; (*p == ' ') || (*p == '\t'); p++); |
174 | 348k | if (LocaleNCompare(BoundingBox,p,strlen(BoundingBox)) != 0) |
175 | 329k | continue; |
176 | 18.5k | if (sscanf(p,"viewbox %lf %lf %lf %lf",&bounds.x1,&bounds.y1, |
177 | 18.5k | &bounds.x2,&bounds.y2) == 4) |
178 | 18.5k | { |
179 | 18.5k | image->columns=(unsigned long) (bounds.x2-bounds.x1+0.5); |
180 | 18.5k | image->rows=(unsigned long) (bounds.y2-bounds.y1+0.5); |
181 | 18.5k | (void) LogMagickEvent(CoderEvent,GetMagickModule(), |
182 | 18.5k | "ViewBox: %g %g %g %g, Geometry: %lux%lu", |
183 | 18.5k | bounds.x1, bounds.y1, bounds.x2, bounds.y2, |
184 | 18.5k | image->columns,image->rows); |
185 | 18.5k | } |
186 | 18.5k | break; |
187 | 348k | } |
188 | 26.1k | } |
189 | 31.4k | if ((image->columns == 0) || (image->rows == 0)) |
190 | 23.7k | ThrowReaderException(OptionError,MustSpecifyImageSize,image); |
191 | | |
192 | 23.7k | if (CheckImagePixelLimits(image, exception) != MagickPass) |
193 | 22.5k | ThrowReaderException(ResourceLimitError,ImagePixelLimitExceeded,image); |
194 | | |
195 | | /* |
196 | | Render drawing. |
197 | | */ |
198 | | /* |
199 | | Initialize image canvas with the background color (default white), |
200 | | which may be transparent. |
201 | | */ |
202 | 22.5k | SetImageColor(image,&image->background_color); |
203 | 22.5k | draw_info=CloneDrawInfo(image_info,(DrawInfo *) NULL); |
204 | 22.5k | draw_info->fill=image_info->pen; |
205 | 22.5k | draw_info->primitive=NULL; |
206 | 22.5k | if (GetBlobStreamData(image)) |
207 | 18.0k | { |
208 | 18.0k | if ((blob_size = GetBlobSize(image)) > 0) |
209 | 18.0k | { |
210 | 18.0k | length=(size_t) blob_size; |
211 | 18.0k | draw_info->primitive=MagickAllocateMemory(char *,length+1); |
212 | 18.0k | if (draw_info->primitive != (char *) NULL) |
213 | 18.0k | { |
214 | 18.0k | (void) memcpy(draw_info->primitive,GetBlobStreamData(image),length); |
215 | 18.0k | draw_info->primitive[length]=0; |
216 | 18.0k | } |
217 | 0 | else |
218 | 0 | { |
219 | 0 | DestroyDrawInfo(draw_info); |
220 | 0 | ThrowReaderException(ResourceLimitError,MemoryAllocationFailed,image); |
221 | 0 | } |
222 | 18.0k | } |
223 | 18.0k | } |
224 | 4.47k | else |
225 | 4.47k | { |
226 | 4.47k | draw_info->primitive=(char *) FileToBlob(image->filename,&length,exception); |
227 | 4.47k | } |
228 | 22.5k | if (draw_info->primitive == (char *) NULL) |
229 | 0 | { |
230 | 0 | DestroyDrawInfo(draw_info); |
231 | 0 | CloseBlob(image); |
232 | 0 | return (Image *) NULL; |
233 | 0 | } |
234 | | /* |
235 | | Don't allow MVG files to side-load a file as the primitive |
236 | | */ |
237 | 22.5k | if (draw_info->primitive[0] == '@') |
238 | 1 | { |
239 | 1 | DestroyDrawInfo(draw_info); |
240 | 1 | ThrowReaderException(CorruptImageError,ImproperImageHeader,image); |
241 | 0 | } |
242 | | /* SetImageAttribute concatenates values! Delete with NULL */ |
243 | 22.5k | (void) SetImageAttribute(image,"[MVG]",NULL); |
244 | 22.5k | (void) SetImageAttribute(image,"[MVG]",draw_info->primitive); |
245 | 22.5k | (void) DrawImage(image,draw_info); |
246 | 22.5k | DestroyDrawInfo(draw_info); |
247 | 22.5k | CloseBlob(image); |
248 | 22.5k | StopTimer(&image->timer); |
249 | 22.5k | return(image); |
250 | 22.5k | } |
251 | | |
252 | | /* |
253 | | %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% |
254 | | % % |
255 | | % % |
256 | | % % |
257 | | % R e g i s t e r M V G I m a g e % |
258 | | % % |
259 | | % % |
260 | | % % |
261 | | %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% |
262 | | % |
263 | | % Method RegisterMVGImage adds attributes for the MVG image format |
264 | | % to the list of supported formats. The attributes include the image format |
265 | | % tag, a method to read and/or write the format, whether the format |
266 | | % supports the saving of more than one frame to the same file or blob, |
267 | | % whether the format supports native in-memory I/O, and a brief |
268 | | % description of the format. |
269 | | % |
270 | | % The format of the RegisterMVGImage method is: |
271 | | % |
272 | | % RegisterMVGImage(void) |
273 | | % |
274 | | */ |
275 | | ModuleExport void RegisterMVGImage(void) |
276 | 6 | { |
277 | 6 | MagickInfo |
278 | 6 | *entry; |
279 | | |
280 | 6 | entry=SetMagickInfo("MVG"); |
281 | 6 | entry->decoder=(DecoderHandler) ReadMVGImage; |
282 | 6 | entry->encoder=(EncoderHandler) WriteMVGImage; |
283 | 6 | entry->magick=(MagickHandler) IsMVG; |
284 | 6 | entry->adjoin=False; |
285 | 6 | entry->seekable_stream=True; |
286 | 6 | entry->description="Magick Vector Graphics"; |
287 | 6 | entry->module="MVG"; |
288 | 6 | entry->extension_treatment=IgnoreExtensionTreatment; |
289 | 6 | (void) RegisterMagickInfo(entry); |
290 | 6 | } |
291 | | |
292 | | /* |
293 | | %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% |
294 | | % % |
295 | | % % |
296 | | % % |
297 | | % U n r e g i s t e r M V G I m a g e % |
298 | | % % |
299 | | % % |
300 | | % % |
301 | | %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% |
302 | | % |
303 | | % Method UnregisterMVGImage removes format registrations made by the |
304 | | % MVG module from the list of supported formats. |
305 | | % |
306 | | % The format of the UnregisterMVGImage method is: |
307 | | % |
308 | | % UnregisterMVGImage(void) |
309 | | % |
310 | | */ |
311 | | ModuleExport void UnregisterMVGImage(void) |
312 | 0 | { |
313 | 0 | (void) UnregisterMagickInfo("MVG"); |
314 | 0 | } |
315 | | |
316 | | /* |
317 | | %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% |
318 | | % % |
319 | | % % |
320 | | % % |
321 | | % W r i t e M V G I m a g e % |
322 | | % % |
323 | | % % |
324 | | % % |
325 | | %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% |
326 | | % |
327 | | % Method WriteMVGImage writes MVG vector drawing data associated with an |
328 | | % image structure using the attribute key "[MVG]" to a file in MVG |
329 | | % vector-graphics format. |
330 | | % |
331 | | % The format of the WriteMVGImage method is: |
332 | | % |
333 | | % unsigned int WriteMVGImage(const ImageInfo *image_info,Image *image) |
334 | | % |
335 | | % A description of each parameter follows. |
336 | | % |
337 | | % o status: Method WriteMVGImage return True if the image is written. |
338 | | % False is returned is there is a memory shortage or if the image file |
339 | | % fails to write. |
340 | | % |
341 | | % o image_info: Specifies a pointer to a ImageInfo structure. |
342 | | % |
343 | | % o image: A pointer to an Image structure. |
344 | | % |
345 | | % |
346 | | */ |
347 | | static unsigned int WriteMVGImage(const ImageInfo *image_info,Image *image) |
348 | 3.46k | { |
349 | 3.46k | const ImageAttribute |
350 | 3.46k | *attribute; |
351 | | |
352 | 3.46k | unsigned int |
353 | 3.46k | status; |
354 | | |
355 | | /* |
356 | | Open output image file. |
357 | | */ |
358 | 3.46k | assert(image_info != (const ImageInfo *) NULL); |
359 | 3.46k | assert(image_info->signature == MagickSignature); |
360 | 3.46k | assert(image != (Image *) NULL); |
361 | 3.46k | assert(image->signature == MagickSignature); |
362 | 3.46k | attribute=GetImageAttribute(image,"[MVG]"); |
363 | 3.46k | if (attribute == (ImageAttribute *) NULL) |
364 | 3.46k | ThrowWriterException(CoderError,NoImageVectorGraphics,image); |
365 | 3.46k | status=OpenBlob(image_info,image,WriteBlobMode,&image->exception); |
366 | 3.46k | if (status == False) |
367 | 3.46k | ThrowWriterException(FileOpenError,UnableToOpenFile,image); |
368 | 3.46k | (void) WriteBlob(image,strlen(attribute->value),attribute->value); |
369 | 3.46k | status &= CloseBlob(image); |
370 | 3.46k | return(status); |
371 | 3.46k | } |