1/*
2%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3%                                                                             %
4%                                                                             %
5%               BBBB   RRRR    AAA   IIIII  L      L      EEEEE               %
6%               B   B  R   R  A   A    I    L      L      E                   %
7%               BBBB   RRRR   AAAAA    I    L      L      EEE                 %
8%               B   B  R R    A   A    I    L      L      E                   %
9%               BBBB   R  R   A   A  IIIII  LLLLL  LLLLL  EEEEE               %
10%                                                                             %
11%                                                                             %
12%                          Read/Write Braille Format                          %
13%                                                                             %
14%                               Samuel Thibault                               %
15%                                February 2008                                %
16%                                                                             %
17%                                                                             %
18%  Copyright 1999-2016 ImageMagick Studio LLC, a non-profit organization      %
19%  dedicated to making software imaging solutions freely available.           %
20%                                                                             %
21%  You may not use this file except in compliance with the License.  You may  %
22%  obtain a copy of the License at                                            %
23%                                                                             %
24%    http://www.imagemagick.org/script/license.php                            %
25%                                                                             %
26%  Unless required by applicable law or agreed to in writing, software        %
27%  distributed under the License is distributed on an "AS IS" BASIS,          %
28%  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.   %
29%  See the License for the specific language governing permissions and        %
30%  limitations under the License.                                             %
31%                                                                             %
32%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
33%
34%
35*/
36
37/*
38  Include declarations.
39*/
40#include "MagickCore/studio.h"
41#include "MagickCore/attribute.h"
42#include "MagickCore/blob.h"
43#include "MagickCore/blob-private.h"
44#include "MagickCore/cache.h"
45#include "MagickCore/color-private.h"
46#include "MagickCore/colorspace.h"
47#include "MagickCore/constitute.h"
48#include "MagickCore/exception.h"
49#include "MagickCore/exception-private.h"
50#include "MagickCore/image.h"
51#include "MagickCore/image-private.h"
52#include "MagickCore/list.h"
53#include "MagickCore/magick.h"
54#include "MagickCore/memory_.h"
55#include "MagickCore/module.h"
56#include "MagickCore/monitor.h"
57#include "MagickCore/monitor-private.h"
58#include "MagickCore/pixel-accessor.h"
59#include "MagickCore/property.h"
60#include "MagickCore/quantize.h"
61#include "MagickCore/static.h"
62#include "MagickCore/string_.h"
63#include "MagickCore/utility.h"
64
65/*
66  Forward declarations.
67*/
68static MagickBooleanType
69  WriteBRAILLEImage(const ImageInfo *,Image *,ExceptionInfo *);
70
71/*
72%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
73%                                                                             %
74%                                                                             %
75%                                                                             %
76%   R e g i s t e r B R A I L L E I m a g e                                   %
77%                                                                             %
78%                                                                             %
79%                                                                             %
80%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
81%
82%  RegisterBRAILLEImage() adds values for the Braille format to
83%  the list of supported formats.  The values include the image format
84%  tag, a method to read and/or write the format, whether the format
85%  supports the saving of more than one frame to the same file or blob,
86%  whether the format supports native in-memory I/O, and a brief
87%  description of the format.
88%
89%  The format of the RegisterBRAILLEImage method is:
90%
91%      size_t RegisterBRAILLEImage(void)
92%
93*/
94ModuleExport size_t RegisterBRAILLEImage(void)
95{
96  MagickInfo
97    *entry;
98
99  entry=AcquireMagickInfo("BRAILLE","BRF","BRF ASCII Braille format");
100  entry->encoder=(EncodeImageHandler *) WriteBRAILLEImage;
101  entry->flags^=CoderAdjoinFlag;
102  (void) RegisterMagickInfo(entry);
103  entry=AcquireMagickInfo("BRAILLE","UBRL","Unicode Text format");
104  entry->encoder=(EncodeImageHandler *) WriteBRAILLEImage;
105  entry->flags^=CoderAdjoinFlag;
106  (void) RegisterMagickInfo(entry);
107  entry=AcquireMagickInfo("BRAILLE","UBRL6","Unicode Text format 6dot");
108  entry->encoder=(EncodeImageHandler *) WriteBRAILLEImage;
109  entry->flags^=CoderAdjoinFlag;
110  (void) RegisterMagickInfo(entry);
111  entry=AcquireMagickInfo("BRAILLE","ISOBRL","ISO/TR 11548-1 format");
112  entry->encoder=(EncodeImageHandler *) WriteBRAILLEImage;
113  entry->flags^=CoderAdjoinFlag;
114  (void) RegisterMagickInfo(entry);
115  entry=AcquireMagickInfo("BRAILLE","ISOBRL6","ISO/TR 11548-1 format 6dot");
116  entry->encoder=(EncodeImageHandler *) WriteBRAILLEImage;
117  entry->flags^=CoderAdjoinFlag;
118  (void) RegisterMagickInfo(entry);
119  return(MagickImageCoderSignature);
120}
121
122/*
123%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
124%                                                                             %
125%                                                                             %
126%                                                                             %
127%   U n r e g i s t e r B R A I L L E I m a g e                               %
128%                                                                             %
129%                                                                             %
130%                                                                             %
131%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
132%
133%  UnregisterBRAILLEImage() removes format registrations made by the
134%  BRAILLE module from the list of supported formats.
135%
136%  The format of the UnregisterBRAILLEImage method is:
137%
138%      UnregisterBRAILLEImage(void)
139%
140*/
141ModuleExport void UnregisterBRAILLEImage(void)
142{
143  (void) UnregisterMagickInfo("BRF");
144  (void) UnregisterMagickInfo("UBRL");
145  (void) UnregisterMagickInfo("UBRL6");
146  (void) UnregisterMagickInfo("ISOBRL");
147  (void) UnregisterMagickInfo("ISOBRL6");
148}
149
150/*
151%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
152%                                                                             %
153%                                                                             %
154%                                                                             %
155%   W r i t e B R A I L L E I m a g e                                         %
156%                                                                             %
157%                                                                             %
158%                                                                             %
159%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
160%
161%  WriteBRAILLEImage() writes an image to a file in the Braille format.
162%
163%  The format of the WriteBRAILLEImage method is:
164%
165%      MagickBooleanType WriteBRAILLEImage(const ImageInfo *image_info,
166%        Image *image,ExceptionInfo *exception)
167%
168%  A description of each parameter follows.
169%
170%    o image_info: The image info.
171%
172%    o image:  The image.
173%
174%    o exception: return any errors or warnings in this structure.
175%
176*/
177static MagickBooleanType WriteBRAILLEImage(const ImageInfo *image_info,
178  Image *image,ExceptionInfo *exception)
179{
180  char
181    buffer[MagickPathExtent];
182
183  const char
184    *value;
185
186  int
187    unicode = 0,
188    iso_11548_1 = 0;
189
190  MagickBooleanType
191    status;
192
193  Quantum
194    polarity;
195
196  register const Quantum
197    *p;
198
199  register ssize_t
200    x;
201
202  size_t
203    cell_height = 4;
204
205  ssize_t
206    y;
207
208  /*
209    Open output image file.
210  */
211  assert(image_info != (const ImageInfo *) NULL);
212  assert(image_info->signature == MagickCoreSignature);
213  assert(image != (Image *) NULL);
214  assert(image->signature == MagickCoreSignature);
215  if (LocaleCompare(image_info->magick, "UBRL") == 0)
216    unicode=1;
217  else if (LocaleCompare(image_info->magick, "UBRL6") == 0)
218    {
219      unicode=1;
220      cell_height=3;
221    }
222  else if (LocaleCompare(image_info->magick, "ISOBRL") == 0)
223    iso_11548_1=1;
224  else if (LocaleCompare(image_info->magick, "ISOBRL6") == 0)
225    {
226      iso_11548_1=1;
227      cell_height=3;
228    }
229  else
230    cell_height=3;
231  if (image->debug != MagickFalse)
232    (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
233  assert(exception != (ExceptionInfo *) NULL);
234  assert(exception->signature == MagickCoreSignature);
235  status=OpenBlob(image_info,image,WriteBinaryBlobMode,exception);
236  if (status == MagickFalse)
237    return(status);
238  if (!iso_11548_1)
239    {
240      value=GetImageProperty(image,"label",exception);
241      if (value != (const char *) NULL)
242        {
243          (void) FormatLocaleString(buffer,MagickPathExtent,"Title: %s\n", value);
244          (void) WriteBlobString(image,buffer);
245        }
246      if (image->page.x != 0)
247        {
248          (void) FormatLocaleString(buffer,MagickPathExtent,"X: %.20g\n",(double)
249            image->page.x);
250          (void) WriteBlobString(image,buffer);
251        }
252      if (image->page.y != 0)
253        {
254          (void) FormatLocaleString(buffer,MagickPathExtent,"Y: %.20g\n",(double)
255            image->page.y);
256          (void) WriteBlobString(image,buffer);
257        }
258      (void) FormatLocaleString(buffer,MagickPathExtent,"Width: %.20g\n",(double)
259        (image->columns+(image->columns % 2)));
260      (void) WriteBlobString(image,buffer);
261      (void) FormatLocaleString(buffer,MagickPathExtent,"Height: %.20g\n",(double)
262        image->rows);
263      (void) WriteBlobString(image,buffer);
264      (void) WriteBlobString(image,"\n");
265    }
266  (void) SetImageType(image,BilevelType,exception);
267  polarity = 0;
268  if (image->storage_class == PseudoClass) {
269    polarity=(Quantum) (GetPixelInfoIntensity(image,&image->colormap[0]) >=
270      (QuantumRange/2.0));
271    if (image->colors == 2)
272      polarity=(Quantum) (GetPixelInfoIntensity(image,&image->colormap[0]) >=
273        GetPixelInfoIntensity(image,&image->colormap[1]));
274  }
275  for (y=0; y < (ssize_t) image->rows; y+=(ssize_t) cell_height)
276  {
277    if ((y+cell_height) > image->rows)
278      cell_height = (size_t) (image->rows-y);
279
280    p=GetVirtualPixels(image,0,y,image->columns,cell_height,exception);
281    if (p == (const Quantum *) NULL)
282      break;
283    for (x=0; x < (ssize_t) image->columns; x+=2)
284    {
285      unsigned char cell = 0;
286      int two_columns = x+1 < (ssize_t) image->columns;
287
288      do
289      {
290#define do_cell(dx,dy,bit) do { \
291        if (image->storage_class == PseudoClass) \
292          cell |= (GetPixelIndex(image,p+x+dx+dy*image->columns) == polarity) << bit; \
293        else \
294          cell |= (GetPixelGreen(image,p+x+dx+dy*image->columns) == 0) << bit; \
295DisableMSCWarning(4127) \
296} while (0) \
297RestoreMSCWarning
298
299        do_cell(0,0,0);
300        if (two_columns)
301          do_cell(1,0,3);
302        if (cell_height < 2)
303          break;
304
305        do_cell(0,1,1);
306        if (two_columns)
307          do_cell(1,1,4);
308        if (cell_height < 3)
309          break;
310
311        do_cell(0,2,2);
312        if (two_columns)
313          do_cell(1,2,5);
314        if (cell_height < 4)
315          break;
316
317        do_cell(0,3,6);
318        if (two_columns)
319          do_cell(1,3,7);
320DisableMSCWarning(4127)
321      } while(0);
322RestoreMSCWarning
323
324      if (unicode)
325        {
326          unsigned char utf8[3];
327          /* Unicode text */
328          utf8[0] = (unsigned char) (0xe0|((0x28>>4)&0x0f));
329          utf8[1] = 0x80|((0x28<<2)&0x3f)|(cell>>6);
330          utf8[2] = 0x80|(cell&0x3f);
331          (void) WriteBlob(image,3,utf8);
332        }
333      else if (iso_11548_1)
334        {
335          /* ISO/TR 11548-1 binary */
336          (void) WriteBlobByte(image,cell);
337        }
338      else
339        {
340          /* BRF */
341          static const unsigned char iso_to_brf[64] = {
342            ' ', 'A', '1', 'B', '\'', 'K', '2', 'L',
343            '@', 'C', 'I', 'F', '/', 'M', 'S', 'P',
344            '"', 'E', '3', 'H', '9', 'O', '6', 'R',
345            '^', 'D', 'J', 'G', '>', 'N', 'T', 'Q',
346            ',', '*', '5', '<', '-', 'U', '8', 'V',
347            '.', '%', '[', '$', '+', 'X', '!', '&',
348            ';', ':', '4', '\\', '0', 'Z', '7', '(',
349            '_', '?', 'W', ']', '#', 'Y', ')', '='
350          };
351          (void) WriteBlobByte(image,iso_to_brf[cell]);
352        }
353    }
354    if (iso_11548_1 == 0)
355      (void) WriteBlobByte(image,'\n');
356    status=SetImageProgress(image,SaveImageTag,(MagickOffsetType) y,
357      image->rows);
358    if (status == MagickFalse)
359      break;
360  }
361  (void) CloseBlob(image);
362  return(MagickTrue);
363}
364