text: """
  This module defines pyHanko's high-level API entry points.
  """
  
  from datetime import datetime
  from typing import Optional
  
  import tzlocal
  from asn1crypto import cms
  
  from pyhanko.pdf_utils import embed
  from pyhanko.pdf_utils.writer import BasePdfFileWriter
  from pyhanko.sign.fields import SigFieldSpec
  from pyhanko.sign.general import SigningError
  from pyhanko.sign.timestamps import TimeStamper
  
  from .pdf_cms import Signer
  from .pdf_signer import PdfSignatureMetadata, PdfSigner
  
  __all__ = ['sign_pdf', 'embed_payload_with_cms']
  
  
  def sign_pdf(pdf_out: BasePdfFileWriter,
               signature_meta: PdfSignatureMetadata, signer: Signer,
               timestamper: TimeStamper = None,
               new_field_spec: Optional[SigFieldSpec] = None,
               existing_fields_only=False, bytes_reserved=None, in_place=False,
               output=None):
      """
      Thin convenience wrapper around :meth:`.PdfSigner.sign_pdf`.
  
      :param pdf_out:
          An :class:`.IncrementalPdfFileWriter`.
      :param bytes_reserved:
          Bytes to reserve for the CMS object in the PDF file.
          If not specified, make an estimate based on a dummy signature.
      :param signature_meta:
          The specification of the signature to add.
      :param signer:
          :class:`.Signer` object to use to produce the signature object.
      :param timestamper:
          :class:`.TimeStamper` object to use to produce any time stamp tokens
          that might be required.
      :param in_place:
          Sign the input in-place. If ``False``, write output to a
          :class:`.BytesIO` object.
      :param existing_fields_only:
          If ``True``, never create a new empty signature field to contain
          the signature.
          If ``False``, a new field may be created if no field matching
          :attr:`~.PdfSignatureMetadata.field_name` exists.
      :param new_field_spec:
          If a new field is to be created, this parameter allows the caller
          to specify the field's properties in the form of a
          :class:`.SigFieldSpec`. This parameter is only meaningful if
          ``existing_fields_only`` is ``False``.
      :param output:
          Write the output to the specified output stream.
          If ``None``, write to a new :class:`.BytesIO` object.
          Default is ``None``.
      :return:
          The output stream containing the signed output.
      """
  
      if new_field_spec is not None and existing_fields_only:
          raise SigningError(
              "Specifying a signature field spec is not meaningful when "
              "existing_fields_only=True."
          )
  
      signer = PdfSigner(
          signature_meta, signer, timestamper=timestamper,
          new_field_spec=new_field_spec
      )
      return signer.sign_pdf(
          pdf_out, existing_fields_only=existing_fields_only,
          bytes_reserved=bytes_reserved, in_place=in_place, output=output
      )
  
  
  def embed_payload_with_cms(pdf_writer: BasePdfFileWriter,
                             file_spec_string: str,
                             payload: embed.EmbeddedFileObject,
                             cms_obj: cms.ContentInfo, extension='.sig',
                             file_name: Optional[str] = None,
                             file_spec_kwargs=None, cms_file_spec_kwargs=None):
      """
      Embed some data as an embedded file stream into a PDF, and associate it
      with a CMS object.
  
      The resulting CMS object will also be turned into an embedded file, and
      associated with the original payload through a related file relationship.
  
      This can be used to bundle (non-PDF) detached signatures with PDF
      attachments, for example.
  
      .. versionadded:: 0.7.0
  
      :param pdf_writer:
          The PDF writer to use.
      :param file_spec_string:
          See :attr:`~pyhanko.pdf_utils.embed.FileSpec.file_spec_string` in
          :class:`~pyhanko.pdf_utils.embed.FileSpec`.
      :param payload:
          Payload object.
      :param cms_obj:
          CMS object pertaining to the payload.
      :param extension:
          File extension to use for the CMS attachment.
      :param file_name:
          See :attr:`~pyhanko.pdf_utils.embed.FileSpec.file_name` in
          :class:`~pyhanko.pdf_utils.embed.FileSpec`.
      :param file_spec_kwargs:
          Extra arguments to pass to the
          :class:`~pyhanko.pdf_utils.embed.FileSpec` constructor
          for the main attachment specification.
      :param cms_file_spec_kwargs:
          Extra arguments to pass to the
          :class:`~pyhanko.pdf_utils.embed.FileSpec` constructor
          for the CMS attachment specification.
      """
  
      # prepare an embedded file object for the signature
      now = datetime.now(tz=tzlocal.get_localzone())
      cms_ef_obj = embed.EmbeddedFileObject.from_file_data(
          pdf_writer=pdf_writer,
          data=cms_obj.dump(), compress=False,
          mime_type='application/pkcs7-mime',
          params=embed.EmbeddedFileParams(
              creation_date=now, modification_date=now
          )
      )
  
      # replace extension
      cms_data_f = file_spec_string.rsplit('.', 1)[0] + extension
  
      # deal with new-style Unicode file names
      cms_data_uf = uf_related_files = None
      if file_name is not None:
          cms_data_uf = file_name.rsplit('.', 1)[0] + extension
          uf_related_files = [
              embed.RelatedFileSpec(cms_data_uf, embedded_data=cms_ef_obj)
          ]
  
      spec = embed.FileSpec(
          file_spec_string=file_spec_string, file_name=file_name,
          embedded_data=payload,
          f_related_files=[
              embed.RelatedFileSpec(cms_data_f, embedded_data=cms_ef_obj)
          ],
          uf_related_files=uf_related_files,
          **(file_spec_kwargs or {}),
      )
  
      embed.embed_file(pdf_writer, spec)
  
      # also embed the CMS data as a standalone attachment
      cms_spec = embed.FileSpec(
          file_spec_string=cms_data_f, file_name=cms_data_uf,
          embedded_data=cms_ef_obj, **(cms_file_spec_kwargs or {})
      )
      embed.embed_file(pdf_writer, cms_spec)
  