1 //C-  -*- C++ -*-
2 //C- -------------------------------------------------------------------
3 //C- DjVuLibre-3.5
4 //C- Copyright (c) 2002  Leon Bottou and Yann Le Cun.
5 //C- Copyright (c) 2001  AT&T
6 //C-
7 //C- This software is subject to, and may be distributed under, the
8 //C- GNU General Public License, either Version 2 of the license,
9 //C- or (at your option) any later version. The license should have
10 //C- accompanied the software or you may obtain a copy of the license
11 //C- from the Free Software Foundation at http://www.fsf.org .
12 //C-
13 //C- This program is distributed in the hope that it will be useful,
14 //C- but WITHOUT ANY WARRANTY; without even the implied warranty of
15 //C- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16 //C- GNU General Public License for more details.
17 //C-
18 //C- DjVuLibre-3.5 is derived from the DjVu(r) Reference Library from
19 //C- Lizardtech Software.  Lizardtech Software has authorized us to
20 //C- replace the original DjVu(r) Reference Library notice by the following
21 //C- text (see doc/lizard2002.djvu and doc/lizardtech2007.djvu):
22 //C-
23 //C-  ------------------------------------------------------------------
24 //C- | DjVu (r) Reference Library (v. 3.5)
25 //C- | Copyright (c) 1999-2001 LizardTech, Inc. All Rights Reserved.
26 //C- | The DjVu Reference Library is protected by U.S. Pat. No.
27 //C- | 6,058,214 and patents pending.
28 //C- |
29 //C- | This software is subject to, and may be distributed under, the
30 //C- | GNU General Public License, either Version 2 of the license,
31 //C- | or (at your option) any later version. The license should have
32 //C- | accompanied the software or you may obtain a copy of the license
33 //C- | from the Free Software Foundation at http://www.fsf.org .
34 //C- |
35 //C- | The computer code originally released by LizardTech under this
36 //C- | license and unmodified by other parties is deemed "the LIZARDTECH
37 //C- | ORIGINAL CODE."  Subject to any third party intellectual property
38 //C- | claims, LizardTech grants recipient a worldwide, royalty-free,
39 //C- | non-exclusive license to make, use, sell, or otherwise dispose of
40 //C- | the LIZARDTECH ORIGINAL CODE or of programs derived from the
41 //C- | LIZARDTECH ORIGINAL CODE in compliance with the terms of the GNU
42 //C- | General Public License.   This grant only confers the right to
43 //C- | infringe patent claims underlying the LIZARDTECH ORIGINAL CODE to
44 //C- | the extent such infringement is reasonably necessary to enable
45 //C- | recipient to make, have made, practice, sell, or otherwise dispose
46 //C- | of the LIZARDTECH ORIGINAL CODE (or portions thereof) and not to
47 //C- | any greater extent that may be necessary to utilize further
48 //C- | modifications or combinations.
49 //C- |
50 //C- | The LIZARDTECH ORIGINAL CODE is provided "AS IS" WITHOUT WARRANTY
51 //C- | OF ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED
52 //C- | TO ANY WARRANTY OF NON-INFRINGEMENT, OR ANY IMPLIED WARRANTY OF
53 //C- | MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE.
54 //C- +------------------------------------------------------------------
55 
56 #ifdef HAVE_CONFIG_H
57 # include "config.h"
58 #endif
59 #if NEED_GNUG_PRAGMAS
60 # pragma implementation
61 #endif
62 
63 #include "GMapAreas.h"
64 #include "GException.h"
65 #include "debug.h"
66 
67 #include <math.h>
68 #include <stdio.h>
69 
70 
71 #ifdef HAVE_NAMESPACES
72 namespace DJVU {
73 # ifdef NOT_DEFINED // Just to fool emacs c++ mode
74 }
75 #endif
76 #endif
77 
78 
79 /****************************************************************************
80 ***************************** GMapArea definition ***************************
81 ****************************************************************************/
82 
83 const char GMapArea::MAPAREA_TAG[] = 		"maparea";
84 const char GMapArea::RECT_TAG[] = 		"rect";
85 const char GMapArea::POLY_TAG[] = 		"poly";
86 const char GMapArea::OVAL_TAG[] = 		"oval";
87 const char GMapArea::NO_BORDER_TAG[] = 		"none";
88 const char GMapArea::XOR_BORDER_TAG[] = 	"xor";
89 const char GMapArea::SOLID_BORDER_TAG[] = 	"border";
90 const char GMapArea::SHADOW_IN_BORDER_TAG[] = 	"shadow_in";
91 const char GMapArea::SHADOW_OUT_BORDER_TAG[] = 	"shadow_out";
92 const char GMapArea::SHADOW_EIN_BORDER_TAG[] = 	"shadow_ein";
93 const char GMapArea::SHADOW_EOUT_BORDER_TAG[] = "shadow_eout";
94 const char GMapArea::BORDER_AVIS_TAG[] = 	"border_avis";
95 const char GMapArea::HILITE_TAG[] = 		"hilite";
96 const char GMapArea::URL_TAG[] = 		"url";
97 const char GMapArea::TARGET_SELF[] = 		"_self";
98 static const char zero_width[] = ERR_MSG("GMapAreas.zero_width");
99 static const char zero_height[] = ERR_MSG("GMapAreas.zero_height");
100 static const char width_1[] = ERR_MSG("GMapAreas.width_1");
101 static const char width_3_32 [] = ERR_MSG("GMapAreas.width_3-32");
102 static const char error_poly_border [] = ERR_MSG("GMapAreas.poly_border");
103 static const char error_poly_hilite [] = ERR_MSG("GMapAreas.poly_hilite");
104 static const char error_oval_border [] = ERR_MSG("GMapAreas.oval_border");
105 static const char error_oval_hilite [] = ERR_MSG("GMapAreas.oval_hilite");
106 static const char error_too_few_points [] = ERR_MSG("GMapAreas.too_few_points");
107 static const char error_intersect [] = ERR_MSG("GMapAreas.intersect");
108 
~GMapArea()109 GMapArea::~GMapArea() {}
110 
~GMapRect()111 GMapRect::~GMapRect() {}
112 
~GMapPoly()113 GMapPoly::~GMapPoly() {}
114 
~GMapOval()115 GMapOval::~GMapOval() {}
116 
117 void
initialize_bounds(void)118 GMapArea::initialize_bounds(void)
119 {
120    xmin=gma_get_xmin();
121    xmax=gma_get_xmax();
122    ymin=gma_get_ymin();
123    ymax=gma_get_ymax();
124    bounds_initialized=true;
125 }
126 
127 int
get_xmin(void) const128 GMapArea::get_xmin(void) const
129 {
130    if (!bounds_initialized)
131      const_cast<GMapArea *>(this)->initialize_bounds();
132    return xmin;
133 }
134 
135 int
get_ymin(void) const136 GMapArea::get_ymin(void) const
137 {
138    if (!bounds_initialized)
139      const_cast<GMapArea *>(this)->initialize_bounds();
140    return ymin;
141 }
142 
143 int
get_xmax(void) const144 GMapArea::get_xmax(void) const
145 {
146    if (!bounds_initialized)
147      const_cast<GMapArea *>(this)->initialize_bounds();
148    return xmax;
149 }
150 
151 int
get_ymax(void) const152 GMapArea::get_ymax(void) const
153 {
154    if (!bounds_initialized)
155      const_cast<GMapArea *>(this)->initialize_bounds();
156    return ymax;
157 }
158 
159 GRect
get_bound_rect(void) const160 GMapArea::get_bound_rect(void) const
161 {
162    return GRect(get_xmin(), get_ymin(), get_xmax()-get_xmin(),
163 		get_ymax()-get_ymin());
164 }
165 
166 void
move(int dx,int dy)167 GMapArea::move(int dx, int dy)
168 {
169    if (dx || dy)
170    {
171      if (bounds_initialized)
172      {
173         xmin+=dx;
174         ymin+=dy;
175         xmax+=dx;
176         ymax+=dy;
177      }
178      gma_move(dx, dy);
179    }
180 }
181 
182 void
resize(int new_width,int new_height)183 GMapArea::resize(int new_width, int new_height)
184 {
185    if (get_xmax()-get_xmin()!=new_width ||
186        get_ymax()-get_ymin()!=new_height)
187    {
188      gma_resize(new_width, new_height);
189      bounds_initialized=false;
190    }
191 }
192 
193 void
transform(const GRect & grect)194 GMapArea::transform(const GRect & grect)
195 {
196    if (grect.xmin!=get_xmin() || grect.ymin!=get_ymin() ||
197        grect.xmax!=get_xmax() || grect.ymax!=get_ymax())
198    {
199      gma_transform(grect);
200      bounds_initialized=false;
201    }
202 }
203 
204 char const * const
check_object(void)205 GMapArea::check_object(void)
206 {
207    char const *retval;
208    if (get_xmax()==get_xmin())
209    {
210      retval=zero_width;
211    }
212    else if (get_ymax()==get_ymin())
213    {
214      retval=zero_height;
215    }
216    else if ((border_type==XOR_BORDER ||
217        border_type==SOLID_BORDER) && border_width!=1)
218    {
219      retval=width_1;
220    }
221    else if ((border_type==SHADOW_IN_BORDER ||
222        border_type==SHADOW_OUT_BORDER ||
223        border_type==SHADOW_EIN_BORDER ||
224        border_type==SHADOW_EOUT_BORDER)&&
225        (border_width<3 || border_width>32))
226    {
227      retval=width_3_32;
228    }else
229    {
230      retval=gma_check_object();
231    }
232    return retval;
233 }
234 
235 bool
is_point_inside(int x,int y) const236 GMapArea::is_point_inside(int x, int y) const
237 {
238    if (!bounds_initialized)
239      const_cast<GMapArea *>(this)->initialize_bounds();
240    return (x>=xmin && x<xmax && y>=ymin && y<ymax) ?
241 	      gma_is_point_inside(x, y) : false;
242 }
243 
make_c_string(GUTF8String string)244 static GUTF8String make_c_string(GUTF8String string)
245 {
246   GUTF8String buffer;
247   const char *data = (const char*)string;
248   int length = string.length();
249   buffer = GUTF8String("\"");
250   while (*data && length>0)
251     {
252       int span = 0;
253       while (span<length && (unsigned char)(data[span])>=0x20 &&
254              data[span]!=0x7f && data[span]!='"' && data[span]!='\\' )
255         span++;
256       if (span > 0)
257         {
258           buffer = buffer + GUTF8String(data, span);
259           data += span;
260           length -= span;
261         }
262       else
263         {
264           char buf[8];
265           static const char *tr1 = "\"\\tnrbf";
266           static const char *tr2 = "\"\\\t\n\r\b\f";
267           sprintf(buf,"\\%03o", (int)(((unsigned char*)data)[span]));
268           for (int i=0; tr2[i]; i++)
269             if (data[span] == tr2[i])
270               buf[1] = tr1[i];
271           if (buf[1]<'0' || buf[1]>'3')
272             buf[2] = 0;
273           buffer = buffer + GUTF8String(buf);
274           data += 1;
275           length -= 1;
276         }
277     }
278   buffer = buffer + GUTF8String("\"");
279   return buffer;
280 }
281 
282 GUTF8String
print(void)283 GMapArea::print(void)
284 {
285       // Make this hard check to make sure, that *no* illegal GMapArea
286       // can be stored into a file.
287    const char * const errors=check_object();
288    if (errors[0])
289    {
290      G_THROW(errors);
291    }
292 
293    GUTF8String url1 = make_c_string((GUTF8String)url);
294    GUTF8String target1 = make_c_string(target);
295    GUTF8String comment1 = make_c_string(comment);
296 
297    GUTF8String border_color_str;
298    border_color_str.format("#%02X%02X%02X",
299 	   (border_color & 0xff0000) >> 16,
300 	   (border_color & 0xff00) >> 8,
301 	   (border_color & 0xff));
302 
303    static const GUTF8String left('(');
304    static const GUTF8String right(')');
305    static const GUTF8String space(' ');
306    GUTF8String border_type_str;
307    switch(border_type)
308    {
309       case NO_BORDER:
310         border_type_str=left+NO_BORDER_TAG+right;
311         break;
312       case XOR_BORDER:
313         border_type_str=left+XOR_BORDER_TAG+right;
314         break;
315       case SOLID_BORDER:
316         border_type_str=left+SOLID_BORDER_TAG+space+border_color_str+right;
317         break;
318       case SHADOW_IN_BORDER:
319         border_type_str=left+SHADOW_IN_BORDER_TAG+space+GUTF8String(border_width)+right;
320         break;
321       case SHADOW_OUT_BORDER:
322         border_type_str=left+SHADOW_OUT_BORDER_TAG+space+GUTF8String(border_width)+right;
323         break;
324       case SHADOW_EIN_BORDER:
325         border_type_str=left+SHADOW_EIN_BORDER_TAG+space+GUTF8String(border_width)+right;
326         break;
327       case SHADOW_EOUT_BORDER:
328         border_type_str=left+SHADOW_EOUT_BORDER_TAG+space+GUTF8String(border_width)+right;
329         break;
330       default:
331         border_type_str=left+XOR_BORDER_TAG+right;
332         break;
333    }
334 
335    GUTF8String hilite_str;
336    if (hilite_color!=0xffffffff)
337    {
338       hilite_str.format("(%s #%02X%02X%02X)",
339 	      HILITE_TAG, (hilite_color & 0xff0000) >> 16,
340 	      (hilite_color & 0xff00) >> 8,
341 	      (hilite_color & 0xff));
342    }
343 
344    GUTF8String URL;
345    if (target1==TARGET_SELF)
346    {
347       URL=url1;
348    }else
349    {
350       URL=left+URL_TAG+space+url1+space+target1+right;
351    }
352 
353    GUTF8String total=left+MAPAREA_TAG+space+URL+space+comment1+space+gma_print()+border_type_str;
354    if (border_always_visible)
355      total+=space+left+BORDER_AVIS_TAG+right;
356    if ( hilite_str.length() > 0 )
357      total+=space+hilite_str;
358    total+=right;
359    return total;
360 }
361 
362 /*
363 void
364 GMapArea::map(GRectMapper &mapper)
365 {
366     get_bound_rect();
367     GRect rect = GRect(xmin, ymin, xmax, ymax);
368     mapper.map(rect);
369     xmin = rect.xmin;
370     ymin = rect.ymin;
371     xmax = rect.xmax;
372     ymax = rect.ymax;
373     clear_bounds();
374 }
375 void
376 GMapArea::unmap(GRectMapper &mapper)
377 {
378     get_bound_rect();
379     GRect rect = GRect(xmin, ymin, xmax, ymax);
380     mapper.unmap(rect);
381     xmin = rect.xmin;
382     ymin = rect.ymin;
383     xmax = rect.xmax;
384     ymax = rect.ymax;
385     clear_bounds();
386 }
387 */
388 
389 
390 /// Virtual function generating a list of defining coordinates
391 /// (default are the opposite corners of the enclosing rectangle)
get_coords(GList<int> & CoordList) const392 void GMapArea::get_coords( GList<int> & CoordList ) const
393 {
394   CoordList.append( get_xmin() );
395   CoordList.append( get_ymin() );
396   CoordList.append( get_xmax() );
397   CoordList.append( get_ymax() );
398 }
399 
400 
401 /****************************************************************************
402 **************************** GMapRect definition ****************************
403 ****************************************************************************/
404 
405 void
gma_resize(int new_width,int new_height)406 GMapRect::gma_resize(int new_width, int new_height)
407 {
408    xmax=xmin+new_width;
409    ymax=ymin+new_height;
410 }
411 
412 void
gma_transform(const GRect & grect)413 GMapRect::gma_transform(const GRect & grect)
414 {
415    xmin=grect.xmin; ymin=grect.ymin;
416    xmax=grect.xmax; ymax=grect.ymax;
417 }
418 
419 GUTF8String
gma_print(void)420 GMapRect::gma_print(void)
421 {
422    GUTF8String buffer;
423    return buffer.format("(%s %d %d %d %d) ",
424 	   RECT_TAG, xmin, ymin, xmax-xmin, ymax-ymin);
425 }
426 
427 void
map(GRectMapper & mapper)428 GMapRect::map(GRectMapper &mapper)
429 {
430     get_bound_rect();
431     GRect rect;
432     rect.xmin = xmin;
433     rect.xmax = xmax;
434     rect.ymin = ymin;
435     rect.ymax = ymax;
436     mapper.map(rect);
437     xmin = rect.xmin;
438     ymin = rect.ymin;
439     xmax = rect.xmax;
440     ymax = rect.ymax;
441     clear_bounds();
442 }
443 void
unmap(GRectMapper & mapper)444 GMapRect::unmap(GRectMapper &mapper)
445 {
446     get_bound_rect();
447     GRect rect;
448     rect.xmin = xmin;
449     rect.xmax = xmax;
450     rect.ymin = ymin;
451     rect.ymax = ymax;
452     mapper.unmap(rect);
453     xmin = rect.xmin;
454     ymin = rect.ymin;
455     xmax = rect.xmax;
456     ymax = rect.ymax;
457     clear_bounds();
458 }
459 
460 /****************************************************************************
461 **************************** GMapPoly definition ****************************
462 ****************************************************************************/
463 
464 inline int
sign(int x)465 GMapPoly::sign(int x) { return x<0 ? -1 : x>0 ? 1 : 0; }
466 
467 bool
does_side_cross_rect(const GRect & grect,int side)468 GMapPoly::does_side_cross_rect(const GRect & grect, int side)
469 {
470    int x1=xx[side], x2=xx[(side+1)%points];
471    int y1=yy[side], y2=yy[(side+1)%points];
472    int xmin=x1<x2 ? x1 : x2;
473    int ymin=y1<y2 ? y1 : y2;
474    int xmax=x1+x2-xmin;
475    int ymax=y1+y2-ymin;
476 
477    if (xmax<grect.xmin || xmin>grect.xmax ||
478        ymax<grect.ymin || ymin>grect.ymax)
479      return false;
480 
481    return
482      (x1>=grect.xmin && x1<=grect.xmax && y1>=grect.ymin && y1<=grect.ymax) ||
483      (x2>=grect.xmin && x2<=grect.xmax && y2>=grect.ymin && y2<=grect.ymax) ||
484      do_segments_intersect(grect.xmin, grect.ymin, grect.xmax, grect.ymax,
485 			   x1, y1, x2, y2) ||
486      do_segments_intersect(grect.xmax, grect.ymin, grect.xmin, grect.ymax,
487 			   x1, y1, x2, y2);
488 }
489 
490 bool
is_projection_on_segment(int x,int y,int x1,int y1,int x2,int y2)491 GMapPoly::is_projection_on_segment(int x, int y, int x1, int y1, int x2, int y2)
492 {
493    int res1=(x-x1)*(x2-x1)+(y-y1)*(y2-y1);
494    int res2=(x-x2)*(x2-x1)+(y-y2)*(y2-y1);
495    return sign(res1)*sign(res2)<=0;
496 }
497 
498 bool
do_segments_intersect(int x11,int y11,int x12,int y12,int x21,int y21,int x22,int y22)499 GMapPoly::do_segments_intersect(int x11, int y11, int x12, int y12,
500 				int x21, int y21, int x22, int y22)
501 {
502    int res11=(x11-x21)*(y22-y21)-(y11-y21)*(x22-x21);
503    int res12=(x12-x21)*(y22-y21)-(y12-y21)*(x22-x21);
504    int res21=(x21-x11)*(y12-y11)-(y21-y11)*(x12-x11);
505    int res22=(x22-x11)*(y12-y11)-(y22-y11)*(x12-x11);
506    if (!res11 && !res12)
507    {
508       // Segments are on the same line
509       return
510 	 is_projection_on_segment(x11, y11, x21, y21, x22, y22) ||
511 	 is_projection_on_segment(x12, y12, x21, y21, x22, y22) ||
512 	 is_projection_on_segment(x21, y21, x11, y11, x12, y12) ||
513 	 is_projection_on_segment(x22, y22, x11, y11, x12, y12);
514    }
515    int sign1=sign(res11)*sign(res12);
516    int sign2=sign(res21)*sign(res22);
517    return sign1<=0 && sign2<=0;
518 }
519 
520 bool
are_segments_parallel(int x11,int y11,int x12,int y12,int x21,int y21,int x22,int y22)521 GMapPoly::are_segments_parallel(int x11, int y11, int x12, int y12,
522 				int x21, int y21, int x22, int y22)
523 {
524    return (x12-x11)*(y22-y21)-(y12-y11)*(x22-x21)==0;
525 }
526 
527 char const * const
check_data(void)528 GMapPoly::check_data(void)
529 {
530   if ((open && points<2) || (!open && points<3))
531     return error_too_few_points;
532   for(int i=0;i<sides;i++)
533     {
534       for(int j=i+2;j<sides;j++)
535 	{
536 	  if (i != (j+1)%points )
537 	    if (do_segments_intersect(xx[i], yy[i], xx[i+1], yy[i+1],
538 				      xx[j], yy[j], xx[(j+1)%points], yy[(j+1)%points]))
539 	      return error_intersect;
540 	}
541     }
542   return "";
543 }
544 
545 void
optimize_data(void)546 GMapPoly::optimize_data(void)
547 {
548   // Removing segments of length zero
549   int i;
550   for(i=0;i<sides;i++)
551     {
552       while(xx[i]==xx[(i+1)%points] && yy[i]==yy[(i+1)%points])
553 	{
554 	  for(int k=(i+1)%points;k<points-1;k++)
555 	    {
556 	      xx[k]=xx[k+1]; yy[k]=yy[k+1];
557 	    }
558 	  points--; sides--;
559 	  if (!points) return;
560 	}
561     }
562   // Concatenating consequitive parallel segments
563   for(i=0;i<sides;i++)
564     {
565       while(((open && i+1<sides) || !open) &&
566 	    are_segments_parallel(xx[i], yy[i],
567 				  xx[(i+1)%points], yy[(i+1)%points],
568 				  xx[(i+1)%points], yy[(i+1)%points],
569 				  xx[(i+2)%points], yy[(i+2)%points]))
570 	{
571 	  for(int k=(i+1)%points;k<points-1;k++)
572 	    {
573 	      xx[k]=xx[k+1]; yy[k]=yy[k+1];
574 	    }
575 	  points--; sides--;
576 	  if (!points) return;
577 	}
578     }
579 }
580 
581 bool
gma_is_point_inside(const int xin,const int yin) const582 GMapPoly::gma_is_point_inside(const int xin, const int yin) const
583 {
584    if (open)
585      return false;
586 
587    int xfar=get_xmax()+(get_xmax()-get_xmin());
588 
589    int intersections=0;
590    for(int i=0;i<points;i++)
591    {
592       int res1=yy[i]-yin;
593       if (!res1) continue;
594       int res2, isaved=i;
595       while(!(res2=yy[(i+1)%points]-yin)) i++;
596       if (isaved!=i)
597       {
598 	 // Some points fell exactly on the line
599 	 if ((xx[(isaved+1)%points]-xin)*
600 	     (xx[i%points]-xin)<=0)
601 	 {
602 	    // Test point is exactly on the boundary
603 	    return true;
604 	 }
605       }
606       if ((res1<0 && res2>0) || (res1>0 && res2<0))
607       {
608 	 int x1=xx[i%points], y1=yy[i%points];
609 	 int x2=xx[(i+1)%points], y2=yy[(i+1)%points];
610 	 int _res1=(xin-x1)*(y2-y1)-(yin-y1)*(x2-x1);
611 	 int _res2=(xfar-x1)*(y2-y1)-(yin-y1)*(x2-x1);
612 	 if (!_res1 || !_res2)
613 	 {
614 	    // The point is on this boundary
615 	    return true;
616 	 }
617 	 if (sign(_res1)*sign(_res2)<0) intersections++;
618       }
619    }
620    return (intersections % 2)!=0;
621 }
622 
623 int
gma_get_xmin(void) const624 GMapPoly::gma_get_xmin(void) const
625 {
626    int x=xx[0];
627    for(int i=1;i<points;i++)
628       if (x>xx[i]) x=xx[i];
629    return x;
630 }
631 
632 int
gma_get_xmax(void) const633 GMapPoly::gma_get_xmax(void) const
634 {
635    int x=xx[0];
636    for(int i=1;i<points;i++)
637       if (x<xx[i]) x=xx[i];
638    return x+1;
639 }
640 
641 int
gma_get_ymin(void) const642 GMapPoly::gma_get_ymin(void) const
643 {
644    int y=yy[0];
645    for(int i=1;i<points;i++)
646       if (y>yy[i]) y=yy[i];
647    return y;
648 }
649 
650 int
gma_get_ymax(void) const651 GMapPoly::gma_get_ymax(void) const
652 {
653    int y=yy[0];
654    for(int i=1;i<points;i++)
655       if (y<yy[i]) y=yy[i];
656    return y+1;
657 }
658 
659 void
gma_move(int dx,int dy)660 GMapPoly::gma_move(int dx, int dy)
661 {
662    for(int i=0;i<points;i++)
663    {
664       xx[i]+=dx; yy[i]+=dy;
665    }
666 }
667 
668 void
gma_resize(int new_width,int new_height)669 GMapPoly::gma_resize(int new_width, int new_height)
670 {
671    int width=get_xmax()-get_xmin();
672    int height=get_ymax()-get_ymin();
673    int xmin=get_xmin(), ymin=get_ymin();
674    for(int i=0;i<points;i++)
675    {
676       xx[i]=xmin+(xx[i]-xmin)*new_width/width;
677       yy[i]=ymin+(yy[i]-ymin)*new_height/height;
678    }
679 }
680 
681 void
gma_transform(const GRect & grect)682 GMapPoly::gma_transform(const GRect & grect)
683 {
684    int width=get_xmax()-get_xmin();
685    int height=get_ymax()-get_ymin();
686    int xmin=get_xmin(), ymin=get_ymin();
687    for(int i=0;i<points;i++)
688    {
689       xx[i]=grect.xmin+(xx[i]-xmin)*grect.width()/width;
690       yy[i]=grect.ymin+(yy[i]-ymin)*grect.height()/height;
691    }
692 }
693 
694 char const * const
gma_check_object(void) const695 GMapPoly::gma_check_object(void) const
696 {
697    const char * str;
698    str=(border_type!=NO_BORDER &&
699         border_type!=SOLID_BORDER &&
700         border_type!=XOR_BORDER) ? error_poly_border:
701        ((hilite_color!=0xffffffff) ? error_poly_hilite:"");
702    return str;
703 }
704 
GMapPoly(const int * _xx,const int * _yy,int _points,bool _open)705 GMapPoly::GMapPoly(const int * _xx, const int * _yy, int _points, bool _open) :
706    open(_open), points(_points)
707 {
708    sides=points-(open!=0);
709 
710    xx.resize(points-1); yy.resize(points-1);
711    for(int i=0;i<points;i++)
712    {
713       xx[i]=_xx[i]; yy[i]=_yy[i];
714    }
715    optimize_data();
716    char const * const res=check_data();
717    if (res[0])
718      G_THROW(res);
719 }
720 
721 int
add_vertex(int x,int y)722 GMapPoly::add_vertex(int x, int y)
723 {
724     points++;
725     sides=points-(open!=0);
726 
727     xx.resize(points-1); yy.resize(points-1);
728     xx[points-1] = x;
729     yy[points-1] = y;
730 
731     return points;
732 }
733 
734 void
close_poly()735 GMapPoly::close_poly()
736 {
737     open = false;
738     sides=points;
739 }
740 
741 GUTF8String
gma_print(void)742 GMapPoly::gma_print(void)
743 {
744    static const GUTF8String space(' ');
745    GUTF8String res=GUTF8String('(')+POLY_TAG+space;
746    for(int i=0;i<points;i++)
747    {
748       GUTF8String buffer;
749       res+=buffer.format("%d %d ", xx[i], yy[i]);
750    }
751    res.setat(res.length()-1, ')');
752    res+=space;
753    return res;
754 }
755 
756 /// Virtual function generating a list of defining coordinates
get_coords(GList<int> & CoordList) const757 void GMapPoly::get_coords( GList<int> & CoordList ) const
758 {
759   for(int i = 0 ; i < points ; i++)
760   {
761     CoordList.append( xx[i] );
762     CoordList.append( yy[i] );
763   }
764 }
765 
766 void
map(GRectMapper & mapper)767 GMapPoly::map(GRectMapper &mapper)
768 {
769     get_bound_rect();
770     for(int i=0; i<points; i++)
771     {
772         mapper.map(xx[i], yy[i]);
773     }
774     clear_bounds();
775 }
776 
777 void
unmap(GRectMapper & mapper)778 GMapPoly::unmap(GRectMapper &mapper)
779 {
780     get_bound_rect();
781     for(int i=0; i<points; i++)
782     {
783         mapper.unmap(xx[i], yy[i]);
784     }
785     clear_bounds();
786 }
787 
788 
789 
790 /****************************************************************************
791 **************************** GMapOval definition ****************************
792 ****************************************************************************/
793 
794 void
gma_resize(int new_width,int new_height)795 GMapOval::gma_resize(int new_width, int new_height)
796 {
797    xmax=xmin+new_width;
798    ymax=ymin+new_height;
799    initialize();
800 }
801 
802 void
gma_transform(const GRect & grect)803 GMapOval::gma_transform(const GRect & grect)
804 {
805    xmin=grect.xmin; ymin=grect.ymin;
806    xmax=grect.xmax; ymax=grect.ymax;
807    initialize();
808 }
809 
810 bool
gma_is_point_inside(const int x,const int y) const811 GMapOval::gma_is_point_inside(const int x, const int y) const
812 {
813    return
814       sqrt((double)((x-xf1)*(x-xf1)+(y-yf1)*(y-yf1))) +
815       sqrt((double)((x-xf2)*(x-xf2)+(y-yf2)*(y-yf2))) <= 2*rmax;
816 }
817 
818 char const * const
gma_check_object(void) const819 GMapOval::gma_check_object(void) const
820 {
821    return (border_type!=NO_BORDER &&
822        border_type!=SOLID_BORDER &&
823        border_type!=XOR_BORDER)?error_oval_border:
824       ((hilite_color!=0xffffffff) ? error_oval_hilite:"");
825 }
826 
827 void
initialize(void)828 GMapOval::initialize(void)
829 {
830    int xc=(xmax+xmin)/2;
831    int yc=(ymax+ymin)/2;
832    int f;
833 
834    a=(xmax-xmin)/2;
835    b=(ymax-ymin)/2;
836    if (a>b)
837    {
838       rmin=b; rmax=a;
839       f=(int) sqrt((double)(rmax*rmax-rmin*rmin));
840       xf1=xc+f; xf2=xc-f; yf1=yf2=yc;
841    } else
842    {
843       rmin=a; rmax=b;
844       f=(int) sqrt((double)(rmax*rmax-rmin*rmin));
845       yf1=yc+f; yf2=yc-f; xf1=xf2=xc;
846    }
847 }
848 
GMapOval(const GRect & rect)849 GMapOval::GMapOval(const GRect & rect) : xmin(rect.xmin), ymin(rect.ymin),
850    xmax(rect.xmax), ymax(rect.ymax)
851 {
852    initialize();
853 }
854 
855 GUTF8String
gma_print(void)856 GMapOval::gma_print(void)
857 {
858    GUTF8String buffer;
859    return buffer.format("(%s %d %d %d %d) ",
860 	   OVAL_TAG, xmin, ymin, xmax-xmin, ymax-ymin);
861 }
862 
863 void
map(GRectMapper & mapper)864 GMapOval::map(GRectMapper &mapper)
865 {
866     get_bound_rect();
867     GRect rect;
868     rect.xmin = xmin;
869     rect.xmax = xmax;
870     rect.ymin = ymin;
871     rect.ymax = ymax;
872     mapper.map(rect);
873     xmin = rect.xmin;
874     ymin = rect.ymin;
875     xmax = rect.xmax;
876     ymax = rect.ymax;
877     clear_bounds();
878     initialize();
879 }
880 
881 void
unmap(GRectMapper & mapper)882 GMapOval::unmap(GRectMapper &mapper)
883 {
884     get_bound_rect();
885     GRect rect;
886     rect.xmin = xmin;
887     rect.xmax = xmax;
888     rect.ymin = ymin;
889     rect.ymax = ymax;
890     mapper.unmap(rect);
891     xmin = rect.xmin;
892     ymin = rect.ymin;
893     xmax = rect.xmax;
894     ymax = rect.ymax;
895     clear_bounds();
896     initialize();
897 }
898 
GMapArea(void)899 GMapArea::GMapArea(void) : target("_self"), border_type(NO_BORDER),
900    border_always_visible(false), border_color(0xff), border_width(1),
901    hilite_color(0xffffffff), bounds_initialized(0) {}
902 
GMapRect(void)903 GMapRect::GMapRect(void) : xmin(0), ymin(0), xmax(0), ymax(0) {}
904 
GMapRect(const GRect & rect)905 GMapRect::GMapRect(const GRect & rect) : xmin(rect.xmin), ymin(rect.ymin),
906    xmax(rect.xmax), ymax(rect.ymax) {}
907 
908 GMapRect &
operator =(const GRect & rect)909 GMapRect::operator=(const GRect & rect)
910 {
911    xmin=rect.xmin;
912    xmax=rect.xmax;
913    ymin=rect.ymin;
914    ymax=rect.ymax;
915    return *this;
916 }
917 
918 void
gma_move(int dx,int dy)919 GMapRect::gma_move(int dx, int dy)
920 {
921    xmin+=dx;
922    xmax+=dx;
923    ymin+=dy;
924    ymax+=dy;
925 }
926 
927 bool
gma_is_point_inside(const int x,const int y) const928 GMapRect::gma_is_point_inside(const int x, const int y) const
929 {
930    return (x>=xmin)&&(x<xmax)&&(y>=ymin)&&(y<ymax);
931 }
932 
933 GP<GMapArea>
get_copy(void) const934 GMapRect::get_copy(void) const { return new GMapRect(*this); }
935 
GMapPoly(void)936 GMapPoly::GMapPoly(void) : points(0), sides(0) {}
937 
938 void
move_vertex(int i,int x,int y)939 GMapPoly::move_vertex(int i, int x, int y)
940 {
941    xx[i]=x; yy[i]=y;
942    clear_bounds();
943 }
944 
945 GP<GMapArea>
get_copy(void) const946 GMapPoly::get_copy(void) const { return new GMapPoly(*this); }
947 
GMapOval(void)948 GMapOval::GMapOval(void) : xmin(0), ymin(0), xmax(0), ymax(0) {}
949 
950 void
gma_move(int dx,int dy)951 GMapOval::gma_move(int dx, int dy)
952 {
953    xmin+=dx; xmax+=dx; ymin+=dy; ymax+=dy;
954    xf1+=dx; yf1+=dy; xf2+=dx; yf2+=dy;
955 }
956 
957 GP<GMapArea>
get_copy(void) const958 GMapOval::get_copy(void) const
959 {
960   return new GMapOval(*this);
961 }
962 
963 static GUTF8String
GMapArea2xmltag(const GMapArea & area,const GUTF8String & coords)964 GMapArea2xmltag(const GMapArea &area,const GUTF8String &coords)
965 {
966   GUTF8String retval("<AREA coords=\""
967     +coords+"\" shape=\""+area.get_shape_name()+"\" "
968     +"alt=\""+area.comment.toEscaped()+"\" ");
969   if(area.url.length())
970   {
971     retval+="href=\""+area.url+"\" ";
972   }else
973   {
974     retval+="nohref=\"nohref\" ";
975   }
976   if(area.target.length())
977   {
978     retval+="target=\""+area.target.toEscaped()+"\" ";
979   }
980   //  highlight
981   if( area.hilite_color != GMapArea::NO_HILITE &&
982       area.hilite_color != GMapArea::XOR_HILITE )
983   {
984     retval+=GUTF8String().format( "highlight=\"#%06X\" ", area.hilite_color );
985   }
986   const char *b_type="none";
987   switch( area.border_type )
988   {
989   case GMapArea::NO_BORDER:
990     b_type = "none";
991     break;
992   case GMapArea::XOR_BORDER:
993     b_type = "xor";
994     break;
995   case GMapArea::SOLID_BORDER:
996     b_type = "solid";
997     break;
998   case GMapArea::SHADOW_IN_BORDER:
999     b_type = "shadowin";
1000     break;
1001   case GMapArea::SHADOW_OUT_BORDER:
1002     b_type = "shadowout";
1003     break;
1004   case GMapArea::SHADOW_EIN_BORDER:
1005     b_type = "etchedin";
1006     break;
1007   case GMapArea::SHADOW_EOUT_BORDER:
1008     b_type = "etchedout";
1009     break;
1010   }
1011   retval=retval+"bordertype=\""+b_type+"\" ";
1012   if( area.border_type != GMapArea::NO_BORDER)
1013   {
1014     retval+="bordercolor=\""+GUTF8String().format("#%06X",area.border_color)
1015       +"\" border=\""+GUTF8String(area.border_width)+"\" ";
1016   }
1017   if(area.border_always_visible )
1018     retval=retval+"visible=\"visible\" ";
1019   return retval+"/>\n";
1020 }
1021 
1022 GUTF8String
get_xmltag(const int height) const1023 GMapRect::get_xmltag(const int height) const
1024 {
1025   return GMapArea2xmltag( *this, GUTF8String(get_xmin())
1026     +","+GUTF8String(height-1-get_ymax())
1027     +","+GUTF8String(get_xmax())
1028     +","+GUTF8String(height-1-get_ymin()));
1029 #if 0
1030   GUTF8String retval;
1031   return retval;
1032 #endif
1033 }
1034 
1035 GUTF8String
get_xmltag(const int height) const1036 GMapOval::get_xmltag(const int height) const
1037 {
1038   return GMapArea2xmltag( *this, GUTF8String(get_xmin())
1039     +","+GUTF8String(height-1-get_ymax())
1040     +","+GUTF8String(get_xmax())
1041     +","+GUTF8String(height-1-get_ymin()));
1042 #if 0
1043   GUTF8String retval;
1044   return retval;
1045 #endif
1046 }
1047 
1048 GUTF8String
get_xmltag(const int height) const1049 GMapPoly::get_xmltag(const int height) const
1050 {
1051   GList<int> CoordList;
1052   get_coords(CoordList);
1053   GPosition pos=CoordList;
1054   GUTF8String retval;
1055   if(pos)
1056   {
1057     GUTF8String coords(CoordList[pos]);
1058     while(++pos)
1059     {
1060       coords+=","+GUTF8String(height-1-CoordList[pos]);
1061       if(! ++pos)
1062         break;
1063       coords+=","+GUTF8String(CoordList[pos]);
1064     }
1065     retval=GMapArea2xmltag( *this, coords);
1066   }
1067   return retval;
1068 }
1069 
1070 
1071 #ifdef HAVE_NAMESPACES
1072 }
1073 # ifndef NOT_USING_DJVU_NAMESPACE
1074 using namespace DJVU;
1075 # endif
1076 #endif
1077 
1078