Calculates impositions for the elements in the content list and
    returns a set of page-layouts for each as manager instances.

    An imposition is a way of laying out pages on a larger sheet, such
    that they can be folded and cut to form a booklet.
    The signature size of an imposition controls how many of these
    large sheets can fold together as one. Larger books are made up of
    multiple signatures sewn together in a codex.

    Arguments:

    ``imposition_type``
        One of the FORMAT_* constants defined in this module: controls
        the kind of imposition to make.

    ``sheets_per_sig``
        How many sheets of paper will be folded, cut and bind
        together. This is often 1 for commercial presses, but DIY
        laser printouts often have all the pages in one signature (for
        stapling). If this value isn't provided, the function will
        assume the latter.

    ``fold_then_collate``
        If this is true then each printed sheet will be folded and
        cut, then the bundle of resulting pages will be combined with
        the others from its signature. This kindof defeats the object
        of printing in signatures, but is needed when building
        multi-signature books using a laser printer. It only makes
        sense if you have more than one sheet per sig.

    ``thickness``
        When many pages are folded together, the thickness of the
        sheets adds up. Giving a page thickness to this argument moves
        the printed pages around slightly to compensate.

    ``signature_mark``
        When working with many signatures, it can be fiddly to sort
        the signatures into the correct order for binding. To help we
        can add 'signature marks' - a triangle on the spine of each
        signature indicating up, and offset from signature to
        signature. When the signatures are correctly collated, the
        marks form a diagonal pattern: it is obvious when they are not
        correct. The value of this argument indicates the width of
        this pattern. Zero indicates that no mark should be added.

    ``elements``
        The list of individual page element to impose. There can be
        fewer pages to layout than spaces on the layout (i.e. in a 16
        page imposition, we could only have 13 pages), any additional
        space is left blank.

    The returned page layouts can be given to a PageLM for rendering
    onto individual pages of output. This method isn't a layout
    manager in its own right.

