def _add_rounded_rectangles(self, a): """Add a few rounded rectangles with different border radii.""" y1, y2 = 360, 410 xs = [10, 60, 110] rxs = [5, 10, 15] rys = [5, 5, 15] for x1, rx, ry in zip(xs, rxs, rys): x2 = x1 + 40 location = Location(x1=x1, y1=y1, x2=x2, y2=y2, page=0) content_stream = ContentStream([ Save(), StrokeColor(1, 0, 0), FillColor(0, 1, 0), ]) add_rounded_rectangle( stream=content_stream, x=x1, y=y1, width=(x2 - x1), height=(y2 - y1), rx=rx, ry=ry, ) content_stream.extend([ StrokeAndFill(), Restore(), ]) appearance = Appearance(appearance_stream=content_stream, ) a.add_annotation('square', location, appearance)
def test_transform_content_stream(self): cs = ContentStream([ Save(), Move(1, 1), Restore(), ]) transformed = cs.transform([2, 0, 0, 2, 5, 10]).resolve() assert transformed == 'q 7 12 m Q'
def make_default_appearance(self): """Returns a DA string for the text object, e.g. '1 0 0 rg /Helv 12 Tf' """ A = self._appearance stream = ContentStream([ FillColor(*A.fill[:3]), Font(PDF_ANNOTATOR_FONT, A.font_size), ]) return stream.resolve()
def make_appearance_stream(self): L = self._location A = self._appearance stream = ContentStream([Save()]) set_appearance_state(stream, A) add_bezier_circle(stream, L.x1, L.y1, L.x2, L.y2) stroke_or_fill(stream, A) stream.add(Restore()) return stream
def test_text_content_stream(self): cs = ContentStream([ CTM([1, 0, 0, 1, 0, 0]), Font('Helvetica', 12), TextMatrix([1, 0, 0, 1, 20, 50]), BeginText(), Text('Sure, why not?'), EndText(), ]) assert cs.resolve() == ('1 0 0 1 0 0 cm /Helvetica 12 Tf ' '1 0 0 1 20 50 Tm BT ' '(Sure, why not?) Tj ET')
def make_appearance_stream(self): A = self._appearance L = self._location stream = ContentStream([Save()]) set_appearance_state(stream, A) stream.extend([ Rect(L.x1, L.y1, L.x2 - L.x1, L.y2 - L.y1), CTM(self.get_ctm(L.x1, L.y1, L.x2, L.y2)), XObject('Image'), Restore(), ]) return stream
def make_appearance_stream(self): A = self._appearance L = self._location stream = ContentStream([ Save(), BeginText(), FillColor(*A.fill[:3]), Font(PDF_ANNOTATOR_FONT, A.font_size), ]) graphics_state = A.get_graphics_state() if graphics_state.has_content(): stream.add(CSGraphicsState(GRAPHICS_STATE_NAME)) # Actually draw the text inside the rectangle stream.extend( get_text_commands( L.x1, L.y1, L.x2, L.y2, text=A.content, font_size=A.font_size, wrap_text=A.wrap_text, align=A.text_align, baseline=A.text_baseline, line_spacing=A.line_spacing, )) stream.extend([ EndText(), Restore(), ]) return stream
def make_appearance_stream(self): A = self._appearance points = self._location.points stream = ContentStream([Save()]) set_appearance_state(stream, A) stream.add(Move(points[0][0], points[0][1])) for x, y in points[1:]: stream.add(CSLine(x, y)) stream.add(Close()) stroke_or_fill(stream, A) stream.add(Restore()) return stream
def _add_explicit_graphics_state_annotation(self, a): graphics_states = { 'BevelSquare': GraphicsState( line_join=constants.LINE_JOIN_BEVEL, line_cap=constants.LINE_CAP_SQUARE, stroke_transparency=0.75, ), 'MiterButt': GraphicsState( line_join=constants.LINE_JOIN_MITER, line_cap=constants.LINE_CAP_BUTT, stroke_transparency=0.5, ), 'RoundRound': GraphicsState( line_join=constants.LINE_JOIN_ROUND, line_cap=constants.LINE_CAP_ROUND, stroke_transparency=0.25, ), } # Defines the bounding box of the chevrons x1, y1, x2, y2 = 60, 310, 100, 350 lines_location = Location(x1=x1, y1=y1, x2=x2, y2=y2, page=0) # Defines the start/end of the chevrons x1, midpoint, x2 = 65, 80, 95 y1 = 315 content_stream = ContentStream([ Save(), StrokeWidth(5), CSGraphicsState('BevelSquare'), Move(x1, y1), Line(midpoint, y1 + 10), Line(x2, y1), Stroke(), CSGraphicsState('MiterButt'), Move(x1, y1 + 10), Line(midpoint, y1 + 20), Line(x2, y1 + 10), Stroke(), CSGraphicsState('RoundRound'), Move(x1, y1 + 20), Line(midpoint, y1 + 30), Line(x2, y1 + 20), Stroke(), Restore(), ]) appearance = Appearance( appearance_stream=content_stream, graphics_states=graphics_states, ) a.add_annotation( 'square', location=lines_location, appearance=appearance, )
def _add_explicit_image_annotation(self, a): """Add an image annotation using ContentStream commands instead of the Image type's commands. This is testing that the external XObjects API works, and that images can be embedded inside other, more complex annotations. """ x1, y1, x2, y2 = 10, 310, 50, 350 location = Location(x1=x1, y1=y1, x2=x2, y2=y2, page=0) content_stream = ContentStream([ StrokeColor(1, 0, 0), Rect(x1, y1, x2 - x1, y2 - y1), Save(), # The image is inside an outer rectangle CTM(Image.get_ctm(x1 + 10, y1 + 10, x2 - 10, y2 - 10)), XObject('MyXObject'), Restore(), Stroke(), ]) appearance = Appearance( appearance_stream=content_stream, xobjects={ 'MyXObject': Image.make_image_xobject(PNG_FILES[0]), }, ) a.add_annotation( 'square', location=location, appearance=appearance, )
def make_appearance_stream(self): A = self._appearance points = self._location.points stream = ContentStream([Save()]) set_appearance_state(stream, A) stream.add(Move(points[0][0], points[0][1])) for x, y in points[1:]: stream.add(CSLine(x, y)) # TODO add a 'close' attribute? stream.extend([Stroke(), Restore()]) return stream
def make_appearance_stream(self): A = self._appearance points = self._location.points stream = ContentStream([Save()]) set_appearance_state(stream, A) stream.add(Move(points[0][0], points[0][1])) # TODO "real" PDF editors do smart smoothing of ink points using # interpolated Bezier curves. for x, y in points[1:]: stream.add(CSLine(x, y)) stream.extend([Stroke(), Restore()]) return stream
def make_appearance_stream(self): L = self._location A = self._appearance stream = ContentStream([Save()]) set_appearance_state(stream, A) stream.add(Rect( L.x1, L.y1, L.x2 - L.x1, L.y2 - L.y1, )) stroke_or_fill(stream, A) stream.add(Restore()) # TODO dash array return stream
def test_commands_get_number_formatting(self): # Regression test to make sure that all commands that output number # have sane formatting. stream = ContentStream([ StrokeWidth(0.0), StrokeColor(0.0, 0.0, 0.0), FillColor(0.0, 0.0, 0.0), Rect(0.0, 0.0, 0.0, 0.0), Move(0.0, 0.0), Line(0.0, 0.0), Bezier(0.0, 0.0, 0.0, 0.0, 0.0, 0.0), CTM([0.0, 0.0, 0.0, 0.0, 0.0, 0.0]), TextMatrix([0.0, 0.0, 0.0, 0.0, 0.0, 0.0]), ]).resolve() assert stream == ('0 w ' '0 0 0 RG ' '0 0 0 rg ' '0 0 0 0 re ' '0 0 m ' '0 0 l ' '0 0 0 0 0 0 c ' '0 0 0 0 0 0 cm ' '0 0 0 0 0 0 Tm')
def _add_explicit_text_annotation(self, a): x1, y1, x2, y2 = 110, 310, 200, 350 font_size = 4 content_stream = ContentStream([ Save(), BeginText(), FillColor(0, 0, 0), Font('MyFontyFont', font_size), ]) content_stream.extend( get_text_commands( x1, y1, x2, y2, text=(r'Twas brilling and the slithy toves \n' r'Did gyre and gimbel in the wabe \n' r'All mimsy were the borogroves \n' r'And the mome raths outgrabe \n'), font_size=font_size, wrap_text=True, align=constants.TEXT_ALIGN_LEFT, baseline=constants.TEXT_BASELINE_TOP, line_spacing=1.2, )) content_stream.extend([ EndText(), Restore(), ]) appearance = Appearance( appearance_stream=content_stream, fonts={'MyFontyFont': FreeText.make_font_object()}, ) a.add_annotation( 'square', location=Location(x1=x1, y1=y1, x2=x2, y2=y2, page=0), appearance=appearance, )
class TestContentStream(TestCase): # a list of (ContentStream, stream_string) pairs for testing parse/resolve FIXTURES = [ (ContentStream([ CTM([1, 0, 0, 1, 0, 0]), Font('Helvetica', 12), TextMatrix([1, 0, 0, 1, 20, 50]), BeginText(), Text('Sure, why not?'), EndText(), ]), ('1 0 0 1 0 0 cm /Helvetica 12 Tf ' '1 0 0 1 20 50 Tm BT ' '(Sure, why not?) Tj ET')), (ContentStream([ Save(), StrokeWidth(2), StrokeColor(0, 0, 0), FillColor(1, 0, 0), Move(10, 10), Line(20, 20), Bezier(30, 30, 40, 40, 50, 50), Rect(50, 50, 10, 10), Close(), StrokeAndFill(), Stroke(), Fill(), Restore(), ]), ('q 2 w 0 0 0 RG 1 0 0 rg 10 10 m 20 20 l ' '30 30 40 40 50 50 c 50 50 10 10 re ' 'h B S f Q')), ] def test_equality(self): assert ContentStream() == ContentStream() cs1 = ContentStream([Save(), FillColor(1, 0, 0)]) cs2 = ContentStream([Save(), FillColor(1, 0, 0)]) assert cs1 == cs2 def test_content_stream_not_equal_to_string(self): assert ContentStream() != '' assert ContentStream([Save()]) != 'q' def test_resolve(self): for cs, stream_string in self.FIXTURES: assert cs.resolve() == stream_string def test_content_stream(self): # Basically a smoke test for all the simple functions of ContentStream cs = ContentStream([Save(), StrokeWidth(2)]) cs.add(StrokeColor(0, 0, 0)) cs.add(FillColor(1, 0, 0)) cs.extend([ Move(10, 10), Line(20, 20), Bezier(30, 30, 40, 40, 50, 50), Rect(50, 50, 10, 10), ]) cs = ContentStream.join( cs, ContentStream([ Close(), StrokeAndFill(), Stroke(), Fill(), Restore(), ])) assert cs.resolve() == ('q 2 w 0 0 0 RG 1 0 0 rg 10 10 m 20 20 l ' '30 30 40 40 50 50 c 50 50 10 10 re ' 'h B S f Q') def test_text_content_stream(self): cs = ContentStream([ CTM([1, 0, 0, 1, 0, 0]), Font('Helvetica', 12), TextMatrix([1, 0, 0, 1, 20, 50]), BeginText(), Text('Sure, why not?'), EndText(), ]) assert cs.resolve() == ('1 0 0 1 0 0 cm /Helvetica 12 Tf ' '1 0 0 1 20 50 Tm BT ' '(Sure, why not?) Tj ET') def test_transform_move(self): transformed = Move(1, 1).transform([2, 0, 0, 2, 5, 10]).resolve() assert transformed == '7 12 m' def test_transform_line(self): transformed = Line(1, 1).transform([2, 0, 0, 2, 5, 10]).resolve() assert transformed == '7 12 l' def test_transform_rect(self): transformed = Rect(1, 1, 2, 2).transform([2, 0, 0, 2, 5, 10]).resolve() assert transformed == '7 12 4 4 re' def test_transform_bezier(self): transformed = Bezier(1, 1, 2, 2, 3, 3).transform([2, 0, 0, 2, 5, 10], ).resolve() assert transformed == '7 12 9 14 11 16 c' def test_transform_content_stream(self): cs = ContentStream([ Save(), Move(1, 1), Restore(), ]) transformed = cs.transform([2, 0, 0, 2, 5, 10]).resolve() assert transformed == 'q 7 12 m Q'
def test_equality(self): assert ContentStream() == ContentStream() cs1 = ContentStream([Save(), FillColor(1, 0, 0)]) cs2 = ContentStream([Save(), FillColor(1, 0, 0)]) assert cs1 == cs2
def test_content_stream_not_equal_to_string(self): assert ContentStream() != '' assert ContentStream([Save()]) != 'q'
def test_content_stream(self): # Basically a smoke test for all the simple functions of ContentStream cs = ContentStream([Save(), StrokeWidth(2)]) cs.add(StrokeColor(0, 0, 0)) cs.add(FillColor(1, 0, 0)) cs.extend([ Move(10, 10), Line(20, 20), Bezier(30, 30, 40, 40, 50, 50), Rect(50, 50, 10, 10), ]) cs = ContentStream.join( cs, ContentStream([ Close(), StrokeAndFill(), Stroke(), Fill(), Restore(), ])) assert cs.resolve() == ('q 2 w 0 0 0 RG 1 0 0 rg 10 10 m 20 20 l ' '30 30 40 40 50 50 c 50 50 10 10 re ' 'h B S f Q')
def test_field(self): content_stream = ContentStream([]) f = self.F(integer=2, content_stream=content_stream) assert f.integer == 2 assert f.content_stream is content_stream