Coverage Report

Created: 2026-04-01 07:49

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/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
}