1/*
2%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3%                                                                             %
4%                                                                             %
5%                                                                             %
6%                        H   H  TTTTT  M   M  L                               %
7%                        H   H    T    MM MM  L                               %
8%                        HHHHH    T    M M M  L                               %
9%                        H   H    T    M   M  L                               %
10%                        H   H    T    M   M  LLLLL                           %
11%                                                                             %
12%                                                                             %
13%                  Write A Client-Side Image Map Using                        %
14%                 Image Montage & Directory Information.                      %
15%                                                                             %
16%                              Software Design                                %
17%                                   Cristy                                    %
18%                                 July 1992                                   %
19%                                                                             %
20%                                                                             %
21%  Copyright 1999-2016 ImageMagick Studio LLC, a non-profit organization      %
22%  dedicated to making software imaging solutions freely available.           %
23%                                                                             %
24%  You may not use this file except in compliance with the License.  You may  %
25%  obtain a copy of the License at                                            %
26%                                                                             %
27%    http://www.imagemagick.org/script/license.php                            %
28%                                                                             %
29%  Unless required by applicable law or agreed to in writing, software        %
30%  distributed under the License is distributed on an "AS IS" BASIS,          %
31%  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.   %
32%  See the License for the specific language governing permissions and        %
33%  limitations under the License.                                             %
34%                                                                             %
35%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
36%
37%
38*/
39
40/*
41  Include declarations.
42*/
43#include "MagickCore/studio.h"
44#include "MagickCore/blob.h"
45#include "MagickCore/blob-private.h"
46#include "MagickCore/color-private.h"
47#include "MagickCore/colorspace.h"
48#include "MagickCore/colorspace-private.h"
49#include "MagickCore/constitute.h"
50#include "MagickCore/exception.h"
51#include "MagickCore/exception-private.h"
52#include "MagickCore/geometry.h"
53#include "MagickCore/list.h"
54#include "MagickCore/magick.h"
55#include "MagickCore/memory_.h"
56#include "MagickCore/paint.h"
57#include "MagickCore/property.h"
58#include "MagickCore/quantum-private.h"
59#include "MagickCore/static.h"
60#include "MagickCore/string_.h"
61#include "MagickCore/module.h"
62#include "MagickCore/utility.h"
63
64/*
65  Forward declarations.
66*/
67static MagickBooleanType
68  WriteHTMLImage(const ImageInfo *,Image *,ExceptionInfo *);
69
70/*
71%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
72%                                                                             %
73%                                                                             %
74%                                                                             %
75%   I s H T M L                                                               %
76%                                                                             %
77%                                                                             %
78%                                                                             %
79%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
80%
81%  IsHTML() returns MagickTrue if the image format type, identified by the
82%  magick string, is HTML.
83%
84%  The format of the IsHTML method is:
85%
86%      MagickBooleanType IsHTML(const unsigned char *magick,const size_t length)
87%
88%  A description of each parameter follows:
89%
90%    o magick: compare image format pattern against these bytes.
91%
92%    o length: Specifies the length of the magick string.
93%
94*/
95static MagickBooleanType IsHTML(const unsigned char *magick,const size_t length)
96{
97  if (length < 5)
98    return(MagickFalse);
99  if (LocaleNCompare((char *) magick,"<html",5) == 0)
100    return(MagickTrue);
101  return(MagickFalse);
102}
103
104/*
105%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
106%                                                                             %
107%                                                                             %
108%                                                                             %
109%   R e g i s t e r H T M L I m a g e                                         %
110%                                                                             %
111%                                                                             %
112%                                                                             %
113%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
114%
115%  RegisterHTMLImage() adds properties for the HTML image format to
116%  the list of supported formats.  The properties include the image format
117%  tag, a method to read and/or write the format, whether the format
118%  supports the saving of more than one frame to the same file or blob,
119%  whether the format supports native in-memory I/O, and a brief
120%  description of the format.
121%
122%  The format of the RegisterHTMLImage method is:
123%
124%      size_t RegisterHTMLImage(void)
125%
126*/
127ModuleExport size_t RegisterHTMLImage(void)
128{
129  MagickInfo
130    *entry;
131
132  entry=AcquireMagickInfo("HTML","HTM",
133    "Hypertext Markup Language and a client-side image map");
134  entry->encoder=(EncodeImageHandler *) WriteHTMLImage;
135  entry->magick=(IsImageFormatHandler *) IsHTML;
136  entry->flags^=CoderAdjoinFlag;
137  (void) RegisterMagickInfo(entry);
138  entry=AcquireMagickInfo("HTML","HTML",
139    "Hypertext Markup Language and a client-side image map");
140  entry->encoder=(EncodeImageHandler *) WriteHTMLImage;
141  entry->magick=(IsImageFormatHandler *) IsHTML;
142  entry->flags^=CoderAdjoinFlag;
143  (void) RegisterMagickInfo(entry);
144  entry=AcquireMagickInfo("HTML","SHTML",
145    "Hypertext Markup Language and a client-side image map");
146  entry->encoder=(EncodeImageHandler *) WriteHTMLImage;
147  entry->magick=(IsImageFormatHandler *) IsHTML;
148  entry->flags^=CoderAdjoinFlag;
149  (void) RegisterMagickInfo(entry);
150  return(MagickImageCoderSignature);
151}
152
153/*
154%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
155%                                                                             %
156%                                                                             %
157%                                                                             %
158%   U n r e g i s t e r H T M L I m a g e                                     %
159%                                                                             %
160%                                                                             %
161%                                                                             %
162%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
163%
164%  UnregisterHTMLImage() removes format registrations made by the
165%  HTML module from the list of supported formats.
166%
167%  The format of the UnregisterHTMLImage method is:
168%
169%      UnregisterHTMLImage(void)
170%
171*/
172ModuleExport void UnregisterHTMLImage(void)
173{
174  (void) UnregisterMagickInfo("HTM");
175  (void) UnregisterMagickInfo("HTML");
176  (void) UnregisterMagickInfo("SHTML");
177}
178
179/*
180%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
181%                                                                             %
182%                                                                             %
183%                                                                             %
184%   W r i t e H T M L I m a g e                                               %
185%                                                                             %
186%                                                                             %
187%                                                                             %
188%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
189%
190%  WriteHTMLImage() writes an image in the HTML encoded image format.
191%
192%  The format of the WriteHTMLImage method is:
193%
194%      MagickBooleanType WriteHTMLImage(const ImageInfo *image_info,
195%        Image *image,ExceptionInfo *exception)
196%
197%  A description of each parameter follows.
198%
199%    o image_info: the image info.
200%
201%    o image:  The image.
202%
203%    o exception: return any errors or warnings in this structure.
204%
205*/
206static MagickBooleanType WriteHTMLImage(const ImageInfo *image_info,
207  Image *image,ExceptionInfo *exception)
208{
209  char
210    basename[MagickPathExtent],
211    buffer[MagickPathExtent],
212    filename[MagickPathExtent],
213    mapname[MagickPathExtent],
214    url[MagickPathExtent];
215
216  Image
217    *next;
218
219  ImageInfo
220    *write_info;
221
222  MagickBooleanType
223    status;
224
225  RectangleInfo
226    geometry;
227
228  register char
229    *p;
230
231  /*
232    Open image.
233  */
234  assert(image_info != (const ImageInfo *) NULL);
235  assert(image_info->signature == MagickCoreSignature);
236  assert(image != (Image *) NULL);
237  assert(image->signature == MagickCoreSignature);
238  if (image->debug != MagickFalse)
239    (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",
240      image_info->filename);
241  assert(exception != (ExceptionInfo *) NULL);
242  assert(exception->signature == MagickCoreSignature);
243  status=OpenBlob(image_info,image,WriteBinaryBlobMode,exception);
244  if (status == MagickFalse)
245    return(status);
246  (void) CloseBlob(image);
247  (void) TransformImageColorspace(image,sRGBColorspace,exception);
248  *url='\0';
249  if ((LocaleCompare(image_info->magick,"FTP") == 0) ||
250      (LocaleCompare(image_info->magick,"HTTP") == 0))
251    {
252      /*
253        Extract URL base from filename.
254      */
255      p=strrchr(image->filename,'/');
256      if (p != (char *) NULL)
257        {
258          p++;
259          (void) CopyMagickString(url,image_info->magick,MagickPathExtent);
260          (void) ConcatenateMagickString(url,":",MagickPathExtent);
261          url[strlen(url)+p-image->filename]='\0';
262          (void) ConcatenateMagickString(url,image->filename,
263            p-image->filename+2);
264          (void) CopyMagickString(image->filename,p,MagickPathExtent);
265        }
266    }
267  /*
268    Refer to image map file.
269  */
270  (void) CopyMagickString(filename,image->filename,MagickPathExtent);
271  AppendImageFormat("map",filename);
272  GetPathComponent(filename,BasePath,basename);
273  (void) CopyMagickString(mapname,basename,MagickPathExtent);
274  (void) CopyMagickString(image->filename,image_info->filename,MagickPathExtent);
275  (void) CopyMagickString(filename,image->filename,MagickPathExtent);
276  write_info=CloneImageInfo(image_info);
277  *write_info->magick='\0';
278  write_info->adjoin=MagickTrue;
279  status=MagickTrue;
280  if (LocaleCompare(image_info->magick,"SHTML") != 0)
281    {
282      const char
283        *value;
284
285      /*
286        Open output image file.
287      */
288      assert(exception != (ExceptionInfo *) NULL);
289      status=OpenBlob(image_info,image,WriteBinaryBlobMode,exception);
290      if (status == MagickFalse)
291        return(status);
292      /*
293        Write the HTML image file.
294      */
295      (void) WriteBlobString(image,"<?xml version=\"1.0\" "
296        "encoding=\"US-ASCII\"?>\n");
297      (void) WriteBlobString(image,"<!DOCTYPE html PUBLIC \"-//W3C//DTD XHTML "
298        "1.0 Strict//EN\" "
299        "\"http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd\">\n");
300      (void) WriteBlobString(image,"<html>\n");
301      (void) WriteBlobString(image,"<head>\n");
302      value=GetImageProperty(image,"label",exception);
303      if (value != (const char *) NULL)
304        (void) FormatLocaleString(buffer,MagickPathExtent,"<title>%s</title>\n",
305          value);
306      else
307        {
308          GetPathComponent(filename,BasePath,basename);
309          (void) FormatLocaleString(buffer,MagickPathExtent,
310            "<title>%s</title>\n",basename);
311        }
312      (void) WriteBlobString(image,buffer);
313      (void) WriteBlobString(image,"</head>\n");
314      (void) WriteBlobString(image,"<body style=\"text-align: center;\">\n");
315      (void) FormatLocaleString(buffer,MagickPathExtent,"<h1>%s</h1>\n",
316        image->filename);
317      (void) WriteBlobString(image,buffer);
318      (void) WriteBlobString(image,"<div>\n");
319      (void) CopyMagickString(filename,image->filename,MagickPathExtent);
320      AppendImageFormat("png",filename);
321      (void) FormatLocaleString(buffer,MagickPathExtent,"<img usemap=\"#%s\" "
322        "src=\"%s\" style=\"border: 0;\" alt=\"Image map\" />\n",mapname,
323        filename);
324      (void) WriteBlobString(image,buffer);
325      /*
326        Determine the size and location of each image tile.
327      */
328      SetGeometry(image,&geometry);
329      if (image->montage != (char *) NULL)
330        (void) ParseAbsoluteGeometry(image->montage,&geometry);
331      /*
332        Write an image map.
333      */
334      (void) FormatLocaleString(buffer,MagickPathExtent,
335        "<map id=\"%s\" name=\"%s\">\n",mapname,mapname);
336      (void) WriteBlobString(image,buffer);
337      (void) FormatLocaleString(buffer,MagickPathExtent,"  <area href=\"%s",url);
338      (void) WriteBlobString(image,buffer);
339      if (image->directory == (char *) NULL)
340        {
341          (void) FormatLocaleString(buffer,MagickPathExtent,
342            "%s\" shape=\"rect\" coords=\"0,0,%.20g,%.20g\" alt=\"\" />\n",
343            image->filename,(double) geometry.width-1,(double) geometry.height-
344            1);
345          (void) WriteBlobString(image,buffer);
346        }
347      else
348        for (p=image->directory; *p != '\0'; p++)
349          if (*p != '\n')
350            (void) WriteBlobByte(image,(unsigned char) *p);
351          else
352            {
353              (void) FormatLocaleString(buffer,MagickPathExtent,"\" shape="
354                "\"rect\" coords=\"%.20g,%.20g,%.20g,%.20g\" alt=\"\" />\n",
355                (double) geometry.x,(double) geometry.y,(double) (geometry.x+
356                geometry.width-1),(double) (geometry.y+geometry.height-1));
357              (void) WriteBlobString(image,buffer);
358              if (*(p+1) != '\0')
359                {
360                  (void) FormatLocaleString(buffer,MagickPathExtent,
361                    "  <area href=%s\"",url);
362                  (void) WriteBlobString(image,buffer);
363                }
364              geometry.x+=(ssize_t) geometry.width;
365              if ((geometry.x+4) >= (ssize_t) image->columns)
366                {
367                  geometry.x=0;
368                  geometry.y+=(ssize_t) geometry.height;
369                }
370            }
371      (void) WriteBlobString(image,"</map>\n");
372      (void) CopyMagickString(filename,image->filename,MagickPathExtent);
373      (void) WriteBlobString(image,"</div>\n");
374      (void) WriteBlobString(image,"</body>\n");
375      (void) WriteBlobString(image,"</html>\n");
376      (void) CloseBlob(image);
377      /*
378        Write the image as PNG.
379      */
380      (void) CopyMagickString(image->filename,filename,MagickPathExtent);
381      AppendImageFormat("png",image->filename);
382      next=GetNextImageInList(image);
383      image->next=NewImageList();
384      (void) CopyMagickString(image->magick,"PNG",MagickPathExtent);
385      (void) WriteImage(write_info,image,exception);
386      image->next=next;
387      /*
388        Determine image map filename.
389      */
390      GetPathComponent(image->filename,BasePath,filename);
391      (void) ConcatenateMagickString(filename,"_map.shtml",MagickPathExtent);
392      (void) CopyMagickString(image->filename,filename,MagickPathExtent);
393    }
394  /*
395    Open image map.
396  */
397  status=OpenBlob(write_info,image,WriteBinaryBlobMode,exception);
398  if (status == MagickFalse)
399    return(status);
400  write_info=DestroyImageInfo(write_info);
401  /*
402    Determine the size and location of each image tile.
403  */
404  SetGeometry(image,&geometry);
405  if (image->montage != (char *) NULL)
406    (void) ParseAbsoluteGeometry(image->montage,&geometry);
407  /*
408    Write an image map.
409  */
410  (void) FormatLocaleString(buffer,MagickPathExtent,
411    "<map id=\"%s\" name=\"%s\">\n",mapname,mapname);
412  (void) WriteBlobString(image,buffer);
413  (void) FormatLocaleString(buffer,MagickPathExtent,"  <area href=\"%s",url);
414  (void) WriteBlobString(image,buffer);
415  if (image->directory == (char *) NULL)
416    {
417      (void) FormatLocaleString(buffer,MagickPathExtent,
418        "%s\" shape=\"rect\" coords=\"0,0,%.20g,%.20g\" alt=\"\" />\n",
419        image->filename,(double) geometry.width-1,(double) geometry.height-1);
420      (void) WriteBlobString(image,buffer);
421    }
422  else
423    for (p=image->directory; *p != '\0'; p++)
424      if (*p != '\n')
425        (void) WriteBlobByte(image,(unsigned char) *p);
426      else
427        {
428          (void) FormatLocaleString(buffer,MagickPathExtent,"\" shape=\"rect\""
429            " coords=\"%.20g,%.20g,%.20g,%.20g\" alt=\"\" />\n",
430            (double) geometry.x,(double) geometry.y,geometry.x+(double)
431            geometry.width-1,geometry.y+(double) geometry.height-1);
432          (void) WriteBlobString(image,buffer);
433          if (*(p+1) != '\0')
434            {
435              (void) FormatLocaleString(buffer,MagickPathExtent,
436                "  <area href=%s\"",url);
437              (void) WriteBlobString(image,buffer);
438            }
439          geometry.x+=(ssize_t) geometry.width;
440          if ((geometry.x+4) >= (ssize_t) image->columns)
441            {
442              geometry.x=0;
443              geometry.y+=(ssize_t) geometry.height;
444            }
445        }
446  (void) WriteBlobString(image,"</map>\n");
447  (void) CloseBlob(image);
448  (void) CopyMagickString(image->filename,filename,MagickPathExtent);
449  return(status);
450}
451