### Configuration File Example Source: https://pdfimpose.readthedocs.io/en/latest/usage Example of a YAML configuration file for pdfimpose, specifying general settings and schema-specific options. ```yaml [general] schema = hardcover files = foo.pdf bar.pdf [hardcover] imargin = 1cm omargin = .5cm ``` -------------------------------- ### Getting Schema Help Source: https://pdfimpose.readthedocs.io/en/latest/_sources/usage.rst.txt Run this command to get detailed help and information about the available options for a specific imposition schema. ```bash pdfimpose SCHEMA --help ``` -------------------------------- ### Get Schema Help Source: https://pdfimpose.readthedocs.io/en/latest/usage Run this command to get detailed help for a specific imposition schema, including available options. ```bash pdfimpose SCHEMA --help ``` -------------------------------- ### Install Pdfimpose using pip Source: https://pdfimpose.readthedocs.io/en/latest/_sources/index.rst.txt Install the Pdfimpose package from PyPI using pip. This is the recommended method for most users. ```bash python -m pip install pdfimpose ``` -------------------------------- ### Install Pdfimpose from source Source: https://pdfimpose.readthedocs.io/en/latest/_sources/index.rst.txt Install the Pdfimpose package from its source code. This is useful for developers or users who need the latest changes. ```bash python -m pip install . ``` -------------------------------- ### Install Pdfimpose on Debian Source: https://pdfimpose.readthedocs.io/en/latest Install the pdfimpose package on Debian systems by building a .deb package. ```bash python setup.py --command-packages=stdeb.command bdist_deb sudo dpkg -i deb_dist/python-pdfimpose__all.deb ``` -------------------------------- ### Configuration File Example (cfg) Source: https://pdfimpose.readthedocs.io/en/latest/_sources/usage.rst.txt An example of a configuration file in YAML format for pdfimpose. It specifies the schema and files to process, along with schema-specific margins. ```cfg [general] schema = hardcover files = foo.pdf bar.pdf [hardcover] imargin = 1cm omargin = .5cm ``` -------------------------------- ### Example: Saddle Schema Imposition Source: https://pdfimpose.readthedocs.io/en/latest/_sources/usage.rst.txt An example demonstrating how to impose a PDF file using the 'saddle' schema, suitable for magazine-like layouts. ```bash pdfimpose saddle foo.pdf ``` -------------------------------- ### Argument Parsing Setup Source: https://pdfimpose.readthedocs.io/en/latest/_modules/pdfimpose/schema Sets up command-line arguments for the pdfimpose tool, including options for page counts, margins, output files, and signatures. Handles default values and argument validation. ```python def add_arguments(self, options): if "pages" in options: self.add_argument( "--pages", "-p", help=( "Number of pages to keep as first pages. " "Useful to keep the title page as a title page." ), default=default, type=_type_positive_int, ) if "last" in options: self.add_argument( "--last", "-l", help=( "Number of pages to keep as last pages. " "Useful to keep the back cover as a back cover." ), type=_type_positive_int, default=0, ) if "imargin" in options: self.add_argument( "--imargin", "-m", help=( "Margin (in points) added to input pages when imposed on the " "output page. Defaults to 0pt." ), default=0, type=_type_length, ) if "omargin" in options: self.add_argument( "--omargin", "-M", help="Margin (in points) added to output pages. Defaults to 0pt", default=0, type=_type_length, ) if "mark" in options: self.add_argument( "--mark", "-k", help="List of marks to add (crop or bind). Can be given multiple times.", choices=["bind", "crop"], action="append", default=[], ) if "mark-crop" in options: self.add_argument( "--mark", "-k", help="Use '--mark=crop' to add crop marks.", choices=["crop"], action="append", default=[], ) self.add_argument( "--output", "-o", metavar="FILE", help=( 'Destination file. Default is "-impose" appended to first source file. ' 'If "-", print to standard output.' ), type=str, ) if "signature" in options: group.add_argument( "--signature", "-s", metavar="WIDTHxHEIGHT", type=_type_signature, help="Size of the destination pages (relative to the source page), e.g. 2x3.", default=None, ) if "cutsignature" in options: group.add_argument( "--signature", "-s", metavar="WIDTHxHEIGHT", type=_type_signature, help=textwrap.dedent(""" Size of the destination pages, e.g. 2x3. This represents the number of sheets you will get after having cut each printed sheet. """), default=None, ) ``` ```python def parse_args(self, *args, **kwargs): args = super().parse_args(*args, **kwargs) # Add missing extension (turn "foo" into "foo.pdf", if it exists) for i, path in enumerate(args.files): if (not os.path.exists(path)) and os.path.exists(f"{path}.pdf"): args.files[i] = f"{path}.pdf" # Process output file, if not a file if args.output is None and args.files: source = pathlib.Path(args.files[0]) args.output = source.parent / f"{source.stem}-impose{source.suffix}" elif args.output is None or args.output == "-": args.output = io.BytesIO() # If no source file given: read from standard input. if not args.files: args.files = [io.BytesIO(sys.stdin.buffer.read())] return args ``` -------------------------------- ### PDFimpose Argument Parser Setup Source: https://pdfimpose.readthedocs.io/en/latest/_modules/pdfimpose/schema Configures the main argument parser for pdfimpose, setting default program name, formatter class, and epilog. ```python if "prog" not in kwargs: kwargs["prog"] = f"pdfimpose {subcommand}" if "formatter_class" not in kwargs: kwargs["formatter_class"] = argparse.RawTextHelpFormatter if "epilog" not in kwargs: kwargs["epilog"] = ( "For more information, see: https://pdfimpose.readthedocs.io" ) super().__init__(**kwargs) ``` -------------------------------- ### Build Debian package for Pdfimpose Source: https://pdfimpose.readthedocs.io/en/latest/_sources/index.rst.txt Build a Debian package for Pdfimpose using stdeb. This is for users who prefer to install packages via dpkg. ```bash python setup.py --command-packages=stdeb.command bdist_deb sudo dpkg -i deb_dist/python-pdfimpose__all.deb ``` -------------------------------- ### Custom ArgumentParser Class Source: https://pdfimpose.readthedocs.io/en/latest/_modules/pdfimpose/schema Extends the standard argparse.ArgumentParser to include common configurations for PDF imposition schemas. This class simplifies the setup of argument parsers for different imposition commands. ```python class ArgumentParser(argparse.ArgumentParser): """A \"pre-seeded\" argument parser, with configuration common to several schemas.""" # pylint: disable=line-too-long def __init__(self, subcommand, options=None, **kwargs): # pylint: disable=too-many-branches if options is None: options = [] ``` -------------------------------- ### Equivalent Command from Configuration Source: https://pdfimpose.readthedocs.io/en/latest/_sources/usage.rst.txt This command line is the equivalent of applying the settings defined in the 'foo.cfg' configuration file. It demonstrates how file-based configuration translates to command-line arguments. ```bash pdfimpose hardcover --imargin 1cm --omargin .5cm foo.pdf bar.pdf ``` -------------------------------- ### Apply Imposition using Configuration Source: https://pdfimpose.readthedocs.io/en/latest/usage This command applies imposition settings from a configuration file, equivalent to specifying options on the command line. ```bash pdfimpose apply foo.cfg ``` -------------------------------- ### SaddleImpositor Initialization and Imposition Source: https://pdfimpose.readthedocs.io/en/latest/_modules/pdfimpose/schema/saddle This code block demonstrates the initialization of the SaddleImpositor class and its subsequent use to impose files onto an output. It handles parameter validation and computes folding and margin settings. ```python if mark is None: mark = [] if (signature, size, folds).count(None) <= 1: raise ValueError( "Only one of `size`, `folds` and `signature` arguments can be other than `None`." ) if folds is None: files = pdf.Reader(files) if bind in ("top", "bottom"): sourcesize = (files.size[1], files.size[0]) else: sourcesize = (files.size[0], files.size[1]) # Compute folds (from signature and format), and remove signature and format if isinstance(size, str): size = tuple(float(dim) for dim in papersize.parse_papersize(size)) folds, size = _any2folds(signature, size, inputsize=sourcesize) if ( size is not None and imargin == 0 and creep == nocreep # pylint: disable=comparison-with-callable ): omargin = _folds2margins(size, sourcesize, folds, imargin) SaddleImpositor( omargin=omargin, imargin=imargin, mark=mark, last=last, bind=bind, folds=folds, creep=creep, group=group, ).impose(files, output) ``` -------------------------------- ### Calculate Crop Marks for Card Imposition Source: https://pdfimpose.readthedocs.io/en/latest/_modules/pdfimpose/schema/cards Generates coordinates for crop marks based on input and output dimensions, margins, and signature layout. This is used to guide cutting after printing. ```python def crop_marks(self, *, number, total, matrix, outputsize, inputsize): # pylint: disable=too-many-arguments left, right, top, bottom = self._crop_space() for x in range(self.signature[0]): yield ( (self.omargin.left + x * (inputsize[0] + self.imargin), 0), ( self.omargin.left + x * (inputsize[0] + self.imargin), self.omargin.top - top, ), ) yield ( (self.omargin.left + (x + 1) * inputsize[0] + x * self.imargin, 0), ( self.omargin.left + (x + 1) * inputsize[0] + x * self.imargin, self.omargin.top - top, ), ) yield ( (self.omargin.left + x * (inputsize[0] + self.imargin), outputsize[1]), ( self.omargin.left + x * (inputsize[0] + self.imargin), outputsize[1] - self.omargin.bottom + bottom, ), ) yield ( ( self.omargin.left + (x + 1) * inputsize[0] + x * self.imargin, outputsize[1], ), ( self.omargin.left + (x + 1) * inputsize[0] + x * self.imargin, outputsize[1] - self.omargin.bottom + bottom, ), ) for y in range(self.signature[1]): yield ( (0, self.omargin.top + y * (inputsize[1] + self.imargin)), ( self.omargin.left - left, self.omargin.top + y * (inputsize[1] + self.imargin), ), ) yield ((0, self.omargin.top + (y + 1) * inputsize[1] + y * self.imargin)), ( self.omargin.left - left, self.omargin.top + (y + 1) * inputsize[1] + y * self.imargin, ) yield ( (outputsize[0], self.omargin.top + y * (inputsize[1] + self.imargin)), ( outputsize[0] - self.omargin.right + right, self.omargin.top + y * (inputsize[1] + self.imargin), ), ) yield ( ( outputsize[0], self.omargin.top + (y + 1) * inputsize[1] + y * self.imargin, ) ), ( outputsize[0] - self.omargin.right + right, self.omargin.top + (y + 1) * inputsize[1] + y * self.imargin, ) ``` -------------------------------- ### Apply Imposition with Configuration File Source: https://pdfimpose.readthedocs.io/en/latest/usage This command applies imposition settings, optionally using a configuration file and specifying the schema and PDF files to process. ```bash pdfimpose apply [-h] [--schema SCHEMA] [CONF] [PDF ...] ``` -------------------------------- ### Initialize and Impose with CardsImpositor Source: https://pdfimpose.readthedocs.io/en/latest/_modules/pdfimpose/schema/cards Instantiates the CardsImpositor class with various margin and layout options, then uses the impose method to process input files and generate an output file. ```python CardsImpositor( omargin=omargin, imargin=imargin, mark=mark, signature=signature, back=back, bind=bind, ).impose(files, output) ``` -------------------------------- ### OnePageZineImpositor Class Initialization Source: https://pdfimpose.readthedocs.io/en/latest/_modules/pdfimpose/schema/onepagezine Initializes the OnePageZineImpositor with various layout and imposition parameters. Use this to configure the imposition process before applying it to files. ```python OnePageZineImpositor( omargin=omargin, last=last, mark=mark, bind=bind, ).impose(files, output) ``` -------------------------------- ### AbstractImpositor Initialization Source: https://pdfimpose.readthedocs.io/en/latest/_modules/pdfimpose/schema Handles the initialization of the AbstractImpositor class, converting various margin input types to a Margins object. Ensures consistent margin handling. ```python def __post_init__(self): if isinstance(self.omargin, numbers.Real): self.omargin = Margins(self.omargin) elif isinstance(self.omargin, decimal.Decimal): self.omargin = Margins(float(self.omargin)) elif isinstance(self.omargin, str): self.omargin = Margins(float(papersize.parse_length(self.omargin))) ``` -------------------------------- ### Basic PDF Imposition Command Source: https://pdfimpose.readthedocs.io/en/latest/_sources/usage.rst.txt Use this command to impose a PDF file with a chosen schema. Replace SCHEMA with the desired imposition schema and foo.pdf with your PDF file. ```bash pdfimpose SCHEMA foo.pdf ``` -------------------------------- ### CopyCutFoldImpositor Class Source: https://pdfimpose.readthedocs.io/en/latest/_modules/pdfimpose/schema/copycutfold The `CopyCutFoldImpositor` class handles the logic for the 'copycutfold' imposition schema. It defines methods for calculating blank pages, generating base matrices for page arrangements, and creating the final sequence of matrices for the imposed output. ```APIDOC ## Class CopyCutFoldImpositor ### Description Performs imposition of source files, with the 'copycutfold' schema. This class manages the arrangement and transformation of pages to facilitate cutting and folding for book production. ### Methods - **blank_page_number(source)**: Calculates the number of blank pages needed based on the source page number. - **base_matrix(total)**: Yields the initial matrices representing the arrangement of source pages on output pages for recto and verso sides. - **matrixes(pages)**: Generates the sequence of matrices for the entire imposition, handling grouping and repetition of page arrangements. ``` -------------------------------- ### Open PDF Files with PDFImpose Source: https://pdfimpose.readthedocs.io/en/latest/_modules/pdfimpose/schema/cards Initializes a PdfReader object to process input files, optionally applying a back-side configuration. ```python def open_pdf(self, files): # pylint: disable=arguments-differ return PdfReader(files, back=self.back) ``` -------------------------------- ### PDFImpose Apply Command Syntax Source: https://pdfimpose.readthedocs.io/en/latest/_sources/usage.rst.txt The general syntax for the 'pdfimpose apply' command, which allows for storing options in a configuration file. Arguments like schema, configuration file, and PDF files are optional. ```bash pdfimpose apply [-h] [--schema SCHEMA] [CONF] [PDF ...] ``` -------------------------------- ### CardsImpositor Class Source: https://pdfimpose.readthedocs.io/en/latest/_modules/pdfimpose/schema/cards Performs imposition of source files into a flash card format. It configures margins, signature layout, and binding direction. ```python @dataclasses.dataclass class CardsImpositor(AbstractImpositor): """Perform imposition of source files, with the 'card' schema.""" imargin: float = 0 signature: tuple[int] = (0, 0) back: str = "" bind: str = "left" def __post_init__(self): super().__post_init__() if isinstance(self.imargin, decimal.Decimal): self.imargin = float(self.imargin) elif isinstance(self.imargin, str): self.imargin = float(papersize.parse_length(self.imargin)) def blank_page_number(self, source): pagesperpage = 2 * self.signature[0] * self.signature[1] if source % pagesperpage == 0: return 0 return pagesperpage - (source % pagesperpage) def base_matrix(self, total): """Yield a single matrix. This matrix contains the arrangement of source pages on the output pages. """ # pylint: disable=unused-argument recto, verso = ( [[None for _ in range(self.signature[1])] for _ in range(self.signature[0])] for _ in range(2) ) for i, coord in enumerate(itertools.product(*map(range, self.signature))): x, y = coord recto[x][y] = Page( 2 * i, left=self.omargin.left if x == 0 else self.imargin / 2, right=( self.omargin.right if x == self.signature[0] - 1 else self.imargin / 2 ), top=self.omargin.top if y == 0 else self.imargin / 2, bottom=( self.omargin.bottom if y == self.signature[1] - 1 else self.imargin / 2 ), ) verso[self.signature[0] - x - 1][y] = Page( 2 * i + 1, left=( self.omargin.left if x == self.signature[0] - 1 else self.imargin / 2 ), right=self.omargin.right if x == 0 else self.imargin / 2, top=self.omargin.top if y == 0 else self.imargin / 2, bottom=( self.omargin.bottom if y == self.signature[1] - 1 else self.imargin / 2 ), rotate=0 if self.bind in ("left", "right") else 180, ) yield Matrix(recto) yield Matrix(verso) def matrixes(self, pages: int): step = 2 * self.signature[0] * self.signature[1] assert pages % step == 0 ``` -------------------------------- ### PDF Writing Context Manager Source: https://pdfimpose.readthedocs.io/en/latest/_modules/pdfimpose/schema A context manager for writing output PDF files to disk. Simplifies the process of saving generated PDFs. ```python @staticmethod @contextlib.contextmanager def write(output): """Write output file to disk.""" with pdf.Writer(output) as writer: yield writer ``` -------------------------------- ### Generate Matrixes for Imposition Source: https://pdfimpose.readthedocs.io/en/latest/_modules/pdfimpose/schema/hardcover Generates a sequence of matrices for the entire book imposition process based on the total number of pages and signature configuration. Asserts that the total pages are a multiple of the pages per group. ```python def matrixes(self, pages: int): pages_per_group = ( 2 * self.signature[0] * self.signature[1] * self.fix_group(pages) ) assert pages % pages_per_group == 0 yield from self.stack_matrixes( list(self.group_matrixes(pages)), repeat=pages // pages_per_group, step=pages_per_group, ) ``` -------------------------------- ### CopyCutFoldImpositor Usage Source: https://pdfimpose.readthedocs.io/en/latest/_modules/pdfimpose/schema/copycutfold Illustrates the instantiation and usage of the CopyCutFoldImpositor class for imposing PDF files. It handles parameter validation and conversion, such as determining the signature from a given page size. ```python from pdfimpose.schema import copycutfold import pathlib import io # Example usage (assuming files and output are defined) # copycutfold.copycutfold( # files=["input1.pdf", "input2.pdf"], # output="output.pdf", # omargin="1cm", # imargin=10, # last=5, # mark=["crop"], # signature=(2, 3), # size="A4", # bind="left", # creep=lambda sheets: 0.5 * sheets, # group=4 # ) ``` -------------------------------- ### Generate Matrix Sequence for One Page Zine Source: https://pdfimpose.readthedocs.io/en/latest/_modules/pdfimpose/schema/onepagezine Generates a sequence of matrices for imposition, repeating the base matrix as needed. Asserts that the total number of pages is a multiple of 8. ```python def matrixes(self, pages: int): assert pages % 8 == 0 yield from self.stack_matrixes( list(self.base_matrix(pages)), step=8, repeat=pages // 8, ) ``` -------------------------------- ### Stacking Imposition Matrixes Source: https://pdfimpose.readthedocs.io/en/latest/_modules/pdfimpose/schema Iterates over copies of imposition matrixes, incrementing page numbers by a specified step. Useful for creating repeated imposition layouts. ```python def stack_matrixes(self, matrixes, step: int, repeat: int): """Iterate over copies of the matrixes, incrementing pages numbers by ``step``. :param list[Matrix] matrix: A list of matrixes. :param int repeat: Number of repetitions of the matrixes given in argument. :param int step: At each repetition, is page is increased by this number. For instance, given a single matrix containing pages from 1 to 8, with `repeat=3` and `step=8`, this method will yield: - one matrix with pages from 1 to 8; - one matrix with pages from 9 to 16; - one matrix with pages from 17 to 24. """ raise NotImplementedError() ``` -------------------------------- ### Iterate Matrix Stacking Source: https://pdfimpose.readthedocs.io/en/latest/_modules/pdfimpose/schema Generates stacked matrices for imposition, useful for creating repeating patterns. ```python def insert_matrixes(self, matrixes, repeat, step): """Iterates over "copies" of matrixes that can be inserted into each other. For instance, if `matrixes` is the single matrix 1|2, a set of sheets that can be inserted into each other (as in magazines, for instance), is: 1|8 2|7 3|6 4|5. :param matrixes: Matrixes to copy and insert into each other. :param int sheets: Number of inserted sheets. :param int pages: Total number of pages in the source document. :param int pagespersheet: Nomber of source pages per output sheets. """ # pylint: disable=no-self-use for i in range(repeat): for matrix in matrixes: yield matrix.stack(i * step) ``` -------------------------------- ### PDF Reader Utility Source: https://pdfimpose.readthedocs.io/en/latest/_modules/pdfimpose/schema A static method to open PDF files and return a list of pdf.Reader objects. Essential for accessing PDF content. ```python @staticmethod def open_pdf(files): """Open the PDF files, and return a list of :class:`pdf.Reader` objects.""" return pdf.Reader(files) ``` -------------------------------- ### Compute Signature for Imposition Source: https://pdfimpose.readthedocs.io/en/latest/_modules/pdfimpose/schema Calculates the optimal signature (number of source pages per destination page) and rotation needed to fit source pages onto destination pages. Raises an error if the source page is too large for the destination. ```python def compute_signature(source, dest): """Compute the signature to fit as many as possible sources in dest. :param tuple[float] source: Size of the source page. :param tuple[float] dest: Size of the destination page. Return a tuple ``(signature, rotated)``, where: - ``signature`` is a tuple of integers: ``(2, 3)`` means that the best fit is 2 source pages wide by 3 source pages tall on the destination page; - ``rotated`` is a boolean, indicating that the destination page has to be rotated to fit the signature. """ notrotated = ( math.floor(round(dest[0] / source[0], 6)), math.floor(round(dest[1] / source[1], 6)), ) rotated = ( math.floor(round(dest[1] / source[0], 6)), math.floor(round(dest[0] / source[1], 6)), ) if 0 in notrotated and 0 in rotated: raise UserError("The source page is too big to fit in the destination page.") if notrotated[0] * notrotated[1] > rotated[0] * rotated[1]: return notrotated, False return rotated, True ``` -------------------------------- ### PDFimpose Back Side Argument Configuration Source: https://pdfimpose.readthedocs.io/en/latest/_modules/pdfimpose/schema Adds the '--back' argument for specifying back sides of cards, with detailed usage explanations. ```python self.add_argument( "--back", "-b", help=("Back sides of cards.\n\n" \ "- If absent, pages of source files are considered to be : front1, back1, front2, back2, etc.\n" \ "- If a one-page file, source files are the front pages, and argument to this command is the common back side of all those pages.\n" \ "- If a file with as many page as the source files, pages of the source files are considered to be : front1, front2, front3, etc., while pages of the back file are considered to be : back1, back2, back3, etc.\n" \ "- If a file with any other number of pages, the behavior is the same as the previous item, excepted that the back pages are repeated as much as needed, and extra back pages are ignored.\n"), type=str, default="", ) ``` -------------------------------- ### HardcoverImpositor Class Source: https://pdfimpose.readthedocs.io/en/latest/_sources/lib/hardcover.rst.txt The HardcoverImpositor class is used to manage the creation of hardcover layouts. It likely contains methods for configuring and executing the imposition process. ```APIDOC ## Class: HardcoverImpositor ### Description Manages the creation of hardcover layouts. Provides functionality to configure and execute the imposition process for hardcover designs. ### Methods (Specific methods are not detailed in the provided source, but would typically be found here) ### Usage (Example usage would be provided here if available in the source) ``` -------------------------------- ### WireImpositor Class Source: https://pdfimpose.readthedocs.io/en/latest/_sources/lib/wire.rst.txt Represents the WireImpositor class used for PDF imposition. ```APIDOC ## Class: WireImpositor ### Description Provides functionality for PDF imposition using a wire schema. ### Methods (No specific methods are detailed in the source for direct user invocation beyond the class itself.) ### Usage (Refer to the `impose` function for usage examples.) ``` -------------------------------- ### CopyCutFoldImpositor Class Source: https://pdfimpose.readthedocs.io/en/latest/lib/copycutfold Represents the copy-cut-fold imposition schema. It is used to perform imposition of source files according to this schema. ```python class pdfimpose.schema.copycutfold.CopyCutFoldImpositor(_last: int = 0, omargin: ~pdfimpose.schema.Margins | str | ~numbers.Real | ~decimal.Decimal = , mark: list[str] = , bind: str = 'left', creep: ~typing.Callable[[int], float] = , imargin: str | ~numbers.Real | ~decimal.Decimal = 0, signature: tuple[int] = (0, 0), group: int = 0_)[source]¶ ``` -------------------------------- ### Perform PDF Imposition Source: https://pdfimpose.readthedocs.io/en/latest/_modules/pdfimpose/schema Executes the PDF imposition process, reading input files, creating new pages, inserting source pages, and optionally drawing crop and bind marks. ```python def impose( self, files: Sequence[str, pathlib.Path, io.BytesIO], output: str | pathlib.Path | io.BytesIO, ): """Perform imposition. :param Sequence[str, pathlib.Path, io.BytesIO] files: List of files (as names or io.BytesIO tream) to impose. :param str | pathlib.Path | io.BytesIOstr output: Name of the output file. Warning: You might have noticed that `files` can also be a list of :class:`fitz.Document` or a :class:`pdfimpose.pdf.Reader` object. This is an implementation detail, and can change without notice in the future. Use at your own risk. """ with self.read(files) as reader, self.write(output) as writer: for matrix in self.matrixes(len(reader)): destpage_size = matrix.pagesize(reader.size) destpage = writer.new_page(*destpage_size) for x, y in matrix.coordinates(): if reader[matrix[x, y].number] is None: # Blank page continue sourcepage = reader[matrix[x, y].number] writer.insert( destpage, sourcepage, topleft=matrix.topleft((x, y), reader.size), rotate=matrix[x, y].rotate, ) if "crop" in self.mark: for point1, point2 in self.crop_marks( number=destpage, total=len(reader), matrix=matrix, outputsize=destpage_size, inputsize=reader.size, ): writer[destpage].draw_line(point1, point2) if "bind" in self.mark: for rect in self.bind_marks( number=destpage, total=len(reader), matrix=matrix, outputsize=destpage_size, inputsize=reader.size, ): writer.draw_rectangle(destpage, rect) writer.set_metadata(reader) ``` -------------------------------- ### Impose Source Files into Output Cards Source: https://pdfimpose.readthedocs.io/en/latest/_modules/pdfimpose/schema/cards Main function to perform card imposition. It takes source files and configures output margins, signature layout, and page size. ```python def impose( files: Sequence[str | pathlib.Path | io.BytesIO], output: str | pathlib.Path | io.BytesIO, *, imargin=0, omargin=0, mark=None, signature=None, size=None, back="", bind: str = "left", ): # pylint: disable=too-many-arguments """Perform imposition of source files into an output file, to be cut as flash cards. :param Sequence[str|pathlib.Path|io.BytesIO] files: List of source files (as filenames (strings or :class:`pathlib.Path`), or :class:`io.BytesIO` streams). :param str | pathlib.Path | io.BytesIO output: Output file. :param float omargin: Output margin, in pt. Can also be a :class:`pdfimpose.schema.Margins` object. :param float imargin: Input margin, in pt. :param list[str] mark: List of marks to add. Only crop marks are supported (``mark=['crop']``); everything else is silently ignored. :param tuple[int] signature: Layout of source pages on output pages. For instance, ``(2, 3)`` means that each output page will contain 2 columns and 3 rows of source pages. Incompatible with option `size`. :param str|tuple[float] size: Size of the output page. Signature is computed to fit the page. This option is incompatible with `signature`. :param Optional[str] back: Back sides of cards. See --back help for more information. :param Optional[str] bind: Bind side: if "left" or "right", once printed, the card is to be flipped horizontally; if "top" or "bottom", it is to be flipped vertically. """ if mark is None: mark = [] files = PdfReader(files, back=back) if signature is None: if isinstance(size, str): size = tuple(float(dim) for dim in papersize.parse_papersize(size)) signature, omargin = size2signature( size, sourcesize=files.size, imargin=imargin, omargin=omargin, ) ``` -------------------------------- ### PDFimpose Format Size Argument Source: https://pdfimpose.readthedocs.io/en/latest/_modules/pdfimpose/schema Adds the '--format' argument to specify the destination page format for source pages. ```python group.add_argument( "--format", "-f", dest="size", type=_type_papersize, help=( "Put as much source pages into the destination page of the given format. " "Note that margins are ignored when computing this; " "if options --imargin and --omargin are set, " "the resulting file might be larger than the required format." ), default=None, ) ``` -------------------------------- ### AbstractImpositor Class Source: https://pdfimpose.readthedocs.io/en/latest/_modules/pdfimpose/schema The AbstractImpositor class provides a base implementation for PDF imposition schemas, defining common methods and properties. ```APIDOC class AbstractImpositor: """Perform imposition of source files onto output file. This is an abstract method, with common methods, to be inherited by imposition schemas. """ last: int = 0 omargin: Margins | str | numbers.Real | decimal.Decimal = dataclasses.field( default_factory=Margins ) mark: list[str] = dataclasses.field(default_factory=list) creep = nocreep def __post_init__(self): """Initialize margins based on input type.""" if isinstance(self.omargin, numbers.Real): self.omargin = Margins(self.omargin) elif isinstance(self.omargin, decimal.Decimal): self.omargin = Margins(float(self.omargin)) elif isinstance(self.omargin, str): self.omargin = Margins(float(papersize.parse_length(self.omargin))) def blank_page_number(self, source): """Return the number of blank pages to insert. For instance, if the source document has 13 pages, and the output document fits 8 source pages per destination pages, 3 source blank pages have to be inserted. """ raise NotImplementedError() def matrixes(self, pages: int): """Yield the list of all imposition matrixes.""" raise NotImplementedError() def _crop_space(self): """Calculate crop space based on output margins.""" left = right = bottom = top = 20 if top > self.omargin.top: top = self.omargin.top / 2 if bottom > self.omargin.bottom: bottom = self.omargin.bottom / 2 if left > self.omargin.left: left = self.omargin.left / 2 if right > self.omargin.right: right = self.omargin.right / 2 return left, right, bottom, top def crop_marks(self, *, number, total, matrix, outputsize, inputsize): """Yield coordinates of crop marks.""" # pylint: disable=unused-argument, no-self-use, too-many-arguments yield from [] def bind_marks(self, *, number, total, matrix, outputsize, inputsize): """Yield coordinates of bind marks.""" # pylint: disable=unused-argument, no-self-use, too-many-arguments yield from [] @staticmethod def open_pdf(files): """Open the PDF files, and return a list of :class:`pdf.Reader` objects.""" return pdf.Reader(files) @contextlib.contextmanager def read(self, files): """Context manager to read a list of files. Return an object that can be processed as a list of pages (regardless of the original files). At the end of this function, the return value has exactly the right number of pages to fit on a dest page. Note that `files` can be either a list of files, or a :class:`pdfimpose.pdf.Reader` object, but only the list of files (names or io.BytesIO streams) is supported: the other type is an implementation detail. """ if isinstance(files, pdf.Reader): opener = files else: opener = self.open_pdf(files) with opener as reader: if len(reader) == 0: raise UserError("Input files do not have any page.") reader.set_final_blank_pages( self.blank_page_number(len(reader)), len(reader) - self.last ) yield reader @staticmethod @contextlib.contextmanager def write(output): """Write output file to disk.""" with pdf.Writer(output) as writer: yield writer def stack_matrixes(self, matrixes, step: int, repeat: int): """Iterate over copies of the matrixes, incrementing pages numbers by ``step``. :param list[Matrix] matrix: A list of matrixes. :param int repeat: Number of repetitions of the matrixes given in argument. :param int step: At each repetition, is page is increased by this number. For instance, given a single matrix containing pages from 1 to 8, with `repeat=3` and `step=8`, this method will yield: - one matrix with pages from 1 to 8; - one matrix with pages from 9 to 16; - one matrix with pages from 17 to 24. """ ``` -------------------------------- ### PDFimpose Creep Argument Configuration Source: https://pdfimpose.readthedocs.io/en/latest/_modules/pdfimpose/schema Adds the '--creep' argument for setting creep space, including a warning about its current broken state. ```python self.add_argument( "--creep", "-c", help=(textwrap.dedent(""" Set creep (space added at each fold). This is a linear function of "s", the number of inner sheets (e.g. ".1s+2mm"). Note that "s" is the number of inner *printed* sheets: if a sheet is printed and folded, it still counts as 1 in this function. You might need to do some math… The output of this function is the space separating two input pages on the output page: it is twice the distance to the spine. Warning This option is broken. It is left not to break the workflow of anyone who might be using it, but computed space might be wrong on the output document. It might be fixed some day, but it is cumbersome, so I lack motivation to do so… Help (or money) is welcome. See https://framagit.org/spalax/pdfimpose/-/issues/36 for more information. """)), type=_type_creep, default=nocreep, ) ``` -------------------------------- ### CutStackFoldImpositor Class - Yielding Coordinates Source: https://pdfimpose.readthedocs.io/en/latest/_modules/pdfimpose/schema/cutstackfold This code snippet demonstrates how the CutStackFoldImpositor class yields coordinates for cutting and stacking operations. It calculates positions based on input size, margins, and signature layout. ```python for y in range(self.signature[1]): yield ( (0, self.omargin.top + y * (inputsize[1] + self.imargin)), ( self.omargin.left - left, self.omargin.top + y * (inputsize[1] + self.imargin), ), ) yield ((0, self.omargin.top + (y + 1) * inputsize[1] + y * self.imargin)), ( self.omargin.left - left, self.omargin.top + (y + 1) * inputsize[1] + y * self.imargin, ) yield ( (outputsize[0], self.omargin.top + y * (inputsize[1] + self.imargin)), ( outputsize[0] - self.omargin.right + right, self.omargin.top + y * (inputsize[1] + self.imargin), ), ) yield ( ( outputsize[0], self.omargin.top + (y + 1) * inputsize[1] + y * self.imargin, ) ), ( outputsize[0] - self.omargin.right + right, self.omargin.top + (y + 1) * inputsize[1] + y * self.imargin, ) ``` -------------------------------- ### Calculate Output Margins Source: https://pdfimpose.readthedocs.io/en/latest/_modules/pdfimpose/schema/hardcover Calculates the left/right and top/bottom margins for the output based on output size, source size, fold information, and inter-margin size. Use when determining the final layout dimensions. ```python def _folds2margins(outputsize, sourcesize, folds, imargin): """Return output margins.""" leftright = ( outputsize[0] - sourcesize[0] * 2 ** folds.count("h") - imargin * (2 ** folds.count("h") - 1) ) topbottom = ( outputsize[1] - sourcesize[1] * 2 ** folds.count("v") - imargin * (2 ** folds.count("v") - 1) ) return Margins(top=topbottom, bottom=topbottom, left=leftright, right=leftright) ``` -------------------------------- ### AbstractImpositor Crop Space Calculation Source: https://pdfimpose.readthedocs.io/en/latest/_modules/pdfimpose/schema Calculates the cropping space for PDF elements, adjusting based on output margins. This helps define safe areas for content. ```python def _crop_space(self): left = right = bottom = top = 20 if top > self.omargin.top: top = self.omargin.top / 2 if bottom > self.omargin.bottom: bottom = self.omargin.bottom / 2 if left > self.omargin.left: left = self.omargin.left / 2 if right > self.omargin.right: right = self.omargin.right / 2 return left, right, bottom, top ``` -------------------------------- ### Compute Signature and Margins Source: https://pdfimpose.readthedocs.io/en/latest/_modules/pdfimpose/schema Calculates the imposition signature and margins based on source and destination paper sizes, and input/output margins. Handles default paper sizes and rotation. ```python def size2signature(destsize, *, sourcesize, imargin, omargin): """Compute the signature and margins corresponding to a paper size.""" if destsize is None: destsize = tuple(map(float, papersize.parse_papersize(DEFAULT_PAPER_SIZE))) signature, rotated = compute_signature(sourcesize, destsize) if rotated: destsize = (destsize[1], destsize[0]) if imargin == 0: omargin = Margins( top=(destsize[1] - sourcesize[1] * signature[1]) / 2, bottom=(destsize[1] - sourcesize[1] * signature[1]) / 2, left=(destsize[0] - sourcesize[0] * signature[0]) / 2, right=(destsize[0] - sourcesize[0] * signature[0]) / 2, ) return (signature, omargin) ```