diff --git a/pdf.c b/pdf.c
index 01ea6f349a07553568386f1ceb6f28151f55da6d..89e95584c5fd0a2c903591fd8ded78e3e3ecc46d 100644
--- a/pdf.c
+++ b/pdf.c
@@ -36,8 +36,14 @@
 uint8_t log_level = 0;
 uint64_t log_messages = 0; // XXX: maybe make it static to log_messages
 uint64_t log_capacity = 0; // XXX: this too
-uint8_t logs_failed = 0;
+uint8_t logs_failed = 0; //XXX: would also be better off without being a global
 
+/*
+ * "Severity" is more for the sake of act_viol(), and I'm not sure it's a good idea to use the same scale for other errors
+ * other errors (such as error messages in semantic actions other than act_viol). Use this to avoid having to assign severity to an error message.
+ * Don't use it for violations.
+*/
+#define SEV_DONTCARE 99998
 
 typedef struct log_entry_S {
 	const char *message;
@@ -2085,7 +2091,7 @@ act_TJ_op(const HParseResult *p, void *u)
 			//		txte->tarray.elts[i].tstr.nchars, txte->tarray.elts[i].tstr.text);
 			break;
 		default:
-			fprintf(stderr, "act_TJ_op:: Unexpected element type :: %d\n", elt->seq->elements[0]->token_type);
+			log_message(SEV_DONTCARE, "act_TJ_op:: Unexpected element type :: %d\n", elt->seq->elements[0]->token_type);
 			fflush(stderr);
 			assert(false);
 		}
