Coverage Report

Created: 2026-05-16 07:22

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/imagemagick/coders/pango.c
Line
Count
Source
1
/*
2
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3
%                                                                             %
4
%                                                                             %
5
%                                                                             %
6
%                     PPPP    AAA   N   N   GGGG   OOO                        %
7
%                     P   P  A   A  NN  N  G      O   O                       %
8
%                     PPPP   AAAAA  N N N  G GGG  O   O                       %
9
%                     P      A   A  N  NN  G   G  O   O                       %
10
%                     P      A   A  N   N   GGGG   OOO                        %
11
%                                                                             %
12
%                                                                             %
13
%                     Read Pango Markup Language Format                       %
14
%                                                                             %
15
%                              Software Design                                %
16
%                                   Cristy                                    %
17
%                                 March 2012                                  %
18
%                                                                             %
19
%                                                                             %
20
%  Copyright @ 1999 ImageMagick Studio LLC, a non-profit organization         %
21
%  dedicated to making software imaging solutions freely available.           %
22
%                                                                             %
23
%  You may not use this file except in compliance with the License.  You may  %
24
%  obtain a copy of the License at                                            %
25
%                                                                             %
26
%    https://imagemagick.org/license/                                         %
27
%                                                                             %
28
%  Unless required by applicable law or agreed to in writing, software        %
29
%  distributed under the License is distributed on an "AS IS" BASIS,          %
30
%  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.   %
31
%  See the License for the specific language governing permissions and        %
32
%  limitations under the License.                                             %
33
%                                                                             %
34
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
35
%
36
%
37
*/
38

39
/*
40
  Include declarations.
41
*/
42
#include "MagickCore/studio.h"
43
#include "MagickCore/annotate.h"
44
#include "MagickCore/artifact.h"
45
#include "MagickCore/blob.h"
46
#include "MagickCore/blob-private.h"
47
#include "MagickCore/composite-private.h"
48
#include "MagickCore/draw.h"
49
#include "MagickCore/draw-private.h"
50
#include "MagickCore/exception.h"
51
#include "MagickCore/exception-private.h"
52
#include "MagickCore/image.h"
53
#include "MagickCore/image-private.h"
54
#include "MagickCore/list.h"
55
#include "MagickCore/magick.h"
56
#include "MagickCore/memory_.h"
57
#include "MagickCore/module.h"
58
#include "MagickCore/monitor.h"
59
#include "MagickCore/monitor-private.h"
60
#include "MagickCore/option.h"
61
#include "MagickCore/pixel-accessor.h"
62
#include "MagickCore/property.h"
63
#include "MagickCore/quantum-private.h"
64
#include "MagickCore/static.h"
65
#include "MagickCore/string_.h"
66
#include "MagickCore/string-private.h"
67
#include "MagickCore/token.h"
68
#include "MagickCore/utility.h"
69
#if defined(MAGICKCORE_PANGOCAIRO_DELEGATE)
70
#include <pango/pango.h>
71
#include <pango/pangocairo.h>
72
#include <pango/pango-features.h>
73
#endif
74

75
/*
76
  Define declarations.
77
*/
78
#define DefaultSVGDensity  96.0
79

80
#if defined(MAGICKCORE_PANGOCAIRO_DELEGATE)
81
/*
82
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
83
%                                                                             %
84
%                                                                             %
85
%                                                                             %
86
%   R e a d P A N G O I m a g e                                               %
87
%                                                                             %
88
%                                                                             %
89
%                                                                             %
90
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
91
%
92
%  ReadPANGOImage() reads an image in the Pango Markup Language Format.
93
%
94
%  The format of the ReadPANGOImage method is:
95
%
96
%      Image *ReadPANGOImage(const ImageInfo *image_info,
97
%        ExceptionInfo *exception)
98
%
99
%  A description of each parameter follows:
100
%
101
%    o image_info: the image info.
102
%
103
%    o exception: return any errors or warnings in this structure.
104
%
105
*/
106
static inline int ScalePangoValue(double value,double resolution)
107
{
108
  return((int) ((value*(resolution == 0.0 ? DefaultSVGDensity : resolution)*
109
    PANGO_SCALE+DefaultSVGDensity/2)/DefaultSVGDensity+0.5));
110
}
111
112
static Image *ReadPANGOImage(const ImageInfo *image_info,
113
  ExceptionInfo *exception)
