11/*
2- * Copyright (c) 1995, 2018 , Oracle and/or its affiliates. All rights reserved.
2+ * Copyright (c) 1995, 2025 , Oracle and/or its affiliates. All rights reserved.
33 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
44 *
55 * This code is free software; you can redistribute it and/or modify it
2323 * questions.
2424 */
2525
26- /*-
26+ /*
2727 * Reads xbitmap format images into a DIBitmap structure.
2828 */
2929package sun .awt .image ;
3030
31- import java .io .*;
32- import java .awt .image .*;
31+ import java .awt .image .ImageConsumer ;
32+ import java .awt .image .IndexColorModel ;
33+ import java .io .BufferedInputStream ;
34+ import java .io .BufferedReader ;
35+ import java .io .IOException ;
36+ import java .io .InputStream ;
37+ import java .io .InputStreamReader ;
38+ import java .util .regex .Matcher ;
39+ import java .util .regex .Pattern ;
40+
41+ import static java .lang .Math .multiplyExact ;
3342
3443/**
3544 * Parse files of the form:
@@ -50,6 +59,8 @@ public class XbmImageDecoder extends ImageDecoder {
5059 ImageConsumer .COMPLETESCANLINES |
5160 ImageConsumer .SINGLEPASS |
5261 ImageConsumer .SINGLEFRAME );
62+ private static final int MAX_XBM_SIZE = 16384 ;
63+ private static final int HEADER_SCAN_LIMIT = 100 ;
5364
5465 public XbmImageDecoder (InputStreamImageSource src , InputStream is ) {
5566 super (src , is );
@@ -72,107 +83,125 @@ private static void error(String s1) throws ImageFormatException {
7283 * produce an image from the stream.
7384 */
7485 public void produceImage () throws IOException , ImageFormatException {
75- char [] nm = new char [80 ];
76- int c ;
77- int i = 0 ;
78- int state = 0 ;
7986 int H = 0 ;
8087 int W = 0 ;
8188 int x = 0 ;
8289 int y = 0 ;
83- boolean start = true ;
90+ int n = 0 ;
91+ int state = 0 ;
8492 byte [] raster = null ;
8593 IndexColorModel model = null ;
86- while (!aborted && (c = input .read ()) != -1 ) {
87- if ('a' <= c && c <= 'z' ||
88- 'A' <= c && c <= 'Z' ||
89- '0' <= c && c <= '9' || c == '#' || c == '_' ) {
90- if (i < 78 )
91- nm [i ++] = (char ) c ;
92- } else if (i > 0 ) {
93- int nc = i ;
94- i = 0 ;
95- if (start ) {
96- if (nc != 7 ||
97- nm [0 ] != '#' ||
98- nm [1 ] != 'd' ||
99- nm [2 ] != 'e' ||
100- nm [3 ] != 'f' ||
101- nm [4 ] != 'i' ||
102- nm [5 ] != 'n' ||
103- nm [6 ] != 'e' )
104- {
105- error ("Not an XBM file" );
94+
95+ String matchRegex = "(0[xX])?[0-9a-fA-F]+[\\ s+]?[,|};]" ;
96+ String replaceRegex = "(0[xX])|,|[\\ s+]|[};]" ;
97+
98+ String line ;
99+ int lineNum = 0 ;
100+
101+ try (BufferedReader br = new BufferedReader (new InputStreamReader (input ))) {
102+ // loop to process XBM header - width, height and create raster
103+ while (!aborted && (line = br .readLine ()) != null
104+ && lineNum <= HEADER_SCAN_LIMIT ) {
105+ lineNum ++;
106+ // process #define stmts
107+ if (line .trim ().startsWith ("#define" )) {
108+ String [] token = line .split ("\\ s+" );
109+ if (token .length != 3 ) {
110+ error ("Error while parsing define statement" );
111+ }
112+ try {
113+ if (!token [2 ].isBlank () && state == 0 ) {
114+ W = Integer .parseInt (token [2 ]);
115+ state = 1 ; // after width is set
116+ } else if (!token [2 ].isBlank () && state == 1 ) {
117+ H = Integer .parseInt (token [2 ]);
118+ state = 2 ; // after height is set
119+ }
120+ } catch (NumberFormatException nfe ) {
121+ // parseInt() can throw NFE
122+ error ("Error while parsing width or height." );
106123 }
107- start = false ;
108124 }
109- if (nm [nc - 1 ] == 'h' )
110- state = 1 ; /* expecting width */
111- else if (nm [nc - 1 ] == 't' && nc > 1 && nm [nc - 2 ] == 'h' )
112- state = 2 ; /* expecting height */
113- else if (nc > 2 && state < 0 && nm [0 ] == '0' && nm [1 ] == 'x' ) {
114- int n = 0 ;
115- for (int p = 2 ; p < nc ; p ++) {
116- c = nm [p ];
117- if ('0' <= c && c <= '9' )
118- c = c - '0' ;
119- else if ('A' <= c && c <= 'Z' )
120- c = c - 'A' + 10 ;
121- else if ('a' <= c && c <= 'z' )
122- c = c - 'a' + 10 ;
123- else
124- c = 0 ;
125- n = n * 16 + c ;
125+
126+ if (state == 2 ) {
127+ if (W <= 0 || H <= 0 ) {
128+ error ("Invalid values for width or height." );
126129 }
127- for (int mask = 1 ; mask <= 0x80 ; mask <<= 1 ) {
128- if (x < W ) {
129- if ((n & mask ) != 0 )
130- raster [x ] = 1 ;
131- else
132- raster [x ] = 0 ;
133- }
134- x ++;
130+ if (multiplyExact (W , H ) > MAX_XBM_SIZE ) {
131+ error ("Large XBM file size."
132+ + " Maximum allowed size: " + MAX_XBM_SIZE );
135133 }
136- if (x >= W ) {
137- if (setPixels (0 , y , W , 1 , model , raster , 0 , W ) <= 0 ) {
138- return ;
134+ model = new IndexColorModel (8 , 2 , XbmColormap ,
135+ 0 , false , 0 );
136+ setDimensions (W , H );
137+ setColorModel (model );
138+ setHints (XbmHints );
139+ headerComplete ();
140+ raster = new byte [W ];
141+ state = 3 ;
142+ break ;
143+ }
144+ }
145+
146+ if (state != 3 ) {
147+ error ("Width or Height of XBM file not defined" );
148+ }
149+
150+ // loop to process image data
151+ while (!aborted && (line = br .readLine ()) != null ) {
152+ lineNum ++;
153+
154+ if (line .contains ("[]" )) {
155+ Matcher matcher = Pattern .compile (matchRegex ).matcher (line );
156+ while (matcher .find ()) {
157+ if (y >= H ) {
158+ error ("Scan size of XBM file exceeds"
159+ + " the defined width x height" );
160+ }
161+
162+ int startIndex = matcher .start ();
163+ int endIndex = matcher .end ();
164+ String hexByte = line .substring (startIndex , endIndex );
165+
166+ if (!(hexByte .startsWith ("0x" )
167+ || hexByte .startsWith ("0X" ))) {
168+ error ("Invalid hexadecimal number at Ln#:" + lineNum
169+ + " Col#:" + (startIndex + 1 ));
139170 }
140- x = 0 ;
141- if (y ++ >= H ) {
142- break ;
171+ hexByte = hexByte .replaceAll (replaceRegex , "" );
172+ if (hexByte .length () != 2 ) {
173+ error ("Invalid hexadecimal number at Ln#:" + lineNum
174+ + " Col#:" + (startIndex + 1 ));
143175 }
144- }
145- } else {
146- int n = 0 ;
147- for (int p = 0 ; p < nc ; p ++)
148- if ('0' <= (c = nm [p ]) && c <= '9' )
149- n = n * 10 + c - '0' ;
150- else {
151- n = -1 ;
152- break ;
176+
177+ try {
178+ n = Integer .parseInt (hexByte , 16 );
179+ } catch (NumberFormatException nfe ) {
180+ error ("Error parsing hexadecimal at Ln#:" + lineNum
181+ + " Col#:" + (startIndex + 1 ));
182+ }
183+ for (int mask = 1 ; mask <= 0x80 ; mask <<= 1 ) {
184+ if (x < W ) {
185+ if ((n & mask ) != 0 )
186+ raster [x ] = 1 ;
187+ else
188+ raster [x ] = 0 ;
189+ }
190+ x ++;
153191 }
154- if (n > 0 && state > 0 ) {
155- if (state == 1 )
156- W = n ;
157- else
158- H = n ;
159- if (W == 0 || H == 0 )
160- state = 0 ;
161- else {
162- model = new IndexColorModel (8 , 2 , XbmColormap ,
163- 0 , false , 0 );
164- setDimensions (W , H );
165- setColorModel (model );
166- setHints (XbmHints );
167- headerComplete ();
168- raster = new byte [W ];
169- state = -1 ;
192+
193+ if (x >= W ) {
194+ int result = setPixels (0 , y , W , 1 , model , raster , 0 , W );
195+ if (result <= 0 ) {
196+ error ("Unexpected error occurred during setPixel()" );
197+ }
198+ x = 0 ;
199+ y ++;
170200 }
171201 }
172202 }
173203 }
204+ imageComplete (ImageConsumer .STATICIMAGEDONE , true );
174205 }
175- input .close ();
176- imageComplete (ImageConsumer .STATICIMAGEDONE , true );
177206 }
178207}
0 commit comments