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