def get_page_impositions(imposition_type,
                         sheets_per_sig=None, fold_then_collate=False,
                         thickness=0.2835, signature_mark=0,
                         elements=[]):
    """
    Calculates impositions for the elements in the content list and
    returns a set of page-layouts for each as manager instances.

    An imposition is a way of laying out pages on a larger sheet, such
    that they can be folded and cut to form a booklet.
    The signature size of an imposition controls how many of these
    large sheets can fold together as one. Larger books are made up of
    multiple signatures sewn together in a codex.

    Arguments:

    ``imposition_type``
        One of the FORMAT_* constants defined in this module: controls
        the kind of imposition to make.

    ``sheets_per_sig``
        How many sheets of paper will be folded, cut and bind
        together. This is often 1 for commercial presses, but DIY
        laser printouts often have all the pages in one signature (for
        stapling). If this value isn't provided, the function will
        assume the latter.

    ``fold_then_collate``
        If this is true then each printed sheet will be folded and
        cut, then the bundle of resulting pages will be combined with
        the others from its signature. This kindof defeats the object
        of printing in signatures, but is needed when building
        multi-signature books using a laser printer. It only makes
        sense if you have more than one sheet per sig.

    ``thickness``
        When many pages are folded together, the thickness of the
        sheets adds up. Giving a page thickness to this argument moves
        the printed pages around slightly to compensate.

    ``signature_mark``
        When working with many signatures, it can be fiddly to sort
        the signatures into the correct order for binding. To help we
        can add 'signature marks' - a triangle on the spine of each
        signature indicating up, and offset from signature to
        signature. When the signatures are correctly collated, the
        marks form a diagonal pattern: it is obvious when they are not
        correct. The value of this argument indicates the width of
        this pattern. Zero indicates that no mark should be added.

    ``elements``
        The list of individual page element to impose. There can be
        fewer pages to layout than spaces on the layout (i.e. in a 16
        page imposition, we could only have 13 pages), any additional
        space is left blank.

    The returned page layouts can be given to a PageLM for rendering
    onto individual pages of output. This method isn't a layout
    manager in its own right.
    """
    # Take a copy of the elements list so we can mess with it.
    elements = elements[:]

    # Sanity check the type
    s = set(imposition_type[2])
    assert len(s) == imposition_type[0] * imposition_type[1] * 2
    assert max(*list(s)) == imposition_type[0] * imposition_type[1] * 2
    assert min(*list(s)) == 1

    # Calculate the sheets per signature.
    if sheets_per_sig is None:
        if imposition_type == FORMAT_4_PAGE:
            sheets_per_sig = int(math.ceil(len(elements) / 4.0))
        else:
            sheets_per_sig = 1

    # Find basic data.
    cols, rows, pattern = imposition_type
    pages_per_side = cols * rows
    pages_per_sheet = pages_per_side * 2
    pages_per_signature = sheets_per_sig * pages_per_sheet
    num_pages = len(elements)
    sheets_needed = int(math.ceil(num_pages / float(pages_per_sheet)))
    signatures_needed = int(math.ceil(sheets_needed / float(sheets_per_sig)))

    # Add effective margins to account for page thickness.
    if thickness > 0:
        total_offset = (
            sheets_per_sig * pages_per_side * thickness * math.pi * 0.25
            )

        for index, element in enumerate(elements):
            # Work out which spread this page is on.
            index_in_sig = index % pages_per_signature
            if (index_in_sig >= pages_per_signature / 2):
                index_in_sig = pages_per_signature - 1 - index_in_sig

            # How many thicknesses are we from the outside of the sig
            out_d = (index_in_sig + 1) // 2

            # And what offset is that from the inside.
            outer_extra = out_d * thickness * math.pi * 0.5
            inner_extra = total_offset - outer_extra

            # Work it out in terms of right and left
            if index % 2 == 0:
                left, right = inner_extra, outer_extra
            else:
                left, right = outer_extra, inner_extra

            # Add the new margins
            margined = margins.MarginsLM(0, right, 0, left, element)
            elements[index] = margined

    # Come up with the output plan: the order of pages to output.
    output_elements = []
    for sig in range(signatures_needed):
        sig_offset = sig * pages_per_signature

        # Add the signature mark to the start page of the
        # signature.
        if signature_mark > 0:
            elements[sig_offset] = overlay.OverlayLM(
                elements = [
                    elements[sig_offset],
                    mark.SignatureMark(sig, signatures_needed, signature_mark)
                    ]
                )

        if fold_then_collate:
            # We fold and cut each sheet, then combine them into
            # their signatures.
            for sheet in range(sheets_per_sig):
                offset = sig_offset + sheet * pages_per_side
                second_half_offset = \
                    offset + pages_per_signature - (sheet+1)*pages_per_sheet

                for index, slot in enumerate(pattern):
                    if slot > pages_per_side:
                        page_index = second_half_offset + slot - 1
                    else:
                        page_index = offset + slot - 1

                    if page_index >= num_pages:
                        output_elements.append(None)
                    else:
                        output_elements.append(elements[page_index])

        else:
            # We collate the sheets together, then fold them as a whole
            # and cut to form the signature.
            sig_elements = [None] * pages_per_signature
            page_number = 0
            increasing = True
            for pattern_index in range(0, pages_per_sheet, 2):
                # We're looping for each double folio in the sheet

                # Find where these folios are in the pattern
                location_in_pattern = [
                    pattern.index(pattern_index+1),
                    pattern.index(pattern_index+2)
                    ]

                # Go through each sheet in the signature
                for sheet in range(sheets_per_sig):
                    sheet_number = \
                        sheet if increasing else sheets_per_sig-1-sheet

                    # Place the next page at this given location
                    for page in range(2):
                        slot_index = (
                            sheet_number * pages_per_sheet +
                            location_in_pattern[page]
                            )
                        page_index = page_number + sig_offset
                        if page_index < num_pages:
                            sig_elements[slot_index] = elements[page_index]

                        page_number += 1

                # Next time through, go in the reverse order.
                increasing = not increasing

            # Add all the elements for this signature to the whole
            output_elements.extend(sig_elements)



    # Invert the relevant pages
    for index, element in enumerate(output_elements):
        # Check if it needs to be upside down.
        row_from_bottom = rows - 1 - (index % pages_per_side) // cols
        if element is not None and row_from_bottom % 2 > 0:
            output_elements[index] = transform.RotateLM(2, element)

    # Split them into simple grids and pages.
    pages = []
    for index in range(0, len(output_elements), pages_per_side):
        side_elements = output_elements[index:index+pages_per_side]
        pages.append(grid.SimpleGridLM(
                cols, rows, margin=0, elements=side_elements
                ))
    return pages