@@ -246,6 +246,87 @@ static PdfError* read_lit_string(
246246 }
247247}
248248
249+ static bool char_to_hex (char c , int * out ) {
250+ if (c >= '0' && c <= '9' ) {
251+ * out = c - '0' ;
252+ return true;
253+ } else if (c >= 'A' && c <= 'F' ) {
254+ * out = c - 'A' + 10 ;
255+ return true;
256+ } else if (c >= 'a' && c <= 'f' ) {
257+ * out = c - 'a' + 10 ;
258+ return true;
259+ } else {
260+ return false;
261+ }
262+ }
263+
264+ static PdfError * read_hex_string (
265+ PostscriptTokenizer * tokenizer ,
266+ PostscriptString * string_out ,
267+ bool first_pass
268+ ) {
269+ RELEASE_ASSERT (tokenizer );
270+ RELEASE_ASSERT (string_out );
271+
272+ size_t restore_offset = tokenizer -> offset ;
273+
274+ if (first_pass ) {
275+ string_out -> data = NULL ;
276+ string_out -> len = 0 ;
277+ }
278+
279+ size_t write_offset = 0 ;
280+ bool upper = true;
281+
282+ while (tokenizer -> offset < tokenizer -> data_len ) {
283+ char c = tokenizer -> data [tokenizer -> offset ++ ];
284+
285+ if (is_postscript_whitespace (c )) {
286+ continue ;
287+ }
288+
289+ if (c == '>' ) {
290+ break ;
291+ }
292+
293+ int hex ;
294+ if (char_to_hex (c , & hex )) {
295+ if (upper ) {
296+ if (first_pass ) {
297+ string_out -> len ++ ;
298+ } else {
299+ string_out -> data [write_offset ] = (uint8_t )(hex << 4 );
300+ }
301+
302+ upper = false;
303+ } else {
304+ if (!first_pass ) {
305+ string_out -> data [write_offset ] |= (uint8_t )hex ;
306+ }
307+
308+ upper = true;
309+ write_offset ++ ;
310+ }
311+ } else {
312+ return PDF_ERROR (
313+ PDF_ERR_POSTSCRIPT_INVALID_CHAR ,
314+ "Non-hex character `%c` found in postscript hex string" ,
315+ c
316+ );
317+ }
318+ }
319+
320+ if (first_pass ) {
321+ string_out -> data =
322+ arena_alloc (tokenizer -> arena , sizeof (uint8_t ) * string_out -> len );
323+ tokenizer -> offset = restore_offset ;
324+ return read_hex_string (tokenizer , string_out , false);
325+ } else {
326+ return NULL ;
327+ }
328+ }
329+
249330PdfError * postscript_next_token (
250331 PostscriptTokenizer * tokenizer ,
251332 PostscriptToken * token_out ,
@@ -279,7 +360,23 @@ PdfError* postscript_next_token(
279360 token_out -> type = POSTSCRIPT_TOKEN_LIT_STRING ;
280361 return read_lit_string (tokenizer , & token_out -> data .string , true);
281362 } else if (c == '<' ) {
282- LOG_TODO ("Hex string, base-85 string, or start dictionary" );
363+ if (tokenizer -> offset >= tokenizer -> data_len ) {
364+ return PDF_ERROR (
365+ PDF_ERR_POSTSCRIPT_EOF ,
366+ "EOF after start of hex string."
367+ );
368+ }
369+
370+ char c2 = tokenizer -> data [tokenizer -> offset ];
371+ if (c2 == '<' ) {
372+ tokenizer -> offset ++ ;
373+ token_out -> type = POSTSCRIPT_TOKEN_START_DICT ;
374+ } else if (c2 == '~' ) {
375+ LOG_TODO ("Base-85 postscript strings" );
376+ } else {
377+ token_out -> type = POSTSCRIPT_TOKEN_HEX_STRING ;
378+ return read_hex_string (tokenizer , & token_out -> data .string , true);
379+ }
283380 } else if (c == '/' ) {
284381 if (tokenizer -> offset >= tokenizer -> data_len ) {
285382 token_out -> type = POSTSCRIPT_TOKEN_LIT_NAME ;
@@ -682,6 +779,17 @@ TEST_FUNC(test_postscript_tokenize_hex_string_odd_len) {
682779 return TEST_RESULT_PASS ;
683780}
684781
782+ TEST_FUNC (test_postscript_tokenize_hex_string_unexpected ) {
783+ SETUP_TOKENIZER ("<90x3>" )
784+
785+ TEST_PDF_REQUIRE_ERR (
786+ postscript_next_token (tokenizer , & token , & got_token ),
787+ PDF_ERR_POSTSCRIPT_INVALID_CHAR
788+ );
789+
790+ return TEST_RESULT_PASS ;
791+ }
792+
685793TEST_FUNC (test_postscript_tokenize_name ) {
686794 SETUP_TOKENIZER ("abc" )
687795 GET_TOKEN_WITH_DATA (POSTSCRIPT_TOKEN_EXE_NAME , name , "abc" )
0 commit comments