def add_destination_box(self, page, direction, destination_name): """Add destination boxes to page. Args: page (Image): Page to add image to. direction (str): Direction boat is travelling. destination_name (str): Name of destination island. """ draw = ImageDraw.Draw(page) textbox_drawer = TextBoxDrawer(page, draw) if direction == "left": x_position = 0 else: x_position = BOX_WIDTH vertices = calculate_box_vertices( (x_position, BOX_HEIGHT * 3), BOX_WIDTH, BOX_HEIGHT, ) text_box = TextBox( vertices, BOX_WIDTH, BOX_HEIGHT, font_path=FONT_PATH, font_size=50, color=FONT_COLOUR, ) textbox_drawer.write_text_box( text_box, destination_name, horiz_just="center", vert_just="center", )
def add_island_box(self, page, island_id, island_data): """Add island label to top of page. Args: page (Image): Page to add image to. island_id (str): ID of island. island_data (dict): Data of island. """ draw = ImageDraw.Draw(page) textbox_drawer = TextBoxDrawer(page, draw) island_image = Image.open(BASE_PATH.format(island_id)) island_width, island_height = island_image.size coords = (int(BOX_WIDTH - (island_width / 2)), int(((BOX_HEIGHT * 2 * 0.7) - island_height) / 2)) page.paste(island_image, box=coords) island_name_width = BOX_WIDTH * 2 island_name_height = BOX_HEIGHT * 2 * 0.3 vertices = calculate_box_vertices( (0, BOX_HEIGHT * 2 * 0.7), island_name_width, island_name_height) box = TextBox( vertices, island_name_width, island_name_height, font_path=FONT_PATH, font_size=200, color=FONT_COLOUR, ) textbox_drawer.write_text_box( box, island_data["name"], horiz_just="center", vert_just="top", )
def test_default_font_override_he(self): image = Image.new("RGB", (2000, 4000)) draw = ImageDraw.Draw(image) tbd = TextBoxDrawer(image, draw) with translation.override("he"): self.assertEqual(tbd.get_default_font(), "static/fonts/OpenSansHebrew-Regular.ttf")
def test_write_text_box_object_justification_smoke_test(self): image = Image.new("RGB", (1000, 1000)) draw = ImageDraw.Draw(image) vertices = [(0, 0), (200, 0), (200, 100), (0, 100)] width = 200 height = 100 box = TextBox(vertices, width, height) for horiz_just in ["left", "center", "right"]: for vert_just in ["top", "center", "bottom"]: TextBoxDrawer.write_text_box_object( image, draw, box, "This is a string", horiz_just=horiz_just, vert_just=vert_just, ) with translation.override("he"): for horiz_just in ["left", "center", "right"]: for vert_just in ["top", "center", "bottom"]: TextBoxDrawer.write_text_box_object( image, draw, box, "עברית", horiz_just=horiz_just, vert_just=vert_just, )
def test_default_font_override_ja(self): image = Image.new("RGB", (2000, 4000)) draw = ImageDraw.Draw(image) tbd = TextBoxDrawer(image, draw) with translation.override("ja"): self.assertEqual(tbd.get_default_font(), "static/fonts/NotoSansCJKjp-Regular.otf")
def test_get_box_core(self): svg = os.path.join(BASE_PATH, "basic.svg") # In PNG Space, x is *2, and y is *4 from SVG Space image = Image.new("RGB", (2000, 4000)) tbd = TextBoxDrawer(image, None, svg) box = tbd.get_box("box1") self.assertIsInstance(box, TextBox) self.assertIsInstance(box.vertices, list) self.assertEqual(4, len(box.vertices)) x, y = box.vertices[0] # SVG x=100, *2 to convert into PNG space self.assertAlmostEqual(2 * 100, x) # SVG y=200, *4 to convert into PNG space self.assertAlmostEqual(4 * 200, y) # SVG width=50, *2 to convert into PNG space self.assertAlmostEqual(2 * 50.0, box.width) # SVG height=75, *4 to convert into PNG space self.assertAlmostEqual(4 * 75.0, box.height) self.assertEqual("#006838", box.color) self.assertEqual("static/fonts/PatrickHand-Regular.ttf", box.font_path) # SVG font size is 48px, converted into PNG space self.assertEqual(4 * 48, box.font_size) self.assertIsInstance(box.font_size, int) self.assertEqual(0, box.angle)
def data(self): """Create data for a copy of the Left and Right Cards resource. Returns: A dictionary of the one page for the resource. """ image_path = "static/img/resources/left-right-cards/left-right-cards.png" image = Image.open(image_path) draw = ImageDraw.Draw(image) textbox_drawer = TextBoxDrawer(image, draw) for label, label_data in LABEL_DATA.items(): label_text = label_data["text"] for (top_left, width, height) in label_data["areas"]: vertices = calculate_box_vertices(top_left, width, height) box = TextBox(vertices, width, height, font_path=FONT_PATH, font_size=FONT_SIZE, angle=label_data["rotation"]) textbox_drawer.write_text_box( box, label_text, horiz_just="center", ) image = image.rotate(90, expand=True) return {"type": "image", "data": image}
def test_default_font_override_ar(self): image = Image.new("RGB", (2000, 4000)) draw = ImageDraw.Draw(image) tbd = TextBoxDrawer(image, draw) with translation.override("ar"): self.assertEqual(tbd.get_default_font(), "static/fonts/DejaVuSans.ttf")
def label_keys(self, image, label_type): """Label keys on image. Args: image (Image): PillowImage of image. label_type (str): Key of label type. """ draw = ImageDraw.Draw(image) textbox_drawer = TextBoxDrawer(image, draw) labels = KEY_LABELS[label_type] for index, (vertices, width, height) in enumerate(KEY_AREAS): position = index % 7 box = TextBox( vertices, width, height, color=LABEL_COLOURS[position], font_path="static/fonts/PatrickHand-Regular.ttf", font_size=120, ) textbox_drawer.write_text_box( box, labels[position], horiz_just="center", )
def test_fallback_font_if_required_valid_font(self): svg = os.path.join(BASE_PATH, "basic.svg") image = Image.new("RGB", (1000, 1000)) tbd = TextBoxDrawer(image, None, svg) font_path = "static/fonts/PatrickHand-Regular.ttf" font = tbd.fallback_font_if_required(font_path, "abc") self.assertEqual(font_path, font)
def create_map_page(self): """Create map page for students. Returns: A dictionary of the data for the page. """ path = BASE_PATH.format(MAP_TYPE) image = Image.open(path) draw = ImageDraw.Draw(image) textbox_drawer = TextBoxDrawer(image, draw) for island_id, island_data in ISLANDS.items(): box_data = island_data["map-location"] top_left_coords = box_data["top-left-coords"] width = box_data["width"] height = box_data["height"] vertices = calculate_box_vertices(top_left_coords, width, height) box = TextBox( vertices, width, height, font_path=FONT_PATH, font_size=80, color=FONT_COLOUR, ) textbox_drawer.write_text_box( box, island_data["name"], horiz_just="center", vert_just="top", ) image = image.rotate(90, expand=True) page_data = {"type": "image", "data": image} return page_data
def test_fallback_font_if_required_ordinal_too_high(self): svg = os.path.join(BASE_PATH, "basic.svg") image = Image.new("RGB", (1000, 1000)) tbd = TextBoxDrawer(image, None, svg) font_path = "static/fonts/PatrickHand-Regular.ttf" new_font_path = tbd.fallback_font_if_required(font_path, chr(300)) self.assertNotEqual(font_path, new_font_path) self.assertEqual(DEFAULT_FONT, new_font_path)
def text_get_box_without_style(self): svg = os.path.join(BASE_PATH, "basic.svg") image = Image.new("RGB", (1000, 1000)) tbd = TextBoxDrawer(image, None, svg) box = tbd.get_box("withoutstyle") self.assertEqual(None, box.color) self.assertEqual(None, box.font_path) self.assertEqual(None, box.font_size)
def text_get_box_with_tspan(self): svg = os.path.join(BASE_PATH, "basic.svg") image = Image.new("RGB", (1000, 1000)) tbd = TextBoxDrawer(image, None, svg) box = tbd.get_box("withtspan") self.assertEqual("#414042", box.color) self.assertEqual("static/fonts/PatrickHand-Regular.ttf", box.font_path) self.assertEqual(48, box.font_size) self.assertIsInstance(box.font_size, int)
def test_write_text_box_defaults_smoke_test(self): image = Image.new("RGB", (2000, 4000)) draw = ImageDraw.Draw(image) tbd = TextBoxDrawer(image, draw) vertices = [(0, 0), (200, 0), (200, 100), (0, 100)] width = 200 height = 100 box = TextBox(vertices, width, height) tbd.write_text_box( box, "This is a string", )
def test_write_text_box_object_rotated_smoke_test(self): image = Image.new("RGB", (1000, 1000)) draw = ImageDraw.Draw(image) vertices = [(0, 200), (0, 0), (100, 0), (100, 200)] width = 200 height = 100 rotation = 90 box = TextBox(vertices, width, height, angle=rotation) TextBoxDrawer.write_text_box_object( image, draw, box, "This is a string", )
def test_get_box_rotated(self): svg = os.path.join(BASE_PATH, "basic.svg") image = Image.new("RGB", (1000, 1000)) tbd = TextBoxDrawer(image, None, svg) box = tbd.get_box("rotated") self.assertAlmostEqual(math.radians(45.0), box.angle, places=3) # Check location of bottom left corner bottomleft_x, bottomleft_y = box.vertices[3] self.assertAlmostEqual(bottomleft_x, 1 / math.sqrt(2) * box.width, places=1) self.assertAlmostEqual(bottomleft_y, 1 / math.sqrt(2) * box.height, places=1)
def test_initialisation_basic_svg(self): svg = os.path.join(BASE_PATH, "basic.svg") image = Image.new("RGB", (2000, 4000)) tbd = TextBoxDrawer(image, None, svg) self.assertIsInstance(tbd.svg, ET._Element) self.assertAlmostEqual(tbd.width_ratio, 2) self.assertAlmostEqual(tbd.height_ratio, 4)
def test_write_text_box_object_params_smoke_test(self): image = Image.new("RGB", (1000, 1000)) draw = ImageDraw.Draw(image) vertices = [(0, 0), (200, 0), (200, 100), (0, 100)] width = 200 height = 100 box = TextBox(vertices, width, height) TextBoxDrawer.write_text_box_object( image, draw, box, "This is a string", font_path="static/fonts/PatrickHand-Regular", font_size=17, line_spacing=10, color="#013291")
def test_fit_text_smoke_test_one_word(self): font_path = "static/fonts/PatrickHand-Regular.ttf" font_size = 10 text = "word" font = ImageFont.truetype(font_path, font_size) text_width, text_height = font.getsize(text) new_font_size, lines, new_text_width, new_text_height = TextBoxDrawer.fit_text( text, text_width, text_height, font_path, font_size, 4)
def test_fit_text_valid_fontsize(self): font_path = "static/fonts/PatrickHand-Regular.ttf" font_size = 10 text = "This is a string" font = ImageFont.truetype(font_path, font_size * 2) text_width, text_height = font.getsize(text) new_font_size, lines, new_text_width, new_text_height = TextBoxDrawer.fit_text( text, text_width, text_height, font_path, font_size, 4) self.assertEqual(new_font_size, font_size)
def data(self): """Create data for a copy of the Job Badges resource. Returns: A dictionary of the one page for the resource. """ image_path = "static/img/resources/job-badges/job-badges.png" image = Image.open(image_path) draw = ImageDraw.Draw(image) textbox_drawer = TextBoxDrawer(image, draw) # coordinates of top left point of text box top_left_x_coords = [50, 1200] top_left_y_coord = 100 # Add text labels for label, label_text in LABEL_DATA.items(): for top_left_x_coord in top_left_x_coords: vertices = calculate_box_vertices( (top_left_x_coord, top_left_y_coord), TEXTBOX_WIDTH, TEXTBOX_HEIGHT ) box = TextBox( vertices=vertices, width=TEXTBOX_WIDTH, height=TEXTBOX_HEIGHT, font_path=FONT_PATH, font_size=FONT_SIZE, ) textbox_drawer.write_text_box( box, label_text, horiz_just="center", vert_just="center", ) # increase y coord for next name tag down top_left_y_coord += 675 return {"type": "image", "data": image}
def data(self): """Create data for a copy of the Job Badges resource. Returns: A dictionary of the one page for the resource. """ path = "static/img/resources/job-badges/job-badges" image_path = "{}.png".format(path) svg_path = "{}.svg".format(path) image = Image.open(image_path) draw = ImageDraw.Draw(image) textbox_drawer = TextBoxDrawer(image, draw, svg_path) hello_ids = [ "programmer_hello1", "programmer_hello2", "tester_hello1", "tester_hello2", "bot_hello1", "bot_hello2", ] for hello_id in hello_ids: textbox_drawer.write_text_box(hello_id, _("Hello, I'm a"), horiz_just="center") for i in range(1, 3): textbox_drawer.write_text_box("programmer{}".format(i), _("Programmer"), horiz_just="center") textbox_drawer.write_text_box("tester{}".format(i), _("Tester"), horiz_just="center") textbox_drawer.write_text_box("bot{}".format(i), _("Bot"), horiz_just="center") return {"type": "image", "data": image}
def test_fit_text_multiple_decrease_loop(self): font_path = "static/fonts/PatrickHand-Regular.ttf" font_size = 100 text = "This is a string" font = ImageFont.truetype(font_path, font_size) text_width, text_height = font.getsize(text) new_font_size, lines, new_text_width, new_text_height = TextBoxDrawer.fit_text( text, text_width * 0.5, # Force many decrease loops text_height, font_path, font_size, 4) self.assertTrue(new_font_size < font_size)
def test_fit_text_one_line(self): font_path = "static/fonts/PatrickHand-Regular.ttf" font_size = 50 font = ImageFont.truetype(font_path, font_size) text = "This is a string" text_width, text_height = font.getsize(text) new_font_size, lines, new_text_width, new_text_height = TextBoxDrawer.fit_text( text, text_width * 2, text_height * 2, font_path, font_size, 4) self.assertEqual(1, len(lines)) self.assertEqual(text, lines[0]) self.assertEqual(font_size, new_font_size) self.assertIsInstance(new_text_width, int) self.assertIsInstance(new_text_height, int)
def data(self): """Create data for a copy of the Grid resource. Returns: A dictionary of the one page for the resource. """ path = "static/img/resources/barcode-checksum-poster/{}-digits" barcode_length = self.options["barcode_length"].value path = path.format(barcode_length) image_path = "{}.png".format(path) svg_path = "{}.svg".format(path) image = Image.open(image_path) draw = ImageDraw.Draw(image) textbox_drawer = TextBoxDrawer(image, draw, svg_path) textbox_drawer.write_text_box( "title", _("{} Digit Barcode".format(barcode_length)), horiz_just="center", ) headings = { "heading1": _("Separate!"), "heading2": _("Operate!"), "heading3": _("Calculate!") } for heading_id, heading in headings.items(): textbox_drawer.write_text_box( heading_id, heading, ) textbox_drawer.write_text_box( "paragraph", _("Remember that this algorithm uses modulo 10, so we are only " "interested in the number in the one's column."), ) return {"type": "image", "data": image}
def test_fit_text_decrease_fontsize(self): font_path = "static/fonts/PatrickHand-Regular.ttf" font_size = 50 font = ImageFont.truetype(font_path, font_size) text = "This is a string" text_width, text_height = font.getsize(text) new_font_size, lines, new_text_width, new_text_height = TextBoxDrawer.fit_text( text, text_width * 0.8, # Make box slightly narrower than text, to force 2 lines text_height, # Make box too short to force smaller text font_path, font_size, 4) self.assertEqual(text, "".join(lines)) self.assertTrue(new_font_size < font_size) self.assertTrue(new_font_size > 1) self.assertIsInstance(new_text_width, int) self.assertIsInstance(new_text_height, int)
def data(self): """Create data for a copy of the Number Hunt resource. Returns: A dictionary of the two pages for the resource. """ pages = [] prefilled_values = self.options["prefilled_values"].value number_order = self.options["number_order"].value image = Image.open(IMAGE_PATH.format("template")) draw = ImageDraw.Draw(image) textbox_drawer = TextBoxDrawer(image, draw) # Add text labels for label_id, label_data in LABEL_DATA.items(): if label_id == "subtitle": label_text = self.subtitle_text() else: label_text = label_data["text"] top_left_coords = label_data["top-left-coords"] width = label_data["width"] height = label_data["height"] vertices = calculate_box_vertices(top_left_coords, width, height) box = TextBox( vertices, width, height, font_path=FONT_PATH, font_size=label_data["font-size"], color=label_data["font-colour"], ) textbox_drawer.write_text_box( box, label_text, horiz_just=label_data["horiz-just"], vert_just=label_data["vert-just"], ) # Add numbers to image (if required) if prefilled_values != "blank": range_min, range_max, font_size = self.get_number_range( prefilled_values) total_numbers = 31 numbers = sample(range(range_min, range_max), total_numbers) if number_order == "sorted": numbers.sort() coord_x = 106 base_coord_y = 161 coord_y_increment = 107.8 width = 264 height = 88 for i, number in enumerate(numbers): coord_y = base_coord_y + (coord_y_increment * i) vertices = calculate_box_vertices((coord_x, coord_y), width, height) box = TextBox( vertices, width, height, font_path=FONT_PATH, font_size=font_size, ) textbox_drawer.write_text_box( box, str(number), horiz_just="center", vert_just="center", ) pages.append({ "type": "resource-number-hunt", "data": [image, INSTRUCTIONS_HTML], "thumbnail": True }) return pages
def test_get_box_id_with_underscore(self): svg = os.path.join(BASE_PATH, "basic.svg") image = Image.new("RGB", (2000, 4000)) tbd = TextBoxDrawer(image, None, svg) tbd.get_box("element_2")
def test_get_box_id_start_with_digit(self): svg = os.path.join(BASE_PATH, "basic.svg") image = Image.new("RGB", (2000, 4000)) tbd = TextBoxDrawer(image, None, svg) tbd.get_box("123")