Ticket #2661: mappdf.c

File mappdf.c, 66.9 kB (added by kyngchaos, 5 months ago)
Line 
1 /******************************************************************************
2  * $Id: mappdf.c 7585 2008-05-13 05:19:24Z tbonfort $
3  *
4  * Project:  MapServer
5  * Purpose:  An api for PDF format output using the pdflib or libharu library
6  *           http://www.pdflib.com
7  *           http://libharu.org
8  * Author:   <jwall@webpeak.com> , <jspielberg@webpeak.com>
9  * libHaru support: <kyngchaos@kyngchaos.com>
10  *
11  ******************************************************************************
12  * Copyright (c) 1996-2005 Regents of the University of Minnesota.
13  *
14  * Permission is hereby granted, free of charge, to any person obtaining a
15  * copy of this software and associated documentation files (the "Software"),
16  * to deal in the Software without restriction, including without limitation
17  * the rights to use, copy, modify, merge, publish, distribute, sublicense,
18  * and/or sell copies of the Software, and to permit persons to whom the
19  * Software is furnished to do so, subject to the following conditions:
20  *
21  * The above copyright notice and this permission notice shall be included in
22  * all copies of this Software or works derived from this Software.
23  *
24  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
25  * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
26  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
27  * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
28  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
29  * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
30  * DEALINGS IN THE SOFTWARE.
31  ****************************************************************************/
32
33 #ifdef USE_PDF
34
35 #include <assert.h>
36 #if !defined(_WIN32)
37 #include <zlib.h>
38 #endif
39 #include "mapserver.h"
40 #if defined USE_HARUPDF
41 #include <math.h>
42 #endif
43
44 MS_CVSID("$Id: mappdf.c 7585 2008-05-13 05:19:24Z tbonfort $")
45
46 /* -------------------------------------------------------------------- */
47 /*      prototypes.                                                     */
48 /* -------------------------------------------------------------------- */
49 /*see mapserver.h*/
50
51 #include "mappdf.h"
52
53 /* -------------------------------------------------------------------- */
54 /*      PDF API functions and aliases                                   */
55 /* -------------------------------------------------------------------- */
56
57 /* NOTE: PDFlib PDF object is a pointer, thus needs a * def.  libHaru   */
58 /*       PDF, page and other objects are structs, and don't need * def. */
59
60 /* error handler for libharu */
61 #if defined USE_HARUPDF
62 static void error_handler (HPDF_STATUS error_no, HPDF_STATUS detail_no, void *user_data)
63 {
64     /* from haru_font_demo.c:
65     printf ("ERROR: error_no=%04X, detail_no=%u\n", (HPDF_UINT)error_no,
66                 (HPDF_UINT)detail_no);
67     longjmp(env, 1);
68     */
69     msSetError(MS_GDERR, "HPDF error=%04X , detail=%u.", "HPDF_Doc",
70                    (HPDF_UINT)error_no, (HPDF_UINT)detail_no);
71 }
72 #endif
73
74 /* translate page matrix */
75 static void MAPPDF_Translate(MAPPDF_DRAWOBJ_TYPE pdfpage, float x, float y)
76 {
77 #if defined USE_HARUPDF
78     HPDF_Page_Concat(pdfpage, 1, 0, 0, 1, x, y);
79 #elif defined USE_PDFLIB
80     PDF_translate(pdfpage, x, y);
81 #endif
82 }
83
84 /* scale page matrix */
85 static void MAPPDF_Scale(MAPPDF_DRAWOBJ_TYPE pdfpage, float sx, float sy)
86 {
87 #if defined USE_HARUPDF
88     HPDF_Page_Concat(pdfpage, sx, 0, 0, sy, 0, 0);
89 #elif defined USE_PDFLIB
90     PDF_scale(pdfpage, sx, sy);
91 #endif
92 }
93
94 /* rotate page matrix */
95 static void MAPPDF_Rotate(MAPPDF_DRAWOBJ_TYPE pdfpage, float a)
96 {
97 #if defined USE_HARUPDF
98     float ca = cosf(MS_DEG_TO_RAD * a);
99     float sa = sinf(MS_DEG_TO_RAD * a);
100     HPDF_Page_Concat(pdfpage, ca, sa, -sa, ca, 0, 0);
101 #elif defined USE_PDFLIB
102     PDF_rotate(pdfpage, a);
103 #endif
104 }
105
106 /************************************************************************/
107 /*                        PDF *initializeDocument                       */
108 /*                                                                      */
109 /*              Internal function to create a PDF type                  */
110 /************************************************************************/
111 MAPPDF_DOC_TYPE initializeDocument()
112 {
113     /* First we need to make the PDF */
114 #if defined USE_HARUPDF
115     /* Haru always starts in memory */
116     HPDF_Doc pdf = HPDF_New(error_handler, NULL);
117 #elif defined USE_PDFLIB
118     PDF *pdf = PDF_new();
119     PDF_open_file (pdf,""); /*This creates the file in-memory, not on disk */
120 #endif
121     MAPPDF_FN_SETINFO (pdf, MAPPDF_INFO_CREATOR, "UMN MapServer");
122     MAPPDF_FN_SETINFO (pdf, MAPPDF_INFO_AUTHOR, "Implementation for MapServer 3.7 - DM Solutions Group, based on original work by Market Insite Group");
123     MAPPDF_FN_SETINFO (pdf, MAPPDF_INFO_TITLE, "DM Solutions Group and Market Insite PDF Map");
124
125     return pdf;
126 }
127
128 /************************************************************************/
129 /*int msLoadFontSetPDF                                                  */
130 /*                                                                      */
131 /* load fonts from file.  for libHaru, we need to setup extra hash      */
132 /* table to track font name aliases.                                    */
133 /************************************************************************/
134 /**** FIXME  loading ALL fontset fonts into the PDF is silly, they may not
135              all be used in the map file.  Improved font loading: load
136              as needed in the text drawing functions (orig PDFlib
137              sortof does this already.  libHaru still needs to track
138              alias names.
139              Or, maybe PDFlib and Haru clean out unused fonts on save? ****/
140
141 #if defined USE_HARUPDF
142 int msLoadFontSetPDF(fontSetObj *fontset, HPDF_Doc pdf, hashTableObj *hfonts)
143 #elif defined USE_PDFLIB
144 int msLoadFontSetPDF(fontSetObj *fontset, PDF *pdf)
145 #endif
146 {
147
148 #if defined USE_HARUPDF
149     HPDF_Page   page;
150 #endif
151     FILE        *stream;
152     char        buffer[MS_BUFFER_LENGTH];
153     char        alias[64], file1[MS_PATH_LENGTH], file2[MS_PATH_LENGTH];
154     char        *path=NULL, *fullPath, szPath[MS_MAXPATHLEN];
155     int         i;
156
157     if (fontset == NULL) return(0);
158     if (fontset->filename == NULL) return(0);
159    
160 #if defined USE_HARUPDF
161     page = HPDF_GetCurrentPage(pdf);
162     if (!page)
163     {
164         msSetError(MS_GDERR, "Unable to get current pdf page.", "msLoadFontSetPDF()");
165         return(-1);
166     }
167     /*initHashTable(hfonts);*/
168     hfonts = msCreateHashTable();
169 #endif
170
171     /**** FIXME  why not use the path paths from the fontset object? -
172                  hasn't the fontset data already been loaded? ****/
173     stream =
174       fopen(msBuildPath(szPath, fontset->map->mappath, fontset->filename), "r");
175
176     if (!stream)
177     {
178         msSetError(MS_IOERR, "Error opening fontset %s.", "msLoadFontSetPDF()",
179                    fontset->filename);
180         return(-1);
181     }
182
183     i = 0;
184     path = msGetPath(fontset->filename);
185
186     while (fgets(buffer, MS_BUFFER_LENGTH, stream))
187     { /* while there's something to load */
188 #if defined USE_HARUPDF
189         /* get discard qualifiers warning otherwise for haru */
190         const
191 #endif
192               char *fontParam;
193
194         if(buffer[0] == '#' ||
195            buffer[0] == '\n' ||
196            buffer[0] == '\r' ||
197            buffer[0] == ' ')
198             continue; /* skip comments and blank lines */
199
200         fullPath = NULL;
201         sscanf(buffer, "%s %s", alias,  file1);
202
203         fullPath = file1;
204
205 #if defined(_WIN32) && !defined(__CYGWIN__)
206         if(strlen(file1) <= 1 || (file1[0] != '\\' && file1[1] != ':'))
207         { /* not a full path */
208             sprintf(file2, "%s%s", path, file1);
209             fullPath = msBuildPath(szPath, fontset->map->mappath, file2);
210         }
211 #else
212        if(file1[0] != '/')
213         { /* not full path */
214             sprintf(file2, "%s%s", path, file1);
215             fullPath = msBuildPath(szPath, fontset->map->mappath, file2);
216         }
217 #endif
218
219         /*ok we have the alias and the full name*/
220 #if defined USE_HARUPDF
221         /* for haru we need to load font AND store returned name in a hashtable */
222         /**** FIXME  assume TT only for now, need to check file extension
223                      and use appropriate load function ****/
224         fontParam = HPDF_LoadTTFontFromFile(pdf, fullPath, 1);
225         msInsertHashTable(hfonts, alias, fontParam);
226 #elif defined USE_PDFLIB
227         /* PDFlib allows alias naming of fonts, so it keeps track for us */
228         fontParam = (char *)malloc(sizeof(char)*(strlen(fullPath)+strlen(alias)+3));
229         sprintf(fontParam, "%s==%s", alias, fullPath);
230         PDF_set_parameter(pdf, "FontOutline", fontParam);
231         free(fontParam);
232 #endif
233         i++;
234     }
235
236     fclose(stream); /* close the file */
237
238     if( path )
239         free(path);
240
241     return(0);
242 }
243
244 /************************************************************************/
245 /*                        imageObj *msImageCreatePDF                    */
246 /*                                                                      */
247 /*      Utility function to create an image object of PDF type          */
248 /************************************************************************/
249 imageObj *msImageCreatePDF(int width, int height, outputFormatObj *format,
250                            char *imagepath, char *imageurl, mapObj *map)
251 {
252     imageObj         *oImage = NULL;
253     MAPPDF_DOC_TYPE  pdf = NULL;
254 #if defined USE_HARUPDF
255     HPDF_Page        page = NULL;
256 #endif
257     char             *driver = strdup("GD/GIF");
258
259     assert(strcasecmp(format->driver,"PDF") == 0);
260
261     oImage = (imageObj *)calloc(1,sizeof(imageObj));
262
263     oImage->format = format;
264     format->refcount++;
265
266     oImage->width = width;
267     oImage->height = height;
268     oImage->imagepath = NULL;
269     oImage->imageurl = NULL;
270
271     if (imagepath)
272     {
273         oImage->imagepath = strdup(imagepath);
274     }
275     if (imageurl)
276     {
277         oImage->imageurl = strdup(imageurl);
278     }
279
280     oImage->img.pdf = (PDFObj *)malloc(sizeof(PDFObj));
281     oImage->img.pdf->pdf = NULL;
282     oImage->img.pdf->pdf = initializeDocument();
283     oImage->img.pdf->map = map;
284     if(!oImage->img.pdf->pdf)
285     {
286         msSetError(MS_GDERR, "Unable to initialize pdfPage.", "msDrawMap()");
287         return(NULL);
288     }
289
290     pdf = oImage->img.pdf->pdf;
291     /*  load fonts and set the pdf ready to be drawn into*/
292 #if defined USE_HARUPDF
293     /* oImage->img.pdf->hfonts = NULL; */
294     msLoadFontSetPDF(&(map->fontset), pdf, &(oImage->img.pdf->hfonts));
295     page = HPDF_AddPage(pdf);
296     if (!page)
297     {
298         msSetError(MS_GDERR, "Unable to add new pdf page.", "msDrawMap()");
299         return(NULL);
300     }
301     HPDF_Page_SetWidth(page, (float)width);
302     HPDF_Page_SetHeight(page, (float)height);
303 #elif defined USE_PDFLIB
304     msLoadFontSetPDF((&(map->fontset)), pdf);
305     PDF_begin_page(pdf, (float)width, (float)height);
306 #endif
307
308     /* pdf has it's origin in the bottom left so we need to flip the coordinate */
309     /* system to fit with the rest of mapserver. */
310     MAPPDF_Translate(MAPPDF_DRAWOBJ, 0, (float)map->height);
311     MAPPDF_Scale(MAPPDF_DRAWOBJ, 1, -1);
312
313     /*render text with both a fill and a stroke*/
314 #if defined USE_HARUPDF
315     HPDF_Page_SetTextRenderingMode(page, HPDF_FILL_THEN_STROKE);
316 #elif defined USE_PDFLIB
317     PDF_set_value(pdf, "textrendering", 2);
318 #endif
319
320 /* -------------------------------------------------------------------- */
321 /*      if the output raster, we create a GD image that                 */
322 /*      will be used to contain the rendering of all the layers.        */
323 /* -------------------------------------------------------------------- */
324     if (strcasecmp(msGetOutputFormatOption(oImage->format,"OUTPUT_TYPE",""),
325                    "RASTER") != 0)
326     {
327         oImage->img.pdf->imagetmp = NULL;
328     }
329     else
330     {
331 #ifdef USE_GD_GIF
332         driver = strdup("GD/GIF");
333 #else
334
335 #ifdef USE_GD_PNG
336         driver = strdup("GD/PNG");
337 #else
338
339 #ifdef USE_GD_JPEG
340         driver = strdup("GD/JPEG");
341 #else
342
343 #ifdef USE_GD_WBMP
344         driver = strdup("GD/WBMP");
345 #endif
346
347 #endif
348 #endif
349 #endif
350
351         oImage->img.pdf->imagetmp = (imageObj *)
352           msImageCreateGD(map->width, map->height,
353                           msCreateDefaultOutputFormat(map, driver),
354                           map->web.imagepath, map->web.imageurl);
355     }
356     return oImage;
357 }
358
359 /************************************************************************/
360 /* void drawPolylinePDF                                                 */
361 /*                                                                      */
362 /* internal function to do line drawing in the pdf                      */
363 /************************************************************************/
364 void drawPolylinePDF(MAPPDF_DOC_TYPE pdf, shapeObj *p, colorObj  *c, double width)
365 {
366     int        i, j;
367 #if defined USE_HARUPDF
368     HPDF_Page  page = NULL;
369
370     page = HPDF_GetCurrentPage(pdf);
371     if (!page)
372     {
373         msSetError(MS_GDERR, "Unable to get current pdf page.", "msDrawMap()");
374         return;
375     }
376 #endif
377
378     if (width)
379     {
380         MAPPDF_FN_SETLINEJOIN(MAPPDF_DRAWOBJ, MAPPDF_LINEJOIN_ROUND);
381         MAPPDF_FN_SETLINEWIDTH(MAPPDF_DRAWOBJ, (float)width);
382     }
383
384     if (c)
385     {
386         /* old version set both fill & stroke color, but we only need stroke */
387         MAPPDF_FN_SETRGBSTROKECOLOR(MAPPDF_DRAWOBJ, (float)c->red/255,
388                                                     (float)c->green/255,
389                                                     (float)c->blue/255);
390     }
391
392     for (i = 0; i < p->numlines; i++)
393     {
394         if (p->line[i].numpoints)
395         {
396             MAPPDF_FN_MOVETO(MAPPDF_DRAWOBJ, (float)p->line[i].point[0].x,
397                                              (float)p->line[i].point[0].y);
398         }
399         for(j=1; j<p->line[i].numpoints; j++)
400         {
401             MAPPDF_FN_LINETO(MAPPDF_DRAWOBJ, (float)p->line[i].point[j].x,
402                                              (float)p->line[i].point[j].y);
403         }
404     }
405     MAPPDF_FN_STROKE(MAPPDF_DRAWOBJ);
406 /*  MAPPDF_FN_SETLINEJOIN(MAPPDF_DRAWOBJ, MAPPDF_LINEJOIN_MITER); */
407     MAPPDF_FN_SETLINEWIDTH(MAPPDF_DRAWOBJ, 1);
408
409 }
410
411 /**** FIXME  why pdflib >= 6?  I see dashed lines in v5.  bugs? ****/
412 #if defined USE_HARUPDF || (defined USE_PDFLIB && PDFLIB_MAJORVERSION >= 6)
413  /************************************************************************/
414  /* void drawDashedPolylinePDF                                           */
415  /*                                                                      */
416  /* internal function to do line drawing in the pdf, using dashed        */
417  /* patterns.  Note that haru currently has a max pattern length of 8    */
418  /* while MS has a max of 10.                                            */
419  /************************************************************************/
420  void drawDashedPolylinePDF(MAPPDF_DOC_TYPE pdf, shapeObj *p, symbolObj  *s,
421                             colorObj *c, double width)
422  {
423     int          i, j;
424 #if defined USE_HARUPDF
425     HPDF_Page    page = NULL;
426     HPDF_UINT16  optlist[8];
427 #elif defined USE_PDFLIB
428     int          optlistlength = 14;
429     char         *optlist = NULL;
430 #endif
431
432 #if defined USE_HARUPDF
433     page = HPDF_GetCurrentPage(pdf);
434     if (!page)
435     {
436         msSetError(MS_GDERR, "Unable to get current pdf page.", "msDrawMap()");
437         return;
438     }
439
440     if (s->patternlength > 8)
441     {
442         msSetError(MS_SYMERR, "Max PDF pattern length is 8.", "drawDashedPolylinePDF()");
443         return;
444     }
445 #endif
446    
447     if (s->patternlength > 0)
448     {
449         for (i = 0; i < s->patternlength; i++)
450         {
451             if (s->pattern[i] > 0)
452 #if defined USE_HARUPDF
453                 optlist[i] = (HPDF_UINT16)s->pattern[i];
454 #elif defined USE_PDFLIB
455                 optlistlength += (int)(log10(s->pattern[i])) + 2;
456 #endif
457             else
458             {
459                 msSetError(MS_SYMERR, "Symbol patterns must be positive.", "drawDashedPolylinePDF()");
460                 return;
461             }
462         }
463 #if defined USE_HARUPDF
464         HPDF_Page_SetDash(page, optlist, s->patternlength, 0);
465 #elif defined USE_PDFLIB
466         optlist = (char*)malloc(optlistlength*sizeof(char));
467         sprintf(optlist, "dasharray={");
468         for (i = 0; i < s->patternlength; i++)
469             sprintf(optlist, "%s %d", optlist, s->pattern[i]);
470         sprintf(optlist, "%s }", optlist);
471         PDF_setdashpattern(pdf, optlist);
472 #endif
473     }
474
475     if (width)
476     {
477         MAPPDF_FN_SETLINEJOIN(MAPPDF_DRAWOBJ, MAPPDF_LINEJOIN_ROUND);
478         MAPPDF_FN_SETLINEWIDTH(MAPPDF_DRAWOBJ, (float)width);
479     }
480
481     if (c)
482     {
483         MAPPDF_FN_SETRGBSTROKECOLOR(MAPPDF_DRAWOBJ, (float)c->red/255,
484                                                     (float)c->green/255,
485                                                     (float)c->blue/255);
486     }
487
488     for (i = 0; i < p->numlines; i++)
489     {
490         if (p->line[i].numpoints)
491         {
492             MAPPDF_FN_MOVETO(MAPPDF_DRAWOBJ, (float)p->line[i].point[0].x,
493                                              (float)p->line[i].point[0].y);
494         }
495         for(j = 1; j<p->line[i].numpoints; j++)
496         {
497             MAPPDF_FN_LINETO(MAPPDF_DRAWOBJ, (float)p->line[i].point[j].x,
498                                              (float)p->line[i].point[j].y);
499         }
500     }
501      
502     MAPPDF_FN_STROKE(MAPPDF_DRAWOBJ);
503 #if defined USE_HARUPDF
504     HPDF_Page_SetDash(page, NULL, 0, 0);
505 #elif defined USE_PDFLIB
506     PDF_setdashpattern(pdf, "");
507     free(optlist);
508 #endif
509     MAPPDF_FN_SETLINEWIDTH(MAPPDF_DRAWOBJ, 1);
510 }
511 #endif
512
513 /************************************************************************/
514 /*  void msDrawLineSymbolPDF                                            */
515 /*                                                                      */
516 /*  Draw a line symbol of the specified size and color                  */
517 /************************************************************************/
518 void msDrawLineSymbolPDF(symbolSetObj *symbolset, imageObj *image, shapeObj *p,
519                          styleObj *style, double scalefactor)
520 {
521     int              size;
522     MAPPDF_DOC_TYPE  pdf;
523
524     if (style->size == -1)
525     {
526         size = msSymbolGetDefaultSize(symbolset->symbol[style->symbol]);
527         size = MS_NINT(size*scalefactor);
528     }
529     else
530         size = MS_NINT(style->size*scalefactor);
531     pdf = image->img.pdf->pdf;
532
533     if (p->numlines <= 0)
534         return;
535
536   /* no such symbol, 0 is OK */
537     if (style->symbol > symbolset->numsymbols || style->symbol < 0)
538         return;
539
540     if (!(MS_VALID_COLOR(style->color))) /* invalid color */
541         return;
542
543     if (size < 1) /* size too small */
544         return;
545
546 #if defined USE_HARUPDF || (defined USE_PDFLIB && PDFLIB_MAJORVERSION >= 6)
547     if (symbolset && symbolset->symbol[style->symbol]->patternlength)
548        drawDashedPolylinePDF(pdf, p, symbolset->symbol[style->symbol],
549                              &(style->color), size);
550     else
551 #endif
552        drawPolylinePDF(pdf, p, &(style->color),  size);
553
554     return;
555 }
556
557 /************************************************************************/
558 /*  void msFilledPolygonPDF                                             */
559 /*                                                                      */
560 /*  creates a filled polygon in the pdf from a shape                    */
561 /*  Note: not part of external api, despite name                        */
562 /************************************************************************/
563 void msFilledPolygonPDF(MAPPDF_DOC_TYPE pdf, shapeObj *p, colorObj  *c)
564 {
565     int        i, j;
566 #if defined USE_HARUPDF
567     HPDF_Page  page = NULL;
568
569     page = HPDF_GetCurrentPage(pdf);
570     if (!page)
571     {
572         msSetError(MS_GDERR, "Unable to get current pdf page.", "msDrawMap()");
573         return;
574     }
575 #endif
576
577     if (c)
578     {
579         MAPPDF_FN_SETRGBSTROKECOLOR(MAPPDF_DRAWOBJ, (float)c->red/255,
580                                                     (float)c->green/255,
581                                                     (float)c->blue/255);
582         MAPPDF_FN_SETRGBFILLCOLOR(MAPPDF_DRAWOBJ, (float)c->red/255,
583                                                   (float)c->green/255,
584                                                   (float)c->blue/255);
585     }
586    
587     for (i = 0; i < p->numlines; i++)
588     {
589         if (p->line[i].numpoints)
590         {
591             MAPPDF_FN_MOVETO(MAPPDF_DRAWOBJ, (float)p->line[i].point[0].x,
592                                              (float)p->line[i].point[0].y);
593         }
594         for(j=1; j<p->line[i].numpoints; j++){
595             MAPPDF_FN_LINETO(MAPPDF_DRAWOBJ, (float)p->line[i].point[j].x,
596                                              (float)p->line[i].point[j].y);
597         }
598     }
599     MAPPDF_FN_CLOSEFILLSTROKE(MAPPDF_DRAWOBJ);
600
601 }
602
603 /************************************************************************/
604 /*  void billboardPDF                                                   */
605 /*                                                                      */
606 /*  creates a filled polygon in the pdf as a background                 */
607 /************************************************************************/
608 void billboardPDF(MAPPDF_DOC_TYPE pdf, shapeObj *shape, labelObj *label)
609 {
610     int       i;
611     shapeObj  temp;
612
613     msInitShape(&temp);
614     msAddLine(&temp, &shape->line[0]);
615
616     if (label->backgroundshadowcolor.pen >= 0)
617     {
618         for (i=0; i<temp.line[0].numpoints; i++)
619         {
620             temp.line[0].point[i].x += label->backgroundshadowsizex;
621             temp.line[0].point[i].y += label->backgroundshadowsizey;
622         }
623         msFilledPolygonPDF(pdf, &temp, &label->backgroundshadowcolor);
624         for (i=0; i<temp.line[0].numpoints; i++)
625         {
626             temp.line[0].point[i].x -= label->backgroundshadowsizex;
627             temp.line[0].point[i].y -= label->backgroundshadowsizey;
628         }
629     }
630
631     msFilledPolygonPDF(pdf, &temp, &label->backgroundcolor);
632
633     msFreeShape(&temp);
634 }
635
636 /************************************************************************/
637 /*  void msDrawLabelPDF                                                 */
638 /*                                                                      */
639 /*  draws a single label at the specified position                      */
640 /************************************************************************/
641 int msDrawLabelPDF(imageObj *image, pointObj labelPnt, char *string,
642                    labelObj *label, fontSetObj *fontset, double scalefactor)
643 {
644     if (!string)
645         return(0); /* not an error, just don't want to do anything */
646
647     if (strlen(string) == 0)
648         return(0); /* not an error, just don't want to do anything */
649
650     if (label->position != MS_XY)
651     {
652         pointObj p;
653         rectObj r;
654
655         if (msGetLabelSize(image,string, label, &r, fontset, scalefactor, MS_FALSE) == -1)
656             return(-1);
657         p = get_metrics(&labelPnt, label->position, r,
658                                    label->offsetx, label->offsety,
659                                    label->angle, 0, NULL);
660         msDrawTextPDF(image, p, string,
661                  label, fontset, scalefactor);
662     }
663     else
664     {
665         labelPnt.x += label->offsetx;
666         labelPnt.y += label->offsety;
667         msDrawTextPDF(image,labelPnt, string,
668                  label, fontset, scalefactor);
669     }
670
671     return(0);
672
673 }
674
675 /************************************************************************/
676 /*  int msDrawLabelCachePDF                                             */
677 /*                                                                      */
678 /*  draws a set of labels                                               */
679 /************************************************************************/
680 int msDrawLabelCachePDF(imageObj *image, mapObj *map)
681 {
682     pointObj             p;
683     int                  i, j, l, priority;
684     rectObj              r;
685     labelCacheMemberObj  *cachePtr=NULL;
686     layerObj             *layerPtr=NULL;
687     labelObj             *labelPtr=NULL;
688
689     int      draw_marker;
690     int      marker_width, marker_height;
691     int      marker_offset_x, marker_offset_y;
692     rectObj  marker_rect;
693    
694     imageObj *imagetmp = NULL;
695
696 /* -------------------------------------------------------------------- */
697 /*      if not PDF, return.                                             */
698 /* -------------------------------------------------------------------- */
699     if (image == NULL || map == NULL || !MS_DRIVER_PDF(image->format))
700         return -1;
701 /* -------------------------------------------------------------------- */
702 /*      if the output raster, we crate a GD image that                  */
703 /*      will be used to conating the rendering of all the layers.       */
704 /* -------------------------------------------------------------------- */
705     if (strcasecmp(msGetOutputFormatOption(image->format,"OUTPUT_TYPE",""),
706                    "RASTER") == 0)
707     {
708         int orig_renderer;
709         imagetmp = (imageObj *)image->img.pdf->imagetmp;
710         msImageInitGD( imagetmp, &map->imagecolor);
711         orig_renderer=image->format->renderer;
712         image->format->renderer = MS_RENDER_WITH_GD;
713         msDrawLabelCache(imagetmp, map);
714         image->format->renderer = orig_renderer;
715         return 0;
716     }
717
718     for (priority=MS_MAX_LABEL_PRIORITY-1; priority>=0; priority--)
719     {
720         labelCacheSlotObj *cacheslot;
721         cacheslot = &(map->labelcache.slots[priority]);
722
723         for (l=cacheslot->numlabels-1; l>=0; l--)
724         {
725             /* point to right spot in cache */
726             cachePtr = &(cacheslot->labels[l]);
727
728             /* set a couple of other pointers, avoids nasty references */
729             layerPtr = (GET_LAYER(map, cachePtr->layerindex));
730             /* classPtr = &(cachePtr->class); */
731             labelPtr = &(cachePtr->label);
732
733             if (!cachePtr->text)
734                 continue; /* not an error, just don't want to do anything */
735
736             if (strlen(cachePtr->text) == 0)
737                 continue; /* not an error, just don't want to do anything */
738
739             if (msGetLabelSize(image,cachePtr->text, labelPtr, &r, &(map->fontset), layerPtr->scalefactor, MS_TRUE) == -1)
740                 return(-1);
741
742             if (labelPtr->autominfeaturesize && ((r.maxx-r.minx) > cachePtr->featuresize))
743                 continue; /* label too large relative to the feature */
744
745             draw_marker = marker_offset_x = marker_offset_y = 0; /* assume no marker */
746             if (layerPtr->type == MS_LAYER_ANNOTATION || layerPtr->type == MS_LAYER_POINT)
747             { /* there *is* a marker */
748
749                     /* TO DO: at the moment only checks the bottom style,
750                        perhaps should check all of them */
751                 if (cachePtr->numstyles==0)
752                     marker_width = marker_height = 1;
753                 else if (msGetMarkerSize(&map->symbolset, &(cachePtr->styles[0]), &marker_width, &marker_height, layerPtr->scalefactor) != MS_SUCCESS)
754                         return(-1);
755
756                 marker_offset_x = MS_NINT(marker_width/2.0);
757                 marker_offset_y = MS_NINT(marker_height/2.0);
758
759                 marker_rect.minx = MS_NINT(cachePtr->point.x - .5 * marker_width);
760                 marker_rect.miny = MS_NINT(cachePtr->point.y - .5 * marker_height);
761                 marker_rect.maxx = marker_rect.minx + (marker_width-1);
762                 marker_rect.maxy = marker_rect.miny + (marker_height-1);
763
764                 /* actually draw the marker */
765                 if (layerPtr->type == MS_LAYER_ANNOTATION) draw_marker = 1;
766             }
767
768             if (labelPtr->position == MS_AUTO)
769             {
770                 if (layerPtr->type == MS_LAYER_LINE)
771                 {
772                     int position = MS_UC;
773            
774                     /* Two angles or two positions, depending on angle. Steep angles */
775                     /* will use the angle approach, otherwise we'll rotate between*/
776                     /* UC and LC. */
777                     for (j=0; j<2; j++)
778                     {
779                         msFreeShape(cachePtr->poly);
780                         /* assume label *can* be drawn */
781                         cachePtr->status = MS_TRUE;
782
783                         p = get_metrics(&(cachePtr->point), position, r,
784                                         (marker_offset_x + labelPtr->offsetx),
785                                         (marker_offset_y + labelPtr->offsety),
786                                         labelPtr->angle, labelPtr->buffer, cachePtr->poly);
787
788                         /*save marker bounding polygon*/
789                         if (draw_marker)
790                             msRectToPolygon(marker_rect, cachePtr->poly);
791
792                         /* Compare against image bounds, rendered labels and markers
793                            (sets cachePtr->status) */
794                         msTestLabelCacheCollisions(&(map->labelcache), labelPtr,
795                                                    map->width, map->height,
796                                                    labelPtr->buffer, cachePtr, priority, l);
797
798                         /*found a suitable place for this label*/
799                         if (cachePtr->status)
800                             break;
801
802                     }  /*next angle*/
803
804                 }
805                 else
806                 {
807                     /* loop through the outer label positions */
808                     for (j=0; j<=7; j++)
809                     {
810                         msFreeShape(cachePtr->poly);
811                         /* assume label *can* be drawn */
812                         cachePtr->status = MS_TRUE;
813
814                         p = get_metrics(&(cachePtr->point), j, r,
815                                         (marker_offset_x + labelPtr->offsetx),
816                                         (marker_offset_y + labelPtr->offsety),
817                                         labelPtr->angle, labelPtr->buffer, cachePtr->poly);
818
819                         /* save marker bounding polygon*/
820                         if (draw_marker)
821                             msRectToPolygon(marker_rect, cachePtr->poly);
822
823                         /* Compare against image bounds, rendered labels and markers
824                            (sets cachePtr->status) */
825                         msTestLabelCacheCollisions(&(map->labelcache), labelPtr,
826                                                    map->width, map->height,
827                                                    labelPtr->buffer, cachePtr, priority, l);
828
829                         /* found a suitable place for this label*/
830                         if (cachePtr->status)
831                             break;
832                     } /*next position*/
833                 }
834
835                 /* draw in spite of collisions based on last position,
836                    need a *best* position */
837                 if (labelPtr->force) cachePtr->status = MS_TRUE;
838             }
839             else
840             {
841                 /* assume label *can* be drawn */
842                 cachePtr->status = MS_TRUE;
843
844                 /*don't need the marker_offset*/
845                 if (labelPtr->position == MS_CC)
846                 {
847                     p = get_metrics(&(cachePtr->point), labelPtr->position, r,
848                                     labelPtr->offsetx, labelPtr->offsety,
849                                     labelPtr->angle,  labelPtr->buffer, cachePtr->poly);
850                 }