1 /*
2 libdmtx - Data Matrix Encoding/Decoding Library
3 
4 Copyright (C) 2008, 2009 Mike Laughton
5 
6 This library is free software; you can redistribute it and/or
7 modify it under the terms of the GNU Lesser General Public
8 License as published by the Free Software Foundation; either
9 version 2.1 of the License, or (at your option) any later version.
10 
11 This library is distributed in the hope that it will be useful,
12 but WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14 Lesser General Public License for more details.
15 
16 You should have received a copy of the GNU Lesser General Public
17 License along with this library; if not, write to the Free Software
18 Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
19 
20 Contact: mike@dragonflylogic.com
21 */
22 
23 #include "dmtxwrite.h"
24 
25 char *programName;
26 
27 /**
28  * @brief  Main function for the dmtxwrite Data Matrix scanning utility.
29  * @param  argc count of arguments passed from command line
30  * @param  argv list of argument passed strings from command line
31  * @return Numeric exit code
32  */
33 int
main(int argc,char * argv[])34 main(int argc, char *argv[])
35 {
36    int err;
37    char *format;
38    UserOptions opt;
39    DmtxEncode *enc;
40    unsigned char codeBuffer[DMTXWRITE_BUFFER_SIZE];
41    int codeBufferSize;
42 
43    opt = GetDefaultOptions();
44 
45    /* Override defaults with requested options */
46    err = HandleArgs(&opt, &argc, &argv);
47    if(err != DmtxPass)
48       ShowUsage(EX_USAGE);
49 
50    /* Create and initialize libdmtx encoding struct */
51    enc = dmtxEncodeCreate();
52    if(enc == NULL)
53       FatalError(EX_SOFTWARE, "create error");
54 
55    /* Set output image properties */
56    dmtxEncodeSetProp(enc, DmtxPropPixelPacking, DmtxPack24bppRGB);
57    dmtxEncodeSetProp(enc, DmtxPropImageFlip, DmtxFlipNone);
58    dmtxEncodeSetProp(enc, DmtxPropRowPadBytes, 0);
59 
60    /* Set encoding options */
61    dmtxEncodeSetProp(enc, DmtxPropMarginSize, opt.marginSize);
62    dmtxEncodeSetProp(enc, DmtxPropModuleSize, opt.moduleSize);
63    dmtxEncodeSetProp(enc, DmtxPropScheme, opt.scheme);
64    dmtxEncodeSetProp(enc, DmtxPropSizeRequest, opt.sizeIdx);
65 
66    if(opt.gs1 != DmtxUndefined) {
67       dmtxEncodeSetProp(enc, DmtxPropFnc1, opt.gs1);
68    }
69 
70    /* Read input data into buffer */
71    codeBufferSize = ReadInputData(codeBuffer, &opt);
72 
73    /* Create barcode image */
74    if(opt.mosaic == DmtxTrue)
75       err = dmtxEncodeDataMosaic(enc, codeBufferSize, codeBuffer);
76    else
77       err = dmtxEncodeDataMatrix(enc, codeBufferSize, codeBuffer);
78 
79    if(err == DmtxFail)
80       FatalError(EX_SOFTWARE,
81             _("Unable to encode message (possibly too large for requested size)"));
82 
83    /* Write image file, but only if preview and codewords are not used */
84    if(opt.preview == DmtxTrue || opt.codewords == DmtxTrue) {
85       if(opt.preview == DmtxTrue)
86          WriteAsciiPreview(enc);
87       if(opt.codewords == DmtxTrue)
88          WriteCodewordList(enc);
89    }
90    else {
91       format = GetImageFormat(&opt);
92       if(format == NULL)
93          format = "png";
94 
95       if(StrNCmpI(format, "svg", 3) == DmtxTrue)
96          WriteSvgFile(&opt, enc, format);
97       else
98          WriteImageFile(&opt, enc, format);
99    }
100 
101    /* Clean up */
102    dmtxEncodeDestroy(&enc);
103 
104    exit(0);
105 }
106 
107 /**
108  *
109  *
110  */
111 static UserOptions
GetDefaultOptions(void)112 GetDefaultOptions(void)
113 {
114    UserOptions opt;
115    int white[3] = { 255, 255, 255 };
116    int black[3] = {   0,   0,   0 };
117 
118    memset(&opt, 0x00, sizeof(UserOptions));
119 
120    opt.inputPath = NULL;    /* default stdin */
121    opt.outputPath = NULL;   /* default stdout */
122    opt.format = NULL;
123    opt.codewords = DmtxFalse;
124    opt.marginSize = 10;
125    opt.moduleSize = 5;
126    opt.scheme = DmtxSchemeAutoBest;
127    opt.preview = DmtxFalse;
128    opt.rotate = 0;
129    opt.sizeIdx = DmtxSymbolSquareAuto;
130    memcpy(opt.color, black, sizeof(int) * 3);
131    memcpy(opt.bgColor, white, sizeof(int) * 3);
132    opt.mosaic = DmtxFalse;
133    opt.dpi = DmtxUndefined;
134    opt.verbose = DmtxFalse;
135    opt.gs1 = DmtxUndefined;
136 
137    return opt;
138 }
139 
140 /**
141  * @brief  Set and validate user-requested options from command line arguments.
142  * @param  opt runtime options from defaults or command line
143  * @param  argcp pointer to argument count
144  * @param  argvp pointer to argument list
145  * @return DmtxPass | DmtxFail
146  */
147 static DmtxPassFail
HandleArgs(UserOptions * opt,int * argcp,char ** argvp[])148 HandleArgs(UserOptions *opt, int *argcp, char **argvp[])
149 {
150    int err;
151    int i;
152    int optchr;
153    int longIndex;
154    char *ptr;
155 
156    struct option longOptions[] = {
157          {"codewords",        no_argument,       NULL, 'c'},
158          {"module",           required_argument, NULL, 'd'},
159          {"margin",           required_argument, NULL, 'm'},
160          {"encoding",         required_argument, NULL, 'e'},
161          {"format",           required_argument, NULL, 'f'},
162          {"list-formats",     no_argument,       NULL, 'l'},
163          {"output",           required_argument, NULL, 'o'},
164          {"preview",          no_argument,       NULL, 'p'},
165          {"resolution",       required_argument, NULL, 'r'},
166          {"symbol-size",      required_argument, NULL, 's'},
167          {"color",            required_argument, NULL, 'C'},
168          {"bg-color",         required_argument, NULL, 'B'},
169          {"mosaic",           no_argument,       NULL, 'M'},
170          {"rotate",           required_argument, NULL, 'R'},
171          {"gs1",              required_argument, NULL, 'G'},
172          {"verbose",          no_argument,       NULL, 'v'},
173          {"version",          no_argument,       NULL, 'V'},
174          {"help",             no_argument,       NULL,  0 },
175          {0, 0, 0, 0}
176    };
177 
178    programName = Basename((*argvp)[0]);
179 
180    for(;;) {
181       optchr = getopt_long(*argcp, *argvp, "cd:m:e:f:lo:pr:s:C:B:MR:G:vV", longOptions, &longIndex);
182       if(optchr == -1)
183          break;
184 
185       switch(optchr) {
186          case 0: /* --help */
187             ShowUsage(EX_OK);
188             break;
189          case 'c':
190             opt->codewords = DmtxTrue;
191             break;
192          case 'd':
193             err = StringToInt(&opt->moduleSize, optarg, &ptr);
194             if(err != DmtxPass || opt->moduleSize <= 0 || *ptr != '\0')
195                FatalError(EX_USAGE, _("Invalid module size specified \"%s\""), optarg);
196             break;
197          case 'm':
198             err = StringToInt(&opt->marginSize, optarg, &ptr);
199             if(err != DmtxPass || opt->marginSize <= 0 || *ptr != '\0')
200                FatalError(EX_USAGE, _("Invalid margin size specified \"%s\""), optarg);
201             break;
202          case 'e':
203             if(strlen(optarg) != 1) {
204                fprintf(stderr, "Invalid encodation scheme \"%s\"\n", optarg);
205                return DmtxFail;
206             }
207             switch(*optarg) {
208                case 'b':
209                   opt->scheme = DmtxSchemeAutoBest;
210                   break;
211                case 'f':
212                   opt->scheme = DmtxSchemeAutoFast;
213                   fprintf(stderr, "\"Fast optimized\" not implemented\n");
214                   return DmtxFail;
215                case 'a':
216                   opt->scheme = DmtxSchemeAscii;
217                   break;
218                case 'c':
219                   opt->scheme = DmtxSchemeC40;
220                   break;
221                case 't':
222                   opt->scheme = DmtxSchemeText;
223                   break;
224                case 'x':
225                   opt->scheme = DmtxSchemeX12;
226                   break;
227                case 'e':
228                   opt->scheme = DmtxSchemeEdifact;
229                   break;
230                case '8':
231                   opt->scheme = DmtxSchemeBase256;
232                   break;
233                default:
234                   fprintf(stderr, "Invalid encodation scheme \"%s\"\n", optarg);
235                   return DmtxFail;
236             }
237             break;
238          case 'f':
239             opt->format = optarg;
240             break;
241          case 'l':
242             ListImageFormats();
243             exit(EX_OK);
244             break;
245          case 'o':
246             if(strncmp(optarg, "-", 2) == 0)
247                opt->outputPath = NULL;
248             else
249                opt->outputPath = optarg;
250             break;
251          case 'p':
252             opt->preview = DmtxTrue;
253             break;
254          case 'r':
255             err = StringToInt(&(opt->dpi), optarg, &ptr);
256             if(err != DmtxPass || opt->dpi <= 0 || *ptr != '\0')
257                FatalError(EX_USAGE, _("Invalid dpi specified \"%s\""), optarg);
258             break;
259          case 's':
260             /* Determine correct barcode size and/or shape */
261             if(*optarg == 's') {
262                opt->sizeIdx = DmtxSymbolSquareAuto;
263             }
264             else if(*optarg == 'r') {
265                opt->sizeIdx = DmtxSymbolRectAuto;
266             }
267             else {
268                for(i = 0; i < DmtxSymbolSquareCount + DmtxSymbolRectCount; i++) {
269                   if(strncmp(optarg, symbolSizes[i], 8) == 0) {
270                      opt->sizeIdx = i;
271                      break;
272                   }
273                }
274                if(i == DmtxSymbolSquareCount + DmtxSymbolRectCount)
275                   return DmtxFail;
276             }
277             break;
278          case 'C':
279             opt->color[0] = 0;
280             opt->color[1] = 0;
281             opt->color[2] = 0;
282             fprintf(stderr, "Option \"%c\" not implemented\n", optchr);
283             break;
284          case 'B':
285             opt->bgColor[0] = 255;
286             opt->bgColor[1] = 255;
287             opt->bgColor[2] = 255;
288             fprintf(stderr, "Option \"%c\" not implemented\n", optchr);
289             break;
290          case 'M':
291             opt->mosaic = DmtxTrue;
292             break;
293          case 'R':
294             err = StringToInt(&(opt->rotate), optarg, &ptr);
295             if(err != DmtxPass || *ptr != '\0')
296                FatalError(EX_USAGE, _("Invalid rotation angle specified \"%s\""), optarg);
297             break;
298          case 'v':
299             opt->verbose = DmtxTrue;
300             break;
301          case 'G':
302             err = StringToInt(&(opt->gs1), optarg, &ptr);
303             if(err != DmtxPass || opt->gs1 <= 0 || opt->gs1 > 255 || *ptr != '\0')
304                FatalError(EX_USAGE, _("Invalid gs1 character specified \"%s\""), optarg);
305             break;
306          case 'V':
307             fprintf(stderr, "%s version %s\n", programName, DmtxVersion);
308             fprintf(stderr, "libdmtx version %s\n", dmtxVersion());
309             exit(0);
310             break;
311          default:
312             return DmtxFail;
313             break;
314       }
315    }
316 
317    opt->inputPath = (*argvp)[optind];
318 
319    /* XXX here test for incompatibility between options. For example you
320       cannot specify dpi if PNM output is requested */
321 
322    return DmtxPass;
323 }
324 
325 /**
326  *
327  *
328  */
329 static size_t
ReadInputData(unsigned char * codeBuffer,UserOptions * opt)330 ReadInputData(unsigned char *codeBuffer, UserOptions *opt)
331 {
332    int fd;
333    ssize_t bytesRead;
334    size_t bytesReadTotal;
335 
336    /* Open file or stdin for reading */
337    fd = (opt->inputPath == NULL) ? 0 : open(opt->inputPath, O_RDONLY);
338    if(fd == -1)
339       FatalError(EX_IOERR, _("Error while opening file \"%s\""), opt->inputPath);
340 
341    /* Read input contents into buffer */
342    for(bytesReadTotal = 0;; bytesReadTotal += bytesRead) {
343       bytesRead = read(fd, codeBuffer + bytesReadTotal, DMTXWRITE_BUFFER_SIZE);
344       if(bytesRead == 0)
345          break;
346 
347       if(bytesRead == -1)
348          FatalError(EX_IOERR, _("Message read error"));
349       else if(bytesReadTotal == DMTXWRITE_BUFFER_SIZE)
350          FatalError(EX_DATAERR, _("Message to be encoded is too large"));
351    }
352 
353    /* Close file only if not stdin */
354    if(fd != 0 && close(fd) != 0)
355       FatalError(EX_IOERR, _("Error while closing file"));
356 
357    return bytesReadTotal;
358 }
359 
360 /**
361  * @brief  Display program usage and exit with received status.
362  * @param  status error code returned to OS
363  * @return void
364  */
365 static void
ShowUsage(int status)366 ShowUsage(int status)
367 {
368    if(status != 0) {
369       fprintf(stderr, _("Usage: %s [OPTION]... [FILE]\n"), programName);
370       fprintf(stderr, _("Try `%s --help' for more information.\n"), programName);
371    }
372    else {
373       fprintf(stderr, _("Usage: %s [OPTION]... [FILE]\n"), programName);
374       fprintf(stderr, _("\
375 Encode FILE or standard input and write Data Matrix image\n\
376 \n\
377 Example: %s message.txt -o message.png\n\
378 Example: echo -n 123456 | %s -o message.png\n\
379 \n\
380 OPTIONS:\n"), programName, programName);
381       fprintf(stderr, _("\
382   -c, --codewords             print codeword listing\n\
383   -d, --module=N              module size (in pixels)\n\
384   -m, --margin=N              margin size (in pixels)\n"));
385       fprintf(stderr, _("\
386   -e, --encoding=[abcet8x]    primary encodation scheme\n\
387             a = ASCII [default]   b = Best optimized [beta]\n\
388             c = C40               e = EDIFACT\n\
389             t = Text              8 = Base 256\n\
390             x = X12\n"));
391       fprintf(stderr, _("\
392   -f, --format=FORMAT         PNG [default], TIF, GIF, PDF, etc...\n\
393   -l, --list-formats          list supported image formats\n\
394   -o, --output=FILE           output filename\n\
395   -p, --preview               print ASCII art preview\n\
396   -r, --resolution=N          set image print resolution (dpi)\n"));
397       fprintf(stderr, _("\
398   -s, --symbol-size=[sr|RxC]  symbol size (default \"s\")\n\
399         Automatic size options:\n\
400             s = Auto square         r = Auto rectangle\n"));
401       fprintf(stderr, _("\
402         Manual size options for square symbols:\n\
403             10x10   12x12   14x14   16x16   18x18   20x20\n\
404             22x22   24x24   26x26   32x32   36x36   40x40\n\
405             44x44   48x48   52x52   64x64   72x72   80x80\n\
406             88x88   96x96 104x104 120x120 132x132 144x144\n"));
407       fprintf(stderr, _("\
408         Manual size options for rectangle symbols:\n\
409              8x18    8x32   12x26   12x36   16x36   16x48\n"));
410       fprintf(stderr, _("\
411   -C, --color=COLOR           barcode color (not implemented)\n\
412   -B, --bg-color=COLOR        background color (not implemented)\n\
413   -M, --mosaic                create Data Mosaic (non-standard)\n"));
414       fprintf(stderr, _("\
415   -R, --rotate=DEGREES        rotation angle (degrees)\n\
416   -G, --gs1=N                 enable GS1 mode and define character to represent FNC1\n\
417   -v, --verbose               use verbose messages\n\
418   -V, --version               print version information\n\
419       --help                  display this help and exit\n"));
420       fprintf(stderr, _("\nReport bugs to <mike@dragonflylogic.com>.\n"));
421    }
422 
423    exit(status);
424 }
425 
426 /**
427  *
428  *
429  */
430 static void
CleanupMagick(MagickWand ** wand,int magickError)431 CleanupMagick(MagickWand **wand, int magickError)
432 {
433    char *excMessage;
434    ExceptionType excSeverity;
435 
436    if(magickError == DmtxTrue) {
437       excMessage = MagickGetException(*wand, &excSeverity);
438       fprintf(stderr, "%s %s %lu %s\n", GetMagickModule(), excMessage);
439       MagickRelinquishMemory(excMessage);
440    }
441 
442    if(*wand != NULL) {
443       DestroyMagickWand(*wand);
444       *wand = NULL;
445    }
446 }
447 
448 /**
449  * @brief  List supported input image formats on stdout
450  * @return void
451  */
452 static void
ListImageFormats(void)453 ListImageFormats(void)
454 {
455    int i, idx;
456    int row, rowCount;
457    int col, colCount;
458    unsigned long totalCount;
459    char **list;
460 
461    list = MagickQueryFormats("*", &totalCount);
462 
463    if(list == NULL)
464       return;
465 
466    fprintf(stderr, "\n");
467 
468    colCount = 7;
469    rowCount = totalCount / colCount;
470    if(totalCount % colCount)
471       rowCount++;
472 
473    for(i = 0; i < colCount * rowCount; i++) {
474       col = i % colCount;
475       row = i / colCount;
476       idx = col * rowCount + row;
477       fprintf(stderr, "%10s", (idx < totalCount) ? list[col * rowCount + row] : " ");
478       fprintf(stderr, "%s", (col + 1 < colCount) ? " " : "\n");
479    }
480    fprintf(stderr, "\n");
481 
482    MagickRelinquishMemory(list);
483 }
484 
485 /**
486  *
487  *
488  *
489  */
490 static char *
GetImageFormat(UserOptions * opt)491 GetImageFormat(UserOptions *opt)
492 {
493    char *ptr = NULL;
494 
495    /* Derive format from filename extension */
496    if(opt->outputPath != NULL) {
497       ptr = strrchr(opt->outputPath, '.');
498       if(ptr != NULL)
499          ptr++;
500    }
501 
502    /* Found filename extension but format was also provided */
503    if(ptr != NULL && strlen(ptr) > 0 && opt->format != NULL)
504       fprintf(stderr, "WARNING: --format (-f) argument ignored; Format taken from filename\n");
505 
506    /* If still undefined then use format argument */
507    if(ptr == NULL || strlen(ptr) == 0)
508       ptr = opt->format;
509 
510    return ptr;
511 }
512 
513 /**
514  *
515  *
516  */
517 static DmtxPassFail
WriteImageFile(UserOptions * opt,DmtxEncode * enc,char * format)518 WriteImageFile(UserOptions *opt, DmtxEncode *enc, char *format)
519 {
520    MagickBooleanType successA, successB;
521    MagickWand *wand;
522    char *outputPath;
523 
524    MagickWandGenesis();
525 
526    wand = NewMagickWand();
527    if(wand == NULL)
528       FatalError(EX_OSERR, "Undefined error");
529 
530    successA = MagickConstituteImage(wand, enc->image->width, enc->image->height,
531          "RGB", CharPixel, enc->image->pxl);
532    if(successA == MagickFalse) {
533       CleanupMagick(&wand, DmtxTrue);
534       FatalError(EX_OSERR, "Undefined error");
535    }
536 
537    if(opt->dpi != DmtxUndefined) {
538       successA = MagickSetImageResolution(wand, (double)opt->dpi, (double)opt->dpi);
539       successB = MagickSetImageUnits(wand, PixelsPerInchResolution);
540       if(successA == MagickFalse || successB == MagickFalse) {
541          CleanupMagick(&wand, DmtxFalse);
542          FatalError(EX_OSERR, "Illegal resolution \"%d\"", opt->dpi);
543       }
544    }
545 
546    successA = MagickSetImageFormat(wand, format);
547    if(successA == MagickFalse) {
548       CleanupMagick(&wand, DmtxFalse);
549       FatalError(EX_OSERR, "Illegal format \"%s\"", format);
550    }
551 
552    outputPath = (opt->outputPath == NULL) ? "-" : opt->outputPath;
553 
554    successA = MagickWriteImage(wand, outputPath);
555    if(successA == MagickFalse) {
556       CleanupMagick(&wand, DmtxTrue);
557       FatalError(EX_OSERR, "Undefined error");
558    }
559 
560    CleanupMagick(&wand, DmtxFalse);
561 
562    MagickWandTerminus();
563 
564    return DmtxPass;
565 }
566 
567 /**
568  *
569  *
570  */
571 static DmtxPassFail
WriteSvgFile(UserOptions * opt,DmtxEncode * enc,char * format)572 WriteSvgFile(UserOptions *opt, DmtxEncode *enc, char *format)
573 {
574    int col, row, rowInv;
575    int symbolCols, symbolRows;
576    int width, height, module;
577    int defineOnly = DmtxFalse;
578    unsigned char mosaicRed, mosaicGrn, mosaicBlu;
579    char *idString = NULL;
580    char style[100];
581    FILE *fp;
582 
583    if(StrNCmpI(format, "svg:", 4) == DmtxTrue) {
584       defineOnly = DmtxTrue;
585       idString = &format[4];
586    }
587 
588    if(idString == NULL || strlen(idString) == 0)
589       idString = "dmtx_0001";
590 
591    if(opt->outputPath == NULL) {
592       fp = stdout;
593    }
594    else {
595       fp = fopen(opt->outputPath, "wb");
596       if(fp == NULL)
597          FatalError(EX_CANTCREAT, "Unable to create output file \"%s\"", opt->outputPath);
598    }
599 
600    width = 2 * enc->marginSize + (enc->region.symbolCols * enc->moduleSize);
601    height = 2 * enc->marginSize + (enc->region.symbolRows * enc->moduleSize);
602 
603    symbolCols = dmtxGetSymbolAttribute(DmtxSymAttribSymbolCols, enc->region.sizeIdx);
604    symbolRows = dmtxGetSymbolAttribute(DmtxSymAttribSymbolRows, enc->region.sizeIdx);
605 
606    /* Print SVG Header */
607    if(defineOnly == DmtxFalse) {
608       fprintf(fp, "\
609 <?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"no\"?>\n\
610 <!-- Created with dmtxwrite (http://www.libdmtx.org/) -->\n\
611 <svg\n\
612    xmlns:svg=\"http://www.w3.org/2000/svg\"\n\
613    xmlns=\"http://www.w3.org/2000/svg\"\n\
614    xmlns:xlink=\"http://www.w3.org/1999/xlink\"\n\
615    version=\"1.0\"\n\
616    width=\"%d\"\n\
617    height=\"%d\"\n\
618    id=\"svg2\">\n\
619   <defs>\n", width, height);
620    }
621 
622    fprintf(fp, "  <symbol id=\"%s\">\n", idString);
623    fprintf(fp, "    <desc>Layout:%dx%d Symbol:%dx%d Data Matrix</desc>\n",
624          width, height, symbolCols, symbolRows);
625 
626    /* Write Data Matrix ON modules */
627    for(row = 0; row < enc->region.symbolRows; row++) {
628       rowInv = enc->region.symbolRows - row - 1;
629       for(col = 0; col < enc->region.symbolCols; col++) {
630          module = dmtxSymbolModuleStatus(enc->message, enc->region.sizeIdx, row, col);
631          if(opt->mosaic == DmtxTrue) {
632             mosaicRed = (module & DmtxModuleOnRed) ? 0x00 : 0xff;
633             mosaicGrn = (module & DmtxModuleOnGreen) ? 0x00 : 0xff;
634             mosaicBlu = (module & DmtxModuleOnBlue) ? 0x00 : 0xff;
635             snprintf(style, 100, "style=\"fill:#%02x%02x%02x;fill-opacity:1;stroke:none\" ",
636                   mosaicRed, mosaicGrn, mosaicBlu);
637          }
638          else {
639             style[0] = '\0';
640          }
641 
642          if(module & DmtxModuleOn) {
643             fprintf(fp, "    <rect width=\"%d\" height=\"%d\" x=\"%d\" y=\"%d\" %s/>\n",
644                   opt->moduleSize, opt->moduleSize,
645                   col * opt->moduleSize + opt->marginSize,
646                   rowInv * opt->moduleSize + opt->marginSize, style);
647          }
648       }
649    }
650 
651    fprintf(fp, "  </symbol>\n");
652 
653    /* Close SVG document */
654    if(defineOnly == DmtxFalse) {
655       fprintf(fp, "\
656   </defs>\n\
657 \n\
658   <use xlink:href=\"#%s\" x='0' y='0' style=\"fill:#000000;fill-opacity:1;stroke:none\" />\n\
659 \n\
660 </svg>\n", idString);
661    }
662 
663    return DmtxPass;
664 }
665 
666 /**
667  *
668  *
669  */
670 static DmtxPassFail
WriteAsciiPreview(DmtxEncode * enc)671 WriteAsciiPreview(DmtxEncode *enc)
672 {
673    int symbolRow, symbolCol;
674 
675    fputc('\n', stdout);
676 
677    /* ASCII prints from top to bottom */
678    for(symbolRow = enc->region.symbolRows - 1; symbolRow >= 0; symbolRow--) {
679 
680       fputs("    ", stdout);
681       for(symbolCol = 0; symbolCol < enc->region.symbolCols; symbolCol++) {
682          fputs((dmtxSymbolModuleStatus(enc->message, enc->region.sizeIdx,
683                symbolRow, symbolCol) & DmtxModuleOnRGB) ? "XX" : "  ", stdout);
684       }
685       fputs("\n", stdout);
686    }
687 
688    fputc('\n', stdout);
689 
690    return DmtxPass;
691 }
692 
693 /**
694  *
695  *
696  */
697 static DmtxPassFail
WriteCodewordList(DmtxEncode * enc)698 WriteCodewordList(DmtxEncode *enc)
699 {
700    int i;
701    int dataWordLength;
702    int remainingDataWords;
703 
704    dataWordLength = dmtxGetSymbolAttribute(DmtxSymAttribSymbolDataWords, enc->region.sizeIdx);
705 
706    for(i = 0; i < enc->message->codeSize; i++) {
707       remainingDataWords = dataWordLength - i;
708       if(remainingDataWords > enc->message->padCount)
709          fprintf(stdout, "%c:%03d\n", 'd', enc->message->code[i]);
710       else if(remainingDataWords > 0)
711          fprintf(stdout, "%c:%03d\n", 'p', enc->message->code[i]);
712       else
713          fprintf(stdout, "%c:%03d\n", 'e', enc->message->code[i]);
714    }
715 
716    return DmtxPass;
717 }
718 
719 /**
720  *
721  *
722  */
723 static DmtxBoolean
StrNCmpI(const char * s1,const char * s2,size_t n)724 StrNCmpI(const char *s1, const char *s2, size_t n)
725 {
726    size_t i;
727 
728    if(s1 == NULL || s2 == NULL || n == 0)
729       return DmtxFalse;
730 
731    for(i = 0; i < n; i++) {
732       if(tolower(s1[i]) != tolower(s2[i]))
733          return DmtxFalse;
734       if(s1[i] == '\0' || s2[i] == '\0')
735          break;
736    }
737 
738    return DmtxTrue;
739 }
740