114
{
115
  cairo_font_options_t
116
    *font_options;
117
118
  cairo_surface_t
119
    *surface;
120
121
  char
122
    *caption,
123
    *property;
124
125
  cairo_t
126
    *cairo_image;
127
128
  const char
129
    *option;
130
131
  DrawInfo
132
    *draw_info;
133
134
  Image
135
    *image;
136
137
  MagickBooleanType
138
    status;
139
140
  MemoryInfo
141
    *pixel_info;
142
143
  PangoAlignment
144
    align;
145
146
  PangoContext
147
    *context;
148
149
  PangoFontDescription
150
    *description;
151
152
  PangoFontMap
153
    *fontmap;
154
155
  PangoGravity
156
    gravity;
157
158
  PangoLayout
159
    *layout;
160
161
  PangoRectangle
162
    extent;
163
164
  PixelInfo
165
    fill_color;
166
167
  RectangleInfo
168
    page;
169
170
  unsigned char
171
    *p;
172
173
  size_t
174
    stride;
175
176
  ssize_t
177
    y;
178
179
  unsigned char
180
    *pixels;
181
182
  /*
183
    Initialize Image structure.
184
  */
185
  assert(image_info != (const ImageInfo *) NULL);
186
  assert(image_info->signature == MagickCoreSignature);
187
  assert(exception != (ExceptionInfo *) NULL);
188
  assert(exception->signature == MagickCoreSignature);
189
  if (IsEventLogging() != MagickFalse)
190
    (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",
191
      image_info->filename);
192
  image=AcquireImage(image_info,exception);
193
  (void) ResetImagePage(image,"0x0+0+0");
194
  if ((image->columns != 0) && (image->rows != 0))
195
    (void) SetImageBackgroundColor(image,exception);
196
  /*
197
    Format caption.
198
  */
199
  option=GetImageOption(image_info,"filename");
200
  if (option == (const char *) NULL)
201
    property=InterpretImageProperties((ImageInfo *) image_info,image,
202
      image_info->filename,exception);
203
  else
204
    if (LocaleNCompare(option,"pango:",6) == 0)
205
      property=InterpretImageProperties((ImageInfo *) image_info,image,option+6,
206
        exception);
207
    else
208
      property=InterpretImageProperties((ImageInfo *) image_info,image,option,
209
        exception);
210
  if (property != (char *) NULL)
211
    {
212
      (void) SetImageProperty(image,"caption",property,exception);
213
      property=DestroyString(property);
214
    }
215
  caption=ConstantString(GetImageProperty(image,"caption",exception));
216
  /*
217
    Get context.
218
  */
219
  fontmap=pango_cairo_font_map_new();
220
  pango_cairo_font_map_set_resolution(PANGO_CAIRO_FONT_MAP(fontmap),
221
    image->resolution.x == 0.0 ? DefaultSVGDensity : image->resolution.x);
222
  font_options=cairo_font_options_create();
223
  option=GetImageOption(image_info,"pango:hinting");
224
  if (option != (const char *) NULL)
225
    {
226
      if (LocaleCompare(option,"none") != 0)
227
        cairo_font_options_set_hint_style(font_options,CAIRO_HINT_STYLE_NONE);
228
      if (LocaleCompare(option,"full") != 0)
229
        cairo_font_options_set_hint_style(font_options,CAIRO_HINT_STYLE_FULL);
230
    }
231
  draw_info=CloneDrawInfo(image_info,(DrawInfo *) NULL);
232
  if (draw_info->text_antialias == MagickFalse)
233
    cairo_font_options_set_antialias(font_options,CAIRO_ANTIALIAS_NONE);
234
  context=pango_font_map_create_context(fontmap);
235
  pango_cairo_context_set_font_options(context,font_options);
236
  cairo_font_options_destroy(font_options);
237
  option=GetImageOption(image_info,"pango:language");
238
  if (option != (const char *) NULL)
239
    pango_context_set_language(context,pango_language_from_string(option));
240
  pango_context_set_base_dir(context,draw_info->direction ==
241
    RightToLeftDirection ? PANGO_DIRECTION_RTL : PANGO_DIRECTION_LTR);
242
  switch (draw_info->gravity)
243
  {
244
    case NorthGravity:
245
    {
246
      gravity=PANGO_GRAVITY_NORTH;
247
      break;
248
    }
249
    case NorthWestGravity:
250
    case WestGravity:
251
    case SouthWestGravity:
252
    {
253
      gravity=PANGO_GRAVITY_WEST;
254
      break;
255
    }
256
    case NorthEastGravity:
257
    case EastGravity:
258
    case SouthEastGravity:
259
    {
260
      gravity=PANGO_GRAVITY_EAST;
261
      break;
262
    }
263
    case SouthGravity:
264
    {
265
      gravity=PANGO_GRAVITY_SOUTH;
266
      break;
267
    }
268
    default:
269
    {
270
      gravity=PANGO_GRAVITY_AUTO;
271
      break;
272
    }
273
  }
274
  pango_context_set_base_gravity(context,gravity);
275
  option=GetImageOption(image_info,"pango:gravity-hint");
276
  if (option != (const char *) NULL)
277
    {
278
      if (LocaleCompare(option,"line") == 0)
279
        pango_context_set_gravity_hint(context,PANGO_GRAVITY_HINT_LINE);
280
      if (LocaleCompare(option,"natural") == 0)
281
        pango_context_set_gravity_hint(context,PANGO_GRAVITY_HINT_NATURAL);
282
      if (LocaleCompare(option,"strong") == 0)
283
        pango_context_set_gravity_hint(context,PANGO_GRAVITY_HINT_STRONG);
284
    }
285
  /*
286
    Configure layout.
287
  */
288
  layout=pango_layout_new(context);
289
  option=GetImageOption(image_info,"pango:auto-dir");
290
  if (option != (const char *) NULL)
291
    pango_layout_set_auto_dir(layout,1);
292
  option=GetImageOption(image_info,"pango:ellipsize");
293
  if (option != (const char *) NULL)
294
    {
295
      if (LocaleCompare(option,"end") == 0)
296
        pango_layout_set_ellipsize(layout,PANGO_ELLIPSIZE_END);
297
      if (LocaleCompare(option,"middle") == 0)
298
        pango_layout_set_ellipsize(layout,PANGO_ELLIPSIZE_MIDDLE);
299
      if (LocaleCompare(option,"none") == 0)
300
        pango_layout_set_ellipsize(layout,PANGO_ELLIPSIZE_NONE);
301
      if (LocaleCompare(option,"start") == 0)
302
        pango_layout_set_ellipsize(layout,PANGO_ELLIPSIZE_START);
303
    }
304
  option=GetImageOption(image_info,"pango:justify");
305
  if (IsStringTrue(option) != MagickFalse)
306
    pango_layout_set_justify(layout,1);
307
  option=GetImageOption(image_info,"pango:single-paragraph");
308
  if (IsStringTrue(option) != MagickFalse)
309
    pango_layout_set_single_paragraph_mode(layout,1);
310
  option=GetImageOption(image_info,"pango:wrap");
311
  if (option != (const char *) NULL)
312
    {
313
      if (LocaleCompare(option,"char") == 0)
314
        pango_layout_set_wrap(layout,PANGO_WRAP_CHAR);
315
      if (LocaleCompare(option,"word") == 0)
316
        pango_layout_set_wrap(layout,PANGO_WRAP_WORD);
317
      if (LocaleCompare(option,"word-char") == 0)
318
        pango_layout_set_wrap(layout,PANGO_WRAP_WORD_CHAR);
319
    }
320
  option=GetImageOption(image_info,"pango:indent");
321
  if (option != (const char *) NULL)
322
    pango_layout_set_indent(layout,ScalePangoValue(StringToDouble(option,
323
      (char **) NULL),image->resolution.x));
324
  switch (draw_info->align)
325
  {
326
    case CenterAlign: align=PANGO_ALIGN_CENTER; break;
327
    case RightAlign: align=PANGO_ALIGN_RIGHT; break;
328
    case LeftAlign: align=PANGO_ALIGN_LEFT; break;
329
    default:
330
    {
331
      if (draw_info->gravity == CenterGravity)
332
        {
333
          align=PANGO_ALIGN_CENTER;
334
          break;
335
        }
336
      align=PANGO_ALIGN_LEFT;
337
      break;
338
    }
339
  }
340
  if ((align != PANGO_ALIGN_CENTER) &&
341
      (draw_info->direction == RightToLeftDirection))
342
    align=(PangoAlignment) (PANGO_ALIGN_LEFT+PANGO_ALIGN_RIGHT-align);
343
  option=GetImageOption(image_info,"pango:align");
344
  if (option != (const char *) NULL)
345
    {
346
      if (LocaleCompare(option,"center") == 0)
347
        align=PANGO_ALIGN_CENTER;
348
      if (LocaleCompare(option,"left") == 0)
349
        align=PANGO_ALIGN_LEFT;
350
      if (LocaleCompare(option,"right") == 0)
351
        align=PANGO_ALIGN_RIGHT;
352
    }
353
  pango_layout_set_alignment(layout,align);
354
  if (draw_info->font == (char *) NULL)
355
    description=pango_font_description_new();
356
  else
357
    description=pango_font_description_from_string(draw_info->font);
358
  pango_font_description_set_size(description,(int) (PANGO_SCALE*
359
    draw_info->pointsize+0.5));
360
  pango_layout_set_font_description(layout,description);
361
  pango_font_description_free(description);
362
  option=GetImageOption(image_info,"pango:markup");
363
  if ((option != (const char *) NULL) && (IsStringTrue(option) == MagickFalse))
364
    pango_layout_set_text(layout,caption,-1);
365
  else
366
    {
367
      GError
368
        *error;
369
370
      error=(GError *) NULL;
371
      if (pango_parse_markup(caption,-1,0,NULL,NULL,NULL,&error) == 0)
372
        (void) ThrowMagickException(exception,GetMagickModule(),CoderError,
373
          error->message,"`%s'",image_info->filename);
374
      pango_layout_set_markup(layout,caption,-1);
375
    }
376
  if (draw_info->interline_spacing != 0)
377
    pango_layout_set_spacing(layout,ScalePangoValue(
378
      draw_info->interline_spacing,image->resolution.x));
379
  pango_layout_context_changed(layout);
380
  page.x=0;
381
  page.y=0;
382
  if (image_info->page != (char *) NULL)
383
    (void) ParseAbsoluteGeometry(image_info->page,&page);
384
  if (image->columns == 0)
385
    {
386
      pango_layout_get_extents(layout,NULL,&extent);
387
      image->columns=(size_t) ((extent.x+extent.width+PANGO_SCALE/2)/
388
        PANGO_SCALE+2*page.x);
389
    }
390
  else
391
    {
392
      image->columns=(size_t) ((ssize_t) image->columns-2*page.x);
393
      pango_layout_set_width(layout,ScalePangoValue((double) image->columns,
394
        image->resolution.x));
395
    }
396
  if (image->rows == 0)
397
    { 
398
      pango_layout_get_extents(layout,NULL,&extent);
399
      image->rows=(size_t) ((extent.y+extent.height+PANGO_SCALE/2)/
400
        PANGO_SCALE+2*page.y);
401
    }
402
  else
403
    {
404
      image->rows=(size_t) ((ssize_t) image->rows-2*page.y);
405
      pango_layout_set_height(layout,ScalePangoValue((double) image->rows,
406
        image->resolution.y));
407
    }
408
  status=SetImageExtent(image,image->columns,image->rows,exception);
409
  if (status == MagickFalse)
410
    return(DestroyImageList(image));
411
  /*
412
    Render markup.
413
  */
414
  stride=(size_t) cairo_format_stride_for_width(CAIRO_FORMAT_ARGB32,
415
    (int) image->columns);
416
  pixel_info=AcquireVirtualMemory(image->rows,stride*sizeof(*pixels));
417
  if (pixel_info == (MemoryInfo *) NULL)
418
    {
419
      draw_info=DestroyDrawInfo(draw_info);
420
      caption=DestroyString(caption);
421
      ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
422
    }
423
  pixels=(unsigned char *) GetVirtualMemoryBlob(pixel_info);
424
  surface=cairo_image_surface_create_for_data(pixels,CAIRO_FORMAT_ARGB32,
425
    (int) image->columns,(int) image->rows,(int) stride);
426
  cairo_image=cairo_create(surface);
427
  cairo_set_operator(cairo_image,CAIRO_OPERATOR_CLEAR);
428
  cairo_paint(cairo_image);
429
  cairo_set_operator(cairo_image,CAIRO_OPERATOR_OVER);
430
  cairo_translate(cairo_image,(double) page.x,(double) page.y);
431
  cairo_set_source_rgba(cairo_image,QuantumScale*draw_info->fill.red,
432
    QuantumScale*draw_info->fill.green,QuantumScale*draw_info->fill.blue,
433
    QuantumScale*draw_info->fill.alpha);
434
  pango_cairo_show_layout(cairo_image,layout);
435
  cairo_destroy(cairo_image);
436
  cairo_surface_destroy(surface);
437
  g_object_unref(layout);
438
  g_object_unref(fontmap);
439
  /*
440
    Convert surface to image.
441
  */
442
  (void) SetImageBackgroundColor(image,exception);
443
  p=pixels;
444
  GetPixelInfo(image,&fill_color);
445
  for (y=0; y < (ssize_t) image->rows; y++)
446
  {
447
    Quantum
448
      *q;
449
450
    ssize_t
451
      x;
452
453
    q=GetAuthenticPixels(image,0,y,image->columns,1,exception);
454
    if (q == (Quantum *) NULL)
455
      break;
456
    for (x=0; x < (ssize_t) image->columns; x++)
457
    {
458
      double
459
        gamma;
460
461
      fill_color.blue=(double) ScaleCharToQuantum(*p++);
462
      fill_color.green=(double) ScaleCharToQuantum(*p++);
463
      fill_color.red=(double) ScaleCharToQuantum(*p++);
464
      fill_color.alpha=(double) ScaleCharToQuantum(*p++);
465
      /*
466
        Disassociate alpha.
467
      */
468
      gamma=QuantumScale*fill_color.alpha;
469
      gamma=MagickSafeReciprocal(gamma);
470
      fill_color.blue*=gamma;
471
      fill_color.green*=gamma;
472
      fill_color.red*=gamma;
473
      CompositePixelOver(image,&fill_color,fill_color.alpha,q,(double)
474
        GetPixelAlpha(image,q),q);
475
      q+=(ptrdiff_t) GetPixelChannels(image);
476
    }
477
    if (SyncAuthenticPixels(image,exception) == MagickFalse)
478
      break;
479
    if (image->previous == (Image *) NULL)
480
      {
481
        status=SetImageProgress(image,LoadImageTag,(MagickOffsetType) y,
482
        image->rows);
483
        if (status == MagickFalse)
484
          break;
485
      }
486
  }
487
  /*
488
    Relinquish resources.
489
  */
490
  pixel_info=RelinquishVirtualMemory(pixel_info);
491
  draw_info=DestroyDrawInfo(draw_info);
492
  caption=DestroyString(caption);
493
  return(GetFirstImageInList(image));
494
}
495
#endif
496

