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