docs: def main_crop():
      """This function does the real work.  It is called by main() in
      pdfCropMargins.py, which just handles catching exceptions and cleaning up."""
  
      ##
      ## Process some of the command-line arguments.
      ##
  
      if args.verbose:
          print("\nProcessing the PDF with pdfCropMargins (version", __version__+")...")
          print("System type:", ex.system_os)
  
      if len(args.pdf_input_doc) > 1:
          print("\nError in pdfCropMargins: Only one input PDF document is allowed."
                "\nFound more than one on the command line:", file=sys.stderr)
          for f in args.pdf_input_doc:
              print("   ", f, file=sys.stderr)
          ex.cleanup_and_exit(1)
  
      input_doc_fname = ex.glob_if_windows_os(args.pdf_input_doc[0], exact_num_args=1)[0]
      if not input_doc_fname.endswith((".pdf",".PDF")):
          print("\nWarning in pdfCropMargins: The file extension is neither '.pdf'"
                "\nnor '.PDF'; continuing anyway.\n", file=sys.stderr)
      if args.verbose:
          print("\nThe input document's filename is:\n   ", input_doc_fname)
      if not os.path.isfile(input_doc_fname):
          print("\nError in pdfCropMargins: The specified input file\n   "
                + input_doc_fname + "\nis not a file or does not exist.",
                file=sys.stderr)
          ex.cleanup_and_exit(1)
  
      if not args.outfile:
          if args.verbose: print("\nUsing the default-generated output filename.")
          output_doc_fname = generate_default_filename(input_doc_fname)
      else:
          output_doc_fname = ex.glob_if_windows_os(args.outfile[0], exact_num_args=1)[0]
      if args.verbose:
          print("\nThe output document's filename will be:\n   ", output_doc_fname)
  
      if os.path.lexists(output_doc_fname) and args.noclobber:
          print("\nOption '--noclobber' is set, refusing to overwrite an existing"
                "\nfile with filename:\n   ", output_doc_fname, file=sys.stderr)
          ex.cleanup_and_exit(1)
  
      if os.path.lexists(output_doc_fname) and ex.samefile(input_doc_fname,
                                                                  output_doc_fname):
          print("\nError in pdfCropMargins: The input file is the same as"
                "\nthe output file.\n", file=sys.stderr)
          ex.cleanup_and_exit(1)
  
      if args.gsBbox and len(args.fullPageBox) > 1:
          print("\nWarning: only one --fullPageBox value can be used with the -gs option.",
                "\nIgnoring all but the first one.", file=sys.stderr)
          args.fullPageBox = [args.fullPageBox[0]]
      elif args.gsBbox and not args.fullPageBox: args.fullPageBox = ["c"] # gs default
      elif not args.fullPageBox: args.fullPageBox = ["m", "c"] # usual default
  
      if args.verbose:
          print("\nFor the full page size, using values from the PDF box"
                "\nspecified by the intersection of these boxes:", args.fullPageBox)
  
      if args.absolutePreCrop: args.absolutePreCrop *= 4 # expand to 4 offsets
      # See if all four offsets are explicitly set and use those if so.
      if args.absolutePreCrop4: args.absolutePreCrop = args.absolutePreCrop4
      if args.verbose:
          print("\nThe absolute pre-crops to be applied to each margin, in units of bp,"
                " are:\n   ", args.absolutePreCrop)
  
      if args.percentRetain: args.percentRetain *= 4 # expand to 4 percents
      # See if all four percents are explicitly set and use those if so.
      if args.percentRetain4: args.percentRetain = args.percentRetain4
      if args.verbose:
          print("\nThe percentages of margins to retain are:\n   ",
                args.percentRetain)
  
      if args.absoluteOffset: args.absoluteOffset *= 4 # expand to 4 offsets
      # See if all four offsets are explicitly set and use those if so.
      if args.absoluteOffset4: args.absoluteOffset = args.absoluteOffset4
      if args.verbose:
          print("\nThe absolute offsets to be applied to each margin, in units of bp,"
                " are:\n   ", args.absoluteOffset)
  
      # Parse the page ratio into a float if user chose that option.
      if args.setPageRatios:
          ratio = args.setPageRatios[0].split(":")
          if len(ratio) > 2:
              print("\nError in pdfCropMargins: Bad format in aspect ratio command line"
                    " argument.\nToo many colons.")
              ex.cleanup_and_exit(1)
          try:
              if len(ratio) == 2:
                  args.setPageRatios[0] = float(ratio[0])/float(ratio[1])
              else:
                  args.setPageRatios[0] = float(ratio[0])
          except ValueError:
              print("\nError in pdfCropMargins: Bad format in aspect ratio command line"
                    " argument.\nCannot convert to a float.")
              ex.cleanup_and_exit(1)
  
      # Set executable paths to non-default locations if set.
      if args.pdftoppmPath: ex.set_pdftoppm_executable_to_string(args.pdftoppmPath)
      if args.ghostscriptPath: ex.set_gs_executable_to_string(args.ghostscriptPath)
  
      # If the option settings require pdftoppm, make sure we have a running
      # version.  If '--gsBbox' isn't chosen then assume that PDF pages are to be
      # explicitly rendered.  In that case we either need pdftoppm or gs to do the
      # rendering.
      gs_render_fallback_set = False # Set True if we switch to gs option as a fallback.
      if not args.gsBbox and not args.gsRender:
          found_pdftoppm = ex.init_and_test_pdftoppm_executable(
                                                     prefer_local=args.pdftoppmLocal)
          if args.verbose:
              print("\nFound pdftoppm program at:", found_pdftoppm)
          if not found_pdftoppm:
              args.gsRender = True
              gs_render_fallback_set = True
              if args.verbose:
                  print("\nNo pdftoppm executable found; using Ghostscript for rendering.")
  
      # If any options require Ghostscript, make sure it it installed.
      if args.gsBbox or args.gsFix or args.gsRender:
          found_gs = ex.init_and_test_gs_executable()
          if args.verbose:
              print("\nFound Ghostscript program at:", found_gs)
      if args.gsBbox and not found_gs:
          print("\nError in pdfCropMargins: The '--gsBbox' option was specified but"
                "\nthe Ghostscript executable could not be located.  Is it"
                "\ninstalled and in the PATH for command execution?\n", file=sys.stderr)
          ex.cleanup_and_exit(1)
      if args.gsFix and not found_gs:
          print("\nError in pdfCropMargins: The '--gsFix' option was specified but"
                "\nthe Ghostscript executable could not be located.  Is it"
                "\ninstalled and in the PATH for command execution?\n", file=sys.stderr)
          ex.cleanup_and_exit(1)
      if args.gsRender and not found_gs:
          if gs_render_fallback_set:
              print("\nError in pdfCropMargins: Neither Ghostscript nor pdftoppm"
                    "\nwas found in the PATH for command execution.  At least one is"
                    "\nrequired.\n", file=sys.stderr)
          else:
              print("\nError in pdfCropMargins: The '--gsRender' option was specified but"
                    "\nthe Ghostscript executable could not be located.  Is it"
                    "\ninstalled and in the PATH for command execution?\n", file=sys.stderr)
          ex.cleanup_and_exit(1)
  
      # Give a warning message if incompatible option combinations have been selected.
      if args.gsBbox and args.threshold:
          print("\nWarning in pdfCropMargins: The '--threshold' option is ignored"
                "\nwhen the '--gsBbox' option is also selected.\n", file=sys.stderr)
      if args.gsBbox and args.numBlurs:
          print("\nWarning in pdfCropMargins: The '--numBlurs' option is ignored"
                "\nwhen the '--gsBbox' option is also selected.\n", file=sys.stderr)
      if args.gsBbox and args.numSmooths:
          print("\nWarning in pdfCropMargins: The '--numSmooths' option is ignored"
                "\nwhen the '--gsBbox' option is also selected.\n", file=sys.stderr)
  
      ##
      ## Open the input document in a PdfFileReader object.  Due to an apparent bug
      ## in pyPdf we open two PdfFileReader objects for the file.  The time required should
      ## still be small relative to finding the bounding boxes of pages.  The bug is
      ## that writing a PdfFileWriter tends to hang on certain files if 1) pages from
      ## the same PdfFileReader are shared between two PdfFileWriter objects, or 2)
      ## the PdfFileWriter is written, the pages are modified, and there is an attempt
      ## to write the same PdfFileWriter to a different file.
      ##
  
      if args.gsFix:
          if args.verbose:
              print("\nAttempting to fix the PDF input file before reading it...")
          fixed_input_doc_fname = ex.fix_pdf_with_ghostscript_to_tmp_file(input_doc_fname)
      else:
          fixed_input_doc_fname = input_doc_fname
  
      # Open the input file object.
      # TODO: Need try except since might fail for permissions.
      fixed_input_doc_file_object = open(fixed_input_doc_fname, "rb")
  
      try:
          input_doc = PdfFileReader(fixed_input_doc_file_object)
          tmp_input_doc = PdfFileReader(fixed_input_doc_file_object)
      except (KeyboardInterrupt, EOFError):
          raise
      except: # Can raise various exceptions, just catch the rest here.
          print("\nError in pdfCropMargins: The pyPdf module failed in an attempt"
                "\nto read the input file.  Is the file a PDF file?  If so then it"
                "\nmay be corrupted.  If you have Ghostscript, try the '--gsFix'"
                "\noption (assuming you are not using it already).  That option can"
                "\nalso convert some PostScript files to a readable format.",
                file=sys.stderr)
          ex.cleanup_and_exit(1)
  
      ##
      ## See if the document needs to be decrypted.
      ##
  
      if args.password:
          try:
              input_doc.decrypt(args.password)
              tmp_input_doc.decrypt(args.password)
          except KeyError:
              print("\nDecrypting with the password from the '--password' option"
                    "\nfailed.", file=sys.stderr)
              ex.cleanup_and_exit(1)
      else: # try decrypting with an empty password
          try:
              input_doc.decrypt("")
              tmp_input_doc.decrypt("")
          except KeyError:
              pass # document apparently wasn't encrypted with an empty password
  
      ##
      ## Print out some data and metadata in verbose mode.
      ##
  
      if args.verbose:
          print("\nThe input document has %s pages." % input_doc.getNumPages())
  
      try: # This is needed because the call sometimes just raises an error.
          metadata_info = input_doc.getDocumentInfo()
      except:
          print("\nWarning: Document metadata could not be read.", file=sys.stderr)
          metadata_info = None
  
      if args.verbose and not metadata_info:
          print("\nNo readable metadata in the document.")
      elif args.verbose:
          try:
              print("\nThe document's metadata, if set:\n")
              print("   The Author attribute set in the input document is:\n      %s"
                    % (metadata_info.author))
              print("   The Creator attribute set in the input document is:\n      %s"
                    % (metadata_info.creator))
              print("   The Producer attribute set in the input document is:\n      %s"
                    % (metadata_info.producer))
              print("   The Subject attribute set in the input document is:\n      %s"
                    % (metadata_info.subject))
              print("   The Title attribute set in the input document is:\n      %s"
                    % (metadata_info.title))
          # Some metadata cannot be decoded or encoded, at least on Windows.  Could
          # print from a function instead to write all the lines which can be written.
          except (UnicodeDecodeError, UnicodeEncodeError):
              print("\nWarning: Could not write all the document's metadata to the screen."
                    "\nGot a UnicodeEncodeError or a UnicodeDecodeError.")
  
      ##
      ## Now compute the set containing the pyPdf page number of all the pages
      ## which the user has selected for cropping from the command line.  Most
      ## calculations are still carried-out for all the pages in the document.
      ## (There are a few optimizations for expensive operations like finding
      ## bounding boxes; the rest is negligible).  This keeps the correspondence
      ## between page numbers and the positions of boxes in the box lists.  The
      ## function apply_crop_list then just ignores the cropping information for any
      ## pages which were not selected.
      ##
  
      all_page_nums = set(range(0, input_doc.getNumPages()))
      page_nums_to_crop = set() # Note that this set holds page num MINUS ONE, start at 0.
      if args.pages:
          # Parse any page range specifier argument.
          for page_num_or_range in args.pages.split(","):
              split_range = page_num_or_range.split("-")
              try:
                  if len(split_range) == 1:
                      # Note pyPdf page nums start at 0, not 1 like usual PDF pages,
                      # subtract 1.
                      page_nums_to_crop.add(int(split_range[0])-1)
                  else:
                      page_nums_to_crop.update(
                          set(range(int(split_range[0])-1, int(split_range[1]))))
              except ValueError:
                  print(
                      "\nError in pdfCropMargins: The page range specified on the command",
                      "\nline contains a non-integer value or otherwise cannot be parsed.",
                      file=sys.stderr)
                  ex.cleanup_and_exit(1)
          page_nums_to_crop = page_nums_to_crop & all_page_nums # intersect chosen with actual
      else:
          page_nums_to_crop = all_page_nums
  
      # Print out the pages to crop in verbose mode.
      if args.verbose and args.pages:
          print("\nThese pages of the document will be cropped:", end="")
          p_num_list = sorted(list(page_nums_to_crop))
          num_pages_to_crop = len(p_num_list)
          for i in range(num_pages_to_crop):
              if i % 10 == 0 and i != num_pages_to_crop - 1:
                  print("\n   ", end="")
              print("%5d" % (p_num_list[i]+1), " ", end="")
          print()
      elif args.verbose:
          print("\nAll the pages of the document will be cropped.")
  
      ##
      ## Get a list with the full-page boxes for each page: (left,bottom,right,top)
      ## This function also sets the MediaBox and CropBox of the pages to the
      ## chosen full-page size as a side-effect, saving the old boxes.
      ##
  
      full_page_box_list, rotation_list = get_full_page_box_list_assigning_media_and_crop(
                                                                                input_doc)
      tmp_full_page_box_list, tmp_rotation_list = get_full_page_box_list_assigning_media_and_crop(
                                                              tmp_input_doc, quiet=True)
  
      ##
      ## Define a PdfFileWriter object and copy input_doc info over to it.
      ##
  
      output_doc, tmp_output_doc, already_cropped_by_this_program = setup_output_document(
                                                     input_doc, tmp_input_doc, metadata_info)
  
      ##
      ## Write out the PDF document again, with the CropBox and MediaBox reset.
      ## This temp version is only used for calculating the bounding boxes of
      ## pages.  Note we are writing from tmp_output_doc (due to an apparent bug
      ## discussed above).  After this tmp_input_doc and tmp_output_doc are no longer
      ## needed.
      ##
  
      if not args.restore:
          doc_with_crop_and_media_boxes_name = ex.get_temporary_filename(".pdf")
          doc_with_crop_and_media_boxes_object = open(
                                       doc_with_crop_and_media_boxes_name, "wb")
  
          if args.verbose:
              print("\nWriting out the PDF with the CropBox and MediaBox redefined.")
  
          try:
              tmp_output_doc.write(doc_with_crop_and_media_boxes_object)
          except (KeyboardInterrupt, EOFError):
              raise
          except: # PyPDF2 can raise various exceptions.
              print("\nError in pdfCropMargins: The pyPdf program failed in trying to"
                    "\nwrite out a PDF file of the document.  The document may be"
                    "\ncorrupted.  If you have Ghostscript, try using the '--gsFix'"
                    "\noption (assuming you are not already using it).", file=sys.stderr)
              ex.cleanup_and_exit(1)
  
          doc_with_crop_and_media_boxes_object.close()
  
      ##
      ## Calculate the bounding_box_list containing tight page bounds for each page.
      ##
  
      if not args.restore:
          bounding_box_list = get_bounding_box_list(doc_with_crop_and_media_boxes_name,
                  input_doc, full_page_box_list, page_nums_to_crop, args, PdfFileWriter)
          if args.verbose:
              print("\nThe bounding boxes are:")
              for pNum, b in enumerate(bounding_box_list):
                  print("\t", pNum+1, "\t", b)
  
      ##
      ## Calculate the crop_list based on the fullpage boxes and the bounding boxes.
      ##
  
      if not args.restore:
          crop_list = calculate_crop_list(full_page_box_list, bounding_box_list,
                                       rotation_list, page_nums_to_crop)
      else:
          crop_list = None # Restore, not needed in this case.
  
      ##
      ## Apply the calculated crops to the pages of the PdfFileReader input_doc.
      ## These pages are copied to the PdfFileWriter output_doc.
      ##
  
      apply_crop_list(crop_list, input_doc, page_nums_to_crop,
                                            already_cropped_by_this_program)
  
      ##
      ## Write the final PDF out to a file.
      ##
  
      if args.verbose: print("\nWriting the cropped PDF file.")
  
      # TODO: Try and except on the open, since it might fail for permissions.
      output_doc_stream = open(output_doc_fname, "wb")
  
      try:
          output_doc.write(output_doc_stream)
      except (KeyboardInterrupt, EOFError):
          raise
      except: # PyPDF2 can raise various exceptions.
          try:
              # We know the write succeeded on tmp_output_doc or we wouldn't be here.
              # Malformed document catalog info can cause write failures, so get
              # a new output_doc without that data and try the write again.
              print("\nWrite failure, trying one more time...", file=sys.stderr)
              output_doc_stream.close()
              output_doc_stream = open(output_doc_fname, "wb")
              output_doc, tmp_output_doc, already_cropped = setup_output_document(
                      input_doc, tmp_input_doc, metadata_info, copy_document_catalog=False)
              output_doc.write(output_doc_stream)
              print("\nWarning: Document catalog data caused a write failure.  A retry"
                    "\nwithout that data succeeded.  No document catalog information was"
                    "\ncopied to the cropped output file.  Try fixing the PDF file.  If"
                    "\nyou have ghostscript installed, run pdfCropMargins with the '--gsFix'"
                    "\noption.  You can also try blacklisting some of the document catalog"
                    "\nitems using the '--dcb' option.", file=sys.stderr)
          except (KeyboardInterrupt, EOFError):
              raise
          except: # Give up... PyPDF2 can raise many errors for many reasons.
              print("\nError in pdfCropMargins: The pyPdf program failed in trying to"
                    "\nwrite out a PDF file of the document.  The document may be"
                    "\ncorrupted.  If you have Ghostscript, try using the '--gsFix'"
                    "\noption (assuming you are not already using it).", file=sys.stderr)
              ex.cleanup_and_exit(1)
  
      output_doc_stream.close()
  
      # We're finished with this open file; close it and let temp dir removal delete it.
      fixed_input_doc_file_object.close()
  
      ##
      ## Now handle the options which apply after the file is written.
      ##
  
      def do_preview(output_doc_fname):
          viewer = args.preview
          if args.verbose:
              print("\nPreviewing the output document with viewer:\n   ", viewer)
          ex.show_preview(viewer, output_doc_fname)
          return
  
      # Handle the '--queryModifyOriginal' option.
      if args.queryModifyOriginal:
          if args.preview:
              print("\nRunning the preview viewer on the file, will query whether or not"
                    "\nto modify the original file after the viewer is launched in the"
                    "\nbackground...\n")
              do_preview(output_doc_fname)
              # Give preview time to start; it may write startup garbage to the terminal...
              query_wait_time = 2 # seconds
              time.sleep(query_wait_time)
              print()
          while True:
              query_string = "\nModify the original file to the cropped file " \
                  "(saving the original)? [yn] "
              if ex.python_version[0] == "2":
                  query_result = raw_input(query_string).decode("utf-8")
              else:
                  query_result = input(query_string)
              if query_result in ["y", "Y"]:
                  args.modifyOriginal = True
                  print("\nModifying the original file.")
                  break
              elif query_result in ["n", "N"]:
                  print("\nNot modifying the original file.  The cropped file is saved"
                        " as:\n   {0}".format(output_doc_fname))
                  args.modifyOriginal = False
                  break
              else:
                  print("Response must be in the set {y,Y,n,N}, none recognized.")
                  continue
  
      # Handle the '--modifyOriginal' option.
      if args.modifyOriginal:
          generated_uncropped_filename = generate_default_filename(
                                                    input_doc_fname, is_cropped_file=False)
  
          # Remove any existing file with the name generated_uncropped_filename unless a
          # relevant noclobber option is set or it isn't a file.
          if os.path.exists(generated_uncropped_filename):
              if (os.path.isfile(generated_uncropped_filename)
                      and not args.noclobberOriginal and not args.noclobber):
                  if args.verbose:
                      print("\nRemoving the file\n   ", generated_uncropped_filename)
                  try:
                      os.remove(generated_uncropped_filename)
                  except OSError:
                      print("Removing the file {} failed.  Maybe a permission error?"
                            "\nFiles are as if option '--modifyOriginal' were not set."
                            .format(generated_uncropped_filename))
                      args.modifyOriginal = False # Failed.
              else:
                  print("\nA noclobber option is set or else not a file; refusing to"
                      " overwrite:\n   ", generated_uncropped_filename,
                      "\nFiles are as if option '--modifyOriginal' were not set.",
                      file=sys.stderr)
                  args.modifyOriginal = False # Failed.
  
          # Move the original file to the name for uncropped files.  Silently do nothing
          # if the file exists (should have been removed above).
          if not os.path.exists(generated_uncropped_filename):
              if args.verbose: print("\nDoing a file move:\n   ", input_doc_fname,
                                     "\nis moving to:\n   ", generated_uncropped_filename)
              shutil.move(input_doc_fname, generated_uncropped_filename)
  
          # Move the cropped file to the original file's name.  Silently do nothing if
          # the file exists (should have been moved above).
          if not os.path.exists(input_doc_fname):
              if args.verbose: print("\nDoing a file move:\n   ", output_doc_fname,
                                     "\nis moving to:\n   ", input_doc_fname)
              shutil.move(output_doc_fname, input_doc_fname)
  
      # Handle any previewing which still needs to be done.
      if args.preview and not args.queryModifyOriginal: # already previewed in query mod
          if args.modifyOriginal: # already swapped to original filename in this case
              do_preview(input_doc_fname)
          else: # the usual case, preview the output filename
              do_preview(output_doc_fname)
  
      if args.verbose: print("\nFinished this run of pdfCropMargins.\n")