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