diff --git a/borb/io/write/any_object_transformer.py b/borb/io/write/any_object_transformer.py index e2634e08d..251a36b9f 100644 --- a/borb/io/write/any_object_transformer.py +++ b/borb/io/write/any_object_transformer.py @@ -11,6 +11,9 @@ from borb.io.read.types import AnyPDFType from borb.io.write.ascii_art.ascii_art_transformer import ASCIIArtTransformer from borb.io.write.document.document_transformer import DocumentTransformer +from borb.io.write.document.information_dictionary_transformer import ( + InformationDictionaryTransformer, +) from borb.io.write.image.image_transformer import ImageTransformer from borb.io.write.object.array_transformer import ArrayTransformer from borb.io.write.object.dictionary_transformer import DictionaryTransformer @@ -45,6 +48,7 @@ def __init__(self): self.add_child_transformer(XREFTransformer()) self.add_child_transformer(PagesTransformer()) self.add_child_transformer(PageTransformer()) + self.add_child_transformer(InformationDictionaryTransformer()) # object types self.add_child_transformer(ArrayTransformer()) self.add_child_transformer(StreamTransformer()) diff --git a/borb/io/write/ascii_art/ascii_logo.txt b/borb/io/write/ascii_art/ascii_logo.txt index aa8139c08..0413cb23e 100644 --- a/borb/io/write/ascii_art/ascii_logo.txt +++ b/borb/io/write/ascii_art/ascii_logo.txt @@ -1,2 +1,2 @@ -borb version 2.0.12 +borb version 2.0.13 Joris Schellekens diff --git a/borb/io/write/document/document_transformer.py b/borb/io/write/document/document_transformer.py index da7472614..ad1beed51 100644 --- a/borb/io/write/document/document_transformer.py +++ b/borb/io/write/document/document_transformer.py @@ -4,7 +4,6 @@ """ This implementation of WriteBaseTransformer is responsible for writing Document objects """ -import datetime import logging import random import typing @@ -16,7 +15,6 @@ HexadecimalString, List, Name, - String, ) from borb.io.write.transformer import ( Transformer, @@ -38,6 +36,16 @@ def can_be_transformed(self, any: AnyPDFType): """ return isinstance(any, Document) + def _build_empty_document_info_dictionary( + self, object_to_transform: Dictionary + ) -> None: + # create Info dictionary if needed + if "Info" not in object_to_transform["XRef"]["Trailer"]: + object_to_transform["XRef"]["Trailer"][Name("Info")] = Dictionary() + object_to_transform["XRef"]["Trailer"]["Info"].set_parent( + object_to_transform["XRef"]["Trailer"] + ) + def transform( self, object_to_transform: Any, @@ -58,65 +66,24 @@ def transform( # invalidate all references DocumentTransformer._invalidate_all_references(object_to_transform) - # create Info dictionary if needed - if "Info" not in object_to_transform["XRef"]["Trailer"]: - object_to_transform["XRef"]["Trailer"][Name("Info")] = Dictionary() - # set /ID random_id = HexadecimalString("%032x" % random.randrange(16 ** 32)) if "ID" not in object_to_transform["XRef"]["Trailer"]: - object_to_transform["XRef"]["Trailer"][ - Name("ID") - ] = List().set_can_be_referenced( # type: ignore [attr-defined] - False - ) + # fmt: off + object_to_transform["XRef"]["Trailer"][Name("ID")] = List().set_can_be_referenced(False) # type: ignore [attr-defined] object_to_transform["XRef"]["Trailer"]["ID"].append(random_id) object_to_transform["XRef"]["Trailer"]["ID"].append(random_id) + # fmt: on else: object_to_transform["XRef"]["Trailer"]["ID"][1] = random_id object_to_transform["XRef"]["Trailer"]["ID"].set_can_be_referenced(False) - # set CreationDate - modification_date = DocumentTransformer._timestamp_to_str() - if "CreationDate" not in object_to_transform["XRef"]["Trailer"][Name("Info")]: - object_to_transform["XRef"]["Trailer"][Name("Info")][ - Name("CreationDate") - ] = String(modification_date) - - # set ModDate - object_to_transform["XRef"]["Trailer"]["Info"][Name("ModDate")] = String( - modification_date - ) - - # set Producer - object_to_transform["XRef"]["Trailer"]["Info"][Name("Producer")] = String( - "borb" - ) - - # set OutputIntents - # fmt: off - rgb_output_intent: Dictionary = Dictionary() - rgb_output_intent[Name("Type")] = Name("OutputIntent") - rgb_output_intent[Name("S")] = Name("GTS_PDFA1") - rgb_output_intent[Name("OutputConditionIdentifier")] = String("sRGB") - rgb_output_intent[Name("RegistryName")] = String("http://www.color.org") - rgb_output_intent[Name("Info")] = String("Creator:HP Manufacturer:IEC Model:sRGB") - # object_to_transform["XRef"]["Trailer"]["Root"][Name("OutputIntents")] = List() - # object_to_transform["XRef"]["Trailer"]["Root"][Name("OutputIntents")].append(rgb_output_intent) - # fmt: on + # \Info + self._build_empty_document_info_dictionary(object_to_transform) # transform XREF self.get_root_transformer().transform(object_to_transform["XRef"], context) - @staticmethod - def _timestamp_to_str() -> str: - timestamp_str = "D:" - now = datetime.datetime.now() - for n in [now.year, now.month, now.day, now.hour, now.minute, now.second]: - timestamp_str += "{0:02}".format(n) - timestamp_str += "Z00" - return timestamp_str - @staticmethod def _invalidate_all_references(object: AnyPDFType) -> None: objects_done: typing.List[AnyPDFType] = [] diff --git a/borb/io/write/document/information_dictionary_transformer.py b/borb/io/write/document/information_dictionary_transformer.py new file mode 100644 index 000000000..f196a334a --- /dev/null +++ b/borb/io/write/document/information_dictionary_transformer.py @@ -0,0 +1,284 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- + +""" +This implementation of WriteBaseTransformer is responsible for writing \Info Dictionary objects +""" +import xml.etree.ElementTree as ET +import datetime +import logging +import random +import typing +from typing import Any, Optional + +from borb.io.read.types import ( + AnyPDFType, + Dictionary, + Name, + String, + Stream, + Decimal as bDecimal, +) +from borb.io.write.object.dictionary_transformer import DictionaryTransformer +from borb.io.write.object.stream_transformer import StreamTransformer +from borb.io.write.transformer import ( + Transformer, + WriteTransformerState, +) +from borb.pdf.document import Document +from borb.pdf.trailer.document_info import XMPDocumentInfo + +logger = logging.getLogger(__name__) + + +class InformationDictionaryTransformer(Transformer): + """ + This implementation of WriteBaseTransformer is responsible for writing \Info Dictionary objects + """ + + def can_be_transformed(self, any: AnyPDFType): + if not isinstance(any, Dictionary): + return False + parent: AnyPDFType = any.get_parent() + return ( + isinstance(parent, Dictionary) + and "Info" in parent + and parent["Info"] == any + ) + + def _consolidate_xmp_and_info_dictionary(self, document: Document) -> Dictionary: + new_info_dictionary: Dictionary = Dictionary() + + # get \Info Dictionary + if ( + "XRef" in document + and "Trailer" in document["XRef"] + and "Info" in document["XRef"]["Trailer"] + and isinstance(document["XRef"]["Trailer"]["Info"], Dictionary) + ): + info_dictionary: Dictionary = document["XRef"]["Trailer"]["Info"] + for k, v in info_dictionary.items(): + new_info_dictionary[k] = v + + # get XMP \Metadata + if ( + "XRef" in document + and "Trailer" in document["XRef"] + and "Root" in document["XRef"]["Trailer"] + and "Metadata" in document["XRef"]["Trailer"]["Root"] + and isinstance(document["XRef"]["Trailer"]["Root"]["Metadata"], ET.Element) + ): + xmp_document_info: XMPDocumentInfo = document.get_xmp_document_info() + for k,v in {Name("Title"):xmp_document_info.get_title(), + Name("Author"):xmp_document_info.get_author(), + Name("Subject"):xmp_document_info.get_subject(), + Name("Keywords"):xmp_document_info.get_keywords(), + Name("Creator"):xmp_document_info.get_creator(), + Name("Producer"):xmp_document_info.get_producer(), + Name("CreationDate"):xmp_document_info.get_creation_date(), + Name("ModDate"):xmp_document_info.get_modification_date()}.items(): + if v is None: + continue + if k in ["CreationDate", "ModDate"]: + v = InformationDictionaryTransformer._convert_xmp_date_format_to_iso_8824_date_format(v) + new_info_dictionary[k] = String(v) + + # return + return new_info_dictionary + + @staticmethod + def now_as_iso_8824_date_format() -> str: + timestamp_str = "D:" + now = datetime.datetime.now() + for n in [now.year, now.month, now.day, now.hour, now.minute, now.second]: + timestamp_str += "{0:02}".format(n) + timestamp_str += "Z00" + return timestamp_str + + def _update_info_dictionary(self, info_dictionary: Dictionary) -> Dictionary: + + # set CreationDate + if "CreationDate" not in info_dictionary: + info_dictionary[Name("CreationDate")] = String( + InformationDictionaryTransformer.now_as_iso_8824_date_format() + ) + + # set ModDate + info_dictionary[Name("ModDate")] = String( + InformationDictionaryTransformer.now_as_iso_8824_date_format() + ) + + # set Producer + info_dictionary[Name("Producer")] = String("borb") + + return info_dictionary + + @staticmethod + def _convert_iso_8824_date_format_to_xmp_date_format(s: str) -> str: + try: + year: str = s[2:6] + month: str = s[6:8] + day: str = s[8:10] + hour: str = s[10:12] + minute: str = s[12:14] + second: str = s[14:16] + # fmt: off + return year + "-" + month + "-" + day + "T" + hour + ":" + minute + ":" + second + "+00:00" + # fmt: on + except: + return s + + @staticmethod + def _convert_xmp_date_format_to_iso_8824_date_format(s: str) -> str: + return s + + def _write_xmp_metadata_stream(self, info_dictionary: Dictionary) -> Stream: + random_id: str = "".join( + [ + random.choice("0123456789abcdefghijklmnopqrstuvwxyz") + for _ in range(0, 24) + ] + ) + s: str = '' % random_id + s += '\n' + s += '\n\t' + + # rdf:Description + d: typing.Dict[str, str] = { + "rdf:about": "", + "xmlns:dc": "http://purl.org/dc/elements/1.1/", + "xmlns:pdf": "http://ns.adobe.com/pdf/1.3/", + "xmlns:xmp": "http://ns.adobe.com/xap/1.0/", + "dc:format": "application/pdf", + } + if "Producer" in info_dictionary: + d["pdf:Producer"] = str(info_dictionary["Producer"]) + if "Keywords" in info_dictionary: + d["pdf:Keywords"] = str(info_dictionary["Keywords"]) + if "CreationDate" in info_dictionary: + # fmt: off + d["xmp:CreateDate"] = InformationDictionaryTransformer._convert_iso_8824_date_format_to_xmp_date_format(info_dictionary["CreationDate"]) + # fmt: on + if "Creator" in info_dictionary: + d["xmp:CreatorTool"] = str(info_dictionary["Creator"]) + if "ModDate" in info_dictionary: + # fmt: off + d["xmp:ModifyDate"] = InformationDictionaryTransformer._convert_iso_8824_date_format_to_xmp_date_format(info_dictionary["ModDate"]) + # fmt: on + s += ( + "\n\t\t" + ) + + # Author + if "Author" in info_dictionary: + s += ( + "\n\t\t\t\n\t\t\t\t\n\t\t\t\t\t" + + str(info_dictionary["Author"]) + + "\n\t\t\t\t\n\t\t\t" + ) + # Keywords + if "Keywords" in info_dictionary: + s += ( + "\n\t\t\t\n\t\t\t\t" + + "".join( + [ + ("\n\t\t\t\t\t" + x.strip() + "") + for x in str(info_dictionary["Keywords"]).split(" ") + ] + ) + + "\n\t\t\t\t\n\t\t\t" + ) + # Subject + # fmt: off + if "Subject" in info_dictionary: + s += '\n\t\t\t\n\t\t\t\t\n\t\t\t\t\t' + str(info_dictionary["Subject"]) + "\n\t\t\t\t\n\t\t\t" + # fmt: on + + # Title + # fmt: off + if "Title" in info_dictionary: + s += '\n\t\t\t\n\t\t\t\t\n\t\t\t\t\t' + str(info_dictionary["Title"]) + "\n\t\t\t\t\n\t\t\t" + # fmt: on + + # close + s += "\n\t\t" + s += "\n\t" + s += "\n" + s += '\n' + + # build Stream object + metadata_stream: Stream = Stream() + metadata_stream[Name("Type")] = Name("Metadata") + metadata_stream[Name("Subtype")] = Name("XML") + metadata_stream[Name("Bytes")] = bytes(s, "latin1") + metadata_stream[Name("Length")] = bDecimal(len(metadata_stream[Name("Bytes")])) + + # return + return metadata_stream + + def transform( + self, + object_to_transform: Any, + context: Optional[WriteTransformerState] = None, + ): + # get Document + document: Document = object_to_transform.get_root() + assert document is not None + assert isinstance(document, Document) + + # consolidate XMP \Metadata and \Info Dictionary + new_info_dictionary: Dictionary = self._consolidate_xmp_and_info_dictionary( + document + ) + + # update + self._update_info_dictionary(new_info_dictionary) + + # determine whether XMP \Metadata is needed + # fmt: off + has_xmp_metadata: bool = "XRef" in document \ + and "Trailer" in document["XRef"] \ + and "Root" in document["XRef"]["Trailer"] \ + and "Metadata" in document["XRef"]["Trailer"]["Root"] + needs_xmp_metadata = has_xmp_metadata or context.conformance_level in ["PDF/A-1a", "PDF/A-1b"] + # fmt: on + + if needs_xmp_metadata: + + # write XMP \Metadata + xmp_metadata_stream: Stream = self._write_xmp_metadata_stream( + new_info_dictionary + ) + document["XRef"]["Trailer"]["Root"][Name("Metadata")] = self.get_reference( + xmp_metadata_stream, context + ) + xmp_metadata_stream.set_parent(document["XRef"]["Trailer"]["Root"]) + + # delegate XMP \Metadata + for h in self.get_root_transformer()._handlers: + if isinstance(h, StreamTransformer) and h.can_be_transformed( + xmp_metadata_stream + ): + h.transform(xmp_metadata_stream, context) + break + + # write \Info + for k, v in new_info_dictionary.items(): + document["XRef"]["Trailer"][Name("Info")][k] = v + + # delegate \Info + for h in self.get_root_transformer()._handlers: + if isinstance(h, DictionaryTransformer) and h.can_be_transformed( + document["XRef"]["Trailer"][Name("Info")] + ): + h.transform(document["XRef"]["Trailer"][Name("Info")], context) + break + + # assert reference + assert document["XRef"]["Trailer"][Name("Info")].get_reference() is not None + assert ( + document["XRef"]["Trailer"][Name("Info")].get_reference().byte_offset + is not None + ) diff --git a/borb/io/write/reference/xref_transformer.py b/borb/io/write/reference/xref_transformer.py index 692efdc1d..7a7a46e3c 100644 --- a/borb/io/write/reference/xref_transformer.py +++ b/borb/io/write/reference/xref_transformer.py @@ -46,15 +46,18 @@ def transform( # we do this upfront because the normal write_dictionary_transformer will write the dictionary first, # and the references afterwards. This would cause the \Trailer dictionary to not be the last. trailer_out = Dictionary() + # /Root trailer_out[Name("Root")] = self.get_reference( object_to_transform["Trailer"]["Root"], context ) + # /Info if "Info" in object_to_transform["Trailer"]: trailer_out[Name("Info")] = self.get_reference( object_to_transform["Trailer"]["Info"], context ) + # /Size if ( "Trailer" in object_to_transform @@ -65,21 +68,22 @@ def transform( trailer_out[Name("Size")] = Decimal( 0 ) # we'll recalculate this later anyway + # /ID if "ID" in object_to_transform["Trailer"]: trailer_out[Name("ID")] = object_to_transform["Trailer"]["ID"] - # write /Root object - self.get_root_transformer().transform( - object_to_transform["Trailer"]["Root"], context - ) - # write /Info object if "Info" in object_to_transform["Trailer"]: self.get_root_transformer().transform( object_to_transform["Trailer"]["Info"], context ) + # write /Root object + self.get_root_transformer().transform( + object_to_transform["Trailer"]["Root"], context + ) + # write /XREF start_of_xref = context.destination.tell() context.destination.write(bytes("xref\n", "latin1")) diff --git a/borb/io/write/transformer.py b/borb/io/write/transformer.py index 8745d626f..9798f4b4c 100644 --- a/borb/io/write/transformer.py +++ b/borb/io/write/transformer.py @@ -26,6 +26,7 @@ def __init__( self, destination: Optional[typing.Union[io.BufferedIOBase, io.RawIOBase]] = None, root_object: Optional[AnyPDFType] = None, + conformance_level: str = "", ): # fmt: off self.destination = destination # this is the destination to write to (file, byte-buffer, etc) @@ -33,7 +34,8 @@ def __init__( self.indirect_objects_by_id: typing.Dict[int, AnyPDFType] = {} # these are the indirect objects (by id) self.indirect_objects_by_hash: typing.Dict[int, typing.List[AnyPDFType]] = {} # these are the indirect objects (by hash) self.resolved_references: typing.List[Reference] = [] # these references have already been written - self.compression_level = 9 # default compression level + self.compression_level: int = 9 # default compression level + self.conformance_level: str = conformance_level # fmt: on diff --git a/borb/pdf/canvas/font/composite_font/font_type_0.py b/borb/pdf/canvas/font/composite_font/font_type_0.py index 0001aae9c..4c3794910 100644 --- a/borb/pdf/canvas/font/composite_font/font_type_0.py +++ b/borb/pdf/canvas/font/composite_font/font_type_0.py @@ -39,9 +39,10 @@ def _read_to_unicode(self): assert "DecodedBytes" in self["ToUnicode"] cmap_bytes: bytes = self["ToUnicode"]["DecodedBytes"] self._character_identifier_to_unicode_lookup = self._read_cmap(cmap_bytes) - self._unicode_lookup_to_character_identifier: typing.Dict[str, int] = { - v: k for k, v in self._character_identifier_to_unicode_lookup.items() - } + self._unicode_lookup_to_character_identifier: typing.Dict[str, int] = {} + for k, v in self._character_identifier_to_unicode_lookup.items(): + if v not in self._unicode_lookup_to_character_identifier: + self._unicode_lookup_to_character_identifier[v] = k def _read_encoding_cmap(self): if len(self._byte_to_char_identifier) > 0: diff --git a/borb/pdf/canvas/font/font.py b/borb/pdf/canvas/font/font.py index 1c2f38aef..3fcb3994d 100644 --- a/borb/pdf/canvas/font/font.py +++ b/borb/pdf/canvas/font/font.py @@ -104,10 +104,10 @@ def get_space_character_width_estimate(self) -> Decimal: return width # 2. MissingWidth if "FontDescriptor" in self and "MissingWidth" in self["FontDescriptor"]: - return self["FontDescriptor"]["MissingWidth"] + return self["FontDescriptor"]["MissingWidth"] / Decimal(2) # 3. AvgWidth if "FontDescriptor" in self and "AvgWidth" in self["FontDescriptor"]: - return self["FontDescriptor"]["AvgWidth"] + return self["FontDescriptor"]["AvgWidth"] / Decimal(2) # 3. default width if ( "DescendantFonts" in self @@ -115,7 +115,7 @@ def get_space_character_width_estimate(self) -> Decimal: and len(self["DescendantFonts"]) == 1 and "DW" in self["DescendantFonts"][0] ): - return self["DescendantFonts"][0]["DW"] + return self["DescendantFonts"][0]["DW"] / Decimal(2) # 4. other characters may be defined, which give us a clue # fmt: off char_to_space_width_ratio: typing.Dict[str, Decimal] = { diff --git a/borb/pdf/canvas/font/simple_font/true_type_font.py b/borb/pdf/canvas/font/simple_font/true_type_font.py index 20d7f4e50..521a62f3e 100644 --- a/borb/pdf/canvas/font/simple_font/true_type_font.py +++ b/borb/pdf/canvas/font/simple_font/true_type_font.py @@ -200,11 +200,7 @@ def _build_custom_cmap(ttf_font_file: TTFont) -> Stream: /CIDInit /ProcSet findresource begin 12 dict begin begincmap - /CIDSystemInfo - << /Registry (Adobe) - /Ordering (UCS) - /Supplement 0 - >> def + /CIDSystemInfo <> def /CMapName /Adobe-Identity-UCS def /CMapType 2 def 1 begincodespacerange @@ -215,9 +211,8 @@ def _build_custom_cmap(ttf_font_file: TTFont) -> Stream: # 1 beginbfchar # <0000> <0000> # endbfchar - pairs: typing.List[typing.Tuple[str, str]] = [] - for i, g in enumerate(ttf_font_file.glyphOrder): + for cid, g in enumerate(ttf_font_file.glyphOrder): g_unicode: str = toUnicode(g) if len(g_unicode) == 0: continue @@ -228,11 +223,12 @@ def _build_custom_cmap(ttf_font_file: TTFont) -> Stream: g_hex = hex(ord(g_unicode[0]))[2:] + hex(ord(g_unicode[1]))[2:] while len(g_hex) < 4: g_hex = "0" + g_hex - i_hex: str = hex(i)[2:] + i_hex: str = hex(cid)[2:] while len(i_hex) < 4: i_hex = "0" + i_hex pairs.append((i_hex, g_hex)) + # split in lots of 100 cmap_content: str = "" for i in range(0, len(pairs), 100): start_index: int = i @@ -243,12 +239,9 @@ def _build_custom_cmap(ttf_font_file: TTFont) -> Stream: cmap_content += "<%s> <%s>\n" % (pairs[j][0], pairs[j][1]) cmap_content += "endbfchar\n" - cmap_suffix: str = """ - endcmap - CMapName currentdict /CMap defineresource pop - end - end - """ + cmap_suffix: str = ( + "endcmap\nCMapName currentdict /CMap defineresource pop\nend\nend\n" + ) bts: bytes = (cmap_prefix + cmap_content + cmap_suffix).encode("latin1") to_unicode_stream = Stream() @@ -258,6 +251,22 @@ def _build_custom_cmap(ttf_font_file: TTFont) -> Stream: to_unicode_stream[Name("Length")] = pDecimal(len(bts)) return to_unicode_stream + @staticmethod + def _build_custom_widths_array(ttf_font_file: TTFont) -> List: + cmap = ttf_font_file.getBestCmap() + glyph_set = ttf_font_file.getGlyphSet() + widths_array: List = List() + for cid, g in enumerate(ttf_font_file.glyphOrder): + glyph_width: pDecimal = pDecimal(0) + try: + glyph_width = pDecimal(glyph_set[cmap[ord(toUnicode(g))]].width) + except: + pass + widths_array.append(pDecimal(cid)) + widths_array.append(List()) + widths_array[-1].append(pDecimal(glyph_width)) + return widths_array + @staticmethod def _type_0_font_from_file(ttf_font_file: TTFont) -> "Type0Font": type_0_font: Type0Font = Type0Font() @@ -283,22 +292,9 @@ def _type_0_font_from_file(ttf_font_file: TTFont) -> "Type0Font": descendant_font[Name("DW")] = pDecimal(250) # build W array - cmap = ttf_font_file["cmap"].getcmap(3, 1).cmap - glyph_set = ttf_font_file.getGlyphSet() - widths_array: List = List() - for cid, g in enumerate(ttf_font_file.glyphOrder): - glyph_width: float = 0 - try: - glyph_width = glyph_set[cmap[ord(toUnicode(g))]].width - except: - glyph_width = pDecimal(0) - # set DW based on the width of a space character - if toUnicode(g) == " ": - descendant_font[Name("DW")] = pDecimal(glyph_width) - widths_array.append(pDecimal(cid)) - widths_array.append(List()) - widths_array[-1].append(pDecimal(glyph_width)) - descendant_font[Name("W")] = widths_array + descendant_font[Name("W")] = TrueTypeFont._build_custom_widths_array( + ttf_font_file + ) descendant_font[Name("CIDToGIDMap")] = Name("Identity") # build CIDSystemInfo diff --git a/borb/pdf/canvas/layout/layout_element.py b/borb/pdf/canvas/layout/layout_element.py index 39a1d3dcf..c8ff29219 100644 --- a/borb/pdf/canvas/layout/layout_element.py +++ b/borb/pdf/canvas/layout/layout_element.py @@ -183,13 +183,11 @@ def _append_to_content_stream(self, page: "Page", instructions: str): # type: i # prepend whitespace if needed if len(content_stream[Name("DecodedBytes")]) != 0: - decoded_bytes_last_char: str = str( - content_stream["DecodedBytes"][-1:], encoding="latin1" - ) - if decoded_bytes_last_char not in [" ", "\t", "\n"] and instructions[ - 0 - ] not in [" ", "\t", "\n"]: + # fmt: off + decoded_bytes_last_char: str = str(content_stream["DecodedBytes"][-1:], encoding="latin1") + if decoded_bytes_last_char not in [" ", "\t", "\n"] and instructions[0] not in [" ", "\t", "\n"]: instructions = " " + instructions + # fmt: on content_stream[Name("DecodedBytes")] += instructions.encode("latin1") content_stream[Name("Bytes")] = zlib.compress(content_stream["DecodedBytes"], 9) diff --git a/borb/pdf/canvas/layout/text/chunk_of_text.py b/borb/pdf/canvas/layout/text/chunk_of_text.py index af505e70e..ac90f5a15 100644 --- a/borb/pdf/canvas/layout/text/chunk_of_text.py +++ b/borb/pdf/canvas/layout/text/chunk_of_text.py @@ -12,6 +12,7 @@ from borb.pdf.canvas.font.font import Font from borb.pdf.canvas.font.glyph_line import GlyphLine from borb.pdf.canvas.font.simple_font.font_type_1 import StandardType1Font +from borb.pdf.canvas.font.simple_font.true_type_font import TrueTypeFont from borb.pdf.canvas.geometry.rectangle import Rectangle from borb.pdf.canvas.layout.layout_element import Alignment, LayoutElement from borb.pdf.page.page import Page @@ -127,11 +128,13 @@ def _get_font_resource_name(self, font: Font, page: Page): def _write_text_bytes(self) -> str: hex_mode: bool = False + # check glyphs for c in self._text: if ord(c) != self._font.unicode_to_character_identifier(c): hex_mode = True break - if hex_mode: + # delegate + if hex_mode or isinstance(self._font, TrueTypeFont): return self._write_text_bytes_in_hex() else: return self._write_text_bytes_in_ascii() diff --git a/borb/pdf/pdf.py b/borb/pdf/pdf.py index f5bb77fb2..1b7438d87 100644 --- a/borb/pdf/pdf.py +++ b/borb/pdf/pdf.py @@ -19,6 +19,7 @@ from borb.io.write.any_object_transformer import ( AnyObjectTransformer as WriteAnyObjectTransformer, ) +from borb.io.write.transformer import WriteTransformerState from borb.pdf.canvas.event.event_listener import EventListener from borb.pdf.document import Document @@ -57,10 +58,20 @@ def loads( ) @staticmethod - def dumps(file: Union[io.BufferedIOBase, io.RawIOBase], document: Document) -> None: + def dumps( + file: Union[io.BufferedIOBase, io.RawIOBase], + document: Document, + conformance_level: typing.Optional[str] = None, + ) -> None: """ This function writes a Document to a byte-stream output (which may be presented as an io.BufferedIOBase o io.RawIOBase) """ WriteAnyObjectTransformer().transform( - object_to_transform=document, context=None, destination=file + object_to_transform=document, + context=WriteTransformerState( + root_object=document, + destination=file, + conformance_level=conformance_level or "", + ), + destination=file, ) diff --git a/borb/pdf/trailer/document_info.py b/borb/pdf/trailer/document_info.py index 4f6eb9ef3..9b271a169 100644 --- a/borb/pdf/trailer/document_info.py +++ b/borb/pdf/trailer/document_info.py @@ -176,11 +176,9 @@ def get_creation_date(self) -> Optional[str]: readable form (see 7.9.4, “Dates”). """ try: - return ( - self._document["XRef"]["Trailer"]["Root"]["Metadata"] - .findall(".//{*}CreateDate")[0] - .text - ) + return next(iter([v for k, v in + self._document["XRef"]["Trailer"]["Root"]["Metadata"].findall('.//{*}Description')[0].attrib.items() if + k.endswith('CreateDate')]), None) except: return None @@ -191,25 +189,31 @@ def get_modification_date(self) -> Optional[str]: most recently modified, in human-readable form (see 7.9.4, “Dates”). """ try: - return ( - self._document["XRef"]["Trailer"]["Root"]["Metadata"] - .findall(".//{*}ModifyDate")[0] - .text - ) + return next(iter([v for k, v in + self._document["XRef"]["Trailer"]["Root"]["Metadata"].findall('.//{*}Description')[0].attrib.items() if + k.endswith('ModifyDate')]), None) except: return None - def get_metadata_date(self) -> Optional[str]: - """ - (Optional) The date and time the metadata for this document was created, in human- - readable form (see 7.9.4, “Dates”). - """ + def get_author(self) -> Optional[str]: try: - return ( - self._document["XRef"]["Trailer"]["Root"]["Metadata"] - .findall(".//{*}MetadataDate")[0] - .text - ) + return self._document["XRef"]["Trailer"]["Root"]["Metadata"].findall('.//{*}creator')[0][0][0].text + except: + return None + + def get_producer(self) -> Optional[str]: + try: + return next(iter([v for k, v in + self._document["XRef"]["Trailer"]["Root"]["Metadata"].findall('.//{*}Description')[0].attrib.items() if + k.endswith('Producer')]), None) + except: + return None + + def get_keywords(self) -> Optional[str]: + try: + return next(iter([v for k, v in + self._document["XRef"]["Trailer"]["Root"]["Metadata"].findall('.//{*}Description')[0].attrib.items() if + k.endswith('Keywords')]), None) except: return None @@ -218,11 +222,7 @@ def get_title(self) -> Optional[str]: (Optional; PDF 1.1) The document’s title. """ try: - return ( - self._document["XRef"]["Trailer"]["Root"]["Metadata"] - .findall(".//{*}title")[0] - .text - ) + return self._document["XRef"]["Trailer"]["Root"]["Metadata"].findall(".//{*}title")[0][0][0].text except: return None @@ -232,10 +232,33 @@ def get_creator(self) -> Optional[str]: the name of the conforming product that created the original document from which it was converted. """ + try: + return next(iter([v for k, v in + self._document["XRef"]["Trailer"]["Root"]["Metadata"].findall('.//{*}Description')[0].attrib.items() if + k.endswith('CreatorTool')]), None) + except: + return None + + def get_subject(self) -> Optional[str]: + # TODO + try: + return self._document["XRef"]["Trailer"]["Root"]["Metadata"].findall(".//{*}description")[0][0][0].text + except: + return None + + # + # extra properties + # + + def get_metadata_date(self) -> Optional[str]: + """ + (Optional) The date and time the metadata for this document was created, in human- + readable form (see 7.9.4, “Dates”). + """ try: return ( self._document["XRef"]["Trailer"]["Root"]["Metadata"] - .findall(".//{*}creator")[0] + .findall(".//{*}MetadataDate")[0] .text ) except: diff --git a/release_notes.md b/release_notes.md index b3d6e0f2f..3b6a5e7d0 100644 --- a/release_notes.md +++ b/release_notes.md @@ -1,6 +1,21 @@ -# :mega: borb release 2.0.12 +# :mega: borb release 2.0.13 + +With this release, `borb` is one step closer to being able to write a PDF/A-1b document. +We still need to create an `\OutputIntents` Dictionary in the document to be fully compliant. +This is planned for the next release. This release features: -- Minor bugfix to `LayoutElement` to ensure `\Resources` dictionary gets initialized. -- Added asserts to ensure password-protected documents are handled properly. \ No newline at end of file +- Minor bugfix to estimating width of a space character + - Useful in text extraction +- Bugfix in `TrueTypeFont` to build a proper `\Widths` array and `cmap` +- Fixes in `XMPDocumentInfo` class + - Title + - Author + - Creator + - CreatorTool +- Separate logic that writes `\Info` `Dictionary` + - This class now also writes the `XMP` `\Metadata` when needed + - Enables PDF/A-1b + - Added tests for PDF/A-1b (preservation of metadata) + diff --git a/setup.py b/setup.py index fda93e8f5..c1fd85667 100644 --- a/setup.py +++ b/setup.py @@ -29,7 +29,7 @@ setuptools.setup( name="borb", - version="2.0.12", + version="2.0.13", author="Joris Schellekens", author_email="joris.schellekens.1989@gmail.com", description="borb is a library for reading, creating and manipulating PDF files in python.", diff --git a/tests/corpus/test_extract_text_expect_ground_truth.py b/tests/corpus/test_extract_text_expect_ground_truth.py index cd0d24f56..fb8f47c0f 100644 --- a/tests/corpus/test_extract_text_expect_ground_truth.py +++ b/tests/corpus/test_extract_text_expect_ground_truth.py @@ -321,8 +321,11 @@ def _build_document(self): def _test_single_document(self, file) -> typing.Dict[str, int]: txt_001 = "" - with open(self.corpus_dir / (file.stem + ".txt"), "r") as fh_001: - txt_001 = fh_001.read() + try: + with open(self.corpus_dir / (file.stem + ".txt"), "r") as fh_001: + txt_001 = fh_001.read() + except: + pass txt_002: str = "" with open(file, "rb") as fh_002: diff --git a/tests/output/test_add_all_rubber_stamp_annotations/output.pdf b/tests/output/test_add_all_rubber_stamp_annotations/output.pdf index ff87ce291..b606ea9d7 100644 Binary files a/tests/output/test_add_all_rubber_stamp_annotations/output.pdf and b/tests/output/test_add_all_rubber_stamp_annotations/output.pdf differ diff --git a/tests/output/test_add_circle_annotation/output.pdf b/tests/output/test_add_circle_annotation/output.pdf index 07499bf59..0574f72ea 100644 Binary files a/tests/output/test_add_circle_annotation/output.pdf and b/tests/output/test_add_circle_annotation/output.pdf differ diff --git a/tests/output/test_add_free_text_annotation/output_001.pdf b/tests/output/test_add_free_text_annotation/output_001.pdf index 76ec5229e..dd945dc9b 100644 Binary files a/tests/output/test_add_free_text_annotation/output_001.pdf and b/tests/output/test_add_free_text_annotation/output_001.pdf differ diff --git a/tests/output/test_add_free_text_annotation/output_002.pdf b/tests/output/test_add_free_text_annotation/output_002.pdf index 568b0786b..c80dfd0ba 100644 Binary files a/tests/output/test_add_free_text_annotation/output_002.pdf and b/tests/output/test_add_free_text_annotation/output_002.pdf differ diff --git a/tests/output/test_add_highlight_annotation/output_001.pdf b/tests/output/test_add_highlight_annotation/output_001.pdf index 6cbfc9ede..04fe829c1 100644 Binary files a/tests/output/test_add_highlight_annotation/output_001.pdf and b/tests/output/test_add_highlight_annotation/output_001.pdf differ diff --git a/tests/output/test_add_highlight_annotation/output_002.pdf b/tests/output/test_add_highlight_annotation/output_002.pdf index 99002f3e3..0ca88bbc4 100644 Binary files a/tests/output/test_add_highlight_annotation/output_002.pdf and b/tests/output/test_add_highlight_annotation/output_002.pdf differ diff --git a/tests/output/test_add_line_annotation/output_001.pdf b/tests/output/test_add_line_annotation/output_001.pdf index dcad40b3e..9367c762e 100644 Binary files a/tests/output/test_add_line_annotation/output_001.pdf and b/tests/output/test_add_line_annotation/output_001.pdf differ diff --git a/tests/output/test_add_line_annotation/output_002.pdf b/tests/output/test_add_line_annotation/output_002.pdf index f8c8e6a09..4fc1a3245 100644 Binary files a/tests/output/test_add_line_annotation/output_002.pdf and b/tests/output/test_add_line_annotation/output_002.pdf differ diff --git a/tests/output/test_add_outline/output_001.pdf b/tests/output/test_add_outline/output_001.pdf index b0a65aac3..b1a533008 100644 Binary files a/tests/output/test_add_outline/output_001.pdf and b/tests/output/test_add_outline/output_001.pdf differ diff --git a/tests/output/test_add_outline/output_002.pdf b/tests/output/test_add_outline/output_002.pdf index 47377b2d8..077847122 100644 Binary files a/tests/output/test_add_outline/output_002.pdf and b/tests/output/test_add_outline/output_002.pdf differ diff --git a/tests/output/test_add_polygon_annotation_using_line_art_factory/output.pdf b/tests/output/test_add_polygon_annotation_using_line_art_factory/output.pdf index 3f8085dc8..54829ce2b 100644 Binary files a/tests/output/test_add_polygon_annotation_using_line_art_factory/output.pdf and b/tests/output/test_add_polygon_annotation_using_line_art_factory/output.pdf differ diff --git a/tests/output/test_add_polyline_annotation_using_line_art_factory/output.pdf b/tests/output/test_add_polyline_annotation_using_line_art_factory/output.pdf index 54a568531..2a9b910d2 100644 Binary files a/tests/output/test_add_polyline_annotation_using_line_art_factory/output.pdf and b/tests/output/test_add_polyline_annotation_using_line_art_factory/output.pdf differ diff --git a/tests/output/test_add_redact_annotation/output_001.pdf b/tests/output/test_add_redact_annotation/output_001.pdf index 33bef5da6..e7d7f730d 100644 Binary files a/tests/output/test_add_redact_annotation/output_001.pdf and b/tests/output/test_add_redact_annotation/output_001.pdf differ diff --git a/tests/output/test_add_redact_annotation/output_002.pdf b/tests/output/test_add_redact_annotation/output_002.pdf index 200c402b9..de4c3a17f 100644 Binary files a/tests/output/test_add_redact_annotation/output_002.pdf and b/tests/output/test_add_redact_annotation/output_002.pdf differ diff --git a/tests/output/test_add_redact_annotation/output_003.pdf b/tests/output/test_add_redact_annotation/output_003.pdf index 4aed19ead..5b751ecd3 100644 Binary files a/tests/output/test_add_redact_annotation/output_003.pdf and b/tests/output/test_add_redact_annotation/output_003.pdf differ diff --git a/tests/output/test_add_redact_annotation/output_004.pdf b/tests/output/test_add_redact_annotation/output_004.pdf index a7daa1990..b8642634b 100644 Binary files a/tests/output/test_add_redact_annotation/output_004.pdf and b/tests/output/test_add_redact_annotation/output_004.pdf differ diff --git a/tests/output/test_add_redact_annotation/output_005.pdf b/tests/output/test_add_redact_annotation/output_005.pdf index 1b25dd355..f86aedb03 100644 Binary files a/tests/output/test_add_redact_annotation/output_005.pdf and b/tests/output/test_add_redact_annotation/output_005.pdf differ diff --git a/tests/output/test_add_remote_go_to_annotation/output_001.pdf b/tests/output/test_add_remote_go_to_annotation/output_001.pdf index f4e091ce2..ebe565dd1 100644 Binary files a/tests/output/test_add_remote_go_to_annotation/output_001.pdf and b/tests/output/test_add_remote_go_to_annotation/output_001.pdf differ diff --git a/tests/output/test_add_remote_go_to_annotation/output_002.pdf b/tests/output/test_add_remote_go_to_annotation/output_002.pdf index a3dab9918..b57ad875c 100644 Binary files a/tests/output/test_add_remote_go_to_annotation/output_002.pdf and b/tests/output/test_add_remote_go_to_annotation/output_002.pdf differ diff --git a/tests/output/test_add_square_annotation/output.pdf b/tests/output/test_add_square_annotation/output.pdf index c934b9932..50f7d368c 100644 Binary files a/tests/output/test_add_square_annotation/output.pdf and b/tests/output/test_add_square_annotation/output.pdf differ diff --git a/tests/output/test_add_square_annotation_in_free_space/output_001.pdf b/tests/output/test_add_square_annotation_in_free_space/output_001.pdf index 2b2688b92..912bf1c01 100644 Binary files a/tests/output/test_add_square_annotation_in_free_space/output_001.pdf and b/tests/output/test_add_square_annotation_in_free_space/output_001.pdf differ diff --git a/tests/output/test_add_square_annotation_in_free_space/output_002.pdf b/tests/output/test_add_square_annotation_in_free_space/output_002.pdf index 1bfcc2c3b..2f2bac9c1 100644 Binary files a/tests/output/test_add_square_annotation_in_free_space/output_002.pdf and b/tests/output/test_add_square_annotation_in_free_space/output_002.pdf differ diff --git a/tests/output/test_add_squiggle_annotation/output_001.pdf b/tests/output/test_add_squiggle_annotation/output_001.pdf index b5d433377..ad31244a2 100644 Binary files a/tests/output/test_add_squiggle_annotation/output_001.pdf and b/tests/output/test_add_squiggle_annotation/output_001.pdf differ diff --git a/tests/output/test_add_squiggle_annotation/output_002.pdf b/tests/output/test_add_squiggle_annotation/output_002.pdf index f5a022e39..475e9b113 100644 Binary files a/tests/output/test_add_squiggle_annotation/output_002.pdf and b/tests/output/test_add_squiggle_annotation/output_002.pdf differ diff --git a/tests/output/test_add_strikeout_annotation/output_001.pdf b/tests/output/test_add_strikeout_annotation/output_001.pdf index f91bbb2ce..57bb2673f 100644 Binary files a/tests/output/test_add_strikeout_annotation/output_001.pdf and b/tests/output/test_add_strikeout_annotation/output_001.pdf differ diff --git a/tests/output/test_add_strikeout_annotation/output_002.pdf b/tests/output/test_add_strikeout_annotation/output_002.pdf index 0fb90523b..5ed14ff17 100644 Binary files a/tests/output/test_add_strikeout_annotation/output_002.pdf and b/tests/output/test_add_strikeout_annotation/output_002.pdf differ diff --git a/tests/output/test_add_super_mario_annotation/output.pdf b/tests/output/test_add_super_mario_annotation/output.pdf index b59d63824..86f139463 100644 Binary files a/tests/output/test_add_super_mario_annotation/output.pdf and b/tests/output/test_add_super_mario_annotation/output.pdf differ diff --git a/tests/output/test_add_text_annotation/output_001.pdf b/tests/output/test_add_text_annotation/output_001.pdf index b8eefb7c1..d80d953b2 100644 Binary files a/tests/output/test_add_text_annotation/output_001.pdf and b/tests/output/test_add_text_annotation/output_001.pdf differ diff --git a/tests/output/test_add_text_annotation/output_002.pdf b/tests/output/test_add_text_annotation/output_002.pdf index 5e3980517..60362afcb 100644 Binary files a/tests/output/test_add_text_annotation/output_002.pdf and b/tests/output/test_add_text_annotation/output_002.pdf differ diff --git a/tests/output/test_add_underline_annotation/output_001.pdf b/tests/output/test_add_underline_annotation/output_001.pdf index b48660410..790726ea7 100644 Binary files a/tests/output/test_add_underline_annotation/output_001.pdf and b/tests/output/test_add_underline_annotation/output_001.pdf differ diff --git a/tests/output/test_add_underline_annotation/output_002.pdf b/tests/output/test_add_underline_annotation/output_002.pdf index 935b7edfb..e7a5f18e1 100644 Binary files a/tests/output/test_add_underline_annotation/output_002.pdf and b/tests/output/test_add_underline_annotation/output_002.pdf differ diff --git a/tests/output/test_analogous_color_scheme/output.pdf b/tests/output/test_analogous_color_scheme/output.pdf index 404170152..3746ae339 100644 Binary files a/tests/output/test_analogous_color_scheme/output.pdf and b/tests/output/test_analogous_color_scheme/output.pdf differ diff --git a/tests/output/test_append_embedded_file/output_001.pdf b/tests/output/test_append_embedded_file/output_001.pdf index b50fbee14..397f5e432 100644 Binary files a/tests/output/test_append_embedded_file/output_001.pdf and b/tests/output/test_append_embedded_file/output_001.pdf differ diff --git a/tests/output/test_append_embedded_file/output_002.pdf b/tests/output/test_append_embedded_file/output_002.pdf index 5277517ea..01d138713 100644 Binary files a/tests/output/test_append_embedded_file/output_002.pdf and b/tests/output/test_append_embedded_file/output_002.pdf differ diff --git a/tests/output/test_apply_redaction_annotations/output_001.pdf b/tests/output/test_apply_redaction_annotations/output_001.pdf index 6168010bd..4bed222de 100644 Binary files a/tests/output/test_apply_redaction_annotations/output_001.pdf and b/tests/output/test_apply_redaction_annotations/output_001.pdf differ diff --git a/tests/output/test_apply_redaction_annotations/output_002.pdf b/tests/output/test_apply_redaction_annotations/output_002.pdf index 680645f4c..ce9f988f1 100644 Binary files a/tests/output/test_apply_redaction_annotations/output_002.pdf and b/tests/output/test_apply_redaction_annotations/output_002.pdf differ diff --git a/tests/output/test_apply_redaction_annotations/output_003.pdf b/tests/output/test_apply_redaction_annotations/output_003.pdf index 0d800b76b..f45c2feac 100644 Binary files a/tests/output/test_apply_redaction_annotations/output_003.pdf and b/tests/output/test_apply_redaction_annotations/output_003.pdf differ diff --git a/tests/output/test_apply_redaction_annotations/output_004.pdf b/tests/output/test_apply_redaction_annotations/output_004.pdf index a2f18492b..b4b7af3c5 100644 Binary files a/tests/output/test_apply_redaction_annotations/output_004.pdf and b/tests/output/test_apply_redaction_annotations/output_004.pdf differ diff --git a/tests/output/test_apply_redaction_annotations/output_005.pdf b/tests/output/test_apply_redaction_annotations/output_005.pdf index 51d85ca6e..9da63a8f6 100644 Binary files a/tests/output/test_apply_redaction_annotations/output_005.pdf and b/tests/output/test_apply_redaction_annotations/output_005.pdf differ diff --git a/tests/output/test_apply_redaction_annotations/output_006.pdf b/tests/output/test_apply_redaction_annotations/output_006.pdf index 86929e509..c7a74ebc8 100644 Binary files a/tests/output/test_apply_redaction_annotations/output_006.pdf and b/tests/output/test_apply_redaction_annotations/output_006.pdf differ diff --git a/tests/output/test_browser_layout_inline_next_line/output.pdf b/tests/output/test_browser_layout_inline_next_line/output.pdf index cd69a40aa..b60af80af 100644 Binary files a/tests/output/test_browser_layout_inline_next_line/output.pdf and b/tests/output/test_browser_layout_inline_next_line/output.pdf differ diff --git a/tests/output/test_change_info_dictionary_author/output_001.pdf b/tests/output/test_change_info_dictionary_author/output_001.pdf index ffad24d03..3bc562d4b 100644 Binary files a/tests/output/test_change_info_dictionary_author/output_001.pdf and b/tests/output/test_change_info_dictionary_author/output_001.pdf differ diff --git a/tests/output/test_change_info_dictionary_author/output_002.pdf b/tests/output/test_change_info_dictionary_author/output_002.pdf index 48eebed7b..56a8c7ac9 100644 Binary files a/tests/output/test_change_info_dictionary_author/output_002.pdf and b/tests/output/test_change_info_dictionary_author/output_002.pdf differ diff --git a/tests/output/test_concat_documents/output_000.pdf b/tests/output/test_concat_documents/output_000.pdf index 280cd4291..d09f365c2 100644 Binary files a/tests/output/test_concat_documents/output_000.pdf and b/tests/output/test_concat_documents/output_000.pdf differ diff --git a/tests/output/test_concat_documents/output_001.pdf b/tests/output/test_concat_documents/output_001.pdf index f3da6786a..715c51bd5 100644 Binary files a/tests/output/test_concat_documents/output_001.pdf and b/tests/output/test_concat_documents/output_001.pdf differ diff --git a/tests/output/test_concat_documents/output_002.pdf b/tests/output/test_concat_documents/output_002.pdf index b48673849..9d5cb4a6f 100644 Binary files a/tests/output/test_concat_documents/output_002.pdf and b/tests/output/test_concat_documents/output_002.pdf differ diff --git a/tests/output/test_copy_document_compare_size/output.pdf b/tests/output/test_copy_document_compare_size/output.pdf index 87d687e00..a947d27f3 100644 Binary files a/tests/output/test_copy_document_compare_size/output.pdf and b/tests/output/test_copy_document_compare_size/output.pdf differ diff --git a/tests/output/test_copy_document_resize_images_compare_size/output.pdf b/tests/output/test_copy_document_resize_images_compare_size/output.pdf index a0ccdf45f..18036f258 100644 Binary files a/tests/output/test_copy_document_resize_images_compare_size/output.pdf and b/tests/output/test_copy_document_resize_images_compare_size/output.pdf differ diff --git a/tests/output/test_count_annotations/output_001.pdf b/tests/output/test_count_annotations/output_001.pdf index f81270b00..48d0e0e91 100644 Binary files a/tests/output/test_count_annotations/output_001.pdf and b/tests/output/test_count_annotations/output_001.pdf differ diff --git a/tests/output/test_count_annotations/output_002.pdf b/tests/output/test_count_annotations/output_002.pdf index 52c987229..eb6695b4e 100644 Binary files a/tests/output/test_count_annotations/output_002.pdf and b/tests/output/test_count_annotations/output_002.pdf differ diff --git a/tests/output/test_create_document_with_output_intent/output_001.pdf b/tests/output/test_create_document_with_output_intent/output_001.pdf index 9c288b378..6e6c8cd10 100644 Binary files a/tests/output/test_create_document_with_output_intent/output_001.pdf and b/tests/output/test_create_document_with_output_intent/output_001.pdf differ diff --git a/tests/output/test_detect_table/input_000.pdf b/tests/output/test_detect_table/input_000.pdf index b3bbdb5da..c633e6990 100644 Binary files a/tests/output/test_detect_table/input_000.pdf and b/tests/output/test_detect_table/input_000.pdf differ diff --git a/tests/output/test_detect_table/input_001.pdf b/tests/output/test_detect_table/input_001.pdf index dba861d8a..7286be7b2 100644 Binary files a/tests/output/test_detect_table/input_001.pdf and b/tests/output/test_detect_table/input_001.pdf differ diff --git a/tests/output/test_detect_table/input_002.pdf b/tests/output/test_detect_table/input_002.pdf index 7d9ec9f6d..ae051d240 100644 Binary files a/tests/output/test_detect_table/input_002.pdf and b/tests/output/test_detect_table/input_002.pdf differ diff --git a/tests/output/test_detect_table/input_003.pdf b/tests/output/test_detect_table/input_003.pdf index b5f5e9f31..7bf7caa29 100644 Binary files a/tests/output/test_detect_table/input_003.pdf and b/tests/output/test_detect_table/input_003.pdf differ diff --git a/tests/output/test_detect_table/input_004.pdf b/tests/output/test_detect_table/input_004.pdf index 6fff7e534..1b6f6ab86 100644 Binary files a/tests/output/test_detect_table/input_004.pdf and b/tests/output/test_detect_table/input_004.pdf differ diff --git a/tests/output/test_detect_table/input_005.pdf b/tests/output/test_detect_table/input_005.pdf index ea6b1815e..df25bd48b 100644 Binary files a/tests/output/test_detect_table/input_005.pdf and b/tests/output/test_detect_table/input_005.pdf differ diff --git a/tests/output/test_detect_table/input_006.pdf b/tests/output/test_detect_table/input_006.pdf index 8a85989a5..5d3804bc0 100644 Binary files a/tests/output/test_detect_table/input_006.pdf and b/tests/output/test_detect_table/input_006.pdf differ diff --git a/tests/output/test_detect_table/output_000.pdf b/tests/output/test_detect_table/output_000.pdf index b882e2862..a8ba991d1 100644 Binary files a/tests/output/test_detect_table/output_000.pdf and b/tests/output/test_detect_table/output_000.pdf differ diff --git a/tests/output/test_detect_table/output_001.pdf b/tests/output/test_detect_table/output_001.pdf index 6b7634222..1f9f34654 100644 Binary files a/tests/output/test_detect_table/output_001.pdf and b/tests/output/test_detect_table/output_001.pdf differ diff --git a/tests/output/test_detect_table/output_002.pdf b/tests/output/test_detect_table/output_002.pdf index 59b0f2239..068ad0359 100644 Binary files a/tests/output/test_detect_table/output_002.pdf and b/tests/output/test_detect_table/output_002.pdf differ diff --git a/tests/output/test_detect_table/output_003.pdf b/tests/output/test_detect_table/output_003.pdf index 2c95453ea..81c7f4515 100644 Binary files a/tests/output/test_detect_table/output_003.pdf and b/tests/output/test_detect_table/output_003.pdf differ diff --git a/tests/output/test_detect_table/output_004.pdf b/tests/output/test_detect_table/output_004.pdf index 9afe40f7c..030e0020e 100644 Binary files a/tests/output/test_detect_table/output_004.pdf and b/tests/output/test_detect_table/output_004.pdf differ diff --git a/tests/output/test_detect_table/output_005.pdf b/tests/output/test_detect_table/output_005.pdf index fe36f91b3..c08888a8e 100644 Binary files a/tests/output/test_detect_table/output_005.pdf and b/tests/output/test_detect_table/output_005.pdf differ diff --git a/tests/output/test_detect_table/output_006.pdf b/tests/output/test_detect_table/output_006.pdf index fa8760465..6fceeb9a8 100644 Binary files a/tests/output/test_detect_table/output_006.pdf and b/tests/output/test_detect_table/output_006.pdf differ diff --git a/tests/output/test_digit_placement_ubuntu_font/output_001.pdf b/tests/output/test_digit_placement_ubuntu_font/output_001.pdf new file mode 100644 index 000000000..f463eef35 Binary files /dev/null and b/tests/output/test_digit_placement_ubuntu_font/output_001.pdf differ diff --git a/tests/output/test_digit_placement_ubuntu_font/output_001.png b/tests/output/test_digit_placement_ubuntu_font/output_001.png new file mode 100644 index 000000000..35cf41b38 Binary files /dev/null and b/tests/output/test_digit_placement_ubuntu_font/output_001.png differ diff --git a/tests/output/test_export_html_to_pdf/example_html_input_000.pdf b/tests/output/test_export_html_to_pdf/example_html_input_000.pdf index 98a832a5b..ba6397db1 100644 Binary files a/tests/output/test_export_html_to_pdf/example_html_input_000.pdf and b/tests/output/test_export_html_to_pdf/example_html_input_000.pdf differ diff --git a/tests/output/test_export_html_to_pdf/example_html_input_001.pdf b/tests/output/test_export_html_to_pdf/example_html_input_001.pdf index 6fa887705..507bb6694 100644 Binary files a/tests/output/test_export_html_to_pdf/example_html_input_001.pdf and b/tests/output/test_export_html_to_pdf/example_html_input_001.pdf differ diff --git a/tests/output/test_export_html_to_pdf/example_html_input_002.pdf b/tests/output/test_export_html_to_pdf/example_html_input_002.pdf index 570e1db48..4dcd742d5 100644 Binary files a/tests/output/test_export_html_to_pdf/example_html_input_002.pdf and b/tests/output/test_export_html_to_pdf/example_html_input_002.pdf differ diff --git a/tests/output/test_export_html_to_pdf/example_html_input_003.pdf b/tests/output/test_export_html_to_pdf/example_html_input_003.pdf index 716c9c40d..75de67846 100644 Binary files a/tests/output/test_export_html_to_pdf/example_html_input_003.pdf and b/tests/output/test_export_html_to_pdf/example_html_input_003.pdf differ diff --git a/tests/output/test_export_html_to_pdf/example_html_input_004.pdf b/tests/output/test_export_html_to_pdf/example_html_input_004.pdf index 6ec223537..d71acb9e5 100644 Binary files a/tests/output/test_export_html_to_pdf/example_html_input_004.pdf and b/tests/output/test_export_html_to_pdf/example_html_input_004.pdf differ diff --git a/tests/output/test_export_html_to_pdf/example_html_input_005.pdf b/tests/output/test_export_html_to_pdf/example_html_input_005.pdf index dd46db674..430bdc721 100644 Binary files a/tests/output/test_export_html_to_pdf/example_html_input_005.pdf and b/tests/output/test_export_html_to_pdf/example_html_input_005.pdf differ diff --git a/tests/output/test_export_html_to_pdf/example_html_input_006.pdf b/tests/output/test_export_html_to_pdf/example_html_input_006.pdf index df8b8fc56..72170cf3d 100644 Binary files a/tests/output/test_export_html_to_pdf/example_html_input_006.pdf and b/tests/output/test_export_html_to_pdf/example_html_input_006.pdf differ diff --git a/tests/output/test_export_html_to_pdf/example_html_input_007.pdf b/tests/output/test_export_html_to_pdf/example_html_input_007.pdf index ca8b21773..28c8a1695 100644 Binary files a/tests/output/test_export_html_to_pdf/example_html_input_007.pdf and b/tests/output/test_export_html_to_pdf/example_html_input_007.pdf differ diff --git a/tests/output/test_export_html_to_pdf/example_html_input_008.pdf b/tests/output/test_export_html_to_pdf/example_html_input_008.pdf index be7662ad4..eedd43665 100644 Binary files a/tests/output/test_export_html_to_pdf/example_html_input_008.pdf and b/tests/output/test_export_html_to_pdf/example_html_input_008.pdf differ diff --git a/tests/output/test_export_html_to_pdf/example_html_input_009.pdf b/tests/output/test_export_html_to_pdf/example_html_input_009.pdf index 800eaf813..9725f1062 100644 Binary files a/tests/output/test_export_html_to_pdf/example_html_input_009.pdf and b/tests/output/test_export_html_to_pdf/example_html_input_009.pdf differ diff --git a/tests/output/test_export_html_to_pdf/example_html_input_010.pdf b/tests/output/test_export_html_to_pdf/example_html_input_010.pdf index 6e424508b..87a23b5f8 100644 Binary files a/tests/output/test_export_html_to_pdf/example_html_input_010.pdf and b/tests/output/test_export_html_to_pdf/example_html_input_010.pdf differ diff --git a/tests/output/test_export_html_to_pdf/example_html_input_011.pdf b/tests/output/test_export_html_to_pdf/example_html_input_011.pdf index db1185b29..1caeb0d6f 100644 Binary files a/tests/output/test_export_html_to_pdf/example_html_input_011.pdf and b/tests/output/test_export_html_to_pdf/example_html_input_011.pdf differ diff --git a/tests/output/test_export_html_to_pdf/example_html_input_012.pdf b/tests/output/test_export_html_to_pdf/example_html_input_012.pdf index 14c101343..9c5a30b6d 100644 Binary files a/tests/output/test_export_html_to_pdf/example_html_input_012.pdf and b/tests/output/test_export_html_to_pdf/example_html_input_012.pdf differ diff --git a/tests/output/test_export_html_to_pdf/example_html_input_013.pdf b/tests/output/test_export_html_to_pdf/example_html_input_013.pdf index 6073bce7a..19bbd926c 100644 Binary files a/tests/output/test_export_html_to_pdf/example_html_input_013.pdf and b/tests/output/test_export_html_to_pdf/example_html_input_013.pdf differ diff --git a/tests/output/test_export_html_to_pdf/example_html_input_014.pdf b/tests/output/test_export_html_to_pdf/example_html_input_014.pdf index c8c8dc7f3..7829c42b5 100644 Binary files a/tests/output/test_export_html_to_pdf/example_html_input_014.pdf and b/tests/output/test_export_html_to_pdf/example_html_input_014.pdf differ diff --git a/tests/output/test_export_markdown_to_pdf/example-markdown-input-001.md.pdf b/tests/output/test_export_markdown_to_pdf/example-markdown-input-001.md.pdf index aa63553f9..a45ff8267 100644 Binary files a/tests/output/test_export_markdown_to_pdf/example-markdown-input-001.md.pdf and b/tests/output/test_export_markdown_to_pdf/example-markdown-input-001.md.pdf differ diff --git a/tests/output/test_export_markdown_to_pdf/example-markdown-input-002.md.pdf b/tests/output/test_export_markdown_to_pdf/example-markdown-input-002.md.pdf index d557ce951..4e0c757bd 100644 Binary files a/tests/output/test_export_markdown_to_pdf/example-markdown-input-002.md.pdf and b/tests/output/test_export_markdown_to_pdf/example-markdown-input-002.md.pdf differ diff --git a/tests/output/test_export_markdown_to_pdf/example-markdown-input-003.md.pdf b/tests/output/test_export_markdown_to_pdf/example-markdown-input-003.md.pdf index e5935f38e..46fb3e5bd 100644 Binary files a/tests/output/test_export_markdown_to_pdf/example-markdown-input-003.md.pdf and b/tests/output/test_export_markdown_to_pdf/example-markdown-input-003.md.pdf differ diff --git a/tests/output/test_export_markdown_to_pdf/example-markdown-input-004.md.pdf b/tests/output/test_export_markdown_to_pdf/example-markdown-input-004.md.pdf index a48c8e8dc..a4a6a8a9f 100644 Binary files a/tests/output/test_export_markdown_to_pdf/example-markdown-input-004.md.pdf and b/tests/output/test_export_markdown_to_pdf/example-markdown-input-004.md.pdf differ diff --git a/tests/output/test_export_markdown_to_pdf/example-markdown-input-005.md.pdf b/tests/output/test_export_markdown_to_pdf/example-markdown-input-005.md.pdf index 4d404b36b..2ef95127b 100644 Binary files a/tests/output/test_export_markdown_to_pdf/example-markdown-input-005.md.pdf and b/tests/output/test_export_markdown_to_pdf/example-markdown-input-005.md.pdf differ diff --git a/tests/output/test_export_markdown_to_pdf/example-markdown-input-006.md.pdf b/tests/output/test_export_markdown_to_pdf/example-markdown-input-006.md.pdf index f71c063ae..4ba32780d 100644 Binary files a/tests/output/test_export_markdown_to_pdf/example-markdown-input-006.md.pdf and b/tests/output/test_export_markdown_to_pdf/example-markdown-input-006.md.pdf differ diff --git a/tests/output/test_export_markdown_to_pdf/example-markdown-input-007.md.pdf b/tests/output/test_export_markdown_to_pdf/example-markdown-input-007.md.pdf index b67da80c0..c31f846ea 100644 Binary files a/tests/output/test_export_markdown_to_pdf/example-markdown-input-007.md.pdf and b/tests/output/test_export_markdown_to_pdf/example-markdown-input-007.md.pdf differ diff --git a/tests/output/test_export_markdown_to_pdf/example-markdown-input-008.md.pdf b/tests/output/test_export_markdown_to_pdf/example-markdown-input-008.md.pdf index 9a1757070..643583d8c 100644 Binary files a/tests/output/test_export_markdown_to_pdf/example-markdown-input-008.md.pdf and b/tests/output/test_export_markdown_to_pdf/example-markdown-input-008.md.pdf differ diff --git a/tests/output/test_export_markdown_to_pdf/example-markdown-input-009.md.pdf b/tests/output/test_export_markdown_to_pdf/example-markdown-input-009.md.pdf index aed2a4729..288c26094 100644 Binary files a/tests/output/test_export_markdown_to_pdf/example-markdown-input-009.md.pdf and b/tests/output/test_export_markdown_to_pdf/example-markdown-input-009.md.pdf differ diff --git a/tests/output/test_export_markdown_to_pdf/example-markdown-input-010.md.pdf b/tests/output/test_export_markdown_to_pdf/example-markdown-input-010.md.pdf index aa3a747d8..2e8aa8ba6 100644 Binary files a/tests/output/test_export_markdown_to_pdf/example-markdown-input-010.md.pdf and b/tests/output/test_export_markdown_to_pdf/example-markdown-input-010.md.pdf differ diff --git a/tests/output/test_export_to_mp3/output.mp3 b/tests/output/test_export_to_mp3/output.mp3 index 32aa57056..769c46f5a 100644 Binary files a/tests/output/test_export_to_mp3/output.mp3 and b/tests/output/test_export_to_mp3/output.mp3 differ diff --git a/tests/output/test_extract_colors/output_001.pdf b/tests/output/test_extract_colors/output_001.pdf index ba697a3aa..8cca98bdf 100644 Binary files a/tests/output/test_extract_colors/output_001.pdf and b/tests/output/test_extract_colors/output_001.pdf differ diff --git a/tests/output/test_extract_colors/output_002.pdf b/tests/output/test_extract_colors/output_002.pdf index 263546ef5..13a52f1bd 100644 Binary files a/tests/output/test_extract_colors/output_002.pdf and b/tests/output/test_extract_colors/output_002.pdf differ diff --git a/tests/output/test_extract_colors/output_002.png b/tests/output/test_extract_colors/output_002.png index 435bf5921..53fc5a2e6 100644 Binary files a/tests/output/test_extract_colors/output_002.png and b/tests/output/test_extract_colors/output_002.png differ diff --git a/tests/output/test_extract_courier_text/output_001.pdf b/tests/output/test_extract_courier_text/output_001.pdf index 6620a5ab8..0dcd81e3f 100644 Binary files a/tests/output/test_extract_courier_text/output_001.pdf and b/tests/output/test_extract_courier_text/output_001.pdf differ diff --git a/tests/output/test_extract_font_names/output_001.pdf b/tests/output/test_extract_font_names/output_001.pdf index d8ce8dc7d..ff7a5b65f 100644 Binary files a/tests/output/test_extract_font_names/output_001.pdf and b/tests/output/test_extract_font_names/output_001.pdf differ diff --git a/tests/output/test_extract_font_names/output_002.pdf b/tests/output/test_extract_font_names/output_002.pdf index ea91ef18d..5c07408ce 100644 Binary files a/tests/output/test_extract_font_names/output_002.pdf and b/tests/output/test_extract_font_names/output_002.pdf differ diff --git a/tests/output/test_extract_keywords/output_001.pdf b/tests/output/test_extract_keywords/output_001.pdf index cb8b066c4..a313e85f8 100644 Binary files a/tests/output/test_extract_keywords/output_001.pdf and b/tests/output/test_extract_keywords/output_001.pdf differ diff --git a/tests/output/test_extract_keywords/output_002.pdf b/tests/output/test_extract_keywords/output_002.pdf index ae3f71b18..d87dad8f4 100644 Binary files a/tests/output/test_extract_keywords/output_002.pdf and b/tests/output/test_extract_keywords/output_002.pdf differ diff --git a/tests/output/test_extract_keywords/output_003.pdf b/tests/output/test_extract_keywords/output_003.pdf index 0b8ba2ea2..6753945af 100644 Binary files a/tests/output/test_extract_keywords/output_003.pdf and b/tests/output/test_extract_keywords/output_003.pdf differ diff --git a/tests/output/test_extract_red_text/output_001.pdf b/tests/output/test_extract_red_text/output_001.pdf index 3edaa8fbe..16070d5eb 100644 Binary files a/tests/output/test_extract_red_text/output_001.pdf and b/tests/output/test_extract_red_text/output_001.pdf differ diff --git a/tests/output/test_extract_regex/output_001.pdf b/tests/output/test_extract_regex/output_001.pdf index 2c4a0ac15..d912521d7 100644 Binary files a/tests/output/test_extract_regex/output_001.pdf and b/tests/output/test_extract_regex/output_001.pdf differ diff --git a/tests/output/test_extract_regex/output_002.pdf b/tests/output/test_extract_regex/output_002.pdf index b26545f0b..fe4056fa9 100644 Binary files a/tests/output/test_extract_regex/output_002.pdf and b/tests/output/test_extract_regex/output_002.pdf differ diff --git a/tests/output/test_extract_text/output_001.pdf b/tests/output/test_extract_text/output_001.pdf index 3426781a6..350c9a6fd 100644 Binary files a/tests/output/test_extract_text/output_001.pdf and b/tests/output/test_extract_text/output_001.pdf differ diff --git a/tests/output/test_extract_text_expect_ground_truth/output.pdf b/tests/output/test_extract_text_expect_ground_truth/output.pdf index 42c001b0e..8b15139cb 100644 Binary files a/tests/output/test_extract_text_expect_ground_truth/output.pdf and b/tests/output/test_extract_text_expect_ground_truth/output.pdf differ diff --git a/tests/output/test_extract_text_from_self_made_invoice/output.pdf b/tests/output/test_extract_text_from_self_made_invoice/output.pdf index 23c1f8a6f..92ee6b4ac 100644 Binary files a/tests/output/test_extract_text_from_self_made_invoice/output.pdf and b/tests/output/test_extract_text_from_self_made_invoice/output.pdf differ diff --git a/tests/output/test_margin_and_padding/output_001.pdf b/tests/output/test_margin_and_padding/output_001.pdf index 7599c8cb5..a477e42d7 100644 Binary files a/tests/output/test_margin_and_padding/output_001.pdf and b/tests/output/test_margin_and_padding/output_001.pdf differ diff --git a/tests/output/test_margin_and_padding/output_002.pdf b/tests/output/test_margin_and_padding/output_002.pdf index e94b2c41c..d171aff97 100644 Binary files a/tests/output/test_margin_and_padding/output_002.pdf and b/tests/output/test_margin_and_padding/output_002.pdf differ diff --git a/tests/output/test_modify_image/output_001.pdf b/tests/output/test_modify_image/output_001.pdf index 083adaf6b..499ab4d39 100644 Binary files a/tests/output/test_modify_image/output_001.pdf and b/tests/output/test_modify_image/output_001.pdf differ diff --git a/tests/output/test_modify_image/output_002.pdf b/tests/output/test_modify_image/output_002.pdf index 5529c65fe..38f9ee3e1 100644 Binary files a/tests/output/test_modify_image/output_002.pdf and b/tests/output/test_modify_image/output_002.pdf differ diff --git a/tests/output/test_open_document/output.pdf b/tests/output/test_open_document/output.pdf index 8c19b786d..be02cc49b 100644 Binary files a/tests/output/test_open_document/output.pdf and b/tests/output/test_open_document/output.pdf differ diff --git a/tests/output/test_open_encrypted_document/output.pdf b/tests/output/test_open_encrypted_document/output.pdf index cf14e61ea..bc062a40f 100644 Binary files a/tests/output/test_open_encrypted_document/output.pdf and b/tests/output/test_open_encrypted_document/output.pdf differ diff --git a/tests/output/test_optimize_images/output_001.pdf b/tests/output/test_optimize_images/output_001.pdf index ffcc4307a..06623154d 100644 Binary files a/tests/output/test_optimize_images/output_001.pdf and b/tests/output/test_optimize_images/output_001.pdf differ diff --git a/tests/output/test_page_has_empty_resource_dictionary/output_001.pdf b/tests/output/test_page_has_empty_resource_dictionary/output_001.pdf index 9df285595..26e5ff43c 100644 Binary files a/tests/output/test_page_has_empty_resource_dictionary/output_001.pdf and b/tests/output/test_page_has_empty_resource_dictionary/output_001.pdf differ diff --git a/tests/output/test_redact_common_regular_expressions/output_001.pdf b/tests/output/test_redact_common_regular_expressions/output_001.pdf index 664c57195..3307e8f89 100644 Binary files a/tests/output/test_redact_common_regular_expressions/output_001.pdf and b/tests/output/test_redact_common_regular_expressions/output_001.pdf differ diff --git a/tests/output/test_redact_common_regular_expressions/output_002.pdf b/tests/output/test_redact_common_regular_expressions/output_002.pdf index 7de0622b6..e0dd53eef 100644 Binary files a/tests/output/test_redact_common_regular_expressions/output_002.pdf and b/tests/output/test_redact_common_regular_expressions/output_002.pdf differ diff --git a/tests/output/test_redact_common_regular_expressions/output_003.pdf b/tests/output/test_redact_common_regular_expressions/output_003.pdf index 99546f0f0..ef4acb0a4 100644 Binary files a/tests/output/test_redact_common_regular_expressions/output_003.pdf and b/tests/output/test_redact_common_regular_expressions/output_003.pdf differ diff --git a/tests/output/test_remove_annotation/output_001.pdf b/tests/output/test_remove_annotation/output_001.pdf index 0a51dea23..b89403982 100644 Binary files a/tests/output/test_remove_annotation/output_001.pdf and b/tests/output/test_remove_annotation/output_001.pdf differ diff --git a/tests/output/test_remove_annotation/output_002.pdf b/tests/output/test_remove_annotation/output_002.pdf index b069698d2..d970c37ed 100644 Binary files a/tests/output/test_remove_annotation/output_002.pdf and b/tests/output/test_remove_annotation/output_002.pdf differ diff --git a/tests/output/test_remove_annotation/output_003.pdf b/tests/output/test_remove_annotation/output_003.pdf index 13014b8af..33f43fc1b 100644 Binary files a/tests/output/test_remove_annotation/output_003.pdf and b/tests/output/test_remove_annotation/output_003.pdf differ diff --git a/tests/output/test_remove_page/output_001.pdf b/tests/output/test_remove_page/output_001.pdf index f464b57cc..2c17b755b 100644 Binary files a/tests/output/test_remove_page/output_001.pdf and b/tests/output/test_remove_page/output_001.pdf differ diff --git a/tests/output/test_remove_page/output_002.pdf b/tests/output/test_remove_page/output_002.pdf index 2bd24994c..5227a809d 100644 Binary files a/tests/output/test_remove_page/output_002.pdf and b/tests/output/test_remove_page/output_002.pdf differ diff --git a/tests/output/test_remove_page/output_003.pdf b/tests/output/test_remove_page/output_003.pdf index 1d119a1f3..a2fd2a61a 100644 Binary files a/tests/output/test_remove_page/output_003.pdf and b/tests/output/test_remove_page/output_003.pdf differ diff --git a/tests/output/test_remove_page/output_004.pdf b/tests/output/test_remove_page/output_004.pdf index 3424cbd29..1efdbf1f8 100644 Binary files a/tests/output/test_remove_page/output_004.pdf and b/tests/output/test_remove_page/output_004.pdf differ diff --git a/tests/output/test_rotate_page/output_001.pdf b/tests/output/test_rotate_page/output_001.pdf index 3d31d99c7..801fcec10 100644 Binary files a/tests/output/test_rotate_page/output_001.pdf and b/tests/output/test_rotate_page/output_001.pdf differ diff --git a/tests/output/test_rotate_page/output_002.pdf b/tests/output/test_rotate_page/output_002.pdf index 9b31649a5..be08dd9a7 100644 Binary files a/tests/output/test_rotate_page/output_002.pdf and b/tests/output/test_rotate_page/output_002.pdf differ diff --git a/tests/output/test_rotate_page/output_003.pdf b/tests/output/test_rotate_page/output_003.pdf index 629cd9040..766252247 100644 Binary files a/tests/output/test_rotate_page/output_003.pdf and b/tests/output/test_rotate_page/output_003.pdf differ diff --git a/tests/output/test_split_complementary_color_scheme/output.pdf b/tests/output/test_split_complementary_color_scheme/output.pdf index 9aba0b444..af468145d 100644 Binary files a/tests/output/test_split_complementary_color_scheme/output.pdf and b/tests/output/test_split_complementary_color_scheme/output.pdf differ diff --git a/tests/output/test_tetradic_rectangle_color_scheme/output.pdf b/tests/output/test_tetradic_rectangle_color_scheme/output.pdf index b59a069fc..12f65c786 100644 Binary files a/tests/output/test_tetradic_rectangle_color_scheme/output.pdf and b/tests/output/test_tetradic_rectangle_color_scheme/output.pdf differ diff --git a/tests/output/test_tetradic_square_color_scheme/output.pdf b/tests/output/test_tetradic_square_color_scheme/output.pdf index c36191757..baeb1c238 100644 Binary files a/tests/output/test_tetradic_square_color_scheme/output.pdf and b/tests/output/test_tetradic_square_color_scheme/output.pdf differ diff --git a/tests/output/test_triadic_color_scheme/output.pdf b/tests/output/test_triadic_color_scheme/output.pdf index b9e847c76..f4671f935 100644 Binary files a/tests/output/test_triadic_color_scheme/output.pdf and b/tests/output/test_triadic_color_scheme/output.pdf differ diff --git a/tests/output/test_write_2_scatter_plots/output.pdf b/tests/output/test_write_2_scatter_plots/output.pdf index 04a0444df..bb943a21d 100644 Binary files a/tests/output/test_write_2_scatter_plots/output.pdf and b/tests/output/test_write_2_scatter_plots/output.pdf differ diff --git a/tests/output/test_write_3d_density_chart/output.pdf b/tests/output/test_write_3d_density_chart/output.pdf index 825418f49..233cdfc3a 100644 Binary files a/tests/output/test_write_3d_density_chart/output.pdf and b/tests/output/test_write_3d_density_chart/output.pdf differ diff --git a/tests/output/test_write_3d_surface_plot/output.pdf b/tests/output/test_write_3d_surface_plot/output.pdf index f3fdb3767..18755fd45 100644 Binary files a/tests/output/test_write_3d_surface_plot/output.pdf and b/tests/output/test_write_3d_surface_plot/output.pdf differ diff --git a/tests/output/test_write_all_types_of_barcode/output.pdf b/tests/output/test_write_all_types_of_barcode/output.pdf index 4828b1258..2e1c6485c 100644 Binary files a/tests/output/test_write_all_types_of_barcode/output.pdf and b/tests/output/test_write_all_types_of_barcode/output.pdf differ diff --git a/tests/output/test_write_battleship/output.pdf b/tests/output/test_write_battleship/output.pdf index e33603468..15e03fb9e 100644 Binary files a/tests/output/test_write_battleship/output.pdf and b/tests/output/test_write_battleship/output.pdf differ diff --git a/tests/output/test_write_blobs/output.pdf b/tests/output/test_write_blobs/output.pdf index 283fe45bd..2e1bf875c 100644 Binary files a/tests/output/test_write_blobs/output.pdf and b/tests/output/test_write_blobs/output.pdf differ diff --git a/tests/output/test_write_check_box/output_001.pdf b/tests/output/test_write_check_box/output_001.pdf index 400773c4d..1d48baaa5 100644 Binary files a/tests/output/test_write_check_box/output_001.pdf and b/tests/output/test_write_check_box/output_001.pdf differ diff --git a/tests/output/test_write_check_box/output_002.pdf b/tests/output/test_write_check_box/output_002.pdf index 7ea5c8f87..87ed9cd11 100644 Binary files a/tests/output/test_write_check_box/output_002.pdf and b/tests/output/test_write_check_box/output_002.pdf differ diff --git a/tests/output/test_write_chunk_of_text/output.pdf b/tests/output/test_write_chunk_of_text/output.pdf index 64c5f6143..82e8d5208 100644 Binary files a/tests/output/test_write_chunk_of_text/output.pdf and b/tests/output/test_write_chunk_of_text/output.pdf differ diff --git a/tests/output/test_write_chunk_of_text_escaped_chars/output.pdf b/tests/output/test_write_chunk_of_text_escaped_chars/output.pdf index 95c9f31db..7178a5024 100644 Binary files a/tests/output/test_write_chunk_of_text_escaped_chars/output.pdf and b/tests/output/test_write_chunk_of_text_escaped_chars/output.pdf differ diff --git a/tests/output/test_write_chunk_of_text_in_rainbow_colors/output.pdf b/tests/output/test_write_chunk_of_text_in_rainbow_colors/output.pdf index edfde24ef..c1a6a09b2 100644 Binary files a/tests/output/test_write_chunk_of_text_in_rainbow_colors/output.pdf and b/tests/output/test_write_chunk_of_text_in_rainbow_colors/output.pdf differ diff --git a/tests/output/test_write_chunks_of_text/output_001.pdf b/tests/output/test_write_chunks_of_text/output_001.pdf index 8fa468557..5dd428fe4 100644 Binary files a/tests/output/test_write_chunks_of_text/output_001.pdf and b/tests/output/test_write_chunks_of_text/output_001.pdf differ diff --git a/tests/output/test_write_chunks_of_text/output_002.pdf b/tests/output/test_write_chunks_of_text/output_002.pdf index 337aba22f..862196040 100644 Binary files a/tests/output/test_write_chunks_of_text/output_002.pdf and b/tests/output/test_write_chunks_of_text/output_002.pdf differ diff --git a/tests/output/test_write_chunks_of_text/output_003.pdf b/tests/output/test_write_chunks_of_text/output_003.pdf index 0e9ac7504..2ef3955e4 100644 Binary files a/tests/output/test_write_chunks_of_text/output_003.pdf and b/tests/output/test_write_chunks_of_text/output_003.pdf differ diff --git a/tests/output/test_write_chunks_of_text/output_004.pdf b/tests/output/test_write_chunks_of_text/output_004.pdf index eb4cd1f15..5d567599e 100644 Binary files a/tests/output/test_write_chunks_of_text/output_004.pdf and b/tests/output/test_write_chunks_of_text/output_004.pdf differ diff --git a/tests/output/test_write_chunks_of_text/output_005.pdf b/tests/output/test_write_chunks_of_text/output_005.pdf index f7d909141..f5cb5dd0b 100644 Binary files a/tests/output/test_write_chunks_of_text/output_005.pdf and b/tests/output/test_write_chunks_of_text/output_005.pdf differ diff --git a/tests/output/test_write_chunks_of_text_preserves_bounding_boxes/output.pdf b/tests/output/test_write_chunks_of_text_preserves_bounding_boxes/output.pdf index 567944a24..58bed927d 100644 Binary files a/tests/output/test_write_chunks_of_text_preserves_bounding_boxes/output.pdf and b/tests/output/test_write_chunks_of_text_preserves_bounding_boxes/output.pdf differ diff --git a/tests/output/test_write_code_128_barcode/output.pdf b/tests/output/test_write_code_128_barcode/output.pdf index c10cee3a5..8b2e39a99 100644 Binary files a/tests/output/test_write_code_128_barcode/output.pdf and b/tests/output/test_write_code_128_barcode/output.pdf differ diff --git a/tests/output/test_write_code_128_barcode_in_color/output.pdf b/tests/output/test_write_code_128_barcode_in_color/output.pdf index 1249f7742..c0f2cf18e 100644 Binary files a/tests/output/test_write_code_128_barcode_in_color/output.pdf and b/tests/output/test_write_code_128_barcode_in_color/output.pdf differ diff --git a/tests/output/test_write_codeblock/output.pdf b/tests/output/test_write_codeblock/output.pdf index 2eca02a44..68c6d6d1a 100644 Binary files a/tests/output/test_write_codeblock/output.pdf and b/tests/output/test_write_codeblock/output.pdf differ diff --git a/tests/output/test_write_dragon_curve/output.pdf b/tests/output/test_write_dragon_curve/output.pdf index d1891101c..b91adcc33 100644 Binary files a/tests/output/test_write_dragon_curve/output.pdf and b/tests/output/test_write_dragon_curve/output.pdf differ diff --git a/tests/output/test_write_drop_down_list/output_001.pdf b/tests/output/test_write_drop_down_list/output_001.pdf index 5d93e8a0d..92460e102 100644 Binary files a/tests/output/test_write_drop_down_list/output_001.pdf and b/tests/output/test_write_drop_down_list/output_001.pdf differ diff --git a/tests/output/test_write_drop_down_list/output_002.pdf b/tests/output/test_write_drop_down_list/output_002.pdf index d06dfd66b..0e097e774 100644 Binary files a/tests/output/test_write_drop_down_list/output_002.pdf and b/tests/output/test_write_drop_down_list/output_002.pdf differ diff --git a/tests/output/test_write_emoji/output.pdf b/tests/output/test_write_emoji/output.pdf index 68ee0e048..7dd1efb06 100644 Binary files a/tests/output/test_write_emoji/output.pdf and b/tests/output/test_write_emoji/output.pdf differ diff --git a/tests/output/test_write_empty_document/output.pdf b/tests/output/test_write_empty_document/output.pdf index 094edadfd..b706a708f 100644 Binary files a/tests/output/test_write_empty_document/output.pdf and b/tests/output/test_write_empty_document/output.pdf differ diff --git a/tests/output/test_write_fixed_column_width_table/output_001.pdf b/tests/output/test_write_fixed_column_width_table/output_001.pdf index 491041fd7..70a00c398 100644 Binary files a/tests/output/test_write_fixed_column_width_table/output_001.pdf and b/tests/output/test_write_fixed_column_width_table/output_001.pdf differ diff --git a/tests/output/test_write_fixed_column_width_table/output_002.pdf b/tests/output/test_write_fixed_column_width_table/output_002.pdf index 9b20439c8..6a6879660 100644 Binary files a/tests/output/test_write_fixed_column_width_table/output_002.pdf and b/tests/output/test_write_fixed_column_width_table/output_002.pdf differ diff --git a/tests/output/test_write_fixed_column_width_table/output_003.pdf b/tests/output/test_write_fixed_column_width_table/output_003.pdf index 10c9b789d..04ce2a96e 100644 Binary files a/tests/output/test_write_fixed_column_width_table/output_003.pdf and b/tests/output/test_write_fixed_column_width_table/output_003.pdf differ diff --git a/tests/output/test_write_fixed_column_width_table/output_004.pdf b/tests/output/test_write_fixed_column_width_table/output_004.pdf index 593656507..f5760b5dd 100644 Binary files a/tests/output/test_write_fixed_column_width_table/output_004.pdf and b/tests/output/test_write_fixed_column_width_table/output_004.pdf differ diff --git a/tests/output/test_write_fixed_column_width_table/output_005.pdf b/tests/output/test_write_fixed_column_width_table/output_005.pdf index c1fbda208..ddec4b74e 100644 Binary files a/tests/output/test_write_fixed_column_width_table/output_005.pdf and b/tests/output/test_write_fixed_column_width_table/output_005.pdf differ diff --git a/tests/output/test_write_flexi_table/output_001.pdf b/tests/output/test_write_flexi_table/output_001.pdf index 5d84b789b..e31effce2 100644 Binary files a/tests/output/test_write_flexi_table/output_001.pdf and b/tests/output/test_write_flexi_table/output_001.pdf differ diff --git a/tests/output/test_write_flexi_table/output_002.pdf b/tests/output/test_write_flexi_table/output_002.pdf index 83b5b4418..a8a235fd0 100644 Binary files a/tests/output/test_write_flexi_table/output_002.pdf and b/tests/output/test_write_flexi_table/output_002.pdf differ diff --git a/tests/output/test_write_flexi_table/output_003.pdf b/tests/output/test_write_flexi_table/output_003.pdf index 622bb3d87..fb0131a94 100644 Binary files a/tests/output/test_write_flexi_table/output_003.pdf and b/tests/output/test_write_flexi_table/output_003.pdf differ diff --git a/tests/output/test_write_flexi_table/output_004.pdf b/tests/output/test_write_flexi_table/output_004.pdf index 9fa537616..ca36d9a9f 100644 Binary files a/tests/output/test_write_flexi_table/output_004.pdf and b/tests/output/test_write_flexi_table/output_004.pdf differ diff --git a/tests/output/test_write_flexi_table/output_005.pdf b/tests/output/test_write_flexi_table/output_005.pdf index 033179c32..639ce9c37 100644 Binary files a/tests/output/test_write_flexi_table/output_005.pdf and b/tests/output/test_write_flexi_table/output_005.pdf differ diff --git a/tests/output/test_write_flexi_table_with_preferred_width/output.pdf b/tests/output/test_write_flexi_table_with_preferred_width/output.pdf index bb993cab5..e61503447 100644 Binary files a/tests/output/test_write_flexi_table_with_preferred_width/output.pdf and b/tests/output/test_write_flexi_table_with_preferred_width/output.pdf differ diff --git a/tests/output/test_write_flowchart_line_art/output.pdf b/tests/output/test_write_flowchart_line_art/output.pdf index 9774035a2..0dfe3c3b3 100644 Binary files a/tests/output/test_write_flowchart_line_art/output.pdf and b/tests/output/test_write_flowchart_line_art/output.pdf differ diff --git a/tests/output/test_write_flyer/output.pdf b/tests/output/test_write_flyer/output.pdf index 1aeee81ca..9d645d578 100644 Binary files a/tests/output/test_write_flyer/output.pdf and b/tests/output/test_write_flyer/output.pdf differ diff --git a/tests/output/test_write_grayscale_image/output.pdf b/tests/output/test_write_grayscale_image/output.pdf index 5b6ee3214..f552b5eb2 100644 Binary files a/tests/output/test_write_grayscale_image/output.pdf and b/tests/output/test_write_grayscale_image/output.pdf differ diff --git a/tests/output/test_write_hello_world_with_monaco_font/output_001.pdf b/tests/output/test_write_hello_world_with_monaco_font/output_001.pdf new file mode 100644 index 000000000..82d61c537 Binary files /dev/null and b/tests/output/test_write_hello_world_with_monaco_font/output_001.pdf differ diff --git a/tests/output/test_write_hello_world_with_monaco_font/output_001_ground_truth.png b/tests/output/test_write_hello_world_with_monaco_font/output_001_ground_truth.png new file mode 100644 index 000000000..f9887e5a2 Binary files /dev/null and b/tests/output/test_write_hello_world_with_monaco_font/output_001_ground_truth.png differ diff --git a/tests/output/test_write_hyphenated_paragraph/output.pdf b/tests/output/test_write_hyphenated_paragraph/output.pdf index aa14f7f0e..5ede4b01f 100644 Binary files a/tests/output/test_write_hyphenated_paragraph/output.pdf and b/tests/output/test_write_hyphenated_paragraph/output.pdf differ diff --git a/tests/output/test_write_image_aligned_center/output.pdf b/tests/output/test_write_image_aligned_center/output.pdf index c4cef6be6..c3235e039 100644 Binary files a/tests/output/test_write_image_aligned_center/output.pdf and b/tests/output/test_write_image_aligned_center/output.pdf differ diff --git a/tests/output/test_write_image_by_url/output.pdf b/tests/output/test_write_image_by_url/output.pdf index 1b49096b2..29e40fc5b 100644 Binary files a/tests/output/test_write_image_by_url/output.pdf and b/tests/output/test_write_image_by_url/output.pdf differ diff --git a/tests/output/test_write_incomplete_table/output.pdf b/tests/output/test_write_incomplete_table/output.pdf index d595ae05c..222b31762 100644 Binary files a/tests/output/test_write_incomplete_table/output.pdf and b/tests/output/test_write_incomplete_table/output.pdf differ diff --git a/tests/output/test_write_line_of_text_justified_center/output.pdf b/tests/output/test_write_line_of_text_justified_center/output.pdf index 8ff41e05b..85a1611a2 100644 Binary files a/tests/output/test_write_line_of_text_justified_center/output.pdf and b/tests/output/test_write_line_of_text_justified_center/output.pdf differ diff --git a/tests/output/test_write_line_of_text_justified_full/output.pdf b/tests/output/test_write_line_of_text_justified_full/output.pdf index 729be2132..618246830 100644 Binary files a/tests/output/test_write_line_of_text_justified_full/output.pdf and b/tests/output/test_write_line_of_text_justified_full/output.pdf differ diff --git a/tests/output/test_write_line_of_text_justified_right/output.pdf b/tests/output/test_write_line_of_text_justified_right/output.pdf index 6a3297d01..5897948db 100644 Binary files a/tests/output/test_write_line_of_text_justified_right/output.pdf and b/tests/output/test_write_line_of_text_justified_right/output.pdf differ diff --git a/tests/output/test_write_lissajours_line_art/output.pdf b/tests/output/test_write_lissajours_line_art/output.pdf index 980fc2c83..2405c4a23 100644 Binary files a/tests/output/test_write_lissajours_line_art/output.pdf and b/tests/output/test_write_lissajours_line_art/output.pdf differ diff --git a/tests/output/test_write_long_unordered_list/output.pdf b/tests/output/test_write_long_unordered_list/output.pdf index 0bc9a33b1..f834bacf4 100644 Binary files a/tests/output/test_write_long_unordered_list/output.pdf and b/tests/output/test_write_long_unordered_list/output.pdf differ diff --git a/tests/output/test_write_multiple_pages/output.pdf b/tests/output/test_write_multiple_pages/output.pdf index e10edefaf..0e64667cb 100644 Binary files a/tests/output/test_write_multiple_pages/output.pdf and b/tests/output/test_write_multiple_pages/output.pdf differ diff --git a/tests/output/test_write_nested_ordered_list/output.pdf b/tests/output/test_write_nested_ordered_list/output.pdf index 42a457f91..0d8dea8b9 100644 Binary files a/tests/output/test_write_nested_ordered_list/output.pdf and b/tests/output/test_write_nested_ordered_list/output.pdf differ diff --git a/tests/output/test_write_nested_unordered_list/output.pdf b/tests/output/test_write_nested_unordered_list/output.pdf index c7f3733ef..ebf5648e6 100644 Binary files a/tests/output/test_write_nested_unordered_list/output.pdf and b/tests/output/test_write_nested_unordered_list/output.pdf differ diff --git a/tests/output/test_write_nested_unordered_list/output.png b/tests/output/test_write_nested_unordered_list/output.png index 35bd924c8..aeb8e6f6e 100644 Binary files a/tests/output/test_write_nested_unordered_list/output.png and b/tests/output/test_write_nested_unordered_list/output.png differ diff --git a/tests/output/test_write_ordered_list/output_001.pdf b/tests/output/test_write_ordered_list/output_001.pdf index a1b94d7c1..5fcf86282 100644 Binary files a/tests/output/test_write_ordered_list/output_001.pdf and b/tests/output/test_write_ordered_list/output_001.pdf differ diff --git a/tests/output/test_write_ordered_list/output_002.pdf b/tests/output/test_write_ordered_list/output_002.pdf index 0d5e00950..5858580c1 100644 Binary files a/tests/output/test_write_ordered_list/output_002.pdf and b/tests/output/test_write_ordered_list/output_002.pdf differ diff --git a/tests/output/test_write_paragraph/output.pdf b/tests/output/test_write_paragraph/output.pdf index 4c366c6f5..3f0aa9890 100644 Binary files a/tests/output/test_write_paragraph/output.pdf and b/tests/output/test_write_paragraph/output.pdf differ diff --git a/tests/output/test_write_paragraph_alignment/output.pdf b/tests/output/test_write_paragraph_alignment/output.pdf index cf29cc080..2d7103f86 100644 Binary files a/tests/output/test_write_paragraph_alignment/output.pdf and b/tests/output/test_write_paragraph_alignment/output.pdf differ diff --git a/tests/output/test_write_paragraph_border_left/output.pdf b/tests/output/test_write_paragraph_border_left/output.pdf index 37109ec77..36a73dc24 100644 Binary files a/tests/output/test_write_paragraph_border_left/output.pdf and b/tests/output/test_write_paragraph_border_left/output.pdf differ diff --git a/tests/output/test_write_paragraph_force_split/output.pdf b/tests/output/test_write_paragraph_force_split/output.pdf index 7619525b0..2114e99fe 100644 Binary files a/tests/output/test_write_paragraph_force_split/output.pdf and b/tests/output/test_write_paragraph_force_split/output.pdf differ diff --git a/tests/output/test_write_paragraph_justified_center/output_001.pdf b/tests/output/test_write_paragraph_justified_center/output_001.pdf index 999675f7e..94d0f76b6 100644 Binary files a/tests/output/test_write_paragraph_justified_center/output_001.pdf and b/tests/output/test_write_paragraph_justified_center/output_001.pdf differ diff --git a/tests/output/test_write_paragraph_justified_center/output_002.pdf b/tests/output/test_write_paragraph_justified_center/output_002.pdf index 0658548e8..8601dc0fe 100644 Binary files a/tests/output/test_write_paragraph_justified_center/output_002.pdf and b/tests/output/test_write_paragraph_justified_center/output_002.pdf differ diff --git a/tests/output/test_write_paragraph_justified_center_with_padding/output.pdf b/tests/output/test_write_paragraph_justified_center_with_padding/output.pdf index 8c74acd30..e16d8dc12 100644 Binary files a/tests/output/test_write_paragraph_justified_center_with_padding/output.pdf and b/tests/output/test_write_paragraph_justified_center_with_padding/output.pdf differ diff --git a/tests/output/test_write_paragraph_justified_center_with_padding_and_border/output.pdf b/tests/output/test_write_paragraph_justified_center_with_padding_and_border/output.pdf index b6e2e88c3..d804208cc 100644 Binary files a/tests/output/test_write_paragraph_justified_center_with_padding_and_border/output.pdf and b/tests/output/test_write_paragraph_justified_center_with_padding_and_border/output.pdf differ diff --git a/tests/output/test_write_paragraph_justified_center_with_padding_and_border_and_background/output.pdf b/tests/output/test_write_paragraph_justified_center_with_padding_and_border_and_background/output.pdf index 7cee56c26..c760a9038 100644 Binary files a/tests/output/test_write_paragraph_justified_center_with_padding_and_border_and_background/output.pdf and b/tests/output/test_write_paragraph_justified_center_with_padding_and_border_and_background/output.pdf differ diff --git a/tests/output/test_write_paragraph_justified_full/output.pdf b/tests/output/test_write_paragraph_justified_full/output.pdf index d273b70fe..bb5638f80 100644 Binary files a/tests/output/test_write_paragraph_justified_full/output.pdf and b/tests/output/test_write_paragraph_justified_full/output.pdf differ diff --git a/tests/output/test_write_paragraph_justified_right/output.pdf b/tests/output/test_write_paragraph_justified_right/output.pdf index b24625f5c..602a742b4 100644 Binary files a/tests/output/test_write_paragraph_justified_right/output.pdf and b/tests/output/test_write_paragraph_justified_right/output.pdf differ diff --git a/tests/output/test_write_paragraph_preserve_space/output.pdf b/tests/output/test_write_paragraph_preserve_space/output.pdf index 59d703067..33a746928 100644 Binary files a/tests/output/test_write_paragraph_preserve_space/output.pdf and b/tests/output/test_write_paragraph_preserve_space/output.pdf differ diff --git a/tests/output/test_write_paragraph_save_twice/output_001.pdf b/tests/output/test_write_paragraph_save_twice/output_001.pdf index 5e34bd4fd..0063fe26c 100644 Binary files a/tests/output/test_write_paragraph_save_twice/output_001.pdf and b/tests/output/test_write_paragraph_save_twice/output_001.pdf differ diff --git a/tests/output/test_write_paragraph_save_twice/output_002.pdf b/tests/output/test_write_paragraph_save_twice/output_002.pdf index 92f59e8f7..ad3c0fd75 100644 Binary files a/tests/output/test_write_paragraph_save_twice/output_002.pdf and b/tests/output/test_write_paragraph_save_twice/output_002.pdf differ diff --git a/tests/output/test_write_paragraph_with_accented_letters/output.pdf b/tests/output/test_write_paragraph_with_accented_letters/output.pdf index e038cbe21..d3461a034 100644 Binary files a/tests/output/test_write_paragraph_with_accented_letters/output.pdf and b/tests/output/test_write_paragraph_with_accented_letters/output.pdf differ diff --git a/tests/output/test_write_paragraphs_using_multi_column_layout/output.pdf b/tests/output/test_write_paragraphs_using_multi_column_layout/output.pdf index b7390e5d0..1806ada96 100644 Binary files a/tests/output/test_write_paragraphs_using_multi_column_layout/output.pdf and b/tests/output/test_write_paragraphs_using_multi_column_layout/output.pdf differ diff --git a/tests/output/test_write_paragraphs_using_single_column_layout/output.pdf b/tests/output/test_write_paragraphs_using_single_column_layout/output.pdf index b414ca263..e2774baa3 100644 Binary files a/tests/output/test_write_paragraphs_using_single_column_layout/output.pdf and b/tests/output/test_write_paragraphs_using_single_column_layout/output.pdf differ diff --git a/tests/output/test_write_paragraphs_with_headings/output.pdf b/tests/output/test_write_paragraphs_with_headings/output.pdf index 6c7b3db17..9255cdfa3 100644 Binary files a/tests/output/test_write_paragraphs_with_headings/output.pdf and b/tests/output/test_write_paragraphs_with_headings/output.pdf differ diff --git a/tests/output/test_write_pdf_a_1b/output_001.pdf b/tests/output/test_write_pdf_a_1b/output_001.pdf new file mode 100644 index 000000000..9102abbde Binary files /dev/null and b/tests/output/test_write_pdf_a_1b/output_001.pdf differ diff --git a/tests/output/test_write_pdf_a_1b/output_001_ground_truth.png b/tests/output/test_write_pdf_a_1b/output_001_ground_truth.png new file mode 100644 index 000000000..aa9f925bf Binary files /dev/null and b/tests/output/test_write_pdf_a_1b/output_001_ground_truth.png differ diff --git a/tests/output/test_write_pdf_a_1b/output_002.pdf b/tests/output/test_write_pdf_a_1b/output_002.pdf new file mode 100644 index 000000000..5405d8a50 Binary files /dev/null and b/tests/output/test_write_pdf_a_1b/output_002.pdf differ diff --git a/tests/output/test_write_pdf_a_1b/output_002.png b/tests/output/test_write_pdf_a_1b/output_002.png new file mode 100644 index 000000000..aa9f925bf Binary files /dev/null and b/tests/output/test_write_pdf_a_1b/output_002.png differ diff --git a/tests/output/test_write_pil_image/output.pdf b/tests/output/test_write_pil_image/output.pdf index b6f875601..c734cb51e 100644 Binary files a/tests/output/test_write_pil_image/output.pdf and b/tests/output/test_write_pil_image/output.pdf differ diff --git a/tests/output/test_write_png_image_by_url/output.pdf b/tests/output/test_write_png_image_by_url/output.pdf index 39a0fc4d0..fd514255d 100644 Binary files a/tests/output/test_write_png_image_by_url/output.pdf and b/tests/output/test_write_png_image_by_url/output.pdf differ diff --git a/tests/output/test_write_radar_plot/output.pdf b/tests/output/test_write_radar_plot/output.pdf index df8af7a1b..99a4dbf59 100644 Binary files a/tests/output/test_write_radar_plot/output.pdf and b/tests/output/test_write_radar_plot/output.pdf differ diff --git a/tests/output/test_write_table_with_col_span/output.pdf b/tests/output/test_write_table_with_col_span/output.pdf index 7cb9f6180..f29ac7531 100644 Binary files a/tests/output/test_write_table_with_col_span/output.pdf and b/tests/output/test_write_table_with_col_span/output.pdf differ diff --git a/tests/output/test_write_table_with_image/output.pdf b/tests/output/test_write_table_with_image/output.pdf index 2d41bc74e..9b0d0f930 100644 Binary files a/tests/output/test_write_table_with_image/output.pdf and b/tests/output/test_write_table_with_image/output.pdf differ diff --git a/tests/output/test_write_table_with_non_black_paragraphs/output.pdf b/tests/output/test_write_table_with_non_black_paragraphs/output.pdf index a9e601608..c8b15b1e1 100644 Binary files a/tests/output/test_write_table_with_non_black_paragraphs/output.pdf and b/tests/output/test_write_table_with_non_black_paragraphs/output.pdf differ diff --git a/tests/output/test_write_table_with_rainbow_background/output.pdf b/tests/output/test_write_table_with_rainbow_background/output.pdf index ebe67d896..80b5b1809 100644 Binary files a/tests/output/test_write_table_with_rainbow_background/output.pdf and b/tests/output/test_write_table_with_rainbow_background/output.pdf differ diff --git a/tests/output/test_write_table_with_row_span/output.pdf b/tests/output/test_write_table_with_row_span/output.pdf index a1f55c1c4..d26ab3152 100644 Binary files a/tests/output/test_write_table_with_row_span/output.pdf and b/tests/output/test_write_table_with_row_span/output.pdf differ diff --git a/tests/output/test_write_table_with_special_characters/output.pdf b/tests/output/test_write_table_with_special_characters/output.pdf index 52704c818..ef6fb425b 100644 Binary files a/tests/output/test_write_table_with_special_characters/output.pdf and b/tests/output/test_write_table_with_special_characters/output.pdf differ diff --git a/tests/output/test_write_tents_and_trees/output.pdf b/tests/output/test_write_tents_and_trees/output.pdf index 03f5ea1bd..157eb6c7d 100644 Binary files a/tests/output/test_write_tents_and_trees/output.pdf and b/tests/output/test_write_tents_and_trees/output.pdf differ diff --git a/tests/output/test_write_text_area/output_001.pdf b/tests/output/test_write_text_area/output_001.pdf index af472aa10..84faa0673 100644 Binary files a/tests/output/test_write_text_area/output_001.pdf and b/tests/output/test_write_text_area/output_001.pdf differ diff --git a/tests/output/test_write_text_area/output_002.pdf b/tests/output/test_write_text_area/output_002.pdf index 405a09800..edd8a2b1e 100644 Binary files a/tests/output/test_write_text_area/output_002.pdf and b/tests/output/test_write_text_area/output_002.pdf differ diff --git a/tests/output/test_write_text_field/output_001.pdf b/tests/output/test_write_text_field/output_001.pdf index ef79a23e3..bc7874e66 100644 Binary files a/tests/output/test_write_text_field/output_001.pdf and b/tests/output/test_write_text_field/output_001.pdf differ diff --git a/tests/output/test_write_text_field/output_002.pdf b/tests/output/test_write_text_field/output_002.pdf index e01cc50c8..9d11dc0a4 100644 Binary files a/tests/output/test_write_text_field/output_002.pdf and b/tests/output/test_write_text_field/output_002.pdf differ diff --git a/tests/output/test_write_unordered_list/output.pdf b/tests/output/test_write_unordered_list/output.pdf index 26e2a8ddd..3adfe79ed 100644 Binary files a/tests/output/test_write_unordered_list/output.pdf and b/tests/output/test_write_unordered_list/output.pdf differ diff --git a/tests/output/test_write_using_low_level_instructions/output.pdf b/tests/output/test_write_using_low_level_instructions/output.pdf index 64bb4451c..bf6ec75c2 100644 Binary files a/tests/output/test_write_using_low_level_instructions/output.pdf and b/tests/output/test_write_using_low_level_instructions/output.pdf differ diff --git a/tests/output/test_write_with_truetype_font/output_001.pdf b/tests/output/test_write_with_truetype_font/output_001.pdf index 46a5cabed..6ed316dfa 100644 Binary files a/tests/output/test_write_with_truetype_font/output_001.pdf and b/tests/output/test_write_with_truetype_font/output_001.pdf differ diff --git a/tests/output/test_write_with_truetype_font/output_001_ground_truth.png b/tests/output/test_write_with_truetype_font/output_001_ground_truth.png index b41fbda51..94f434524 100644 Binary files a/tests/output/test_write_with_truetype_font/output_001_ground_truth.png and b/tests/output/test_write_with_truetype_font/output_001_ground_truth.png differ diff --git a/tests/output/test_write_with_truetype_font/output_002.pdf b/tests/output/test_write_with_truetype_font/output_002.pdf index 28b6c3043..508a2ef8e 100644 Binary files a/tests/output/test_write_with_truetype_font/output_002.pdf and b/tests/output/test_write_with_truetype_font/output_002.pdf differ diff --git a/tests/output/test_write_with_truetype_font/output_002.png b/tests/output/test_write_with_truetype_font/output_002.png index f57b2d9e5..a4e99e2db 100644 Binary files a/tests/output/test_write_with_truetype_font/output_002.png and b/tests/output/test_write_with_truetype_font/output_002.png differ diff --git a/tests/output/test_write_with_truetype_font/output_003.pdf b/tests/output/test_write_with_truetype_font/output_003.pdf index 19234869e..00286580e 100644 Binary files a/tests/output/test_write_with_truetype_font/output_003.pdf and b/tests/output/test_write_with_truetype_font/output_003.pdf differ diff --git a/tests/output/test_write_with_truetype_font/output_003_ground_truth.png b/tests/output/test_write_with_truetype_font/output_003_ground_truth.png index 4f6bfbd07..284988579 100644 Binary files a/tests/output/test_write_with_truetype_font/output_003_ground_truth.png and b/tests/output/test_write_with_truetype_font/output_003_ground_truth.png differ diff --git a/tests/output/test_write_xl_image/output.pdf b/tests/output/test_write_xl_image/output.pdf index c5bdb8b92..271ec30e9 100644 Binary files a/tests/output/test_write_xl_image/output.pdf and b/tests/output/test_write_xl_image/output.pdf differ diff --git a/tests/pdf/canvas/font/Monaco-Regular.ttf b/tests/pdf/canvas/font/Monaco-Regular.ttf new file mode 100644 index 000000000..57217b3f0 Binary files /dev/null and b/tests/pdf/canvas/font/Monaco-Regular.ttf differ diff --git a/tests/pdf/canvas/font/Ubuntu-Light.ttf b/tests/pdf/canvas/font/Ubuntu-Light.ttf new file mode 100644 index 000000000..0e9f90d7c Binary files /dev/null and b/tests/pdf/canvas/font/Ubuntu-Light.ttf differ diff --git a/tests/pdf/canvas/font/test_digit_placement_ubuntu_font.py b/tests/pdf/canvas/font/test_digit_placement_ubuntu_font.py new file mode 100644 index 000000000..e02339881 --- /dev/null +++ b/tests/pdf/canvas/font/test_digit_placement_ubuntu_font.py @@ -0,0 +1,86 @@ +import unittest +from datetime import datetime +from decimal import Decimal +from pathlib import Path + +from borb.pdf.canvas.font.simple_font.true_type_font import TrueTypeFont +from borb.pdf.canvas.layout.page_layout.multi_column_layout import SingleColumnLayout +from borb.pdf.canvas.layout.page_layout.page_layout import PageLayout +from borb.pdf.canvas.layout.table.fixed_column_width_table import FixedColumnWidthTable +from borb.pdf.canvas.layout.text.paragraph import Paragraph +from borb.pdf.document import Document +from borb.pdf.page.page import Page +from borb.pdf.pdf import PDF +from tests.test_util import compare_visually_to_ground_truth + + +class TestDigitPlacementUbuntuFont(unittest.TestCase): + """ + This test loads a truetype _font from a .ttf file and attempts to use it to write 2 paragraphs of lorem ipsum. + """ + + def __init__(self, methodName="runTest"): + super().__init__(methodName) + + # find output dir + p: Path = Path(__file__).parent + while "output" not in [x.stem for x in p.iterdir() if x.is_dir()]: + p = p.parent + p = p / "output" + self.output_dir = Path(p, Path(__file__).stem.replace(".py", "")) + if not self.output_dir.exists(): + self.output_dir.mkdir() + + def test_write_document_001(self): + + # create document + pdf = Document() + + # add page + page = Page() + pdf.append_page(page) + + # layout + layout: PageLayout = SingleColumnLayout(page) + + # add test information + layout.add( + FixedColumnWidthTable(number_of_columns=2, number_of_rows=3) + .add(Paragraph("Date", font="Helvetica-Bold")) + .add(Paragraph(datetime.now().strftime("%d/%m/%Y, %H:%M:%S"))) + .add(Paragraph("Test", font="Helvetica-Bold")) + .add(Paragraph(Path(__file__).stem)) + .add(Paragraph("Description", font="Helvetica-Bold")) + .add( + Paragraph( + "This test loads a truetype _font from a .ttf file and attempts to write A1 with it." + ) + ) + .set_padding_on_all_cells(Decimal(2), Decimal(2), Decimal(2), Decimal(2)) + ) + + # path to _font + font_path: Path = Path(__file__).parent / "Ubuntu-Light.ttf" + assert font_path.exists() + + # add paragraph 1 + layout.add( + Paragraph( + "A1", + font=TrueTypeFont.true_type_font_from_file(font_path), + font_size=Decimal(14), + ) + ) + + # determine output location + out_file = self.output_dir / "output_001.pdf" + + # attempt to store PDF + with open(out_file, "wb") as in_file_handle: + PDF.dumps(in_file_handle, pdf) + + # attempt to re-open PDF + with open(out_file, "rb") as in_file_handle: + PDF.loads(in_file_handle) + + compare_visually_to_ground_truth(out_file) diff --git a/tests/pdf/canvas/font/test_write_hello_world_with_monaco_font.py b/tests/pdf/canvas/font/test_write_hello_world_with_monaco_font.py new file mode 100644 index 000000000..d395dc927 --- /dev/null +++ b/tests/pdf/canvas/font/test_write_hello_world_with_monaco_font.py @@ -0,0 +1,68 @@ +import unittest +from decimal import Decimal +from pathlib import Path + +from borb.pdf.canvas.font.simple_font.true_type_font import TrueTypeFont +from borb.pdf.canvas.layout.page_layout.multi_column_layout import SingleColumnLayout +from borb.pdf.canvas.layout.page_layout.page_layout import PageLayout +from borb.pdf.canvas.layout.text.paragraph import Paragraph +from borb.pdf.document import Document +from borb.pdf.page.page import Page +from borb.pdf.pdf import PDF +from tests.test_util import compare_visually_to_ground_truth + + +class TestWriteHelloWorldWithMonacoFont(unittest.TestCase): + """ + This test loads a truetype _font from a .ttf file and attempts to use it to write 2 paragraphs of lorem ipsum. + """ + + def __init__(self, methodName="runTest"): + super().__init__(methodName) + + # find output dir + p: Path = Path(__file__).parent + while "output" not in [x.stem for x in p.iterdir() if x.is_dir()]: + p = p.parent + p = p / "output" + self.output_dir = Path(p, Path(__file__).stem.replace(".py", "")) + if not self.output_dir.exists(): + self.output_dir.mkdir() + + def test_write_document_001(self): + + # create document + pdf = Document() + + # add page + page = Page() + pdf.append_page(page) + + # layout + layout: PageLayout = SingleColumnLayout(page) + + # path to _font + font_path: Path = Path(__file__).parent / "Monaco-Regular.ttf" + assert font_path.exists() + + # add paragraph 1 + layout.add( + Paragraph( + "Hello World!", + font=TrueTypeFont.true_type_font_from_file(font_path), + font_size=Decimal(14), + ) + ) + + # determine output location + out_file = self.output_dir / "output_001.pdf" + + # attempt to store PDF + with open(out_file, "wb") as in_file_handle: + PDF.dumps(in_file_handle, pdf) + + # attempt to re-open PDF + with open(out_file, "rb") as in_file_handle: + PDF.loads(in_file_handle) + + compare_visually_to_ground_truth(out_file) diff --git a/tests/pdf/conformance/__init__.py b/tests/pdf/conformance/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/tests/pdf/conformance/test_write_pdf_a_1b.py b/tests/pdf/conformance/test_write_pdf_a_1b.py new file mode 100644 index 000000000..0ee0599b1 --- /dev/null +++ b/tests/pdf/conformance/test_write_pdf_a_1b.py @@ -0,0 +1,99 @@ +import unittest +from pathlib import Path + +from borb.io.read.types import Dictionary, Name, String +from borb.pdf.canvas.layout.page_layout.multi_column_layout import SingleColumnLayout +from borb.pdf.canvas.layout.page_layout.page_layout import PageLayout +from borb.pdf.canvas.layout.text.paragraph import Paragraph +from borb.pdf.document import Document +from borb.pdf.page.page import Page +from borb.pdf.pdf import PDF +from tests.test_util import compare_visually_to_ground_truth + + +class TestWritePDFA1B(unittest.TestCase): + """ + This test creates a PDF with a few PDF graphics in it + """ + + def __init__(self, methodName="runTest"): + super().__init__(methodName) + # find output dir + p: Path = Path(__file__).parent + while "output" not in [x.stem for x in p.iterdir() if x.is_dir()]: + p = p.parent + p = p / "output" + self.output_dir = Path(p, Path(__file__).stem.replace(".py", "")) + if not self.output_dir.exists(): + self.output_dir.mkdir() + + def test_write_pdf_a_1b(self): + + # create empty Document + pdf = Document() + + # create empty Page + page = Page() + + # add Page to Document + pdf.append_page(page) + + # create PageLayout + layout: PageLayout = SingleColumnLayout(page) + + # add Paragraph + layout.add(Paragraph("Hello World!")) + + info_dictionary: Dictionary = Dictionary() + info_dictionary[Name("Title")] = String("Lorem Ipsum (T)") + info_dictionary[Name("Subject")] = String("Lorem Ipsum (S)") + info_dictionary[Name("Creator")] = String("Joris Schellekens (C)") + info_dictionary[Name("Author")] = String("Joris Schellekens (A)") + info_dictionary[Name("Keywords")] = String("Lorem Ipsum Dolor Sit Amet") + pdf["XRef"]["Trailer"][Name("Info")] = info_dictionary + + # attempt to store PDF + out_file = self.output_dir / "output_001.pdf" + with open(out_file, "wb") as in_file_handle: + PDF.dumps(in_file_handle, pdf, "PDF/A-1b") + + def test_re_open_pdfa_1_b(self): + + # attempt to re-open PDF + out_file = self.output_dir / "output_001.pdf" + with open(out_file, "rb") as in_file_handle: + pdf = PDF.loads(in_file_handle) + + # assert XMP meta data + xmp = pdf.get_xmp_document_info() + assert xmp.get_title() == "Lorem Ipsum (T)" + assert xmp.get_creator() == "Joris Schellekens (C)" + assert xmp.get_author() == "Joris Schellekens (A)" + assert xmp.get_subject() == "Lorem Ipsum (S)" + assert xmp.get_keywords() == "Lorem Ipsum Dolor Sit Amet" + + def test_re_save_pdf_a_1_b(self): + + # attempt to re-open PDF + out_file = self.output_dir / "output_001.pdf" + with open(out_file, "rb") as in_file_handle: + pdf = PDF.loads(in_file_handle) + + # attempt to store PDF + out_file = self.output_dir / "output_002.pdf" + with open(out_file, "wb") as in_file_handle: + PDF.dumps(in_file_handle, pdf, "PDF/A-1b") + + # attempt to re-open PDF + with open(out_file, "rb") as in_file_handle: + pdf = PDF.loads(in_file_handle) + + # assert XMP meta data + xmp = pdf.get_xmp_document_info() + assert xmp.get_title() == "Lorem Ipsum (T)" + assert xmp.get_creator() == "Joris Schellekens (C)" + assert xmp.get_author() == "Joris Schellekens (A)" + assert xmp.get_subject() == "Lorem Ipsum (S)" + assert xmp.get_keywords() == "Lorem Ipsum Dolor Sit Amet" + + compare_visually_to_ground_truth(out_file) diff --git a/tests/pdf/page/shape/test_page_has_empty_resource_dictionary.py b/tests/pdf/page/shape/test_page_has_empty_resource_dictionary.py index 9169a41a9..b36ca2be0 100644 --- a/tests/pdf/page/shape/test_page_has_empty_resource_dictionary.py +++ b/tests/pdf/page/shape/test_page_has_empty_resource_dictionary.py @@ -14,7 +14,6 @@ class TestPageHasEmptyResourceDictionary(unittest.TestCase): - def __init__(self, methodName="runTest"): super().__init__(methodName) # find output dir @@ -40,10 +39,14 @@ def test_page_has_empty_resource_dictionary(self): H: Decimal = page.get_page_info().get_height() bottom_y: Decimal = H / Decimal(2) - Decimal(100) left_x: Decimal = W / Decimal(2) - Decimal(100) - bounding_box: Rectangle = Rectangle(left_x, bottom_y, Decimal(200), Decimal(200)) - Shape(LineArtFactory.regular_n_gon(bounding_box, 5), - stroke_color=HexColor("56cbf9"), - fill_color=HexColor("56cbf9")).layout(page, bounding_box) + bounding_box: Rectangle = Rectangle( + left_x, bottom_y, Decimal(200), Decimal(200) + ) + Shape( + LineArtFactory.regular_n_gon(bounding_box, 5), + stroke_color=HexColor("56cbf9"), + fill_color=HexColor("56cbf9"), + ).layout(page, bounding_box) # attempt to store PDF with open(self.output_dir / "output_001.pdf", "wb") as out_file_handle: @@ -55,4 +58,4 @@ def test_page_has_empty_resource_dictionary(self): # check Resources dictionary page_dictionary: Dictionary = pdf["XRef"]["Trailer"]["Root"]["Pages"]["Kids"][0] assert page_dictionary is not None - assert "Resources" in page_dictionary \ No newline at end of file + assert "Resources" in page_dictionary