def test_four_qr_stamps(fixed_size):
    # Share a font subset, the text is the same everywhere
    gaf = GlyphAccumulatorFactory(NOTO_SANS, font_size=10)
    w = empty_page()
    positions = ((10, 700), (10, 500), (10, 10), (260, 10))
    for qr_pos, (x, y) in zip(QRPosition, positions):
        style = QRStampStyle(
            stamp_text='Test stamp text\nAnother line of text',
            text_box_style=TextBoxStyle(font=gaf),
            qr_position=qr_pos, background=STAMP_ART_CONTENT,
            background_opacity=0.4
        )
        if fixed_size:
            if qr_pos.horizontal_flow:
                box = layout.BoxConstraints(width=300, height=100)
            else:
                box = layout.BoxConstraints(width=100, height=300)
        else:
            box = None
        ts = QRStamp(
            writer=w, style=style, box=box, url='https://example.com'
        )
        ts.apply(0, x=x, y=y)
    postfix = 'fixed' if fixed_size else 'natural'
    compare_output(w, f'{EXPECTED_OUTPUT_DIR}/four-stamps-{postfix}.pdf')
def test_simple_qr_noto_stamp(tmp_path):
    outfile: Path = tmp_path / "test-out.pdf"

    ga_factory = GlyphAccumulatorFactory(NOTO_SERIF_JP)
    qr_stamp_file(
        MINIMAL_PATH, str(outfile),
        QRStampStyle(stamp_text="Hi, it's\n%(ts)s",
                     text_box_style=TextBoxStyle(font=ga_factory)),
        dest_page=0, x=70, y=50, url='https://example.com',
    )
Exemple #3
0
def test_simple_text_stamp_string_keyed_font():
    gaf = GlyphAccumulatorFactory(FREE_SERIF, font_size=10)

    w = empty_page()
    style = TextStampStyle(
        stamp_text=("Hi, this is a string-keyed font test.\n"
                    "This should have a ligature: difficult"),
        text_box_style=TextBoxStyle(font=gaf),
    )

    ts = TextStamp(w, style, box=layout.BoxConstraints(200, 50))
    ts.apply(dest_page=0, x=70, y=50)

    compare_output(w, f'{EXPECTED_OUTPUT_DIR}/freeserif-test.pdf')
Exemple #4
0
def _arabic_text_page(stream_xrefs):
    w = empty_page(stream_xrefs=stream_xrefs)
    style = TextStampStyle(
        stamp_text='اَلْفُصْحَىٰ',
        text_box_style=TextBoxStyle(
            font=GlyphAccumulatorFactory(NOTO_SANS_ARABIC), ),
        inner_content_layout=layout.SimpleBoxLayoutRule(
            x_align=layout.AxisAlignment.ALIGN_MID,
            y_align=layout.AxisAlignment.ALIGN_MID,
            inner_content_scaling=layout.InnerScaling.STRETCH_TO_FIT,
            margins=layout.Margins.uniform(5)))
    ts = TextStamp(writer=w,
                   style=style,
                   box=layout.BoxConstraints(width=300, height=200))
    ts.apply(0, x=10, y=60)
    return w
Exemple #5
0
class TextStampStyle(BaseStampStyle):
    """
    Style for text-based stamps.

    Roughly speaking, this stamp type renders some predefined (but parametrised)
    piece of text inside a text box, and possibly applies a background to it.
    """

    text_box_style: TextBoxStyle = TextBoxStyle()
    """
    The text box style for the internal text box used.
    """

    inner_content_layout: layout.SimpleBoxLayoutRule = None
    """
    Rule determining the position and alignment of the inner text box within
    the stamp.
    
    .. warning::
        This only affects the position of the box, not the alignment of the
        text within.
    """

    stamp_text: str = '%(ts)s'
    """
    Text template for the stamp. The template can contain an interpolation
    parameter ``ts`` that will be replaced by the stamping time.
    
    Additional parameters may be added if necessary. Values for these must be
    passed to the :meth:`~.TextStamp.__init__` method of the 
    :class:`.TextStamp` class in the ``text_params`` argument.
    """

    timestamp_format: str = '%Y-%m-%d %H:%M:%S %Z'
    """
    Datetime format used to render the timestamp.
    """

    def create_stamp(self, writer: BasePdfFileWriter,
                     box: layout.BoxConstraints, text_params: dict) \
            -> 'TextStamp':

        return TextStamp(writer=writer,
                         style=self,
                         box=box,
                         text_params=text_params)
