Skip to content

Commit ebff5c6

Browse files
committed
Postscript hex string tokenization
1 parent 0d197d7 commit ebff5c6

1 file changed

Lines changed: 109 additions & 1 deletion

File tree

libs/postscript/src/tokenizer.c

Lines changed: 109 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -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+
249330
PdfError* 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+
685793
TEST_FUNC(test_postscript_tokenize_name) {
686794
SETUP_TOKENIZER("abc")
687795
GET_TOKEN_WITH_DATA(POSTSCRIPT_TOKEN_EXE_NAME, name, "abc")

0 commit comments

Comments
 (0)