@@ -3070,7 +3076,7 @@ parse_obj(struct Env *aux, size_t nr, size_t gen, size_t offset)
 	size_t def_nr, def_gen;
 
 	if (offset >= aux->sz) {
-		fprintf(stderr, "%s: position %zu (%#zx) for object %zu %zu is "
+		log_message(SEV_DONTCARE, "%s: position %zu (%#zx) for object %zu %zu is "
 		    "out of bounds\n", aux->infile, offset, offset, nr, gen);
 		return NULL;
 	}
@@ -3079,7 +3085,7 @@ parse_obj(struct Env *aux, size_t nr, size_t gen, size_t offset)
 	HParser *p = h_right(h_seek(offset * 8, SEEK_SET), p_objdef);	// XXX
 	res = h_parse(p, aux->input, aux->sz); // XXX review
 	if (res == NULL) {
-		fprintf(stderr, "%s: error parsing object %zu %zu at position "
+		log_message(SEV_DONTCARE, "%s: error parsing object %zu %zu at position "
 		    "%zu (%#zx)\n", aux->infile, nr, gen, offset, offset);
 		return NULL;
 	}
@@ -3088,7 +3094,7 @@ parse_obj(struct Env *aux, size_t nr, size_t gen, size_t offset)
 	def_nr = H_INDEX_UINT(res->ast, 0, 0);
 	def_gen = H_INDEX_UINT(res->ast, 0, 1);
 	if (def_nr != nr || def_gen != gen) {
-		fprintf(stderr, "%s: object ID mismatch at position %zu "
+		log_message(SEV_DONTCARE, "%s: object ID mismatch at position %zu "
 		    "(%#zx): sought %zu %zu, found %zu %zu.\n", aux->infile,
 		    offset, offset, nr, gen, def_nr, def_gen);
 		return NULL;
@@ -3137,7 +3143,7 @@ parse_objstm_obj(struct Env *aux, size_t nr, size_t stm_nr, size_t idx)
 	}
 
 	if ((stm = ent->obj) == NULL) {
-		fprintf(stderr, "%s: error parsing object stream at position "
+		log_message(SEV_DONTCARE, "%s: error parsing object stream at position "
 		    "%zu (%#zx)\n", aux->infile, ent->n.offs, ent->n.offs);
 		return NULL;
 	}
@@ -3310,7 +3316,7 @@ depred_png(struct predictor *pred, uint8_t *inp, size_t sz)
 		if (pred->predfun == NULL) {	/* we are before a new row */
 			/* select predictor function */
 			if (inp[i] > 4) {
-				fprintf(stderr, "unknown PNG predictor %d\n",
+				log_message(SEV_DONTCARE, "unknown PNG predictor %d\n",
 				    (int)inp[i]);
 				return -1;
 			}
@@ -3389,31 +3395,31 @@ FlateDecode(const Dict *parms, HBytes b, HParser *p)
 				depredict = depred_png;
 			} else {
 				// XXX add general TIFF predictor (bpc != 8)
-				fprintf(stderr, "FlateDecode: /Predictor %d "
+				log_message(SEV_DONTCARE, "FlateDecode: /Predictor %d "
 				    "not supported for /BitsPerComponent %d\n",
 				    pred.num, pred.bpc);
 				return NULL;
 			}
 		} else {
-			fprintf(stderr, "FlateDecode: /Predictor %d"
+			log_message(SEV_DONTCARE, "FlateDecode: /Predictor %d"
 			    " not supported\n", pred.num);
 			return NULL;
 		}
 
 		if(pred.colors < 1)
 		{
-			fprintf(stderr, "FlateDecode: /Colors has an invalid value of %d\n", pred.colors);
+			log_message(SEV_DONTCARE, "FlateDecode: /Colors has an invalid value of %d\n", pred.colors);
 			return NULL;
 		}
 		if(pred.bpc != 1 && pred.bpc != 2 && pred.bpc != 4 && pred.bpc != 8 && pred.bpc != 16)
 		{
-			fprintf(stderr, "FlateDecode: /BitsPerComponent has an invalid value of %d\n", pred.bpc);
+			log_message(SEV_DONTCARE, "FlateDecode: /BitsPerComponent has an invalid value of %d\n", pred.bpc);
 			return NULL;
 		}
 
 		/* allocate row buffer */
 		if (pred.columns > (INT_MAX - 7) / pred.colors / pred.bpc) {
-			fprintf(stderr, "FlateDecode: overflow\n");
+			log_message(SEV_DONTCARE, "FlateDecode: overflow\n");
 			return NULL;
 		}
 		pred.rowsz = (pred.colors * pred.bpc * pred.columns + 7) / 8;
@@ -3447,7 +3453,7 @@ FlateDecode(const Dict *parms, HBytes b, HParser *p)
 
 		ret = inflate(&strm, Z_NO_FLUSH);
 		if (ret != Z_STREAM_END && ret != Z_OK) {
-			fprintf(stderr, "inflate: %s (%d)\n", strm.msg, ret);
+			log_message(SEV_DONTCARE, "inflate: %s (%d)\n", strm.msg, ret);
 			break;
 		}
 
@@ -3466,7 +3472,7 @@ FlateDecode(const Dict *parms, HBytes b, HParser *p)
 
 	res = h_parse(p, pred.out, pred.nout);
 	if (!res) {
-		fprintf(stdout, "\n\nFlateDecode:: Failed to parse stream of length %lu\n", pred.nout);
+		log_message(SEV_DONTCARE, "\n\nFlateDecode:: Failed to parse stream of length %lu\n", pred.nout);
 	}
 	free(pred.out);
 #endif
@@ -3540,31 +3546,31 @@ LZWDecode(const Dict *parms, HBytes b, HParser *p)
 				depredict = depred_png;
 			} else {
 				// XXX add general TIFF predictor (bpc != 8)
-				fprintf(stderr, "LZWDecode: /Predictor %d "
+				log_message(SEV_DONTCARE, "LZWDecode: /Predictor %d "
 				    "not supported for /BitsPerComponent %d\n",
 				    pred.num, pred.bpc);
 				return NULL;
 			}
 		} else {
-			fprintf(stderr, "LZWDecode: /Predictor %d"
+			log_message(SEV_DONTCARE, "LZWDecode: /Predictor %d"
 			    " not supported\n", pred.num);
 			return NULL;
 		}
 
 		if(pred.colors < 1)
 		{
-			fprintf(stderr, "LZWDecode: /Colors has an invalid value of %d\n", pred.colors);
+			log_message(SEV_DONTCARE, "LZWDecode: /Colors has an invalid value of %d\n", pred.colors);
 			return NULL;
 		}
 		if(pred.bpc != 1 && pred.bpc != 2 &&  pred.bpc != 4 && pred.bpc != 8 && pred.bpc != 16)
 		{
-			fprintf(stderr, "LZWDecode: /BitsPerComponent has an invalid value of %d\n", pred.bpc);
+			log_message(SEV_DONTCARE, "LZWDecode: /BitsPerComponent has an invalid value of %d\n", pred.bpc);
 			return NULL;
 		}
 
 		/* allocate row buffer */
 		if (pred.columns > (INT_MAX - 7) / pred.colors / pred.bpc) {
-			fprintf(stderr, "LZWDecode: overflow\n");
+			log_message(SEV_DONTCARE, "LZWDecode: overflow\n");
 			return NULL;
 		}
 		pred.rowsz = (pred.colors * pred.bpc * pred.columns + 7) / 8;
@@ -3588,7 +3594,7 @@ LZWDecode(const Dict *parms, HBytes b, HParser *p)
 
 	if(!tmp_res)
 	{
-		fprintf(stderr, "parse error in LZWDecode filter\n");
+		log_message(SEV_DONTCARE, "parse error in LZWDecode filter\n");
 		return NULL;
 	}
 
@@ -3619,7 +3625,7 @@ RunLengthDecode(const Dict *parms, HBytes b, HParser *p)
 	res = h_parse(p_rldstring, b.token, b.len);
 	if(!res)
 	{
-		fprintf(stderr, "parse error in RunLengthDecode filter\n");
+		log_message(SEV_DONTCARE, "parse error in RunLengthDecode filter\n");
 		return NULL;
 	}
 
@@ -3645,7 +3651,7 @@ ASCIIHexDecode(const Dict *parms, HBytes b, HParser *p)
 	f_res = h_parse(p_ahexstream, b.token, b.len);
 	if(!f_res)
 	{
-		fprintf(stderr, "parse error in ASCIIHexDecode filter\n");
+		log_message(SEV_DONTCARE, "parse error in ASCIIHexDecode filter\n");
 		return NULL;
 	}
 
@@ -3676,7 +3682,7 @@ ASCII85Decode(const Dict *parms, HBytes b, HParser *p)
 	f_res = h_parse(p_a85string, b.token, b.len);
 	if(!f_res)
 	{
-		fprintf(stderr, "parse error in ASCII85Decode filter\n");
+		log_message(SEV_DONTCARE, "parse error in ASCII85Decode filter\n");
 		return NULL;
 	}
 
@@ -3802,7 +3808,7 @@ decode_contentstream(const Dict *d, HBytes b, HParser *p)
 		else if (bytes_eq(v->bytes, "LZWDecode"))
 			filter = LZWDecode;
 		else {		/* filter not supported */
-			fprintf(stderr, "decode_stream:: Unsupported Filter [%.*s]\n",
+			log_message(SEV_DONTCARE, "decode_stream:: Unsupported Filter [%.*s]\n",
 					(int)v->bytes.len, v->bytes.token);
 			return NULL; /* Treat the stream as a byte array */
 		}
@@ -3876,13 +3882,13 @@ parse_item(struct Env *aux, size_t nr, size_t gen, size_t offset, HParser *p)
 	size_t def_nr, def_gen;
 
 	if (offset >= aux->sz) {
-		fprintf(stderr, "%s: position %zu (%#zx) for object %lu %lu is "
+		log_message(SEV_DONTCARE, "%s: position %zu (%#zx) for object %lu %lu is "
 		    "out of bounds\n", aux->infile, offset, offset, nr, gen);
 		return NULL;
 	}
 
 	if (p == NULL) {
-		fprintf(stderr, "parse_item: Attempt to use a NULL parser!\n");
+		log_message(SEV_DONTCARE, "parse_item: Attempt to use a NULL parser!\n");
 		return NULL;
 	}
 	//fprintf(stdout, "\nparse_item:: Parsing reference = %lu, %lu, at offset = %zu (%#zx)\n",
@@ -3890,7 +3896,7 @@ parse_item(struct Env *aux, size_t nr, size_t gen, size_t offset, HParser *p)
 	HParser *pItem = h_right(h_seek(offset * 8, SEEK_SET), p);
 	res = h_parse(pItem, aux->input, aux->sz);
 	if (res == NULL) {
-		fprintf(stderr, "%s: error parsing object %zu %zu at position "
+		log_message(SEV_DONTCARE, "%s: error parsing object %zu %zu at position "
 		    "%zu (%#zx)\n", aux->infile, nr, gen, offset, offset);
 		return NULL;
 	}
@@ -3903,7 +3909,7 @@ parse_item(struct Env *aux, size_t nr, size_t gen, size_t offset, HParser *p)
 	def_nr = H_INDEX_UINT(res->ast, 0, 0);
 	def_gen = H_INDEX_UINT(res->ast, 0, 1);
 	if (def_nr != nr || def_gen != gen) {
-		fprintf(stderr, "%s: object ID mismatch at position %zu "
+		log_message(SEV_DONTCARE, "%s: object ID mismatch at position %zu "
 			"(%#zx): sought %zu %zu, found %zu %zu.\n", aux->infile,
 			offset, offset, nr, gen, def_nr, def_gen);
 		return NULL;
@@ -3950,7 +3956,7 @@ parse_objstm_item(struct Env *aux, size_t nr, size_t stm_nr, size_t idx, size_t
 	}
 
 	if ((stm = ent->obj) == NULL) {
-		fprintf(stderr, "%s: error parsing object stream at position "
+		log_message(SEV_DONTCARE, "%s: error parsing object stream at position "
 		    "%zu (%#zx)\n", aux->infile, ent->n.offs, ent->n.offs);
 		return NULL;
 	}
@@ -4247,11 +4253,11 @@ kbyteostream(HAllocator *mm__, const HParsedToken *x, void *env)
 	v = resolve_item(aux, v, &nOffset, p_objdef);		/* resolve indirect references */
 	if (v == NULL || v->token_type != TT_SINT || v->sint < 0) {
 		if (v == NULL)
-			fprintf(stderr, "kbyteostream: stream /Length missing\n");
+			log_message(SEV_DONTCARE, "kbyteostream: stream /Length missing\n");
 		else if (v -> token_type != TT_SINT)
-			fprintf(stderr, "kbyteostream: stream /Length not an integer\n");
+			log_message(SEV_DONTCARE, "kbyteostream: stream /Length not an integer\n");
 		else if (v < 0)
-			fprintf(stderr, "kbyteostream: stream /Length negative\n");
+			log_message(SEV_DONTCARE, "kbyteostream: stream /Length negative\n");
 
 		//h_pprintln(stderr, p);	// XXX debug
 		return p_fail;
@@ -4304,11 +4310,11 @@ kcontentstream(HAllocator *mm__, const HParsedToken *x, void *env)
 	v = resolve_item(aux, v, &nOffset, p_objdef);		/* resolve indirect references */
 	if (v == NULL || v->token_type != TT_SINT || v->sint < 0) {
 		if (v == NULL)
-			fprintf(stderr, "kcontentstream: stream /Length missing\n");
+			log_message(SEV_DONTCARE, "kcontentstream: stream /Length missing\n");
 		else if (v -> token_type != TT_SINT)
-			fprintf(stderr, "kcontentstream: stream /Length not an integer\n");
+			log_message(SEV_DONTCARE, "kcontentstream: stream /Length not an integer\n");
 		else if (v < 0)
-			fprintf(stderr, "kcontentstream: stream /Length negative\n");
+			log_message(SEV_DONTCARE, "kcontentstream: stream /Length negative\n");
 
 		//h_pprintln(stderr, p);	// XXX debug
 		return p_fail;
@@ -4543,7 +4549,7 @@ bool parse_fonts(const HParsedToken *dict_t, RsrcDict_T *pgRsrc, struct Env *aux
 	// Handle a font resource
 	if (has_value(fontdict, "Type", "Font")) {
 		if (pgRsrc->fonts) {
-			fprintf(stderr, "\n\nparse_fonts: Attempt to add fonts -- Supported??\n\n");
+			log_message(SEV_DONTCARE, "\n\nparse_fonts: Attempt to add fonts -- Supported??\n\n");
 		}
 		else {
 			pgRsrc->fonts = item;
@@ -4567,7 +4573,7 @@ bool parse_fonts(const HParsedToken *dict_t, RsrcDict_T *pgRsrc, struct Env *aux
 		fprintf(stdout, "parse_fonts: Num fonts used in page = %lu \n", fontlist->used);
 		pp_dict(stdout, item, 0, 0);
 		if (pgRsrc->fonts) {
-			fprintf(stderr, "\n\nparse_fonts: Attempt to add fonts -- Supported??\n\n");
+			log_message(SEV_DONTCARE, "\n\nparse_fonts: Attempt to add fonts -- Supported??\n\n");
 		}
 		else {
 			pgRsrc->fonts = item;
@@ -5263,7 +5269,7 @@ decode_stream(const Dict *d, HBytes b, HParser *p)
 	else if (bytes_eq(v->bytes, "LZWDecode"))
 		filter = LZWDecode;
 	else {		/* filter not supported */
-		fprintf(stderr, "decode_stream:: Unsupported Filter [%.*s\n]",
+		log_message(SEV_DONTCARE, "decode_stream:: Unsupported Filter [%.*s\n]",
 				(int)v->bytes.len, v->bytes.token);
 		return NULL; /* Treat the stream as a byte array */
 	}
@@ -5306,7 +5312,7 @@ p_stream_data__m(HAllocator *mm__, const Dict *dict, struct Env *aux)
 		 */
 		v = dictentry(dict, "Subtype");
 		if (v == NULL) {
-			fprintf(stderr, "\nFailed to parse Subtype of XObject in dictionary");
+			log_message(SEV_DONTCARE, "\nFailed to parse Subtype of XObject in dictionary");
 			return (NULL);
 		}
 		if (bytes_eq(v->bytes, "Form")) {
@@ -5343,7 +5349,7 @@ act_ks_value(const HParseResult *p, void *u)
 		}
 		if (b.len > INT_MAX)
 			b.len = INT_MAX;
-		fprintf(stderr, "parse error in stream (%*s)\n",
+		log_message(SEV_DONTCARE, "parse error in stream (%*s)\n",
 		    (int)b.len, b.token);
 		// XXX return the undecoded stream (p->ast)?
 	}
@@ -5406,11 +5412,11 @@ kstream(HAllocator *mm__, const HParsedToken *x, void *env)
 
 fail:
 	if (v == NULL)
-		fprintf(stderr, "stream /Length missing\n");
+		log_message(SEV_DONTCARE, "stream /Length missing\n");
 	else if (v -> token_type != TT_SINT)
-		fprintf(stderr, "stream /Length not an integer\n");
+		log_message(SEV_DONTCARE, "stream /Length not an integer\n");
 	else if (v < 0)
-		fprintf(stderr, "stream /Length negative\n");
+		log_message(SEV_DONTCARE, "stream /Length negative\n");
 
 	//h_pprintln(stderr, p);	// XXX debug
 	return p_fail;
@@ -5624,7 +5630,7 @@ p_objstm__m(HAllocator *mm__, const Dict *dict)
 	v = dictentry(dict, "N");
 	if (v == NULL || v->token_type != TT_SINT || v->sint < 0 ||
 	    (uint64_t)v->sint > SIZE_MAX) {
-		fprintf(stderr, "p_objstm__m: missing /N on object stream\n");
+		log_message(SEV_DONTCARE, "p_objstm__m: missing /N on object stream\n");
 		return p_fail;
 	}
 	N = v->sint;
@@ -5720,14 +5726,15 @@ parse_xrefs(struct Env *aux)
 			break;
 	}
 	if (res == NULL) {
-		fprintf(stderr, "VIOLATION[5]: startxref not found\n");
+		/* Severity should be SEV_DONTCARE, but we're simulating act_viol/VIOL() here */
+		log_message(5, "VIOLATION[5]: startxref not found\n");
 		return;
 	}
 	offset = H_INDEX_UINT(res->ast, 0);
 
 	// verify the offset recovered is bounded to be in the file
 	if ( (offset <=0) || (offset >= aux->sz) ) {
-		fprintf(stderr, "VIOLATION[5]: Invalid xref table offset = %ld. Valid range <0, %ld>\n",
+		log_message(5, "VIOLATION[5]: Invalid xref table offset = %ld. Valid range <0, %ld>\n",
 				offset, aux->sz);
 		return;
 	}
@@ -5739,7 +5746,7 @@ parse_xrefs(struct Env *aux)
 		p = h_right(h_seek(offset * 8, SEEK_SET), p_xref);	// XXX
 		res = h_parse(p, input, sz);
 		if (res == NULL || res->ast == NULL || H_INDEX_TOKEN(res->ast, 0) == NULL) {
-			fprintf(stderr, "VIOLATION[5]: error parsing xref section at "
+			log_message(5, "VIOLATION[5]: error parsing xref section at "
 			    "position %zu (%#zx)\n", offset, offset);
 			return;
 		}
@@ -5760,7 +5767,7 @@ parse_xrefs(struct Env *aux)
 			trailer = H_CAST(Dict, trailer_t);
 			const HParsedToken *size_t =  dictentry(trailer, "Size");
 			if (size_t == NULL || size_t->token_type != TT_SINT) {
-				fprintf(stderr, "VIOLATION[5]: error parsing trailer section!"
+				log_message(5, "VIOLATION[5]: error parsing trailer section!"
 							    "Missing or malformed -Size- field\n");
 				return;
 			}
@@ -5775,11 +5782,11 @@ parse_xrefs(struct Env *aux)
 		if (tok == NULL)
 			break;
 		if (tok->token_type != TT_SINT) {
-			fprintf(stderr, "%s: /Prev not an integer\n", infile);
+			log_message(SEV_DONTCARE, "%s: /Prev not an integer\n", infile);
 			break;
 		}
 		if (tok->sint < 0) {
-			fprintf(stderr, "%s: /Prev negative\n", infile);
+			log_message(SEV_DONTCARE, "%s: /Prev negative\n", infile);
 			break;
 		}
 
@@ -5793,7 +5800,7 @@ parse_xrefs(struct Env *aux)
 		if ((uint64_t)tok->sint >= offset)
 			nfwd++;
 		if (nfwd > 1) {
-			fprintf(stderr, "%s: /Prev pointer of xref section at "
+			log_message(SEV_DONTCARE, "%s: /Prev pointer of xref section at "
 			    "%zu (%#zx) points forward\n", infile, offset,
 			    offset);
 			break;
@@ -5804,9 +5811,9 @@ parse_xrefs(struct Env *aux)
 
 
 
-	// Make sure we parsed a valid trailer section
+	/* Make sure we parsed a valid trailer section */
 	if (! trailer) {
-		fprintf(stderr, "VIOLATION[7]: Invalid Trailer Section or Trailer Section not found\n");
+		log_message(7, "VIOLATION[7]: Invalid Trailer Section or Trailer Section not found\n");
 		return;
 	}
 
@@ -5819,7 +5826,7 @@ parse_xrefs(struct Env *aux)
 
 
 	if (n > maxObjNum) {
-		fprintf(stderr, "%s: Number of xrefs found -%ld- "
+		log_message(SEV_DONTCARE, "%s: Number of xrefs found -%ld- "
 						"Greater than specified /Size -%ld-.\n"
 						"Ignoring objects numbered greater than -%ld-!\n",
 						infile, n, maxObjNum, n);