497
/*
498
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
499
%                                                                             %
500
%                                                                             %
501
%                                                                             %
502
%   R e g i s t e r P A N G O I m a g e                                       %
503
%                                                                             %
504
%                                                                             %
505
%                                                                             %
506
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
507
%
508
%  RegisterPANGOImage() adds attributes for the Pango Markup Language format to
509
%  the list of supported formats.  The attributes include the image format
510
%  tag, a method to read and/or write the format, whether the format
511
%  supports the saving of more than one frame to the same file or blob,
512
%  whether the format supports native in-memory I/O, and a brief
513
%  description of the format.
514
%
515
%  The format of the RegisterPANGOImage method is:
516
%
517
%      size_t RegisterPANGOImage(void)
518
%
519
*/
520
ModuleExport size_t RegisterPANGOImage(void)
521
7
{
522
7
  char
523
7
    version[MagickPathExtent];
524
525
7
  MagickInfo
526
7
    *entry;
527
528
7
  *version='\0';
529
#if defined(PANGO_VERSION_STRING)
530
  (void) FormatLocaleString(version,MagickPathExtent,"Pangocairo %s",
531
    PANGO_VERSION_STRING);
532
#endif
533
7
  entry=AcquireMagickInfo("PANGO","PANGO","Pango Markup Language");
534
#if defined(MAGICKCORE_PANGOCAIRO_DELEGATE)
535
  entry->decoder=(DecodeImageHandler *) ReadPANGOImage;
536
#endif
537
7
  if (*version != '\0')
538
0
    entry->version=ConstantString(version);
539
7
  entry->flags^=CoderAdjoinFlag;
540
7
  entry->flags^=CoderDecoderThreadSupportFlag;
541
7
  (void) RegisterMagickInfo(entry);
542
7
  return(MagickImageCoderSignature);
543
7
}
544

545
/*
546
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
547
%                                                                             %
548
%                                                                             %
549
%                                                                             %
550
%   U n r e g i s t e r P A N G O I m a g e                                   %
551
%                                                                             %
552
%                                                                             %
553
%                                                                             %
554
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
555
%
556
%  UnregisterPANGOImage() removes format registrations made by the Pango module
557
%  from the list of supported formats.
558
%
559
%  The format of the UnregisterPANGOImage method is:
560
%
561
%      UnregisterPANGOImage(void)
562
%
563
*/
564
ModuleExport void UnregisterPANGOImage(void)
565
0
{
566
0
  (void) UnregisterMagickInfo("PANGO");
567
0
}