def test_fill_preserve(self): path = Path(__file__).parent.joinpath( 'output/polygon_drawer_fill_preserve.svg') path.unlink(missing_ok=True) canvas_builder = CanvasBuilder() canvas_builder.set_path(path) canvas_builder.set_size(Cu.from_pt(100), Cu.from_pt(100)) canvas = canvas_builder.build() polygon_drawer = PolygonDrawer() polygon_drawer.fill_color = (1, 0, 0) polygon_drawer.stroke_color = (0, 1, 0) polygon_drawer.stroke_width = Cu.from_pt(2) polygon_drawer.geoms = [ Polygon([ (30, 30), (70, 30), (70, 70), (30, 70), (30, 30), ]) ] polygon_drawer.draw(canvas) canvas.close() assert path.exists() with open(path, 'r') as file: data = file.read() assert data.find('M 30 30 L 70 30 L 70 70 L 30 70 Z M 30 30') != -1 assert data.find('fill:rgb(100%,0%,0%)') != -1 assert data.find('stroke-width:2') != -1 assert data.find('stroke:rgb(0%,100%,0%);') != -1
def __init__(self, path: Path, surface_type: str, width: float, height: float, scale: float = 1): self.width = width self.height = height self.scale = scale self.path_as_posix = path.as_posix() if surface_type == 'pdf': surface = cairo.PDFSurface(self.path_as_posix, width, height) elif surface_type == 'svg': surface = cairo.SVGSurface(self.path_as_posix, width, height) elif surface_type == 'png': surface = cairo.ImageSurface(cairo.FORMAT_ARGB32, int(width), int(height)) else: raise Exception('Unexpected Format: %s' % surface_type) context = cairo.Context(surface) self.surface = surface self.context = context if isinstance(self.surface, cairo.ImageSurface): self.context.scale( CanvasUnit.from_pt(1).px * self.scale, CanvasUnit.from_pt(1).px * self.scale)
def logical_extents(self) -> CanvasBbox: extent = self._layout.get_extents()[1] x = CanvasUnit.from_pt(pangocffi.units_to_double(extent.x)) y = CanvasUnit.from_pt(pangocffi.units_to_double(extent.y)) x += self._position.x y += self._position.y width = CanvasUnit.from_pt(pangocffi.units_to_double(extent.width)) height = CanvasUnit.from_pt(pangocffi.units_to_double(extent.height)) return CanvasBbox(CanvasCoordinate(x, y), width, height)
def test_identity(self): assert CanvasUnit.from_pt(1).pt == 1 assert CanvasUnit.from_in(1).inches == 1 assert CanvasUnit.from_cm(1).cm == 1 assert CanvasUnit.from_mm(1).mm == 1 assert CanvasUnit.from_px(1).px == 1 assert CanvasUnit.from_pango(1).pango == 1 assert CanvasUnit.from_pt(2).pt == 2 assert CanvasUnit.from_in(2).inches == 2 assert CanvasUnit.from_cm(2).cm == 2 assert CanvasUnit.from_mm(2).mm == 2 assert CanvasUnit.from_px(2).px == 2 assert CanvasUnit.from_pango(2).pango == 2
def draw_symbol(self, key, point: Point, canvas: Canvas): canvas.context.set_source_rgba(point.x/100, point.y/100, 0, 1) CairoHelper.draw_point( canvas.context, point, Cu.from_pt(key).pt )
def test_single_stripe(self): path = Path(__file__).parent.joinpath( 'output/stripe_filled_polygon_drawer_single.svg') path.unlink(missing_ok=True) canvas_builder = CanvasBuilder() canvas_builder.set_path(path) canvas_builder.set_size(Cu.from_pt(100), Cu.from_pt(100)) canvas = canvas_builder.build() drawer = StripeFilledPolygonDrawer() drawer.stripe_colors = [(0, 1, 0)] drawer.geoms = [ Polygon([ (30, 30), (70, 30), (70, 70), (30, 70), (30, 30), ]) ] drawer.draw(canvas) drawer.geoms = [ Polygon([ (40, 40), (60, 40), (60, 60), (40, 60), (40, 40), ]) ] drawer.stripe_colors = [None] drawer.draw(canvas) canvas.close() assert path.exists() with open(path, 'r') as file: data = file.read() assert data.find('M 30 30 L 70 30 L 70 70 L 30 70 Z M 30 30') != -1 assert data.find('M 40 40 L 60 40 L 60 60 L 40 60 Z M 40 40') == -1 assert data.find('fill:rgb(0%,100%,0%)') != -1 assert data.find('stroke:none') != -1 assert data.find('stroke-width:') == -1
def test_z_func(self): path = Path(__file__).parent.joinpath( 'output/symbol_drawer_z_func.svg' ) path.unlink(missing_ok=True) canvas_builder = CanvasBuilder() canvas_builder.set_path(path) canvas_builder.set_size(Cu.from_pt(100), Cu.from_pt(100)) canvas = canvas_builder.build() class MySymbolDrawer(SymbolDrawer): def draw_symbol(self, key, point: Point, canvas: Canvas): canvas.context.set_source_rgba(point.x/100, point.y/100, 0, 1) CairoHelper.draw_point( canvas.context, point, Cu.from_pt(20).pt ) my_symbol_drawer = MySymbolDrawer() my_symbol_drawer.points = [ Point(30, 70), Point(35, 60), Point(40, 50), Point(45, 40), Point(50, 30), Point(55, 40), Point(60, 50), Point(65, 60), Point(70, 70) ] my_symbol_drawer.draw(canvas) canvas.close() assert path.exists() with open(path, 'r') as file: data = file.read() # Assert that the symbols appear assert data.find('rgb(50%,30%,0%)') != -1 assert data.find('rgb(50%,30%,0%)') < data.find('rgb(45%,40%,0%)') assert data.find('rgb(45%,40%,0%)') < data.find('rgb(55%,40%,0%)')
def test_can_iterate_dict(self): path = Path(__file__).parent.joinpath( 'output/symbol_drawer_can_iterate_dict.svg' ) path.unlink(missing_ok=True) canvas_builder = CanvasBuilder() canvas_builder.set_path(path) canvas_builder.set_size(Cu.from_pt(100), Cu.from_pt(100)) canvas = canvas_builder.build() class MySymbolDrawer(SymbolDrawer): def draw_symbol(self, key, point: Point, canvas: Canvas): canvas.context.set_source_rgba(point.x/100, point.y/100, 0, 1) CairoHelper.draw_point( canvas.context, point, Cu.from_pt(key).pt ) my_symbol_drawer = MySymbolDrawer() my_symbol_drawer.points = { 2: Point(30, 70), 4: Point(35, 60), 6: Point(40, 50), 8: Point(45, 40), 10: Point(50, 30), 12: Point(55, 40), 14: Point(60, 50), 16: Point(65, 60), 18: Point(70, 70) } my_symbol_drawer.draw(canvas) canvas.close() assert path.exists() with open(path, 'r') as file: data = file.read() # Assert that the symbols appear with correct size assert data.find('d="M 55 30 C') != -1 assert data.find('d="M 79 70 C') != -1
def test_validation(self): with self.assertRaisesRegex( Exception, 'length of width_arr is not the same as color_arr'): drawer = StripeFilledPolygonDrawer() drawer.stripe_colors = [(0, 1, 0), (0, 1, 0)] drawer.stripe_widths = [Cu.from_pt(10)] drawer.draw(MagicMock()) with self.assertRaisesRegex( Exception, 'width_arr must contain at least one value'): drawer = StripeFilledPolygonDrawer() drawer.stripe_colors = [] drawer.stripe_widths = [] drawer.draw(MagicMock()) with self.assertRaisesRegex( Exception, 'width_arr must be a positive non-zero length'): drawer = StripeFilledPolygonDrawer() drawer.stripe_colors = [(0, 1, 0), (0, 1, 0)] drawer.stripe_widths = [Cu.from_pt(10), Cu.from_pt(0)] drawer.draw(MagicMock())
def test_multi_line_string(self): path = Path(__file__).parent.joinpath( 'output/line_drawer_multi_line_string.svg') path.unlink(missing_ok=True) canvas_builder = CanvasBuilder() canvas_builder.set_path(path) canvas_builder.set_size(Cu.from_pt(100), Cu.from_pt(100)) canvas = canvas_builder.build() line_drawer = LineDrawer() line_drawer.stroke_color = (1, 0, 0) line_drawer.stroke_width = Cu.from_pt(1.5) line_drawer.geoms = [ MultiLineString([[ (30, 30), (70, 30), (70, 70), (30, 70), ], [ (40, 40), (40, 60), (60, 60), (60, 40), ]]) ] line_drawer.draw(canvas) canvas.close() assert path.exists() with open(path, 'r') as file: data = file.read() assert data.find('M 30 30 L 70 30 L 70 70 L 30 70') != -1 assert data.find('M 40 40 L 40 60 L 60 60 L 60 40') != -1 assert data.find('fill:none') != -1 assert data.find('stroke-width:1.5') != -1 assert data.find('stroke:rgb(100%,0%,0%);') != -1
def test_two_vertical_stripes(self): path = Path(__file__).parent.joinpath( 'output/stripe_filled_polygon_drawer_two_vertical_stripes.svg') path.unlink(missing_ok=True) canvas_builder = CanvasBuilder() canvas_builder.set_path(path) canvas_builder.set_size(Cu.from_pt(100), Cu.from_pt(100)) canvas = canvas_builder.build() drawer = StripeFilledPolygonDrawer() drawer.stripe_colors = [(0, 1, 0), (0, 0, 1)] drawer.stripe_widths = [Cu.from_pt(20), Cu.from_pt(20)] drawer.stripe_origin = CanvasCoordinate.from_pt(30, 30) drawer.stripe_angle = math.pi / 2 # Vertical stripes drawer.geoms = [ Polygon([ (30, 30), (70, 30), (70, 70), (30, 70), (30, 30), ]) ] drawer.draw(canvas) canvas.close() assert path.exists() with open(path, 'r') as file: data = file.read() assert data.find('M 30 30 L 30 70 L 50 70 L 50 30 Z M 30 30') != -1 assert data.find('M 50 30 L 50 70 L 70 70 L 70 30 Z M 50 30') != -1 assert data.find('fill:rgb(0%,100%,0%)') != -1 assert data.find('fill:rgb(0%,0%,100%)') != -1 assert data.find('stroke:none') != -1 assert data.find('stroke-width:') == -1
def test_multi_polygons(self): path = Path(__file__).parent.joinpath( 'output/stripe_filled_polygon_drawer_multi_polygons.svg') path.unlink(missing_ok=True) canvas_builder = CanvasBuilder() canvas_builder.set_path(path) canvas_builder.set_size(Cu.from_pt(100), Cu.from_pt(100)) canvas = canvas_builder.build() drawer = StripeFilledPolygonDrawer() drawer.stripe_colors = [(1, 1, 0), None, (0.7, 0, 0.7), (0, 0, 0)] drawer.stripe_widths = [ Cu.from_pt(5), Cu.from_pt(5), Cu.from_pt(5), Cu.from_pt(5), ] drawer.stripe_origin = CanvasCoordinate.from_pt(30, 30) drawer.stripe_angle = math.pi / 8 # Vertical stripes drawer.geoms = [ MultiPolygon([ Polygon([ (30, 30), (70, 30), (70, 70), (30, 70), (30, 30), ], [[ (35, 35), (65, 35), (65, 65), (35, 65), (35, 35), ]]), Polygon([ (40, 40), (60, 40), (60, 60), (40, 60), (40, 40), ]) ]) ] drawer.draw(canvas) canvas.close() assert path.exists() with open(path, 'r') as file: data = file.read() assert data.find('fill:rgb(100%,100%,0%)') != -1 assert data.find('fill:rgb(0%,0%,0%)') != -1 assert data.find('fill:rgb(70%,0%,70%)') != -1 assert data.find('stroke:none') != -1 assert data.find('stroke-width:') == -1
def test_line_string(self): path = Path(__file__).parent.joinpath( 'output/line_drawer_line_string.svg') path.unlink(missing_ok=True) canvas_builder = CanvasBuilder() canvas_builder.set_path(path) canvas_builder.set_size(Cu.from_pt(100), Cu.from_pt(100)) canvas = canvas_builder.build() line_drawer = LineDrawer() line_drawer.stroke_color = (0, 1, 0) line_drawer.stroke_width = Cu.from_pt(1.5) line_drawer.stroke_dashes = ([ Cu.from_pt(1), Cu.from_pt(3), Cu.from_pt(3), Cu.from_pt(3) ], Cu.from_pt(4)) line_drawer.stroke_line_cap = cairocffi.constants.LINE_CAP_ROUND line_drawer.stroke_line_join = cairocffi.constants.LINE_JOIN_MITER line_drawer.geoms = [ LineString([ (30, 30), (70, 30), (70, 70), (30, 70), ]) ] line_drawer.draw(canvas) canvas.close() assert path.exists() with open(path, 'r') as file: data = file.read() assert data.find('M 30 30 L 70 30 L 70 70 L 30 70') != -1 assert data.find('fill:none') != -1 assert data.find('stroke-width:1.5') != -1 assert data.find('stroke:rgb(0%,100%,0%);') != -1
def draw_rectangle(self, canvas: Canvas, offset: int, unit: str): height = CanvasUnit.from_pt(10).pt width = CanvasUnit.from_unit(1, unit).pt offset_height = height * offset top_left = (0, offset_height) top_right = (width, offset_height) bottom_left = (0, offset_height + height) bottom_right = (width, offset_height + height) shape = Polygon([ top_left, top_right, bottom_right, bottom_left, top_left ]) canvas.context.set_source_rgba(*self.color) CairoHelper.draw_polygon(canvas.context, shape) canvas.context.set_source_rgba(0.5, 0.5, 0.5, 1) canvas.context.fill()
def test_setters_and_getters(self): # Create a canvas for the sake of instantiating a layout object. We do # this because that is how Pango actually resolves the font dimensions. path = Path(__file__).parent.joinpath( 'output/layout_setters_and_getters.svg') canvas_builder = CanvasBuilder() canvas_builder.set_path(path) canvas_builder.set_size(Cu.from_pt(100), Cu.from_pt(100)) canvas = canvas_builder.build() layout = Layout(canvas) layout.set_text('Hello world') layout.set_markup('<span weight="bold">Hello world!</span>') assert layout.width is None assert layout.height is None layout.width = Cu.from_pt(100) layout.height = Cu.from_pt(50) assert layout.width.pt == 100 assert layout.height.pt == 50 layout.reset_width() layout.reset_height() assert layout.width is None assert layout.height is None assert layout.position.x.pt == 0 assert layout.position.y.pt == 0 layout.position = CanvasCoordinate(Cu.from_pt(10), Cu.from_pt(5)) assert layout.position.x.pt == 10 assert layout.position.y.pt == 5 assert layout.color == (0, 0, 0, 1) layout.color = (0, 1, 0, 0.5) assert layout.color == (0, 1, 0, 0.5) assert layout.alignment == Alignment.LEFT layout.alignment = Alignment.RIGHT assert layout.alignment == Alignment.RIGHT extents = layout.logical_extents assert extents.pos.x.pt == 10 assert extents.pos.y.pt == 5 assert extents.width.pt > 1 assert extents.height.pt > 1
def height(self) -> Optional[CanvasUnit]: pango_height = self._layout.get_height() if pango_height == -1: return None return CanvasUnit.from_pt(pangocffi.units_to_double(pango_height))
def __init__(self): self.geoms = [] self.stripe_widths = [CanvasUnit.from_pt(1)] self.stripe_colors = [(0, 0, 0, 1)] self.stripe_angle = 0 self.stripe_origin = CanvasCoordinate.origin()
def test_pango_units_are_not_floating_point(self): assert CanvasUnit.from_pt(1 / 4096).pango == 0 assert CanvasUnit.from_pt(2 / 1024 - 1 / 4096).pango == 2 assert CanvasUnit.from_pt(2 / 1024 + 1 / 4096).pango == 2
def read_svg_size(self) -> Tuple[CanvasUnit, CanvasUnit]: svg_surface = SvgSurface(self.path) return ( CanvasUnit.from_pt(svg_surface.svg_width), CanvasUnit.from_pt(svg_surface.svg_height) )
def __init__(self): self.stroke_color = (0, 0, 0, 1) self.stroke_width = CanvasUnit.from_pt(1) self.stroke_dashes = None self.stroke_line_cap = None self.stroke_line_join = None
def test_pango_scale_is_consistent_with_library(self): assert CanvasUnit.from_pt(2).pango == pangocffi.units_from_double(2) assert CanvasUnit.from_pango(512).pt == pangocffi.units_to_double(512)
def test_comparisons(self): assert CanvasUnit.from_in(1).pt > CanvasUnit.from_cm(1).pt assert CanvasUnit.from_cm(1).pt > CanvasUnit.from_mm(1).pt assert CanvasUnit.from_mm(1).pt > CanvasUnit.from_pt(1).pt assert CanvasUnit.from_pt(1).pt > CanvasUnit.from_px(1).pt assert CanvasUnit.from_px(1).pt > CanvasUnit.from_pango(1).pt
def from_pt(cls, x: float, y: float) -> 'CanvasCoordinate': return CanvasCoordinate(CanvasUnit.from_pt(x), CanvasUnit.from_pt(y))
def width(self) -> Optional[CanvasUnit]: pango_width = self._layout.get_width() if pango_width == -1: return None return CanvasUnit.from_pt(pangocffi.units_to_double(pango_width))