/src/imagemagick/coders/ftxt.c
Line | Count | Source |
1 | | /* |
2 | | %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% |
3 | | % % |
4 | | % % |
5 | | % % |
6 | | % FFFFF TTTTT X X TTTTT % |
7 | | % F T X X T % |
8 | | % FFF T X T % |
9 | | % F T X X T % |
10 | | % F T X X T % |
11 | | % % |
12 | | % Read and Write Pixels as Formatted Text % |
13 | | % % |
14 | | % Software Design % |
15 | | % snibgo (Alan Gibson) % |
16 | | % October 2021 % |
17 | | % % |
18 | | % % |
19 | | % % |
20 | | % Copyright @ 1999 ImageMagick Studio LLC, a non-profit organization % |
21 | | % dedicated to making software imaging solutions freely available. % |
22 | | % % |
23 | | % You may not use this file except in compliance with the License. You may % |
24 | | % obtain a copy of the License at % |
25 | | % % |
26 | | % https://imagemagick.org/license/ % |
27 | | % % |
28 | | % Unless required by applicable law or agreed to in writing, software % |
29 | | % distributed under the License is distributed on an "AS IS" BASIS, % |
30 | | % WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. % |
31 | | % See the License for the specific language governing permissions and % |
32 | | % limitations under the License. % |
33 | | % % |
34 | | %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% |
35 | | % |
36 | | % |
37 | | % |
38 | | */ |
39 | | |
40 | | #include "MagickCore/studio.h" |
41 | | #include "MagickCore/annotate.h" |
42 | | #include "MagickCore/artifact.h" |
43 | | #include "MagickCore/attribute.h" |
44 | | #include "MagickCore/blob.h" |
45 | | #include "MagickCore/blob-private.h" |
46 | | #include "MagickCore/cache.h" |
47 | | #include "MagickCore/channel.h" |
48 | | #include "MagickCore/color.h" |
49 | | #include "MagickCore/color-private.h" |
50 | | #include "MagickCore/colorspace.h" |
51 | | #include "MagickCore/constitute.h" |
52 | | #include "MagickCore/draw.h" |
53 | | #include "MagickCore/exception.h" |
54 | | #include "MagickCore/exception-private.h" |
55 | | #include "MagickCore/geometry.h" |
56 | | #include "MagickCore/image.h" |
57 | | #include "MagickCore/image-private.h" |
58 | | #include "MagickCore/list.h" |
59 | | #include "MagickCore/magick.h" |
60 | | #include "MagickCore/memory_.h" |
61 | | #include "MagickCore/module.h" |
62 | | #include "MagickCore/monitor.h" |
63 | | #include "MagickCore/monitor-private.h" |
64 | | #include "MagickCore/option.h" |
65 | | #include "MagickCore/pixel-accessor.h" |
66 | | #include "MagickCore/quantum-private.h" |
67 | | #include "MagickCore/static.h" |
68 | | #include "MagickCore/statistic.h" |
69 | | #include "MagickCore/string_.h" |
70 | | #include "MagickCore/token.h" |
71 | | #include "coders/ftxt.h" |
72 | | |
73 | | /* |
74 | | Define declarations. |
75 | | */ |
76 | 14.7k | #define chEsc '\\' |
77 | 186 | #define dfltChSep "," |
78 | 186 | #define dfltFmt "\\x,\\y:\\c\\n" |
79 | | |
80 | | /* |
81 | | Enumerated declarations. |
82 | | */ |
83 | | typedef enum |
84 | | { |
85 | | vtAny, |
86 | | vtQuant, |
87 | | vtPercent, |
88 | | vtProp, |
89 | | vtIntHex, |
90 | | vtFltHex |
91 | | } ValueTypeT; |
92 | | |
93 | | /* |
94 | | Forward declaration. |
95 | | */ |
96 | | static MagickBooleanType |
97 | | WriteFTXTImage(const ImageInfo *,Image *,ExceptionInfo *); |
98 | | |
99 | | /* |
100 | | %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% |
101 | | % % |
102 | | % % |
103 | | % % |
104 | | % I s F T X T % |
105 | | % % |
106 | | % % |
107 | | % % |
108 | | %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% |
109 | | % |
110 | | % IsFTXT() returns MagickTrue if the image format type, identified by the |
111 | | % magick string, is formatted text. |
112 | | % |
113 | | % The format of the IsFTXT method is: |
114 | | % |
115 | | % MagickBooleanType IsFTXT(const unsigned char *magick,const size_t length) |
116 | | % |
117 | | % A description of each parameter follows: |
118 | | % |
119 | | % o magick: compare image format pattern against these bytes. |
120 | | % |
121 | | % o length: Specifies the length of the magick string. |
122 | | % |
123 | | */ |
124 | | static MagickBooleanType IsFTXT(const unsigned char *magick,const size_t length) |
125 | 0 | { |
126 | 0 | if (length < 7) |
127 | 0 | return(MagickFalse); |
128 | 0 | if (LocaleNCompare((char *) magick,"id=ftxt",7) == 0) |
129 | 0 | return(MagickTrue); |
130 | 0 | return(MagickFalse); |
131 | 0 | } |
132 | | |
133 | | /* |
134 | | %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% |
135 | | % % |
136 | | % % |
137 | | % % |
138 | | % R e a d F T X T I m a g e % |
139 | | % % |
140 | | % % |
141 | | % % |
142 | | %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% |
143 | | % |
144 | | % ReadFTXTImage() reads an formatted text image file and returns it. It |
145 | | % allocates the memory necessary for the new Image structure and returns a |
146 | | % pointer to the new image. |
147 | | % |
148 | | % The format of the ReadFTXTImage method is: |
149 | | % |
150 | | % Image *ReadFTXTImage(image_info,ExceptionInfo *exception) |
151 | | % |
152 | | % A description of each parameter follows: |
153 | | % |
154 | | % o image_info: the image info. |
155 | | % |
156 | | % o exception: return any errors or warnings in this structure. |
157 | | % |
158 | | */ |
159 | | |
160 | | static int ReadChar(Image *image, int *chPushed) |
161 | 36.1k | { |
162 | 36.1k | int |
163 | 36.1k | ch; |
164 | | |
165 | 36.1k | if (*chPushed) |
166 | 6.41k | { |
167 | 6.41k | ch=*chPushed; |
168 | 6.41k | *chPushed=0; |
169 | 6.41k | } |
170 | 29.7k | else |
171 | 29.7k | ch=ReadBlobByte(image); |
172 | 36.1k | return(ch); |
173 | 36.1k | } |
174 | | |
175 | | static int ReadInt(Image * image,MagickBooleanType *eofInp,int *chPushed, |
176 | | MagickBooleanType *err) |
177 | 4.39k | { |
178 | 4.39k | char |
179 | 4.39k | buffer[MaxTextExtent]; |
180 | | |
181 | 4.39k | char |
182 | 4.39k | *p, |
183 | 4.39k | *tail; |
184 | | |
185 | 4.39k | int |
186 | 4.39k | chIn, |
187 | 4.39k | val; |
188 | | |
189 | 4.39k | p=buffer; |
190 | 4.39k | chIn=ReadChar(image,chPushed); |
191 | 4.39k | if (chIn == EOF) |
192 | 78 | *eofInp = MagickTrue; |
193 | 4.39k | while (isdigit(chIn)) |
194 | 6.50k | { |
195 | 6.50k | *p=(char) chIn; |
196 | 6.50k | p++; |
197 | 6.50k | if (p-buffer >= MaxTextExtent) |
198 | 0 | { |
199 | 0 | *eofInp=MagickTrue; |
200 | 0 | break; |
201 | 0 | } |
202 | 6.50k | chIn=ReadChar(image,chPushed); |
203 | 6.50k | } |
204 | 4.39k | if (p == buffer) |
205 | 85 | { |
206 | 85 | *eofInp=MagickTrue; |
207 | 85 | return(0); |
208 | 85 | } |
209 | 4.31k | if (*eofInp) |
210 | 0 | { |
211 | 0 | *chPushed='\0'; |
212 | 0 | return(0); |
213 | 0 | } |
214 | 4.31k | *p='\0'; |
215 | 4.31k | *chPushed=chIn; |
216 | 4.31k | errno=0; |
217 | 4.31k | val=strtol(buffer,&tail,10); |
218 | 4.31k | if (errno || *tail) |
219 | 0 | { |
220 | 0 | *eofInp=MagickTrue; |
221 | 0 | *err=MagickTrue; |
222 | 0 | } |
223 | 4.31k | if (val < 0) |
224 | 0 | *err=MagickTrue; |
225 | 4.31k | return (val); |
226 | 4.31k | } |
227 | | |
228 | | static long double BufToFlt(char * buffer,char ** tail,ValueTypeT expectType, |
229 | | MagickBooleanType *err) |
230 | 3.38k | { |
231 | 3.38k | long double |
232 | 3.38k | val; |
233 | | |
234 | 3.38k | *err=MagickFalse; |
235 | 3.38k | val=0; |
236 | 3.38k | if (*buffer == '#') |
237 | 729 | { |
238 | 729 | char |
239 | 729 | *p; |
240 | | |
241 | | /* read hex integer */ |
242 | 729 | p=buffer+1; |
243 | 3.56k | while (*p) |
244 | 3.49k | { |
245 | 3.49k | short |
246 | 3.49k | v; |
247 | | |
248 | 3.49k | if (*p >= '0' && *p <= '9') |
249 | 1.20k | v=*p-'0'; |
250 | 2.29k | else if (*p >= 'a' && *p <= 'f') |
251 | 529 | v=*p-'a'+10; |
252 | 1.76k | else if (*p >= 'A' && *p <= 'F') |
253 | 1.10k | v=*p-'A'+10; |
254 | 657 | else |
255 | 657 | break; |
256 | 2.83k | val=val*16+v; |
257 | 2.83k | p++; |
258 | 2.83k | } |
259 | 729 | *tail=p; |
260 | 729 | if ((expectType != vtAny) && (expectType != vtIntHex)) |
261 | 0 | *err=MagickTrue; |
262 | 729 | } |
263 | 2.65k | else if ((*buffer == '0') && (*(buffer + 1) == 'x')) |
264 | 0 | { |
265 | | /* read hex floating-point */ |
266 | 0 | val=strtold(buffer,tail); |
267 | 0 | if ((expectType != vtAny) && (expectType != vtFltHex)) |
268 | 0 | *err=MagickTrue; |
269 | 0 | } |
270 | 2.65k | else |
271 | 2.65k | { |
272 | | /* Read decimal floating-point (possibly a percent). */ |
273 | 2.65k | errno=0; |
274 | 2.65k | val=strtold(buffer,tail); |
275 | 2.65k | if (errno) |
276 | 0 | *err=MagickTrue; |
277 | 2.65k | if (**tail=='%') |
278 | 620 | { |
279 | 620 | (*tail)++; |
280 | 620 | val*=(double) QuantumRange/100.0; |
281 | 620 | if ((expectType != vtAny) && (expectType != vtPercent)) |
282 | 0 | *err=MagickTrue; |
283 | 620 | } |
284 | 2.03k | else if (expectType == vtPercent) |
285 | 0 | *err=MagickTrue; |
286 | 2.65k | } |
287 | 3.38k | return(val); |
288 | 3.38k | } |
289 | | |
290 | | static void SkipUntil(Image * image,int UntilChar,MagickBooleanType *eofInp, |
291 | | int *chPushed) |
292 | 0 | { |
293 | 0 | int |
294 | 0 | chIn; |
295 | |
|
296 | 0 | chIn=ReadChar(image,chPushed); |
297 | 0 | if (chIn == EOF) |
298 | 0 | { |
299 | 0 | *eofInp=MagickTrue; |
300 | 0 | *chPushed='\0'; |
301 | 0 | return; |
302 | 0 | } |
303 | 0 | while ((chIn != UntilChar) && (chIn != EOF)) |
304 | 0 | chIn=ReadChar(image,chPushed); |
305 | 0 | if (chIn == EOF) |
306 | 0 | { |
307 | 0 | *eofInp=MagickTrue; |
308 | 0 | *chPushed='\0'; |
309 | 0 | return; |
310 | 0 | } |
311 | 0 | *chPushed=chIn; |
312 | 0 | } |
313 | | |
314 | | static void ReadUntil(Image * image,int UntilChar,MagickBooleanType *eofInp, |
315 | | int *chPushed,char *buf,int bufSize) |
316 | 2.11k | { |
317 | 2.11k | int |
318 | 2.11k | chIn, |
319 | 2.11k | i; |
320 | | |
321 | 2.11k | i=0; |
322 | 2.11k | for (;;) |
323 | 18.8k | { |
324 | 18.8k | chIn=ReadChar(image,chPushed); |
325 | 18.8k | if (chIn == EOF) |
326 | 43 | { |
327 | 43 | if (i==0) |
328 | 2 | *eofInp=MagickTrue; |
329 | 43 | break; |
330 | 43 | } |
331 | 18.7k | if (chIn == UntilChar) |
332 | 2.06k | break; |
333 | 16.7k | if (i >= bufSize) |
334 | 0 | { |
335 | 0 | *eofInp=MagickTrue; |
336 | 0 | break; |
337 | 0 | } |
338 | 16.7k | buf[i++]=(char) chIn; |
339 | 16.7k | } |
340 | 2.11k | if (*eofInp) |
341 | 2 | *chPushed='\0'; |
342 | 2.10k | else |
343 | 2.10k | *chPushed=chIn; |
344 | 2.11k | buf[i]='\0'; |
345 | 2.11k | if ((UntilChar == '\n') && (i > 0) && (buf[i-1] == '\r')) |
346 | 1 | buf[i-1]='\0'; |
347 | 2.11k | } |
348 | | |
349 | | static Image *ReadFTXTImage(const ImageInfo *image_info, |
350 | | ExceptionInfo *exception) |
351 | 186 | { |
352 | 186 | char |
353 | 186 | buffer[MaxTextExtent], |
354 | 186 | chSep, |
355 | 186 | *ppf, |
356 | 186 | procFmt[MaxTextExtent]; |
357 | | |
358 | 186 | const char |
359 | 186 | *pf, |
360 | 186 | *sChSep, |
361 | 186 | *sFmt, |
362 | 186 | *sNumMeta; |
363 | | |
364 | 186 | Image |
365 | 186 | *image; |
366 | | |
367 | 186 | int |
368 | 186 | chIn, |
369 | 186 | chPushed, |
370 | 186 | i, |
371 | 186 | nExpCh, |
372 | 186 | numMeta; |
373 | | |
374 | 186 | long double |
375 | 186 | chVals[MaxPixelChannels]; |
376 | | |
377 | 186 | MagickBooleanType |
378 | 186 | eofInp, |
379 | 186 | firstX, |
380 | 186 | firstY, |
381 | 186 | hasAlpha, |
382 | 186 | intErr, |
383 | 186 | nChErr, |
384 | 186 | status, |
385 | 186 | typeErr; |
386 | | |
387 | 186 | PixelInfo |
388 | 186 | mppBlack; |
389 | | |
390 | 186 | Quantum |
391 | 186 | *q; |
392 | | |
393 | 186 | ssize_t |
394 | 186 | maxX, |
395 | 186 | maxY, |
396 | 186 | nPix, |
397 | 186 | x, |
398 | 186 | y; |
399 | | |
400 | 186 | assert(image_info != (const ImageInfo *) NULL); |
401 | 186 | assert(image_info->signature == MagickCoreSignature); |
402 | 186 | assert(exception != (ExceptionInfo *) NULL); |
403 | 186 | assert(exception->signature == MagickCoreSignature); |
404 | 186 | if (IsEventLogging() != MagickFalse) |
405 | 0 | (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s", |
406 | 0 | image_info->filename); |
407 | 186 | image=AcquireImage(image_info,exception); |
408 | 186 | status=OpenBlob(image_info,image,ReadBinaryBlobMode,exception); |
409 | 186 | if (status == MagickFalse) |
410 | 0 | { |
411 | 0 | image=DestroyImageList(image); |
412 | 0 | return((Image *) NULL); |
413 | 0 | } |
414 | 186 | SetImageColorspace(image,RGBColorspace,exception); |
415 | 186 | SetImageColorspace(image,image_info->colorspace,exception); |
416 | 186 | sFmt=GetImageArtifact(image,"ftxt:format"); |
417 | 186 | if (sFmt == (const char *) NULL) |
418 | 186 | sFmt=dfltFmt; |
419 | 186 | sChSep=GetImageArtifact(image,"ftxt:chsep"); |
420 | 186 | if (sChSep == (const char *) NULL) |
421 | 186 | sChSep=dfltChSep; |
422 | 186 | if ((sChSep[0] == chEsc) && ((sChSep[1] == 'n' || sChSep[1] == 'N'))) |
423 | 0 | chSep='\n'; |
424 | 186 | else |
425 | 186 | chSep=sChSep[0]; |
426 | 186 | hasAlpha=IsStringTrue(GetImageArtifact(image,"ftxt:hasalpha")); |
427 | 186 | numMeta=0; |
428 | 186 | sNumMeta=GetImageArtifact(image,"ftxt:nummeta"); |
429 | 186 | if (sNumMeta != (const char *) NULL) |
430 | 0 | numMeta=atoi(sNumMeta); |
431 | 186 | if (hasAlpha) |
432 | 0 | { |
433 | 0 | if (SetImageAlphaChannel(image,OpaqueAlphaChannel,exception) == MagickFalse) |
434 | 0 | ThrowReaderException(OptionError,"SetImageAlphaChannelFailure"); |
435 | 0 | } |
436 | 186 | if (numMeta) |
437 | 0 | { |
438 | 0 | if (SetPixelMetaChannels (image, (size_t) numMeta, exception) == MagickFalse) |
439 | 0 | ThrowReaderException(OptionError,"SetPixelMetaChannelsFailure"); |
440 | 0 | } |
441 | | /* make image zero (if RGB channels, transparent black). */ |
442 | 186 | GetPixelInfo(image,&mppBlack); |
443 | 186 | if (hasAlpha) |
444 | 0 | mppBlack.alpha=TransparentAlpha; |
445 | 186 | SetImageColor(image,&mppBlack,exception); |
446 | 186 | pf=sFmt; |
447 | 186 | ppf=procFmt; |
448 | 186 | i=0; |
449 | 1.30k | while (*pf) |
450 | 1.11k | { |
451 | 1.11k | if (*pf == chEsc) |
452 | 744 | { |
453 | 744 | pf++; |
454 | 744 | switch (*pf) { |
455 | 0 | case chEsc: |
456 | 0 | if (++i >= MaxTextExtent) |
457 | 0 | ThrowReaderException(DelegateFatalError,"ppf bust"); |
458 | 0 | *ppf=chEsc; |
459 | 0 | ppf++; |
460 | 0 | break; |
461 | 186 | case 'n': |
462 | 186 | if (++i >= MaxTextExtent) |
463 | 186 | ThrowReaderException(DelegateFatalError,"ppf bust"); |
464 | 186 | *ppf='\n'; |
465 | 186 | ppf++; |
466 | 186 | break; |
467 | 0 | case 'j': |
468 | 0 | if (*(pf+1)=='\0') |
469 | 0 | ThrowReaderException(DelegateFatalError,"EscapeJproblem"); |
470 | 0 | magick_fallthrough; |
471 | 558 | default: |
472 | 558 | if ((i+=2) >= MaxTextExtent) |
473 | 558 | ThrowReaderException(DelegateFatalError,"ppf bust"); |
474 | 558 | *ppf=chEsc; |
475 | 558 | ppf++; |
476 | 558 | *ppf=*pf; |
477 | 558 | ppf++; |
478 | 558 | break; |
479 | 744 | } |
480 | 744 | } |
481 | 372 | else |
482 | 372 | { |
483 | | /* Not escape */ |
484 | 372 | if (++i >= MaxTextExtent) |
485 | 372 | ThrowReaderException (DelegateFatalError,"ppf bust"); |
486 | 372 | *ppf=*pf; |
487 | 372 | ppf++; |
488 | 372 | } |
489 | 1.11k | pf++; |
490 | 1.11k | } |
491 | 186 | *ppf='\0'; |
492 | 186 | if ((image->columns == 0) || (image->rows == 0)) |
493 | 152 | ThrowReaderException(OptionError,"MustSpecifyImageSize"); |
494 | | /* How many channel values can we expect? */ |
495 | 152 | nExpCh=0; |
496 | 323 | for (i=0; i < (ssize_t) GetPixelChannels (image); i++) |
497 | 171 | { |
498 | 171 | PixelChannel |
499 | 171 | channel; |
500 | | |
501 | 171 | PixelTrait |
502 | 171 | traits; |
503 | | |
504 | 171 | channel=GetPixelChannelChannel(image,i); |
505 | 171 | traits=GetPixelChannelTraits(image,channel); |
506 | 171 | if ((traits & UpdatePixelTrait) != UpdatePixelTrait) |
507 | 0 | continue; |
508 | 171 | nExpCh++; |
509 | 171 | } |
510 | 9.88k | for (i=0; i < MaxPixelChannels; i++) |
511 | 9.72k | chVals[i] = 0; |
512 | 152 | eofInp=MagickFalse; |
513 | 152 | chPushed=0; |
514 | 152 | x=0; |
515 | 152 | y=0, |
516 | 152 | maxX=-1; |
517 | 152 | maxY=-1; |
518 | 152 | nPix=0; |
519 | 152 | firstX=MagickTrue, |
520 | 152 | firstY=MagickTrue, |
521 | 152 | intErr=MagickFalse, |
522 | 152 | typeErr=MagickFalse, |
523 | 152 | nChErr=MagickFalse; |
524 | 2.30k | while (!eofInp) |
525 | 2.21k | { |
526 | 2.21k | ValueTypeT |
527 | 2.21k | expectType; |
528 | | |
529 | 2.21k | expectType=vtAny; |
530 | 2.21k | ppf=procFmt; |
531 | 15.0k | while (*ppf && eofInp == MagickFalse) |
532 | 12.9k | { |
533 | 12.9k | if (*ppf == chEsc) |
534 | 6.50k | { |
535 | 6.50k | ppf++; |
536 | 6.50k | switch (*ppf) |
537 | 6.50k | { |
538 | 2.21k | case 'x': |
539 | 2.21k | { |
540 | 2.21k | x=ReadInt(image,&eofInp,&chPushed,&intErr); |
541 | 2.21k | if ((intErr != MagickFalse) || (eofInp != MagickFalse)) |
542 | 27 | continue; |
543 | 2.19k | if (firstX != MagickFalse) |
544 | 151 | { |
545 | 151 | firstX=MagickFalse; |
546 | 151 | maxX=x; |
547 | 151 | } |
548 | 2.04k | else if (maxX < x) |
549 | 19 | maxX=x; |
550 | 2.19k | break; |
551 | 2.21k | } |
552 | 2.17k | case 'y': |
553 | 2.17k | { |
554 | 2.17k | y=ReadInt(image,&eofInp,&chPushed,&intErr); |
555 | 2.17k | if ((intErr != MagickFalse) || (eofInp != MagickFalse)) |
556 | 58 | continue; |
557 | 2.11k | if (firstY) |
558 | 90 | { |
559 | 90 | firstY=MagickFalse; |
560 | 90 | maxY=y; |
561 | 90 | } |
562 | 2.02k | else if (maxY < y) |
563 | 6 | maxY=y; |
564 | 2.11k | break; |
565 | 2.17k | } |
566 | 2.11k | case 'c': |
567 | 2.11k | case 'v': |
568 | 2.11k | case 'p': |
569 | 2.11k | case 'o': |
570 | 2.11k | case 'h': |
571 | 2.11k | case 'f': |
572 | 2.11k | { |
573 | 2.11k | char |
574 | 2.11k | *pt, |
575 | 2.11k | *tail; |
576 | | |
577 | 2.11k | int |
578 | 2.11k | untilChar; |
579 | | |
580 | 2.11k | long double |
581 | 2.11k | val; |
582 | | |
583 | 2.11k | if (*ppf == 'c') |
584 | 2.11k | expectType=vtAny; |
585 | 0 | else if (*ppf == 'v') |
586 | 0 | expectType=vtQuant; |
587 | 0 | else if (*ppf=='p') |
588 | 0 | expectType=vtPercent; |
589 | 0 | else if (*ppf=='o') |
590 | 0 | expectType=vtProp; |
591 | 0 | else if (*ppf=='h') |
592 | 0 | expectType=vtIntHex; |
593 | 0 | else if (*ppf=='f') |
594 | 0 | expectType=vtFltHex; |
595 | | /* |
596 | | Read chars until next char in format, |
597 | | then parse that string into chVals[], |
598 | | then write that into image. |
599 | | */ |
600 | 2.11k | untilChar=*(ppf+1); |
601 | 2.11k | ReadUntil(image,untilChar,&eofInp,&chPushed,buffer, |
602 | 2.11k | MaxTextExtent-1); |
603 | 2.11k | if (eofInp != MagickFalse) |
604 | 2 | break; |
605 | 2.10k | pt=buffer; |
606 | 2.10k | i=0; |
607 | 2.10k | for (;;) |
608 | 3.38k | { |
609 | | /* Loop through input channels. */ |
610 | 3.38k | val=BufToFlt(pt,&tail,expectType,&typeErr); |
611 | 3.38k | if (expectType == vtProp) |
612 | 0 | val *= QuantumRange; |
613 | 3.38k | if (typeErr) |
614 | 0 | break; |
615 | 3.38k | if (i < MaxPixelChannels) |
616 | 3.38k | chVals[i]=val; |
617 | 3.38k | if ((*tail == '\r') && (chSep == '\n') && (*(tail + 1) == '\n')) |
618 | 0 | tail++; |
619 | 3.38k | if (*tail == chSep) |
620 | 1.27k | pt=tail+1; |
621 | 2.10k | else |
622 | 2.10k | break; |
623 | 1.27k | i++; |
624 | 1.27k | } |
625 | 2.10k | if (i+1 != nExpCh) |
626 | 2.03k | nChErr=MagickTrue; |
627 | 2.10k | if (x < (ssize_t) image->columns && y < (ssize_t) image->rows) |
628 | 1.66k | { |
629 | 1.66k | q=QueueAuthenticPixels(image,x,y,1,1,exception); |
630 | 1.66k | if (q == (Quantum *) NULL) |
631 | 565 | break; |
632 | 4.41k | for (i=0; i< nExpCh; i++) |
633 | 3.31k | q[i]=(char) chVals[i]; |
634 | 1.10k | if (SyncAuthenticPixels(image,exception) == MagickFalse) |
635 | 0 | break; |
636 | 1.10k | } |
637 | 1.54k | break; |
638 | 2.10k | } |
639 | 1.54k | case 'j': |
640 | 0 | { |
641 | | /* Skip chars until we find char after *ppf. */ |
642 | 0 | SkipUntil(image,*(ppf+1),&eofInp,&chPushed); |
643 | 0 | break; |
644 | 2.10k | } |
645 | 0 | case 'H': |
646 | 0 | case 's': |
647 | 0 | { |
648 | 0 | int |
649 | 0 | untilChar; |
650 | |
|
651 | 0 | PixelInfo |
652 | 0 | pixelinf; |
653 | |
|
654 | 0 | untilChar=*(ppf+1); |
655 | 0 | ReadUntil(image,untilChar,&eofInp,&chPushed,buffer, |
656 | 0 | MaxTextExtent-1); |
657 | 0 | if (eofInp != MagickFalse) |
658 | 0 | break; |
659 | 0 | if (*buffer == 0) |
660 | 0 | ThrowReaderException(CorruptImageError, |
661 | 0 | "No input for escape 'H' or 's'."); |
662 | 0 | if (QueryColorCompliance(buffer,AllCompliance,&pixelinf, |
663 | 0 | exception) == MagickFalse) |
664 | 0 | break; |
665 | 0 | if (x < (ssize_t) image->columns && y < (ssize_t) image->rows) |
666 | 0 | { |
667 | 0 | q=QueueAuthenticPixels(image,x,y,1,1,exception); |
668 | 0 | if (q == (Quantum *) NULL) |
669 | 0 | break; |
670 | 0 | SetPixelViaPixelInfo(image,&pixelinf,q); |
671 | 0 | if (SyncAuthenticPixels(image,exception) == MagickFalse) |
672 | 0 | break; |
673 | 0 | } |
674 | 0 | break; |
675 | 0 | } |
676 | 0 | default: |
677 | 0 | break; |
678 | 6.50k | } |
679 | 6.50k | } |
680 | 6.41k | else |
681 | 6.41k | { |
682 | | /* Not escape */ |
683 | 6.41k | chIn=ReadChar(image,&chPushed); |
684 | 6.41k | if (chIn == EOF) |
685 | 42 | { |
686 | 42 | if (ppf != procFmt) |
687 | 42 | ThrowReaderException(CorruptImageError,"EOFduringFormat"); |
688 | 0 | eofInp=MagickTrue; |
689 | 0 | } |
690 | 6.37k | else |
691 | 6.37k | { |
692 | 6.37k | if ((chIn == '\r') && (*ppf == '\n')) |
693 | 0 | { |
694 | 0 | chIn=ReadChar(image,&chPushed); |
695 | 0 | if (chIn != '\n') |
696 | 0 | ThrowReaderException(CorruptImageError,"BackslashRbad"); |
697 | 0 | } |
698 | 6.37k | if (chIn != *ppf) |
699 | 6.35k | ThrowReaderException(CorruptImageError,"UnexpectedInputChar"); |
700 | 6.35k | } |
701 | 6.41k | } |
702 | 12.7k | ppf++; |
703 | 12.7k | } |
704 | 2.15k | if (eofInp == MagickFalse) |
705 | 2.06k | { |
706 | 2.06k | nPix++; |
707 | 2.06k | if (maxX < x) |
708 | 0 | maxX=x; |
709 | 2.06k | if (maxY < y) |
710 | 0 | maxY=y; |
711 | 2.06k | if ((firstX != MagickFalse) && (firstY != MagickFalse)) |
712 | 0 | { |
713 | 0 | x++; |
714 | 0 | if (x >= (ssize_t) image->columns) |
715 | 0 | { |
716 | 0 | x=0; |
717 | 0 | y++; |
718 | 0 | } |
719 | 0 | } |
720 | 2.06k | } |
721 | 2.15k | } |
722 | 87 | if (intErr != MagickFalse) |
723 | 87 | ThrowReaderException(CorruptImageError,"ParseIntegerError"); |
724 | 87 | if (typeErr != MagickFalse) |
725 | 87 | ThrowReaderException(CorruptImageError,"TypeError"); |
726 | 87 | if (chPushed != 0) |
727 | 87 | ThrowReaderException(CorruptImageError,"UnusedPushedChar"); |
728 | 87 | if ((maxX < 0) && (maxY < 0)) |
729 | 86 | ThrowReaderException(CorruptImageError,"UnexpectedEof"); |
730 | 86 | if (nChErr != MagickFalse) |
731 | 56 | ThrowReaderException(CorruptImageError,"NumChannelsError"); |
732 | 56 | if (nPix > (ssize_t) (image->columns * image->rows)) |
733 | 24 | ThrowMagickException(exception,GetMagickModule(),CorruptImageWarning, |
734 | 24 | "TooManyPixels","`%s'",image_info->filename); |
735 | 32 | else if ((maxX >= (ssize_t) image->columns) || |
736 | 27 | (maxY >= (ssize_t) image->rows)) |
737 | 11 | ThrowMagickException(exception,GetMagickModule(),CorruptImageWarning, |
738 | 11 | "ImageBoundsExceeded","`%s'",image_info->filename); |
739 | 56 | if (CloseBlob(image) == MagickFalse) |
740 | 0 | status=MagickFalse; |
741 | 56 | if (status == MagickFalse) |
742 | 0 | return(DestroyImageList(image)); |
743 | 56 | return(GetFirstImageInList(image)); |
744 | 56 | } |
745 | | |
746 | | /* |
747 | | %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% |
748 | | % % |
749 | | % % |
750 | | % % |
751 | | % R e g i s t e r F T X T I m a g e % |
752 | | % % |
753 | | % % |
754 | | % % |
755 | | %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% |
756 | | % |
757 | | % RegisterFTXTImage() adds properties for the FTXT image format to |
758 | | % the list of supported formats. The properties include the image format |
759 | | % tag, a method to read and/or write the format, whether the format |
760 | | % supports the saving of more than one frame to the same file or blob, |
761 | | % whether the format supports native in-memory I/O, and a brief |
762 | | % description of the format. |
763 | | % |
764 | | % The format of the RegisterFTXTImage method is: |
765 | | % |
766 | | % size_t RegisterFTXTImage(void) |
767 | | % |
768 | | */ |
769 | | ModuleExport size_t RegisterFTXTImage(void) |
770 | 9 | { |
771 | 9 | MagickInfo |
772 | 9 | *entry; |
773 | | |
774 | 9 | entry=AcquireMagickInfo("FTXT","FTXT","Formatted text image"); |
775 | 9 | entry->decoder=(DecodeImageHandler *) ReadFTXTImage; |
776 | 9 | entry->encoder=(EncodeImageHandler *) WriteFTXTImage; |
777 | 9 | entry->magick=(IsImageFormatHandler *) IsFTXT; |
778 | 9 | entry->flags^=CoderAdjoinFlag; |
779 | 9 | (void) RegisterMagickInfo(entry); |
780 | 9 | return(MagickImageCoderSignature); |
781 | 9 | } |
782 | | |
783 | | /* |
784 | | %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% |
785 | | % % |
786 | | % % |
787 | | % % |
788 | | % U n r e g i s t e r F T X T I m a g e % |
789 | | % % |
790 | | % % |
791 | | % % |
792 | | %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% |
793 | | % |
794 | | % UnregisterFTXTImage() removes format registrations made by the |
795 | | % FTXT module from the list of supported formats. |
796 | | % |
797 | | % The format of the UnregisterFTXTImage method is: |
798 | | % |
799 | | % UnregisterFTXTImage(void) |
800 | | % |
801 | | */ |
802 | | ModuleExport void UnregisterFTXTImage(void) |
803 | 0 | { |
804 | 0 | (void) UnregisterMagickInfo("FTXT"); |
805 | 0 | } |
806 | | |
807 | | /* |
808 | | %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% |
809 | | % % |
810 | | % % |
811 | | % % |
812 | | % W r i t e F T X T I m a g e % |
813 | | % % |
814 | | % % |
815 | | % % |
816 | | %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% |
817 | | % |
818 | | % WriteFTXTImage() writes an image in the formatted text image format. |
819 | | % |
820 | | % The format of the WriteFTXTImage method is: |
821 | | % |
822 | | % MagickBooleanType WriteFTXTImage(const ImageInfo *image_info, |
823 | | % Image *image,ExceptionInfo *exception) |
824 | | % |
825 | | % A description of each parameter follows. |
826 | | % |
827 | | % o image_info: the image info. |
828 | | % |
829 | | % o image: The image. |
830 | | % |
831 | | % o exception: return any errors or warnings in this structure. |
832 | | % |
833 | | */ |
834 | | static MagickBooleanType WriteFTXTImage(const ImageInfo *image_info, |
835 | | Image *image,ExceptionInfo *exception) |
836 | 0 | { |
837 | 0 | char |
838 | 0 | buffer[MaxTextExtent], |
839 | 0 | chSep, |
840 | 0 | sSuff[2]; |
841 | |
|
842 | 0 | const char |
843 | 0 | *sChSep, |
844 | 0 | *sFmt; |
845 | |
|
846 | 0 | const Quantum |
847 | 0 | *p; |
848 | |
|
849 | 0 | int |
850 | 0 | precision; |
851 | |
|
852 | 0 | MagickBooleanType |
853 | 0 | status; |
854 | |
|
855 | 0 | MagickOffsetType |
856 | 0 | scene; |
857 | |
|
858 | 0 | PixelInfo |
859 | 0 | pixel; |
860 | |
|
861 | 0 | assert(image_info != (const ImageInfo *) NULL); |
862 | 0 | assert(image_info->signature == MagickCoreSignature); |
863 | 0 | assert(image != (Image *) NULL); |
864 | 0 | assert(image->signature == MagickCoreSignature); |
865 | 0 | if (IsEventLogging() != MagickFalse) |
866 | 0 | (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename); |
867 | 0 | status=OpenBlob(image_info,image,WriteBinaryBlobMode,exception); |
868 | 0 | if (status == MagickFalse) |
869 | 0 | return(status); |
870 | 0 | scene=0; |
871 | 0 | precision=GetMagickPrecision(); |
872 | 0 | sFmt=GetImageArtifact(image,"ftxt:format"); |
873 | 0 | if (sFmt == (const char *) NULL) |
874 | 0 | sFmt=dfltFmt; |
875 | 0 | sChSep=GetImageArtifact(image,"ftxt:chsep"); |
876 | 0 | if (sChSep == (const char *) NULL) |
877 | 0 | sChSep=dfltChSep; |
878 | 0 | if ((sChSep[0]==chEsc) && ((sChSep[1] == 'n') || (sChSep[1] == 'N'))) |
879 | 0 | chSep='\n'; |
880 | 0 | else |
881 | 0 | chSep=sChSep[0]; |
882 | 0 | sSuff[0]='\0'; |
883 | 0 | sSuff[1]='\0'; |
884 | |
|
885 | 0 | do |
886 | 0 | { |
887 | 0 | long |
888 | 0 | x, |
889 | 0 | y; |
890 | |
|
891 | 0 | GetPixelInfo(image,&pixel); |
892 | 0 | for (y=0; y < (long) image->rows; y++) |
893 | 0 | { |
894 | 0 | p=GetVirtualPixels(image,0,y,image->columns,1,exception); |
895 | 0 | if (!p) break; |
896 | 0 | for (x=0; x < (long) image->columns; x++) |
897 | 0 | { |
898 | 0 | const char |
899 | 0 | *pFmt; |
900 | |
|
901 | 0 | long double |
902 | 0 | valMult; |
903 | |
|
904 | 0 | MagickBooleanType |
905 | 0 | fltHexFmt, |
906 | 0 | hexFmt; |
907 | |
|
908 | 0 | valMult=1.0; |
909 | 0 | hexFmt=MagickFalse; |
910 | 0 | fltHexFmt=MagickFalse; |
911 | 0 | pFmt=sFmt; |
912 | 0 | while (*pFmt) |
913 | 0 | { |
914 | 0 | if (*pFmt == chEsc) |
915 | 0 | { |
916 | 0 | pFmt++; |
917 | 0 | switch (*pFmt) |
918 | 0 | { |
919 | 0 | case 'x': |
920 | 0 | { |
921 | 0 | FormatLocaleString(buffer,MaxTextExtent,"%li",x); |
922 | 0 | WriteBlobString(image,buffer); |
923 | 0 | break; |
924 | 0 | } |
925 | 0 | case 'y': |
926 | 0 | { |
927 | 0 | FormatLocaleString (buffer,MaxTextExtent,"%li",y); |
928 | 0 | WriteBlobString(image,buffer); |
929 | 0 | break; |
930 | 0 | } |
931 | 0 | case 'n': |
932 | 0 | { |
933 | 0 | WriteBlobString(image,"\n"); |
934 | 0 | break; |
935 | 0 | } |
936 | 0 | case chEsc: |
937 | 0 | { |
938 | 0 | FormatLocaleString(buffer,MaxTextExtent,"%c%c",chEsc,chEsc); |
939 | 0 | WriteBlobString(image,buffer); |
940 | 0 | break; |
941 | 0 | } |
942 | 0 | case 'c': |
943 | 0 | case 'v': |
944 | 0 | case 'p': |
945 | 0 | case 'o': |
946 | 0 | case 'h': |
947 | 0 | case 'f': |
948 | 0 | { |
949 | 0 | char |
950 | 0 | sSep[2]; |
951 | |
|
952 | 0 | ssize_t |
953 | 0 | i; |
954 | |
|
955 | 0 | hexFmt=MagickFalse; |
956 | 0 | if (*pFmt == 'c') |
957 | 0 | valMult=1.0; |
958 | 0 | else if (*pFmt == 'v') |
959 | 0 | valMult=1.0; |
960 | 0 | else if (*pFmt == 'p') |
961 | 0 | { |
962 | 0 | valMult=100*QuantumScale; |
963 | 0 | sSuff[0]='%'; |
964 | 0 | } |
965 | 0 | else if (*pFmt == 'o') |
966 | 0 | valMult=QuantumScale; |
967 | 0 | else if (*pFmt == 'h') |
968 | 0 | { |
969 | 0 | valMult=1.0; |
970 | 0 | hexFmt=MagickTrue; |
971 | 0 | } |
972 | 0 | else if (*pFmt == 'f') |
973 | 0 | { |
974 | 0 | valMult=1.0; |
975 | 0 | fltHexFmt=MagickTrue; |
976 | 0 | } |
977 | | /* Output all "-channel" channels. */ |
978 | 0 | sSep[0]=sSep[1]='\0'; |
979 | 0 | for (i=0; i < (ssize_t) GetPixelChannels (image); i++) |
980 | 0 | { |
981 | 0 | PixelChannel |
982 | 0 | channel; |
983 | |
|
984 | 0 | PixelTrait |
985 | 0 | traits; |
986 | |
|
987 | 0 | channel=GetPixelChannelChannel(image,i); |
988 | 0 | traits=GetPixelChannelTraits(image,channel); |
989 | 0 | if ((traits & UpdatePixelTrait) != UpdatePixelTrait) |
990 | 0 | continue; |
991 | 0 | if (hexFmt) |
992 | 0 | FormatLocaleString(buffer,MaxTextExtent,"%s#%llx",sSep, |
993 | 0 | (signed long long)(((long double) p[i])+0.5)); |
994 | 0 | else if (fltHexFmt) |
995 | 0 | FormatLocaleString(buffer,MaxTextExtent,"%s%a",sSep, |
996 | 0 | (double) p[i]); |
997 | 0 | else |
998 | 0 | FormatLocaleString(buffer,MaxTextExtent,"%s%.*g%s",sSep, |
999 | 0 | precision,(double) (p[i]*valMult),sSuff); |
1000 | 0 | WriteBlobString(image,buffer); |
1001 | 0 | sSep[0]=chSep; |
1002 | 0 | } |
1003 | 0 | break; |
1004 | 0 | } |
1005 | 0 | case 'j': |
1006 | 0 | { |
1007 | | /* Output nothing. */ |
1008 | 0 | break; |
1009 | 0 | } |
1010 | 0 | case 's': |
1011 | 0 | { |
1012 | 0 | GetPixelInfoPixel(image,p,&pixel); |
1013 | 0 | GetColorTuple(&pixel,MagickFalse,buffer); |
1014 | 0 | WriteBlobString(image,buffer); |
1015 | 0 | break; |
1016 | 0 | } |
1017 | 0 | case 'H': |
1018 | 0 | { |
1019 | 0 | GetPixelInfoPixel(image,p,&pixel); |
1020 | | /* |
1021 | | For reading, QueryColorCompliance misreads 64 bit/channel |
1022 | | hex colours, so when writing we ensure it is at most 32 bits. |
1023 | | */ |
1024 | 0 | if (pixel.depth > 32) |
1025 | 0 | pixel.depth=32; |
1026 | 0 | GetColorTuple(&pixel,MagickTrue,buffer); |
1027 | 0 | WriteBlobString(image,buffer); |
1028 | 0 | break; |
1029 | 0 | } |
1030 | 0 | default: |
1031 | 0 | break; |
1032 | 0 | } |
1033 | 0 | } |
1034 | 0 | else |
1035 | 0 | { |
1036 | | /* Not an escape char. */ |
1037 | 0 | buffer[0]=*pFmt; |
1038 | 0 | buffer[1]='\0'; |
1039 | 0 | (void) WriteBlobString(image,buffer); |
1040 | 0 | } |
1041 | 0 | if (*pFmt) |
1042 | 0 | pFmt++; |
1043 | 0 | } |
1044 | 0 | p+=(ptrdiff_t) GetPixelChannels(image); |
1045 | 0 | } |
1046 | 0 | if ((image->previous == (Image *) NULL) && |
1047 | 0 | (image->progress_monitor != (MagickProgressMonitor) NULL) && |
1048 | 0 | (QuantumTick(y,image->rows) != MagickFalse)) |
1049 | 0 | { |
1050 | 0 | status=image->progress_monitor(SaveImageTag,y,image->rows, |
1051 | 0 | image->client_data); |
1052 | 0 | if (status == MagickFalse) |
1053 | 0 | break; |
1054 | 0 | } |
1055 | 0 | } |
1056 | 0 | if (GetNextImageInList(image) == (Image *) NULL) |
1057 | 0 | break; |
1058 | 0 | image=SyncNextImageInList(image); |
1059 | 0 | status=SetImageProgress(image,SaveImagesTag,scene, |
1060 | 0 | GetImageListLength(image)); |
1061 | 0 | if (status == MagickFalse) |
1062 | 0 | break; |
1063 | 0 | scene++; |
1064 | 0 | } while (image_info->adjoin != MagickFalse); |
1065 | 0 | if (CloseBlob(image) == MagickFalse) |
1066 | 0 | status=MagickFalse; |
1067 | 0 | return(status); |
1068 | 0 | } |