diff --git a/pdf.c b/pdf.c
index f09e564676fa73a774aa8e6df291b4b5fcfd5e84..ad5579c8fd8d81b1ca2cf62f5488beba0ef67f26 100644
--- a/pdf.c
+++ b/pdf.c
@@ -222,6 +222,31 @@ act_hupper(const HParseResult *p, void *u)
 	return H_MAKE_UINT(H_CAST_UINT(p->ast) - 'A');
 }
 
+HParsedToken*
+act_hdigitpair(const HParseResult *p, void *u)
+{
+	uint8_t b = 0;
+	HCountedArray *seq = H_CAST_SEQ(p->ast);
+	size_t digits_processed = 0;
+	uint8_t digits[2];
+	for(size_t i = 0; i < seq->used; ++i)
+	{
+		switch(seq->elements[i]->token_type)
+		{
+			case TT_UINT:
+				digits[digits_processed] = H_CAST_UINT(seq->elements[i]);
+				digits_processed++;
+				break;
+			default:
+				break;
+		}
+		assert(digits_processed == 2);
+	}
+
+	b = (digits[0] << 4) + digits[1];
+	return H_MAKE_UINT(b);
+}
+
 HParsedToken *
 act_ahextruncated(const HParseResult *p, void *u)
 {
@@ -237,6 +262,46 @@ act_ahextruncated(const HParseResult *p, void *u)
 	return H_MAKE_UINT(b);
 }
 
+HParsedToken *
+act_hs_end(const HParseResult *p, void *u)
+{
+	HParsedToken *res;
+	HCountedArray *seq = H_CAST_SEQ(p->ast);
+	assert(seq->used >= 1);
+
+	res = H_MAKE_UINT(H_CAST_UINT(seq->elements[0]));
+	return res;
+}
+
+HParsedToken *
+act_ahexstream(const HParseResult *p, void *u)
+{
+	uint8_t *result_bytes;
+	size_t chunk_number;
+	size_t required_bytes;
+	size_t out_pos = 0;
+	HCountedArray *seq = H_CAST_SEQ(p->ast);
+	HParsedToken *res;
+
+	/* Ignore the the last element, which is EOD */
+	required_bytes = (seq->used - 1) * 8;
+
+	result_bytes = h_arena_malloc(p->arena, required_bytes);
+
+	/* memcpy all but the last group's bytes into a single array */
+	for (size_t i = 0; i < seq->used-1; ++i)
+	{
+		assert(i * 8 < required_bytes);
+		result_bytes[i] = H_CAST_UINT(seq->elements[i]);
+	}
+
+	/* We should have filled the array exactly by this point */
+
+	res = H_MAKE_BYTES(result_bytes, required_bytes);
+	return res;
+}
+
+
 HParsedToken *
 act_a85zero(const HParseResult *p, void *u)
 {
@@ -912,11 +977,11 @@ init_parser(struct Env *aux)
 
 	/* AsciiHexDecode parser */
 	H_RULE(ahexeod,	h_ch('>'));
-	H_RULE(hdigitpair, SEQ(IGN(OPT(h_many(lwchar))), hdigit, IGN(OPT(h_many(lwchar))), hdigit));
-	H_ARULE(ahextruncated, SEQ(IGN(OPT(h_many(lwchar))), hdigit, IGN(OPT(h_many(lwchar))), ahexeod));
+	H_ARULE(hdigitpair, SEQ(IGN(OPT(h_many(lwchar))), hdigit, IGN(OPT(h_many(lwchar))), hdigit));
+	H_ARULE(ahextruncated, SEQ(IGN(OPT(h_many(lwchar))), hdigit, IGN(OPT(h_many(lwchar)))));
 
-	H_RULE(hs_end, CHX(hdigitpair, ahextruncated));
-	H_RULE(hexstream, SEQ(h_many(hdigitpair), hs_end));
+	H_ARULE(hs_end, SEQ(CHX(hdigitpair, ahextruncated), ahexeod));
+	H_ARULE(ahexstream, SEQ(h_many(hdigitpair), hs_end));
 
 
 	/* global parser variables */
@@ -926,7 +991,7 @@ init_parser(struct Env *aux)
 	p_xref = CHX(xr_td, xrstm);
 	p_objdef = objdef;
 	p_a85string = a85string;
-	p_ahexstream = hexstream;
+	p_ahexstream = ahexstream;
 
 	p_fail = h_nothing_p();
 	p_epsilon = epsilon;