def setup_output_document(input_doc, tmp_input_doc, metadata_info,
                                                    copy_document_catalog=True):
    """Create the output `PdfFileWriter` objects and copy over the relevant info."""
    # NOTE: Inserting pages from a PdfFileReader into multiple PdfFileWriters
    # seems to cause problems (writer can hang on write), so only one is used.
    # This is why the tmp_input_doc file was created earlier, to get copies of
    # the page objects which are independent of those in input_doc.  An ugly
    # hack for a nasty bug to track down.

    # NOTE: You can get the _root_object attribute (dict for the document
    # catalog) from the output document after calling cloneReaderDocumentRoot
    # or else you can just directly get it from the input_doc.trailer dict, as
    # below (which is from the code for cloneReaderDocumentRoot), but you
    # CANNOT set the full _root_object to be the _root_object attribute for the
    # actual output_doc or else only blank pages show up in acroread (whether
    # or not there is any attempt to explicitly copy the pages over).  The same
    # is true for using cloneDocumentFromReader (which just calls
    # cloneReaderDocumentRoot followed by appendPagesFromReader).  At least the
    # '/Pages' key and value in _root_object cause problems, so they are
    # skipped in the partial copy.  Probably a bug in PyPDF2.  See the original
    # code for the routines on the github pages below.
    #
    # https://github.com/mstamy2/PyPDF2/blob/master/PyPDF2/pdf.py
    # https://github.com/mstamy2/PyPDF2/blob/master/PyPDF2/generic.py
    #
    # Files still can change zoom mode on clicking outline links, but that is
    # an Adobe implementation problem, and happens even in the uncropped files:
    # https://superuser.com/questions/278302/

    output_doc = PdfFileWriter()

    def root_objects_not_indirect(input_doc, root_object):
        """This can expand some of the `IndirectObject` objects in  a root object to
        see the actual values.  Currently only used for debugging.  May mess up the
        input doc and require a temporary one."""
        if isinstance(root_object, dict):
            return {root_objects_not_indirect(input_doc, key):
                    root_objects_not_indirect(input_doc, value) for
                                                  key, value in root_object.items()}
        elif isinstance(root_object, list):
            return [root_objects_not_indirect(input_doc, item) for item in root_object]
        elif isinstance(root_object, IndirectObject):
            return input_doc.getObject(root_object)
        else:
            return root_object

    doc_cat_whitelist = args.docCatWhitelist.split()
    if "ALL" in doc_cat_whitelist:
        doc_cat_whitelist = ["ALL"]

    doc_cat_blacklist = args.docCatBlacklist.split()
    if "ALL" in doc_cat_blacklist:
        doc_cat_blacklist = ["ALL"]

    # Partially copy over document catalog data from input_doc to output_doc.
    if not copy_document_catalog or (
            not doc_cat_whitelist and doc_cat_blacklist == ["ALL"]):
        # Check this first, to completely skip the possibly problematic code getting
        # document catalog items when possible.  Does not print a skipped list, though.
        if args.verbose:
            print("\nNot copying any document catalog items to the cropped document.")
    else:
        try:
            root_object = input_doc.trailer["/Root"]

            copied_items = []
            skipped_items = []
            for key, value in root_object.items():
                # Some possible keys can be:
                #
                # /Type -- required, must have value /Catalog
                # /Pages -- required, indirect ref to page tree; skip, will change
                # /PageMode -- set to /UseNone, /UseOutlines, /UseThumbs, /Fullscreen,
                #              /UseOC, or /UseAttachments, with /UseNone default.
                # /OpenAction -- action to take when document is opened, like zooming
                # /PageLayout -- set to /SinglePage, /OneColumn, /TwoColumnLeft,
                #                /TwoColumnRight, /TwoPageLeft, /TwoPageRight
                # /Names -- a name dictionary to avoid having to use object numbers
                # /Outlines -- indirect ref to document outline, i.e., bookmarks
                # /Dests -- a dict of destinations in the PDF
                # /ViewerPreferences -- a viewer preferences dict
                # /MetaData -- XMP metadata, as opposed to other metadata
                # /PageLabels -- alternate numbering for pages, only affect PDF viewers
                if key == "/Pages":
                    skipped_items.append(key)
                    continue
                if doc_cat_whitelist != ["ALL"] and key not in doc_cat_whitelist:
                    if doc_cat_blacklist == ["ALL"] or key in doc_cat_blacklist:
                        skipped_items.append(key)
                        continue
                copied_items.append(key)
                output_doc._root_object[NameObject(key)] = value

            if args.verbose:
                print("\nCopied these items from the document catalog:\n   ", end="")
                print(*copied_items)
                print("Skipped copy of these items from the document catalog:\n   ", end="")
                print(*skipped_items)

        except (KeyboardInterrupt, EOFError):
            raise
        except: # Just catch any errors here; don't know which might be raised.
            # On exception just warn and get a new PdfFileWriter object, to be safe.
            print("\nWarning: The document catalog data could not be copied to the"
                  "\nnew, cropped document.  Try fixing the PDF document using"
                  "\n'--gsFix' if you have Ghostscript installed.", file=sys.stderr)
            output_doc = PdfFileWriter()

    #output_doc.appendPagesFromReader(input_doc) # Works, but wait and test more.
    for page in [input_doc.getPage(i) for i in range(input_doc.getNumPages())]:
        output_doc.addPage(page)

    tmp_output_doc = PdfFileWriter()
    #tmp_output_doc.appendPagesFromReader(tmp_input_doc)  # Works, but test more.
    for page in [tmp_input_doc.getPage(i) for i in range(tmp_input_doc.getNumPages())]:
        tmp_output_doc.addPage(page)

    ##
    ## Copy the metadata from input_doc to output_doc, modifying the Producer string
    ## if this program didn't already set it.  Get bool for whether this program
    ## cropped the document already.
    ##

    already_cropped_by_this_program = set_cropped_metadata(input_doc, output_doc,
                                                           metadata_info)
    return output_doc, tmp_output_doc, already_cropped_by_this_program
Create the output `PdfFileWriter` objects and copy over the relevant info.