Exemple #6
0
def test_japanese_vertical_text_stamp():
    gaf = GlyphAccumulatorFactory(NOTO_SERIF_JP,
                                  font_size=10,
                                  writing_direction='ttb')
    w = empty_page()
    style = QRStampStyle(stamp_text=('テスト\n縦書きテスト\n改行してみましょう(括弧)\nPDF\n'
                                     'ちょっと長めの文を書いてみた。'),
                         text_box_style=TextBoxStyle(font=gaf,
                                                     vertical_text=True),
                         qr_position=QRPosition.ABOVE_TEXT,
                         background=STAMP_ART_CONTENT,
                         background_opacity=0.4)
    box = layout.BoxConstraints(width=100, height=300)
    ts = QRStamp(writer=w, style=style, box=box, url='https://example.com')
    ts.apply(0, x=10, y=415)
    ts = QRStamp(writer=w, style=style, box=None, url='https://example.com')
    ts.apply(0, x=400, y=415)
    compare_output(w, f'{EXPECTED_OUTPUT_DIR}/ja-vert-stamps.pdf')
Exemple #7
0
    def process_entries(cls, config_dict):
        """
        The implementation of :meth:`process_entries` calls
        :meth:`.TextBoxStyle.from_config` to parse the ``text_box_style``
        configuration entry, if present.

        Then, it processes the background specified.
        This can either be a path to an image file, in which case it will
        be turned into an instance of :class:`~.pdf_utils.images.PdfImage`,
        or the special value ``__stamp__``, which is an alias for
        :const:`~pyhanko.stamp.STAMP_ART_CONTENT`.

        See :meth:`.ConfigurableMixin.process_entries` for general
        documentation about this method.
        """

        super().process_entries(config_dict)
        try:
            tbs = config_dict['text_box_style']
            config_dict['text_box_style'] \
                = TextBoxStyle.from_config(tbs)
        except KeyError:
            pass

        try:
            bg_spec = config_dict['background']
            # 'special' value to use the stamp vector image baked into
            # the module
            if bg_spec == '__stamp__':
                config_dict['background'] = STAMP_ART_CONTENT
            elif isinstance(bg_spec, str):
                from pyhanko.pdf_utils.images import PdfImage
                from PIL import Image
                img = Image.open(bg_spec)
                # Setting the writer can be delayed
                config_dict['background'] = PdfImage(img, writer=None)
        except KeyError:
            pass
Exemple #8
0
class TextStampStyle(ConfigurableMixin):
    """
    Style for text-based stamps.

    Roughly speaking, this stamp type renders some predefined (but parametrised)
    piece of text inside a text box, and possibly applies a background to it.
    """

    text_box_style: TextBoxStyle = TextBoxStyle()
    """
    The text box style for the internal text box used.
    """

    border_width: int = 3
    """
    Border width in user units (for the stamp, not the text box).
    """

    stamp_text: str = '%(ts)s'
    """
    Text template for the stamp. The template can contain an interpolation
    parameter ``ts`` that will be replaced by the stamping time.
    
    Additional parameters may be added if necessary. Values for these must be
    passed to the :meth:`~.TextStamp.__init__` method of the 
    :class:`.TextStamp` class in the ``text_params`` argument.
    """

    timestamp_format: str = '%Y-%m-%d %H:%M:%S %Z'
    """
    Datetime format used to render the timestamp.
    """

    background: PdfContent = None
    """
    :class:`~.pdf_utils.content.PdfContent` instance that will be used to render
    the stamp's background.
    """

    background_opacity: float = 0.6
    """
    Opacity value to render the background at. This should be a floating-point
    number between `0` and `1`.
    """
    @classmethod
    def process_entries(cls, config_dict):
        """
        The implementation of :meth:`process_entries` calls
        :meth:`.TextBoxStyle.from_config` to parse the ``text_box_style``
        configuration entry, if present.

        Then, it processes the background specified.
        This can either be a path to an image file, in which case it will
        be turned into an instance of :class:`~.pdf_utils.images.PdfImage`,
        or the special value ``__stamp__``, which is an alias for
        :const:`~pyhanko.stamp.STAMP_ART_CONTENT`.

        See :meth:`.ConfigurableMixin.process_entries` for general
        documentation about this method.
        """

        super().process_entries(config_dict)
        try:
            tbs = config_dict['text_box_style']
            config_dict['text_box_style'] \
                = TextBoxStyle.from_config(tbs)
        except KeyError:
            pass

        try:
            bg_spec = config_dict['background']
            # 'special' value to use the stamp vector image baked into
            # the module
            if bg_spec == '__stamp__':
                config_dict['background'] = STAMP_ART_CONTENT
            elif isinstance(bg_spec, str):
                from pyhanko.pdf_utils.images import PdfImage
                from PIL import Image
                img = Image.open(bg_spec)
                # Setting the writer can be delayed
                config_dict['background'] = PdfImage(img, writer=None)
        except KeyError:
            pass