From e72a7dc2adfd744b79f6ab27f63fd146f30e085b Mon Sep 17 00:00:00 2001 From: Bryce Willey Date: Tue, 3 Feb 2026 14:48:32 -0500 Subject: [PATCH 1/2] Add bates number params to ALDocumentBundle Must be given to the `using` object block, and it sets a param on the ALDocumentBundle object that gets used when `as_pdf` is called. Addds defaults for all as well. --- docassemble/AssemblyLine/al_document.py | 30 +++++++++++++++++++++++++ 1 file changed, 30 insertions(+) diff --git a/docassemble/AssemblyLine/al_document.py b/docassemble/AssemblyLine/al_document.py index 08adcb05..1f577e15 100644 --- a/docassemble/AssemblyLine/al_document.py +++ b/docassemble/AssemblyLine/al_document.py @@ -1581,6 +1581,7 @@ def init(self, *pargs, **kwargs) -> None: self.auto_gather = False if "gathered" not in kwargs: self.gathered = True + self._set_default_attributes() self.initializeAttribute("cache", DALazyAttribute) self.always_enabled = hasattr(self, "enabled") and self.enabled # Pre-cache some DALazyTemplates we set up to aid translation that won't @@ -1589,6 +1590,24 @@ def init(self, *pargs, **kwargs) -> None: # When the key is "preview", append it to the file name self.suffix_to_append = "preview" + def _set_default_attributes(self) -> None: + if not hasattr(self, "add_page_numbers"): + self.add_page_numbers = False + if not hasattr(self, "page_number_prefix"): + self.page_number_prefix = "" + if not hasattr(self, "page_number_start"): + self.page_number_start = 1 + if not hasattr(self, "page_number_digits"): + self.page_number_digits = 5 + if not hasattr(self, "page_number_area"): + self.page_number_area = None + if not hasattr(self, "page_number_font_size"): + self.page_number_font_size = 10 + if not hasattr(self, "page_number_offset_horizontal"): + self.page_number_offset_horizontal = 15 + if not hasattr(self, "page_number_offset_vertical"): + self.page_number_offset_vertical = 15 + def as_pdf( self, key: str = "final", @@ -1648,6 +1667,17 @@ def as_pdf( filename=f"{base_name(self.filename)}{append_suffix}.pdf", pdfa=pdfa, ) + if hasattr(self, "add_page_numbers") and self.add_page_numbers: + self._set_default_attributes() + pdf.bates_number( + prefix=self.page_number_prefix, + start=self.page_number_start, + digits=self.page_number_digits, + area=self.page_number_area, + font_size=self.page_number_font_size, + offset_horizontal=self.page_number_offset_horizontal, + offset_vertical=self.page_number_offset_vertical, + ) pdf.title = self.title setattr(self.cache, safe_key, pdf) From 20cb67bf14fb63e3f219935e0cc2c923820cc29a Mon Sep 17 00:00:00 2001 From: Bryce Willey Date: Thu, 5 Mar 2026 13:43:55 -0500 Subject: [PATCH 2/2] Add proper bates numbers support to ALExhibitDocument Passes along all of the bates parameters through all of the exhibit document classes (ALExhibitDocument, ALExhibitList, ALExhibit). --- docassemble/AssemblyLine/al_document.py | 88 +++++++++++++++++-- .../data/questions/test_aldocument.yml | 2 +- 2 files changed, 82 insertions(+), 8 deletions(-) diff --git a/docassemble/AssemblyLine/al_document.py b/docassemble/AssemblyLine/al_document.py index 1f577e15..be533aeb 100644 --- a/docassemble/AssemblyLine/al_document.py +++ b/docassemble/AssemblyLine/al_document.py @@ -2768,9 +2768,14 @@ def as_pdf( self, *, refresh: bool = False, - prefix: str = "", pdfa: bool = False, add_page_numbers: bool = True, + page_number_prefix: str = "", + page_number_digits: int = 5, + page_number_area=None, + page_number_font_size: float = 10, + page_number_offset_horizontal: float = 15, + page_number_offset_vertical: float = 15, add_cover_page: bool = True, filename: Optional[str] = None, append_matching_suffix: bool = True, @@ -2782,9 +2787,14 @@ def as_pdf( Args: refresh (bool): If True, forces the exhibit to refresh before generating the PDF. (unused, provided for signature compatibility) - prefix (str): Prefix for Bates numbering if 'add_page_numbers' is True. pdfa (bool): If True, the generated PDF will be in PDF/A format. add_page_numbers (bool): If True, apply Bates numbering starting from 'self.start_page'. + page_number_prefix (str): If add_page_numbers is True, this gets added to the beginning on the bates number each page (e.g. `EX-`). + page_number_digits (int): How many digits (i.e. leading 0s) that the bates number will have + page_number_area (str): Where on the page the bates number will go ("TOP_LEFT", "TOP_RIGHT", "BOTTOM_LEFT", "BOTTOM_RIGHT" (default)) + page_number_font_size (float): How big the bates page number will be in points (default is 10). + page_number_offset_horizontal (float): The number of pixels that the bates page number is offset from the left / right of the page. + page_number_offset_vertical (float): The number of pixels that the bates page number is offset from the top / bottom of the page. add_cover_page (bool): If True, prepend the exhibit with a cover page. filename (Optional[str]): Custom filename for the generated PDF. Default is "exhibits.pdf". append_matching_suffix (bool): If True, appends a suffix to the filename based on certain matching criteria. @@ -2812,7 +2822,15 @@ def as_pdf( ) if add_page_numbers: - concatenated_pages.bates_number(prefix=prefix, start=self.start_page) + concatenated_pages.bates_number( + start=self.start_page, + prefix=page_number_prefix, + digits=page_number_digits, + area=page_number_area, + font_size=page_number_font_size, + offset_horizontal=page_number_offset_horizontal, + offset_vertical=page_number_offset_vertical, + ) setattr(self._cache, safe_key, concatenated_pages) return getattr(self._cache, safe_key) @@ -2946,6 +2964,12 @@ def as_pdf( filename="file.pdf", pdfa: bool = False, add_page_numbers: bool = False, + page_number_prefix: str = "", + page_number_digits: int = 5, + page_number_area=None, + page_number_font_size: float = 10, + page_number_offset_horizontal: float = 15, + page_number_offset_vertical: float = 15, toc_pages: int = 0, append_matching_suffix: bool = True, ) -> DAFile: @@ -2956,6 +2980,12 @@ def as_pdf( filename (str): Desired filename for the generated PDF. pdfa (bool): If True, generates the PDF in PDF/A format. add_page_numbers (bool): If True, adds page numbers to the generated PDF. + page_number_prefix (str): What the bates number added to each page should start with (e.g. `EX-`). + page_number_digits (int): How many digits (i.e. leading 0s) that the bates number will have + page_number_area (str): Where on the page the bates number will go ("TOP_LEFT", "TOP_RIGHT", "BOTTOM_LEFT", "BOTTOM_RIGHT" (default)) + page_number_font_size (float): How big the bates page number will be in points (default is 10). + page_number_offset_horizontal (float): The number of pixels that the bates page number is offset from the left / right of the page. + page_number_offset_vertical (float): The number of pixels that the bates page number is offset from the top / bottom of the page. toc_pages (int): Expected number of pages in the table of contents. append_matching_suffix (bool): If True, appends matching suffix to the filename. @@ -2967,12 +2997,19 @@ def as_pdf( exhibit.cover_page if self.include_table_of_contents and toc_pages != 1: self._update_page_numbers(toc_guess_pages=toc_pages) + if not page_number_prefix and self.bates_prefix: + page_number_prefix = self.bates_prefix return pdf_concatenate( [ exhibit.as_pdf( add_cover_page=self.include_exhibit_cover_pages, add_page_numbers=add_page_numbers, - prefix=self.bates_prefix, + page_number_prefix=page_number_prefix, + page_number_digits=page_number_digits, + page_number_area=page_number_area, + page_number_font_size=page_number_font_size, + page_number_offset_horizontal=page_number_offset_horizontal, + page_number_offset_vertical=page_number_offset_vertical, ) for exhibit in self ], @@ -3105,12 +3142,19 @@ class ALExhibitDocument(ALDocument): has_addendum: bool auto_labeler: Callable auto_ocr: bool + # Deprecated bates_prefix: str maximum_size: int maximum_size_per_doc: int suffix_to_append: str exhibits: ALExhibitList table_of_contents: DAFile + page_number_prefix: str + page_number_digits: int + page_number_area: str | None + page_number_font_size: float + page_number_offset_horizontal: float + page_number_offset_vertical: float def init(self, *pargs, **kwargs) -> None: """Standard DAObject init method. @@ -3125,8 +3169,6 @@ def init(self, *pargs, **kwargs) -> None: self.exhibits.auto_labeler = self.auto_labeler if hasattr(self, "auto_ocr"): self.exhibits.auto_ocr = self.auto_ocr - if hasattr(self, "bates_prefix"): - self.exhibits.bates_prefix = self.bates_prefix if hasattr(self, "include_exhibit_cover_pages"): self.exhibits.include_exhibit_cover_pages = self.include_exhibit_cover_pages else: @@ -3144,13 +3186,30 @@ def init(self, *pargs, **kwargs) -> None: else: self.include_table_of_contents = True self.exhibits.include_table_of_contents = True + if hasattr(self, "bates_prefix"): + self.exhibits.bates_prefix = self.bates_prefix if not hasattr(self, "add_page_numbers"): self.add_page_numbers = False + self._set_default_attributes() self.has_addendum = False if not hasattr(self, "suffix_to_append"): # When the key is "preview", append it to the file name self.suffix_to_append = "preview" + def _set_default_attributes(self) -> None: + if not hasattr(self, "page_number_prefix"): + self.page_number_prefix = "" + if not hasattr(self, "page_number_digits"): + self.page_number_digits = 5 + if not hasattr(self, "page_number_area"): + self.page_number_area = None + if not hasattr(self, "page_number_font_size"): + self.page_number_font_size = 10 + if not hasattr(self, "page_number_offset_horizontal"): + self.page_number_offset_horizontal = 15 + if not hasattr(self, "page_number_offset_vertical"): + self.page_number_offset_vertical = 15 + def has_overflow(self) -> bool: """ Check if there is any overflow in the document. @@ -3225,12 +3284,21 @@ def as_pdf( filename = base_name(self.filename) + ".pdf" if len(self.exhibits): + self._set_default_attributes() if self.include_table_of_contents: toc_pages = self.table_of_contents.num_pages() return pdf_concatenate( self.table_of_contents, self.exhibits.as_pdf( - add_page_numbers=self.add_page_numbers, toc_pages=toc_pages + add_page_numbers=self.add_page_numbers, + page_number_prefix=self.page_number_prefix, + page_number_digits=self.page_number_digits, + page_number_area=self.page_number_area, + page_number_font_size=self.page_number_font_size, + page_number_offset_horizontal=self.page_number_offset_horizontal, + page_number_offset_vertical=self.page_number_offset_vertical, + toc_pages=toc_pages, + pdfa=pdfa, ), filename=filename, pdfa=pdfa, @@ -3238,6 +3306,12 @@ def as_pdf( else: return self.exhibits.as_pdf( add_page_numbers=self.add_page_numbers, + page_number_prefix=self.page_number_prefix, + page_number_digits=self.page_number_digits, + page_number_area=self.page_number_area, + page_number_font_size=self.page_number_font_size, + page_number_offset_horizontal=self.page_number_offset_horizontal, + page_number_offset_vertical=self.page_number_offset_vertical, filename=filename, pdfa=pdfa, ) diff --git a/docassemble/AssemblyLine/data/questions/test_aldocument.yml b/docassemble/AssemblyLine/data/questions/test_aldocument.yml index 667a5262..4c04c92f 100644 --- a/docassemble/AssemblyLine/data/questions/test_aldocument.yml +++ b/docassemble/AssemblyLine/data/questions/test_aldocument.yml @@ -31,7 +31,7 @@ code: | --- objects: # No addenda, all start enabled - - multi_bundle_1: ALDocumentBundle.using(elements=[pdf_1, docx_1, notice_to_quit], title="Multiple docs 1", filename="multi_bundle_1" ) + - multi_bundle_1: ALDocumentBundle.using(elements=[pdf_1, docx_1, notice_to_quit], title="Multiple docs 1", filename="multi_bundle_1", add_page_numbers=True, page_numbers_font_size=20 ) - single_pdf_bundle_1: ALDocumentBundle.using(elements=[pdf_1], title="One pdf", filename="single_pdf_1" ) - single_docx_bundle_1: ALDocumentBundle.using(elements=[docx_1], title="One docx", filename="single_docx_1" ) ---