Beispiel #1
0
def get_text_commands(
    x1,
    y1,
    x2,
    y2,
    text,
    font_size,
    wrap_text,
    align,
    baseline,
    line_spacing,
):
    """Return the graphics stream commands necessary to render a free text
    annotation, given the various parameters.

    Text is optionally wrapped, then arranged according to align (horizontal
    alignment), and baseline (vertical alignment).

    :param number x1: bounding box lower left x
    :param number y1: bounding box lower left y
    :param number x2: bounding box upper right x
    :param number y2: bounding box upper right y
    :param str text: text to add to annotation
    :param number font_size: font size
    :param bool wrap_text: whether to wrap the text
    :param str align: 'left'|'center'|'right'
    :param str baseline: 'top'|'middle'|'bottom'
    :param number line_spacing: multiplier to determine line spacing
    """
    font = get_true_type_font(
        path=HELVETICA_PATH,
        font_name=DEFAULT_BASE_FONT,
        font_size=font_size,
    )

    lines = get_wrapped_lines(
        text=text,
        measure=font.measure_text,
        max_length=x2 - x1,
    ) if wrap_text else [text]
    # Line breaking cares about the whitespace in the string, but for the
    # purposes of laying out the broken lines, we want to measure the lines
    # without trailing/leading whitespace.
    lines = [line.strip() for line in lines]
    y_coords = _get_vertical_coordinates(
        lines,
        y1,
        y2,
        font_size,
        line_spacing,
        baseline,
    )
    xs = _get_horizontal_coordinates(lines, x1, x2, font.measure_text, align)
    commands = []
    for line, x, y in zip(lines, xs, y_coords):
        commands.extend([
            TextMatrix(translate(x, y)),
            Text(line),
        ])
    return commands
Beispiel #2
0
 def get_ctm(x1, y1, x2, y2):
     """Get the scaled and translated CTM for an image to be placed in the
     bounding box defined by [x1, y1, x2, y2].
     """
     return matrix_multiply(
         translate(x1, y1),
         scale(x2 - x1, y2 - y1),
     )
Beispiel #3
0
    def test_multiply_is_associative(self):
        R = rotate(45)
        S = scale(3, 2)
        T = translate(5, -1)

        # T*(S*R)
        M1 = matrix_multiply(T, matrix_multiply(S, R))
        # (T*S)*R
        M2 = matrix_multiply(matrix_multiply(T, S), R)

        assert M1 == M2
        assert M1 == matrix_multiply(T, S, R)
Beispiel #4
0
    def _get_transform(bounding_box, rotation, _scale):
        """Get the transformation required to go from the user's desired
        coordinate space to PDF user space, taking into account rotation,
        scaling, translation (for things like weird media boxes).
        """
        # Unrotated width and height, in pts
        W = bounding_box[2] - bounding_box[0]
        H = bounding_box[3] - bounding_box[1]

        scale_matrix = scale(*_scale)

        x_translate = 0 + min(bounding_box[0], bounding_box[2])
        y_translate = 0 + min(bounding_box[1], bounding_box[3])
        mb_translate = translate(x_translate, y_translate)

        # Because of how rotation works the point isn't rotated around an axis,
        # but the axis itself shifts. So we have to represent the rotation as
        # rotation + translation.
        rotation_matrix = rotate(rotation)

        translate_matrix = identity()
        if rotation == 90:
            translate_matrix = translate(W, 0)
        elif rotation == 180:
            translate_matrix = translate(W, H)
        elif rotation == 270:
            translate_matrix = translate(0, H)

        # Order matters here - the transformation matrices are applied in
        # reverse order. So first we scale to get the points in PDF user space,
        # since all other operations are in that space. Then we rotate and
        # scale to capture page rotation, then finally we translate to account
        # for offset media boxes.
        transform = matrix_multiply(
            mb_translate,
            translate_matrix,
            rotation_matrix,
            scale_matrix,
        )
        return transform
Beispiel #5
0
 def test_as_pdf_object(self):
     x1, y1, x2, y2 = 10, 20, 100, 200
     image = Image(
         location=Location(x1=x1, y1=y1, x2=x2, y2=y2, page=0),
         appearance=Appearance(stroke_width=0, image=PNG_FILES[0]),
     )
     obj = image.as_pdf_object(identity(), page=None)
     # Appearance stream should place the Image correctly
     assert obj.AP.N.stream == (
         'q 0 0 0 RG 0 w 10 20 90 180 re 90 0 0 180 10 20 cm /Image Do Q')
     assert obj.Rect == [x1, y1, x2, y2]
     assert obj.AP.N.BBox == [x1, y1, x2, y2]
     assert obj.AP.N.Matrix == translate(-x1, -y1)
Beispiel #6
0
 def test_pdf_object(self):
     x1, y1, x2, y2 = 10, 20, 100, 200
     annotation = FreeText(
         Location(x1=x1, y1=y1, x2=x2, y2=y2, page=0),
         Appearance(
             fill=[0.4, 0, 0],
             stroke_width=1,
             font_size=5,
             content='Hi',
         ),
     )
     obj = annotation.as_pdf_object(identity(), page=None)
     assert obj.AP.N.stream == (
         'q BT 0.4 0 0 rg /{} 5 Tf '
         '1 0 0 1 11 109 Tm (Hi) Tj ET Q').format(PDF_ANNOTATOR_FONT)
     assert obj.DA == '0.4 0 0 rg /{} 5 Tf'.format(PDF_ANNOTATOR_FONT)
     assert obj.Rect == [x1, y1, x2, y2]
     assert obj.AP.N.BBox == [x1, y1, x2, y2]
     assert obj.AP.N.Matrix == translate(-x1, -y1)
Beispiel #7
0
    def _make_appearance_stream_dict(self, bounding_box, transform):
        resources = self._make_ap_resources()

        # Either use user-specified content stream or generate content stream
        # based on annotation type.
        stream = self._appearance.appearance_stream
        if stream is None:
            stream = self.make_appearance_stream()

        # Transform the appearance stream into PDF space and turn it into a str
        appearance_stream = stream.transform(transform).resolve()

        normal_appearance = PdfDict(
            stream=appearance_stream,
            BBox=bounding_box,
            Resources=resources,
            Matrix=translate(-bounding_box[0], -bounding_box[1]),
            Type=PdfName('XObject'),
            Subtype=PdfName('Form'),
            FormType=1,
        )
        return PdfDict(N=normal_appearance)
Beispiel #8
0
 def test_invert_translate(self):
     assert translate(-3, -5) == matrix_inverse(translate(3, 5))
     assert translate(-2, 1) == matrix_inverse(translate(2, -1))
Beispiel #9
0
    def test_translates_add(self):
        T1 = translate(3, 5)
        T2 = translate(2, -1)

        assert translate(5, 4) == matrix_multiply(T1, T2)
        assert translate(5, 4) == matrix_multiply(T2, T1)
Beispiel #10
0
 def test_weird_bounding_box(self):
     self._assert_transform(translate(0, -30), bounding_box=[0, -30, 20, 0])